Skip to content

Engine-turn sub-agents cancelled mid-call leave no UsageIncomplete envelope (research stage's latency ceiling is the first pipeline caller) #1954

Description

@macanderson

Problem

The raw-call chokepoint (crates/stella-pipeline/src/pipeline/raw_usage.rs::metered_raw_call) records a content-free UsageIncomplete envelope when a call is dropped on its latency ceiling, so abandoned spend is visible in accounting. Engine turns have no analog: a sub-agent child cancelled mid-model-call (its future dropped) emits nothing for the in-flight call — committed steps emitted StepUsage, but the final in-flight request's usage is unobservable and unrecorded.

Until PR #1953 nothing in the pipeline cancelled engine turns, so the gap was theoretical. The pre-plan research stage (#1778) now wraps each research child in tokio::time::timeout(research_latency_ceiling, ...) (crates/stella-pipeline/src/pipeline/research_stage.rs); a timed-out child's already-settled spend folds back through SettleChildOnDrop (crates/stella-core/src/subagent.rs), and the stage emits the child's Finished bracket so the event stream stays balanced — but that synthetic bracket reports steps: 0 (the real committed-step count is not observable from outside the dropped future), and no UsageIncomplete is recorded for the in-flight call.

What to build

Two candidate shapes, smallest first:

  1. Have the research stage count the child's forwarded StepUsage events (they pass through the shared EventSender) so the synthetic Finished bracket reports the true committed-step count, and emit a UsageIncomplete { reason: Cancelled } beside it for the dropped in-flight call.
  2. Or push the accounting into the sub-agent primitive: give run_sub_agent_with_sender a cancellation-aware wrapper that owns the bracket + envelope on drop, so any future caller with a ceiling inherits the fix (the same argument that moved the verifier onto the primitive).

Verify

A scripted research child that never answers within the ceiling produces: a balanced Started/Finished bracket, a UsageIncomplete with reason cancelled, and a Finished.steps equal to the child's committed StepUsage count. Today the envelope is absent and steps is 0 — the witness fails.

Constraints

  • crates/stella-pipeline/src/pipeline.rs and pipeline/tests.rs are god files closed to growth — changes land in research_stage.rs / sibling test modules.
  • UsageIncompleteReason lives in crates/stella-protocol/src/event.rs (also grandfathered; additions must fit its retightened ceiling or move more tests out).

Refs #1778, PR #1953.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:pipelinestella-pipeline — triage→…→judge orchestrationtriageUntyped request — convert by adding bug / feature / epic

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions