Skip to content

fix(state): subagent spend not rolled up to parent until SubagentStop — parent fail-fast can be exceeded mid-flight (paper §5 invariant #2) #135

@manzil-infinity180

Description

@manzil-infinity180

Problem

Paper §5 invariant #2 ("Accumulation"): "Sub-agent spend counts toward parent totals."

This only fires AT SubagentStop. While a child agent is running concurrently with its parent, the parent's PostToolUse fail-fast check sees only its own cumulative spend.

Concrete: maxSpendUSD=$10, parent at $9, child spending $5 concurrently — parent's next PostToolUse passes ($9 ≤ $10), child stops, merge brings parent total to $14. Policy ceiling exceeded.

Code reality

mergeChildIntoParent (internal/hooks/handler.go:1645) has exactly one callsite — handler.go:1110 inside handleSubagentStop. No push-up from child PostToolUse to parent during runtime.

Options

A — push-rollup (continuous accumulation): child PostToolUse acquires parent lock, loads parent, merges incremental delta, saves parent. Cost: extra lock contention per child tool call. Cleanest realization of paper invariant #2.

B — pull-rollup (parent-side aggregation): parent PostToolUse enumerates all child sessions whose ParentSessionID == parent.SessionID, sums their metrics on the fly. Cheaper when children are rare; correctness depends on enumeration being complete.

C — qualify paper: acknowledge accumulation is "at child completion" not continuous. Document, drop the implied real-time guarantee.

Recommend A or B for a real "fail-fast" guarantee. Pick one before implementation.

Acceptance

  • Concurrent parent + child cannot exceed parent.maxSpendUSD by more than one child-tool-call delta (A or B), OR paper §5 invariant #2 updated to "at-completion" (C)
  • Test: spawn child, child spends $X via PostToolUse, parent's next PostToolUse gate observes the child spend
  • Documented behaviour matches code

References

  • Paper §5 invariant #2 ("Accumulation")
  • internal/hooks/handler.go:1110 (single mergeChildIntoParent callsite, SubagentStop only)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghookHook-mode-specific (Claude Code hooks pathway). MCP mode unaffected or has different flow.paper-90Close set (assumes paper-85 done) → reaches ~90%paper-gapGap between paper claims and implementation

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions