fix(B4): the roadmap remedy had zero callers, and the hook that would run it exits 2 on its own line 4 - #3048
Open
noahgift wants to merge 3 commits into
Open
fix(B4): the roadmap remedy had zero callers, and the hook that would run it exits 2 on its own line 4#3048noahgift wants to merge 3 commits into
noahgift wants to merge 3 commits into
Conversation
…run it exits 2 on its own line 4 (#3047) `guard-tree` is red on PP-066 #3025 — instance A's only MERGEABLE open PR — for one entry: $ bash scripts/check_roadmap_diff_additive.sh $(git merge-base origin/main pr3025) pr3025 VIOLATION reserialised: id=PMAT-1077 (bytes differ, no field actually changed) roadmap-diff: base=818 head=824 added=6 lifecycle=36 reserialised=1 deleted=0 `pmat work add` re-serialises the whole roadmap on every call. This repo already knows that, already detects it, and already ships the remedy — and the remedy had ZERO call sites: every `git grep roadmap_trim` hit was prose (a contract field, a receipt, this guard's own RED footer). Detection lived only in CI, so the loop was commit → push → wait 11 min for guard-tree → read a footer → run trim → push again. The hook that would have closed that loop could not run at all. `.githooks/pre-commit` was `#!/bin/sh` with `set -euo pipefail`; /bin/sh is dash here, so following the install line in its own header refused EVERY commit before a single check ran: $ /bin/sh .githooks/pre-commit ; echo $? .githooks/pre-commit: 4: set: Illegal option -o pipefail 2 An unpassable gate is not a gate. It is why core.hooksPath on this box points somewhere else entirely. WHAT CHANGED - `check_roadmap_diff_additive.sh --staged` judges the INDEX. `git write-tree` makes the staged content a real tree object and roadmap_diff.py already resolves any ref via `git show <ref>:<file>`, so this is the identical rule set CI applies, run against exactly the bytes about to be committed. One implementation, two entry points — a second copy of the rule list is how bashrs#266 happened. - The base is plain merge-base(origin/main, HEAD), NOT resolve_base, and the reason is written at the divergence: resolve_base's push-shape branch exists because a COMMIT judged against itself passes vacuously, which cannot arise when the head is the index tree. On a branch freshly cut from main it would return the tip's PARENT and attribute main's own entries to this commit. - `.githooks/pre-commit`: bash shebang, and the roadmap guard runs FIRST (cheapest check, costliest miss). rc 2 = "this box cannot judge" warns and does not block, because guard-tree still refuses — nothing goes silently green. FALSIFIED, NOT ASSERTED Rows 19-22 of the case table, both polarities, hermetic (the scratch repos set core.hooksPath=/dev/null so the table judges this guard, not the developer's hook install). 22/22. Two mutations, each caught: M1 base <- resolve_base → row 22 rc=0→1 (main's re-serialisation misattributed), rows 19,20 rc→2. SELF-TEST FAILED. M2 `run_check ... || true` → row 20 rc=1→0 (the gate cannot fail). SELF-TEST FAILED. Restored: 22/22 rows. Row 22 is the discriminating one: it asserts the PASS *and* that the printed base is the origin/main tip, so it cannot pass for the wrong reason. Closes #3047. Refs PMAT-980, G-6, #2874, #3025. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EeUX4ymyt8XhjXbtTuNH8L
|
§13.11 rung 1 — quorum shadow verdict Shadow mode: this records a verdict and merges nothing. A refusal |
This was referenced Sep 8, 2026
…dogfood `pmat work add` was not used, for the two reasons this ticket exists: 1. It allocates from the roadmap it can see. Max across origin/main and every open pp-066 head is PMAT-1079, and three instances are minting concurrently, so an auto-mint races. 1090 leaves 1080-1089 as headroom. A gap is opaque; a collision is data loss (and 12 duplicate ids on stale heads is what turned `ci / security` red on #3031 and #3001). 2. It re-serialises the whole file — the defect this ticket fixes. The new `--staged` mode judged its own commit, which is the point: $ bash scripts/check_roadmap_diff_additive.sh --staged === roadmap.yaml STAGED diff is additive: base=c04eda87d (merge-base(origin/main, HEAD)) head=8e4ceee37 (index tree) === roadmap-diff: base=818 head=819 added=1 lifecycle=0 reserialised=0 deleted=0 PASS $ git diff --cached --stat docs/roadmaps/roadmap.yaml | 19 +++++++++++++++++++ added=1, reserialised=0, 0 deletions. That is the diff the rule asks for, and `pmat work add` cannot produce it — which is why the remedy script exists and why nothing calling it made the rule unenforceable in practice. Refs #3047, PMAT-1090. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EeUX4ymyt8XhjXbtTuNH8L
Contributor
Author
|
quorum-review (AD-04): three PASS — agreed {
"ticket": "PMAT-1090",
"head": "6b90c14437ad98588b1fa3a9c5e79eb93ef94da4",
"width": 3,
"executor": "agy",
"agreed": true,
"lanes": [
{
"lane": 1,
"verdict": "PASS",
"findings": 0
},
{
"lane": 2,
"verdict": "PASS",
"findings": 1
},
{
"lane": 3,
"verdict": "PASS",
"findings": 0
}
]
} |
Three independent agy lanes judged the diff against PMAT-1090 and the receipt. AGREED: lane 1=PASS, lane 2=PASS, lane 3=PASS. No lane raised a finding with cited or measured grounding; the single asserted finding restates the ticket. Refs #3047, PMAT-1090. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EeUX4ymyt8XhjXbtTuNH8L
noahgift
enabled auto-merge
September 8, 2026 12:15
github-merge-queue
Bot
removed this pull request from the merge queue due to no response for status checks
Sep 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ticket — #3047 (
bse-001,needs-pmat-id— BSE mints no pmat ids). Refs PMAT-980, G-6, #2874.Claim — B4 (roadmap churn). Two defects, both "a result nothing measured": the repo's
own remedy had zero callers, and the hook that would call it could not run.
Measured before (basis=)
guard-treeis red on #3025, instance A's only MERGEABLE open PP-066 PR, for one entry:→
guard-tree failed: failure→gateexit 1 (run 34200927021, jobs 101979274909 / 101989986640).#!/bin/sh+set -euo pipefail. Following the install line in the hook's own headerrefused every commit before
cargo fmt— an unpassable gate, which is whycore.hooksPathon this box points at a hand-installed chain instead.Change
check_roadmap_diff_additive.sh --stagedjudges the INDEX.git write-treemakes thestaged content a real tree object;
roadmap_diff.pyalready resolves any ref viagit show <ref>:<file>. So this is the identical rule set CI applies, against exactly thebytes about to be committed — one implementation, two entry points. A second copy of the rule
list here is precisely how bashrs#266 happened.
merge-base(origin/main, HEAD), notresolve_base, and the reason iswritten at the divergence: resolve_base's push-shape branch exists because a commit judged
against itself passes vacuously, which cannot arise when the head is the index tree. On a
branch freshly cut from main it returns the tip's PARENT and attributes main's own entries to
this commit.
.githooks/pre-commit: bash shebang; roadmap guard first (cheapest check, costliest miss).rc 2 = "this box cannot judge" → warn, do not block:
guard-treestill refuses, so nothinggoes silently green.
Self-test (both polarities)
bash scripts/check_roadmap_diff_additive.sh --self-test→ 22/22 rows (was 18).Rows 19–22 are
--staged, run in a scratch repo carrying the real scripts in a real layout —REPO_ROOTcomes fromBASH_SOURCE, so the guard under test is genuinely this file runningsomewhere it was not born, not a seam that exists only for tests. The scratch repos set
core.hooksPath=/dev/null: the case table judges this guard, not the developer's hook install.added=1roadmap_trim.pyMutation (RED → GREEN)
Run locally; both mutations are caught, and by the row designed to catch them.
resolve_base HEADSELF-TEST FAILEDrun_check … || true(the gate cannot fail)SELF-TEST FAILEDRow 22 asserts the PASS and the printed base sha, so it cannot pass for the wrong reason.
Queries at the change site
The
git grep roadmap_trimcensus and the dash probe are both quoted in the file headers theyjustify —
check_roadmap_diff_additive.sh's usage block and.githooks/pre-commit's shebangcomment — so the next reader who is tempted to "simplify" either one meets the measurement first.
Predicted after
A
pmat work addre-serialisation is refused atgit commit, naming the one-command remedy,instead of surfacing as a red required check ~11 min after push. Six of eight open
pp-066PRscarry roadmap mints and queue up behind this same wall as they rebase.
Writes
scripts/check_roadmap_diff_additive.sh,.githooks/pre-commit. Noroadmap.yaml, noworkflow, no
crates/, no PP-066 row file. Two files.Known, and deliberately not fixed here
core.hooksPathon this box is/home/noah/src/aprender/.git/hooks-apr, an apr-agent chain thatexecs the pmat-managed
$GIT_DIR/hooks/pre-commitand never reads.githooks/. So this hookis correct in the repo and inert on this machine until
git config core.hooksPath .githooks— which is the same defect class one layer up, and is why the audit doc records it as measured
rather than as done. Changing a developer's local hooks install is not a change to make
unilaterally inside a PR;
guard-treeremains the authority either way.Closes #3047.
🤖 Generated with Claude Code
https://claude.ai/code/session_01EeUX4ymyt8XhjXbtTuNH8L