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:
- 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.
- 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.
Problem
The raw-call chokepoint (
crates/stella-pipeline/src/pipeline/raw_usage.rs::metered_raw_call) records a content-freeUsageIncompleteenvelope 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 emittedStepUsage, 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 throughSettleChildOnDrop(crates/stella-core/src/subagent.rs), and the stage emits the child'sFinishedbracket so the event stream stays balanced — but that synthetic bracket reportssteps: 0(the real committed-step count is not observable from outside the dropped future), and noUsageIncompleteis recorded for the in-flight call.What to build
Two candidate shapes, smallest first:
StepUsageevents (they pass through the sharedEventSender) so the syntheticFinishedbracket reports the true committed-step count, and emit aUsageIncomplete { reason: Cancelled }beside it for the dropped in-flight call.run_sub_agent_with_sendera 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
UsageIncompletewith reasoncancelled, and aFinished.stepsequal to the child's committedStepUsagecount. Today the envelope is absent andstepsis 0 — the witness fails.Constraints
crates/stella-pipeline/src/pipeline.rsandpipeline/tests.rsare god files closed to growth — changes land inresearch_stage.rs/ sibling test modules.UsageIncompleteReasonlives incrates/stella-protocol/src/event.rs(also grandfathered; additions must fit its retightened ceiling or move more tests out).Refs #1778, PR #1953.