Repository navigation
#178 — design integration PR-2: engine merge (settings panel, unified/split diffs) + port-forward - #188
Conversation
…l, unified/split diffs) + port-forward
Replace the template's ~2100-line inline engine with two first-party
include_str! assets: templates/interaction.js (the merged redesign
interaction engine) and templates/theme.js (the appearance engine), wired
through asset_embed::{INTERACTION_JS,THEME_JS} with banner-pin +
end-of-file-sentinel tests (the REPORT_CSS pattern).
Engine merge (handoff -> shipped, our behaviors win every conflict):
- Settings panel: static Appearance markup (style/theme/accent/density/
diff-style/diff-layout) in the template, wired by theme.js; persists
under cute-dbt.appearance.v1; DataTables dark syncs via html.dark. The
cog + panel are now universal (themes apply to baseline reports too);
the #139 context-lines/normalize rows stay PR-diff-only.
- Diff renderers: GitHub-grade two-column line-number gutters, shared
diffBody/diffNumbers helpers, hunk folds keep the #132/#136 contract,
and a NEW split (side-by-side) renderer - diffViewsHtml emits BOTH
layouts over the same BlockDiff lines verbatim (kind/text/emphasis; no
Rust diff-engine change); CSS shows one per html[data-difflayout] with
a responsive fallback.
- Code cards: file-path headers (defined_in on the YAML drawer,
<model>.sql on the Model-SQL card), single-gutter line numbers on
plain blocks (highlightLines*), incremental badge relocated into the
Model-SQL card header (static span removed per handoff README section 3).
- DAG: theme-aware edge palette (JOIN_COLORS_LIGHT/_DARK + dagEdges),
__cuteRerenderDag re-tint hook; Cytoscape surface deliberately
excluded (Bucket 2).
Port-forward audit (the fork predates these; shipped behavior kept):
- #156 stable DAG node identity: already present in the handoff fork's
JS; the Rust side never forked. Verified, no regression.
- #158 minmax(0,1fr) stacked-panel fix: PR-1 chassis kept it; the 375px
headless regression stays green.
- #161 strategy-correct expect-semantics tooltip: kept OUR wording (the
handoff carried the pre-#159 merge/insert copy - rejected).
- #166 column tooltips: kept OUR payload shape + focusable-button/CSS-
bubble renderer (the handoff's body-appended th.has-col-meta renderer
targets the not-yet-existing ModelPayload.column_meta - rejected;
belongs to handoff PR-3).
Also: report.css drops its PR-1 bridge layer per its own contract and
gains the .settings-row-stack chrome; the edge-vocab-completeness gate +
BDD legend steps re-point at templates/interaction.js and now check both
palettes; headless guards added for the appearance toggles, the split
renderer, the gutters and the relocated badge; goldens + insta
snapshots regenerated (byte-identity re-verified CI-style).
Closes #178
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThis PR integrates the redesigned appearance engine into the report template, restructures column-test metadata to a richer payload format, adds split-diff rendering alongside unified diffs with GitHub-style gutters, introduces localStorage-persisted appearance settings, and updates test coverage to validate all new behaviors. ChangesDesign System Integration (PR-2)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.43.0)examples/jaffle-shop-report.htmlThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
📄 Rendered report previewAll golden examples regenerated cleanly. 🟡 Golden examplesCommitted to
🐶 Live dogfood previewThis PR doesn't touch ▶ Open ↗ opens the report in your browser in one click — The Pages preview may take ~1 min to update after this comment Alternative: GitHub CLI# gh CLI >= 2.63 extracts into ./report-preview-playground/.
gh run download 27308047455 -R breezy-bays-labs/cute-dbt -n report-preview-playground
open report-preview-playground/playground-report.htmlPosted by |
|
Ready to review this PR? Stage has broken it down into 9 individual chapters for you: Chapters generated by Stage for commit ae395a4 on Jun 10, 2026 9:39pm UTC. |
There was a problem hiding this comment.
Code Review
This pull request introduces first-party appearance and interaction engines (theme.js and interaction.js) to support customizable themes, accents, density, and unified/split diff layouts with localStorage persistence. It also cleans up the CSS bridge layer, relocates the incremental badge to the code-card header, and adds extensive headless tests. The review feedback recommends improving compatibility and robustness in theme.js by avoiding direct use of NodeList.prototype.forEach on query selector results and safely binding window.jQuery locally to prevent potential ReferenceErrors.
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.
…r, accent keys, value chips) + gemini fixes Christopher's review override on PR #188: the #166-tooltip deferral is reversed — the column-header tooltips must match the handoff spec in this PR, adapted to OUR shipped per-table column_meta payload (NOT ModelPayload.column_meta, which stays PR-3). Spec match (handoff engine/interaction.js + base.css, README section 2.2): - The WHOLE header cell is the hover/focus trigger — the info icon (buildColTooltip's focusable button + per-th bubble) is gone. decorateColHeader marks a metadata-bearing th with .has-col-meta + data-col-name/desc/tests; showColTip fills + positions the single body-appended #col-tooltip bubble with the handoff's positioning math verbatim (position:fixed, horizontal clamp, flip-above on bottom overflow — never clipped by the .table-fit scroller). - Bubble content per spec: .ct-desc (description, larger) -> "Data tests" label -> per-test .ct-test rows with .ct-key (accent color), .ct-vals > .ct-val chips for accepted_values args, .ct-detail muted mono for relationships/range detail. CSS ported from the handoff base.css (comment bodies kept free of close-sequences per the README porting-bug warning). - Render-layer POD (adapter, not domain): ColumnMetaPayload.tests is now Vec<ColumnTestPayload {name, values, detail}> built by column_test_payload, the section-2.2 display mapping: unique / not_null -> bare prose names; accepted_values -> "accepted values" + values chips; relationships -> detail "model.field" (ref()/source() unwrapped to the last quoted arg); accepted_range (any namespace) -> detail "min-max" / ">= min" / "<= max"; any other test keeps its package-qualified raw identifier with no values/detail. - A11y (the #166 contract carried onto the th): tabindex="0" so keyboard users reach the tip via focusin; aria-label on the trigger summarizes description + tests; the singleton bubble is aria-hidden; hover AND focus both reveal; decorated headers SHED the native title (undecorated headers keep theirs); :focus-visible outline added. Guards updated: - headless: column_header_tooltips_th_trigger_hover_focus_and_skip_ bare_columns — th-trigger semantics (no icon/button in any th), tabindex/aria/title assertions, focus AND hover reveal + hide, .ct-key/.ct-val/.ct-detail content over an accepted_values + relationships fixture, given-table input-model resolution. - BDD: the report_generation scenario matches the structured entries' display `name` ("not_null" -> "not null"); payload contract otherwise unchanged. - render.rs unit tests pin the full section-2.2 mapping table. Gemini review threads (PR #188, both applied): theme.js gains an ES5-safe qsaForEach helper replacing NodeList.prototype.forEach at every control-wiring site, and reflowTables binds jQuery locally. Goldens + the chrome insta snapshot regenerated; byte-identity re-verified CI-style; visual check against the handoff reference.html confirms the tooltip treatment matches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@coderabbitai review |
✅ Action performedReview finished.
|
… display alignment (#189) * feat(adapters): thread the model's full source path onto ModelPayload (#179) ModelPayload gains `path` (the manifest's project-relative original_file_path, serde-skipped when None) and the Model-SQL code-card header renders it — founder call: the full models/…/x.sql, never just the filename. The `<name>.sql` synthesis stays as the fallback for manifests without original_file_path (synthetic / pre-1.8). Covered at three levels: render.rs unit tests (threading + wire omission), a headless full-path header test (+ the existing fallback assert re-scoped), and a BDD payload-fact scenario against the committed jaffle-shop fixture. Goldens + the insta chrome snapshot regenerated; every changed region is the path key + the header JS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: adopt the handoff demo schema YAML + real-fixture column-meta verification (#179) Two AC2/AC3 coverage additions for the handoff README §2.2 column-test display mapping (the mapping itself shipped in #188's ae395a4): - tests/render_integration.rs verifies the mapping against the REAL fusion-compiled playground manifest: bare built-ins, accepted_values pills, relationships ref() unwrapping, and the package-test arm (dbt_expectations.* stays an uninterpreted package-qualified name) all against real fusion kwargs shapes (extra column_name/model keys). - tests/fixtures/healthcare-analytics-schema.yml adopts the handoff's demo schema verbatim (healthcare-SHAPED, pure synthetic; listed in MANIFEST.toml with sha256 + synthetic_only) — it carries the two §2.2 arms no real committed fixture exercises: dbt_utils.accepted_range in min-only (≥ 0) and both-bounds (0–1 / 0–100, en dash) forms, and boolean accepted_values pills. tests/column_meta_handoff_schema.rs extracts all 47 authored data_tests entries and asserts the mapping over them, so the fixture is load-bearing, not decorative. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: harden the handoff-schema YAML walker (gemini review feedback) Three robustness fixes on the fixture extractor, all keeping the fail-loudly stance: accept double-quoted scalars alongside single, reset column state at each model boundary (a misplaced entry now panics instead of leaking onto the previous model's column), and split arg lines on the first ':' with trimming + skip blank/comment arg lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ecklist + tiered findings + suppressed-count The render lane of the coverage-intelligence epic (#168): the per-model Coverage checks panel rendering the #186/#191 engine verdicts in-context. - Payload (render.rs): FindingPayload wraps the domain Finding (flattened, wire keys unchanged) with render-resolved pin_node (DAG anchor: bracketed constructs pin the named CTE node, model-level constructs the terminal) and sketches (the union check's 'suggested given' evidence lifted into copyable blocks). ReportPayload gains check_specs — the offline spec catalog (name/tier/instrument/conditions/exclusions/rationale + the click-only book_href), keyed by fired check only, serde-skipped when empty so findings-free payloads stay byte-stable. - Surface (report.html/interaction.js/report.css): a self-contained .model-findings section per model — three-valued checklist (mark + word, never colour-only), labeled tier chips (TOTAL solid / HIGH outlined / advisory muted — never blended, no percentages), covered-by attribution, evidence rows, evidence pinning (select + smooth-scroll + flash), the recommendation with #188-pattern copyable YAML sketches, the inline 'What is this check?' rationale drawer (fully offline; the book link is a plain click-only anchor), and the visible-but-quiet suppressed reveal (collapsed count -> rows with reason + source). Token-native on the #187 chassis; tier-chip tooltips use the #146/#188 bubble contract. - Dogfood: playground fixture gains unique_key on mart_dq_summary (grain UNCOVERED), an inline ignore pragma on dim_payers (suppressed-with-reason), and the synthetic-body-mod idiom on fct_clinical_events (union UNCOVERED with three given-row sketches) — all three goldens now show the panel, jaffle-shop deliberately renders the quiet empty state. - Tests: 7 render.rs payload units, 2 BDD scenarios (check_specs catalog facts), 2 headless guards (panel/checklist/tier distinctness/tooltip/ sketch+copy/rationale/book-link/pin/both-toggle-views/empty-state; suppressed collapsed-count + reason reveal); insta snapshots + goldens regenerated intended-only. Closes #170 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…+ tiered findings + suppressed-count (#194) * feat(adapters): #170 — report findings surface: per-model coverage checklist + tiered findings + suppressed-count The render lane of the coverage-intelligence epic (#168): the per-model Coverage checks panel rendering the #186/#191 engine verdicts in-context. - Payload (render.rs): FindingPayload wraps the domain Finding (flattened, wire keys unchanged) with render-resolved pin_node (DAG anchor: bracketed constructs pin the named CTE node, model-level constructs the terminal) and sketches (the union check's 'suggested given' evidence lifted into copyable blocks). ReportPayload gains check_specs — the offline spec catalog (name/tier/instrument/conditions/exclusions/rationale + the click-only book_href), keyed by fired check only, serde-skipped when empty so findings-free payloads stay byte-stable. - Surface (report.html/interaction.js/report.css): a self-contained .model-findings section per model — three-valued checklist (mark + word, never colour-only), labeled tier chips (TOTAL solid / HIGH outlined / advisory muted — never blended, no percentages), covered-by attribution, evidence rows, evidence pinning (select + smooth-scroll + flash), the recommendation with #188-pattern copyable YAML sketches, the inline 'What is this check?' rationale drawer (fully offline; the book link is a plain click-only anchor), and the visible-but-quiet suppressed reveal (collapsed count -> rows with reason + source). Token-native on the #187 chassis; tier-chip tooltips use the #146/#188 bubble contract. - Dogfood: playground fixture gains unique_key on mart_dq_summary (grain UNCOVERED), an inline ignore pragma on dim_payers (suppressed-with-reason), and the synthetic-body-mod idiom on fct_clinical_events (union UNCOVERED with three given-row sketches) — all three goldens now show the panel, jaffle-shop deliberately renders the quiet empty state. - Tests: 7 render.rs payload units, 2 BDD scenarios (check_specs catalog facts), 2 headless guards (panel/checklist/tier distinctness/tooltip/ sketch+copy/rationale/book-link/pin/both-toggle-views/empty-state; suppressed collapsed-count + reason reveal); insta snapshots + goldens regenerated intended-only. Closes #170 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(adapters): #170 review dispositions — in-place Cytoscape pin, flash-timer reset, own-property tally guard - finding-pin under the Cytoscape engine now emits a tap on the live cy node (the exact bound click path: in-place lineage classes + __cuteSelectNode) instead of renderDag() — honoring the #180 no-rebuild-per-click contract (CodeRabbit); headless guard added (same cy instance + .sel class after a pin). - rapid re-pins clear the pending flash timer so an old timeout can't cut the new animation short (gemini). - verdict tally uses an own-property guard (gemini). - goldens + chrome snapshot regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(adapters): #170 post-rebase hardening — tag-like payload escaping + qualified-construct pins Rebased onto main with the #173 join check pair (#195), whose spec prose ('WHERE <right>.<key> IS NULL') now rides the payload through the #170 check_specs catalog: - payload_json_for_html_script escapes EVERY tag-opening '<' shape ('</', '<!', '<?', '<letter') to \u003c — inert in browsers either way, but raw '<right>' read as markup to non-HTML5 tag scanners (the tl-based BDD payload extractors choked); bare '< ' / '<digit' (compiled-SQL comparisons) stay raw, preserving the documented contract. - resolve_pin_node understands the #173 qualified construct form (left_join[<consumer>:<right>]) and pins the consumer CTE; unit test added. - the #195 suggested-given BDD steps read the sketch from the finding's 'sketches' array (the #170 lift moved it out of evidence — their sketches get the copy-button affordance for free); the step now also pins that the entry is never duplicated back into evidence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…irement, DAG final-stroke AA fix (#203) * feat(render): three new AA-pinned themes — Dracula, Catppuccin Latte, Rosé Pine Dawn (8 total) + Solarized/Gruvbox AA touch-ups Lands the design pass-2 theme delta on the chassis (cute-dbt#198): - templates/report.css gains the dracula / latte / rosepine [data-theme] blocks verbatim from the pass-2 engine/tokens.css (incl. per-theme code-surface tokens); Solarized --diff-remove-fg #c5341c -> #b82d18 and Gruvbox --code-rem-sigil #fb4934 -> #fb6a5d per the pass-2 AA re-verification; the pinned AA table is replaced with the 8-theme README §2.3 table (do not re-brighten). - templates/theme.js THEMES becomes the 8-entry list, light family first (light/solarized/latte/rosepine/dark/tokyo/gruvbox/dracula); dark-family themes still toggle html.dark for the vendored DataTables dark rules. Static-markup contract kept: only the DATA was lifted from pass-2, not its runtime DOM-injection approach. - templates/report.html theme grid shows all 8 chips (static markup, the #188 contract), reordered light family first with pass-2 swatch dots. - headless appearance guard extended: latte (light family) + dracula (dark family) chips flip [data-theme] + the html.dark DataTables sync, and the grid lists exactly 8 chips. - insta snapshots (chrome + skeleton) regenerated for the above. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(render): retire diff-cells colour/marks control per design pass-2 Founder-confirmed 2026-06-10 (epic #197 open question 1): the #188 "Diff cells: colour / marks" control is retired — diff cells always render in colour (pass-2 README §2.3). - templates/report.html: the Diff-cells settings row (static markup) is removed; the settings-menu comment documents the retirement. - templates/theme.js: applyDiffStyle, the diffstyle pref default, the load() key, the .diff-seg sync/wire blocks and the boot apply are removed. A LEGACY persisted diffstyle key in cute-dbt.appearance.v1 is ignored gracefully: load() copies only the live keys and nothing sets data-diffstyle, so no error and no leftover attribute. - templates/report.css: the :root[data-diffstyle="marks"] rules are removed (a tombstone comment records the retirement). - headless appearance guard updated: asserts no .diff-seg / [data-diffstyle] markup survives, and a seeded legacy diffstyle key still hydrates the live keys with no data-diffstyle attribute after reload. - insta snapshots (chrome + skeleton) regenerated for the above. This commit is deliberately standalone so the retirement is trivially revertable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(render): design-2 chassis chrome — 28rem settings panel, two-layer legend final swatch, cyto card wrap, role_final AA stroke - Settings panel width 21rem -> 28rem (max-width 92vw, box-sizing border-box) with the narrow-viewport fallback min(28rem, 94vw), per the pass-2 engine/chrome.css — room for the 8-theme grid without wrapping. - Legend final-node swatch becomes the two-layer hexagon: a color-mix darkened edge layer + a ::before fill inset (pass-2 engine/base.css), mirroring the darker role_final stroke. - role_final stroke #E69F00 -> #b07400 (AA against the #fdf2dc fill) in BOTH DAG engines: the Mermaid classDef literal in renderDag and the ROLE_FILLS table cuteDagPalette feeds the Cytoscape engine. The #192 dark-bg palette variants are untouched — they cover edges/anchor only; node fills deliberately stay light in every theme, so the darker stroke only gains contrast against its own light fill. - Cytoscape node-card wrap fixes (width max-content, overflow-wrap anywhere, nc-row flex-wrap) per the pass-2 engine/chrome.css. - insta snapshots (chrome) regenerated for the above. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(render): per-theme AA token override for the #194 tier chips on Latte The cute-dbt#198 discovery check ran the WCAG math on the findings-surface tier chips (0.68em text) for all three new themes: - Dracula: total 6.55 / high 5.59 / advisory 6.08 — pass - Rosé Pine Dawn: total 6.11 / high 5.68 / advisory 4.56 — pass - Latte: total 4.91 / high 4.34 / advisory 4.37 — high+advisory a hair under AA 4.5 Fix per the issue's discovery contract: a per-theme token override scoped to .tier-chip (accent -> one-step-darker latte blue #1a5cdd, 5.13:1; muted -> Catppuccin subtext1 #5c5f77, 5.53:1). The chip markup/rules and the verbatim latte theme block are untouched. Pre-existing, out of scope here: shipped Solarized tier-high is 3.41:1 on main today (accent #268bd2 on the cream surface) — flagged on the PR for a follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: regenerate golden examples for the design-2 chassis delta jaffle-shop / playground / diff-showcase re-rendered with the exact example-report-check matrix commands. Reviewed with git diff --text -U0 -- examples/: every changed line is one of the intended deltas (3 new theme blocks + AA touch-ups, 8-chip theme grid, diffstyle retirement, 28rem settings panel, two-layer legend swatch, cyto node-card wrap, role_final #b07400 stroke, latte chip override). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(render): un-cap the narrow-viewport settings panel at 94vw CodeRabbit on #203: the base .settings-panel rule's max-width: 92vw clipped the <=720px fallback width: min(28rem, 94vw), so the extra 2vw never applied and the 8-chip theme grid wrapped early on small screens. The media block now widens max-width alongside the fallback. (The same latent cap exists in the pass-2 engine/chrome.css — this honors the design's 94vw intent rather than its literal bytes.) Goldens + chrome snapshot regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ep setting, split-view folds, per-diff fold toggle, copy-icon buttons Ports the pass-2 directional fold model onto the shipped diff renderers (cute-dbt#199, design package design-return-2): - settings.expandStep in cute-dbt.settings.v1 (default 20, clamp 0–500, 0 = all; NaN-tolerant hydrate) + the static #settings-expand-step row (the #188 static-markup contract). Read live by expandFold/contractFold — no re-render on change. - Unified renderer: data-fold-dir (leading fold expands UP toward the hunk, else DOWN), gutter .fold-steppers (+ by step / − by step, disabled at bounds), per-hunk .fold-collapse-all once anything is revealed, label progression Show N unchanged lines → All N lines shown, and updateFoldControl re-parks the control adjacent to the remaining hidden run (below the run when fully revealed). Reveal stays parent-scoped via closest(code, tbody) — the #132 duplicate-fold-id rule. - Split renderer folds long context runs with the SAME fold model (fold row: stepper gutter + colspan label cell) and gains the ds-c-num/ds-c-code colgroup (3.8em/auto) for a true 50/50; setAllFolds drives both layouts from one control set, so __cuteExpandAllFolds / __cuteCollapseAllFolds keep mirroring every fold control (label + aria-expanded + steppers + collapse-all — the #136 symmetric-mirror invariant on the new anatomy). - Per-diff fold toggle (buildFoldToggleBtn, aria-pressed) in the Model-SQL and Model-YAML diff code headers; the #132 top-of-report .diff-expand-all strip is removed (diffAllExpanded / renderExpandAllToggle / bindDiffViewControls retired with it). - copyIconBtn (inline-SVG icon, aria-label Copy, copied-class flash, execCommand fallback) replaces the absolutely-positioned text Copy in the Model-SQL header and is added to the Model-YAML header; code-header padding per engine/base.css. Functional-SVG-only iconography (README §2.4) — no icon fonts, nothing external, zero-egress untouched. No payload change; no src/domain change. Goldens + insta snapshots regenerated — every changed byte traces to this delta. Closes #199 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ng, split-view folds, per-diff fold toggle, copy-icon buttons (#213) * feat(render): design-2 fold steppers — gutter +/− steppers, expand-step setting, split-view folds, per-diff fold toggle, copy-icon buttons Ports the pass-2 directional fold model onto the shipped diff renderers (cute-dbt#199, design package design-return-2): - settings.expandStep in cute-dbt.settings.v1 (default 20, clamp 0–500, 0 = all; NaN-tolerant hydrate) + the static #settings-expand-step row (the #188 static-markup contract). Read live by expandFold/contractFold — no re-render on change. - Unified renderer: data-fold-dir (leading fold expands UP toward the hunk, else DOWN), gutter .fold-steppers (+ by step / − by step, disabled at bounds), per-hunk .fold-collapse-all once anything is revealed, label progression Show N unchanged lines → All N lines shown, and updateFoldControl re-parks the control adjacent to the remaining hidden run (below the run when fully revealed). Reveal stays parent-scoped via closest(code, tbody) — the #132 duplicate-fold-id rule. - Split renderer folds long context runs with the SAME fold model (fold row: stepper gutter + colspan label cell) and gains the ds-c-num/ds-c-code colgroup (3.8em/auto) for a true 50/50; setAllFolds drives both layouts from one control set, so __cuteExpandAllFolds / __cuteCollapseAllFolds keep mirroring every fold control (label + aria-expanded + steppers + collapse-all — the #136 symmetric-mirror invariant on the new anatomy). - Per-diff fold toggle (buildFoldToggleBtn, aria-pressed) in the Model-SQL and Model-YAML diff code headers; the #132 top-of-report .diff-expand-all strip is removed (diffAllExpanded / renderExpandAllToggle / bindDiffViewControls retired with it). - copyIconBtn (inline-SVG icon, aria-label Copy, copied-class flash, execCommand fallback) replaces the absolutely-positioned text Copy in the Model-SQL header and is added to the Model-YAML header; code-header padding per engine/base.css. Functional-SVG-only iconography (README §2.4) — no icon fonts, nothing external, zero-egress untouched. No payload change; no src/domain change. Goldens + insta snapshots regenerated — every changed byte traces to this delta. Closes #199 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: supersede the #132/#136 fold guards with the #199 step-expansion contract Deliberate guard migration (never deleted, each replaced with the new-behavior assertion): - block_diff_folds_long_context_runs_and_reveals_on_activate: activate = expand-by-step (default step 20 >= hidden 4 still reveals all in one activation); the 'Hide N unchanged lines' relabel assertion is superseded by 'All N lines shown' + disabled + stepper + visible collapse-all; re-collapse moved from band-click to the explicit .fold-collapse-all. Kept: parent-scoped reveal, control stays visible, aria-expanded truth, Enter/Space keyboard activation, short-block never folds, new data-fold-dir/steppers markup assertions. - global_expand_collapse_mirrors_every_fold: the #136 bidirectional mirror retained on the new control anatomy (label, stepper disabled-states, collapse-all visibility); now also pins the retired top-of-report .diff-view-controls strip at 0 nodes. - settings_context_lines_refolds_block_diffs_live: contextLines re-render still live; extended with the expandStep no-re-render proof (a block mounted before the setting change steps by the new value). - split_diff_renders_the_same_block_diff_as_unified: parity extends to folds — same directional control anatomy, count, colgroup geometry, band activation + setAllFolds mirror on the split tbody. - model_sql_section_defaults_to_diff_and_toggles_to_raw / yaml_diff_drawer_defaults_to_diff_and_toggles_to_authored: assert the per-diff fold toggle + the inline-SVG copy-icon button (real focusable <button>, aria-label Copy) in both code headers. - NEW expand_step_steppers_reveal_contract_directionally_and_persist: + reveals exactly step lines toward the hunk, − re-hides them mirroring direction, collapse-all restores, up/down direction proofs, control re-parking, and cute-dbt.settings.v1 persistence across reload. - NEW per_diff_fold_toggle_drives_its_own_diffs_folds: the header toggle expands/restores every fold in its own diff (unified + split) with aria-pressed + label tracking. render_block_diff_honors_a_configurable_fold_pad is unchanged — its fold-pad arithmetic and labels survive the new model. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(render): truthful fold-toggle + copy outcome state (Gemini PR #213 findings) Applies all three Gemini findings — each holds the new controls to the same symmetric-DOM-mirror standard the #136 guards assert: 1. copyIconBtn signals failure truthfully: success flashes 'copied', failure flashes 'copy-failed' (title AND aria-label carry the outcome; both reset after the flash). Write strategy now mirrors copySql — writeText, then the shared execCommand fallbackCopy (which reports its own success) on rejection/absence. The .copy-failed CSS is no longer dead. 2. buildFoldToggleBtn is stateless: the click derives intent from the DOM at activation time (any hidden folded line in this diff => expand-all, else collapse-all) instead of a cached boolean that desyncs under per-hunk stepping or the __cute hooks. 3. setAllFolds keeps the per-diff toggles truthful: every fold mutation funnels through updateFoldControl -> syncFoldToggles, which relabels each toggle (text + aria-pressed) from ITS OWN diff's DOM truth via the root accessor stored on the element. Per-root truth is deliberately stronger than relabeling 'toggles in scope' to one shared state: a partial-scope op never lies about an untouched sibling diff, and per-hunk steppers stay covered too. New/extended guards (headless_toggle): - per_diff_fold_toggle_drives_its_own_diffs_folds: (a) global __cuteExpandAllFolds flips the toggle's label/aria-pressed; a click after the global op acts on DOM truth and collapses; (b) stepping the unified fold fully keeps the toggle truthful while the split twin still holds hidden rows, and the next click expands the remainder. - NEW copy_icon_button_signals_failure_truthfully: both write paths stubbed to fail in-page (writeText rejects, execCommand false) => copy-failed + 'Copy failed' title/aria-label, never 'copied', reset to rest state after the flash. insta render_integration snapshot regenerated (inlined interaction.js). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: regenerate goldens on the merged tree (main @ 3cd2441 + the Gemini fix delta) All three examples re-rendered per the example-report-check recipe after merging origin/main (#204/#205/#211/#212). Byte audit: every changed line traces to the truthful fold-toggle/copy-outcome fix (a4c95d7) — main's cross-join/source-binding golden content came through the merge intact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ution honesty (#283) * feat(domain): #259 — degraded backing, exists-but-disabled, singular honest-UNKNOWN in the grain check The grain.unique-key-unbacked verdict stops overclaiming on the #258 test-config wire (the cute-dbt#259 truthfulness pack): - DegradedBacking POD + Finding.degraded (serde-skipped when empty — payload byte-stability): a covering test that is warn-severity, where-filtered, or limit-capped still attributes, with every cause enumerated per test in domain-composed copy. Never a fourth verdict, never a percentage — the three-valued covered/uncovered/unknown vocabulary stays the trust contract; the cue rides in-row beside the attribution (#262 copy principles). An unrecognized severity surfaces its raw value, never guessed at. - exists-but-disabled evidence: a disabled uniqueness test on the declared grain (config.enabled: false in nodes, or a generic-test entry in the Manifest.disabled map — the shared uniqueness_columns recognizer now serves both linkage shapes) never counts as coverage but surfaces as a distinct fact from absent. - singular-test linkage: with no enabled generic uniqueness backing, an enabled singular (SQL-file) test referencing the model via depends_on (the only wire linkage singular tests carry, #258) degrades the verdict to honest UNKNOWN — evidence states what WAS checked and enumerates the singular tests — never a false Uncovered nag on singular-test shops. Disabled-map singular entries carry no linkage (both engines empty depends_on on disabled nodes) and are declared out in the spec exclusions. Spec conditions/exclusions mirror the new predicate; registry.toml + the book check page are regenerated from SPECS (byte-gated). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(adapters): #259 — degraded-backing chip + per-test cause list on the findings panel FindingPayload flattens the domain Finding, so the new degraded array rides the wire unchanged (pinned by a build_payload test). The findings panel renders it pure-presentationally: a summary 'degraded backing' chip ONLY when every attributing test is weakened (no full-strength backing at all — a partially degraded attribution keeps the summary quiet, no false alarm), under the #146/#188 tooltip contract (focusable trigger, aria-label, CSS-positioned bubble on hover AND focus, never a native title); plus the per-test enumerated causes beside the Covered-by attribution either way (in-row honesty). The exists-but-disabled and singular-test cues render through the existing generic evidence list — no new affordance needed. Chip styling pairs always-AA --text with the non-text amber edge token, so no per-theme contrast stand-in is needed; the latte .f-label deepening extends to the new label. Chrome snapshot re-accepted (CSS/JS bundle only). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: #259 — truthfulness fixture splice + BDD/headless/real-fixture pins + golden regen playground-current.json (current side only, leak-checked node-level splice — every other byte verified identical): unique_dim_payers_payer_key gains severity warn, unique_fct_provider_metrics_provider_metrics_key gains where ('year_actual >= 2024') + limit 100 (each with the matching authored unrendered_config twin), int_patients__never_admitted gains unique_key = 'patient_id' plus a hand-authored synthetic SINGULAR test (assert_never_admitted_patients_distinct, cloned field-for-field from the engine-emitted assert_patient_dates_valid shape, depends_on-linked only), and the disabled map gains a fourth entry — a disabled unique test on mart_dq_summary.entity_type cloned from the engine-emitted disabled-generic specimen. MANIFEST.toml sha256 + provenance updated. Pins: four new check_engine real-fixture tests (warn-degraded covered, where+limit causes, exists-but-disabled on the UNCOVERED row, singular-only honest UNKNOWN); the #258 ingestion pin extended to the fourth disabled entry (none weakened); four new BDD scenarios over the subprocess wire (degraded marks ⊆ by, disabled-MAP placement, singular unknown) with the builders growing a disabled-map injection arm; one new headless test driving the chip/tooltip/causes/quiet-partial DOM. Goldens regenerated per the ci.yml recipes; payload-level diff audit confirms intended-only deltas: dim_payers covered+degraded(warn) and int_patients__never_admitted unknown(singular) and mart_dq_summary uncovered+exists-but-disabled in playground-report, fct_provider_metrics covered+degraded(where,limit) + mart_dq_summary in diff-showcase, check_specs prose everywhere, explore tests.html payload only; jaffle diff is the CSS/JS bundle alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(domain): #259 — borrow instead of allocate in coverage scans Review follow-up (Gemini, PR #283): the grain detector's covering scan collects (&str, &Node) and sorts on the borrowed key — ids become owned Strings only at the POD ownership boundary (verdict.by / DegradedBacking.by), dropping the intermediate owned collection; singular_tests_on returns Vec<&str> borrowed from the manifest — its sole consumer (grain_fallback_verdict) only formats the ids into evidence copy, so owning them allocated just to discard. Pure refactor: no payload change, goldens byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Closes #178
Design integration PR-2: replaces the template's ~2100-line inline engine with the merged redesign engine as two first-party
include_str!assets —templates/interaction.js+templates/theme.jsviaasset_embed::{INTERACTION_JS, THEME_JS}(the PR-1REPORT_CSSpattern), each guarded by banner-pin + end-of-file-sentinel tests. The PR-1 bridge layer inreport.cssis deleted per its own contract.Port-forward audit (AC1 — the critical step)
label || iddisplay + aria-label); the Rust identity fix never forkedself_named_import_cte_renders_distinct_dag_nodes_not_a_cyclestays greenminmax(0,1fr)on the stacked panel)1fr; PR-1 chassis already carried OUR fixstacked_panel_does_not_blow_out_viewport_at_375pxstays greenModelPayload.column_meta+ body-appendedth.has-col-metadivcolumn_meta) and the focusable<button>+ CSS bubble on:hoverAND:focus, aria-label, position:fixed (no overflow clipping);column_header_tooltips_focus_reveal_and_skip_bare_columnsstays green. The handoff renderer belongs to handoff PR-3 (thecolumn_metacontract)Also excluded by design: every Cytoscape surface (engine picker,
cytohost,__cuteSelectNode,cuteDagPalette) — that's handoff Bucket 2 / PR-4, not this slice.AC checklist with evidence
incremental-badgespan removed (JS-rendered fromm.is_incrementalinto the Model-SQL code-card header);defined_inis now the YAML card'scode-headerfile path (thetd-defined-inrow remains only as the no-drawer fallback). Evidence: updatedincremental_badges_modes_tooltip_and_this_given+ thecode-filenameasserts.theme.js; persists undercute-dbt.appearance.v1; DataTables dark syncs viahtml.dark. Evidence: NEWappearance_settings_flip_theme_density_diff_layout_and_persist. The cog/panel are now universal (themes apply to baseline reports too); the feat: report settings menu (cog) — diff context-lines + normalize-equality toggle #139 context-lines/normalize rows stay PR-diff-only.diffViewsHtmlemits both layouts over the samelines[].kind/text/emphasis; zero Rust diff-engine change; CSS selects perhtml[data-difflayout]with the responsive fallback (split only when wide). Evidence: NEWsplit_diff_renders_the_same_block_diff_as_unified+ the layout-flip asserts in the appearance test.templates/interaction.jsand now checks BOTH light/dark palettes).diff -qagainst a fresh render); both insta snapshots regenerated; headless network-block test green; resource-ref lint + in-crate construct scan green (no fetch / script src / link href / img src / @import / url() / protocol-relative).Gates run (all directly, not via lefthook)
cargo fmt --check·cargo clippy --all-targets --locked -- -D warnings·cargo nextest run(886 passed) ·cargo test --test bdd(96/96) ·cargo test --test headless_toggle -- --ignored(31/31) ·cargo test --test headless_zero_egress -- --ignored·RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --locked·cargo deny check·cargo llvm-cov nextest+crap4rs(1295 fns, 0 above threshold, worst 23.0, PASS) · golden byte-identity re-render check.Notes for review
edge-vocab-completeness(ci.yml) now greps the palettes intemplates/interaction.js(per-palette, light AND dark) — the palette moved out ofreport.htmlwith the engine. No lefthook mirror exists for this gate (checked).data-view="current"unchanged — the headless contract pins the data attribute, not the label).theme.jscarries agitleaks:allowon the public localStorage key name (false-positivegeneric-api-keyoncute-dbt.appearance.v1; pre-commit-only gate, no CI job).🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Improvements