Skip to content

#138 — split fixture Cell into display/key axes + POD-render fixture tables - #140

Merged
cmbays merged 3 commits into
mainfrom
domain-138-cell-display-fidelity
Jun 6, 2026
Merged

cmbays merged 3 commits into
mainfrom
domain-138-cell-display-fidelity

Conversation

@cmbays

@cmbays cmbays commented Jun 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A fixture cell carried only its canonical CellValue (1, 1.0, 1.00 all canonicalize to Number("1")) — right for equality (a format-only reformat must not diff) but wrong for display: the Diff view rendered the normalized value, not what the author wrote. This splits the cell into two axes:

  • display — the authored token (truth), rendered in BOTH the Current and Diff views (a csv 1.00 now shows 1.00, not the normalized 1).
  • key — the existing canonical CellValue, used ONLY for the diff's equality decision and row alignment.

This is the foundation for the settings normalize-toggle (#139) and the sql-given tables (#137).

Behaviors

  • A format-only cell change (1 -> 1.00; csv 1.0 -> 1.00) shows the current authored value AND is not flagged (keys equal); a real value change is flagged and shows old-display -> new-display.
  • Row alignment stays Rust-computed on the canonical key (rows do not re-pair when the display lens changes — see feat: report settings menu (cog) — diff context-lines + normalize-equality toggle #139).
  • Both display and key ship per old/new cell so feat: report settings menu (cog) — diff context-lines + normalize-equality toggle #139 can re-flag client-side without a Rust round-trip; the dual-axis wire is pinned by a serde JSON snapshot test.
  • The Current-view table renders from the Rust-computed FixtureTable POD; the JS csv/dict parsers (parseCsvRows, extractRowsFromFixture, collectColumns) are retired — the template is a pure renderer. The sql code-block + external-fixture affordances stay as the non-tabulatable fallback.

Key implementation notes

  • serde_json has no arbitrary_precision, so a dict-numeric 1.00 arrives as 1.0 upstream of this layer. The fidelity fix is therefore demonstrated on the csv path (parse_csv_rows keeps the raw token) and dict-string path — visible in the regenerated examples/playground-pr-diff-report.html (authored 50.00/100.00/0.00 with canonical number keys).
  • CellChange.changed is computed on key only (o.key != n.key); row_key/match_multiset key on the canonical key exclusively, so the display lens never re-pairs rows.
  • Renderer type affordances (cell-num, NULL/absent styling, data-order sort key) come from key.t, never from display — so an authored 1.00 still sorts numerically.
  • tests/headless_csv_parser.rs retired with the JS parseCsvRows twin; CSV RFC 4180 correctness is now owned by the Rust parse_csv_rows unit tests (g22-g26).

Validation

  • All unit/integration/doc tests green; 79 BDD scenarios pass; headless (toggle + zero-egress) green in real Chrome — the new headless coverage asserts the authored 1.00 / TRUE render in the Current grid.
  • Mutation-gated (cell_diff.rs + unit_test_table.rs): zero survivors.
  • Zero-egress (headless network-block + resource-ref lint) unchanged; synthetic-only fixtures intact; the input manifest-digest snapshot is unchanged (only the output chrome snapshot moved, as expected).
  • CodeRabbit local review: no findings.

This is the FOUNDATION for #139 (settings menu) and #137 (sql tables) — their scope is intentionally NOT pulled in.

Closes #138

🤖 Generated with Claude Code


Open in Stage

Summary by CodeRabbit

  • Improvements

    • Reports and example pages now render from precomputed table payloads with cells carrying authored display + canonical type, preserving formatting while enabling type-aware styling and stable diffs.
    • CSV parsing is consolidated to the Rust side for consistent output across engines.
  • Documentation

    • Guidance updated to reflect Rust-side CSV/table parsing and retirement of the JS CSV parser seam.
  • Tests / Chores

    • Tests and headless test seams updated to the new table/cell shape; CI no longer runs the deprecated JS CSV parser headless test.

…ables (#138)

A fixture cell carried only its canonical CellValue (1, 1.0, 1.00 all
canonicalize to Number("1")). That is right for equality -- a format-only
reformat must not diff -- but wrong for display: the Diff view rendered the
normalized value, not what the author wrote. Split the cell into two axes:

  - display: the authored token (truth), rendered in BOTH Current and Diff
    views (a csv 1.00 now shows 1.00, not the normalized 1).
  - key: the existing canonical CellValue, used ONLY for the diff's equality
    decision and row alignment.

A format-only cell change (1 -> 1.00; csv 1.0 -> 1.00) shows the current
authored value AND is not flagged (keys equal); a real value change is
flagged and shows old-display -> new-display. Row alignment stays
Rust-computed on the canonical key (rows do not re-pair when the display
lens changes -- foundation for the #139 normalize toggle). Both display and
key ship per old/new cell so #139 can re-flag client-side.

Domain (unit_test_table.rs):
  - Cell is now { display: String, key: CellValue } with Cell::new (display
    derived from key) and Cell::with_display (authored path).
  - cell_from_value / cell_from_scalar / cell_from_csv_token capture the raw
    authored token as display alongside the canonical key; the normalizers
    thread these Cell-producing fns. (serde_json has no arbitrary_precision,
    so a dict-numeric 1.00 arrives as 1.0 upstream; the csv/dict-string paths
    preserve trailing zeros -- the fidelity is demonstrated on the csv path.)

Diff (cell_diff.rs):
  - CellChange now carries Cell old/new (display + key) per side; changed is
    computed on key only (o.key != n.key). row_key / match_multiset key on
    the canonical key exclusively, so the display lens never re-pairs rows.

Render (render.rs):
  - GivenPayload/ExpectedPayload ship the Rust-computed FixtureTable POD
    (table) for the Current view; rows/format/fixture retained only for the
    non-tabulatable fallback (sql code block, external-fixture affordance).
    current_view_table returns None for sql/opaque and external fixtures.

Template (report.html):
  - buildTable renders the POD: text = authored display, numeric/NULL styling
    + data-order sort key = canonical key.t (a Number key still sorts
    numerically even when displayed as 1.00). The JS csv/dict table parsers
    (parseCsvRows, extractRowsFromFixture, collectColumns) are RETIRED -- the
    template is a pure renderer. The sql code-block fallback stays.

Tests:
  - tests/headless_csv_parser.rs retired with the JS parseCsvRows twin; CSV
    RFC 4180 correctness is owned by the Rust parse_csv_rows unit tests
    (g22-g26). Headless coverage asserts the authored 1.00 / TRUE render in
    the Current grid. Wire-shape test pins the dual-axis { display, key }
    contract via a serde JSON snapshot. The playground PR-diff example now
    shows authored 50.00 / 100.00 / 0.00 with canonical number keys.

Zero-egress (headless + resource-ref lint), synthetic-only fixtures, and the
input manifest-digest snapshot are unchanged.

Closes #138

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ghost

ghost commented Jun 6, 2026 •

Copy link
Copy Markdown

Ready to review this PR? Stage has broken it down into 8 individual chapters for you:

Title
1 Split Cell into display and key axes
2 Compute cell diffs on the key axis
3 Wire precomputed tables through the adapter
4 Retire JS parsers and render from POD
5 Update documentation and CI for Rust CSV parsing
6 Assert fidelity in integration tests
7 Regenerate example reports
8 Other changes
Open in Stage

Chapters generated by Stage for commit 7fc313f on Jun 6, 2026 2:55pm UTC.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 319f3ffe-d8df-444b-a906-a4426c66f764

📥 Commits

Reviewing files that changed from the base of the PR and between b40ea57 and 7fc313f.

📒 Files selected for processing (1)
  • src/domain/cell_diff.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/domain/cell_diff.rs

📝 Walkthrough

Walkthrough

Split Cell into authored display + canonical key, propagate Cells through diff logic and payloads, add optional FixtureTable POD to test payloads, switch templates/examples to render Rust-produced table PODs, retire JS CSV/tabulation seams, and update tests/CI/docs to match.

Changes

Display/Key Cell Refactor & POD-Driven Rendering

Layer / File(s) Summary
Cell IR redesign: display + key foundation
src/domain/unit_test_table.rs
Cell now serializes as {display: String, key: CellValue}; added display_from_key, with_display, and cell-builder functions (cell_from_value, cell_from_scalar, cell_from_csv_token) and updated normalization to emit authored Cells.
Diff pipeline: Cell-based comparison with key-only equality
src/domain/cell_diff.rs
CellChange now carries old: Cell / new: Cell; changed is computed from old.key != new.key; projection/alignment/matching operate on Vec<Vec<Cell>>; row-classification helpers and tests updated for key-based equality and absent-cell rules.
Payload model: FixtureTable POD + dual-axis cells
src/adapters/render.rs
GivenPayload/ExpectedPayload gain pub table: Option<FixtureTable>; build_test_payload populates it via current_view_table(...) which uses table_from_manifest_rows and preserves external-fixture fallback; data_diff/serde tests validate both display and key axes.
Template refactor: render from PODs with {display,key} cells
templates/report.html, examples/playground-report.html, examples/playground-pr-diff-report.html, examples/jaffle-shop-report.html
Templates/examples now consume table PODs; buildTable/buildFixtureView accept table and iterate row.cells of {display,key}; Current/diff rendering uses cell.display for shown text and cell.key for styling/sort and diff decisions; JS CSV/tabulation helpers and seams (parseCsvRows, window.__cuteParseCsv, related extractors) retired; headless seam signatures adjusted (window.__cuteBuildFixtureView(table, cls, diff), window.__cuteCellText).
Test updates and documentation
.github/workflows/ci.yml, AGENTS.md, features/unit_test_format_coverage.feature, tests/cell_table_diff.rs, tests/headless_toggle.rs, tests/steps/cell_table_diff.rs, tests/headless_csv_parser.rs
CI step for headless CSV parser removed; docs updated to note Rust CSV parsing ownership; tests and fixtures changed to use Cell::new(...) and assert cell.key for equality and cell.display for rendered fidelity; examples updated to include structured given.table payloads.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

"🐰 I stitched a cell with two soft seams,
One voice to render, one to judge in dreams.
Rust builds the table, JS only paints,
Format-only whispers leave diffs quaint.
Cheers — a carrot for faithful displays!"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: splitting Cell into display/key axes and POD-rendering fixture tables to preserve authored display fidelity.
Linked Issues check ✅ Passed All acceptance criteria from #138 are met: Cell splits into display/key axes, both Current and Diff views render display, format-only changes are unflagged, row alignment uses canonical key, wire ships both axes, JS parsers are retired, and examples regenerate with authored values.
Out of Scope Changes check ✅ Passed All changes are in scope for #138: Cell IR refactoring, diff/equality split, POD rendering, JS parser retirement, test updates, and example regeneration. No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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 domain-138-cell-display-fidelity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Jun 6, 2026 •

Copy link
Copy Markdown
Contributor

📄 Rendered report preview

All examples regenerated cleanly.

This PR doesn't touch dbt-project/, so there's no PrDiff self-preview row.

Preview Artifact
pr-diff-showcase — pr-diff-showcase-report.html Download
jaffle-shop — jaffle-shop-report.html Download
playground — playground-report.html Download

Click Download to fetch the rendered HTML. Each artifact
is a zip containing the single-file report — extract and open
in any browser. Reports are fully self-contained (zero
external resource requests).

Alternative: GitHub CLI
# gh CLI >= 2.63 extracts into ./report-preview-playground/.
gh run download 27065495540 -R breezy-bays-labs/cute-dbt -n report-preview-playground
open report-preview-playground/playground-report.html

Posted by report-preview.yml for 7fc313fbc47bc36c6e48e79b7ea2cb0875ba77c6. Affordance only — never blocks merge.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request shifts the CSV and dict table parsing logic from the JavaScript frontend to the Rust backend (domain layer). It introduces a dual-axis Cell structure containing both an authored display string (for rendering fidelity) and a canonical key (for semantic equality checks and diffing). This allows the HTML report template to act as a pure renderer of the Rust-computed FixtureTable POD, retiring several JS-side parsers and headless tests. I have no feedback to provide as there are no review comments.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@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

🧹 Nitpick comments (1)
src/domain/unit_test_table.rs (1)

162-169: ⚡ Quick win

Round-trip Cell::with_display explicitly.

The local serde coverage still only exercises Cell::new(...), so a regression that drops or rewrites non-canonical displays like Cell::with_display("1.00", Number("1")) would pass here. Please add the table-driven round-trip cases for divergent display/key pairs, plus the empty-display Null / Absent cases.

🧪 Example cases to fold into the existing matrix
+TableRow::new(vec![
+    Cell::with_display("1.00".into(), CellValue::Number("1".into())),
+    Cell::with_display(String::new(), CellValue::Null),
+    Cell::with_display(String::new(), CellValue::Absent),
+])

As per coding guidelines, src/domain/**/*.rs must write property tests for JSON serde round-trip. Based on learnings, this repo expects those as exhaustive table-driven matrices rather than proptest.

🤖 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 `@src/domain/unit_test_table.rs` around lines 162 - 169, Add explicit JSON
serde round-trip table-driven tests that exercise Cell::with_display alongside
existing Cell::new cases: extend the matrix in the unit tests in
src/domain/unit_test_table.rs to include divergent display/key pairs (e.g.,
display "1.00" with numeric key Number("1"), display "true" with Bool(true), and
other non-canonical presentations) and the empty-display variants mapping Null
display to Absent key and vice versa; for each case serialize to JSON and
deserialize back and assert equality to the original Cell (use the same test
harness/table structure already present so the new rows follow the existing
matrix pattern).

Sources: Coding guidelines, Learnings

🤖 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 `@src/domain/cell_diff.rs`:
- Around line 470-480: The Added/Removed row builders (added_row and
removed_row) currently set CellChange.changed = true unconditionally, violating
the old.key != new.key contract and mislabeling Absent→Absent cells as changed;
change the constructors to compute changed by comparing the cell keys (e.g.,
changed: old.key != new.key or using the Cell.key accessor) instead of
hardcoding true for both added_row and removed_row so Absent→Absent cells remain
unchanged.

---

Nitpick comments:
In `@src/domain/unit_test_table.rs`:
- Around line 162-169: Add explicit JSON serde round-trip table-driven tests
that exercise Cell::with_display alongside existing Cell::new cases: extend the
matrix in the unit tests in src/domain/unit_test_table.rs to include divergent
display/key pairs (e.g., display "1.00" with numeric key Number("1"), display
"true" with Bool(true), and other non-canonical presentations) and the
empty-display variants mapping Null display to Absent key and vice versa; for
each case serialize to JSON and deserialize back and assert equality to the
original Cell (use the same test harness/table structure already present so the
new rows follow the existing matrix pattern).
🪄 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

Run ID: cff3d89f-57a1-456a-8900-cbf6d6715c2c

📥 Commits

Reviewing files that changed from the base of the PR and between 5184c06 and 925329b.

⛔ Files ignored due to path filters (1)
  • tests/snapshots/render_integration__rendered_chrome_jaffle_shop.snap is excluded by !**/*.snap
📒 Files selected for processing (14)
  • .github/workflows/ci.yml
  • AGENTS.md
  • examples/jaffle-shop-report.html
  • examples/playground-pr-diff-report.html
  • examples/playground-report.html
  • features/unit_test_format_coverage.feature
  • src/adapters/render.rs
  • src/domain/cell_diff.rs
  • src/domain/unit_test_table.rs
  • templates/report.html
  • tests/cell_table_diff.rs
  • tests/headless_csv_parser.rs
  • tests/headless_toggle.rs
  • tests/steps/cell_table_diff.rs
💤 Files with no reviewable changes (1)
  • tests/headless_csv_parser.rs

Comment thread src/domain/cell_diff.rs Outdated
cmbays and others added 2 commits June 6, 2026 04:54
…138)

added_row/removed_row hardcoded changed: true, violating the
changed == old.key != new.key contract that modified_row honors. When a
row add/remove coincides with a column add/remove, the unified axis
projects an Absent cell into the added/removed row; that Absent->Absent
cell must not be marked changed. It also matters for the #139 normalize
toggle, which re-derives changed from the key axis client-side -- a
server/client mismatch on these cells would desync the toggle.

Compute changed from the key axis (c.key != Absent) and add a targeted
builder test pinning the contract. Caught by CodeRabbit on PR #140.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…umn overlap (#138)

f_added_removed_rows_keep_absent_cells_unchanged pins the builder contract
directly. This adds the integration counterpart: it drives diff_fixture_tables
through the production normalizer so the unified column axis organically
projects an Absent cell into an Added row (a row-add overlapping a column-add),
proving the end-to-end path leaves that Absent->Absent cell unflagged. Surfaced
by the stage-review pass on PR #140.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cmbays
cmbays merged commit c6936ac into main Jun 6, 2026
31 checks passed
@cmbays
cmbays deleted the domain-138-cell-display-fidelity branch June 6, 2026 14:58
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.

diff: show authored cell values — split cell IR into display/equality axes + POD-render fixture tables

1 participant