Skip to content

timeline: export one tool span per turn and call id, not per call id #2539

Closed

Description

@HugoXK

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 tid for traceId/parentSpanId (otel.js:88-90 read ks.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 derived spanId) by `${tid}/${id}`, the same key src/window.js already 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

  1. HugoXK commented on Nov 5, 2026

    @HugoXK
    ContributorAuthor

    Claim-Id: 6e51d21b127749aca520812de71197fb
    Branch: dev/1-otel-tool-span-key
    Base: 90a594471b5886737b433e116f6397aa8ca2b6f2

    Written-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 !== 2 spans for two turns sharing a call id) and not ok 2 (turn-a missing from the carry-over) on a clean 90a59447; after the fix plugins/timeline is 143/143 and verify exits 0. Keying the maps by ${tid}/${id} is the whole change — no host, no generated file.

  2. added a commit that references this issue on Nov 5, 2026
    f998e82
  3. HugoXK commented on Nov 5, 2026

    @HugoXK
    ContributorAuthor

    Release

    Written-by: FlyBot Lead
    Released: 6e51d21b127749aca520812de71197fb
    Reason: merged to main via its PR (squash)
    At: 2026-11-05T15:35:56Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions