Build1 publisher3 min readPublished
Prompting an LLM to self-audit works some fraction of the time, so one developer moved the check into a Stop hook that parses the session transcript and refuses the end of a turn when the audit keywords are missing.
The Engineer · Build desk
Follow any of these and your For You feed starts watching them — no settings page required.
Compiled by The EngineerSomething wrong?How this is made
Exit 2 is the whole of the enforcement. The Stop event is the harness asking whether the turn may end, and a non-zero exit from the hook hands the refusal back to the model inside the session, not in an instruction it has been drifting away from for the last ten hours [2] [10]. That is the part of this design that travels: the check lives where the session ends, so forgetting the prompt or swapping the model does not disarm it [10].
What gets checked is narrower than the framing suggests. The Stop hook parses the transcript JSONL in Python, pulls the last assistant text, and searches it for audit keywords [3]. Keywords present, exit 0 [4]. The gate checks the report the model writes, and stops there. An agent that has learned the vocabulary clears it with a paragraph. Writing that paragraph still forces a pass over the diff, which is worth something, but this is a lexical check and should be sized as one.
The two-hook split is the good engineering here. PostToolUse fires on a file write or edit and touches /tmp/claude-audit-pending-{sid}; the Stop hook looks for that flag and exits silently when it is absent, so conversation-only turns stay quiet [5]. The author's first version prompted on every turn, and a performance review dated 2026-07-11 concluded the audit had become a ritual that was wrecking the actual work [6]. Getting from there to here required a redesign that went well beyond a config tweak.
The trigger set is worth checking closely. The flag rises on Write and Edit events [5]. An agent that patches through Bash with sed -i or git apply changes the tree without ever touching those tools, and the Stop hook has no way to know a change happened.
The unattended-run fix is where the arithmetic gets interesting. Nineteen sdk-cli automation runs fired overnight on 2026-07-12 and stacked 19 audit requests into stderr with nobody there to read them [7]. There is also a per-session cap of two prompts, held in a counter file [9]. So the cap alone would have left a ceiling of 38 messages on that night's runs rather than eliminating them [12]; the entrypoint check is what actually silences it, by reading the first 15 lines of the session and skipping when "entrypoint":"sdk-cli" appears [8].
The premise underneath all of it is that a prompted "always self-audit" is followed some of the time and dilutes as the session grows [1]. The post does not measure that fraction, and it does not report how much broken code the hook has caught [14]. What it documents precisely is two dated regressions in the hook's own behaviour and the fixes that followed [6] [7]. Read it as a working record of making a blocking gate quiet enough to live with in 67 lines of bash [13]; it says little about actual defect rates. For the mechanism to transfer you need a harness that exposes a blocking Stop hook and a tool-use hook, an agent that edits through those tools, and a human in the session to receive the refusal [8].
Ranked by verification strength, evidence, and original report placement.
A Stop hook combined with a PostToolUse hook: the moment the LLM tries to end the session, a shell script reads its output text and blocks with exit 2 if the audit keywords are not there.
The Stop hook self_audit_stop.sh parses the transcript JSONL with Python and extracts the last assistant text.
The Stop hook searches the extracted text for audit keywords; if present it exits 0 and stays silent, if absent it proceeds to block.
The PostToolUse hook audit_flag_set.sh receives the file-written-or-edited event (Write / Edit) and touches /tmp/claude-audit-pending-{sid}; the Stop hook checks for that flag and exits 0 silently when it is absent.
The first version fired an audit request on every single turn; a performance review on 2026-07-11 concluded that "the audit has become a ritual and is wrecking the actual work", and the design was changed to fire only on turns where something changed.
On 2026-07-12, 19 sdk-cli automation runs fired overnight and stacked up 19 audit requests in stderr, with no human present to read them.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Code you can read, on one machine
The checkable parts of this are checkable because they are quoted: the flag path and the exit codes, plus a diagram of the order the guards run in. The two dated notes are specific enough that whoever kept the logs could falsify them. Against that, nobody outside the author's setup has examined any of it, and the text we have stops inside the sdk-cli detection block, so the closing lines of the script are summarised rather than shown.
One author, two dated revisions
Use begins and ends with the person writing. What exists is a history: the every-turn version abandoned on 2026-07-11 and the entrypoint skip added after 19 overnight runs on 2026-07-12, both of which show the hooks running against real sessions rather than sitting in a gist. No second user, team, repository or install count appears.
Mechanism shown, effect asserted
The load carried by the phrase "some fraction of the time" is enormous and it never gets a number, nor does the payoff side: not one defect is described as caught by the gate that prompting had let through. The mechanical claims are modest and demonstrated. The language wrapped around them, an audit becoming a physical precondition and trust guaranteed by structure, promises a result the post does not attempt to measure.
Personal credibility, nothing to sell
There is no vendor, sponsor or product behind this; the author is describing a configuration on their own disk on a community publishing platform. The one visible interest is reputational, and it is stated openly in the first paragraph, where student earnings against ¥1.2M a month in revenue buy authority for the delegation analogy that frames everything after it.
The scripts check out; the conclusion is shakier
How these hooks behave can be established with fair certainty from what is quoted, and the two incidents are dated and concrete. Whether forcing the audit improves what the agent ships rests on one developer's reading of their own workflow, so our assessment sits mid-scale and would move on either a second implementer's account or any before-and-after count.
build
The entrypoint field in a Claude Code transcript tells a Stop hook if anyone is watching1 publisher
build
A subshell in the log line zeroed $? before the Claude Code hook could return it1 publisher
build
A cost monitor overcounted 4.9x, then went dark for a week when set -e did its job1 publisher
build
A broad deny in Claude Code outranks the narrow allow meant to except it1 publisher
Publishers with included, body-backed reporting in this cluster.
1 article · September 8, 2026