From 4d82c2872cc27e08c0357cb4b7cf071ab81ab9d7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 30 Aug 2026 15:35:42 +0000 Subject: [PATCH 1/2] feat(reports): leftover-map unexplained leftover share on grouping comparison strip (v2.52.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caption persisted leftover-map unexplained leftover share s = U²/R² on grouping comparison leftover-pair buttons through formatLeftoverMapUnexplainedShare (ADR 0295). Distinct aria-label Leftover map comparison unexplained leftover share. Missing or non-finite s omits that badge and keeps e, R̂, d, and coverage notes. Do not invent s from U and R. UI-only; no new columns. --- AGENTS.md | 10 +- ARCHITECTURE.md | 7 +- ...-leftover-map-compare-unexplained-share.md | 43 ++++ CHANGELOG.md | 43 ++++ CLAUDE.md | 2 +- ...-leftover-map-compare-unexplained-share.md | 192 ++++++++++++++++++ docs/product-technical-gap-baseline.md | 76 +++++++ frontend/package.json | 2 +- frontend/src/App.test.tsx | 26 +++ frontend/src/App.tsx | 15 ++ frontend/src/components/LeftoverPairList.tsx | 4 +- frontend/src/i18n.test.ts | 14 ++ frontend/src/i18n.ts | 4 + frontend/src/leftoverMapPlotLayout.ts | 5 +- .../src/leftoverMapUnexplainedShare.test.ts | 14 +- frontend/src/leftoverMapUnexplainedShare.ts | 3 + pyproject.toml | 2 +- 17 files changed, 452 insertions(+), 10 deletions(-) create mode 100644 CHANGELOG.d/2.52.0-leftover-map-compare-unexplained-share.md create mode 100644 docs/adr/0295-leftover-map-compare-unexplained-share.md diff --git a/AGENTS.md b/AGENTS.md index 74f8f5784..5cd94d89f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -273,7 +273,7 @@ stops startup instead of leaving a healthy-looking partial schema, and application code must not compensate for a missing table. Period leftover pairs (ADR 0017 / 0018 / 0048 / 0049 / 0119 / 0158 / 0162 / -0163 / 0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290 / 0291 / 0292 / 0293 / 0294) are computed in `lineageweave/leftover_pairs.py` from the +0163 / 0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290 / 0291 / 0292 / 0293 / 0294 / 0295) are computed in `lineageweave/leftover_pairs.py` from the residual after a real GRM/GPCM score, never invented. Distances are Euclidean on the two-dimensional Gabriel leftover map; missing cells stay out of the factorization. Closest and farthest post–criterion pairs @@ -428,6 +428,12 @@ compares leftover pairs can match the pair-row `R̂²/R²` badge; a missing or n `e` omits that leftover-map comparison explained leftover share badge and does not invent `e` from `R̂` and `R` or from leftover-map distance. The strip does not gain the leftover-map graphic. +ADR 0295 captions grouping comparison leftover-pair buttons with persisted leftover-map +unexplained leftover share `s` through formatLeftoverMapUnexplainedShare so a buyer who +compares leftover pairs can match the pair-row `U²/R²` badge; a missing or non-finite +`s` omits that leftover-map comparison unexplained leftover share badge and does not invent +`s` from `U` and `R` or from leftover-map distance. The strip does not gain the +leftover-map graphic. When `R`, `R̂`, `U`, `x`, `s`, and `e` are finite, `e + s + x = 1`. When `Y`, `E`, and `R` are finite, `Y − E = R`. When `R`, `R̂`, and `U` are @@ -454,6 +460,8 @@ Grouping comparison reconstruction (ADR 0293) captions grouping comparison lefto buttons with persisted leftover-map reconstruction `R̂`. Grouping comparison explained leftover share (ADR 0294) captions grouping comparison leftover-pair buttons with persisted leftover-map explained leftover share `e`. +Grouping comparison unexplained leftover share (ADR 0295) captions grouping comparison leftover-pair +buttons with persisted leftover-map unexplained leftover share `s`. Item complete-case coverage (ADR 0282) captions the leftover-map graphic with how many scored criteria entered the map. Item complete-case coverage on the pair list (ADR 0285) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f0e891166..e2e6b7be8 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -679,6 +679,8 @@ ADR 0293 captions grouping comparison leftover-pair buttons with persisted lefto reconstruction `R̂` through formatLeftoverMapReconstruction. ADR 0294 captions grouping comparison leftover-pair buttons with persisted leftover-map explained leftover share `e` through formatLeftoverMapExplainedShare. +ADR 0295 captions grouping comparison leftover-pair buttons with persisted leftover-map +unexplained leftover share `s` through formatLeftoverMapUnexplainedShare. Click a post marker or a pair button opens that post. Those ADRs are the normative mathematical, storage, and display contracts. Leftover-map axis share @@ -700,8 +702,9 @@ and carries the same ABAC-filtered leftover pairs (ADR 0149) plus persisted leftover-map complete-case coverage (ADR 0289), leftover-map item complete-case coverage (ADR 0290), leftover-map incomplete post coverage (ADR 0291), leftover-map incomplete item coverage (ADR 0292), leftover-map reconstruction `R̂` -(ADR 0293), and leftover-map explained leftover share `e` -(ADR 0294); +(ADR 0293), leftover-map explained leftover share `e` +(ADR 0294), and leftover-map unexplained leftover share `s` +(ADR 0295); `POST .../rebuild` scores every grouping kind (post_admin). `make seed` folds A-100/B-200 Event Lineage fixtures (and the Riverbend calendar post) that already have constructed IRT cells into the same shared diff --git a/CHANGELOG.d/2.52.0-leftover-map-compare-unexplained-share.md b/CHANGELOG.d/2.52.0-leftover-map-compare-unexplained-share.md new file mode 100644 index 000000000..f275ce4eb --- /dev/null +++ b/CHANGELOG.d/2.52.0-leftover-map-compare-unexplained-share.md @@ -0,0 +1,43 @@ +## 2.52.0 — Leftover-map unexplained leftover share on grouping comparison strip pair rows + +- Caption leftover-map unexplained leftover share `s` on grouping comparison + strip leftover-pair buttons through formatLeftoverMapUnexplainedShare + (ADR 0295). After `make seed`, closest and farthest leftover pairs sit + above the member list with the Gabriel biplot of already-named + coordinates, leftover-map axis share when finite, axis ticks that match + `ξ (x, y) ζ (x, y)` on the pair row, pair segments that match `d` on + the pair row, pair segments that match `R̂` on the pair row, pair + segments that match `R̂²/R²` on the pair row, pair segments that match + `U²/R²` on the pair row, pair segments that match `2R̂U/R²` on the pair + row, pair segments that match `U` on the pair row, pair segments that + match `R` on the pair row, pair segments that match `Y` on the pair row, + pair segments that match `E` on the pair row, pair segments that match + leftover-map rank on the pair row, a plot caption that matches + leftover-map complete-case coverage above the pair list, a plot caption + that matches leftover-map item complete-case coverage, a plot caption + that matches leftover-map incomplete post coverage, a plot caption that + matches leftover-map incomplete item coverage, a pair-list note that + matches leftover-map post complete-case coverage, a pair-list note that + matches leftover-map item complete-case coverage, a pair-list note that + matches leftover-map incomplete post coverage, a pair-list note that + matches leftover-map incomplete item coverage, a grouping comparison + note that matches leftover-map post complete-case coverage, a grouping + comparison note that matches leftover-map item complete-case coverage, + a grouping comparison note that matches leftover-map incomplete post + coverage, a grouping comparison note that matches leftover-map + incomplete item coverage, grouping comparison leftover-pair buttons + that match leftover-map reconstruction `R̂` on the pair row, grouping + comparison leftover-pair buttons that match leftover-map explained + leftover share `e` on the pair row, and grouping comparison leftover-pair + buttons that match leftover-map unexplained leftover share `s` on the + pair row; click a post marker or a pair button opens that post. A missing + or non-finite `s` omits that leftover-map comparison unexplained leftover + share badge and keeps leftover-map explained leftover share `e` when + finite, leftover-map reconstruction `R̂` when finite, leftover-map + distance `d`, plus the strip coverage notes. Do not invent `s` from `U` + and `R`, leftover-map distance, plotted coordinates, leftover-map + reconstruction, leftover residual, leftover-map rank, leftover-map post + coverage, leftover-map item coverage, leftover-map incomplete post + coverage, leftover-map incomplete item coverage, or the count of unused + axes. The strip does not gain the leftover-map graphic. Never invent a + leftover score. Never invent a theta. No new columns. diff --git a/CHANGELOG.md b/CHANGELOG.md index b2548f752..b9b2d3e75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,49 @@ All notable changes to this project are documented here. Format follows ### Added +- Period leftover pairs now caption grouping comparison leftover-pair + buttons with persisted leftover-map unexplained leftover share `s` through + formatLeftoverMapUnexplainedShare (ADR 0295 / v2.52.0). After `make seed`, + closest and farthest leftover pairs sit above the member list with the + Gabriel biplot of already-named coordinates, leftover-map axis share when + finite, axis ticks that match `ξ (x, y) ζ (x, y)` on the pair row, pair + segments that match `d` on the pair row, pair segments that match `R̂` on + the pair row, pair segments that match `R̂²/R²` on the pair row, pair + segments that match `U²/R²` on the pair row, pair segments that match + `2R̂U/R²` on the pair row, pair segments that match `U` on the pair row, + pair segments that match `R` on the pair row, pair segments that match + `Y` on the pair row, pair segments that match `E` on the pair row, pair + segments that match leftover-map rank on the pair row, a plot caption + that matches leftover-map complete-case coverage above the pair list, a + plot caption that matches leftover-map item complete-case coverage, a + plot caption that matches leftover-map incomplete post coverage, a plot + caption that matches leftover-map incomplete item coverage, a pair-list + note that matches leftover-map post complete-case coverage, a pair-list + note that matches leftover-map item complete-case coverage, a pair-list + note that matches leftover-map incomplete post coverage, a pair-list + note that matches leftover-map incomplete item coverage, a grouping + comparison note that matches leftover-map post complete-case coverage, + a grouping comparison note that matches leftover-map item + complete-case coverage, a grouping comparison note that matches + leftover-map incomplete post coverage, a grouping comparison note + that matches leftover-map incomplete item coverage, grouping comparison + leftover-pair buttons that match leftover-map reconstruction `R̂` on the + pair row, grouping comparison leftover-pair buttons that match + leftover-map explained leftover share `e` on the pair row, and grouping + comparison leftover-pair buttons that match leftover-map unexplained + leftover share `s` on the pair row; click a post marker or a pair button + opens that post. A missing or non-finite `s` omits that leftover-map + comparison unexplained leftover share badge and keeps leftover-map + explained leftover share `e` when finite, leftover-map reconstruction + `R̂` when finite, leftover-map distance `d`, plus the strip coverage + notes. Do not invent `s` from `U` and `R`, leftover-map distance, + plotted coordinates, leftover-map reconstruction, leftover residual, + leftover-map rank, leftover-map post coverage, leftover-map item + coverage, leftover-map incomplete post coverage, leftover-map + incomplete item coverage, or the count of unused axes. The strip does + not gain the leftover-map graphic. Never invent a leftover score or a + theta. No new columns. + - Period leftover pairs now caption grouping comparison leftover-pair buttons with persisted leftover-map explained leftover share `e` through formatLeftoverMapExplainedShare (ADR 0294 / v2.51.0). After `make seed`, diff --git a/CLAUDE.md b/CLAUDE.md index 6993e4098..fecef469e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,7 +49,7 @@ cutoff. Global Ask optional `knowledge_cutoff` uses the same cover Create/start endpoint rules (ADR 0017 / 0021), tie-vs-miss similarity (ADR 0026), R&R catalog ids (ADR 0019 / 0027), leftover pairs -(ADR 0048–0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290 / 0291 / 0292 / 0293 / 0294), occupational construct catalog search +(ADR 0048–0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290 / 0291 / 0292 / 0293 / 0294 / 0295), occupational construct catalog search (ADR 0257), the text-channel embedding swap and cosine clamp (ADR 0190), per-edge channel-score persistence (ADR 0195), token-backed status notices (ADR 0220), diff --git a/docs/adr/0295-leftover-map-compare-unexplained-share.md b/docs/adr/0295-leftover-map-compare-unexplained-share.md new file mode 100644 index 000000000..397cc7d6f --- /dev/null +++ b/docs/adr/0295-leftover-map-compare-unexplained-share.md @@ -0,0 +1,192 @@ +# ADR 0295 — Name leftover-map unexplained leftover share on grouping comparison strip pair rows + +**Decision status:** Accepted +**Date:** 2026-08-31 + +Amends leftover pairs on the grouping comparison strip +([ADR 0149](0149-leftover-pairs-on-comparison-strip.md)), leftover-map +unexplained leftover share persistence +([ADR 0233](0233-leftover-map-unexplained-share.md)), leftover-map unexplained +leftover share on pair segments +([ADR 0274](0274-leftover-map-segment-unexplained-share.md)), leftover-map +explained leftover share on grouping comparison strip pair rows +([ADR 0294](0294-leftover-map-compare-explained-share.md)), leftover-map +reconstruction on grouping comparison strip pair rows +([ADR 0293](0293-leftover-map-compare-reconstruction.md)), leftover-map +incomplete item coverage on the grouping comparison strip +([ADR 0292](0292-leftover-map-compare-incomplete-item.md)), leftover-map +incomplete post coverage on the grouping comparison strip +([ADR 0291](0291-leftover-map-compare-incomplete-post.md)), leftover-map item +complete-case coverage on the grouping comparison strip +([ADR 0290](0290-leftover-map-compare-item-coverage.md)), leftover-map +complete-case coverage on the grouping comparison strip +([ADR 0289](0289-leftover-map-compare-coverage.md)), leftover-map +explained leftover share persistence +([ADR 0266](0266-leftover-map-explained-share.md)), leftover-map explained +leftover share on pair segments +([ADR 0273](0273-leftover-map-segment-explained-share.md)), leftover-map +reconstruction persistence ([ADR 0201](0201-leftover-map-reconstruction.md)), +and leftover-map reconstruction on pair segments +([ADR 0272](0272-leftover-map-segment-reconstruction.md)). Independent of leftover-map +post complete-case coverage fail-closed on the pair list +([ADR 0288](0288-leftover-map-list-post-coverage-helper.md)), leftover-map +incomplete item coverage on the pair list +([ADR 0287](0287-leftover-map-list-incomplete-item.md)), leftover-map incomplete +post coverage on the pair list +([ADR 0286](0286-leftover-map-list-incomplete-post.md)), leftover-map item +complete-case coverage on the pair list +([ADR 0285](0285-leftover-map-list-item-coverage.md)), leftover-map incomplete +item coverage on the graphic display +([ADR 0284](0284-leftover-map-plot-incomplete-item.md)), leftover-map incomplete +post coverage on the graphic display +([ADR 0283](0283-leftover-map-plot-incomplete.md)), leftover-map item +complete-case coverage on the graphic display +([ADR 0282](0282-leftover-map-plot-item-coverage.md)), leftover-map complete-case +coverage on the graphic display +([ADR 0281](0281-leftover-map-plot-coverage.md)), leftover-map rank on pair +segments ([ADR 0280](0280-leftover-map-segment-rank.md)), leftover expected on pair +segments ([ADR 0279](0279-leftover-map-segment-expected.md)), leftover observed on +pair segments ([ADR 0278](0278-leftover-map-segment-observed.md)), leftover +residual on pair segments ([ADR 0277](0277-leftover-map-segment-residual.md)), +leftover-map unexplained leftover on pair segments +([ADR 0276](0276-leftover-map-segment-unexplained-leftover.md)), leftover-map +cross share on pair segments ([ADR 0275](0275-leftover-map-segment-cross-share.md)), +leftover-map distance on pair segments ([ADR 0271](0271-leftover-map-segment-distance.md)), +leftover-map coordinate ticks ([ADR 0270](0270-leftover-map-coordinate-ticks.md)), +leftover-map axis share on the graphic display +([ADR 0269](0269-leftover-map-axis-share-plot.md)), leftover residual +disclosure ([ADR 0162](0162-leftover-residual-disclosure.md)), leftover +observed `Y` / expected `E` ([ADR 0163](0163-leftover-observed-expected.md)), +leftover-map cross share persistence +([ADR 0185](0185-leftover-map-cross-share.md)), leftover-map unexplained leftover +persistence ([ADR 0182](0182-leftover-map-unexplained.md)), leftover-map rank +persistence ([ADR 0164](0164-leftover-map-rank.md)), and leftover-map axis +share persistence ([ADR 0148](0148-leftover-map-axis-share.md)). + +## Context + +ADR 0233 already persists leftover-map unexplained leftover share +`s = U² / R²` of raw residual after two-axis Gabriel reconstruction. +ADR 0182 already persists unexplained leftover `U`. ADR 0049 already names +that share on the period-report pair list. ADR 0274 already names it on +leftover-map graphic-display pair segments. ADR 0294 already captions +persisted leftover-map explained leftover share `e` on grouping comparison +leftover-pair buttons. The strip pair button still does not name `s`, so a +buyer who compares leftover pairs can treat leftover-map explained leftover +share `e`, leftover-map reconstruction `R̂`, or leftover-map distance `d` as +the leftover the truncated map cannot reconstruct even after the pair list +and graphic already name `U²/R²`. When `R`, `R̂`, `U`, `x`, `s`, and `e` +are finite, `e + s + x = 1`; hiding `s` on the strip lets that identity +vanish without a next action. + +This increment captions persisted leftover-map unexplained leftover share on +each grouping comparison leftover-pair button through +formatLeftoverMapUnexplainedShare. It does not add columns. It does not +recompute `s` from `U` and `R`, leftover-map distance, plotted +coordinates, leftover-map reconstruction, leftover residual, or leftover-map +explained leftover share. It does not persist leftover-map inner product, +cosine, or length as separate columns. Do not invent a leftover score. Do +not invent a theta. + +The dashboard stack already used neighbouring leftover facts under other +numbers. This protected increment uses **0295** so it does not collide with +leftover-map explained leftover share on grouping comparison strip pair rows +(0294), leftover-map reconstruction on grouping comparison strip pair rows +(0293), leftover-map incomplete item coverage on the grouping comparison +strip (0292), leftover-map incomplete post coverage on the grouping +comparison strip (0291), leftover-map item complete-case coverage on the +grouping comparison strip (0290), leftover-map complete-case coverage on +the grouping comparison strip (0289), leftover-map unexplained leftover +share on pair segments (0274), leftover-map unexplained leftover share +persistence (0233), leftover-map explained leftover share on pair segments +(0273), leftover-map explained leftover share persistence (0266), +leftover-map reconstruction on pair segments (0272), leftover-map +reconstruction persistence (0201), or the dashboard stacks. + +## Decision + +On the grouping comparison strip, caption each leftover pair button with +the same persisted leftover-map unexplained leftover share formatter as the +pair-row `U²/R²` badge, next to leftover-map explained leftover share `e`, +leftover-map reconstruction `R̂`, and leftover-map distance `d`. Use the +distinct accessible name `Leftover map comparison unexplained leftover share` +so the strip badge is not the graphic caption +(`leftover-map unexplained leftover share {label}`). A missing or +non-finite `s` omits that leftover-map comparison unexplained leftover +share badge and keeps leftover-map explained leftover share `e` when +finite, leftover-map reconstruction `R̂` when finite, leftover-map +distance `d`, the strip leftover-map post coverage note, leftover-map item +coverage note, leftover-map incomplete post note, leftover-map incomplete +item note, leftover pairs, and any leftover-map captions on the pair list +and graphic. Rank-0 origin cells still name `U²/R² 0.00` when that +persisted share is finite. A finite share greater than 1 is shown, never +clamped. Do not invent `s` from `U` and `R`, leftover-map distance, plotted +coordinates, leftover-map reconstruction, leftover residual, leftover-map +explained leftover share, leftover-map rank, leftover-map post coverage, +leftover-map item coverage, leftover-map incomplete post coverage, +leftover-map incomplete item coverage, or the count of unused axes. Do not +add the leftover-map graphic to the strip. Click a leftover pair on the +strip to open that post. + +Do not add SQL. Do not edit shipped migrations. Do not persist inner +product, cosine, or length as separate columns. Do not invent a leftover +score. Do not invent a theta. + +## Consequences + +After `make seed`, the grouping comparison strip names persisted leftover-map +post complete-case coverage, persisted leftover-map item complete-case +coverage, persisted leftover-map incomplete post coverage, and persisted +leftover-map incomplete item coverage on each grouping row when +leftoverMapCoverageCounts / leftoverMapItemCoverageCounts / +leftoverMapIncompletePostCount / leftoverMapIncompleteItemCount return usable +integers, then names leftover pairs with leftover-map distance `d`, persisted +leftover-map reconstruction `R̂` when finite, persisted leftover-map +explained leftover share `e` when finite, and persisted leftover-map +unexplained leftover share `s` when finite. Closest and farthest leftover +pairs still sit above the member list with the leftover-map graphic display; +click a post marker or a pair button opens that post. +Hidden posts stay hidden. When `R`, `R̂`, `U`, `x`, `s`, and `e` are +finite, `e + s + x = 1`. + +## Related + +Independent of leftover interaction-map persistence, leftover-criterion +evaluation landing, leftover residual disclosure, leftover-map +complete-case coverage persistence, leftover-map axis share persistence, +leftover pairs on the grouping comparison strip, leftover-map complete-case +coverage on the grouping comparison strip, leftover-map item complete-case +coverage on the grouping comparison strip, leftover-map incomplete post +coverage on the grouping comparison strip, leftover-map incomplete item +coverage on the grouping comparison strip, leftover-map reconstruction on +grouping comparison strip pair rows, leftover-map explained leftover share +on grouping comparison strip pair rows, leftover-map inner product, +leftover-map cosine, leftover-map length, leftover-map graphic display, +leftover-map unexplained leftover share on pair segments, leftover-map +unexplained leftover share persistence, leftover-map explained leftover +share on pair segments, leftover-map explained leftover share persistence, +leftover-map reconstruction on pair segments, leftover-map reconstruction +persistence, leftover-map item complete-case coverage on the graphic +display, leftover-map item complete-case coverage on the pair list, +leftover-map incomplete post coverage on the graphic display, leftover-map +incomplete post coverage on the pair list, leftover-map incomplete item +coverage on the graphic display, leftover-map incomplete item coverage on +the pair list, and leftover-map post complete-case coverage fail-closed on +the pair list. + +## References + +Gabriel, K. R. (1971). The biplot graphic display of matrices with +application to principal component analysis. *Biometrika, 58*(3), +453–467. https://doi.org/10.1093/biomet/58.3.453 + +Jeon, M., Jin, I. H., Schweinberger, M., & Baugh, S. (2021). Mapping +unobserved item–respondent interactions: A latent space item response +model with interaction map. *Psychometrika, 86*(2), 378–403. +https://doi.org/10.1007/s11336-021-09762-5 +(LSIRM interaction `−γ‖ξ_j − ζ_i‖` after main effects +`α_j − β_i`; typically `p = 2` for the interaction map. Unexplained leftover +share of the leftover cell is `s = U² / R²` after two-axis Gabriel +reconstruction. Grouping comparison unexplained leftover share names that +persisted square share on the strip pair row only when +formatLeftoverMapUnexplainedShare returns a usable badge.) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 2b7e593ee..507437366 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,81 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-08-31 00:42 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1` after unauthorized squash +> of leftover-map coordinates (v2.24.0 / #782). Revert #808 +> (`revert-pr782-unauthorized` @ `1af3e53e`) restores `main` toward +> `fc13acaa` (v2.23.0) and still needs independent APPROVE. GitHub +> writes work (comment/close/create-PR/push). Token still has empty +> `X-OAuth-Scopes`; repo permission is ADMIN. Open leftover stack +> still lacks independent APPROVE. Do not self-approve. +> #782 leftover-map coordinates through pair-segment distance +> (v2.24.0–v2.28.0 / ADR 0267–0271) is `9bdd3e4d`. #802 explained +> leftover share on pair segments (v2.30.0 / ADR 0273, includes v2.29 +> reconstruction) is `79ec22f0`. #803 unexplained leftover share on pair +> segments (v2.31.0 / ADR 0274) is `089a1571`. #804 leftover-map cross +> share on pair segments (v2.32.0 / ADR 0275) is `c513002b`. #805 leftover-map +> unexplained leftover `U` on pair segments (v2.33.0 / ADR 0276) is +> `e3fdfd74`. #806 leftover residual `R` on pair segments (v2.34.0 / +> ADR 0277) is `33f6c3dd`. #809 leftover observed `Y` on pair segments +> (v2.35.0 / ADR 0278) is `b334b00e`. #810 leftover expected `E` on pair +> segments (v2.36.0 / ADR 0279) is `6e37757a`. #811 leftover-map rank on +> pair segments (v2.37.0 / ADR 0280) is `e626a1d0`. #812 leftover-map +> complete-case coverage on the graphic (v2.38.0 / ADR 0281) is `64964cb6`. +> #813 leftover-map item complete-case coverage on the graphic (v2.39.0 / +> ADR 0282) is `de2a8a8b`. #814 leftover-map incomplete post coverage on +> the graphic (v2.40.0 / ADR 0283) is `77292872`. #815 leftover-map +> incomplete item coverage on the graphic (v2.41.0 / ADR 0284) is +> `63092de`. #816 leftover-map item complete-case coverage on the pair +> list (v2.42.0 / ADR 0285) is `1e3d13e`. #817 leftover-map incomplete +> post coverage on the pair list (v2.43.0 / ADR 0286) is `ef30930`. +> #818 leftover-map incomplete item coverage on the pair list +> (v2.44.0 / ADR 0287) is `aa32089`. #819 leftover-map post complete-case +> coverage fail-closed on the pair list (v2.45.0 / ADR 0288) is `a2c96551`. +> #821 leftover-map complete-case coverage on the grouping comparison +> strip (v2.46.0 / ADR 0289) is `11a78553`. #822 leftover-map item +> complete-case coverage on the grouping comparison strip (v2.47.0 / +> ADR 0290) is `6726353a`. #823 leftover-map singular values on leftover-axis +> badges (v2.47.0 / ADR 0290, plot stack) is `eb172c40`. #824 leftover-map +> incomplete post coverage on the grouping comparison strip (v2.48.0 / +> ADR 0291) is `8b2ed95`. #825 leftover-map incomplete item coverage on +> the grouping comparison strip (v2.49.0 / ADR 0292) is `bca6baa`. #826 leftover-map +> reconstruction `R̂` on grouping comparison leftover-pair buttons +> (v2.50.0 / ADR 0293) is `a4bf239`. #827 leftover-map explained leftover +> share `e` on grouping comparison leftover-pair buttons (v2.51.0 / +> ADR 0294) is `ead81bd`. Leave +> #782/#802/#803/#804/#805/#806/#808/#809/#810/#811/#812/#813/#814/#815/#816/#817/#818/#819/#821/#822/#823/#824/#825/#826/#827 open for +> independent review. Do not squash-merge stacked leftover PRs onto an +> unprotected leftover base. Do not merge #808 without independent APPROVE. +> Strix on #782 failed closed at `Run Strix (quick)`; do not weaken +> fail-closed on `Vulnerabilities [1-9]`. Org Strix `ModelBehaviorError` +> classifier is already on ContextualWisdomLab/.github. Copilot review is +> not independent APPROVE. Issues #79 and #87 stay open. #96 is already +> closed. Only collaborator is `seonghobae`; no independent reviewer can +> be requested from this token. +> +> Next buyer increment on this cycle: leftover-map unexplained leftover share +> `s` on grouping comparison leftover-pair buttons (ADR 0295 / v2.52.0) +> delivered locally on `feat/leftover-map-compare-unexplained-share-v2520`. +> Caption each strip leftover-pair button through +> formatLeftoverMapUnexplainedShare with distinct aria-label +> `Leftover map comparison unexplained leftover share` next to leftover-map +> explained leftover share `e`, leftover-map reconstruction `R̂`, and +> leftover-map distance `d`. UI-only; no new columns. Missing or non-finite +> `s` omits that leftover-map comparison unexplained leftover share badge +> independently of `e`, `R̂`, `d`, and coverage notes. Do not invent `s` +> from `U` and `R`, leftover-map distance, plotted coordinates, +> leftover-map reconstruction, leftover residual, leftover-map explained +> leftover share, leftover-map rank, leftover-map post coverage, +> leftover-map item coverage, leftover-map incomplete post coverage, +> leftover-map incomplete item coverage, or the count of unused axes. Do +> not caption the leftover-map graphic on the strip. Do not invent leftover +> scores. Stack onto leftover branch +> `feat/leftover-map-compare-explained-share-v2510` / #827; leave the PR +> open for independent review. Do not squash-merge onto the leftover +> base. Do not persist leftover-map inner product, cosine, or length as +> separate columns. + + > Exact-head loop overlay: 2026-08-31 00:32 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1` after unauthorized squash > of leftover-map coordinates (v2.24.0 / #782). Revert #808 diff --git a/frontend/package.json b/frontend/package.json index 2238b2b96..10d0e822c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.51.0", + "version": "2.52.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index ef5398028..0649589c4 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -934,6 +934,7 @@ describe("App, authenticated", () => { leftover_residual: 0.4, leftover_map_reconstruction: 0.248, leftover_map_explained_share: 0.76, + leftover_map_unexplained_share: 0.02, }, ], leftover_map_coverage: { @@ -4365,6 +4366,21 @@ describe("App, authenticated", () => { "Leftover map comparison explained leftover share", ), ).toHaveLength(1); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + /leftover-map unexplained leftover share/, + ), + ).not.toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getByLabelText( + "Leftover map comparison unexplained leftover share", + ), + ).toHaveTextContent("U²/R² 0.02"); + expect( + within(screen.getByLabelText("Grouping comparison")).getAllByLabelText( + "Leftover map comparison unexplained leftover share", + ), + ).toHaveLength(1); expect( screen.getByRole("button", { name: /open leftover closest pair from comparison: specification revision requested/i, @@ -4380,6 +4396,11 @@ describe("App, authenticated", () => { name: /open leftover closest pair from comparison: specification revision requested/i, }), ).not.toHaveTextContent("R̂²/R²"); + expect( + screen.getByRole("button", { + name: /open leftover closest pair from comparison: specification revision requested/i, + }), + ).not.toHaveTextContent("U²/R²"); expect( screen.getByRole("button", { name: "Compare Business unit (PU): Demo Report High, mean θ 0.81" }), ).toHaveTextContent("mean θ 0.81"); @@ -4409,6 +4430,11 @@ describe("App, authenticated", () => { name: /open leftover closest pair from comparison: public post/i, }), ).toHaveTextContent("R̂²/R² 0.76"); + expect( + screen.getByRole("button", { + name: /open leftover closest pair from comparison: public post/i, + }), + ).toHaveTextContent("U²/R² 0.02"); await waitFor(() => expect(fetchMock).toHaveBeenCalledWith( expect.stringContaining("/api/reports/thread_group/2026-W02"), diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3be880042..68e4f1a6a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -145,6 +145,10 @@ import { formatLeftoverMapExplainedShare, LEFTOVER_MAP_COMPARE_EXPLAINED_SHARE_LABEL, } from "./leftoverMapExplainedShare"; +import { + formatLeftoverMapUnexplainedShare, + LEFTOVER_MAP_COMPARE_UNEXPLAINED_SHARE_LABEL, +} from "./leftoverMapUnexplainedShare"; import "./App.css"; const AdminPanel = lazy(() => import("./components/AdminPanel").then((module) => ({ default: module.AdminPanel }))); @@ -4073,6 +4077,9 @@ function ReportsPanel({ const explainedShare = formatLeftoverMapExplainedShare( pair.leftover_map_explained_share, ); + const unexplainedShare = formatLeftoverMapUnexplainedShare( + pair.leftover_map_unexplained_share, + ); return (
  • ) : null} + {unexplainedShare ? ( + + {unexplainedShare} + + ) : null}
  • ); diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index 3cef9e167..aa6e6ee85 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -90,7 +90,9 @@ export type LeftoverPairListProps = { * incomplete item coverage on the grouping comparison strip, not this pair list. ADR 0293 * names persisted leftover-map reconstruction on grouping comparison leftover-pair * buttons, not this pair list. ADR 0294 names persisted leftover-map explained leftover - * share on grouping comparison leftover-pair buttons, not this pair list. Every badge still + * share on grouping comparison leftover-pair buttons, not this pair list. ADR 0295 names + * persisted leftover-map unexplained leftover share on grouping comparison leftover-pair + * buttons, not this pair list. Every badge still * renders together before opening the named post. */ export function LeftoverPairList({ diff --git a/frontend/src/i18n.test.ts b/frontend/src/i18n.test.ts index 6fdf4645e..41bc10a18 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -83,6 +83,7 @@ describe("i18n", () => { "Leftover map comparison incomplete items", "Leftover map comparison reconstruction", "Leftover map comparison explained leftover share", + "Leftover map comparison unexplained leftover share", "Leftover-map graphic item coverage", "Leftover map item coverage", "Leftover map incomplete posts", @@ -449,6 +450,19 @@ describe("i18n", () => { }, ); + it.each([ + ["ko", "잔여 지도 비교 미설명 잔여 점유율"], + ["zh", "残差地图比较未解释残差份额"], + ["ja", "残差マップの比較未説明残差割合"], + ["vi", "Phần dư chưa giải thích so sánh bản đồ phần dư"], + ] as const)( + "formats leftover map comparison unexplained leftover share label in %s", + (locale, expected) => { + setLocale(locale); + expect(t("Leftover map comparison unexplained leftover share")).toBe(expected); + }, + ); + it.each([ ["ko", "잔여 지도 그림 기준 포함 범위"], ["zh", "残差图图形准则覆盖范围"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 0383de625..6c08da157 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -274,6 +274,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison incomplete items": "잔여 지도 비교 불완전 기준", "Leftover map comparison reconstruction": "잔여 지도 비교 재구성", "Leftover map comparison explained leftover share": "잔여 지도 비교 설명 잔여 점유율", + "Leftover map comparison unexplained leftover share": "잔여 지도 비교 미설명 잔여 점유율", "Leftover map item coverage": "잔여 지도 기준 포함 범위", "Leftover map incomplete posts": "잔여 지도 불완전 글", "Leftover map incomplete items": "잔여 지도 불완전 기준", @@ -886,6 +887,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison incomplete items": "残差地图比较不完整准则", "Leftover map comparison reconstruction": "残差地图比较重建", "Leftover map comparison explained leftover share": "残差地图比较已解释残差份额", + "Leftover map comparison unexplained leftover share": "残差地图比较未解释残差份额", "Leftover map item coverage": "残差地图准则覆盖范围", "Leftover map incomplete posts": "残差地图不完整帖文", "Leftover map incomplete items": "残差地图不完整准则", @@ -1519,6 +1521,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison incomplete items": "残差マップの比較不完全基準", "Leftover map comparison reconstruction": "残差マップの比較再構成", "Leftover map comparison explained leftover share": "残差マップの比較説明済み残差割合", + "Leftover map comparison unexplained leftover share": "残差マップの比較未説明残差割合", "Leftover map item coverage": "残差マップの基準対象範囲", "Leftover map incomplete posts": "残差マップの不完全投稿", "Leftover map incomplete items": "残差マップの不完全基準", @@ -2131,6 +2134,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison incomplete items": "Tiêu chí không đầy đủ so sánh trên bản đồ phần dư", "Leftover map comparison reconstruction": "Tái dựng so sánh bản đồ phần dư", "Leftover map comparison explained leftover share": "Phần dư giải thích so sánh bản đồ phần dư", + "Leftover map comparison unexplained leftover share": "Phần dư chưa giải thích so sánh bản đồ phần dư", "Leftover map item coverage": "Phạm vi tiêu chí bản đồ phần dư", "Leftover map incomplete posts": "Bài không đầy đủ trên bản đồ phần dư", "Leftover map incomplete items": "Tiêu chí không đầy đủ trên bản đồ phần dư", diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index a93617c05..a0858de36 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -21,8 +21,9 @@ * (ADR 0290), grouping comparison incomplete post coverage * (ADR 0291), grouping comparison incomplete item coverage * (ADR 0292), grouping comparison reconstruction - * (ADR 0293), and grouping comparison explained leftover share - * (ADR 0294) caption the pair list or the grouping comparison strip, + * (ADR 0293), grouping comparison explained leftover share + * (ADR 0294), and grouping comparison unexplained leftover share + * (ADR 0295) caption the pair list or the grouping comparison strip, * not this graphic layout. */ diff --git a/frontend/src/leftoverMapUnexplainedShare.test.ts b/frontend/src/leftoverMapUnexplainedShare.test.ts index a5ce71272..735a76eba 100644 --- a/frontend/src/leftoverMapUnexplainedShare.test.ts +++ b/frontend/src/leftoverMapUnexplainedShare.test.ts @@ -1,5 +1,8 @@ import { describe, expect, it } from "vitest"; -import { formatLeftoverMapUnexplainedShare } from "./leftoverMapUnexplainedShare"; +import { + formatLeftoverMapUnexplainedShare, + LEFTOVER_MAP_COMPARE_UNEXPLAINED_SHARE_LABEL, +} from "./leftoverMapUnexplainedShare"; describe("formatLeftoverMapUnexplainedShare", () => { it("names leftover-map unexplained leftover share without inventing a leftover score", () => { @@ -15,4 +18,13 @@ describe("formatLeftoverMapUnexplainedShare", () => { expect(formatLeftoverMapUnexplainedShare(Number.POSITIVE_INFINITY)).toBeNull(); expect(formatLeftoverMapUnexplainedShare(Number.NEGATIVE_INFINITY)).toBeNull(); }); + + it("keeps the grouping comparison unexplained leftover share label distinct from the graphic unexplained leftover share label", () => { + expect(LEFTOVER_MAP_COMPARE_UNEXPLAINED_SHARE_LABEL).toBe( + "Leftover map comparison unexplained leftover share", + ); + expect(LEFTOVER_MAP_COMPARE_UNEXPLAINED_SHARE_LABEL).not.toBe( + "leftover-map unexplained leftover share {label}", + ); + }); }); diff --git a/frontend/src/leftoverMapUnexplainedShare.ts b/frontend/src/leftoverMapUnexplainedShare.ts index 5189471f4..4a21400db 100644 --- a/frontend/src/leftoverMapUnexplainedShare.ts +++ b/frontend/src/leftoverMapUnexplainedShare.ts @@ -3,6 +3,9 @@ export const LEFTOVER_MAP_UNEXPLAINED_SHARE_ACTION = "Leftover map leaves unexplained leftover share {value} of raw residual after IRT main effects. Open this post to read {criterion}."; +export const LEFTOVER_MAP_COMPARE_UNEXPLAINED_SHARE_LABEL = + "Leftover map comparison unexplained leftover share"; + export function formatLeftoverMapUnexplainedShare( value: number | null | undefined, ): string | null { diff --git a/pyproject.toml b/pyproject.toml index 33c2ed641..8fc35c845 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.51.0" +version = "2.52.0" description = "Reconstructs git-branch-style lineage DAGs from scattered short records using multi-channel score fusion and LLM adjudication." readme = "README.md" license = { text = "MIT" } From 07301271f813b8bb0e40f57aa22373baf5efbf01 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 21:07:42 +0000 Subject: [PATCH 2/2] fix(reports): deliver grouping comparison unexplained share --- backend/app/report_ingestion.py | 7 ++- backend/tests/test_api.py | 13 +++++ ...test_report_ingestion_unexplained_share.py | 48 +++++++++++++++++++ ...-leftover-map-compare-unexplained-share.md | 4 +- 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 backend/tests/test_report_ingestion_unexplained_share.py diff --git a/backend/app/report_ingestion.py b/backend/app/report_ingestion.py index 58c6c2dc4..b4bfb3187 100644 --- a/backend/app/report_ingestion.py +++ b/backend/app/report_ingestion.py @@ -1052,7 +1052,7 @@ async def fetch_period_comparison( f""" select lp.grouping_kind, lp.grouping_key, lp.pair_kind, lp.post_id, lp.criterion_code, lp.leftover_distance, lp.leftover_residual, - lp.leftover_map_reconstruction, + lp.leftover_map_reconstruction, lp.leftover_map_unexplained_share, p.post_title, p.visibility_code, p.corporate_entity_id, ({_SOURCE_CONTEXT_PRESENT_SQL}) as has_real_source_context from report_leftover_pair lp @@ -1138,6 +1138,11 @@ async def fetch_period_comparison( if pair["leftover_map_reconstruction"] is None else float(pair["leftover_map_reconstruction"]) ), + "leftover_map_unexplained_share": ( + None + if pair["leftover_map_unexplained_share"] is None + else float(pair["leftover_map_unexplained_share"]) + ), "visibility_code": pair["visibility_code"], "corporate_entity_id": str(pair["corporate_entity_id"]), "has_real_source_context": bool(pair["has_real_source_context"]), diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index 95431c425..e3590a16c 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -5860,6 +5860,19 @@ def test_seed_period_report_surfaces_on_get_reports(client, demo_analyst_token, or isinstance(pair["leftover_map_reconstruction"], (int, float)) for pair in leftover_thread.get("leftover_pairs", []) ) + assert all( + "leftover_map_unexplained_share" in pair + for pair in leftover_thread.get("leftover_pairs", []) + ) + assert any( + pair["leftover_map_unexplained_share"] is not None + for pair in leftover_thread.get("leftover_pairs", []) + ) + assert all( + pair["leftover_map_unexplained_share"] is None + or isinstance(pair["leftover_map_unexplained_share"], (int, float)) + for pair in leftover_thread.get("leftover_pairs", []) + ) def test_seed_period_report_includes_fixture_event_lineage_posts( diff --git a/backend/tests/test_report_ingestion_unexplained_share.py b/backend/tests/test_report_ingestion_unexplained_share.py new file mode 100644 index 000000000..0c6f6dff3 --- /dev/null +++ b/backend/tests/test_report_ingestion_unexplained_share.py @@ -0,0 +1,48 @@ +"""Regression for persisted grouping-comparison unexplained-share transport.""" + +from __future__ import annotations + +import asyncio +from decimal import Decimal + +from backend.app import report_ingestion + + +class _ComparisonConnection: + """Minimal asyncpg-compatible read boundary for one comparison row.""" + + async def fetch(self, query: str, *_args: object) -> list[dict[str, object]]: + if "from report_period_score" in query: + return [{"grouping_kind": "thread_group", "grouping_key": "synthetic-thread", "mean_theta": 0.1, "post_count": 2, "link_method": "synthetic"}] + if "from report_member_score" in query: + return [] + if "from report_leftover_pair" in query: + assert "lp.leftover_map_unexplained_share" in query, "leftover_map_unexplained_share is absent from comparison SELECT" + common = { + "grouping_kind": "thread_group", + "grouping_key": "synthetic-thread", + "post_id": "00000000-0000-0000-0000-000000000001", + "post_title": "Synthetic comparison post", + "criterion_code": "synthetic_criterion", + "leftover_distance": 0.5, + "leftover_residual": 0.4, + "leftover_map_reconstruction": Decimal("0.25"), + "visibility_code": "public", + "corporate_entity_id": "00000000-0000-0000-0000-000000000002", + "has_real_source_context": False, + } + return [ + {**common, "pair_kind": "closest", "leftover_map_unexplained_share": Decimal("0.02")}, + {**common, "pair_kind": "farthest", "leftover_map_unexplained_share": None}, + ] + if "from report_leftover_map_coverage" in query or "from report_leftover_map_axis" in query: + return [] + raise AssertionError(f"unexpected comparison query: {query}") + + +def test_fetch_period_comparison_transports_persisted_unexplained_share() -> None: + """Persisted finite values reach the read model while SQL NULL remains unknown.""" + payload = asyncio.run(report_ingestion.fetch_period_comparison(_ComparisonConnection(), "2026-W02")) # type: ignore[arg-type] + pairs = payload[0]["leftover_pairs"] + assert pairs[0]["leftover_map_unexplained_share"] == 0.02 + assert pairs[1]["leftover_map_unexplained_share"] is None diff --git a/docs/adr/0369-leftover-map-compare-unexplained-share.md b/docs/adr/0369-leftover-map-compare-unexplained-share.md index 4cb51938a..ce851b826 100644 --- a/docs/adr/0369-leftover-map-compare-unexplained-share.md +++ b/docs/adr/0369-leftover-map-compare-unexplained-share.md @@ -3,7 +3,7 @@ **Decision status:** Proposed ## Problem -The grouping-comparison read model already carries persisted `leftover_map_unexplained_share` (`s = U² / R²`) for authorized leftover pairs, but exact #828 does not surface it on comparison pair actions. Historical #829 proved the buyer-visible delta but used stale ancestry/identity and put the metric only in a child badge while the parent button supplied an explicit accessible name. +The period-report pair model persists `leftover_map_unexplained_share` (`s = U² / R²`) for authorized leftover pairs, but exact #828's grouping-comparison read path omits that field, so the frontend cannot receive it from `GET /api/reports/compare/{period_code}`. Historical #829 proved the buyer-visible delta but used stale ancestry/identity and put the metric only in a child badge while the parent button supplied an explicit accessible name. ## Constraints - Consume persisted psychometric truth; never derive `s` from `U`, `R`, reconstruction, distance, geometry, rank, coverage, or other shares. @@ -18,7 +18,7 @@ Hiding `s` loses persisted buyer evidence. Recomputing it duplicates psychometri This is the serialized successor of exact #828 `7b6dbbb99a09d0523bce43c6872bbd9fa382a43a`, adopts the distinct historical #829 delta, and allocates v2.55.0. ADR0295/v2.52.0 is historical evidence only. ## Verification -The focused current-parent test must first RED while the parent name omits `U²/R² 0.02`, then GREEN after only the accessible-name/duplicate-badge fix. Full backend, frontend lint/test/build/Storybook, version parity, clean-tree authority, and non-force promotion are required. Hosted exact-product-head checks, browser/keyboard/focus/a11y evidence, and qualifying independent approval remain separate merge gates. +The accessibility test must RED while the parent name omits `U²/R² 0.02`, then GREEN after only the accessible-name/duplicate-badge fix. The transport regression must independently RED while the comparison SELECT omits the persisted field, then GREEN without deriving or clamping it. A real authenticated PostgreSQL/Keycloak/Valkey `GET /api/reports/compare/2026-W02` acceptance must prove the persisted key/value reaches authorized leftover pairs. Full backend, frontend lint/test/build/Storybook, version parity, clean-tree authority, and non-force promotion are required. Hosted exact-product-head checks, browser/keyboard/focus/a11y evidence, and qualifying independent approval remain separate merge gates. ## Evidence Gabriel, K. R. (1971). The biplot graphic display of matrices with application to principal component analysis. *Biometrika, 58*(3), 453–467.