Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hooks: add ContinuationGate to auto-continue unfinished work
What this is
Claude Code ends every turn whether or not the work is done. Measured on my install: across 1,605 human turns over 30 days, 12.1% were a bare "go" — 91% of them after a reply that had asked me nothing. Median dead time before each: 357s. LifeOS already knows what "done" means (ISC criteria); this adds the converse: when the declared work is demonstrably not done, hand the run one more turn instead of handing it back.
ContinuationGateis a Stop gate, registered last inStopGates— every stop-reason outranks the one continue-reason. It continues only when ALL hold, and any failure lands on hand-back (today's behavior):MEMORY/OBSERVABILITY/continuation-gate.jsonl, never acted on, and no model is ever called while unarmed. Three arming surfaces: say "auto-continue for 2 hours" in a prompt (deterministic UserPromptSubmit parser → session-scoped expiring grant, 12h/50-continue clamps, "auto-continue off" revokes; mention or deliberation never arms),bun LIFEOS/TOOLS/ContinuationDoctor.ts --arm Nfor a standing cap, or per-ISAautocontinue: Nfrontmatter. Separate keys (isa/all/grant) so nothing arms by accident through one knob. A grant buys unattended TIME, never unattended judgement — questions, tool errors, and no-work turns still hand back.AskUserQuestion(a truncated transcript scan that can't find the turn boundary reads as "asked"), no question in prose (conservative regex; a false positive costs one hand-back).TxEventexceptuser-textis tool-derived; anyisErrorhands back.TOOLS/Inference.ts; only an explicit, well-formed{"finished": false}continues — crash, timeout, prose, hedge, wrong shape, extra keys, or an ambiguous double-verdict all hand back.CONTINUATIONGATE_OFF=1.Loop safety (the part that matters)
stop_hook_active— the counter is the loop breaker instead. The cap is enforced inside an exclusive-create lockfile critical section (commitContinue: re-read, re-check freshness + cap, write, read back). A contended lock, an unpersisted write, or a present-but-corrupt state file each refuse the continue.O_EXCL.Includes: gate-chain arbitration fix
StopGates' old inline reducer kept the FIRST object any gate returned, so a passivesystemMessagefrom an early gate silently swallowed adecision:"block"from any gate below it. Extracted tolib/gate-chain.ts(tested): first block wins and short-circuits; a block from any gate outranks earlier non-block output. Compatibility: existing gates emit blocks or nothing on the block path, so the only behavior change is the previously-dropped case.Deliberate tradeoffs (from three adversarial cross-model review passes)
finished:falsebuys at most cap-bounded extra turns of the same session with no new privileges. An echoed exact{finished,why}object in judge output is narrowed by the ambiguity refusal (two"finished"mentions = no answer) but not fully closed — accepted, cap-bounded.origin.kind === "human", tool-name scan) matches whatlib/transcript-evidence.tsalready relies on; if the format drifts, the counter stops resetting and continuation stops sooner, not later.Evidence (author install, 18 days)
1,341 Stop verdicts: 117 continuations (~105/week armed ≈ 5–10h of dead waiting eliminated weekly at the measured 357s median); judge approved only 50 of 459 consultations (11%) — not a rubber stamp; zero runaways (max streak 3, wall-clock ceiling caught 10, zero counter failures). Reproducible on any install from
continuation-gate.jsonl— shadow mode populates it without acting.Tests
121 pass (bun test): verdict-parser strictness (12 refusal shapes + extra-key + ambiguity refusals), judge failure modes (throw/timeout/garbage/child-deadline), directive grammar (arming forms, clamps, off forms, 9 mention-is-not-consent cases, voice-request vs deliberation), grant fail-closed reading + budget raising, arm-hook merge/read-back/permissions, cap precedence + clamping for both keys, lock contention/staleness, gate-chain arbitration (block precedence, short-circuit, throw isolation), wiring tripwires (registered, LAST, judge after deterministic gates, cap-under-lock shape, ContinuationArm registered sync at UserPromptSubmit).