fix: reopen parked projects in incremental star scans

Parking a project during a full review left its previous screen-out or rejection active, so incremental scans hid it instead of revisiting it.

Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
Vinta Chen
2026-10-16 19:18:59 +08:00
co-authored by Codex
parent 3827f0e622
commit 32ac8c9b3c
3 changed files with 129 additions and 8 deletions
+3 -3
View File
@@ -7,7 +7,7 @@ disable-model-invocation: true
# Scan GitHub stars
A star scan turns the maintainer's GitHub stars into proposed additions. Every star the scan sees ends in one place: listed in README.md, screened out in `local/starred-scan.tsv`, or rejected under `docs/audit-logs.md` "Rejected Additions". Judgment belongs to `audit-the-list` and the rules to CONTRIBUTING.md; this skill only sources candidates and records outcomes.
A star scan turns the maintainer's GitHub stars into proposed additions. Every star the scan sees ends in one place: listed in README.md, screened out in `local/starred-scan.tsv`, rejected under `docs/audit-logs.md` "Rejected Additions", or parked for another review. Judgment belongs to `audit-the-list` and the rules to CONTRIBUTING.md; this skill only sources candidates and records outcomes.
## 1. Scan
@@ -30,7 +30,7 @@ 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.
- **Rejected**: a candidate that reached review and was not added gets one line under "Rejected Additions": `- owner/repo -- yyyy-mm-dd, <downloads>/month (<source>): <reason>.`, replacing its earlier line.
- **Parked**: a row the maintainer parks gets no record, so it resurfaces next run.
- **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.
Done when every candidate from step 1 is listed in README.md, a ledger row, a Rejected Additions line, or parked by the maintainer.
@@ -60,13 +60,16 @@ def listed_repos() -> set[str]:
return repos
def prior_verdicts() -> dict[str, str]:
"""Repo -> earlier verdict. The ledger is append-only, so its last row per repo wins; a maintainer rejection outranks both."""
prior = {}
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)
prior[repo.lower()] = f"screened out {date}: {reason}"
if reason == "parked":
prior[repo.lower()] = None
else:
prior[repo.lower()] = f"screened out {date}: {reason}"
register = REGISTER.read_text().splitlines()
start = register.index("## Rejected Additions")
for line in register[start + 1 :]:
@@ -92,7 +95,7 @@ def main() -> None:
print("repo\tlanguage\tstars\tpushed\ttopics\tdescription\tprior")
for star in stars:
repo = star["full_name"].lower()
verdict = prior.get(repo) or prior.get(f"{star['owner']['login'].lower()}/*")
verdict = prior[repo] if repo in prior else prior.get(f"{star['owner']['login'].lower()}/*")
if repo in listed:
dropped["already listed"] += 1
elif star["owner"]["login"].lower() == login:
+118
View File
@@ -0,0 +1,118 @@
import importlib.util
import json
from datetime import UTC, datetime, timedelta
from pathlib import Path
import pytest
@pytest.fixture
def scan(tmp_path, monkeypatch, capsys):
script = Path(__file__).resolve().parents[2] / ".claude/skills/scan-github-stars/scripts/scan_github_stars.py"
spec = importlib.util.spec_from_file_location("scan_github_stars", script)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
monkeypatch.setattr(module, "LEDGER", tmp_path / "starred-scan.tsv")
monkeypatch.setattr(module, "REGISTER", tmp_path / "audit-logs.md")
monkeypatch.setattr(module, "listed_repos", lambda: {"example/listed"})
module.LEDGER.write_text("repo\tdate\treason\n")
module.REGISTER.write_text("## Rejected Additions\n\n## Last Audited\n")
now = datetime.now(UTC)
star = {
"full_name": "Example/Project",
"owner": {"login": "Example"},
"archived": False,
"pushed_at": now.isoformat(),
"created_at": (now - timedelta(days=100)).isoformat(),
"language": "Python",
"stargazers_count": 100,
"topics": [],
"description": "A candidate",
}
def run(*args, stars=None):
monkeypatch.setattr(module.sys, "argv", [str(script), *args])
monkeypatch.setattr(module, "gh", lambda *args: "maintainer" if args[1] == "user" else "\n".join(json.dumps(s) for s in (stars or [star])))
module.main()
return capsys.readouterr()
return module, star, run
@pytest.mark.parametrize("rejected", [False, True])
def test_full_review_can_park_a_previous_verdict(scan, rejected):
module, _, run = scan
history = "repo\tdate\treason\nExample/Project\t2026-10-16\tno matching use case\n"
module.LEDGER.write_text(history)
if rejected:
module.REGISTER.write_text("## Rejected Additions\n- example/project -- 2026-10-16: no matching use case\n")
assert "Example/Project" not in run().out
review = run("--full").out
assert "Example/Project" in review
assert ("rejected" if rejected else "screened out") in review
module.LEDGER.write_text(history + "example/project\t2026-10-16\tparked\n")
if rejected:
module.REGISTER.write_text("## Rejected Additions\n")
assert "Example/Project" in run().out
assert module.LEDGER.read_text().startswith(history)
@pytest.mark.parametrize("decision", ["screen", "reject"])
def test_later_decision_on_same_day_excludes_parked_repo(scan, decision):
module, _, run = scan
history = "repo\tdate\treason\nexample/project\t2026-10-16\tparked\n"
module.LEDGER.write_text(history)
assert "Example/Project" in run().out
if decision == "screen":
module.LEDGER.write_text(history + "EXAMPLE/PROJECT\t2026-10-16\tstill no use case\n")
else:
module.REGISTER.write_text("## Rejected Additions\n- EXAMPLE/PROJECT -- 2026-10-16: still no use case\n")
assert "Example/Project" not in run().out
assert "still no use case" in run("--full").out
def test_parking_does_not_override_exact_registered_rejection(scan):
module, _, run = scan
module.LEDGER.write_text("repo\tdate\treason\nexample/project\t2026-10-16\tparked\n")
module.REGISTER.write_text("## Rejected Additions\n- Example/Project -- 2026-10-16: maintainer rejection\n")
assert "Example/Project" not in run().out
assert "maintainer rejection" in run("--full").out
def test_parking_reopens_one_repo_from_rejected_organization(scan):
module, star, run = scan
module.REGISTER.write_text("## Rejected Additions\n- EXAMPLE/* -- 2026-10-16: maintainer rejection\n")
peer = star | {"full_name": "Example/Other"}
assert "Example/Project" not in run().out
module.LEDGER.write_text("repo\tdate\treason\nexample/project\t2026-10-16\tparked\n")
result = run(stars=[star, peer]).out
assert "Example/Project" in result
assert "Example/Other" not in result
module.REGISTER.write_text(module.REGISTER.read_text() + "- example/project -- 2026-10-16: rejected again\n")
assert "Example/Project" not in run().out
assert "rejected again" in run("--full").out
def test_parking_keeps_other_exclusions(scan):
module, star, run = scan
now = datetime.now(UTC)
changes = [
{"full_name": "example/listed"},
{"owner": {"login": "maintainer"}},
{"archived": True},
{"pushed_at": (now - timedelta(days=400)).isoformat()},
{"created_at": now.isoformat()},
]
for change in changes:
candidate = star | change
module.LEDGER.write_text(f"repo\tdate\treason\n{candidate['full_name']}\t2026-10-16\tparked\n")
assert candidate["full_name"] not in run(stars=[candidate]).out