Summary
A dev session that hits session_timeout_min with a finished, gate-green product in the worktree is routed like one that produced nothing: decide_dev (escalation.py) returns RETRY while max_dev_attempts lasts, then defers. The review side distinguishes these cases through review.on_timeout = "salvage-if-done"; the dev side has no equivalent.
Observed (0.11.1)
Setup: an unattended nightly sweep run with [limits] session_timeout_min = 180, max_dev_attempts = 2 and [scm] isolation = "worktree"; the dev adapter is Claude Code.
One story, two dev attempts, both session-end status timeout after 10800 s.
- Attempt 1 was killed re-running the deterministic gates after a successful review-fix round. Its worktree held a 486-line diff; the snapshot at
refs/attempt-preserve-dirty/<run>-<baseline>-1 built and its changed test files passed.
- Attempt 2 was killed in the same phase with the deterministic gates green.
- Journal:
dev-decision action: retry, reason: dev session timeout, then action: defer.
- The kept unit branch merged to main by hand the next morning after every
[verify] command passed in the worktree.
Proposal
A [dev] on_timeout policy mirroring the review-side modes:
retry (default, current behaviour).
salvage-if-done: on timeout / stalled / over_budget, if the spec frontmatter shows the product finalized (done, or in-review reset forward, as review.on_timeout already tests) and the [verify] commands pass against the worktree, proceed to review instead of rolling back; otherwise retry.
defer: give up on the first timeout-like verdict.
The post-kill reconcile already rescues a finished session whose artifact is on disk (policy.py comment on dev_stall_nudges); this extends it to a session killed during verification before the artifact was written. With salvage-if-done a timeout costs one verify pass instead of the attempt plus a from-scratch retry (#777). Related: #271 (the review-side salvage this mirrors), #764.
Summary
A dev session that hits
session_timeout_minwith a finished, gate-green product in the worktree is routed like one that produced nothing:decide_dev(escalation.py) returns RETRY whilemax_dev_attemptslasts, then defers. The review side distinguishes these cases throughreview.on_timeout = "salvage-if-done"; the dev side has no equivalent.Observed (0.11.1)
Setup: an unattended nightly sweep run with
[limits] session_timeout_min = 180,max_dev_attempts = 2and[scm] isolation = "worktree"; the dev adapter is Claude Code.One story, two dev attempts, both
session-endstatustimeoutafter 10800 s.refs/attempt-preserve-dirty/<run>-<baseline>-1built and its changed test files passed.dev-decision action: retry, reason: dev session timeout, thenaction: defer.[verify] commandpassed in the worktree.Proposal
A
[dev] on_timeoutpolicy mirroring the review-side modes:retry(default, current behaviour).salvage-if-done: ontimeout/stalled/over_budget, if the spec frontmatter shows the product finalized (done, orin-reviewreset forward, asreview.on_timeoutalready tests) and the[verify] commandspass against the worktree, proceed to review instead of rolling back; otherwiseretry.defer: give up on the first timeout-like verdict.The post-kill reconcile already rescues a finished session whose artifact is on disk (policy.py comment on
dev_stall_nudges); this extends it to a session killed during verification before the artifact was written. Withsalvage-if-donea timeout costs one verify pass instead of the attempt plus a from-scratch retry (#777). Related: #271 (the review-side salvage this mirrors), #764.