From 7c9709802dbe3d4112351785756b77c585567154 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Mon, 6 Jul 2026 19:33:48 +0200 Subject: [PATCH] job4: test memory-commit masked failure (red) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New T8 in lib/tests/run-deterministic.sh: pre-commit hook that always rejects (exit 1), then attempts a memory-commit. Demonstrates J4-04 (UNTESTABLE, consequence CRITICAL) against the CURRENT code, on purpose — this commit is RED: rc=0 (expected 5), stdout leaks the stale (unchanged) HEAD hash instead of staying empty. `set -uo pipefail` (no -e) means a rejected `git commit` doesn't stop the function — it falls through to `git rev-parse --short HEAD`, which prints the PREVIOUS HEAD and succeeds, so the caller sees what looks like a valid hash for a commit that never happened. HEAD itself is correctly unmoved (git did block it) — only the reporting is masked. This intentionally reds `make test` (memory-commit.sh not yet fixed). Next commit fixes it. --- lib/tests/run-deterministic.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/tests/run-deterministic.sh b/lib/tests/run-deterministic.sh index 1a5a78a..1d1f9ff 100755 --- a/lib/tests/run-deterministic.sh +++ b/lib/tests/run-deterministic.sh @@ -141,6 +141,20 @@ if [ "$after1" -eq "$((base + 1))" ] && [ -n "$h1" ]; then ok "run1 created exac if [ "$after2" -eq "$after1" ] && [ -z "$h2" ]; then ok "run2 is a no-op (no 2nd commit, empty stdout)"; else ko "run2 was not a no-op"; fi rm -rf "$R" +echo "T8 — pre-commit hook REJECTS commit → fail LOUD (exit 5), no stale hash, HEAD unmoved" +R="$(new_repo)" +printf '#!/bin/sh\nexit 1\n' >"$R/.git/hooks/pre-commit"; chmod +x "$R/.git/hooks/pre-commit" +BEFORE="$(git -C "$R" rev-parse --short HEAD)" +printf 'REJECTED CHANGE\n' >>"$R/.claude/memory/decisions.md" +OUT="$( (cd "$R" && "$HELPER" commit "chore(memory): T8 rejected") 2>/dev/null )" +RC=$? +AFTER="$(git -C "$R" rev-parse --short HEAD)" +printf ' rc=%s out=[%s] before=[%s] after=[%s]\n' "$RC" "$OUT" "$BEFORE" "$AFTER" +if [ "$RC" -eq 5 ]; then ok "rejected commit → exit 5 (fail-loud)"; else ko "expected 5, got $RC (rc0+stale-hash = masked failure)"; fi +if [ -z "$OUT" ]; then ok "stdout empty on rejection (no stale hash)"; else ko "stdout leaked a hash on rejection: [$OUT]"; fi +if [ "$BEFORE" = "$AFTER" ]; then ok "HEAD unmoved"; else ko "HEAD moved despite rejection"; fi +rm -rf "$R" + echo printf 'RESULT: %d passed, %d failed\n' "$PASS" "$FAIL" [ "$FAIL" -eq 0 ]