Commit 4370066
ci(gates): re-run the commit-time gates in CI, which a replayed commit skips (BACKLOG #1395) (#823)
* ci(gates): re-run the commit-time gates in CI, which a replayed commit skips (BACKLOG #1395)
git does not invoke the pre-commit hook for a commit created by the sequencer, so a rebase or
cherry-pick lands a commit with none of the eleven gates having run and nothing reports it. #1395
prefers a CI-side re-run over a second local hook, because a local hook is advisory by construction
and that row is the proof.
precommit-replay.yml runs `pre-commit run --from-ref --to-ref` over the pull request's diff. It
skips exactly two hooks, and the rule is narrow: skip only where running it on a runner gives a
WRONG answer, not merely an inconvenient one.
ledger-gate its hook entry omits --ci, so the ownership arm would read an allocation
registry living in .git/mefor-coord/ that never reaches a runner. ci.yml
already runs ledger_check.py --ci, the half CI can enforce.
forbidden-content fails closed on a git-ignored token file no runner has. Reproducing
security.yml's fork/secret branching in a NON-REQUIRED leg would either red
every fork pull request or return a structural-only green that reads as a
leak-gate pass. security.yml's REQUIRED job already scans the whole tree.
Three guards, because the leg's failure modes are all silent. The skip list is pinned as a SET: it
grows one convenient entry at a time until the leg runs nothing and still reports green, and
pre-commit does not validate SKIP, so a typo skips nothing while reading as deliberate coverage.
The invocation is asserted over the resolved run block rather than the file, because the header
prose names --from-ref/--to-ref and a file-wide check would survive deleting the step. And the job
is asserted absent from .github/required-contexts.txt.
Also pins the VERSION axis _MIRRORS does not reach. It anchors on the invocation, so it proved the
right tool ran with the right discriminating flag and said nothing about which BUILD ran. gitleaks
(v8.18.4) and actionlint (v1.7.12) were held only by prose comments saying to keep the version in
step, and a comment cannot fail. Anchored on each repo's own download URL so a stray VER=
elsewhere cannot satisfy the comparison.
Deliberately NOT a required context: it builds every pinned hook environment per run. Promoting it
needs branch protection moved first, then required-contexts.txt, then the count in
tests/test_required_contexts.py, and a merge_group trigger before any of that.
Every guard was mutation-tested. Rev drift, SKIP growth, a SKIP typo, the run step losing
--from-ref while the header kept it, a bare `pre-commit run`, and the job acquiring a required
context all go red, and the tree returns to green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(gates): hold the bandit hook rev against the version CI installs (BACKLOG #1395)
The two bandit arms in this file have pinned SCOPE since the hook and CI first drifted apart, and
nothing pinned VERSION. That is the same divergence one level down: `--skip B101,...` means
different findings under different bandit releases, so two halves that agree on every skip and
every exclude can still enforce different standards with nothing saying so.
It is not hypothetical here. The ci-scanners group's own comment records an unpinned 1.9.x upgrade
silently changing `# nosec` parsing and breaking a green branch, which is why that pin is exact.
It matters most on a commit nobody gated. Under #1395 git does not run pre-commit for a commit
created by the sequencer, so after a rebase the CI build is the only bandit that ever looked, and
a developer whose commit passed locally has learned nothing about the version that will judge it.
`pre-commit autoupdate` is the likely author: .pre-commit-config.yaml already warns that a bare run
walks the ruff rev past its cap, and it walks this one too.
Lives here rather than in tests/test_gate_ci_mirror_parity.py deliberately. That file owns the
eight hooks this one does not, and bandit is one of the three it defers here. One hook, one place,
so the two files cannot grow a second silently different definition of the same rule. The sibling
gitleaks and actionlint revs are held there in the same change.
The `==` is asserted rather than borrowed from test_ci_venv_pinning's EXACT_GROUP_PINS: `uv export`
writes a fully pinned lock from a `>=` spec just as readily, so a floor would leave this comparison
holding the rev against a version nobody promised to install, and nothing downstream would reveal
it.
Mutation-tested: moving the hook rev to 1.9.3 against the group's 1.9.4 goes red, and restoring it
returns the file to green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(backlog): record the #1395 CI leg, and correct a claim in the row that was never true
Two changes to the #1395 row.
SHIPPED: the CI-side re-run the row prefers, plus the version axis nothing held. Records which two
hooks the leg skips and why each would give a WRONG answer on a runner rather than merely an
inconvenient one, that the skip list is pinned as a set, that the leg is deliberately not a
required context and what promoting it would cost, and which mirror still covers each skipped hook.
It also records what the leg does NOT buy, so the row is not read as closure of the whole finding.
The leg is late in exactly the way the 2026-09-03 scoring note says, a changed-file set against the
checked-out tree still cannot see content that lives only in an intermediate commit, and the
ownership arm still has no enforcement path on a replayed commit and cannot be given one.
CORRECTION: the row ended a paragraph with "A PARITY TEST FOR THOSE EIGHT IS THE DURABLE FIX AND IS
NOT BUILT", and that was already false when it was written. tests/test_gate_ci_mirror_parity.py
covers exactly those eight and landed in 389168a, the very commit that filed this row. The
scoring note above repeats the claim, so it over-counted the remaining work.
Corrected in place rather than annotated, because a reader who reaches that sentence decides
whether to BUILD that module, and two of them would then exist. A second silently different
definition of one rule is the defect that whole family of tests exists to prevent. The correction
is recorded rather than the sentence quietly deleted, so the next reader knows which half is real
instead of wondering.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: wshallwshall <mefordev@messagefoundry.org>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>1 parent 99887f5 commit 4370066
4 files changed
Lines changed: 497 additions & 2 deletions
File tree
- .github/workflows
- docs
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19745 | 19745 | | |
19746 | 19746 | | |
19747 | 19747 | | |
19748 | | - | |
19749 | | - | |
| 19748 | + | |
| 19749 | + | |
| 19750 | + | |
| 19751 | + | |
| 19752 | + | |
| 19753 | + | |
| 19754 | + | |
| 19755 | + | |
| 19756 | + | |
| 19757 | + | |
| 19758 | + | |
19750 | 19759 | | |
19751 | 19760 | | |
19752 | 19761 | | |
| |||
19761 | 19770 | | |
19762 | 19771 | | |
19763 | 19772 | | |
| 19773 | + | |
| 19774 | + | |
| 19775 | + | |
| 19776 | + | |
| 19777 | + | |
| 19778 | + | |
| 19779 | + | |
| 19780 | + | |
| 19781 | + | |
| 19782 | + | |
| 19783 | + | |
| 19784 | + | |
| 19785 | + | |
| 19786 | + | |
| 19787 | + | |
| 19788 | + | |
| 19789 | + | |
| 19790 | + | |
| 19791 | + | |
| 19792 | + | |
| 19793 | + | |
| 19794 | + | |
| 19795 | + | |
| 19796 | + | |
| 19797 | + | |
| 19798 | + | |
| 19799 | + | |
| 19800 | + | |
| 19801 | + | |
| 19802 | + | |
| 19803 | + | |
| 19804 | + | |
| 19805 | + | |
| 19806 | + | |
| 19807 | + | |
19764 | 19808 | | |
19765 | 19809 | | |
19766 | 19810 | | |
| |||
0 commit comments