Skip to content

fix(ci): INV-CI-3 blocked a Dependabot bump and told the reader to run something that cannot fix it (INV-CI-5) - #65

Merged
neosun100 merged 1 commit into
mainfrom
fix/pin-sync-tool
Aug 6, 2026
Merged

fix(ci): INV-CI-3 blocked a Dependabot bump and told the reader to run something that cannot fix it (INV-CI-5)#65
neosun100 merged 1 commit into
mainfrom
fix/pin-sync-tool

Conversation

@neosun100

Copy link
Copy Markdown
Contributor

The defect

INV-CI-3 works — it blocked Dependabot PR #61, which bumped the three github/codeql-action sub-actions to a SHA the _AUTHORITATIVE table did not know.

But it was not actionable. Its failure message said to resolve each SHA against GitHub and that "SENTINEL_VERIFY_ACTION_PINS=1 does this". That was false, measured by reproducing PR #61 in the working tree:

offline layer                          2 failed   (SHA not in the authoritative table)
online layer (VERIFY_ACTION_PINS=1)    PASSED     (it iterates only entries already in the
                                                   table, so a brand-new SHA is never seen)

So the only route left was a human hand-copying a 40-hex SHA and hand-resolving it against the API — precisely the manual work INV-CI-3 exists to eliminate, on Dependabot's weekly cadence. A guard that turns the labour it prevents into a mandatory ceremony has traded one defect for another. ("The guards are code too, so they get invariants" — round 18.)

Fix

scripts/sync_action_pins.py + make sync-action-pins, re-deriving the table and comments from the authoritative direction (SHA → the tag pointing at it). Never comment → table: reading the comment would launder a stale label into the source of truth, which is INV-CI-3's defect (setup-python claimed v6.3.0 while pinned to v7.0.0).

Verified end-to-end against PR #61's exact state:

  • guard fails → one command → guard passes
  • table gained d1ba80a13dd9 = v4.37.5, dropped the superseded entry, 3 comments rewritten
  • on a clean tree: nothing to do for all 12 pins — the negative control proving tool and guard agree
  • it refuses to write when any SHA is unresolvable (verified live: rc=1, explicit UNRESOLVED line, zero writes), because a partial table is worse than a stale one: the guard would pass on the resolved entries while the suspicious pin stayed unrecorded

Two of my own defects, recorded rather than quietly fixed

1. The tool nearly reintroduced the very defect it fixes. Its first tag ranking counted dot-separated components, and actions/deploy-pages has three tags on one commit:

v5.0.0            the release
v5                a moving major alias
v3.0.2-node.24    a historical re-tag under the old major

The count chose v3.0.2-node.24, so the tool proposed rewriting a correct v5.0.0 comment into a misleading one — the guard's own defect class, reintroduced by its remediation. Now ranked semantically, and pinned by a test over the real ambiguous tag set.

2. Three of the new module's assertions initially SURVIVED mutation:

  • "/tags" in <all strings> was satisfied by the resolver's docstring while the code queried /commits — documentation standing in for implementation.
  • "refusing to write" in main() was satisfied by a second refuse-to-write path when the first was deleted.
  • Locating the gate by any(Name == "unresolved") accepted if False and unresolved:a guard must test whether a gate can FIRE, not whether it exists.

All three now resolve structurally (call arguments only, branch-scoped strings, exact-condition match).

Two existing guards fired on the new directory — both real

  • INV-PKG-1: scripts/ had no __init__.py, making it a namespace package that any installed scripts package would silently outrank — the litellm/ accident.
  • INV-PKG-3: the sdist shipped a test reading scripts/ but not scripts/ itself, so a downstream packager's bundled-suite run would hit FileNotFoundError.

Fixed by adding __init__.py and registering scripts/ as deliberately-absent alongside .github/ (maintainer tooling is not source), with the guard made repository-scoped via repo_infra.require_git_checkout so it skips honestly in an sdist. INV-MAKE-1 also required the new target in KEY_TARGETS and .PHONY — added.

Verification

  • New guard: 8 passed, 1 skipped. Mutation-tested 10/10, including all three former survivors and the original defect (replacing every tool pointer with the false flag).
  • Full suite: 4083 passed / 9 skipped. ruff clean (the new scripts/ tree is linted, not exempt).

Once this merges, make sync-action-pins is the supported fix for PR #61 and every future Action bump.

…n something that cannot fix it (INV-CI-5)

INV-CI-3 works — it blocked Dependabot PR #61, which bumped the three
github/codeql-action sub-actions to a SHA the _AUTHORITATIVE table did not know.
But it was not ACTIONABLE. Its message said to resolve each SHA against GitHub
and that "SENTINEL_VERIFY_ACTION_PINS=1 does this". That was false, measured by
reproducing PR #61 in the working tree:

  offline layer                        2 failed   (SHA not in the table)
  online layer (VERIFY_ACTION_PINS=1)  PASSED     (iterates only entries already
                                                   in the table, so a new SHA is
                                                   never seen)

So the only route left was a human hand-copying a 40-hex SHA and hand-resolving
it against the API — the manual work INV-CI-3 exists to eliminate, on
Dependabot's weekly cadence. A guard that turns the labour it prevents into a
mandatory ceremony has traded one defect for another.

Fix: scripts/sync_action_pins.py + `make sync-action-pins`, re-deriving the table
and comments from the AUTHORITATIVE direction (SHA -> the tag pointing at it,
never comment -> table: reading the comment would launder a stale label into the
source of truth, which IS INV-CI-3's defect).

Verified end-to-end against PR #61's exact state: guard fails -> one command ->
guard passes; table gained d1ba80a13dd9 = v4.37.5, dropped the superseded entry,
3 comments rewritten. On a clean tree it reports `nothing to do` for all 12 pins
— the negative control proving tool and guard agree. It REFUSES to write when any
SHA is unresolvable (verified live: rc=1, explicit UNRESOLVED line, zero writes),
because a partial table is worse than a stale one.

Two of my own defects are recorded rather than quietly fixed:

1. The tool's first tag ranking counted dot-separated components, and
   actions/deploy-pages has three tags on one commit: v5.0.0, the moving alias
   v5, and the historical re-tag v3.0.2-node.24. The count chose
   v3.0.2-node.24, so the tool proposed rewriting a CORRECT comment into a
   misleading one — the guard's own defect class, reintroduced by its
   remediation. Now ranked semantically and pinned by a test over the real
   ambiguous tag set.

2. Three of the new module's assertions initially SURVIVED mutation:
   - `"/tags" in <all strings>` was satisfied by the resolver's DOCSTRING while
     the code queried /commits — documentation standing in for implementation.
   - `"refusing to write" in main()` was satisfied by a SECOND refuse-to-write
     path when the first was deleted.
   - Locating the gate by `any(Name == "unresolved")` accepted
     `if False and unresolved:` — a guard must test whether a gate can FIRE, not
     whether it exists.
   All three now resolve structurally (call arguments only, branch-scoped
   strings, exact-condition match). Mutation-tested 10/10.

Two existing packaging guards fired on the new scripts/ directory and both were
real: INV-PKG-1 (no __init__.py -> a namespace package an installed `scripts`
would outrank, the litellm/ accident) and INV-PKG-3 (the sdist shipped a test
reading scripts/ but not scripts/). Fixed by adding __init__.py and registering
scripts/ as deliberately-absent alongside .github/ — maintainer tooling is not
source — with the guard made repository-scoped via require_git_checkout.

Suite: 4083 passed / 9 skipped. ruff clean.
@neosun100
neosun100 merged commit 446a890 into main Aug 6, 2026
13 checks passed
@neosun100
neosun100 deleted the fix/pin-sync-tool branch August 6, 2026 14:07
neosun100 added a commit that referenced this pull request Aug 6, 2026
#66)

Takes Dependabot PR #61's SHA bump for the three github/codeql-action
sub-actions, with the authoritative table and the version comments re-derived by
`scripts/sync_action_pins.py --write` (INV-CI-5) rather than hand-edited:

  _AUTHORITATIVE  + d1ba80a13dd9 = v4.37.5
                  - f205ea1c3313   (was v4.37.4, no longer pinned)
  comments        codeql.yml:55, codeql.yml:68, scorecard.yml:65
                  v4.37.4 -> v4.37.5

This is the first real use of the tool added in #65, and the reason it exists:
PR #61 could not be merged as-authored because Dependabot cannot update the
table, and the guard's only advice was a flag that does not regenerate it.

Verified: guard failed on PR #61's tree, one command fixed it, guard passes.
Suite 4083 passed / 9 skipped. Zero unrelated files touched.
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