Skip to content

feat(ci): ratchet ci-pipeline adoption behind a shrink-only ledger - #948

Merged
hyperpolymath merged 2 commits into
mainfrom
feat/pipeline-adoption-ratchet
Sep 22, 2026
Merged

hyperpolymath merged 2 commits into
mainfrom
feat/pipeline-adoption-ratchet

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

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 main and callers can pin a main SHA.

Where 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.

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 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, 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 statusCheckRollup on each default branch — "does this
repository'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:

Case rc denominator
ledger MISSING 1 (fails closed) pipeline debt: 0 repos
ledger comments-only 1 pipeline debt: 0 repos
slug IS ledgered 0 pipeline debt: 3 repos
slug NOT ledgered 1 pipeline debt: 2 repos
substring near-miss 1 pipeline debt: 2 repos

The substring case is the one that would have made this a rubber stamp quietly:
hyperpolymath/standards-extra and prefix-hyperpolymath/standards must not
exempt hyperpolymath/standards. grep -Fxq, not grep -q.

Ratchet teeth, against the real ledger at origin/main:

Case rc report
empty, no growth 0 (skipped — both counts 0)
grow by one, no trailer 1 GREW …/pipeline-allow.txt: 0 -> 1
same growth, matching trailer 0 OK (declared) … [Ratchet-exception present]
trailer naming a different ledger 1 GREW …/pipeline-allow.txt: 0 -> 1

scripts/tests/exemption-ratchet-test.sh: 14 passed, 0 failed with the new
ledger 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

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
@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 2c668f5f-3f53-4fe6-9e7e-b2db842d7f3a

📥 Commits

Reviewing files that changed from the base of the PR and between 7b05a32 and a5b8334.

📒 Files selected for processing (3)
  • .github/workflows/ci-pipeline.yml
  • .machine_readable/pipeline-allow.txt
  • scripts/check-exemption-ratchet.sh
 ____________________________________________________________
< In God we trust. All others must go through a code review. >
 ------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • 🔴 Error generating docstrings - (🔄 Check to retry)

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hyperpolymath
hyperpolymath merged commit e977cc6 into main Sep 22, 2026
34 of 38 checks passed
@hyperpolymath
hyperpolymath deleted the feat/pipeline-adoption-ratchet branch September 22, 2026 09:59
@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

❌ Failed to create Coding Agent finishing-touch task. Please try again.

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

❌ Failed to create Coding Agent finishing-touch task. Please try again.

hyperpolymath added a commit that referenced this pull request Sep 22, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant