Skip to content

fix(bench): handle Body::Structured records in harness + C1 (PR-K4) - #61

Merged
jensholdgaard merged 3 commits into
mainfrom
fix/bench-structured-body
May 29, 2026
Merged

fix(bench): handle Body::Structured records in harness + C1 (PR-K4)#61
jensholdgaard merged 3 commits into
mainfrom
fix/bench-structured-body

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented May 29, 2026

Copy link
Copy Markdown
Owner

Context

Surfaced by triggering the PR-K3 workflow with corpus_dir=crates/ourios-bench/tests/data/otlp: the run failed twice, exposing two latent gaps the plain-text path never exercised.

  1. Harness BenchError::Pipelinetemplates_for() returned no matching leaf for (template_id=4, template_version=1). Real cause: RFC 0001 §6.1 assigns Body::Structured records a sentinel template id outside the Drain tree, so the lookup correctly returns nothing — but the harness assumed every non-lossy record had a leaf.
  2. Parquet writer rejectionattributes / resource_attributes and Body::Structured rows are blocked by the deferred RFC 0005 §3.3 KeyValue / AnyValue canonicalisation PR.

Fixes

Harness + C1 (the surfaces the bench owns)

  • harness::run narrows want_snapshot to BodyKind::String. Structured records get snapshot = None like lossy ones do — no contract violation.
  • c1::record skips BodyKind::Structured from both numerator and denominator. Per RFC 0001 §6.4 / RFC 0003 §6.4, structured reconstruction is the storage-layer AnyValue-bytes round-trip, not template + params, so the template-based equality C1 measures doesn't apply. Structured ≠ lossy (independent axes).
  • RFC 0006 §3.4.2 amended to document the new exclusion alongside the lossy one.

OTLP loader (workarounds for the Parquet writer's canonicalisation gaps)

  • attributes and resource_attributes stripped to empty Vecs in map_log_record. TODO comment points at RFC 0005 §3.3; the loader test pins the strip so the revert PR has to update it (closing the loop).
  • kvlist body record removed from the committed fixture (writer rejects Body::Structured rows for the same reason). Loader coverage for structured-body mapping moved to an inline-synthetic test — the loader still pins the Body::Structured(AnyValue) round-trip per RFC 0003 §6.4; it just doesn't sit in a fixture the writer chokes on.

Local end-to-end (the failure mode now fixed)

$ ourios-bench --corpus crates/ourios-bench/tests/data/otlp ...
corpus crates/ourios-bench/tests/data/otlp — 3 line(s), 1 file(s), 924 raw byte(s)
  A1 compression: ourios 0.001× vs zstd-19 2.780× → delta 0.000× — FAIL  (expected on tiny corpus)
  C1 reconstruction: 1.000000 (3/3 non-lossy rows) — PASS
  C2 convergence: ratio n/a — ABSTAIN (corpus < 1 M lines)

Test plan

  • cargo fmt --all --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test --all-features286 passed / 19 ignored (+1 vs main: the colocated c1::tests::structured_body_record_is_excluded_from_denominator)
  • mdbook build
  • Local ourios-bench --corpus crates/ourios-bench/tests/data/otlp — succeeds end-to-end, C1 PASS

Follow-ups (out of scope here)

The real unblocker for richer OTLP corpora is the RFC 0005 §3.3 canonicalisation PR — KeyValue canonical-JSON encoding + Body::Structured-bytes canonicalisation in the Parquet writer. Once it lands, three things flip back: the loader stops stripping attributes, the kvlist record returns to the fixture, and the harness/C1 exclusions stay (structured reconstruction is still semantically out of scope for C1, but at least it'd be Parquet-writable for A1).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Structured-body records no longer count toward the C1 non-lossy reconstruction denominator and no longer trigger template snapshot capture.
    • OTLP/JSONL ingestion now strips record attributes for consistent canonical handling; sample corpus reduced accordingly.
  • Tests

    • Added and updated tests to verify structured-body exclusion and adjusted corpus expectations.
  • Documentation

    • C1 methodology and harness behavior updated to reflect the new structured-body handling.

Review Change Stack

Surfaced by the PR-K3 OTLP-fixture CI trigger: the harness crashed
with a contract violation, and the Parquet writer rejected the
fixture's kvlist body + non-empty attributes. Both gaps trace to
the deferred RFC 0005 §3.3 canonicalisation PR — fixed here for
the surfaces the bench owns, documented for the surfaces it
doesn't.

**Harness (the immediate contract violation):** RFC 0001 §6.1
assigns `Body::Structured` records a sentinel template id outside
the Drain tree, so `cluster.templates_for(...)` correctly returns
no leaf for the sentinel — but the harness's `want_snapshot`
predicate assumed every non-lossy / non-`NO_TEMPLATE` record had
one, raising `BenchError::Pipeline`. The predicate now narrows to
`BodyKind::String`; structured records get `snapshot = None` like
lossy ones do.

**C1 (symmetric):** structured records are excluded from C1's
denominator too. Per RFC 0001 §6.4 / RFC 0003 §6.4, structured
reconstruction is the storage-layer `AnyValue`-bytes round-trip,
*not* template + params, so the template-based equality C1
measures doesn't apply to them. Structured ≠ lossy (independent
axes — a structured record can be high-confidence).

**OTLP loader (bench-side workarounds for the Parquet writer's
canonicalisation gaps):**

- `attributes` / `resource_attributes` stripped to empty `Vec`s
  in `map_log_record`, with a TODO pointing at RFC 0005 §3.3 and
  the loader test pinning the strip so the revert PR has to
  update it (closing the loop). The receiver-amendment PR
  restores the 1:1 mapping when canonicalisation lands.
- The kvlist body record removed from the committed fixture so
  the workflow can actually run on
  `crates/ourios-bench/tests/data/otlp` today (the writer rejects
  `Body::Structured` rows for the same RFC 0005 §3.3 reason).
  Loader coverage of the structured-body mapping moved to an
  inline-synthetic test, so the loader still pins the
  `Body::Structured(AnyValue)` round-trip per RFC 0003 §6.4 — it
  just doesn't sit in a fixture the writer chokes on.

**RFC 0006 §3.4.2 amended** to document the C1 structured-body
exclusion alongside the existing lossy exclusion.

**Test plan:** ran `ourios-bench --corpus
crates/ourios-bench/tests/data/otlp` locally; result was 3
records, **C1 PASS (3/3 non-lossy)**, A1 FAIL (expected on a
small corpus), C2 ABSTAIN. +1 colocated test
`c1::tests::structured_body_record_is_excluded_from_denominator`
pins the C1 exclusion. fmt + clippy + 286 tests + mdbook all green.

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

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR excludes BodyKind::Structured records from C1 non-lossy reconstruction metrics, refines the harness to capture snapshots only for String bodies, strips attributes from OTLP-loaded records, and updates tests, fixtures, and RFC documentation accordingly.

Changes

Structured Body Exclusion and OTLP Refactor

Layer / File(s) Summary
C1 Structured Body Exclusion Logic
crates/ourios-bench/src/c1.rs
Imports BodyKind, adds early return in C1Accumulator::record() when emitted.body_kind is Structured to exclude those records from non-lossy denominator tracking and reconstruction comparison. New unit test structured_body_record_is_excluded_from_denominator asserts structured records have non_lossy_total and non_lossy_reconstruct_ok at 0 while all_total remains incremented.
Harness Snapshot Capture Gate
crates/ourios-bench/src/harness.rs
Imports BodyKind and refines want_snapshot predicate to capture template snapshots only for non-lossy, non-NO_TEMPLATE records with body_kind == BodyKind::String, excluding structured and other body kinds from snapshot management.
OTLP Corpus Loader Attribute Stripping
crates/ourios-bench/src/corpus.rs
Removes resource-attributes hoisting from ingest_otlp_jsonl, updates map_log_record signature to drop resource-attributes parameter, and implements attribute stripping: both attributes and resource_attributes are now set to empty vectors while preserving dropped_attributes_count from wire records.
Test Expectations, Fixtures, and Documentation
crates/ourios-bench/src/corpus.rs, crates/ourios-bench/tests/data/otlp/sample.jsonl, docs/rfcs/0006-bench-harness.md
OTLP test assertions updated to require empty attributes/resource_attributes vectors; sample corpus record-count assertion adjusted; structured-body mapping test rewritten to use inline JSONL fixture instead of file-based shape; third log entry with kvlistValue startup payload removed from sample fixture; RFC amended to document C1 methodology excluding BodyKind::Structured from the non-lossy denominator.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • jensholdgaard/ourios#56: Modifies C1 reconstruction diagnostics; interacts with when mismatch diagnostics are produced relative to structured-body exclusion.
  • jensholdgaard/ourios#58: Earlier OTLP/JSON loader changes that began emitting Body::Structured for non-string wire bodies; this PR updates downstream metrics and loader behavior accordingly.
  • jensholdgaard/ourios#51: Prior C1 measurement changes related to template snapshot and denominator logic that this PR further refines to exclude structured bodies.

Poem

🐰 I hop through records, nimble and spry,
Structured bodies politely pass me by.
Strings keep their snapshots, templates delight,
Attributes stripped clean in the OTLP light,
C1 counts stay tidy through day and night.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically identifies the main change: handling Body::Structured records in the harness and C1 metric calculation.
Description check ✅ Passed The description comprehensively covers context, fixes, rationale, test results, and follow-ups, though it lacks explicit checklist confirmation.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/bench-structured-body

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

This PR updates the bench harness to handle OTLP structured bodies and current Parquet writer limitations without failing the end-to-end bench path.

Changes:

  • Skips template snapshot lookup and C1 denominator accounting for BodyKind::Structured records.
  • Temporarily strips OTLP log/resource attributes in the bench loader until RFC 0005 §3.3 canonicalization lands.
  • Removes the structured-body row from the committed OTLP fixture while preserving structured-body loader coverage via an inline synthetic test.

Reviewed changes

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

Show a summary per file
File Description
docs/rfcs/0006-bench-harness.md Documents the structured-body C1 exclusion and matching harness snapshot behavior.
crates/ourios-bench/tests/data/otlp/sample.jsonl Keeps the committed OTLP fixture limited to Parquet-writable string-body records.
crates/ourios-bench/src/harness.rs Avoids templates_for() snapshot lookup for structured-body records.
crates/ourios-bench/src/corpus.rs Strips attributes/resource attributes for now and adds inline structured-body mapping coverage.
crates/ourios-bench/src/c1.rs Excludes structured-body records from C1 reconstruction accounting and adds regression coverage.

💡 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: 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-bench/src/corpus.rs`:
- Around line 427-442: Update the function/module contract comments to reflect
that attributes and resource_attributes are currently stripped (assigned to
Vec::new()) rather than preserved 1:1; locate the docstrings around the code
that mentions 1:1 attribute/resource mapping and change them to state that
attributes and resource attributes are intentionally dropped (and reference
dropped_attributes_count is preserved) until canonical-JSON KeyValue support is
added, and mention that the loader test pins this behavior so changes must
update the test.

In `@crates/ourios-bench/src/harness.rs`:
- Around line 138-154: The snapshot predicate was tightened to require
BodyKind::String (let want_snapshot = !record.lossy_flag && record.template_id
!= NO_TEMPLATE && matches!(record.body_kind, BodyKind::String)), so update the
run rustdoc examples and the non_lossy_callbacks_carry_a_template_snapshot test
to reflect that non-lossy records with non-String BodyKind now produce snapshot
= None; change assertions to check record.body_kind == BodyKind::String before
expecting Some(snapshot), update any explanatory text in the rustdoc to describe
the new BodyKind gate, and add/relocate unit tests next to harness.rs verifying
both paths (String → Some, Structured/other → None) as per the crate's colocated
test guideline.
🪄 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: e143df83-72b4-467f-8265-0c513e885f1a

📥 Commits

Reviewing files that changed from the base of the PR and between 3cc985b and 5c36e47.

📒 Files selected for processing (5)
  • crates/ourios-bench/src/c1.rs
  • crates/ourios-bench/src/corpus.rs
  • crates/ourios-bench/src/harness.rs
  • crates/ourios-bench/tests/data/otlp/sample.jsonl
  • docs/rfcs/0006-bench-harness.md
💤 Files with no reviewable changes (1)
  • crates/ourios-bench/tests/data/otlp/sample.jsonl

Comment thread crates/ourios-bench/src/corpus.rs
Comment thread crates/ourios-bench/src/harness.rs
…R-K4)

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: 1

🤖 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-bench/src/corpus.rs`:
- Around line 407-413: Update the ingest_otlp_jsonl rustdoc to match the current
stripped-attributes contract: clarify that ingest_otlp_jsonl does not perform a
strict 1:1 mapping of the OTLP envelope into OtlpLogRecord but instead strips
per-record attributes/resource_attributes (consistent with map_log_record and
the module docs) until RFC 0005 §3.3 canonicalisation is applied; reference
ingest_otlp_jsonl and map_log_record by name and explicitly mention attributes
and resource_attributes behavior so the documentation no longer claims a 1:1
mapping.
🪄 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: adfca398-4ad2-4dd3-9754-9eebb85b90a9

📥 Commits

Reviewing files that changed from the base of the PR and between 5c36e47 and 6a632e6.

📒 Files selected for processing (2)
  • crates/ourios-bench/src/corpus.rs
  • crates/ourios-bench/src/harness.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/ourios-bench/src/harness.rs

Comment thread crates/ourios-bench/src/corpus.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 5 changed files in this pull request and generated 2 comments.

Comment thread crates/ourios-bench/src/c1.rs Outdated
Comment thread crates/ourios-bench/src/harness.rs
…R-K4)

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.

🧹 Nitpick comments (1)
crates/ourios-bench/src/harness.rs (1)

365-372: ⚡ Quick win

Tighten test assertions to exact counts.

The fixture contains exactly 2 records (1 string-body, 1 structured-body), so the assertions can be more precise. Current >= 1 assertions would pass even if both records received snapshots or neither did, though the structured_seen == 1 + line 352 assertion provides partial coverage.

✨ Proposed refinement
-        assert!(
-            snapshots_present >= 1,
-            "the string-body record gets a snapshot"
-        );
-        assert!(
-            snapshots_absent >= 1,
-            "the structured-body record's `None` is counted"
-        );
+        assert_eq!(
+            snapshots_present, 1,
+            "exactly one record (the string body) gets a snapshot"
+        );
+        assert_eq!(
+            snapshots_absent, 1,
+            "exactly one record (the structured body) gets None"
+        );
🤖 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-bench/src/harness.rs` around lines 365 - 372, Tighten the two
snapshot-count assertions to require exact counts: change the assert! checks on
snapshots_present and snapshots_absent to assert_eq!(snapshots_present, 1,
"...") and assert_eq!(snapshots_absent, 1, "...") (keeping or updating the
message) so the test enforces that exactly one string-body record and exactly
one structured-body `None` snapshot were recorded; locate the variables
snapshots_present and snapshots_absent in the same test around the
structured_seen assertion and replace the >= 1 checks with equality checks.
🤖 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.

Nitpick comments:
In `@crates/ourios-bench/src/harness.rs`:
- Around line 365-372: Tighten the two snapshot-count assertions to require
exact counts: change the assert! checks on snapshots_present and
snapshots_absent to assert_eq!(snapshots_present, 1, "...") and
assert_eq!(snapshots_absent, 1, "...") (keeping or updating the message) so the
test enforces that exactly one string-body record and exactly one
structured-body `None` snapshot were recorded; locate the variables
snapshots_present and snapshots_absent in the same test around the
structured_seen assertion and replace the >= 1 checks with equality checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 82ba9f2d-e080-46cb-b7c1-4657b020584d

📥 Commits

Reviewing files that changed from the base of the PR and between 6a632e6 and ef97bf8.

📒 Files selected for processing (3)
  • crates/ourios-bench/src/c1.rs
  • crates/ourios-bench/src/corpus.rs
  • crates/ourios-bench/src/harness.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/ourios-bench/src/corpus.rs
  • crates/ourios-bench/src/c1.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 5 changed files in this pull request and generated no new comments.

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