Repository navigation
#169 — check-engine walking skeleton: heuristics! registry + supersedes pipeline + grain.unique-key-unbacked - #186
Conversation
…ersedes pipeline, grain.unique-key-unbacked The coverage-intelligence walking skeleton (#169, epic #168), Lane-A: - heuristics! macro_rules: one central block declares each check's spec metadata + detector fn adjacently and generates the HeuristicId enum, SPECS (one HeuristicSpec per variant, declaration order), and the id->detector pairing as an exhaustive NO-WILDCARD match. Spec without detector / detector without spec = macro expansion failure; dangling supersedes edge references a nonexistent enum VARIANT = compile error. - Verdict model per the #169 founder comment: Finding is a verdict per (construct, check) — Covered { by: attributed test ids } / Uncovered / Unknown. SUPPRESSED is display-layer only, deliberately not a variant. Finding denormalizes tier/instrument/recommendation from the spec (recommendation only on Uncovered). - Fixed pipeline order: evaluate_all (every registered check, always) -> resolve_supersedes (drop findings superseded by another FIRED finding on the same (model, construct)) -> filter_for_display (downstream). Required invariant pinned: disabling the superseding check does NOT resurrect the superseded finding. supersedes_is_acyclic gates cycles. Pipeline behaviour is exercised against synthetic multi-check registries generated by the same macro (the production registry holds one check); property-style invariants over every firing subset. - Walking-skeleton check grain.unique-key-unbacked (TOTAL, data-test): NodeConfig::unique_key() typed accessor mirrors fusion's DbtUniqueKey untagged string | array-of-strings wire shape (dbt-schemas common.rs, dbt-fusion 9977b6cbb1b761065536300037560d8e3c037011) with a tolerant Unrecognized arm -> honest UNKNOWN verdict. Satisfaction: an enabled uniqueness data test (unique kwargs.column_name; dbt_utils unique_combination_of_columns kwargs.combination_of_columns) attached to the model with column set ⊆ the key (ascii-case-folded). The composite combination stays COMPOSITE — fusion's PK inference flattens it per column (primary_key_inference.rs), which would be unsound here (pair-uniqueness does not imply per-column uniqueness). - Ledger generators (registry_toml / check_page_markdown / checks_index_markdown) render the GENERATED artifacts from SPECS — pure string computation, byte-gated by tests/heuristics_ledger.rs. Domain purity intact: std + serde (+ serde_json::Value passthrough). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ModelPayload gains `findings: Vec<Finding<HeuristicId>>`, computed during payload assembly (the parse_ctes precedent: the run loop's per-model work happens one stage downstream) via the fixed evaluate-all -> resolve-supersedes pipeline. serde-skipped when empty so every payload whose models trip no check stays byte-stable — the jaffle-shop golden is untouched. Payload-level only (#169 Lane-A): no template/render-surface changes — the findings surface is #170. Regenerated goldens whose fixture models DO carry unique_key verdicts; verified findings-payload-only diffs (payload-minus-findings and the full non-payload chrome are byte-equal to the previous artifacts): - examples/playground-report.html: dim_payers + fct_encounters_incremental gain covered grain.unique-key-unbacked findings with attribution - examples/diff-showcase-report.html: fct_provider_metrics gains a covered finding Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ication - features/coverage_checks.feature (13th feature): payload-fact scenarios over the real subprocess wire round-trip — uncovered gap + recommendation, covered-with-attribution (unique on the key column), composite combination on exactly the key, the anti-flattening case (combination WIDER than the key must NOT cover), disabled test never covers, and no-unique_key => no findings key (serde-skip). Wire-shape injection (flat model config carrying unique_key, generic-test nodes with test_metadata/attached_node/flat config.enabled) follows the serialize_incremental_to_tmp precedent. - tests/check_engine.rs: the same pipeline over the committed fusion-compiled playground fixture through the real Stage-1 adapter — composite-key UNCOVERED (fct_encounters_monthly), string-key COVERED with attribution (fct_encounters_incremental), and the real dbt_utils.unique_combination_of_columns staying composite (fct_patient_summary); jaffle-shop carries zero findings. Plus an insta snapshot pinning the serialized findings payload contract #170 will consume. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- heuristics/registry.toml + book/src/checks/{index,grain.unique-key-unbacked}.md
are GENERATED from the heuristics! block's SPECS, committed with the
"GENERATED — do not edit" header (design sketch §5b: code is the single
source; the ledger exists for the book build + human reading).
- tests/heuristics_ledger.rs: default mode regenerates in memory and
asserts byte-identity (fast signal inside cargo nextest); write mode
(GEN_HEURISTICS_LEDGER=1) regenerates in place. Also pins no-stale-pages
and every-page-in-SUMMARY (mdbook renders only SUMMARY-listed chapters).
- ci.yml heuristics-ledger job: regenerate-and-diff, the
example-report-check pattern; lefthook.yml carries the matching pre-push
mirror (mirror rule: both, atomically). The new job is NOT yet a
required branch-protection context — wired post-merge by the
orchestrator.
- feature-count expected= 12 -> 13 in BOTH ci.yml and lefthook.yml for
coverage_checks.feature, with history comments updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ite unique_key customer_order_days: incremental delete+insert at the (customer_id, order_date) grain — the LIST wire form of fusion's DbtUniqueKey — with deliberately NO uniqueness data test backing it (the paired-fixture rule, epic #168). The not_null on customer_id proves a non-uniqueness test never satisfies the grain check. The gap is visible at the payload level on the live PR-diff preview (this PR touches dbt-project/, so report-preview.yml self-renders it from the ephemeral CI-compiled fusion manifest); the report surface lands with #170. Verified locally: dbt-fusion 2.0.0-preview.177 compiles the project (8 models | 23 tests | 3 seeds, all success); the rendered --pr-diff payload carries grain.unique-key-unbacked UNCOVERED for customer_order_days and COVERED (attributed) for order_events_incremental. No target/ or root_path-bearing artifact is committed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 10 minutes and 31 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (21)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
Ready to review this PR? Stage has broken it down into 8 individual chapters for you: Chapters generated by Stage for commit 216bde8 on Jun 10, 2026 4:04pm UTC. |
There was a problem hiding this comment.
Code Review
This pull request introduces the coverage-intelligence check engine, implementing the grain.unique-key-unbacked check to identify models with declared unique keys that lack backing uniqueness tests. It adds the core pipeline for evaluating checks, resolving supersedes, and filtering findings, along with automated ledger generation, documentation, and comprehensive integration tests. Feedback on the changes suggests an improvement in src/domain/manifest.rs to parse the unique_key string array more idiomatically and avoid unnecessary string allocations by using into_iter and collecting into an Option.
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.
📄 Rendered report previewAll golden examples regenerated cleanly. 🟡 Golden examplesCommitted to
🐶 Live dogfood previewThis PR's own
▶ 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 27288984654 -R breezy-bays-labs/cute-dbt -n report-preview-playground
open report-preview-playground/playground-report.htmlPosted by |
…#171) - ChecksConfig POD: sqlfluff-style dual modes (opt-out default with disable, opt-in with enable), [[checks.suppress]] entries (check + model + required reason), deny_unknown_fields throughout - resolve_check_policy: fail-closed validation against the CheckId registry (mode/field legality, exact ids + group.* globs, unknown ids/globs error with remediation naming known checks/groups) - scan_pragmas: inline '-- cute-dbt: ignore(check-id, "reason")' pragma grammar (file-level granularity, model-wide, reason optional) - apply_check_policy: the grown filter_for_display stage — selection removes, suppression marks (Finding.suppressed carries source + reason into the payload); runs strictly after resolve_supersedes - #186 invariant extended: suppressing/disabling the superseding check never resurrects the superseded finding Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…suppress entries, inline pragma (#193) * feat(domain): [checks] selection + suppression policy, pragma scanner (#171) - ChecksConfig POD: sqlfluff-style dual modes (opt-out default with disable, opt-in with enable), [[checks.suppress]] entries (check + model + required reason), deny_unknown_fields throughout - resolve_check_policy: fail-closed validation against the CheckId registry (mode/field legality, exact ids + group.* globs, unknown ids/globs error with remediation naming known checks/groups) - scan_pragmas: inline '-- cute-dbt: ignore(check-id, "reason")' pragma grammar (file-level granularity, model-wide, reason optional) - apply_check_policy: the grown filter_for_display stage — selection removes, suppression marks (Finding.suppressed carries source + reason into the payload); runs strictly after resolve_supersedes - #186 invariant extended: suppressing/disabling the superseding check never resurrects the superseded finding Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(cli): build + thread the check display policy into payload assembly (#171) The src/adapters/render.rs touch is confined to mechanical parameter threading (render_report_with_externals / build_payload_with_externals / build_model_payload gain a check_policy param; the conveniences pass CheckPolicy::default()) plus the one findings call site now applying apply_check_policy AFTER model_findings' evaluate->resolve pipeline. No template change — the findings surface is cute-dbt#170. cli::build_check_policy resolves the validated [checks] config and extends it with inline pragmas scanned from each in-scope model's manifest raw_code (the cute-dbt#111 precedent: verbatim authored SQL, no filesystem read, works in both scope modes). Unknown pragma ids warn on stderr and stay inert — source text warns, config fails closed. Real-fixture payload tests in tests/check_engine.rs pin the two arms: disable removes (key serde-skipped), suppress keeps + marks with reason. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(adapters): fail-closed [checks] validation at --config parse time (#171) load_config now resolves the [checks] section against the production HeuristicId registry after the TOML parse; a mode/field-legality failure, unknown check id/group glob, or glob/empty-reason suppress entry is ConfigLoadError::Checks — the same clap usage-error path (exit 2) as a TOML syntax error, with the CheckConfigError remediation text naming the registry's known checks and groups. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: BDD scenarios for [checks] modes, suppression, and the inline pragma (#171) features/check_selection.feature: 9 scenarios through the real subprocess — opt-out disable (group glob), opt-in enable, opt-in empty enable, suppress entry (reason carried into the payload), inline pragma with and without a reason, and the two fail-closed usage-error paths (enable in opt-out mode; unknown check id with remediation naming known checks). Synthetic fixtures only (temp-file manifests + config TOML, nothing committed). feature-count gate bumped 13 -> 14 in ci.yml AND lefthook.yml atomically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: book page for check selection, suppression, and the pragma grammar (#171) book/src/check-selection.md (outside book/src/checks/ — that directory is generated + stale-gated): dual selection modes with fail-closed glob resolution, [[checks.suppress]] with required reason, the inline pragma grammar (file-level granularity, model-wide, optional reason, unknown id warns), and the display-layer invariant. ARCHITECTURE.md section 3 config paragraph updated for the [checks] section + ConfigLoadError::Checks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: fix render call site + pedantic lints in test code (#171) headless_toggle's render_report_with_externals call gains the new check_policy param (default policy); default_trait_access + an unused test import surfaced by clippy --all-targets --locked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: qualify the resolve_supersedes intra-doc link in check_config (#171) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(domain): pragma reason may contain a closing parenthesis (#171) parse_pragma now consumes the quoted reason structurally before looking for the call's closing paren, so ignore(id, "see RFC-12 (appendix B)") parses whole instead of truncating at the inner ')' and silently failing (PR #193 gemini review). Junk between the quoted reason and ')' is still rejected; regression tests for both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: extend selection + suppression invariant coverage to union.arm-coverage (#171) Post-rebase onto main @ 43b68d2 (#191): apply_check_policy now wraps the 3-arg model_findings(current, model, Some(&graph)) at the render call site, keeping #191's CheckContext/cte_graph shape intact. The two registry-shape-dependent tests become shape-robust (disabling an id/group removes exactly that id/group, asserted across EVERY registered check), and a new real-fixture test pins the display-layer invariant on the new check: suppressing union.arm-coverage marks the finding and changes nothing else; disabling union.* removes exactly it. 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>
Summary
Closes #169 (Lane-A of epic #168). The coverage-intelligence check engine walking skeleton at the payload level — NO template/render-surface changes (the findings surface is #170).
What landed
heuristics!single-source macro (src/domain/checks.rs): one central block declares each check's spec metadata + detector fn adjacently and generates theHeuristicIdenum,SPECS(oneHeuristicSpecper variant, declaration order), and the id→detector pairing as an exhaustive no-wildcardmatch. Spec-without-detector / detector-without-spec fails macro expansion; a danglingsupersedesentry references a nonexistent enum variant and fails to compile.Findingis a verdict per (construct, check) —Covered { by: [test ids] }(attribution from day one) /Uncovered(recommendation fires) /Unknown(honest tier, never nagged).SUPPRESSEDis display-layer only and deliberately not a variant.Finding::newdenormalizes tier/instrument/recommendation from the spec so detectors cannot mislabel them.evaluate_all(every registered check, always) →resolve_supersedes(drop findings superseded by another fired finding on the same (model, construct); shallow, no chaining) →filter_for_display(downstream). The required invariant is pinned: disabling the superseding check does NOT resurrect the superseded finding. Supersedes acyclicity is a total unit test (a deliberately-cyclic macro-generated registry proves the gate detects cycles).grain.unique-key-unbacked(TOTAL tier, data-test instrument): fires onconfig.unique_key(new typedNodeConfig::unique_key()accessor — string OR list-of-strings, with a tolerantUnrecognizedarm →UNKNOWNverdict); satisfied by any enabled uniqueness data test attached to the model whose column set ⊆ the key (case-folded).dbt_utils.unique_combination_of_columnsstays composite — fusion's PK inference flattens it per column, which would be unsound here.heuristics/registry.toml+book/src/checks/{index,grain.unique-key-unbacked}.mdare generated fromSPECSwith a GENERATED header;tests/heuristics_ledger.rscompares byte-identity in default mode and writes inGEN_HEURISTICS_LEDGER=1mode; newheuristics-ledgerCI job (regenerate-and-diff, the example-report-check pattern) + the matchinglefthook.ymlpre-push mirror.ModelPayload.findings(serde-skipped when empty), computed during payload assembly via the fixed pipeline — theparse_ctesprecedent for run-loop work happening one stage downstream.dbt-project/models/marts/customer_order_days.sql— incremental delete+insert at the composite(customer_id, order_date)grain with deliberately NO uniqueness test (the list wire form;order_events_incrementalremains the COVERED-with-attribution example). Source files only; no target/root_path artifact.dbt-fusion verification (source-first)
Pinned to dbt-fusion
9977b6cbb1b761065536300037560d8e3c037011:DbtUniqueKey= untaggedSingle(String) | Multiple(Vec<String>)(dbt-schemas/src/schemas/common.rs; carried asOption<DbtUniqueKey>on the model config).dbt-parser/src/resolve/resolve_tests/../primary_key_inference.rs(kwargs.column_namestring forunique;kwargs.combination_of_columnsstring array for the composite) — except the flattening, which is deliberately not copied.playground-current.jsonfusion fixture (tests/check_engine.rs): composite-key UNCOVERED (fct_encounters_monthly), string-key COVERED with attribution (fct_encounters_incremental), and the realdbt_utils.unique_combination_of_columnsonfct_patient_summarycorrectly NOT attributing at the single-column grain. Plus an insta snapshot pinning the findings payload contract feature: report findings surface — per-model coverage checklist + tiered findings + suppressed-count #170 consumes.Builder's calls (Discovery items)
gen-registryis test-mode write+compare (GEN_HEURISTICS_LEDGER=1 cargo test --test heuristics_ledger), not a bin target — zero new binary surface, runs inside the normal test battery in default mode, CI job does regenerate-and-diff.HeuristicSpecfield set:id(variant) /id_str/name/group/tier/instrument/supersedes/evidence/conditions/exclusions/recommendation/rationale.tests/heuristics_ledger.rspins every generated page is SUMMARY-listed and no stale pages exist) — no mdbook follow-up needed.features/coverage_checks.feature(a new product surface rather than extending an existing feature's contract): feature-count bumped 12 → 13 in bothci.ymlandlefthook.yml.CheckIdtrait implemented only by macro expansion — that is how multi-check supersedes/suppression behaviour is tested (synthetic macro-generated registries) while the production registry holds one check.Goldens
examples/playground-report.html+examples/diff-showcase-report.htmlregenerated: their fixture models carryunique_key, so they gain findings entries. Verified findings-payload-only: payload-minus-findings and the full non-payload chrome are byte-equal to the previous artifacts;examples/jaffle-shop-report.html(nounique_keyanywhere) is untouched. All three re-verified byte-identical to fresh renderer output.Gates (run directly — lefthook skips in fresh worktrees)
cargo fmt --all --check✅cargo clippy --all-targets --locked -- -D warnings✅ (exit 0)cargo nextest run --all-targets --locked✅ 844 passedcargo test --test bdd✅ 13 features / 92 scenarios / 580 steps, all passedcargo test --test headless_zero_egress --locked -- --ignored✅;headless_toggle✅ 29 passed (untouched-green)RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items --locked✅cargo deny check✅mdbook build book✅dbt compileon dbt-project ✅ (8 models / 23 tests / 3 seeds)Note for the orchestrator
The new
heuristics-ledgerCI job is not a required branch-protection context yet — wire it post-merge.🤖 Generated with Claude Code