Skip to content

[Feature]: Restructure the README badges (NPM, unique cloners, usage stats) #253

Description

@omaiesh

Problem / Motivation

The badge block at the top of README.md shows PyPI version and monthly downloads for rosetta-mcp and rosetta-cli, the Instructions workflow status, the Python version and the licence.

It misses the rest of how Rosetta is shipped and used. The four public npm packages have no badge at all, and nothing on the page shows how much Rosetta is actually used. The two download badges are also both labelled Downloads, so a reader cannot tell which package each one belongs to.

Proposed Solution

Requested by @isolomatov-gd, verbatim:

  1. Bring NPM ones
  2. Bring unique cloners
  3. Bring stats on usage of rosetta

Rosetta Plan

Plan

Objective: Restructure the README badge block into a two-row layout that adds npm version/downloads badges for all 4 published npm packages, native GitHub stars/forks badges, and an automated unique-cloners badge, while fixing the duplicate "Downloads" label bug.

Approach (two independently-mergeable commits, one PR):

  1. Commit 1 - native badges only, no new infra. Edit the README.md badge block (lines 7-17) and the drifted copy in docs/web/docs/introduction.md (lines 11-15) so both carry the same badge set. Firm decision: sync introduction.md in this same commit, not a follow-up.
  2. Split the single flat p tag into two p rows inside the existing div align=center: Row 1 = packages (12 badges): pypi version+downloads x2 (rosetta-mcp, rosetta-cli) + npm version+downloads x4 (rosettify, rosettify-plugins, rosettify-prompts, curiocity). src/hooks (rosetta-hooks) is private:true - excluded. Row 2 = usage/meta (9 badges): 3 workflow-status badges (unchanged), GitHub stars, GitHub forks, unique-cloners (commit 2), Python 3.12+ (kept, not dropped), Apache-2.0 license.
  3. Fix duplicate-label bug: shields renders the visible label from the URL ?label= query param, not from HTML alt=. Every downloads badge must carry a distinct ?label=. Use npm dm badge type (monthly downloads), matching existing pypi dm badges.
  4. Commit 2 - cloners pipeline. New scheduled workflow .github/workflows/collect-github-stats.yml (weekly cron + workflow_dispatch) runs the existing .claude/skills/collect-github-stats/scripts/collect_stats.py, derives a shields "endpoint badge" JSON, writes it under docs/web/assets/data/unique-cloners.json (must live under docs/web/** or GitHub Pages will never publish it, per .github/workflows/pages.yml lines 6-9), commits+pushes both that file and updated docs/github-stats.json to main.
  5. No new secrets: reuse the SELF_AUTOMATION_PROJECTS_TOKEN bot-push pattern already used in .github/workflows/repo-implement.yml (lines 107-127).

Files:

  • README.md - badge block rewrite: two p rows per above, ?label= on all 6 download badges
  • docs/web/docs/introduction.md - sync badge block to match README (preserve its own relative-URL/Liquid conventions)
  • .github/workflows/collect-github-stats.yml (NEW) - schedule (weekly) + workflow_dispatch triggers, permissions: contents: write, runs collector + derive step, pushes via bot token, uses a concurrency group
  • docs/web/assets/data/unique-cloners.json (NEW, generated only) - never hand-authored; first value produced by a manual workflow_dispatch run before merge
  • docs/reviews/DOC-STRUCTURE-PLAN.md (line 51) - 1-line doc-consistency fix: "PyPI badges (rosetta-mcp, rosetta-cli)" -> also mention npm packages + GitHub usage-stats badges

Testing strategy:

  • Commit 1: preview-render README.md and docs/web/docs/introduction.md; curl -sI every new/changed shields.io URL -> expect 200 + content-type image/svg+xml; visually confirm no two badges share the same rendered label text; confirm the two-row split doesn't wrap awkwardly at ~800px width.
  • Commit 2: trigger via workflow_dispatch BEFORE relying on cron. Verify: (a) docs/github-stats.json gained one new weekly_snapshots entry, (b) docs/web/assets/data/unique-cloners.json parses as JSON and matches the shields endpoint schema, (c) the Pages deploy workflow fired off the docs/web/** path filter, (d) https://griddynamics.github.io/rosetta/assets/data/unique-cloners.json returns HTTP 200, (e) the README's endpoint badge renders a count and a visible date.

Risks / open questions:

  • Silent staleness: if the bot push or collector step fails, the badge freezes with no alert. Mitigated by embedding snapshot_date inside the badge message text. Accepted for this scope; failure notification is a reasonable future follow-up, not required here.
  • Traffic API requires push access: collect_stats.py calls /traffic/*, which 403s without it - workflow MUST authenticate with SELF_AUTOMATION_PROJECTS_TOKEN, not the default GITHUB_TOKEN.
  • Concurrent bot pushes to main: other scheduled workflows (repo-implement.yml, repo-plan.yml, repo-analysis.yml) already push to main under the same token - add a concurrency group and handle rebase-and-retry on push rejection.

Tech Specs

Data model - docs/web/assets/data/unique-cloners.json (shields.io endpoint badge schema):
{"schemaVersion": 1, "label": "unique cloners", "message": "678 (14d, 2026-08-10)", "color": "blue"}

Derived from the selected entry of docs/github-stats.json -> weekly_snapshots[]: message = "{unique_cloners_14d} (14d, {snapshot_date})". schemaVersion MUST be the integer 1; label and message MUST be strings.

"Latest" selection logic: max(weekly_snapshots, key=snapshot_date) - explicit max by ISO date string, not weekly_snapshots[-1]. Lexical max equals chronological max for YYYY-MM-DD strings, robust to an out-of-order or backfilled entry. The derive step MUST fail loudly (non-zero exit, no file written) if weekly_snapshots is empty or the selected entry lacks unique_cloners_14d - never write a placeholder value.

Metric choice: use unique_cloners_14d, NOT clones_14d. The latter is CI/mirror-noise-inflated - it jumped 7024 to 17997 in one week in the existing data while unique_cloners_14d moved only 551 to 678 over the same window (docs/github-stats.json, 2026-08-03 vs 2026-08-10 snapshot rows).

Integration points:

  • Reuse .claude/skills/collect-github-stats/scripts/collect_stats.py unmodified (default --out docs/github-stats.json). It append-merges - never delete or regenerate that file; its traffic history (GitHub's 14-day rolling window) is unrecoverable once lost.
  • Reuse the SELF_AUTOMATION_PROJECTS_TOKEN bot-push pattern from .github/workflows/repo-implement.yml (checkout with persist-credentials: false, then git remote set-url origin with the token, bot git identity github-actions[bot]). Same token doubles as GH_TOKEN for the collector's gh api calls. Job needs permissions: contents: write. No new secret required.
  • Hard constraint: GitHub Pages (.github/workflows/pages.yml) redeploys only on push to docs/web/**, llms-full.txt, or the workflow file itself - the derived badge JSON MUST live under docs/web/assets/data/, since docs/github-stats.json alone (under docs/, not docs/web/) is never published and cannot serve as a shields endpoint source.
  • Schedule: weekly cron aligned with the collector script's documented weekly-Monday-run intent, plus workflow_dispatch for manual/on-demand runs and initial testing.

Acceptance criteria:

  • README.md renders npm version + downloads (dm) badges for rosettify, rosettify-plugins, rosettify-prompts, curiocity (8 badges total); src/hooks excluded (private package).
  • pypi version + downloads badges for rosetta-mcp and rosetta-cli retained unchanged in position/meaning.
  • No two badges in the entire block render the same visible label text; all 6 downloads badges (2 pypi + 4 npm) carry a distinct ?label=.
  • GitHub stars and forks badges present in the usage/meta row, pointing to griddynamics/rosetta.
  • Badge block is two p rows inside the existing div align=center wrapper: row 1 = packages, row 2 = usage/meta (workflow status x3, stars, forks, unique cloners, Python 3.12+, license - Python badge is kept, not dropped).
  • docs/web/docs/introduction.md badge set matches README.md's badge set (same badges; preserve that file's own relative-URL/Liquid conventions).
  • .github/workflows/collect-github-stats.yml exists with both schedule and workflow_dispatch triggers, permissions: contents: write, and a concurrency group.
  • A manual workflow_dispatch run appends one snapshot to docs/github-stats.json and writes a valid docs/web/assets/data/unique-cloners.json, pushing both to main under the bot identity, completed successfully at least once before merge.
  • https://griddynamics.github.io/rosetta/assets/data/unique-cloners.json returns HTTP 200 with valid endpoint-badge JSON.
  • The README's endpoint badge renders a count plus a visible snapshot date.
  • docs/reviews/DOC-STRUCTURE-PLAN.md no longer states PyPI badges are the only badge source.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions