Repository navigation
fix(server): settling a parent cancels its delegated child's armed usage-limit resume - #30175
juliusmarminge wants to merge 7 commits into
Conversation
…ttles These tests reproduce #30144: a subagent child armed for usage-limit recovery while its parent was open still yields a limit-resume: command after the parent is settled. The final assertion of the #27951 test encoded the bug; it now expects no resume while the parent is settled, and a resume once the parent is unsettled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ttled An auto-resume armed for a subagent while its parent was open still fired after the parent was settled, and the child's completion reopened the parent. The settled-parent guard in getLimitRecoveryCandidates only covered fresh arming; it now gates the armed-resume branch too. Unsettling the parent makes the armed resume eligible again. Fixes #30144 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ttles Gating the resume branch on the parent only paused it: unsettling the parent later resumed the abandoned subagent at any age. The sweep now disarms such a recovery (autoResume false for the same run and reset, as Cancel auto-resume does) through a plain dispatch, so it stays disarmed after the parent is unsettled. Fresh arming stays blocked in SQL as on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…arent settles The usage-limit sweep can read a subagent's candidates while its parent is open, and the parent can be settled before the saved limit-resume dispatch is decided. The continuation guard checked only the child's own settled state, so the child started and its completion wake reopened the settled parent. The guard now also refuses when the thread is a subagent whose direct parent is settled, taking the existing no-op path; the next sweep then sees parentSettled and disarms the recovery. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous shape split the cancellation across the recovery sweep, the worker and the orchestrator guard, which left two races: a skipped resume kept the same requestId and replayed its receipt forever, and a disarm prepared while the parent was settled could overwrite a newer explicit re-arm. Revert the store and worker to main and do the cancellation in the serialized resume dispatch: when the parent is settled and the recovery still matches the command, the no-op metadata update sets autoResume to false with a fresh requestId. Stale commands keep the existing no-op. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gate Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — Cancels a delegated task’s scheduled usage-limit resume when its parent remains settled, preventing unwanted work from reopening the parent. The change is narrowly scoped and preserves newer recovery choices without changing scheduling defaults. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
… settled at the reset Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dismissing prior approval to re-evaluate 0b048fa
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Problem
A delegated child that fails on a usage limit while its parent is open gets an auto-resume armed for its reset. If the parent is settled before the reset, the armed resume still runs the child, and the child's delegated-completion wake reopens the settled parent and starts a new run on it. https://git.cafe/pingdotgg/t3code/pulls/27951 only stopped new arms for children of settled parents; an already-armed resume still fired. Reproduced on
main(cefb544) with the runtime test below, through the real orchestrator.Change
The usage-limit continuation guard in
Orchestrator.dispatchMessagenow also refuses a resume when the thread is a subagent whose direct parent is settled. The parent row is read only for subagents, only on this path, and inside the child's serialized dispatch, so a parent settled after the sweep read it is covered.When the refused command still matches the thread's armed recovery (same run and request id), the refusal retires it:
autoResume: falsewith a fresh request id, the same state as "Cancel auto-resume". Reopening the parent later does not revive it, and the sweep has nothing left to offer for that thread. A stale resume whose request id no longer matches (the user re-armed in the meantime) takes the existing no-op path, so it never overwrites a newer choice. If the parent is reopened before the reset, the child resumes as before.The user guide's usage-limit section now says a delegated task's continuation is cancelled if its parent is still settled when the reset arrives.
Scope and approval
Fixes #30144
Triaged via the automated issue flow.
Verification
Server-only change with no visible surface, so no captures. Proof is one regression in
runtimeLayer.test.ts, "retires a usage-limited subagent resume once its parent settles", through the real orchestrator: child armed while the parent is open → parent settled → reset passes → the sweep's resume is dispatched. It asserts no new child run, the parent still settled, the recovery disarmed; that unsettling the parent afterwards yields nolimit-resume:and nolimit-arm:; that re-dispatching the same saved resume does not leave the child armed but stuck; and that a stale resume after an explicit re-arm leaves the user's recovery untouched.Red with
main'sOrchestrator.ts(the child starts a second run):Green at this head:
Not checked: a live provider hitting a real usage limit through child completion and parent wake (no test environment can trigger a real Claude limit), and the reporter's macOS desktop build. The change is server-side and platform-independent. Delegation to a linked environment uses a different lineage and is not covered by this change (the same scope as https://git.cafe/pingdotgg/t3code/pulls/27951).
Claude Opus 5.5 (Claude Code in T3 Code), with reviews by GPT-6.1 Sol (Codex) and Claude Opus 5.5.
🤖 Generated with Claude Code