Skip to content

πŸ€– Sub-agent goals: follow-ups (parent spend ceiling, UI test, deleted child agent)Β #5411

Description

@ThomasK33

Follow-ups from the sub-agent goals stack (#5393 ← #5383 ← #5389 ← #5396, stack #5394).

  1. Parent budget is not a hard ceiling on child spend. A child goal's spend is attributed to the parent's goal after the child reports (see the comment at 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.
  2. Test gap: add a RightSidebar test 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.
  3. Deleted child agent definition: if a child's persisted agentType names 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, so set_goal and complete_goal are 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

Activity

  1. self-assigned this
    on Oct 1, 2026
  2. ThomasK33 commented on Oct 1, 2026

    @ThomasK33
    MemberAuthor

    Two more deferred Codex findings from #5396 (round 5):

    1. Archive of an idle shared-desktop child (PRRT_kwDOPxxmWM6oDM0W): settleArchivedSharedDesktopTask records taskGoalPauseOwed but doesn't settle it, so the Goal tab shows "active" (fenced, unrunnable) until reactivation settles it. Settle it as part of the archive transition.
    2. Two-backend termination pause race (PRRT_kwDOPxxmWM6oDM0f): settleChildGoalPause reads the goal under the lock, then pauses it through a separate setGoal. 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.
  3. ThomasK33 commented on Oct 1, 2026

    @ThomasK33
    MemberAuthor

    More follow-ups from the final #5396 review round:

    1. Goal resume on a reactivated reported child. A follow-up or ancestor reactivation keeps taskStatus: "reported" and runs the new execution through taskExecutionStatus. isChildGoalActivationAllowed accepts only taskStatus: "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.
    2. Accounting receipts on workspace removal. WorkspaceGoalService keeps open and evicted stream-accounting receipts per workspace (at most 8 each). Workspace removal notifies TaskService but not WorkspaceGoalService, so a removed workspace's entries stay in memory. Fix: clear both maps on removal.
  4. ThomasK33 commented on Oct 1, 2026

    @ThomasK33
    MemberAuthor

    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_goal with any positive budgetCents or turnCap. 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 model set_goal in sub-agents.

  5. ThomasK33 commented on Oct 2, 2026

    @ThomasK33
    MemberAuthor

    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: high

  6. ThomasK33 commented on Oct 2, 2026

    @ThomasK33
    MemberAuthor

    Triage (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_goal has 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" in aiService.test.ts). TaskService.sendChildGoalTurn refuses 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, with set_goal/complete_goal refused. This fails closed as the item describes, and no goal turn ever runs on the fallback.
    (a) Archiving an idle shared-desktop child leaves taskGoalPauseOwed unsettled A 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.finalizeWorkspaceTurnFromStreamEnd and never reaches child goal arbitration, so no turn would drive the resumed goal. None of the execution's termination writes (persistAgentTaskExecutionState, the Stop cascade's preserved-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: high

  7. ThomasK33 commented on Oct 2, 2026

    @ThomasK33
    MemberAuthor

    Deferred from #5454 (round 4 normal review, thread PRRT_kwDOPxxmWM6oTCuo), tracked with item (b) because it is the same class of defect.

    stillCurrent fences 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 commit paused for 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: high

  8. ThomasK33 commented on Oct 2, 2026

    @ThomasK33
    MemberAuthor

    Status after #5455, merged as 1d7b4a0 (Refs #5411):

    Item State
    2. RightSidebar test gap Done (1d7b4a0). tests/ui/rightSidebar/childGoalBudget.test.ts covers 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). noteWorkspaceRemoved releases open receipts as evicted and 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 backlog for the remaining items.


    Generated with xum β€’ Model: anthropic:claude-opus-5-5 β€’ Thinking: high

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions