spanDetail.test.tsxfeat: draw measured tool calls as real bars, and stop the filter inventing dead time
Tool spans now render as bars scaled to the root window when timingSource is
'measured', keeping the minimum clickable floor; 'completion-only' rows keep the
point marker. The discriminator is the flag, never durationMs === 0, because a
genuinely instantaneous measured call is now representable and must look
different from one that was never measured.
The stale copy was inverted, not merely incomplete: the module claimed
`created_at` was a start ("Tool calls store only a start time") when it is the
completion, so the marker was drawn at an end and labelled a start. The labels
are fixed too, not just the shape.
The gap indicator was structurally incapable of firing on any execution whose
children are tool calls — they were all zero-width, so coverage was empty and it
returned early. That is the whole leaf tier of every trace. Measured tool calls
now count as coverage and the bands appear where they were always meant to.
The design's "usually the model thinking" is documented as an upper bound rather
than an identity: a mixed trace still hides un-instrumented tool time inside the
gap, only direct children count, and framework latency lands there too.
One live correctness bug found while verifying that: gaps were computed from the
FILTERED span list. Harmless while tools covered nothing, but with real widths,
filtering to one tool deleted a covering call and fabricated ~2.8s of thinking
that never happened. Gaps are now computed over the unfiltered spans — a gap is a
fact about the run, not about the view.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 11, 2026, 3:05 PMtraceFixture.tsfeat: draw measured tool calls as real bars, and stop the filter inventing dead time
Tool spans now render as bars scaled to the root window when timingSource is
'measured', keeping the minimum clickable floor; 'completion-only' rows keep the
point marker. The discriminator is the flag, never durationMs === 0, because a
genuinely instantaneous measured call is now representable and must look
different from one that was never measured.
The stale copy was inverted, not merely incomplete: the module claimed
`created_at` was a start ("Tool calls store only a start time") when it is the
completion, so the marker was drawn at an end and labelled a start. The labels
are fixed too, not just the shape.
The gap indicator was structurally incapable of firing on any execution whose
children are tool calls — they were all zero-width, so coverage was empty and it
returned early. That is the whole leaf tier of every trace. Measured tool calls
now count as coverage and the bands appear where they were always meant to.
The design's "usually the model thinking" is documented as an upper bound rather
than an identity: a mixed trace still hides un-instrumented tool time inside the
gap, only direct children count, and framework latency lands there too.
One live correctness bug found while verifying that: gaps were computed from the
FILTERED span list. Harmless while tools covered nothing, but with real widths,
filtering to one tool deleted a covering call and fabricated ~2.8s of thinking
that never happened. Gaps are now computed over the unfiltered spans — a gap is a
fact about the run, not about the view.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 11, 2026, 3:05 PMtraceKeyboard.test.tsxfeat: the trace viewer — an Axiom-style waterfall over agent runs (design: curated-agenda phase 7)
A run reads like a distributed trace: an execution is a span, a dispatched
subagent is a child span, a tool call is a leaf span, all on one time axis
scaled to the root's window so overlapping siblings read as genuinely parallel
and a serialized fan-out reads as a staircase. Route at
/settings/agentExecutions/:runId, reachable from every execution list row and
from a paste-a-run-id box.
Zero-duration tool spans are drawn honestly. agent_tool_calls records no
completion timestamp, so every tool span has durationMs 0; rather than floor it
into a bar that implies a duration nobody measured, it renders as a point marker
with the duration column showing an em dash and the reason stated on hover. The
minimum-bar-width floor still exists, but for genuinely short executions, which
is the real sub-pixel case.
Gap indicators mark dead time inside a parent not covered by any child — usually
the model thinking, and where a run's latency actually goes. Tool-call points
cover nothing, so they cannot fake a gap away.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 9, 2026, 5:21 PMtraceWaterfall.test.tsxfeat: draw measured tool calls as real bars, and stop the filter inventing dead time
Tool spans now render as bars scaled to the root window when timingSource is
'measured', keeping the minimum clickable floor; 'completion-only' rows keep the
point marker. The discriminator is the flag, never durationMs === 0, because a
genuinely instantaneous measured call is now representable and must look
different from one that was never measured.
The stale copy was inverted, not merely incomplete: the module claimed
`created_at` was a start ("Tool calls store only a start time") when it is the
completion, so the marker was drawn at an end and labelled a start. The labels
are fixed too, not just the shape.
The gap indicator was structurally incapable of firing on any execution whose
children are tool calls — they were all zero-width, so coverage was empty and it
returned early. That is the whole leaf tier of every trace. Measured tool calls
now count as coverage and the bands appear where they were always meant to.
The design's "usually the model thinking" is documented as an upper bound rather
than an identity: a mixed trace still hides un-instrumented tool time inside the
gap, only direct children count, and framework latency lands there too.
One live correctness bug found while verifying that: gaps were computed from the
FILTERED span list. Harmless while tools covered nothing, but with real widths,
filtering to one tool deleted a covering call and fabricated ~2.8s of thinking
that never happened. Gaps are now computed over the unfiltered spans — a gap is a
fact about the run, not about the view.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 11, 2026, 3:05 PM