From 70d47957c6991e8c41c3c96779ef10de6107d249 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Mon, 6 Jul 2026 18:59:44 +0200 Subject: [PATCH] job4: SPEC-04 hook-exemption-matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- lib/gitflow-test.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/gitflow-test.sh b/lib/gitflow-test.sh index 3a7cf90..87b0645 100644 --- a/lib/gitflow-test.sh +++ b/lib/gitflow-test.sh @@ -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 ]