rfc: 0043 title: Derive event_name from the legacy event.name attribute at ingest status: accepted author: Jens Holdgaard Pedersen jens@holdgaard.org drafting-assistance: Claude created: 2026-07-29 supersedes: — superseded-by: —
RFC 0043 — Derive event_name from the legacy event.name attribute at ingest
Status:
accepted(2026-07-29, maintainer sign-off). Terminal. No thesis-gate applies (validatedvacuous, RFC 0008 precedent); the flip records maintainer approval of the derivation as binding.Status:
green(2026-07-29). All seven §5 criteria pass: the derivation + boundary shapes landed in #668 (RFC0043.1–.4/.7, both encodings through the onematerialize_recordseam), and #669 closed .5 (attr-only records through the real miner into a real store, matched end-to-end byevent_name ==) and .6 (the id-separation observable, criterion refined inline — the originally referenced counter does not exist as a distinct instrument). No thesis-gate applies;acceptedis a maintainer flip.(
specified, same date: §5 criteria written and testable; the one fidelity question (§7) resolved in the design — the attribute is preserved verbatim, the field is derived, nothing is corrected.)
1. Summary
When an incoming LogRecord has no top-level event_name but carries an
event.name attribute, ingest populates the stored record’s event_name
from that attribute — and keeps the attribute itself byte-for-byte. This
is the OTel spec’s own migration story executed at the backend: the
event.name attribute is the legacy precursor of the top-level
EventName field, and real sources (Claude Code, opencode-plugin-otel)
still emit only the attribute. With the field populated, RFC 0037’s
event-keyed templating engages for those sources and
event_name == "…" becomes the idiomatic DSL filter the OTel events
semconv prescribes (“when users query for a specific event name…”).
2. Motivation
The idiomatic query exists but the data never reaches it. The DSL
already exposes event_name as a queryable field and the OTLP receiver
already parses the wire field — but our two flagship GenAI sources
predate the field and emit only the event.name attribute, so every
such record stores event_name: NULL. Users fall back to body ==
(bitten by #664) or attribute-implicit grouping.
The events semconv points here, not at body. Semconv MUST NOT give
body a value beyond a display message, and event identity queries are
EventName’s job. This RFC makes the backend meet the spec’s intent for
sources that haven’t caught up.
RFC 0037 gets its keying for free. Event-keyed templating (§3.1)
activates on event_name presence; today it never engages for
Claude Code/opencode events. Deriving the field turns their template
handling from body-mining into the designed event-keyed path.
3. Proposed design
At the OTLP decode boundary (ourios-core OTLP conversion, both
protobuf and JSON paths per the RFC0003.6 checklist):
- If
LogRecord.event_nameis set and non-empty, it wins. Theevent.nameattribute, if also present, is stored untouched — no comparison, no correction, no flag (a mismatch between the two is source telemetry, and we preserve it; the read path returns both as received). - If
LogRecord.event_nameis unset or empty and anevent.nameattribute is present with a non-empty string value, the stored record’sevent_nameis set to that string. The attribute remains inattributesverbatim — derivation, not a move. Non-string and empty-stringevent.namevalues derive nothing. - Neither present →
event_namestays NULL, exactly as today.
“Set” is defined identically for both encodings: protobuf cannot
distinguish an absent string field from an empty one (proto3 default),
and RFC0003.6 JSON may spell absence as a missing key, null, or ""
— all three read as unset. An empty string is therefore never a value,
on either side of the derivation, and the two decode paths cannot
diverge (RFC0043.3/.7).
The invariant posture: the OTLP-fidelity rule (preserve / flag / never
correct) is untouched because nothing received is altered or dropped —
the derived field is additive, and a reader comparing stored attributes
against the source’s export sees byte identity. Downstream (mining,
RFC 0037 keying, the DSL field, the RFC 0032 query-schema document) all
consume event_name unchanged; they simply see it populated for more
sources.
4. Alternatives considered
- Do nothing; teach
body ==instead. #664’s fix (RFC 0044) makesbody ==correct, but the semconv is explicit that event identity is the event name’s job; leaving the field NULL keeps the idiomatic query dead for the most important corpus and keeps RFC 0037’s keying inert. - Move the attribute into the field (hoist-and-drop). Violates the fidelity rule — the stored attributes would no longer match what the source exported.
- Collector-side remapping (OTTL). Works per deployment, but every deployment must know to do it; the backend doing the spec’s documented migration once is strictly less operational surface. A deployment that remaps anyway hits rule 1 and nothing double-applies.
5. Acceptance criteria
- RFC0043.1 — the wire field wins
- Given a record with
event_nameset and a differingevent.nameattribute - When it is ingested and read back
- Then the stored
event_nameis the wire field’s value - And the attribute is returned verbatim, unflagged.
- Given a record with
- RFC0043.2 — derivation from the attribute
- Given a record with no
event_nameand anevent.namestring attribute - When it is ingested and read back
- Then
event_nameequals the attribute’s value - And the
event.nameattribute is still present, byte-identical.
- Given a record with no
- RFC0043.3 — both encodings
- Given the RFC0043.2 record encoded as OTLP protobuf and as RFC0003.6 JSON
- When each is ingested
- Then both derive identically.
- RFC0043.4 — non-string derives nothing
- Given an
event.nameattribute whose value is not a string - When ingested
- Then
event_namestays NULL and the attribute is preserved.
- Given an
- RFC0043.5 — the idiomatic query works end-to-end
- Given an ingested attr-only corpus (Claude Code-shaped fixture)
- When
event_name == "claude_code.api_request"runs - Then exactly the api_request records return.
- RFC0043.6 — RFC 0037 keying engages, observably
- Given three attr-only structured records: two sharing an
event.name(with differing body content) and one with a distinctevent.name(RFC 0037 §3.1 keys(severity, scope, event_name)for structured bodies; without derivation all three collapse into the one no-event sentinel) - When mined
- Then the two same-name records carry the same
template_iddespite differing content - And the distinct-name record carries a different
template_id— the separation is the externally visible proof the derived name reached the template key, since the sentinel would have merged all three. (Refined at implementation time: the originally referenced “event-keyed counter” does not exist as a distinct instrument; the id separation is a strictly stronger observable.)
- Given three attr-only structured records: two sharing an
- RFC0043.7 — empty is never a value, in either encoding
- Given records with (a) empty-string wire
event_nameplus anevent.nameattribute, (b) an empty-stringevent.nameattribute only, and (c) JSONnullevent.name— each encoded as protobuf and as RFC0003.6 JSON where representable - When ingested
- Then (a) derives from the attribute, (b) and (c) derive nothing, and the protobuf and JSON results are identical shape-for-shape.
- Given records with (a) empty-string wire
6. Testing strategy
Unit tests at the decode boundary for .1–.4 (both encodings, per the
RFC0003.6 checklist — struct round-trip alone is insufficient);
integration through ingest→query for .5; a miner-level assertion for .6
against a Claude Code-shaped fixture. No new metrics: derivation is not
an anomaly (the anomaly-telemetry rule reserves error.type on the
existing counters for rejections).
7. Open questions
- Fidelity vs. derivation — resolved in §3: derive additively, never mutate received data. The read path returns both.
- Should the RFC 0032 query-schema document call out
event_nameavailability per tenant (it is population-dependent)? Deferred — the field is already listed as queryable.
8. References
- OTel Logs Data Model —
EventNamefield; events semconv (§Body: “MUST NOT define a value for body except … display message”; §Event name: “when users query for a specific event name…”). - RFC 0003 (OTLP receiver) + the RFC0003.6 JSON checklist.
- RFC 0037 (GenAI/structured-event logs) §3.1 event-keyed templates.
- RFC 0044 (template-aware body equality) — the complementary half: this RFC gives event filtering its idiomatic path; 0044 makes the compatibility path correct. #664 is closed by 0044, not this RFC.