Skip to content

feat(parquet): implement RFC 0005 §3.3 canonical-JSON encoding (PR-L1) - #62

Merged
jensholdgaard merged 5 commits into
mainfrom
feat/parquet-otlp-canonical-json
May 29, 2026
Merged

feat(parquet): implement RFC 0005 §3.3 canonical-JSON encoding (PR-L1)#62
jensholdgaard merged 5 commits into
mainfrom
feat/parquet-otlp-canonical-json

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented May 29, 2026

Copy link
Copy Markdown
Owner

Summary

Lands the long-deferred RFC 0005 §3.3 encoder/decoder so the storage layer's three canonical-JSON columns (attributes, resource_attributes, and body when body_kind = Structured) stop rejecting records with non-empty OTLP envelopes.

Unblocks (in order):

  1. The bench loader's 1:1 OTLP envelope mapping — PR-L2 (this PR's direct follow-up) reverts the strip and restores the kvlist record to the committed OTLP fixture.
  2. Richer OTLP corpora in CI — operators can point bench.yml at a real fileexporter output without losing attributes.
  3. One of the two gates on the production OTLP receiver — the Parquet data path is now ready; WAL-before-ack per RFC 0003 §6.5 + CLAUDE.md §3.4 remains the other.

Design

ourios_core::otlp::canonical is a thin wrapper over opentelemetry-proto's with-serde derives — the same spec mapping rotel's OTLP HTTP receiver uses on ExportLogsServiceRequest. Keeps the OTLP-JSON spec single-sourced through opentelemetry-proto rather than a hand-rolled encoder that could drift.

Four pub fns, all Result<…, CanonicalJsonError>:

encode_any_value(&AnyValue) -> Vec<u8>
decode_any_value(&[u8])     -> AnyValue
encode_attributes(&[KeyValue]) -> Vec<u8>
decode_attributes(&[u8])       -> Vec<KeyValue>

Encoder failure is a wire-decode bug (e.g. an f64::NAN in a DoubleValue) — the production receiver narrows that at wire decode, so the fallback path in ingest_structured (debug rendering + lossy_flag = true) is defence in depth, not a hot path.

Touch points

Crate What
ourios-core New otlp::canonical module + serde_json dep + 4 round-trip tests (every AnyValue variant, attribute lists, encoder determinism for RFC0006.7, empty-input sentinel)
ourios-miner ingest_structured replaces format!("{any_value:?}") placeholder with encode_any_value; encoder-error fallback flags the row lossy so the reader returns the debug bytes verbatim
ourios-parquet (writer) append_attributes routes non-empty input through encode_attributes; structured-body rejection gone; AttributesNotYetEncodedAttributeEncode { source }; two deferred-error tests rewritten as encode → decode round-trip tests
ourios-parquet (reader) decode_attributes consumes non-empty columns; empty short-circuit mirrors the writer; AttributesNotYetDecodedAttributeDecode { row_index, source }

Test plan

  • cargo fmt --all --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test --all-features291 passed / 19 ignored (+6 vs main: 4 canonical-helper tests + 2 round-trip tests replacing the old deferred-error tests)
  • mdbook build
  • E2E: ran ourios-bench on a synthetic single-line LogsData whose body is a kvlistValue (the exact path that rejected before this PR). Writer accepts the structured row, Parquet file lands, bench summary reports A1/C1/C2 normally. C1 = 1.000000 (0/0 non-lossy strings — structured rows correctly excluded from the denominator).

Follow-up

  • PR-L2 (next, blocked on this): bench loader reverts the attributes / resource_attributes strip + restores the kvlist record to the committed OTLP fixture + RFC 0006 §3.1 amendment revert.
  • The production OTLP receiver (RFC 0003 §6.5) is the other gate behind WAL-before-ack — still a multi-RFC arc (an ourios-wal RFC + implementation, then RFC 0003 → specified → implementation). This PR closes the storage-side gap.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Canonical JSON encode/decode for structured log attributes and bodies for deterministic, round-trip-safe storage.
  • Improvements

    • Structured bodies are stored verbatim as canonical JSON (not debug text); empty attributes encode to "[]".
    • Reader and writer now emit/consume canonical JSON for attribute-like columns and structured bodies.
  • Bug Fixes

    • More precise error reporting on attribute decode/encode failures with row/column context.
  • Tests

    • Added tests for round-trip correctness, determinism, and preserved structured bodies.

Review Change Stack

Lands the long-deferred encoder/decoder for the storage layer's
three §3.3 columns (`attributes`, `resource_attributes`, and `body`
when `body_kind = Structured`) so the writer / reader stop rejecting
records with non-empty OTLP envelopes. Unblocks:

- the bench loader's 1:1 OTLP envelope mapping (PR-L2 follow-up
  reverts the strip and restores the kvlist fixture record),
- richer OTLP corpora in CI (operators can point `bench.yml` at
  fileexporter output without losing attributes),
- one of the two gates on the production OTLP receiver (the
  Parquet data path is now ready; WAL-before-ack per RFC 0003
  §6.5 + CLAUDE.md §3.4 remains the other).

**Engine** (`ourios_core::otlp::canonical`):

- Thin `serde_json::to_vec` / `from_slice` wrappers over
  `opentelemetry-proto`'s `with-serde` derives — the same spec
  mapping rotel's OTLP HTTP receiver uses on
  `ExportLogsServiceRequest`. Keeps the OTLP-JSON spec
  single-sourced through `opentelemetry-proto` rather than a
  hand-rolled encoder that could drift.
- Round-trip tests: every `AnyValue` variant
  (string / int / double / bool / bytes / array / kvlist)
  encode → decode round-trips at the `AnyValue` level.
  Determinism test: re-encoding the same in-memory tree
  produces byte-identical bytes (RFC0006.7 carries through
  the canonicalisation boundary). Empty `Vec<KeyValue>`
  encodes to `[]`.
- `serde_json` added to `ourios-core`'s deps (smallest std
  feature set).

**Miner** (`ourios-miner::cluster::ingest_structured`):

- The `format!("{any_value:?}")` Debug-rendering placeholder
  is gone; `MinedRecord.body` for structured rows now carries
  the canonical-JSON bytes the §3.3 column wants. Encoder
  fallback (debug rendering + `lossy_flag = true`) survives
  pathological inputs the receiver should narrow at wire
  decode (e.g. `f64::NAN`).

**Writer** (`ourios-parquet::record_batch`):

- `append_attributes` routes non-empty input through
  `canonical::encode_attributes`. Empty case still
  short-circuits to literal `[]` (no per-row allocation on the
  clean-attach hot path).
- The `StructuredBodyNotYetCanonical` rejection is gone; the
  writer appends the miner-encoded body bytes verbatim.
- `BatchError::AttributesNotYetEncoded` →
  `BatchError::AttributeEncode { column, count, source }` —
  fires only on actual encoder failure, not on the presence
  of non-empty input.
- Two existing deferred-error tests rewritten as round-trip
  tests asserting the encoded bytes decode back to the
  in-memory `Vec<KeyValue>` / `AnyValue`. The structured-body
  test asserts the producer's canonical bytes land verbatim
  in the column.

**Reader** (`ourios-parquet::reader`):

- `decode_attributes` consumes non-empty columns to recover
  `Vec<KeyValue>`. Empty case short-circuits (mirrors the
  writer).
- `ReaderError::AttributesNotYetDecoded` →
  `ReaderError::AttributeDecode { column, row_index, source }`
  — fires only on actual decoder failure (file corruption /
  foreign producer).

**Test plan:**

- `cargo fmt --all --check` — clean.
- `cargo clippy --all-targets --all-features -- -D warnings` — clean.
- `cargo test --all-features` — 291 passed / 19 ignored.
- `mdbook build` — clean.
- E2E: ran `ourios-bench` on a synthetic single-line
  `LogsData` containing a `kvlistValue` body (the path that
  rejected before PR-L1). Writer accepts the structured row,
  Parquet file lands, bench summary reports A1/C1/C2
  normally. C1 PASS (0/0 non-lossy strings, structured rows
  correctly excluded from the denominator).

Follow-up PR-L2 reverts the bench loader's `attributes`
strip and restores the kvlist record to the committed OTLP
fixture.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jensholdgaard
jensholdgaard requested a review from Copilot May 29, 2026 11:45
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7d53b3ce-40c4-4a96-9dca-adfa18bf4f8f

📥 Commits

Reviewing files that changed from the base of the PR and between 75fc8b1 and 9c3bacf.

📒 Files selected for processing (1)
  • .gitignore
✅ Files skipped from review due to trivial changes (1)
  • .gitignore

📝 Walkthrough

Walkthrough

This PR implements RFC 0005 §3.3 canonical JSON encoding for OTLP structured-log storage. A new canonical module in ourios-core provides serde_json-based encode/decode functions for AnyValue and KeyValue attributes. The miner encodes structured bodies using this library (panic on encode failure), the Parquet writer encodes attributes to canonical JSON strings and accepts structured bodies verbatim, and the reader decodes those strings back during batch deserialization.

Changes

Canonical JSON encoding and storage

Layer / File(s) Summary
Canonical JSON library in ourios-core
crates/ourios-core/Cargo.toml, crates/ourios-core/src/otlp.rs
New canonical module provides CanonicalJsonError, encode_any_value/decode_any_value, and encode_attributes/decode_attributes functions with unit tests. Adds serde_json dependency with std feature.
Producer structured body encoding
crates/ourios-miner/src/cluster.rs
MinerCluster::ingest_structured encodes AnyValue body to canonical JSON and stores UTF‑8 bytes in MinedRecord.body; encoding failures now panic (no debug fallback).
Parquet writer batch encoding
crates/ourios-parquet/src/record_batch.rs
Arrow batch builder encodes non-empty attributes and resource_attributes to canonical JSON via encode_attributes, short-circuits "[]" for empty inputs, and appends producer-provided canonical body bytes verbatim. Error model replaced by BatchError::AttributeEncode. Tests updated to assert round-trip decoding and body byte preservation.
Parquet reader batch decoding
crates/ourios-parquet/src/reader.rs
batch_to_mined_records decodes canonical JSON attributes and resource_attributes into KeyValue lists, short-circuits "[]" to empty vectors, and returns ReaderError::AttributeDecode { column, row_index, source } on decode failure. Documentation and error source propagation updated.

Sequence Diagram(s)

sequenceDiagram
  participant Producer as ourios-miner::ingest_structured
  participant Canon as ourios_core::otlp::canonical
  participant Writer as Parquet Writer::record_batch
  participant Reader as Parquet Reader::batch_to_mined_records

  Producer->>Canon: encode_any_value(body)
  Canon-->>Producer: canonical JSON bytes or CanonicalJsonError
  Producer->>Writer: MinedRecord with canonical body bytes
  Writer->>Canon: encode_attributes(attrs)
  Canon-->>Writer: canonical JSON bytes or CanonicalJsonError
  Writer->>Reader: Arrow batch with JSON columns
  Reader->>Canon: decode_attributes(json_string)
  Canon-->>Reader: Vec<KeyValue> or CanonicalJsonError
  Reader->>Reader: construct MinedRecord with decoded attributes
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • jensholdgaard/ourios#45: Related changes to Parquet reader handling and error behavior for attribute canonical-JSON decoding.

Poem

🐰 I hop through bytes both small and grand,

I fold key‑values with a careful hand,
Miner bakes the body crisp and true,
Writer stores the JSON just like you,
Reader fetches pairs — a round‑trip through!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly identifies the main change: implementing RFC 0005 §3.3 canonical-JSON encoding for the Parquet storage layer, which aligns with the primary objective across all modified crates.
Description check ✅ Passed Description is comprehensive and covers all template sections: Summary explains the purpose and impact, Related links the RFC and follow-ups, and Checklist confirms completion of fmt, clippy, tests, and documentation requirements.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/parquet-otlp-canonical-json

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements RFC 0005 §3.3 OTLP-canonical-JSON encoding/decoding so the storage layer can persist non-empty attributes, resource_attributes, and structured body columns instead of returning deferred-encoding errors. A new ourios_core::otlp::canonical module wraps opentelemetry-proto's with-serde derives to provide encode/decode helpers; the Parquet writer/reader and the miner's structured-body path are wired through them, and the previous "not yet encoded/decoded" error variants are replaced with real encode/decode error variants that carry the underlying serde_json source.

Changes:

  • Add ourios_core::otlp::canonical (encode/decode for AnyValue and Vec<KeyValue>) plus serde_json dependency, with round-trip and determinism tests.
  • Replace AttributesNotYetEncoded / StructuredBodyNotYetCanonical / AttributesNotYetDecoded with real AttributeEncode / AttributeDecode error variants in ourios-parquet; route non-empty attributes through the canonical helpers and append structured bodies verbatim.
  • Update miner ingest_structured to populate body with canonical-JSON bytes (with a lossy_flag = true debug-rendering fallback on encoder failure).

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
crates/ourios-core/Cargo.toml Add serde_json dependency for canonical-JSON helpers.
crates/ourios-core/src/otlp.rs Introduce canonical submodule with encode_/decode_any_value, encode_/decode_attributes, CanonicalJsonError, and tests.
crates/ourios-miner/src/cluster.rs Encode structured AnyValue via canonical helper; fallback to debug rendering + lossy_flag = true on encoder failure.
crates/ourios-parquet/src/record_batch.rs Encode non-empty attribute lists; drop structured-body rejection; rename error variant; update tests to round-trip.
crates/ourios-parquet/src/reader.rs Decode non-empty attribute columns; replace AttributesNotYetDecoded with AttributeDecode carrying row index and serde source.
Cargo.lock Reflect serde_json dep on ourios-core.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/ourios-parquet/src/record_batch.rs (1)

326-339: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject BodyKind::Structured rows when body is missing.

Line 336 currently allows (BodyKind::Structured, None) and writes a NULL body. That silently persists unreconstructable structured rows.

💡 Proposed fix
 pub enum BatchError {
+    MissingBodyForStructured,
     ...
 }

 impl fmt::Display for BatchError {
     match self {
+        Self::MissingBodyForStructured => write!(
+            f,
+            "structured record has body = None; RFC 0005 §3.3 structured rows must carry canonical body bytes"
+        ),
         ...
     }
 }

 impl std::error::Error for BatchError {
     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
         match self {
             ...
+            Self::MissingBodyForStructured => None,
         }
     }
 }

-        match r.body.as_deref() {
-            Some(s) => self.body.append_value(s.as_bytes()),
-            None => self.body.append_null(),
-        }
+        match (r.body_kind, r.body.as_deref()) {
+            (BodyKind::Structured, None) => return Err(BatchError::MissingBodyForStructured),
+            (_, Some(s)) => self.body.append_value(s.as_bytes()),
+            (_, None) => self.body.append_null(),
+        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ourios-parquet/src/record_batch.rs` around lines 326 - 339, The match
on r.body currently treats None the same for all BodyKind variants; change the
branch so that when r.body is None and r.body_kind == BodyKind::Structured you
reject the row instead of writing a NULL — e.g. detect this condition in the
code path that contains the match (referencing r.body, r.body_kind, and the
current self.body.append_null() call) and return or propagate an error (or
otherwise skip/flag the record) with a clear message indicating a missing body
for BodyKind::Structured; for other BodyKind values keep the existing behavior
(append_value or append_null).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ourios-parquet/src/reader.rs`:
- Around line 357-373: The AttributeDecode errors are using the batch-local
index i (in the ReaderError::AttributeDecode for columns::ATTRIBUTES and
columns::RESOURCE_ATTRIBUTES), causing row_index to reset per batch; compute and
use the file-global row index (e.g. row_base + i or a single file_row_index
derived from the batch start offset you already have in the reader) and replace
i with that file-global index in both error constructions so multi-batch files
report correct absolute row numbers.

---

Outside diff comments:
In `@crates/ourios-parquet/src/record_batch.rs`:
- Around line 326-339: The match on r.body currently treats None the same for
all BodyKind variants; change the branch so that when r.body is None and
r.body_kind == BodyKind::Structured you reject the row instead of writing a NULL
— e.g. detect this condition in the code path that contains the match
(referencing r.body, r.body_kind, and the current self.body.append_null() call)
and return or propagate an error (or otherwise skip/flag the record) with a
clear message indicating a missing body for BodyKind::Structured; for other
BodyKind values keep the existing behavior (append_value or append_null).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 04775825-a36b-49ba-b099-2bff7cf6b819

📥 Commits

Reviewing files that changed from the base of the PR and between f48c16d and 9118802.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • crates/ourios-core/Cargo.toml
  • crates/ourios-core/src/otlp.rs
  • crates/ourios-miner/src/cluster.rs
  • crates/ourios-parquet/src/reader.rs
  • crates/ourios-parquet/src/record_batch.rs

Comment thread crates/ourios-parquet/src/reader.rs
… (PR-L1)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 7 changed files in this pull request and generated 2 comments.

Comment thread crates/ourios-miner/src/cluster.rs Outdated
Comment thread crates/ourios-parquet/src/reader.rs
… (PR-L1)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ourios-miner/src/cluster.rs`:
- Line 1683: The current assignment rec.body =
Some(String::from_utf8(bytes).expect("...")) changed the structured-body
contract but tests only check body.is_some(); add a colocated unit test in the
same file that constructs a non-trivial AnyValue, runs the code path that
produces rec (the code that sets rec.body in cluster.rs), and asserts rec.body
== "<expected_canonical_json>" (the exact canonical JSON string for that
AnyValue) to prevent debug/pretty-printed JSON regressions; name the test e.g.,
test_canonical_json_body_for_anyvalue and place it next to the code that assigns
rec.body so it runs as part of the module tests.
- Around line 1671-1678: The ingest path currently panics on canonical-JSON
encoding failure in MinerCluster::ingest_structured by calling
encode_any_value(...).unwrap_or_else(|e| panic!(...)); change this to a
recoverable error flow (return/propagate a Result or log and mark the record as
failed rather than aborting the process) so encoding failures do not abort the
process, and update the ingest_structured call-sites to handle the new Result.
Add a colocated unit test that constructs a structured record, computes the
expected canonical bytes via
ourios_core::otlp::canonical::encode_any_value(any_value) and asserts the
miner-produced rec.body equals those exact bytes (ensuring the emitted body is
OTLP-canonical JSON), and include a regression case that would have triggered
the previous panic to ensure the new code returns/handles an error instead of
panicking.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0dab3466-0b20-4c17-acfe-ab4adeafa5b8

📥 Commits

Reviewing files that changed from the base of the PR and between bbb28ef and da61269.

📒 Files selected for processing (1)
  • crates/ourios-miner/src/cluster.rs

Comment thread crates/ourios-miner/src/cluster.rs Outdated
Comment thread crates/ourios-miner/src/cluster.rs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 7 changed files in this pull request and generated 1 comment.

Comment thread .claude/scheduled_tasks.lock Outdated
… (PR-L1)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 7 changed files in this pull request and generated 1 comment.

Comment thread .claude/scheduled_tasks.lock Outdated
… (PR-L1)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 7 changed files in this pull request and generated no new comments.

@jensholdgaard
jensholdgaard merged commit 618d8c2 into main May 29, 2026
10 checks passed
jensholdgaard added a commit that referenced this pull request May 29, 2026
…2) (#63)

PR-K4 stripped `attributes` / `resource_attributes` in the bench
loader and removed the kvlist record from the committed OTLP
fixture, both as workarounds for the deferred RFC 0005 §3.3
canonicalisation. PR-L1 (#62) landed the canonicalisation
encoders/decoders end-to-end, so the workarounds can go:

- `map_log_record` no longer empties `attributes` /
  `resource_attributes`; both fields map verbatim from the
  wire per RFC 0003 §6.6. The resource-attrs per-`ResourceLogs`
  hoist is restored alongside.
- `crates/ourios-bench/tests/data/otlp/sample.jsonl` regains
  its kvlist body record; the fixture is now 3 LogsData lines
  / 4 records (1 + 2 + 1) as PR-K2 originally shipped it.
- Loader test reverts: `loads_otlp_corpus_envelope_one_to_one`
  asserts `first.attributes.len() == 1` and
  `first.resource_attributes.len() == 2` again, and the
  structured-body test reads the fixture's 4th record (the
  inline-synthetic workaround test goes away too).
- Module / `ingest_otlp_jsonl` / `map_log_record` rustdocs lose
  the "stripped pending §3.3" caveats now that the writer
  carries the envelope through.

Local end-to-end on the restored fixture: 4 records ingest +
write + read cleanly. C1 = 1.000000 (3/3 non-lossy strings;
the kvlist record is correctly excluded from C1's denominator
per RFC 0001 §6.4 — that exclusion landed in PR-K4 and stays).

Test plan: cargo fmt / clippy / test --all-features (292
passed / 19 ignored, same count: -1 inline-synthetic test, +1
fixture-restored assertion). `mdbook build` clean.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants