In this harness version only `Stop` hooks emit a structured transcript record.
`PreToolUse` and `PostToolUse` hooks leave no durable execution trace — which is
every guard this repo relies on. #773 §5.4 could therefore only count filename
mentions in settings dumps, i.e. inference, so a silently broken hook looked
identical to a working one indefinitely.
Each hook now sources one shared sink, `scripts/hook-fire-log.sh`, and records a
`fire` record on entry and an `exit` record carrying its status and decision. The
decision is parsed from the bytes the hook actually emitted rather than declared
by the author: Claude hooks always exit 0 and decide by printing JSON, so stdout
is diverted and replayed; git hooks decide by exit code and stream their output,
so theirs is left alone. That avoided ~40 edits inside pretooluse-merge-consent
and records what the harness sees rather than what the author meant.
The instrumentation sits in the stdin/stdout path of the most authoritative
guards in the repo, so transparency is the load-bearing claim, not coverage:
test_instrumentation_changes_NOTHING_the_harness_can_see drives every hook with
and without it over a payload matrix reaching each hook's deciding branch, and
demands byte-equal stdout and equal exit status. It shipped with two defects that
only that shape catches — `out=$(cat f)` stripping a trailing newline, and an
unconditional stdin slurp that hangs on a TTY and would have frozen every
interactive `git commit`. Both are now pinned, the second through a real pty.
fixes#776
Decisions-Edit: yes
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>