Repository navigation
π€ Sub-agent goals: follow-ups (parent spend ceiling, UI test, deleted child agent)Β #5411
Description
Activity
Two more deferred Codex findings from #5396 (round 5):
- Archive of an idle shared-desktop child (PRRT_kwDOPxxmWM6oDM0W):
settleArchivedSharedDesktopTaskrecordstaskGoalPauseOwedbut doesn't settle it, so the Goal tab shows "active" (fenced, unrunnable) until reactivation settles it. Settle it as part of the archive transition. - Two-backend termination pause race (PRRT_kwDOPxxmWM6oDM0f):
settleChildGoalPausereads the goal under the lock, then pauses it through a separatesetGoal. With two backends, a delayed pause can land on the successor attempt's resumed goal. This is fail-safe: the goal ends up paused. Make read+pause one goal-lock operation, or revalidate the owed attempt before writing.
- Archive of an idle shared-desktop child (PRRT_kwDOPxxmWM6oDM0W):
More follow-ups from the final #5396 review round:
- Goal resume on a reactivated reported child. A follow-up or ancestor reactivation keeps
taskStatus: "reported"and runs the new execution throughtaskExecutionStatus.isChildGoalActivationAllowedaccepts onlytaskStatus: "running", so the Goal tab's Reopen/Resume is refused while that execution is live. Fix: accept the live reactivated execution in the activation invariant, and make sure its termination still settles the owed goal pause. π€ feat: let sub-agents own and pursue goalsΒ #5396 states this as out of scope. - Accounting receipts on workspace removal.
WorkspaceGoalServicekeeps open and evicted stream-accounting receipts per workspace (at most 8 each). Workspace removal notifies TaskService but notWorkspaceGoalService, so a removed workspace's entries stay in memory. Fix: clear both maps on removal.
- Goal resume on a reactivated reported child. A follow-up or ancestor reactivation keeps
Security framing for item 1, from the #5396 Codex security review (Cap delegated child goals with a trusted limit). The maintainer accepted the risk for #5396; this issue tracks the fix.
A prompt-injected exec sub-agent can call
set_goalwith any positivebudgetCentsorturnCap. TaskService then keeps the child working through goal turns instead of reporting. No task-level turn ceiling counts those turns, and the parent goal is charged only after the child reports. Candidate fixes: clamp a child's model-created goal to a trusted ceiling (for example the parent goal's remaining limits), or refuse modelset_goalin sub-agents.Picked up by the issue coordinator: workspace workspace-63 (89a17e992e), branch
fix/5411-child-goal-followups.
Generated with
xumβ’ Model:anthropic:claude-opus-5-5β’ Thinking:highTriage (issue coordinator lane, workspace-63). Categories: A = single-backend defect with a small fix and no new persisted field; B = needs two backends; C = needs a new persisted field or subsystem; D = low priority; E = already fixed.
Item Category Plan 1. Parent spend ceiling (a child set_goalhas no trusted cap)Accepted risk The maintainer accepted this risk. Not fixed here; this issue keeps tracking it. 2. RightSidebar test gap (no composer-model budget pre-check for a child goal) A (tests only) Branch fix/5411-child-goal-followups.3. Deleted child agent definition E Resolved by #5451 (fail closed). An automatic goal turn in a child resolves only the pinned first candidate, never a later candidate or exec(agentResolution.ts,resolveAgentForStream; test "a sub-agent's goal turn does not fall through to a later candidate or exec" inaiService.test.ts).TaskService.sendChildGoalTurnrefuses that turn, pauses the goal and publishes the report (test "an unavailable pinned agent pauses the goal and sends no goal turn"). An ordinary non-goal turn may still fall back, withset_goal/complete_goalrefused. This fails closed as the item describes, and no goal turn ever runs on the fallback.(a) Archiving an idle shared-desktop child leaves taskGoalPauseOwedunsettledA Same branch. Settle the owed pause after the archive (and unarchive) config write. (b) Two-backend termination pause race B Fail-safe (the goal ends paused). Left tracked. (c) A reactivated reported child cannot Reopen/Resume its goal C Accepting the live execution in the activation invariant alone is not enough. A reactivated execution's stream end goes through WorkspaceTurnManager.finalizeWorkspaceTurnFromStreamEndand never reaches child goal arbitration, so no turn would drive the resumed goal. None of the execution's termination writes (persistAgentTaskExecutionState, the Stop cascade'spreserved-completed-report) owe or settle a goal pause. A correct fix needs a goal driver inside workspace-turn executions (same-turn continuation sends) plus a termination-settlement hook. That is about 200β300 lines across the TaskService/WorkspaceTurnManager seam. Waiting on the coordinator's decision.(d) Stream-accounting receipts not cleared on workspace removal A Same branch. Clear both receipt maps from TaskService's workspace-removal hook. The PR will say "Refs #5411". Item 1, (b) and (c) unless approved stay tracked here.
Generated with
xumβ’ Model:anthropic:claude-opus-5-5β’ Thinking:highDeferred from #5454 (round 4 normal review, thread PRRT_kwDOPxxmWM6oTCuo), tracked with item (b) because it is the same class of defect.
stillCurrentfences the child's unavailable-agent pause under the goal file lock, right before the write. An attempt rotation can still land during the write's own file I/O. The task attempt lives in config, which does not take the goal lock, and another backend does not share the in-process lock at all. In that window the stale pause can still commitpausedfor the successor attempt.This is fail-safe: the goal ends paused and the user resumes it. A goal never runs on an unavailable agent. It predates #5454, which had no fence at all and now narrows the window to the write itself.
A sound fix serializes the attempt CAS with goal persistence, or rechecks after the write and restores the prior record. It belongs with (b), the two-backend termination pause race.
Generated with
xumβ’ Model:anthropic:claude-opus-5-5β’ Thinking:highStatus after #5455, merged as 1d7b4a0 (Refs #5411):
Item State 2. RightSidebar test gap Done (1d7b4a0). tests/ui/rightSidebar/childGoalBudget.test.tscovers both branches: a child's budget edit skips the composer pre-check, and a top-level edit keeps it.(a) Archiving a shared-desktop child leaves its goal pause owed Done (1d7b4a0). Archive settles the owed pause after the archive write and after live activity stops; unarchive settles it too. (d) Accounting receipts kept after workspace removal Done (1d7b4a0). noteWorkspaceRemovedreleases open receipts asevictedand drops both maps.Reported-child resume refusal text (coordinator decision) Done (1d7b4a0). The text says resuming a reported sub-agent's goal is not supported yet. The refusal itself is unchanged. 3. Deleted child agent definition Already fixed by #5451 (see the triage comment). 1. Parent spend ceiling Open. The maintainer accepted this risk. (b) Two-backend termination pause race Open (B). The pause-write race deferred from #5454 belongs here too: #5411 (comment) (c) A reactivated reported child cannot Resume its goal Open (C). It needs a termination-settlement hook plus a goal driver inside workspace-turn executions. Labeled
backlogfor the remaining items.
Generated with
xumβ’ Model:anthropic:claude-opus-5-5β’ Thinking:high- added 4 commits that reference this issue
on Oct 2, 2026 - addedinvestigationTriage: proposal / research / trackingTriage: proposal / research / tracking
on Oct 9, 2026
Follow-ups from the sub-agent goals stack (#5393 β #5383 β #5389 β #5396, stack #5394).
attributeChildReport). A child goal can also make the parent wait longer, up to the child's own budget or turn cap. This needs a product decision: cap child budgets at the parent's remaining budget, or keep attribution-only.RightSidebartest for skipping the composer-model budget pre-check on child workspaces (π€ feat: let sub-agents own and pursue goalsΒ #5396, commit "no composer-model budget pre-check for a sub-agent's goal"). The backend side is covered.agentTypenames a custom agent whose definition was deleted, the turn falls back to the next candidate or to exec. The selected-agent gate still compares against the first candidate, soset_goalandcomplete_goalare refused on that turn. This fails closed, and the goal still ends through the report or the budget limit. It is related to π€ Goal continuations fall back to exec when the selected agent no longer resolvesΒ #5402.Generated with
xumβ’ Model:anthropic:claude-opus-5-5β’ Thinking:highβ’ Cost:$74.31