Skip to content

ci-pipeline.yml: the ledger pin's "same commit" invariant is unsatisfiable (12/12 merges are squash) #980

Description

@hyperpolymath

Finding

.github/workflows/ci-pipeline.yml pins the exemption ledger checkout at line 884:

          ref: a2ff696a

and the comment block immediately above it (lines 876–879) states the rule that governs it:

The pin and the first entry move in the SAME commit: whoever adds a slug must
bump this ref to the commit that carries it, or their own exemption will not
be read.

This invariant cannot be satisfied. A ref: must name a commit that already exists. The commit that carries the new ledger entry does not exist until it is created, so no single commit can both add a slug and pin itself. The rule as written asks for a SHA to be written into the commit that produces that SHA.

Measured evidence

Under squash merge the landed commit is not even the branch head that was authored, so the target SHA is unknowable in advance. Last 12 merged PRs on this repository, mergeCommit.oid vs headRefOid:

gh pr list --repo hyperpolymath/standards --state merged --limit 12 \
  --json number,mergeCommit,headRefOid
PR merge SHA head SHA equal?
#977 68acee77 f5cde2f6 no
#974 417094b2 48415f6b no
#973 2eb9bb19 529296b5 no
#971 8aea869b 7ff3ae0b no
#970 436a89b3 3f654c6f no
#965 12d76e89 c522733a no
#962 051f98ec 04593c85 no
#961 3a7c90ae d3fc4d61 no
#959 38d211f1 0c261f12 no
#954 9c256b67 e60e3ad6 no
#952 be6c9580 2c602c44 no
#948 e977cc67 d88410af no

12 of 12 differ. Repository settings return allow_merge_commit=true, allow_squash_merge=true, allow_rebase_merge=true, so the strategy is chosen per merge and is not predictable even in principle.

This is a documentation defect, not a security hole

The gate fails closed, exactly as the surrounding comment claims. At a2ff696a the ledger file does not exist; the checkout is continue-on-error: true and the following step blocks when it cannot read the ledger, so "could not read the ledger" and "empty ledger" yield the same verdict. Nobody is wrongly exempted. The cost is a round trip, never a false pass.

The harm is that the rule misdirects whoever first seeds the ledger: following it literally is impossible, and the natural workaround (pin @main) is explicitly and correctly forbidden four lines below.

Proposed cure

Replace the simultaneity requirement with the ordering that actually works, and name the third step, which is the one most likely to be missed:

  1. PR A adds the slug(s) to .machine_readable/pipeline-allow.txt, carrying the column-0 Ratchet-exception: .machine_readable/pipeline-allow.txt — <why> trailer. It lands at some SHA S1.
  2. PR B bumps ref: to S1. It lands at S2.
  3. Between the two, the gate stays blocked — which is the intended conservative state.
  4. ⚠ Callers must then be re-pinned. A caller pinned to ci-pipeline.yml@<sha> older than S2 checks out the ledger at the old ref, so its exemption is not read. Every caller relying on an exemption must move to S2.

Acceptance criteria

  • The comment at ci-pipeline.yml:876–879 no longer asserts that the pin and the entry move in the same commit.
  • It states the two-PR ordering, and states that the gate is blocked in between by design.
  • It states that callers pinned before the ref: bump do not see the exemption and must be re-pinned.
  • The prohibition on @main directly below is retained unchanged.

Surfaced while executing the dry-run wave that seeds this ledger by observation. Decision sheet: #787.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions