job4: SPEC-04 hook-exemption-matrix

New T14 block in lib/gitflow-test.sh (+3 assertions, 80→83), direct
.githooks/pre-commit invocation (T10-style): T14a mixed code+.claude
staged together on main → BLOCKED (whitelist must not let code ride
along .claude/). T14b MERGE_HEAD present + code staged on main →
exit 0 (conflict-resolution commit exemption, gitflow.sh:222). T14c
hook installed+activated BEFORE the first commit (gitflow_install_hook,
not gitflow_init's deferred activation) → root commit still succeeds
(gitflow.sh:221). Closes J4-05 (WEAK): these 3 exemption paths were
untested — a whitelist regression, or the root/merge exemptions
breaking, would have been silent.

Mutations (scratch copy, applied via Bash/sed — not Edit/Write, avoids
tripping config-protection's path-suffix guard on lib/gitflow.sh for a
throwaway file that's never committed), one at a time, each reverted
before the next:
- T14c: deleted the root-commit guard (gitflow.sh:221,
  `git rev-parse --verify -q HEAD ... || exit 0`) → T14c reds alone.
- T14b: deleted the MERGE_HEAD guard (gitflow.sh:222) → T14b reds alone.
- T14a: report's candidate mutation ("remove grep -v '^\.claude/'")
  self-corrects (still blocks mixed, via the inverted over-blocking
  direction — doesn't red). Used the pinned alternative instead:
  `head -1` → `head -0` in the whitelist check (gitflow.sh:230),
  neutering the non-empty test so every protected-branch commit is
  wrongly allowed. T14a reds, plus (expected, same root cause) the
  pre-existing T3 "block direct code on main" and T10 DRIFT(main)/
  DRIFT(develop) also red — consistent with a whitelist regression
  of this shape being a broad, not narrow, break.
GREEN: real repo unmutated, 83/83 passed (T14a/b/c included).
This commit is contained in:
Bastien Chanot
2026-07-06 18:59:44 +02:00
parent 55fad4b7e9
commit 70d47957c6
+20
View File
@@ -197,6 +197,26 @@ chk "T13c develop has bugfix commit" 'git log develop --oneline | grep -q "Merge
chk "T13c main untouched" "[ \"\$(git rev-parse main)\" = \"$main_before\" ]"
chk "T13c bugfix branch deleted" '! git rev-parse --verify -q refs/heads/bugfix/bx >/dev/null'
echo "T14 — hook exemption matrix (mixed-block / MERGE_HEAD / root-commit), direct invocation"
newrepo hookmix; echo a>a; hookon; gitflow_init >/dev/null 2>&1
git checkout -q main
echo "console.log(1)" > src.js
mkdir -p .claude/tasks; echo t > .claude/tasks/t.md
git add src.js .claude/tasks/t.md
chk "T14a mixed code+.claude BLOCKED on main" '! git commit -q -m mixed 2>/dev/null'
newrepo mergehead; echo a>a; hookon; gitflow_init >/dev/null 2>&1
git checkout -q main
echo "console.log(1)" > src.js; git add src.js
touch "$(git rev-parse --git-dir)/MERGE_HEAD"
chk "T14b MERGE_HEAD exemption allows commit on main" 'git commit -q -m "resolve conflict" 2>/dev/null'
newrepo root14c
git symbolic-ref HEAD refs/heads/main # name the unborn branch 'main' (protected)
gitflow_install_hook # write + activate BEFORE any commit (unlike newrepo/hookon)
echo x > x.txt; git add x.txt
chk "T14c root commit succeeds hook-active-before-first-commit" 'git commit -q -m root 2>/dev/null'
echo
echo "==== RESULT: $PASS passed, $FAIL failed ===="
[ "$FAIL" -eq 0 ]