Skip to content

#169 — check-engine walking skeleton: heuristics! registry + supersedes pipeline + grain.unique-key-unbacked - #186

Merged
cmbays merged 6 commits into
mainfrom
feature-169-check-engine-skeleton
Jun 10, 2026
Merged

cmbays merged 6 commits into
mainfrom
feature-169-check-engine-skeleton

Conversation

@cmbays

@cmbays cmbays commented Jun 10, 2026 •

Copy link
Copy Markdown
Contributor

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 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 fails macro expansion; a dangling supersedes entry references a nonexistent enum variant and fails to compile.
  • Verdict model (per the feature: check-engine walking skeleton — heuristics! registry + supersedes pipeline + byte-gated ledger + grain.unique-key-unbacked #169 founder comment): a Finding is a verdict per (construct, check) — Covered { by: [test ids] } (attribution from day one) / Uncovered (recommendation fires) / Unknown (honest tier, never nagged). SUPPRESSED is display-layer only and deliberately not a variant. Finding::new denormalizes tier/instrument/recommendation from the spec so detectors cannot mislabel them.
  • Fixed pipeline order: 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 on config.unique_key (new typed NodeConfig::unique_key() accessor — string OR list-of-strings, with a tolerant Unrecognized arm → UNKNOWN verdict); satisfied by any enabled uniqueness data test attached to the model whose column set ⊆ the key (case-folded). dbt_utils.unique_combination_of_columns stays composite — fusion's PK inference flattens it per column, which would be unsound here.
  • Generated byte-gated ledger: heuristics/registry.toml + book/src/checks/{index,grain.unique-key-unbacked}.md are generated from SPECS with a GENERATED header; tests/heuristics_ledger.rs compares byte-identity in default mode and writes in GEN_HEURISTICS_LEDGER=1 mode; new heuristics-ledger CI job (regenerate-and-diff, the example-report-check pattern) + the matching lefthook.yml pre-push mirror.
  • Payload exposure: ModelPayload.findings (serde-skipped when empty), computed during payload assembly via the fixed pipeline — the parse_ctes precedent for run-loop work happening one stage downstream.
  • Dogfood: 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_incremental remains the COVERED-with-attribution example). Source files only; no target/root_path artifact.

dbt-fusion verification (source-first)

Pinned to dbt-fusion 9977b6cbb1b761065536300037560d8e3c037011:

  • DbtUniqueKey = untagged Single(String) | Multiple(Vec<String>) (dbt-schemas/src/schemas/common.rs; carried as Option<DbtUniqueKey> on the model config).
  • Test-kwargs extraction + enabled-with-default semantics mirror dbt-parser/src/resolve/resolve_tests/../primary_key_inference.rs (kwargs.column_name string for unique; kwargs.combination_of_columns string array for the composite) — except the flattening, which is deliberately not copied.
  • Verified against the real committed playground-current.json fusion fixture (tests/check_engine.rs): composite-key UNCOVERED (fct_encounters_monthly), string-key COVERED with attribution (fct_encounters_incremental), and the real dbt_utils.unique_combination_of_columns on fct_patient_summary correctly 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-registry is 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.
  • HeuristicSpec field set: id (variant) / id_str / name / group / tier / instrument / supersedes / evidence / conditions / exclusions / recommendation / rationale.
  • Book wiring done in this PR (SUMMARY gains a Checks section; tests/heuristics_ledger.rs pins every generated page is SUMMARY-listed and no stale pages exist) — no mdbook follow-up needed.
  • New BDD file features/coverage_checks.feature (a new product surface rather than extending an existing feature's contract): feature-count bumped 12 → 13 in both ci.yml and lefthook.yml.
  • Engine pipeline is generic over a CheckId trait 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.html regenerated: their fixture models carry unique_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 (no unique_key anywhere) 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 passed
  • cargo test --test bdd ✅ 13 features / 92 scenarios / 580 steps, all passed
  • cargo 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-fusion 2.0.0-preview.177 dbt compile on dbt-project ✅ (8 models / 23 tests / 3 seeds)
  • heuristics ledger byte-gate ✅ (regenerate-and-diff clean)

Note for the orchestrator

The new heuristics-ledger CI job is not a required branch-protection context yet — wire it post-merge.

🤖 Generated with Claude Code


Open in Stage

github-actions Bot and others added 5 commits June 10, 2026 11:22
…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>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@cmbays, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a8ac471b-80f1-4b4a-85ea-a27d70f073e0

📥 Commits

Reviewing files that changed from the base of the PR and between fc6e1d4 and 216bde8.

⛔ Files ignored due to path filters (1)
  • tests/snapshots/check_engine__playground_unique_key_findings.snap is excluded by !**/*.snap
📒 Files selected for processing (21)
  • .github/workflows/ci.yml
  • book/src/SUMMARY.md
  • book/src/checks/grain.unique-key-unbacked.md
  • book/src/checks/index.md
  • dbt-project/models/marts/_incremental__models.yml
  • dbt-project/models/marts/customer_order_days.sql
  • examples/diff-showcase-report.html
  • examples/playground-report.html
  • features/coverage_checks.feature
  • heuristics/registry.toml
  • lefthook.yml
  • src/adapters/render.rs
  • src/domain/checks.rs
  • src/domain/manifest.rs
  • src/domain/mod.rs
  • tests/check_engine.rs
  • tests/heuristics_ledger.rs
  • tests/steps/builders.rs
  • tests/steps/coverage_checks.rs
  • tests/steps/mod.rs
  • tests/steps/world.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature-169-check-engine-skeleton

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.

@ghost

ghost commented Jun 10, 2026 •

Copy link
Copy Markdown

@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 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.

Comment thread src/domain/manifest.rs
@github-actions

Copy link
Copy Markdown
Contributor

📄 Rendered report preview

All golden examples regenerated cleanly.

🟡 Golden examples

Committed to examples/ and byte-identity gated — the canonical reports contributors and consumers browse. Stable across PRs.

Report View Download
diff-showcase-report.html ▶ Open ↗ ⬇ Download
playground-report.html ▶ Open ↗ ⬇ Download
jaffle-shop-report.html ▶ Open ↗ ⬇ Download

🐶 Live dogfood preview

This PR's own dbt-project/ diff, freshly compiled by fusion into an ephemeral manifest (never committed) and rendered with --pr-diff. Regenerated every PR — the live self-dogfood, not a committed example.

Report View Download
dbt-project-report.html ▶ Open ↗ ⬇ Download

▶ Open ↗ opens the report in your browser in one click —
published to this repo's GitHub Pages under /pr-186/.
⬇ Download fetches the same self-contained HTML as a workflow
artifact (auth-gated; works fully offline). Either way the report
makes zero external resource requests.

The Pages preview may take ~1 min to update after this comment
posts. On PRs from forks the Open link is unavailable (read-only
token) — use Download.

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.html

Posted by report-preview.yml for 216bde8f3973c5242cc937061efd8994231ca192. Affordance only — never blocks merge.

@cmbays
cmbays merged commit 6d12891 into main Jun 10, 2026
42 of 44 checks passed
@cmbays
cmbays deleted the feature-169-check-engine-skeleton branch June 10, 2026 16:14
github-actions Bot added a commit that referenced this pull request Jun 10, 2026
cmbays pushed a commit that referenced this pull request Jun 10, 2026
…#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>
cmbays added a commit that referenced this pull request Jun 10, 2026
…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>
cmbays pushed a commit that referenced this pull request Jun 11, 2026
…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>
cmbays added a commit that referenced this pull request Jun 11, 2026
…+ 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>
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.

feature: check-engine walking skeleton — heuristics! registry + supersedes pipeline + byte-gated ledger + grain.unique-key-unbacked

1 participant