feat(ci): ratchet ci-pipeline adoption behind a shrink-only ledger - #948
Conversation
Owner ruling: adopt ci-pipeline.yml estate-wide as a ratchet, "like Deno and the lock gate" — blocking where the pipeline is green, ledgered where it is not. This adds the mechanism only. No repository adopts it in this commit. The gate lives where the verdict already lived: the `report` job's final step, which until now was an unconditional `exit 1`. It now consults a central shrink-only ledger, `.machine_readable/pipeline-allow.txt`, registered in LEDGERS=() in scripts/check-exemption-ratchet.sh. No new gate machinery — the ledger is read exactly the way the Deno gate reads deno-allow.txt. Three properties are load-bearing: 1. The ledger is consulted ONLY on the failure path. A green repository never reads it, never pays for the checkout, and cannot be affected by the ledger being stale, empty or unreachable. This is also why the pinned ref can safely predate the ledger: on the failure path an unreadable ledger and an empty ledger give the SAME verdict — blocked. The gate fails closed for free, and "could not read the ledger" can never read as "is exempt". 2. The denominator is printed on BOTH paths, as `pipeline debt: N repos`. An exemption is never a silent pass, and the exempt step summary says in as many words that the gates really failed and that an exemption is debt someone agreed to carry, not a pass. 3. THE LEDGER IS SEEDED EMPTY, AND DELIBERATELY SO. An entry must be earned by observation — the pipeline ran there and went red. It is explicitly NOT seeded from the estate census, which measured `statusCheckRollup` on each default branch (388 of 447 red). That answers "does this repo's EXISTING CI pass", not "does ci-pipeline.yml pass here", and the two populations are close to independent: a repo with four dead mirror jobs and clean code passes gitleaks, semgrep and the lint gates, while a green-rollup repo can fail the format gate on day one. Seeding 388 slugs from that number would have made the ledger a rubber stamp on its first commit and the denominator it prints a lie. Verdict matrix, measured against the run: body extracted from the parsed YAML, so the test exercises exactly what CI executes: ledger MISSING rc=1 pipeline debt: 0 repos (fails closed) ledger comments-only rc=1 pipeline debt: 0 repos slug IS ledgered rc=0 pipeline debt: 3 repos slug NOT ledgered rc=1 pipeline debt: 2 repos substring near-miss rc=1 pipeline debt: 2 repos (grep -Fxq, not -q) The substring case is the one that would have made this gate a rubber stamp quietly: `hyperpolymath/standards-extra` and `prefix-hyperpolymath/standards` must NOT exempt `hyperpolymath/standards`. scripts/tests/exemption-ratchet-test.sh: 14 passed, 0 failed with the new ledger registered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
|
❌ Failed to create Coding Agent finishing-touch task. Please try again. |
|
❌ Failed to create Coding Agent finishing-touch task. Please try again. |
Round 3. #954 merged as 9c256b6 (with its 3 known failures); this PR carries the two fixes that landed after the merge, rebased onto main+9c256b67 (+#948, +#952): 1. **Standards map integrity** (`www` unmapped since #851, `SECURITY.md` unmapped since the #947 squash, `.well-known` entry stale since #851): replace with `www/` + `SECURITY.md` entries, entry_count 122 -> 123. All 5 assertions pass locally. 2. **Both Hypatia gates**: the real scanner emits `invalid_actions_lock` TWICE (verified by building Hypatia locally and scanning this tree): `workflow_audit` (acked in round 2) and WH004 standalone (`rule_module workflow_hardening`, full path — acked here, 211 -> 212, same #951 root cause). Also verified by local full-fidelity scan (194 findings, token-enabled): the only unacknowledged findings are CI-invisible (git-state dirty-tree + code-scanning-alert echoes that need a live PAT — see #957 for the PAT-refresh tripwire). #948/#952 introduce zero new findings. Local battery: debt run 0 breached, map rc=0, uses-lock clean, registry in sync, both ratchets OK, gate keeps nothing CI-visible. Co-authored-by: hyperpolymath <hyperpolymath@users.noreply.github.com>



What this is
The mechanism for the ruled ci-pipeline.yml adoption ratchet — "like Deno and
the lock gate". No repository adopts the pipeline in this PR. Rollout follows
once this is on
mainand callers can pin amainSHA.Where the gate lives
Where the verdict already lived: the
reportjob's final step, which until now wasan unconditional
exit 1. It now consults a central shrink-only ledger,.machine_readable/pipeline-allow.txt, registered inLEDGERS=()inscripts/check-exemption-ratchet.sh. No new gate machinery — the ledger is readexactly the way the Deno gate reads
deno-allow.txt.A consequence worth stating, because it shaped the design: the ledger is consulted
inside the callee at run time, so the caller workflow is identical in every
repository. There is no per-repo green/red configuration to get wrong, and no
adoption-time split to compute.
Three load-bearing properties
1. The ledger is read ONLY on the failure path. A green repository never reads
it, never pays for the checkout, and cannot be affected by the ledger being stale,
empty or unreachable. This is also why the pinned ref can safely predate the
ledger it reads: on the failure path an unreadable ledger and an empty ledger give
the same verdict — blocked. The gate fails closed for free, and "could not read
the ledger" can never read as "is exempt". The pin and the first entry move in the
same commit.
2. The denominator prints on both paths as
pipeline debt: N repos. The exemptstep summary says in as many words that the gates really failed and that an
exemption is debt someone agreed to carry, not a pass.
3. The ledger is seeded EMPTY, deliberately.
Why it is not seeded from the census
I measured the estate and found 388 of 447 default branches red. It was
tempting to seed those 388 straight into the ledger. That would have been wrong,
and it is worth being explicit about why.
The census measured
statusCheckRollupon each default branch — "does thisrepository's existing CI pass". The ratchet needs "does ci-pipeline.yml
pass here". Those populations are close to independent: a repo with four dead
mirror jobs and clean code passes gitleaks, semgrep and the lint gates; a
green-rollup repo can fail the format gate on day one. Seeding 388 slugs from that
number would have made the ledger a rubber stamp on its first commit, and the
denominator it prints a lie.
An entry must be earned by observation — the pipeline ran there and went red.
The honest seed is a dry-run wave, which is the next step.
Measured
Verdict body extracted from the parsed YAML, so the test exercises exactly what
CI executes:
pipeline debt: 0 repospipeline debt: 0 repospipeline debt: 3 repospipeline debt: 2 repospipeline debt: 2 reposThe substring case is the one that would have made this a rubber stamp quietly:
hyperpolymath/standards-extraandprefix-hyperpolymath/standardsmust notexempt
hyperpolymath/standards.grep -Fxq, notgrep -q.Ratchet teeth, against the real ledger at
origin/main:GREW …/pipeline-allow.txt: 0 -> 1OK (declared) … [Ratchet-exception present]GREW …/pipeline-allow.txt: 0 -> 1scripts/tests/exemption-ratchet-test.sh: 14 passed, 0 failed with the newledger registered.
Not in this PR
Adoption itself. Next: a dry-run wave on a deliberate sample (some green-rollup,
some red-rollup, one docs-only, one per ecosystem) to produce the real seed and
the real split, pinned to this PR's merge SHA.
Related: #932, #933, #934, #935. Decision surface: #787.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR