Three hard gates wired into Claude Code on this Mac, 9 September 2026. Every ask becomes a goal, every question is deduplicated across live chats, every finding is checked with ChatGPT the same day.
completion_stop_hook.py is the one gate on this Mac that decides stopping from
project state rather than from the wording of a reply. It abstains whenever no task contract
is open. Nothing opened a contract on an ordinary ask, so the strongest gate sat idle through
every turn and the weaker prose gates carried the whole load. The fix opens the contract from
the ask itself.
goal_loop_gate.py runs on UserPromptSubmit and writes a
contract from the verbatim prompt. Nobody types /goal.
The enumerated sub-asks become required outcomes, and the Stop gate refuses while any one of them is open.
question_dedup.py reads a shared ledger and the tails of sibling
transcripts before any question goes to ChatGPT.
Five sessions were live when this was built. Without the check he reads one question five times.
findings_chatgpt_gate.py blocks a findings-shaped reply when no
ChatGPT check was recorded today by this session.
Work narration passes untouched. A claim about what is true does not.
The package default is one shared contract file for the whole Mac. Auto-opening there would have each live session overwriting the others.
Contracts now live at no_quit_state/contracts/<session8>.json,
with the shared file kept as the fallback.
A prompt sharing under 0.15 content words with the open instruction closes the old contract as superseded and opens a fresh one.
History is preserved in scope_contract_history.jsonl. Nothing is deleted.
| Piece | Path | Hook point |
|---|---|---|
| Goal opener | ~/.claude/hooks/goal_loop_gate.py | UserPromptSubmit |
| Question dedup + ledger | ~/.claude/skills/_shared/question_dedup.py | called by hand and by the findings gate |
| Findings check | ~/.claude/hooks/findings_chatgpt_gate.py | Stop, ahead of completion_stop_hook |
| Per-session contracts | ~/.claude/no_quit_state/contracts/ | read by completion_stop_hook |
| Shared ledger | ~/.claude/no_quit_state/chatgpt_question_ledger.jsonl | questions and findings checks |
Seven behaviours were exercised against throwaway sessions and fixtures. This is a record of what was tried, and it is not a claim of comprehensive validation: a full confusion set of known-duplicate and known-different question pairs has not been run, so the thresholds below are provisional.
$ completion_stop_hook.py <- reply claiming "all done" completion_controller BLOCK: the requested scope is not complete. contract sc-6a5c847f11 stop_rule: if Claude stops, the requested scope must be complete REFUSED: 3 in-scope item(s) remain actionable REFUSED: 2 required outcome(s) still open: R1, R2 $ scope_controller.py meet R1 --evidence ev_test_r1 <- fake evidence REFUSED: 2 required outcome(s) closed on evidence that does not verify: grade every bass loop in the export fold -> UNSUPPORTED $ scope_controller.py meet R1 <- from a session that does not own it REFUSED: contract sc-6a5c847f11 belongs to session testfake, this caller is 'e6534be4'. Open your own contract. $ question_dedup.py check "Before I upload revision 7, should the snare-lane ghost notes on Watermelon In Easter Hay get restored?" VERDICT: DUPLICATE (a sibling session is already asking this) session e6534be4 window 240m candidates scanned 52 MATCH 0.64 session 034bbadd [ledger] do NOT send. Read the sibling's answer. $ findings_chatgpt_gate.py <- findings reply, no check today findings_chatgpt_gate BLOCK: this reply reports findings and no ChatGPT check was recorded today for this session. markers matched: i found, found that, root cause, verdict, audit, discrepanc $ findings_chatgpt_gate.py <- pure work narration exit 0 (passes untouched)
These findings went to ChatGPT before this page shipped, which is the third gate applied to itself. The review returned four corrections and each one is implemented here.
| Correction | What changed |
|---|---|
| Word overlap is the wrong authority for findings coverage. Recurring words such as gate, session, contract and hook let a broad early check bless later findings. | Coverage is transactional: a check id bound to a fingerprint of the payload, plus
an evidence watermark. Similarity survives only behind
--allow-similarity for a check made by hand. |
| Any new measurement after a check should invalidate it. | The watermark records the evidence count and newest mtime at check time. New evidence moves it, and coverage reads STALE. |
| A duplicate needs a shared subject anchor, not just a score. | A duplicate must share a file, song, id, url, route or proper name when both questions carry any. Same words on a different song now clears. |
| The 500-character and two-marker rule was weaker than the stated guarantee. | The floor dropped to 220 characters, and numeric claims count as their own evidence of a report. Marker matching is a detector heuristic now. |
queue_state.py compared the contract's offset-aware timestamp against
naive queue timestamps and raised TypeError. The caller reported that as
“completion state could not be evaluated” and fail-closed. Every turn would
have blocked for the wrong reason once contracts opened automatically. Both sides now
normalise through _aware(). The pre-fix file is kept as
queue_state.py.PRE_TZFIX_2026-09-09.bak.
A background-task completion notice reached goal_loop_gate.py through the
same stdin channel as a typed turn. It scored 0.00 content overlap against the open
instruction, the pivot rule read that as a change of subject, and it closed a contract
carrying ten required outcomes and opened an empty one named after the notification text.
The gate built to prevent premature stopping discarded the record of what completion meant.
A MACHINE_TEXT guard now makes notifications, system reminders, command
stdout and hook output open nothing and supersede nothing. The same shape as the standing
rule that a peer session's message is never Brandon's consent: arriving on the user's
channel does not make something the user.
Recovery worked because nothing is deleted. scope_contract_history.jsonl
keeps a snapshot on every save, so the contract was rebuilt from its richest snapshot with
closed_at cleared and a restore_reason recorded. Eleven outcomes
now carry evidence ids.
The hook is global, so every session on this Mac picked it up on its next prompt. Five sibling sessions were writing transcripts within the same three minutes, and per-session contracts appeared for four of them without any further action.