Skip to content

r2il: fix lossy v1 sink artifacts, add facts/residuals reader - #102

Merged
AdaWorldAPI merged 1 commit into
mainfrom
claude/r2il-pr2-facts-reader
Aug 18, 2026
Merged

r2il: fix lossy v1 sink artifacts, add facts/residuals reader#102
AdaWorldAPI merged 1 commit into
mainfrom
claude/r2il-pr2-facts-reader

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

What

Continues PR2 (.claude/plans/r2il-behavioral-ir-v1.md) after PR #101 shipped
the RefinedTruthSink trait + OfflineSink + the §12 corpus-profile finding.

While building the reader for sink.rs's TSV artifacts, found that the v1
writer (merged in #101) was write-only and lossy:

  • The residuals TSV wrote only reason.as_str() plus the three facet
    coordinates — dropping ResidualReason's per-variant typed payload
    entirely, plus ResidualFact::at_prefix and ResidualFact::provenance.
  • The facts TSV omitted FactProvenance.value (the value_id column).

No reader ever existed to catch this — v1 shipped without a round-trip test.

What changed

  • Bumped FACTS_VERSION 1→2 (added value_id column) and
    RESIDUALS_VERSION 1→2 (widened 4→14 columns: typed reason payload, the
    FacetPrefix variant, and the full FactProvenance tuple) — additive per
    STAGED-CODEGEN-GUIDE.md §4 (append columns, never reorder, version the
    header).
  • Added read_facts / read_residuals plus the shared codecs both facts and
    residuals need (provenance_cols/parse_provenance_cols,
    facet_cols/parse_facet_cols, prefix_cols/parse_prefix_cols,
    reason_payload_cols/parse_reason, concern_from_str,
    fact_kind_from_str, facet_from_raw).
  • Added OpTag::parse — the exact inverse of as_str() (one arm per
    variant, None on anything unrecognized), needed so a residual reason's
    opcode payload can round-trip through the TSV. Named parse rather than
    from_str to avoid clippy's should_implement_trait lint against
    std::str::FromStr's canonical shape.
  • New round-trip tests, both disable-run verified (mutated the write or
    parse path, confirmed red, restored, confirmed green):
    read_facts_round_trips_every_field_including_value_id,
    read_facts_refuses_an_unknown_opcode_rather_than_guessing,
    read_residuals_round_trips_a_two_opcode_payload_reason,
    read_residuals_round_trips_a_payload_free_reason_with_no_facet.

Not in this slice

Per PR2's own incremental scoping (see #101): DTO/codebook wiring beyond
what facet.rs already has, the round-trip reconstruction oracle (R2IL →
routes → semantic-equivalent R2IL — SPO is explicitly NOT this oracle per
the plan), and the optional SPO projection of semantic facts (calls/objects
only).

Test plan

  • cargo test -p ruff_r2il — 40 lib + 12 integration tests, all green
  • cargo clippy -p ruff_r2il --all-targets -- -D warnings — clean
  • cargo fmt --check — clean
  • Two disable-run verifications on the new round-trip tests (mutate →
    red → restore → green)
  • uv run --only-group dev prek run --files <changed files> — clean

🤖 Generated with Claude Code


Generated by Claude Code

The v1 sink shipped in PR #101 wrote a lossy artifact: the residuals TSV
carried only reason.as_str() plus the three facet coordinates, dropping
ResidualReason's per-variant typed payload, ResidualFact::at_prefix, and
ResidualFact::provenance entirely. The facts TSV likewise omitted
FactProvenance.value. No reader existed for either, so this was never
caught by round-tripping — v1 was write-only.

Widen both schemas additively (append columns, never reorder, per this
crate's own STAGED-CODEGEN-GUIDE.md §4 convention) and bump both version
headers:
- FACTS_VERSION 1->2: add value_id column.
- RESIDUALS_VERSION 1->2: widen from 4 columns to 14, carrying the typed
  reason payload (two opcode/u32/usize slots), the FacetPrefix variant,
  and the full FactProvenance tuple.

Add read_facts/read_residuals plus the shared codecs both facts and
residuals need (provenance_cols/parse_provenance_cols, facet_cols/
parse_facet_cols, prefix_cols/parse_prefix_cols, reason_payload_cols/
parse_reason, concern_from_str, fact_kind_from_str, facet_from_raw).

Add OpTag::parse as the exact inverse of as_str (one arm per variant,
None on any unrecognized string) so the residual reason payload can
round-trip an OpTag through the TSV. Named `parse` rather than
`from_str` to avoid colliding with clippy's should_implement_trait
lint against std::str::FromStr's canonical shape.

Both new round-trip tests are disable-run verified: mutating the write
or parse path drives them red, restoring the fix brings them back green.
@cursor

cursor Bot commented Aug 18, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_c20360c9-72bc-4fbb-a1c0-a3613c1aa113)

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review August 18, 2026 12:03
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@AdaWorldAPI
AdaWorldAPI merged commit 29715a7 into main Aug 18, 2026
61 of 65 checks passed
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