feat: account chatd turn time by stage and category - #28974
Draft
jscottmiller wants to merge 1 commit into
Draft
Conversation
Contributor
Docs previewCheck off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here. |
This was referenced Sep 4, 2026
jscottmiller
force-pushed
the
scott/chatd-stages/6-capacity-wait
branch
from
September 9, 2026 23:19
773324f to
b901eb5
Compare
jscottmiller
force-pushed
the
scott/chatd-stages/7-turn-accounting
branch
from
September 9, 2026 23:19
422889e to
fbf8b65
Compare
jscottmiller
force-pushed
the
scott/chatd-stages/6-capacity-wait
branch
from
September 11, 2026 02:25
b901eb5 to
09ef319
Compare
jscottmiller
force-pushed
the
scott/chatd-stages/7-turn-accounting
branch
from
September 11, 2026 02:25
fbf8b65 to
2eec66e
Compare
ibetitsmike
approved these changes
Sep 11, 2026
Each chat_turn carries a TurnAccumulator that turn-scoped stages report to as they end. Stages that partition the turn form an attribution tree so a step's own time is split into disjoint categories, with tool execution separated from chatd overhead by the step's generation action. When a turn that Complete marked finished settles, its per-stage totals, counts, shares, and category partition are observed once on the turn histograms; Invalidate drops the accounting of a turn that stopped partway through on an error or interruption. Turns whose duration is not positive or whose categories overrun it are counted as anomalies. The per-turn families are labelled by chat kind and model; reasoning effort stays a span attribute. coderd_chatd_turn_time_seconds is registered at the basic and full stage metrics levels; the per-stage totals, counts, and share families are registered only at full. The per-turn histograms use an 11-bucket ladder from 1s to 1h, since sub-second resolution carries nothing for a value summed over a turn.
jscottmiller
force-pushed
the
scott/chatd-stages/6-capacity-wait
branch
from
September 11, 2026 19:17
09ef319 to
388e95d
Compare
jscottmiller
force-pushed
the
scott/chatd-stages/7-turn-accounting
branch
from
September 11, 2026 19:17
2eec66e to
948b3e7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a turn finishes normally, its stages are rolled up and emitted once as five histograms labelled
{stage|category, chat_kind, model}(reasoning effort is a span attribute, not a metric label):coderd_chatd_turn_stage_seconds,turn_stage_count,stage_share_of_turn: per stage, the total seconds, occurrences, and fraction of the turn. Stages overlap, so shares can exceed 1; the share buckets extend to 10 and the count buckets past the 1200 step limit.coderd_chatd_turn_time_seconds,turn_time_share: an exclusive partition of the turn's wall time intoscheduling,time_to_first_token,streaming,provider_error,retry_backoff,tool_execution,compaction,preparation,persistence,chatd_overhead, andunattributed. All eleven are emitted every turn.turn_time_secondsis registered at thebasicandfullstage metrics levels; the other four families are registered only at--chat-stage-metrics=full. The accumulator runs at every level, so the partition is available atbasiceven for stages that the level leaves span-only. The per-turn histograms use an 11-bucket ladder from 1s to 1h, since sub-second resolution carries nothing for a value summed over a turn.How the partition is built
Stages nest, and a nested stage's time is also its parent's time, so summing stage durations over-counts. That is why the per-stage totals above are a profile rather than a partition. The partition instead assigns each stage's own time (its duration minus its attributing children) to one category. A
TurnAccumulatorrides on the turn context; each attributing stage reports its full duration to its parent when it ends, and the parent's category receives only what is left.provider_attempt,thinking, andtool_callare recorded in the per-stage totals but never claim time from their parent or land in a category: each lies entirely inside a stage that is already categorized, so counting them would double-count.capacity_waitis excluded for the same reason (its window lies insideacquisition). Only turn-scoped stages started by a live tracer report to the accumulator, so background work never lands in a turn. Errored or interrupted turns are invalidated and emit nothing.A single 10s step, drawn to scale:
In practice the step's own time is not zero: the gaps between phases (decision logic, buffer bookkeeping, tool dispatch) are what
chatd_overheadfrom the step itself captures.A turn whose categories sum to more than its duration is emitted as measured and counted in
coderd_chatd_stage_anomalies_total{reason="overattributed"}; a finished turn with a non-positive duration is counted asnonpositive_turn.capacity_waitis not categorized because its window lies insideacquisition.Part of the chatd lifecycle observability stack: 1 extract step · 2 stage tracer · 3 stream stages · 4 provider attempts · 5 turn span · 6 capacity wait · 7 turn accounting · 8 dashboard. Supersedes the experimental branch in #28741.
🤖 This PR was generated by Coder Agents on behalf of @jscottmiller.