feat: link repos in the star-scan ledger with full GitHub URLs

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Vinta Chen
2026-11-08 21:46:27 +08:00
co-authored by Claude
parent 66e177d983
commit fc0fda7fa7
3 changed files with 22 additions and 9 deletions
+3 -3
View File
@@ -16,7 +16,7 @@ Run `uv run python .claude/skills/scan-github-stars/scripts/scan_github_stars.py
Judge each candidate from its name, description, topics, and language against CONTRIBUTING Quality Requirement 1 (Serves Python Developers), and name the current use case it would compete in. The screen is loose: drop only what plainly fails, and pass anything uncertain on, since `audit-the-list` is the real judge. In a full scan, read the `prior` column as context and overturn a rejection only on new evidence.
Append each screen-out to `local/starred-scan.tsv` as it is decided, one `repo<TAB>yyyy-mm-dd<TAB>reason` row (create the file with header `repo date reason`), so an interrupted run keeps its work. Only append rows: the script reads the last row per repo, and it prunes rows for repos the maintainer no longer stars. A survivor no current use case fits is a structure question: carry it to review with its evidence. Done when every candidate is a ledger row, holds a target use case, or is a structure question.
Append each screen-out to `local/starred-scan.tsv` as it is decided, one `https://github.com/owner/repo<TAB>yyyy-mm-dd<TAB>reason` row (create the file with header `url date reason`), so an interrupted run keeps its work. Only append rows: the script reads the last row per repo, and it prunes rows for repos the maintainer no longer stars. A survivor no current use case fits is a structure question: carry it to review with its evidence. Done when every candidate is a ledger row, holds a target use case, or is a structure question.
## 3. Judge
@@ -28,8 +28,8 @@ Run `preview-verdicts` over all groups: survivors with kind `candidate` and verd
## 5. Record
- **Added**: follow `audit-the-list` step 5 for the README change, one commit per entry. When the entry links a different repo than the star (a Python binding or client of a starred engine), also append a ledger row `already listed as <entry> (<owner/repo>)` for the star, since the script only recognizes a star whose own repo is linked.
- **Added**: follow `audit-the-list` step 5 for the README change, one commit per entry. When the entry links a different repo than the star (a Python binding or client of a starred engine), also append a ledger row for the star's URL with reason `already listed as <entry> (<owner/repo>)`, since the script only recognizes a star whose own repo is linked.
- **Rejected**: a candidate with verdict `reject` gets one line under "Rejected Additions": `- owner/repo -- yyyy-mm-dd, <downloads>/month (<source>): <reason>.`, replacing its earlier line. This exact rejection takes precedence over a parked ledger row.
- **Parked**: leave listed entries in README.md. An unlisted repo with no prior decision needs no record. To reopen one screened out or rejected before, remove its exact "Rejected Additions" line and append `owner/repo<TAB>yyyy-mm-dd<TAB>parked` to the ledger. Its earlier screen-out rows stay as history. The marker also exempts this repo from an `owner/*` rejection; leave that organization line in place for its other repos. A later screen-out or exact rejection excludes the repo again. A new organization-wide rejection must explicitly resolve any parked exceptions it is intended to cover.
- **Parked**: leave listed entries in README.md. An unlisted repo with no prior decision needs no record. To reopen one screened out or rejected before, remove its exact "Rejected Additions" line and append `https://github.com/owner/repo<TAB>yyyy-mm-dd<TAB>parked` to the ledger. Its earlier screen-out rows stay as history. The marker also exempts this repo from an `owner/*` rejection; leave that organization line in place for its other repos. A later screen-out or exact rejection excludes the repo again. A new organization-wide rejection must explicitly resolve any parked exceptions it is intended to cover.
Done when every candidate from step 1 is listed in README.md, a ledger row, a Rejected Additions line, or parked by the maintainer.
@@ -61,16 +61,18 @@ def listed_repos() -> set[str]:
return repos
def ledger_repo(row: str) -> str:
"""A ledger row starts with the repo's GitHub URL, or a bare owner/repo in older rows."""
return row.split("\t", 1)[0].removeprefix("https://github.com/").lower()
def prior_verdicts() -> dict[str, str | None]:
"""The last ledger row wins; `parked` reopens a repo. An exact registered rejection still wins."""
prior: dict[str, str | None] = {}
if LEDGER.exists():
for line in LEDGER.read_text().splitlines()[1:]:
repo, date, reason = line.split("\t", 2)
if reason == "parked":
prior[repo.lower()] = None
else:
prior[repo.lower()] = f"screened out {date}: {reason}"
_, date, reason = line.split("\t", 2)
prior[ledger_repo(line)] = None if reason == "parked" else f"screened out {date}: {reason}"
register = REGISTER.read_text().splitlines()
start = register.index("## Rejected Additions")
for line in register[start + 1 :]:
@@ -85,7 +87,7 @@ def prune_ledger(starred: set[str]) -> int:
if not LEDGER.exists():
return 0
header, *rows = LEDGER.read_text().splitlines()
kept = [row for row in rows if row.split("\t", 1)[0].lower() in starred]
kept = [row for row in rows if ledger_repo(row) in starred]
if len(kept) < len(rows):
LEDGER.write_text("\n".join([header, *kept]) + "\n")
return len(rows) - len(kept)
+11
View File
@@ -111,6 +111,17 @@ def test_unstarred_repos_are_pruned_from_ledger(scan):
assert module.LEDGER.read_text() == kept
def test_ledger_rows_keyed_by_github_url(scan):
module, _, run = scan
kept = "url\tdate\treason\nhttps://github.com/Example/Project\t2026-10-16\tno use case\n"
module.LEDGER.write_text(kept + "https://github.com/gone/repo\t2026-10-16\tno use case\n")
result = run()
assert "Example/Project" not in result.out
assert "pruned 1" in result.err
assert module.LEDGER.read_text() == kept
def test_parking_keeps_other_exclusions(scan):
module, star, run = scan
now = datetime.now(UTC)