Repository navigation
timeline: export one tool span per turn and call id, not per call id - #2540
Conversation
|
@Birfy review request for the current tip of this PR.
If the workflows for this fork PR did not start, approve/run them as well. Nothing else is needed from the author side; the tip will not move while this request stands. |
|
Verdict: PASS at de8314a Ran at this tip: Scope: 4 files, +58/−8 — Red first: with the changed source reverted to the base blob and the new test kept, both cases fail — Fix check: Notes: tool-span ids change for every tool record (previously hashed from id alone), deterministic per record and not asserted anywhere; no OTLP endpoint was contacted, as these functions are pure. All check-runs at the tip are terminal; only |
Fixes #2539
Owner: Dev-1
Written-by: Dev-1
Reviewer: Birfy
Claim-Id: 6e51d21b127749aca520812de71197fb
Base: 0889d8c
Risk tier: T2
What changed
The OTLP export keyed a tool call by
record.idalone:spansFrombuilttoolStarts/toolEndswithset(record.id, record), andpendingRecordsdid the same foropenTools. A call id is unique only inside its turn: the recorder writesidandtidtogether (plugins/timeline/src/recorder.js:110/:118), and the window folds the two as`${tid}/${id}`(plugins/timeline/src/window.js:77). Two turns of one trace — two Bots, or a provider that numbers its calls per request — can both call a toolcall_0. The laterksreplaced the earlier one in the map, so:kswas gone, and itskepaired with the later call);After: both maps and
pendingRecordskey a tool record by`${record.tid ?? ''}/${record.id}`(the same pair the window folds by), andspanIdderives from that key (tool-span:${key}), so two turns that both call a toolcall_0get two spans with two ids.traceIdandparentSpanIdstill name the record's own turn (ks.tid), andhexIditself is unchanged: the same record still maps to the same id across exports.Hand-written files:
plugins/timeline/src/otel.js(+6/−4, plus a four-line comment naming the pair),plugins/timeline/test/otel-tool-ids.test.js(new, 43 lines, 2 cases),docs/subsystems/timeline.md(one clause in invariant 11),changelog.d/2539-otel-tool-span-key.fixed.md. No generated file moves: this plugin has nopatches/ds-bot/carrier,core/is untouched andcore/client.jsis not built fromplugins/.Red first
On a clean
90a59447with onlyplugins/timeline/test/otel-tool-ids.test.jsadded,cd plugins/timeline && TZ=UTC node --import ./test/resolve.mjs --test test/otel-tool-ids.test.js:Verification at the tip
Tip SHA: b21ee920e671dad8b901e6d8bd087263890abe4d (tree db4d999f6098193adc310907bb67de433ba8a96c), single parent
0889d8ca8029a62b63e2b8713c216edc2d5060e4= live main.Ran on a clean tree at that commit:
plugins/timelinesuite:# tests 143 / # pass 143 / # fail 0(141 before this test file, 2 new).TZ=UTC ARCH_DOCS_BASE=0889d8ca… npm run verify: exit 0, nonot okanywhere — core 2928/2928, timeline 143/143, tree 137/137,.github/scripts+policy671/671, interop 37/37, live-e2e 26/26, plus social and workflow as the root script runs them.node .github/scripts/changelog.mjs check:36 fragment(s) in changelog.d/ are well formed.ARCH_DOCS_BASE=0889d8ca… node core/scripts/check-arch-docs.mjs:arch-docs: OK (4 changed file(s)).node scripts/ds-bot-patches.mjs check/canonical(both insideverify):10 patches apply cleanly in order,core/ equals ds-bot/ + patches/ds-bot/, and generate is a no-op — expected, since nothing undercore/changed.Not run
createExportertakessendas an argument, and no test in this repo sends to a real collector. The change is insidespansFrom/pendingRecords, which are pure.index.js, no host endpoint and no client bundle changed.Files
plugins/timeline/src/otel.js—toolKeyhelper;spansFrompairsks/keby${tid}/${id}and derivesspanIdfrom it;pendingRecordskeysopenToolsthe same way.plugins/timeline/test/otel-tool-ids.test.js— new: one span per call with its own parent; an open call in an earlier turn is carried.docs/subsystems/timeline.md— invariant 11 names the pair a tool span is exported by.changelog.d/2539-otel-tool-span-key.fixed.md— the user-visible entry.Checkpoint
{ "pr": 2540, "claim": "Dev-1", "issue": 2539, "summary": "The OTLP export paired a tool call's start and end by its call id alone, while a call id is unique only inside its turn (the recorder writes id and tid, the window folds them as tid/id). Two turns that both call a tool call_0 overwrote each other: one tool span was dropped, and an open call in the earlier turn was never carried. Both maps and pendingRecords now key by tid/id and the span id derives from that pair; trace and parent still name the record's own turn.", "files": [ "plugins/timeline/src/otel.js", "plugins/timeline/test/otel-tool-ids.test.js", "docs/subsystems/timeline.md", "changelog.d/2539-otel-tool-span-key.fixed.md" ], "checks": { "ran": "plugins/timeline 143/143; TZ=UTC ARCH_DOCS_BASE=0889d8ca npm run verify (exit 0); changelog check; arch-docs; ds-bot-patches check + canonical (no-op)", "result": "green", "at": "b21ee920e671dad8b901e6d8bd087263890abe4d" }, "next": "none — ready for review" }