client_version survives Claude Code moving the version to the OTLP resource - #861
Conversation
Neutral review: approve, with two doc fixes pushedReviewed The fix itself is correct. No correctness or behaviour defect found in the
Verified on a worktree of Findings1. 2. Both fixed in 3. Remaining, out of scope for this PR
Also noted while reading step 6: its query filters on |
Review:
|
…source (#854) Claude Code 2.1.233 stamped `app.version` on every telemetry event; 2.1.235 sends it nowhere on the records and reports the version once per export, on the OTLP resource, as `service.version`. `flattenClaudeTelemetryEvents` touched the resource only to check the self-telemetry marker and then threw it away, so the projector had nothing to read and every `ai_gateway_messages` row produced by the OTEL capture path landed with a null `client_version`. The decoder now decodes each resource group once (which the self-marker check already paid for) and carries the decoded attributes on the events that group produced, for logs and metrics alike. The projector reads `app.version` first and falls back to the resource's `service.version`, so the older shape keeps working rather than being traded away for the newer one. `resource` is a field of its own on `ClaudeTelemetryEvent`, not a merge into `attributes`: `claude_telemetry_events` rows are per-event, and per-export facts do not belong in that JSON. The acceptance procedure's `claude_otel_shape_check` pass conditions said the version comes off the events, which would now read a healthy 2.1.235 capture as a failure; both spots say where to look on each version. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two pass conditions this branch rewrote still read wrong once the fix is in. Step 6 told the operator that a null `with_version` "is the regression issue #854 records": after this lands, #854 is closed and the resource fallback is in place, so a null there means the version reached neither place, which is new drift to file rather than a closed issue to reopen. Step 8 noted `app.entrypoint` alongside `app.version` under one explanation, but only the version moved to the resource. 2.1.235 drops the entrypoint attribute with nothing on the resource to fall back to, so `ai_gateway_messages.entrypoint` goes null on that client. Saying so keeps the step from normalizing a gap it does not actually cover. Docs only: no behavior change, no test change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5ceec1e to
bf5522a
Compare
What broke
Claude Code 2.1.233 stamped
app.versionon every telemetry event. 2.1.235does not send it on any record and reports the version once per export, on the
OTLP resource, as
service.version.flattenClaudeTelemetryEventstouched the resource only to check theself-telemetry marker and then discarded it, so
mergeSessionFactshad nothingto read:
Every
ai_gateway_messagesrow produced by the OTEL capture path landed with anull
client_version, which is a silent column regression on the clienthyp attach claudenow routes through, and a failure ofclaude_otel_shape_checkstep 6 (with_versionabove zero).The fix
flattenClaudeTelemetryEventsandflattenClaudeTelemetryMetricsdecodeeach resource group once (the self-marker check already paid for that decode)
and carry the decoded attributes on the events that group produced.
app.versionfirst and falls back to the resource'sservice.version. Event first keeps the 2.1.233 shape working instead oftrading one version floor for the other.
resourceis its own field onClaudeTelemetryEvent, not a merge intoattributes:claude_telemetry_eventsrows are per-event and per-exportfacts do not belong in that JSON, so the behavioral dataset is unchanged.
No Accepted LLP is edited. LLP 0262's field-parity table names
app.versionasthe source for
client_version; that is the observation the spike made, andthis closes the gap the upstream shape change opened under the same
requirement (R1), rather than changing what the RFC settled.
Evidence
test/plugins/claude-telemetry-listener.test.jsgains three tests. On masterthe first one fails with
undefinedwhere2.1.235is expected:After the fix all three pass:
app.versionplusservice.versionon the resource:projection and every projected
ai_gateway_messagesrow carry2.1.235.app.versionpresent alongside a differing resourceservice.version: theevent attribute still wins (
2.1.233), so the older client is not regressed.client_versionstays unset rather than becoming an emptystring.
HYP_HOME=$(mktemp -d) npm testis green apart from the known pre-existingrepo-scratch-hygienefailure (#852, PR #853 open).npm run typecheckisclean.
npm run smoke -- claude_telemetry_capturepasses.Docs
docs/ACCEPTANCE.mdsaid the version comes off the events in two passconditions, which would read a healthy 2.1.235 capture as a failure. Both now
say where to look on each version.
Fixes #854