ADR-070: The Stop hook holds only the run's owning session, and only while it makes progress
Status: Accepted (2026-10-28)
Amends: ADR-028: Unattended delivery without branch protection (decision 3: “or after 8 blocked stops”), spec 088 (AC-088.18: the environment cause of a stuck-run escalation); spec 126
Amended by: ADR-075: An unattended ship or build runs as one session per unit, each saved at a checkpoint (unit sessions)
Context
Section titled “Context”The Stop hook of ADR-028 kept every session in a checkout with an unfinished ship run going, and let go only after 8 blocked stops,
recording the cause environment. In the week of 2026-10-21, 14 of 32 escalated ships ended this way (10 from the hook, 4 from the
headless “no progress” rule), and escalated ships cost more than delivered ones. Three things were wrong:
- A count of stops says nothing about whether the session is getting anywhere: a stuck model spent six more turns, and a working one could be stopped at the eighth.
- The run state is per checkout, not per session, so a research session or a second session in the same checkout was held on a run it never started (a stale run of an already merged spec).
- The cause
environmentsent the weekly retro to the wrong owner: usually the model could not get further, a code or a decision problem.
Decision
Section titled “Decision”- A run has an owner. The session that starts or resumes a run (
cplat component ship start,cplat system ship start, including the sessionscplat headlessstarts) is recorded in the run state asowner(Claude Code’sCLAUDE_CODE_SESSION_ID, orCPLAT_SESSION_IDthatcplat headlessexports). The latest start or resume wins. - Only the owner is held. The hook compares the Stop input’s
session_idwithowner. Any other session stops silently and changes nothing. A run without an owner (state written before this ADR, or started outside Claude Code) never holds a session; the hook prints one line saying how to resume it. - Progress, not a count, decides. A stop of the owner shows progress when the feature branch has a new commit or the run’s phase
changed since the previous blocked stop (the first stop after a start or resume always does). Progress resets the count; two stops in a
row without progress end the run as an escalation
no progress: …. There is no cap on stops that show progress; the run budget of spec 102 bounds them. - A fixed rule names the cause of a no-progress escalation (from the hook or from
cplat headless): the AC-088.16 rule matching the session’s last message, elsecodewhen tests, criteria or a required PR check are red, elsedecision. Neverenvironmentby default, neverplatform, neverunknown.
Consequences
Section titled “Consequences”- Escalations from a stuck run come after two wasted turns instead of eight, and the retro’s cause counts point at the code or the decision that stopped the run.
- Parallel sessions and worktrees sharing a run state no longer interfere; a run started before this change has to be resumed once
(
/component:ship <spec>) before the hook holds a session for it again. - A session started outside Claude Code 2.1.132 or later (no
CLAUDE_CODE_SESSION_ID) is never held;cplat headlesscovers unattended runs throughCPLAT_SESSION_ID. - Uncommitted edits do not count as progress: a model that edits but never commits escalates after two stops. That is intended, a ship run commits each finished task.
Not included
Section titled “Not included”- Deciding progress or cause with a model; both come from fixed rules.
- Any change to the run budget (spec 102), its equivalent-failure stop (AC-102.9) or the 3-fix-round escalation of
cplat ship.