diff --git a/AGENTS.md b/AGENTS.md index a99b4005c..67c1a19cf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -536,3 +536,5 @@ columns). Do not silently rewrite either historical form. The SHACL shapes graph (`docs/ontology/lineageweave-kg-shapes.ttl`) is the closed-world data-validation boundary for DB-to-RDF projections and is published beside the ontology. + +ADR 0294 keeps grouping-comparison incomplete-item coverage in the LineageWeave read-model boundary: consume only authorization-filtered persisted coverage for a full grouping, omit partial visibility, and never recompute psychometrics in the client. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index e91d602bc..52d2ecb61 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1089,3 +1089,5 @@ so it also covers the multi-entity opposite-order case a per-name lock would still deadlock on. Every already-cataloged entity still resolves through the unchanged, lock-free similarity-matching fast path; only the rare creation branch serializes. + +ADR 0294 adds persisted incomplete-item coverage to grouping comparison presentation while the API remains the full-group visibility authority; the frontend formats only an admitted persisted count. diff --git a/CHANGELOG.d/2.51.0-leftover-map-compare-incomplete-item.md b/CHANGELOG.d/2.51.0-leftover-map-compare-incomplete-item.md new file mode 100644 index 000000000..1daa0a71e --- /dev/null +++ b/CHANGELOG.d/2.51.0-leftover-map-compare-incomplete-item.md @@ -0,0 +1,5 @@ +## 2.51.0 — Grouping comparison incomplete-item coverage + +- Show persisted leftover-map incomplete-item coverage on grouping comparison rows with a distinct accessible label (ADR 0294). +- Preserve API authorization: partial-visibility groupings have no full-population coverage object; the client does not reconstruct or recompute psychometrics. +- Persisted zero remains visible; invalid or contradictory counts are omitted. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8340181a9..969413fc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to this project are documented here. Format follows ### Added +- Grouping comparison rows now show persisted leftover-map incomplete-item count through `leftoverMapIncompleteItemCount` (ADR 0294 / v2.51.0) only for fully caller-visible persisted groupings. Partial visibility remains omitted by the API; valid zero stays visible, and the UI never derives dropped criteria from scored-minus-used. + - Grouping comparison rows now show persisted leftover-map incomplete-post count through `leftoverMapIncompletePostCount` (ADR 0293 / v2.50.0) only for a fully caller-visible persisted grouping. Partial visibility omits the aggregate; valid persisted zero remains visible, and the UI never derives dropped posts from scored-minus-used. - Period leftover pairs now caption leftover-axis report badges with persisted leftover-map singular values `σ_k` (ADR 0292 / v2.49.0). Missing, non-finite, or negative singular values omit only that `σ` caption; rank-0 zero remains `σ 0.00`, and axis share remains distinct. diff --git a/CLAUDE.md b/CLAUDE.md index 1963b8114..58c6119de 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,3 +57,5 @@ migration replay (ADR 0166), docstring coverage, and the measurement boundary are all stated in [AGENTS.md](AGENTS.md) -- read it before changing code, tests, or runtime policy rather than restating anything here. + +ADR 0294 is the Proposed grouping-comparison incomplete-item presentation contract; preserve server-side full-group visibility gating and do not infer dropped criteria in the frontend. diff --git a/docs/adr/0294-leftover-map-compare-incomplete-item.md b/docs/adr/0294-leftover-map-compare-incomplete-item.md new file mode 100644 index 000000000..f6d84780b --- /dev/null +++ b/docs/adr/0294-leftover-map-compare-incomplete-item.md @@ -0,0 +1,13 @@ +# ADR 0294 — Grouping comparison incomplete-item coverage + +**Decision status:** Proposed + +## Problem +The grouping comparison strip omits the persisted incomplete-item count even though the full persisted grouping read model already carries authorization-filtered coverage. + +## Decision +Render `leftoverMapIncompleteItemCount(row.leftover_map_coverage)` with the distinct accessible label `Leftover map comparison incomplete items`. Reuse the persisted-count validator. Do not derive the count from scored-minus-used, pair count, plotted criteria, or any other client-side proxy. + +Only a full persisted grouping authorized by the API may carry the coverage object. Partial visibility must keep coverage absent; the frontend must not reconstruct a hidden full-population denominator or recompute psychometrics from the visible subset. Valid persisted zero remains visible. Missing, negative, non-integer, or contradictory counts fail closed. + +This ADR remains Proposed while the PR is Draft. Accepted status requires current-head hosted tests, rendered keyboard/accessibility evidence, canonical eight-locale translation-ledger convergence, independent approval, and normal protected-branch merge. diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index de424d411..1942ff510 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1389,3 +1389,7 @@ The ONET rows stacked into base branches (#743/#745/#746/#740/#732) reached `main` together through the #759 promotion; their per-base merge records are historical evidence only. The job-architecture artifact ship originally via #749 is now re-verified on `main` from the promotion. + +### 2026-09-07 — #825 current-parent reconstruction candidate + +Exact parent `#824@499d653ed6e9206249e3f3a07518ad3fc01f14bd` lacks the grouping-comparison incomplete-item note while carrying authorization-filtered persisted coverage. ADR 0294 / v2.51.0 adds that buyer-visible read-model presentation and regression. Candidate evidence is not protected-main/release evidence; current-head browser/a11y, canonical eight-locale ledger consumption, independent review, and normal protected merge remain outstanding. diff --git a/frontend/package.json b/frontend/package.json index 497940a94..2238b2b96 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.50.0", + "version": "2.51.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 2f7a7c9b1..61369d1b6 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -4308,15 +4308,30 @@ describe("App, authenticated", () => { expect( within(screen.getByLabelText("Grouping comparison")).queryByLabelText("Leftover map incomplete items"), ).not.toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getAllByLabelText( + "Leftover map comparison incomplete items", + ), + ).toHaveLength(2); + expect( + within(screen.getByLabelText("Grouping comparison")).getAllByLabelText( + "Leftover map comparison incomplete items", + )[0], + ).toHaveTextContent("Leftover map dropped 0 incomplete criteria"); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + "Leftover-map graphic incomplete items", + ), + ).not.toBeInTheDocument(); expect( screen.getByRole("button", { name: "Compare Business unit (PU): Demo Report High, mean θ 0.81" }), ).toHaveTextContent("mean θ 0.81"); await userEvent.click( screen.getByRole("button", { name: "Compare Thread group: A-100, mean θ 0.81" }), ); - expect(screen.getByRole("status")).toHaveTextContent( - "A-100 is the opened grouping. Read its mean θ and member posts below, then open a post.", - ); + expect( + screen.getByText("A-100 is the opened grouping. Read its mean θ and member posts below, then open a post."), + ).toBeInTheDocument(); expect( screen.getByRole("button", { name: /open leftover closest pair from comparison: public post/i, diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index fe3be602d..8c8773619 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -127,6 +127,7 @@ import { LEFTOVER_MAP_COMPARE_COVERAGE_LABEL, LEFTOVER_MAP_COMPARE_ITEM_COVERAGE_LABEL, LEFTOVER_MAP_COMPARE_INCOMPLETE_POST_LABEL, + LEFTOVER_MAP_COMPARE_INCOMPLETE_ITEM_LABEL, LEFTOVER_MAP_LIST_COVERAGE_LABEL, LEFTOVER_MAP_LIST_INCOMPLETE_ITEM_LABEL, LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL, @@ -4004,6 +4005,7 @@ function ReportsPanel({ const comparisonCoverageCounts = leftoverMapCoverageCounts(row.leftover_map_coverage); const comparisonItemCoverageCounts = leftoverMapItemCoverageCounts(row.leftover_map_coverage); const comparisonIncompletePostCount = leftoverMapIncompletePostCount(row.leftover_map_coverage); + const comparisonIncompleteItemCount = leftoverMapIncompleteItemCount(row.leftover_map_coverage); return (