Problem
TallyFold::observe (crates/stella-serve/src/observe/tally.rs, the match event around line 51) drops AgentEvent::TurnParked and AgentEvent::TurnWoken into its inert _ => {} arm.
That is precisely the distinction the tally exists to make. Its own field doc for TurnTally::stages (crates/stella-serve/src/observe/event.rs, around line 535) says:
A turn whose stages stopped advancing while a reverse request's wait climbs is wedged; one that keeps advancing is merely slow.
A parked turn (#1471) stops advancing stages on purpose — it is probing external state on the engine's own clock, with zero model calls, for up to its deadline. With park/wake dropped, a host reading the tally sees a stages-stall it cannot tell apart from a hang, and the one signal that would explain it was on the stream and discarded.
This is not a regression from #1857 — before the typed events the park arrived as AgentEvent::Text, which this fold also ignored. It is a gap that only became fixable once the events existed.
What done looks like
TurnTally gains park accounting — at minimum a parked_spans count; polls_used and/or accumulated parked seconds are worth considering, since "wedged vs deliberately waiting for 20 minutes" is a duration question.
TallyFold::observe gains explicit TurnParked/TurnWoken arms. Follow the existing saturating_add idiom used by every other counter in that match.
- A host reading the tally can answer "is this turn wedged?" correctly for a parked turn. Assert it: a fold over
[Stage, TurnParked, TurnWoken, Stage] must be distinguishable from a fold over [Stage] alone.
- Check whether
TurnTally is a wire type reaching docs/wire/serveframe.schema.json. If so, regenerate via scripts/export-agentevent-schema.sh in the same PR (the wire-schema gate) and keep the addition serde-additive so a host recorded before this parses.
Constraints
- Content-free. The tally is observability.
TurnParked.description is tool-authored free text and must NOT be counted into or copied onto the tally — counts and the closed WakeReason token only. Same rule the bus payloads follow in crates/stella-core/src/driver/lifecycle.rs::turn_parked_payload, and the diag bridge in crates/stella-cli/src/diag_bridge.rs.
- The existing test
crates/stella-serve/src/observe/tally.rs (fold.finish() == TurnTally::default() for content events) pins that text/reasoning/steer contribute nothing — do not weaken it.
- Park semantics must not change; this is an observability gap only.
Context
Refs #1857, #1471
Problem
TallyFold::observe(crates/stella-serve/src/observe/tally.rs, thematch eventaround line 51) dropsAgentEvent::TurnParkedandAgentEvent::TurnWokeninto its inert_ => {}arm.That is precisely the distinction the tally exists to make. Its own field doc for
TurnTally::stages(crates/stella-serve/src/observe/event.rs, around line 535) says:A parked turn (#1471) stops advancing stages on purpose — it is probing external state on the engine's own clock, with zero model calls, for up to its deadline. With park/wake dropped, a host reading the tally sees a stages-stall it cannot tell apart from a hang, and the one signal that would explain it was on the stream and discarded.
This is not a regression from #1857 — before the typed events the park arrived as
AgentEvent::Text, which this fold also ignored. It is a gap that only became fixable once the events existed.What done looks like
TurnTallygains park accounting — at minimum aparked_spanscount;polls_usedand/or accumulated parked seconds are worth considering, since "wedged vs deliberately waiting for 20 minutes" is a duration question.TallyFold::observegains explicitTurnParked/TurnWokenarms. Follow the existingsaturating_addidiom used by every other counter in that match.[Stage, TurnParked, TurnWoken, Stage]must be distinguishable from a fold over[Stage]alone.TurnTallyis a wire type reachingdocs/wire/serveframe.schema.json. If so, regenerate viascripts/export-agentevent-schema.shin the same PR (the wire-schema gate) and keep the addition serde-additive so a host recorded before this parses.Constraints
TurnParked.descriptionis tool-authored free text and must NOT be counted into or copied onto the tally — counts and the closedWakeReasontoken only. Same rule the bus payloads follow incrates/stella-core/src/driver/lifecycle.rs::turn_parked_payload, and the diag bridge incrates/stella-cli/src/diag_bridge.rs.crates/stella-serve/src/observe/tally.rs(fold.finish() == TurnTally::default()for content events) pins that text/reasoning/steer contribute nothing — do not weaken it.Context
TurnTallyfield rather than a match arm.RemoteToolExecutorcannot forward parked-wait requests across the wire) — a served turn cannot park at all yet, so this is the observability half of the same story and the two are worth sequencing together.Refs #1857, #1471