Repository navigation
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 6e51d21b127749aca520812de71197fb
Branch: dev/1-otel-tool-span-key
Base: 0889d8c
At: 2026-11-05T13:48:56Z
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
What happens
The OTLP export keys tool calls by the call id alone: plugins/timeline/src/otel.js:55 does toolStarts.set(record.id, record) (and :56 the same for ends), while the recorder writes { id, tid } and src/window.js:77 folds a call under `${record.tid}/${record.id}`. Two turns that use the same call id — two Bots calling the same tool, or a provider that numbers calls per request — therefore overwrite each other:
- the earlier tool loses its whole span (a tool run silently disappears from the trace), and
- the surviving span carries the other turn's
tidfortraceId/parentSpanId(otel.js:88-90readks.tid), so it is attached under the wrong turn; - a call still open in an earlier turn is dropped from the carry-over set instead of being carried (
:136-139, keyed the same way).
Reproduction (red first)
Clean 90a59447, only plugins/timeline/test/otel-tool-ids.test.js added; cd plugins/timeline && TZ=UTC node --import ./test/resolve.mjs --test test/otel-tool-ids.test.js:
not ok 1 - two turns that both call a tool with the same call id export one tool span each
error: |-
one span per tool call, saw execute_tool bash
1 !== 2
not ok 2 - a tool call still open in an earlier turn is carried, not replaced by a later one
error: |-
+ actual - expected
[
- 'turn-a',
'turn-b'
]
# tests 2 / # pass 0 / # fail 2
Expected
One tool span per {tid, id} pair. Two turns that call a tool with the same call id export two spans, each with its own turn's traceId/parentSpanId; an unfinished call in an earlier turn is carried into the next batch instead of being replaced.
Scope
plugins/timeline/src/otel.js— key the tool maps (and the derivedspanId) by`${tid}/${id}`, the same keysrc/window.jsalready folds on;+6/−4.plugins/timeline/test/otel-tool-ids.test.js— the two cases above, 43 lines.docs/subsystems/timeline.md— one sentence on the key.changelog.d/<this issue>-otel-tool-span-key.fixed.md— one fragment.
Tier: T2 (plugins/**). No host, no client bundle, no generated file.
Duplicate check
Searched live main (the behaviour is present at otel.js:55) and the issue tracker before filing: otel in:title → 0; "tool span" → 7, all closed and different (#484/#483 built the export, #661 same-millisecond cursor, #2444 untimed records, #246/#241 the recorder). No open issue covers the key collision. Twelve open pull requests at filing time: none touches plugins/timeline/**.
Done when
Both cases pass on a clean base plus the fix, plugins/timeline is green end to end (143/143 with the two new cases), TZ=UTC ARCH_DOCS_BASE=<base> npm run verify exits 0, and the export still emits exactly one span per tool call for a single-turn trace.
Activity
HugoXK commentedon Nov 5, 2026
Claim-Id: 6e51d21b127749aca520812de71197fb
Branch:
dev/1-otel-tool-span-keyBase:
90a594471b5886737b433e116f6397aa8ca2b6f2Written-by: FlyBot Lead
Claiming for the FlyBot team under the implementation marker above: one branch, one PR with
Fixes #2539, released with a comment here if abandoned rather than silently dropped.Red first already exists:
not ok 1(1 !== 2spans for two turns sharing a call id) andnot ok 2(turn-amissing from the carry-over) on a clean90a59447; after the fixplugins/timelineis 143/143 andverifyexits 0. Keying the maps by${tid}/${id}is the whole change — no host, no generated file.timeline: export one tool span per turn and call id, not per call id (T…
HugoXK commentedon Nov 5, 2026
Release
Written-by: FlyBot Lead
Released: 6e51d21b127749aca520812de71197fb
Reason: merged to main via its PR (squash)
At: 2026-11-05T15:35:56Z