From 8b9d6ebf43aefe8c9a8797e973365eeeecc27aed Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 1 Nov 2026 01:53:20 +0000 Subject: [PATCH] feat(readme): dynamic update date from stats.json, plus first-screen Q&As Stop baking YYYY-MM-DD into the shields badge URL. The badge now reads today_utc from data/stats.json; the stamp line is filled from generated_at on every render_readme / daily-update run. Add two short FAQ items (how numbers are computed; how to use or contribute) and a CONTRIBUTING.md footer link. Co-authored-by: PaintingCoder --- CURRENT_PLAN.md | 28 +++++++++---------- README.ja.md | 14 ++++++++-- README.ko.md | 14 ++++++++-- README.md | 14 ++++++++-- README.zh-Hans.md | 14 ++++++++-- README.zh-Hant.md | 14 ++++++++-- docs/project-governance/ACCEPTANCE.md | 6 ++++ docs/project-governance/CHANGELOG.md | 5 ++++ docs/project-governance/DECISIONS.md | 9 ++++++ docs/project-governance/WORKLOG.md | 8 ++++++ tests/test_repo.py | 37 ++++++++++++++++++++++--- tools/i18n.py | 40 ++++++++++++++++++++------- tools/render_readme.py | 30 ++++++++++++++++---- 13 files changed, 185 insertions(+), 48 deletions(-) diff --git a/CURRENT_PLAN.md b/CURRENT_PLAN.md index ff63a28..4b8abb7 100644 --- a/CURRENT_PLAN.md +++ b/CURRENT_PLAN.md @@ -2,32 +2,32 @@ ## Plan ID -PLAN-002 +PLAN-003 ## Goal -Raise the Verify `history_total` vs `stars` slack so a 3-star GitHub discrepancy no longer blocks the daily data push, without dropping the integrity check. +Polish the generated README first screen and stop baking an update date into the badge URL; the date must come from `data/stats.json` and refresh on every daily-update run. ## In Scope -- The absolute-delta assertion in `tests/test_repo.py` (and a named constant for it) -- A regression test that a 3-star gap is accepted and a larger gap still fails -- Docs that currently claim the history sum equals `stargazers_count` exactly +- `tools/render_readme.py` and `tools/i18n.py` (README / HOW_IT_WORKS / DETAILS copy) +- Regenerated `README*.md` (and the other generated pages the renderer writes) +- Tests that the update badge is dynamic and the stamp text is filled from `stats.json` +- Concise first-screen copy: what the list is, the Actions schedule, how numbers are computed, how to use or contribute, links ## Out of Scope -- Ranking logic, categories, newcomers, hooks, translations, GEO files -- Disabling `tools/verify.py` or skipping the history cross-check -- Secrets, workflow credentials, merging to `main` +- Ranking logic, categories, the ⚠ suspected-crack rules, `data/stats.json` numbers +- Secrets, the daily-update cron times, GEO files (`docs/FAQ.md`, `docs/FACTS.md`, `CITATION.cff`, social preview) +- Merging to `main` ## Tasks -- [x] Confirm the 2026-11-01 daily-update failure is `assertLessEqual(..., 2)` on `affaan-m/ECC` (diff 3) -- [x] Raise the allowed absolute delta from 2 to 5 via a named constant; keep the check -- [x] Add a unit test that delta 3 passes and delta > 5 still fails -- [x] Document the slack (why 5, why not exact) and record the decision -- [x] Run `python3 tools/verify.py` and `python3 tools/next_plan.py --check` -- [x] Open a PR against `main` (do not merge) +- [x] Replace the static `updated-YYYY--MM--DD` shields badge with a dynamic badge that reads `today_utc` from `data/stats.json` +- [x] Keep the stamp line filled from `stats.generated_at` on every `render_readme` / daily-update run +- [x] Add two first-screen FAQ items (how numbers are computed; how to use or contribute) linking existing `docs/FAQ.md` anchors +- [x] Add a test that templates contain no hard-coded update date and that the rendered stamp follows `generated_at` +- [ ] Owner reviews the PR; merge only with approval ## Verification diff --git a/README.ja.md b/README.ja.md index 8f51d1f..c3e15d7 100644 --- a/README.ja.md +++ b/README.ja.md @@ -4,11 +4,11 @@ GitHub スター急上昇ランキング / GitHub Risers -[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CI](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml/badge.svg)](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml) [![updated](https://img.shields.io/badge/updated-2026--10--31-brightgreen.svg)](latest/rankings.ja.md) +[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CI](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml/badge.svg)](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml) [![updated](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjammyfu%2Fgithub-risers%2Fmain%2Fdata%2Fstats.json&query=%24.today_utc&label=updated&color=brightgreen)](latest/rankings.ja.md) 総スター数ではなく、1 日あたりに増えたスター数で GitHub リポジトリを並べます。AI が先頭で、開発者ツール、セキュリティ、Web / フロントエンド、インフラ、デザイン / クリエイティブ、データが続きます。1 日 2 回更新。 -2026-10-31 04:53(UTC)更新、1 日 2 回(北京時間 0 時、ロサンゼルス時間 0 時)。 +データ取得 2026-10-31 04:53(UTC)。1 日 2 回:北京時間 0 時(UTC+8)、ロサンゼルス時間 0 時(America/Los_Angeles)。 [ウェブサイト](https://jammyfu.github.io/github-risers/) · [仕組み](docs/HOW_IT_WORKS.ja.md) · [ファクト](docs/FACTS.md) · [履歴](rankings/README.ja.md) · [最新](latest/rankings.ja.md) @@ -42,6 +42,14 @@ GitHub Trending は GitHub 公式のページです。GitHub Risers は独立し 1 日 2 回、北京時間 0 時とロサンゼルス時間 0 時です。実行のたびに保存します。 [詳しく見る](docs/FAQ.md#how-often-is-it-updated) +### 数値はどう出す? + +直近 7・30・365 の完了した UTC 日の増加スターを日数で割ります。数値はすべて GitHub API から取得し、手入力や推定はしません。 [詳しく見る](docs/FAQ.md#how-is-the-growth-rate-computed) + +### 使い方と参加方法は? + +下のランキングを見るか、latest/ の上位 20 件を見てください。収録プロジェクトを足すには data/projects.json(5 言語)を編集して pull request を出します。 [詳しく見る](docs/FAQ.md#how-can-i-suggest-a-project-or-report-a-mistake) + ## AI > LLM・エージェント・MCP・コーディング助手・生成 AI @@ -521,4 +529,4 @@ GitHub Trending は GitHub 公式のページです。GitHub Risers は独立し 引用の仕方:[CITATION.cff](CITATION.cff) を見てください。 -数字の出し方と参加方法:[docs/HOW_IT_WORKS.ja.md](docs/HOW_IT_WORKS.ja.md)。新規リポジトリのルール:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。カテゴリ:[docs/CATEGORIES.md](docs/CATEGORIES.md)。 +数字の出し方と参加方法:[docs/HOW_IT_WORKS.ja.md](docs/HOW_IT_WORKS.ja.md)。新規リポジトリのルール:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。カテゴリ:[docs/CATEGORIES.md](docs/CATEGORIES.md)。プルリクエスト:[CONTRIBUTING.md](CONTRIBUTING.md)。 diff --git a/README.ko.md b/README.ko.md index 290a55f..cce143a 100644 --- a/README.ko.md +++ b/README.ko.md @@ -4,11 +4,11 @@ GitHub 스타 급상승 랭킹 / GitHub Risers -[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CI](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml/badge.svg)](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml) [![updated](https://img.shields.io/badge/updated-2026--10--31-brightgreen.svg)](latest/rankings.ko.md) +[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CI](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml/badge.svg)](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml) [![updated](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjammyfu%2Fgithub-risers%2Fmain%2Fdata%2Fstats.json&query=%24.today_utc&label=updated&color=brightgreen)](latest/rankings.ko.md) 총 스타 수가 아니라 하루에 늘어난 스타 수로 GitHub 저장소를 줄 세웁니다. AI가 맨 앞이고 개발자 도구, 보안, Web / 프론트엔드, 인프라, 디자인 / 크리에이티브, 데이터가 뒤따릅니다. 하루 두 번 갱신합니다. -2026-10-31 04:53(UTC) 갱신, 하루 두 번(베이징 0시, 로스앤젤레스 0시). +데이터 시각 2026-10-31 04:53(UTC). 하루 두 번: 베이징 0시(UTC+8), 로스앤젤레스 0시(America/Los_Angeles). [웹사이트](https://jammyfu.github.io/github-risers/) · [작동 방식](docs/HOW_IT_WORKS.ko.md) · [팩트](docs/FACTS.md) · [지난 랭킹](rankings/README.ko.md) · [최신](latest/rankings.ko.md) @@ -42,6 +42,14 @@ GitHub Trending은 GitHub 공식 페이지입니다. GitHub Risers는 독립적 하루 두 번, 베이징 시간 0시와 로스앤젤레스 시간 0시입니다. 실행할 때마다 보관합니다. [더 보기](docs/FAQ.md#how-often-is-it-updated) +### 숫자는 어떻게 계산하나요? + +최근 7·30·365개의 완료된 UTC 일에 늘어난 스타를 그 일수로 나눕니다. 숫자는 모두 GitHub API에서 가져오며 손으로 넣거나 추정하지 않습니다. [더 보기](docs/FAQ.md#how-is-the-growth-rate-computed) + +### 어떻게 보고 어떻게 기여하나요? + +아래 랭킹을 보거나 latest/의 상위 20개를 보세요. 수록 프로젝트를 추가하려면 data/projects.json(다섯 언어)을 고친 뒤 pull request를 보내세요. [더 보기](docs/FAQ.md#how-can-i-suggest-a-project-or-report-a-mistake) + ## AI > LLM, 에이전트, MCP, 코딩 어시스턴트, 생성형 AI @@ -521,4 +529,4 @@ GitHub Trending은 GitHub 공식 페이지입니다. GitHub Risers는 독립적 인용 방법: [CITATION.cff](CITATION.cff)를 보세요. -숫자를 만드는 방법과 참여 방법: [docs/HOW_IT_WORKS.ko.md](docs/HOW_IT_WORKS.ko.md). 신규 저장소 규칙: [docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md). 카테고리: [docs/CATEGORIES.md](docs/CATEGORIES.md). +숫자를 만드는 방법과 참여 방법: [docs/HOW_IT_WORKS.ko.md](docs/HOW_IT_WORKS.ko.md). 신규 저장소 규칙: [docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md). 카테고리: [docs/CATEGORIES.md](docs/CATEGORIES.md). 풀 리퀘스트: [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/README.md b/README.md index b4e149f..dcf96ed 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ GitHub Risers: Ranked by Stars Gained per Day -[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CI](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml/badge.svg)](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml) [![updated](https://img.shields.io/badge/updated-2026--10--31-brightgreen.svg)](latest/rankings.md) +[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CI](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml/badge.svg)](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml) [![updated](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjammyfu%2Fgithub-risers%2Fmain%2Fdata%2Fstats.json&query=%24.today_utc&label=updated&color=brightgreen)](latest/rankings.md) Trending GitHub repos ranked by star growth (stars gained per day), not total stars: AI first, then developer tools, security, web, infrastructure, design and data. -Updated 2026-10-31 04:53 UTC, twice a day (00:00 Beijing, 00:00 Los Angeles). +Data from 2026-10-31 04:53 UTC. Twice a day: 00:00 Beijing (UTC+8) and 00:00 Los Angeles (America/Los_Angeles). [Website](https://jammyfu.github.io/github-risers/) · [How it works](docs/HOW_IT_WORKS.md) · [Facts](docs/FACTS.md) · [History](rankings/README.md) · [Latest](latest/rankings.md) @@ -42,6 +42,14 @@ GitHub Trending is GitHub's own page. GitHub Risers is an independent open-sourc Twice a day, at 00:00 Beijing time and 00:00 Los Angeles time. Every run is archived. [Read more](docs/FAQ.md#how-often-is-it-updated) +### How is the data computed? + +Stars gained in the last 7, 30 or 365 complete UTC days, divided by those days. All numbers come from the GitHub API; nothing is typed or estimated. [Read more](docs/FAQ.md#how-is-the-growth-rate-computed) + +### How do I use or contribute? + +Read the boards below, or the full top 20 in latest/. To add a tracked project, edit data/projects.json (five languages) and open a pull request. [Read more](docs/FAQ.md#how-can-i-suggest-a-project-or-report-a-mistake) + ## AI > LLMs, agents, MCP, coding copilots and generative AI @@ -521,4 +529,4 @@ If this saved you time, give the repo a star so others can find it. How to cite: see [CITATION.cff](CITATION.cff). -How the numbers are produced and how to contribute: [docs/HOW_IT_WORKS.md](docs/HOW_IT_WORKS.md). New-repo rules: [docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md). Categories: [docs/CATEGORIES.md](docs/CATEGORIES.md). +How the numbers are produced and how to contribute: [docs/HOW_IT_WORKS.md](docs/HOW_IT_WORKS.md). New-repo rules: [docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md). Categories: [docs/CATEGORIES.md](docs/CATEGORIES.md). Pull requests: [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/README.zh-Hans.md b/README.zh-Hans.md index 01f60ba..5ebf608 100644 --- a/README.zh-Hans.md +++ b/README.zh-Hans.md @@ -4,11 +4,11 @@ GitHub 涨星榜 / GitHub Risers -[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CI](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml/badge.svg)](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml) [![updated](https://img.shields.io/badge/updated-2026--10--31-brightgreen.svg)](latest/rankings.zh-Hans.md) +[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CI](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml/badge.svg)](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml) [![updated](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjammyfu%2Fgithub-risers%2Fmain%2Fdata%2Fstats.json&query=%24.today_utc&label=updated&color=brightgreen)](latest/rankings.zh-Hans.md) 按每天新增星标(而不是总星标)给 GitHub 仓库排名:AI 在前,其后是开发者工具、安全、Web / 前端、基础设施、设计 / 创意和数据;每天更新两次。 -更新于 2026-10-31 04:53(UTC),每天两次(北京时间 0 点、洛杉矶时间 0 点)。 +数据取自 2026-10-31 04:53(UTC)。每天两次:北京时间 0 点(UTC+8)、洛杉矶时间 0 点(America/Los_Angeles)。 [网站](https://jammyfu.github.io/github-risers/) · [运作方式](docs/HOW_IT_WORKS.zh-Hans.md) · [事实速查](docs/FACTS.md) · [历史榜单](rankings/README.zh-Hans.md) · [最新快照](latest/rankings.zh-Hans.md) @@ -42,6 +42,14 @@ GitHub Trending 是 GitHub 官方页面;GitHub 涨星榜是独立的开源项 每天两次,北京时间 0 点和洛杉矶时间 0 点;每次运行都会存档。 [了解更多](docs/FAQ.md#how-often-is-it-updated) +### 数据怎么算? + +取最近 7、30 或 365 个完整 UTC 日的新增星标,再除以天数。数字全部来自 GitHub API,没有手填或估算。 [了解更多](docs/FAQ.md#how-is-the-growth-rate-computed) + +### 怎么看、怎么参与? + +看下面的榜,完整前 20 名在 latest/。要收录项目,改 data/projects.json(五种语言)并发 pull request。 [了解更多](docs/FAQ.md#how-can-i-suggest-a-project-or-report-a-mistake) + ## 人工智能 > 大模型、智能体、MCP、编程助手与生成式 AI @@ -521,4 +529,4 @@ GitHub Trending 是 GitHub 官方页面;GitHub 涨星榜是独立的开源项 如何引用:见 [CITATION.cff](CITATION.cff)。 -数据怎么算和如何参与:[docs/HOW_IT_WORKS.zh-Hans.md](docs/HOW_IT_WORKS.zh-Hans.md)。新仓库规则:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。分类说明:[docs/CATEGORIES.md](docs/CATEGORIES.md)。 +数据怎么算和如何参与:[docs/HOW_IT_WORKS.zh-Hans.md](docs/HOW_IT_WORKS.zh-Hans.md)。新仓库规则:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。分类说明:[docs/CATEGORIES.md](docs/CATEGORIES.md)。提交改动:[CONTRIBUTING.md](CONTRIBUTING.md)。 diff --git a/README.zh-Hant.md b/README.zh-Hant.md index 1f60cc8..3b71e7d 100644 --- a/README.zh-Hant.md +++ b/README.zh-Hant.md @@ -4,11 +4,11 @@ GitHub 漲星榜 / GitHub Risers -[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CI](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml/badge.svg)](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml) [![updated](https://img.shields.io/badge/updated-2026--10--31-brightgreen.svg)](latest/rankings.zh-Hant.md) +[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CI](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml/badge.svg)](https://github.com/jammyfu/github-risers/actions/workflows/ci.yml) [![updated](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjammyfu%2Fgithub-risers%2Fmain%2Fdata%2Fstats.json&query=%24.today_utc&label=updated&color=brightgreen)](latest/rankings.zh-Hant.md) 依每天新增星號(而不是總星號)替 GitHub 儲存庫排名:AI 在前,接著是開發者工具、資安、Web / 前端、基礎建設、設計 / 創意與資料;每天更新兩次。 -更新於 2026-10-31 04:53(UTC),每天兩次(北京時間 0 點、洛杉磯時間 0 點)。 +資料取自 2026-10-31 04:53(UTC)。每天兩次:北京時間 0 點(UTC+8)、洛杉磯時間 0 點(America/Los_Angeles)。 [網站](https://jammyfu.github.io/github-risers/) · [運作方式](docs/HOW_IT_WORKS.zh-Hant.md) · [事實速查](docs/FACTS.md) · [歷史榜單](rankings/README.zh-Hant.md) · [最新快照](latest/rankings.zh-Hant.md) @@ -42,6 +42,14 @@ GitHub Trending 是 GitHub 官方頁面;GitHub 漲星榜是獨立的開源專 每天兩次,北京時間 0 點和洛杉磯時間 0 點;每次執行都會存檔。 [了解更多](docs/FAQ.md#how-often-is-it-updated) +### 資料怎麼算? + +取最近 7、30 或 365 個完整 UTC 日的新增星號,再除以天數。數字全部來自 GitHub API,沒有手填或估算。 [了解更多](docs/FAQ.md#how-is-the-growth-rate-computed) + +### 怎麼看、怎麼參與? + +看下面的榜,完整前 20 名在 latest/。要收錄專案,改 data/projects.json(五種語言)並發 pull request。 [了解更多](docs/FAQ.md#how-can-i-suggest-a-project-or-report-a-mistake) + ## 人工智慧 > 大型語言模型、代理、MCP、程式設計助手與生成式 AI @@ -521,4 +529,4 @@ GitHub Trending 是 GitHub 官方頁面;GitHub 漲星榜是獨立的開源專 如何引用:見 [CITATION.cff](CITATION.cff)。 -數據怎麼算與如何參與:[docs/HOW_IT_WORKS.zh-Hant.md](docs/HOW_IT_WORKS.zh-Hant.md)。新儲存庫規則:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。分類說明:[docs/CATEGORIES.md](docs/CATEGORIES.md)。 +資料怎麼算與如何參與:[docs/HOW_IT_WORKS.zh-Hant.md](docs/HOW_IT_WORKS.zh-Hant.md)。新儲存庫規則:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。分類說明:[docs/CATEGORIES.md](docs/CATEGORIES.md)。提交改動:[CONTRIBUTING.md](CONTRIBUTING.md)。 diff --git a/docs/project-governance/ACCEPTANCE.md b/docs/project-governance/ACCEPTANCE.md index 19acb08..8e0cf02 100644 --- a/docs/project-governance/ACCEPTANCE.md +++ b/docs/project-governance/ACCEPTANCE.md @@ -18,6 +18,12 @@ - Every scheduled entry (both Beijing and Los Angeles crons, `workflow_dispatch`, local launchd) runs `tools/update_stars.py` (`tests/test_spam.py::ScheduledPathTests`) - README is a first screen (top 10 per board); the full top 20 is in `latest/`; GEO files exist and are linked +## README update date + +- The `updated` badge is dynamic (`shields.io` JSON → `data/stats.json` `today_utc`); i18n templates contain no `YYYY-MM-DD` +- The stamp line is written from `stats.generated_at` on every `python3 tools/render_readme.py` run +- First screen states what the list is, the Beijing / Los Angeles schedule, how numbers are computed, and how to use or contribute + ## Phase 4: harness - `CURRENT_PLAN.md` passes `python3 tools/next_plan.py --check` (one Plan ID, one Goal, 3-7 tasks, Out of Scope, Verification, Next Candidates) diff --git a/docs/project-governance/CHANGELOG.md b/docs/project-governance/CHANGELOG.md index 111dd04..c5de926 100644 --- a/docs/project-governance/CHANGELOG.md +++ b/docs/project-governance/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG.md +## 2026-11-01 (README date and first screen) + +- The README `updated` badge is a dynamic shields.io JSON badge that reads `today_utc` from `data/stats.json`. The stamp line is filled from `generated_at` on every daily-update render. No update date is typed into `tools/i18n.py`. +- First screen adds two short Q&As: how the numbers are computed, and how to use or contribute. Footer links `CONTRIBUTING.md`. Schedule on the stamp matches Actions: Beijing 00:00 (UTC+8) and Los Angeles 00:00 (`America/Los_Angeles`). + ## 2026-10-29 - Rankings by stars/day (week, month, year) with total stars last, all from the GitHub API. diff --git a/docs/project-governance/DECISIONS.md b/docs/project-governance/DECISIONS.md index f9d2f28..028437b 100644 --- a/docs/project-governance/DECISIONS.md +++ b/docs/project-governance/DECISIONS.md @@ -67,3 +67,12 @@ - The daily-update Verify step (`tests/test_repo.py`) still requires a full history sum to stay close to `stargazers_count`. The allowed absolute delta is now 5 (`tools/risers_core.py::HISTORY_TOTAL_STAR_SLACK`). - A 3-star gap is accepted; a gap larger than 5 still fails. The check is not removed. + +## 2026-11-01 (README update date) + +### Do not bake the update date into the badge URL + +- The `updated` badge used to be `img.shields.io/badge/updated-YYYY--MM--DD-…`, which froze the date in the generated README. +- It now reads `$.today_utc` from `data/stats.json` on `main` via shields.io dynamic JSON. When the daily job writes a new `today_utc`, the badge refreshes without anyone editing the README by hand. +- The stamp line still comes from `stats.generated_at` on every `tools/render_readme.py` run (the same path the daily-update workflow uses). Templates in `tools/i18n.py` contain `{ts}` only; no `YYYY-MM-DD` is typed in. +- First-screen FAQ grew by two items (how the numbers are computed; how to use or contribute). They link existing `docs/FAQ.md` anchors; the GEO FAQ file is not generated. diff --git a/docs/project-governance/WORKLOG.md b/docs/project-governance/WORKLOG.md index eb95f2e..0298b8b 100644 --- a/docs/project-governance/WORKLOG.md +++ b/docs/project-governance/WORKLOG.md @@ -58,3 +58,11 @@ - Read `jammyfu/CoderSkills` (`d37305a`). Applicable: `skills/continuous-project-loop`, `skills/multi-project-program-management`; partly: `skills/readme-tech-visualizer` (audit only, README first screen already free of runtime tables, timestamps-as-noise and "Generated by"). Not applicable: `git-autosync-signal` (only `dramaforge`/`clippipeline`), `skill-lab-builder`, content and media skills. - Gaps found: stale `CURRENT_PLAN.md`/`MASTER_PLAN.md`/`TODO_BACKLOG.md`/governance files; dirty-worktree and record-order rules only in chat; no check of the plan shape; no regression test that `verify.py` and `local_daily.sh` avoid PATH/`shell=True`. - Added `tools/next_plan.py --check`, `tests/test_harness.py`, and wrote the rules into `AGENTS.md` and `docs/`. + +## 2026-11-01 — README date and first-screen polish (PLAN-003) + +- Replaced the static `updated-YYYY--MM--DD` shields badge with a dynamic JSON badge that reads `$.today_utc` from `data/stats.json` on `main`. +- Stamp line still filled from `stats.generated_at` via `render_readme.stamp_ts` on every render (daily-update path unchanged). +- Added FAQ q4/q5 (compute; use/contribute) linking existing GEO FAQ anchors; footer links `CONTRIBUTING.md`. +- Test: templates contain no `YYYY-MM-DD`; rendered stamp follows `generated_at`; badge URL has no baked date. +- Verification: `python3 tools/verify.py` green (202 tests). diff --git a/tests/test_repo.py b/tests/test_repo.py index 1bb9491..74477e6 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -203,10 +203,33 @@ def test_first_screen_order_and_nothing_that_moved_out(self): render_readme.SITE, f"## {s['tops_title']}", f"## {s['faq_title']}"] positions = [head.index(x) for x in order] self.assertEqual(positions, sorted(positions), code) - self.assertLessEqual(len(head.splitlines()), 45, code) + self.assertLessEqual(len(head.splitlines()), 58, code) for gone in (s["intro"], s["schedule"], s["tops_intro"], s["new_intro"], s["method"][0], s["contrib"], "
"): self.assertNotIn(gone, text, f"{code}: {gone[:40]}") + def test_update_date_is_from_stats_not_baked_into_templates(self): + """The update date is never typed into i18n strings or the badge URL.""" + for code, s in render_readme.S.items(): + for key, value in s.items(): + blob = value if isinstance(value, str) else " ".join(value) + self.assertNotRegex(blob, r"20\d{2}-\d{2}-\d{2}", f"{code}.{key} has a hard-coded date") + src = (ROOT / "tools/render_readme.py").read_text("utf-8") + self.assertNotRegex(src, r"badge/updated-\d{4}") + self.assertIn("today_utc", src) + ts = render_readme.stamp_ts(stats) + cats_by_id = {c["id"]: c for c in cats} + for code, (fname, _l) in LANGS.items(): + text = self.readme(code) + head = self.head(code) + self.assertIn(ts, head, fname) + self.assertIn("badge/dynamic/json", head, fname) + self.assertNotRegex(head, r"badge/updated-\d{4}--\d\d--\d\d") + moved = dict(stats) + moved["generated_at"] = "2026-11-01T12:34:00Z" + rendered = render_readme.render("en", projects, moved, cats_by_id) + self.assertIn("2026-11-01 12:34", rendered) + self.assertNotIn(ts, rendered.split("##", 1)[0]) + def test_title_names_and_english_adds_the_ranking_rule(self): S_ = render_readme.S self.assertEqual(S_["zh-Hans"]["title"], "GitHub 涨星榜 / GitHub Risers") @@ -220,16 +243,22 @@ def test_at_most_three_badges_mit_ci_updated(self): self.assertEqual(len(badges), 3, code) self.assertIn("license-MIT", badges[0]) self.assertIn("actions/workflows/ci.yml/badge.svg", badges[1]) - self.assertRegex(badges[2], r"badge/updated-\d{4}--\d\d--\d\d-") + self.assertIn("badge/dynamic/json", badges[2]) + self.assertIn("data%2Fstats.json", badges[2]) + self.assertIn("today_utc", badges[2]) + self.assertNotRegex(badges[2], r"updated-\d{4}--\d\d--\d\d") def test_geo_links_are_present_in_every_language(self): for code in LANG_CODES: text, s = self.readme(code), render_readme.S[code] for needle in ("https://jammyfu.github.io/github-risers/", "docs/FAQ.md#what-is-github-risers", "docs/FAQ.md#is-it-the-same-as-github-trending", "docs/FAQ.md#how-often-is-it-updated", - "docs/FACTS.md", "CITATION.cff", ".github/social-preview.png", s["website"], s["star_ask"]): + "docs/FAQ.md#how-is-the-growth-rate-computed", + "docs/FAQ.md#how-can-i-suggest-a-project-or-report-a-mistake", + "docs/FACTS.md", "CITATION.cff", "CONTRIBUTING.md", ".github/social-preview.png", + s["website"], s["star_ask"]): self.assertIn(needle, text, f"{code}: {needle}") - for key in ("q1", "q2", "q3"): + for key in ("q1", "q2", "q3", "q4", "q5"): self.assertIn(f"### {s[key]}", text, code) def test_faq_links_point_at_real_faq_headings(self): diff --git a/tools/i18n.py b/tools/i18n.py index 95715cb..2b83128 100644 --- a/tools/i18n.py +++ b/tools/i18n.py @@ -42,11 +42,15 @@ "a2": "GitHub Trending is GitHub's own page. GitHub Risers is an independent open-source project with published rules, five languages and a saved history of every update. It is not affiliated with GitHub.", "q3": "How often is it updated?", "a3": "Twice a day, at 00:00 Beijing time and 00:00 Los Angeles time. Every run is archived.", + "q4": "How is the data computed?", + "a4": "Stars gained in the last 7, 30 or 365 complete UTC days, divided by those days. All numbers come from the GitHub API; nothing is typed or estimated.", + "q5": "How do I use or contribute?", + "a5": "Read the boards below, or the full top 20 in latest/. To add a tracked project, edit data/projects.json (five languages) and open a pull request.", "more": "Read more", "star_ask": "If this saved you time, give the repo a star so others can find it.", "cite": "How to cite: see [CITATION.cff](CITATION.cff).", - "stamp": "Updated {ts} UTC, twice a day (00:00 Beijing, 00:00 Los Angeles).", - "footer": "How the numbers are produced and how to contribute: [{how}]({how}). New-repo rules: [docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md). Categories: [docs/CATEGORIES.md](docs/CATEGORIES.md).", + "stamp": "Data from {ts} UTC. Twice a day: 00:00 Beijing (UTC+8) and 00:00 Los Angeles (America/Los_Angeles).", + "footer": "How the numbers are produced and how to contribute: [{how}]({how}). New-repo rules: [docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md). Categories: [docs/CATEGORIES.md](docs/CATEGORIES.md). Pull requests: [CONTRIBUTING.md](CONTRIBUTING.md).", "doc_title": "How GitHub Risers works", "doc_back": "← Back to the rankings", "intro": "Total-star lists keep the old giants on top and bury what is taking off right now. GitHub Risers ranks repos by stars gained per day (last 7, 30 and 365 days) and groups them into categories, with AI as the main board. Every category has a hottest-new-repos board for repos created in the last 7 days, then week, month and year star-growth rankings, and total stars last. All numbers come straight from the GitHub API. Classification rules: [docs/CATEGORIES.md](docs/CATEGORIES.md).", @@ -136,11 +140,15 @@ "a2": "GitHub Trending 是 GitHub 官方页面;GitHub 涨星榜是独立的开源项目,规则公开,有五种语言,并保存每次更新的历史。它与 GitHub 官方没有关联。", "q3": "多久更新一次?", "a3": "每天两次,北京时间 0 点和洛杉矶时间 0 点;每次运行都会存档。", + "q4": "数据怎么算?", + "a4": "取最近 7、30 或 365 个完整 UTC 日的新增星标,再除以天数。数字全部来自 GitHub API,没有手填或估算。", + "q5": "怎么看、怎么参与?", + "a5": "看下面的榜,完整前 20 名在 latest/。要收录项目,改 data/projects.json(五种语言)并发 pull request。", "more": "了解更多", "star_ask": "如果它帮你省了时间,请给仓库点个星标,让更多人找得到。", "cite": "如何引用:见 [CITATION.cff](CITATION.cff)。", - "stamp": "更新于 {ts}(UTC),每天两次(北京时间 0 点、洛杉矶时间 0 点)。", - "footer": "数据怎么算和如何参与:[{how}]({how})。新仓库规则:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。分类说明:[docs/CATEGORIES.md](docs/CATEGORIES.md)。", + "stamp": "数据取自 {ts}(UTC)。每天两次:北京时间 0 点(UTC+8)、洛杉矶时间 0 点(America/Los_Angeles)。", + "footer": "数据怎么算和如何参与:[{how}]({how})。新仓库规则:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。分类说明:[docs/CATEGORIES.md](docs/CATEGORIES.md)。提交改动:[CONTRIBUTING.md](CONTRIBUTING.md)。", "doc_title": "GitHub 飙升榜是怎么做的", "doc_back": "← 返回榜单", "intro": "多数榜单按总星标数排序,老项目长期占据榜首,正在爆发的新项目反而被埋没。GitHub 飙升榜按“每天新增多少星标”来排:统计近 7 天、30 天和 365 天的日均涨星,并按分类展示,AI 是默认置顶的主榜。每个分类先看“7 天新晋”榜(最近 7 天内创建的仓库),再看周、月、年涨星榜,总星标榜放在最后。数据全部来自 GitHub API。分类规则见 [docs/CATEGORIES.md](docs/CATEGORIES.md)。", @@ -234,11 +242,15 @@ "a2": "GitHub Trending 是 GitHub 官方頁面;GitHub 漲星榜是獨立的開源專案,規則公開,有五種語言,並保存每次更新的歷史。它與 GitHub 官方沒有關聯。", "q3": "多久更新一次?", "a3": "每天兩次,北京時間 0 點和洛杉磯時間 0 點;每次執行都會存檔。", + "q4": "資料怎麼算?", + "a4": "取最近 7、30 或 365 個完整 UTC 日的新增星號,再除以天數。數字全部來自 GitHub API,沒有手填或估算。", + "q5": "怎麼看、怎麼參與?", + "a5": "看下面的榜,完整前 20 名在 latest/。要收錄專案,改 data/projects.json(五種語言)並發 pull request。", "more": "了解更多", "star_ask": "如果它幫你省了時間,請給儲存庫按個星號,讓更多人找得到。", "cite": "如何引用:見 [CITATION.cff](CITATION.cff)。", - "stamp": "更新於 {ts}(UTC),每天兩次(北京時間 0 點、洛杉磯時間 0 點)。", - "footer": "數據怎麼算與如何參與:[{how}]({how})。新儲存庫規則:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。分類說明:[docs/CATEGORIES.md](docs/CATEGORIES.md)。", + "stamp": "資料取自 {ts}(UTC)。每天兩次:北京時間 0 點(UTC+8)、洛杉磯時間 0 點(America/Los_Angeles)。", + "footer": "資料怎麼算與如何參與:[{how}]({how})。新儲存庫規則:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。分類說明:[docs/CATEGORIES.md](docs/CATEGORIES.md)。提交改動:[CONTRIBUTING.md](CONTRIBUTING.md)。", "doc_title": "GitHub 飆升榜是怎麼做的", "doc_back": "← 回到榜單", "intro": "多數榜單依總星號數排序,老專案長期霸占榜首,正在爆發的新專案反而被埋沒。GitHub 飆升榜依「每天新增多少星號」來排:統計近 7 天、30 天和 365 天的日均漲星,並依分類呈現,AI 是預設置頂的主榜。每個分類先看「7 天新晉」榜(最近 7 天內建立的儲存庫),再看週、月、年漲星榜,總星號榜放在最後。資料全部來自 GitHub API。分類規則請見 [docs/CATEGORIES.md](docs/CATEGORIES.md)。", @@ -332,11 +344,15 @@ "a2": "GitHub Trending は GitHub 公式のページです。GitHub Risers は独立したオープンソースのプロジェクトで、ルールを公開し、5 言語に対応し、更新のたびに履歴を残します。GitHub とは無関係です。", "q3": "更新は何回?", "a3": "1 日 2 回、北京時間 0 時とロサンゼルス時間 0 時です。実行のたびに保存します。", + "q4": "数値はどう出す?", + "a4": "直近 7・30・365 の完了した UTC 日の増加スターを日数で割ります。数値はすべて GitHub API から取得し、手入力や推定はしません。", + "q5": "使い方と参加方法は?", + "a5": "下のランキングを見るか、latest/ の上位 20 件を見てください。収録プロジェクトを足すには data/projects.json(5 言語)を編集して pull request を出します。", "more": "詳しく見る", "star_ask": "役に立ったら、ほかの人にも見つけてもらえるようリポジトリにスターを付けてください。", "cite": "引用の仕方:[CITATION.cff](CITATION.cff) を見てください。", - "stamp": "{ts}(UTC)更新、1 日 2 回(北京時間 0 時、ロサンゼルス時間 0 時)。", - "footer": "数字の出し方と参加方法:[{how}]({how})。新規リポジトリのルール:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。カテゴリ:[docs/CATEGORIES.md](docs/CATEGORIES.md)。", + "stamp": "データ取得 {ts}(UTC)。1 日 2 回:北京時間 0 時(UTC+8)、ロサンゼルス時間 0 時(America/Los_Angeles)。", + "footer": "数字の出し方と参加方法:[{how}]({how})。新規リポジトリのルール:[docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md)。カテゴリ:[docs/CATEGORIES.md](docs/CATEGORIES.md)。プルリクエスト:[CONTRIBUTING.md](CONTRIBUTING.md)。", "doc_title": "GitHub Risers の仕組み", "doc_back": "← ランキングに戻る", "intro": "総スター数順のリストでは古い大物が居座り、いま伸びているものが埋もれます。GitHub Risers は 1 日あたりの増加スター数(直近 7・30・365 日)で並べ、カテゴリごとに表示します。AI がメインのランキングです。各カテゴリは、直近 7 日以内に作られたリポジトリの「新着」ランキング、週・月・年の増加ランキング、最後に総スター数の順です。数値はすべて GitHub API から取得しています。分類ルール:[docs/CATEGORIES.md](docs/CATEGORIES.md)。", @@ -426,11 +442,15 @@ "a2": "GitHub Trending은 GitHub 공식 페이지입니다. GitHub Risers는 독립적인 오픈 소스 프로젝트로, 규칙을 공개하고 5개 언어를 지원하며 갱신 때마다 기록을 남깁니다. GitHub와는 관련이 없습니다.", "q3": "얼마나 자주 갱신하나요?", "a3": "하루 두 번, 베이징 시간 0시와 로스앤젤레스 시간 0시입니다. 실행할 때마다 보관합니다.", + "q4": "숫자는 어떻게 계산하나요?", + "a4": "최근 7·30·365개의 완료된 UTC 일에 늘어난 스타를 그 일수로 나눕니다. 숫자는 모두 GitHub API에서 가져오며 손으로 넣거나 추정하지 않습니다.", + "q5": "어떻게 보고 어떻게 기여하나요?", + "a5": "아래 랭킹을 보거나 latest/의 상위 20개를 보세요. 수록 프로젝트를 추가하려면 data/projects.json(다섯 언어)을 고친 뒤 pull request를 보내세요.", "more": "더 보기", "star_ask": "도움이 됐다면 다른 사람도 찾을 수 있도록 저장소에 스타를 눌러 주세요.", "cite": "인용 방법: [CITATION.cff](CITATION.cff)를 보세요.", - "stamp": "{ts}(UTC) 갱신, 하루 두 번(베이징 0시, 로스앤젤레스 0시).", - "footer": "숫자를 만드는 방법과 참여 방법: [{how}]({how}). 신규 저장소 규칙: [docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md). 카테고리: [docs/CATEGORIES.md](docs/CATEGORIES.md).", + "stamp": "데이터 시각 {ts}(UTC). 하루 두 번: 베이징 0시(UTC+8), 로스앤젤레스 0시(America/Los_Angeles).", + "footer": "숫자를 만드는 방법과 참여 방법: [{how}]({how}). 신규 저장소 규칙: [docs/NEW_ARRIVALS.md](docs/NEW_ARRIVALS.md). 카테고리: [docs/CATEGORIES.md](docs/CATEGORIES.md). 풀 리퀘스트: [CONTRIBUTING.md](CONTRIBUTING.md).", "doc_title": "GitHub Risers 작동 방식", "doc_back": "← 랭킹으로 돌아가기", "intro": "총 스타 순 목록에서는 오래된 대형 프로젝트가 계속 상위를 차지하고, 지금 뜨는 프로젝트는 묻힙니다. GitHub Risers는 하루 평균 늘어난 스타 수(최근 7·30·365일)로 순위를 매기고 카테고리별로 보여 주며, AI가 메인 랭킹입니다. 각 카테고리는 최근 7일 안에 만들어진 저장소의 신규 랭킹, 주·월·연 증가 랭킹, 마지막으로 총 스타 순입니다. 모든 수치는 GitHub API에서 가져옵니다. 분류 규칙: [docs/CATEGORIES.md](docs/CATEGORIES.md).", diff --git a/tools/render_readme.py b/tools/render_readme.py index 9de7b38..9589116 100755 --- a/tools/render_readme.py +++ b/tools/render_readme.py @@ -28,7 +28,21 @@ SITE = "https://jammyfu.github.io/github-risers/" SOCIAL_IMAGE = ".github/social-preview.png" # Anchors of docs/FAQ.md (owned by the GEO work, checked by a test); only linked, never generated here. -FAQ_ANCHORS = ("what-is-github-risers", "is-it-the-same-as-github-trending", "how-often-is-it-updated") +FAQ_ANCHORS = ( + "what-is-github-risers", + "is-it-the-same-as-github-trending", + "how-often-is-it-updated", + "how-is-the-growth-rate-computed", + "how-can-i-suggest-a-project-or-report-a-mistake", +) +# shields.io reads data/stats.json on main; the README markdown never embeds a date in the badge URL. +UPDATED_BADGE = ( + "https://img.shields.io/badge/dynamic/json?" + "url=https%3A%2F%2Fraw.githubusercontent.com%2Fjammyfu%2Fgithub-risers%2Fmain%2Fdata%2Fstats.json" + "&query=%24.today_utc" + "&label=updated" + "&color=brightgreen" +) def switcher(lang: str) -> str: @@ -233,13 +247,19 @@ def tops_table(stats: dict, projects: list[dict], cats: dict, lang: str, link: b return "\n".join(lines) +def stamp_ts(stats: dict) -> str: + """UTC timestamp from stats.json (`generated_at`), not a date typed into the README.""" + return stats["generated_at"].replace("T", " ").replace("Z", "")[:16] + + def badges(stats: dict, lang: str) -> str: - """Three badges, no more: licence, CI, last update.""" - day = stats["generated_at"][:10].replace("-", "--") # shields.io: "--" is a literal hyphen + """Three badges, no more: licence, CI, last update (date from stats.json, not baked in).""" + if "today_utc" not in stats: + raise KeyError("stats.json must have today_utc for the updated badge") return " ".join([ "[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)", f"[![CI](https://github.com/{REPO}/actions/workflows/ci.yml/badge.svg)](https://github.com/{REPO}/actions/workflows/ci.yml)", - f"[![updated](https://img.shields.io/badge/updated-{day}-brightgreen.svg)](latest/{archive.rankings_name(lang)})", + f"[![updated]({UPDATED_BADGE})](latest/{archive.rankings_name(lang)})", ]) @@ -263,7 +283,7 @@ def render(lang: str, projects: list[dict], stats: dict, cats: dict, tr: dict | f'{s[', "", badges(stats, lang), "", s["tagline"], "", - s["stamp"].format(ts=stats["generated_at"].replace("T", " ").replace("Z", "")[:16]), "", + s["stamp"].format(ts=stamp_ts(stats)), "", s["links"].format(website=s["website"], site=SITE, how=how, index=f"rankings/{archive.index_name(lang)}", latest=latest), "", f"## {s['tops_title']}", "",