From ead81bd3f51005f083871dc0bd49148a42a351b2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 30 Aug 2026 15:30:30 +0000 Subject: [PATCH] feat(reports): leftover-map explained leftover share on grouping comparison strip (v2.51.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caption persisted leftover-map explained leftover share e on GET /api/reports/compare/{period} leftover-pair buttons through formatLeftoverMapExplainedShare (ADR 0294). Distinct aria-label Leftover map comparison explained leftover share. Omit when e is missing or non-finite. Do not invent e from R̂ and R. No SQL. No leftover score or theta. --- AGENTS.md | 10 +- ARCHITECTURE.md | 7 +- ....0-leftover-map-compare-explained-share.md | 41 ++++ CHANGELOG.md | 40 ++++ CLAUDE.md | 2 +- ...94-leftover-map-compare-explained-share.md | 179 ++++++++++++++++++ docs/product-technical-gap-baseline.md | 72 +++++++ frontend/package.json | 2 +- frontend/src/App.test.tsx | 26 +++ frontend/src/App.tsx | 15 ++ frontend/src/components/LeftoverPairList.tsx | 3 +- frontend/src/i18n.test.ts | 14 ++ frontend/src/i18n.ts | 4 + .../src/leftoverMapExplainedShare.test.ts | 14 +- frontend/src/leftoverMapExplainedShare.ts | 3 + frontend/src/leftoverMapPlotLayout.ts | 5 +- pyproject.toml | 2 +- 17 files changed, 429 insertions(+), 10 deletions(-) create mode 100644 CHANGELOG.d/2.51.0-leftover-map-compare-explained-share.md create mode 100644 docs/adr/0294-leftover-map-compare-explained-share.md diff --git a/AGENTS.md b/AGENTS.md index 29b964d56..74f8f5784 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) 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) 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 @@ -422,6 +422,12 @@ leftover pairs can match the pair-row reconstruction badge; a missing or non-fin `R̂` omits that leftover-map comparison reconstruction badge and does not invent `R̂` from leftover-map distance or plotted coordinates. The strip does not gain the leftover-map graphic. +ADR 0294 captions grouping comparison leftover-pair buttons with persisted leftover-map +explained leftover share `e` through formatLeftoverMapExplainedShare so a buyer who +compares leftover pairs can match the pair-row `R̂²/R²` badge; a missing or non-finite +`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. 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 @@ -446,6 +452,8 @@ Grouping comparison incomplete item coverage (ADR 0292) captions the grouping comparison strip with how many scored criteria stayed out of each grouping's map. Grouping comparison reconstruction (ADR 0293) captions grouping comparison leftover-pair 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`. 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 460cfc592..f0e891166 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -677,6 +677,8 @@ ADR 0292 captions the grouping comparison strip with persisted leftover-map inco item coverage through leftoverMapIncompleteItemCount. ADR 0293 captions grouping comparison leftover-pair buttons with persisted leftover-map reconstruction `R̂` through formatLeftoverMapReconstruction. +ADR 0294 captions grouping comparison leftover-pair buttons with persisted leftover-map +explained leftover share `e` through formatLeftoverMapExplainedShare. 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 @@ -697,8 +699,9 @@ including on the pair list. Results persist to 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), and leftover-map reconstruction `R̂` -(ADR 0293); +incomplete item coverage (ADR 0292), leftover-map reconstruction `R̂` +(ADR 0293), and leftover-map explained leftover share `e` +(ADR 0294); `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.51.0-leftover-map-compare-explained-share.md b/CHANGELOG.d/2.51.0-leftover-map-compare-explained-share.md new file mode 100644 index 000000000..0b0023171 --- /dev/null +++ b/CHANGELOG.d/2.51.0-leftover-map-compare-explained-share.md @@ -0,0 +1,41 @@ +## 2.51.0 — Leftover-map explained leftover share on grouping comparison strip pair rows + +- Caption leftover-map explained leftover share `e` on grouping comparison + strip leftover-pair buttons through formatLeftoverMapExplainedShare + (ADR 0294). 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, and grouping + comparison leftover-pair buttons that match leftover-map explained + leftover share `e` on the pair row; click a post marker or a pair button + opens that post. A missing or non-finite `e` omits that leftover-map + comparison explained leftover share badge and keeps leftover-map + reconstruction `R̂` when finite, leftover-map distance `d`, plus the + strip coverage notes. Do not invent `e` from `R̂` 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 6baa70f54..b2548f752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,46 @@ 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 explained leftover share `e` through + formatLeftoverMapExplainedShare (ADR 0294 / v2.51.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, and grouping comparison leftover-pair buttons that match + leftover-map explained leftover share `e` on the pair row; click a post + marker or a pair button opens that post. A missing or non-finite `e` + omits that leftover-map comparison explained leftover share badge and + keeps leftover-map reconstruction `R̂` when finite, leftover-map distance + `d`, plus the strip coverage notes. Do not invent `e` from `R̂` 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 reconstruction `R̂` through formatLeftoverMapReconstruction (ADR 0293 / v2.50.0). After `make seed`, diff --git a/CLAUDE.md b/CLAUDE.md index 1963b8114..6993e4098 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), 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), 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/0294-leftover-map-compare-explained-share.md b/docs/adr/0294-leftover-map-compare-explained-share.md new file mode 100644 index 000000000..43de2dbbe --- /dev/null +++ b/docs/adr/0294-leftover-map-compare-explained-share.md @@ -0,0 +1,179 @@ +# ADR 0294 — Name leftover-map explained leftover share on grouping comparison strip pair rows + +**Decision status:** Accepted +**Date:** 2026-08-30 + +Amends leftover pairs on the grouping comparison strip +([ADR 0149](0149-leftover-pairs-on-comparison-strip.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 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 +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 unexplained leftover share on pair segments +([ADR 0274](0274-leftover-map-segment-unexplained-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 unexplained leftover share persistence +([ADR 0233](0233-leftover-map-unexplained-share.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 0266 already persists leftover-map explained leftover share +`e = R̂² / R²` of raw residual after two-axis Gabriel reconstruction. +ADR 0049 already names that share on the period-report pair list. ADR 0273 +already names it on leftover-map graphic-display pair segments. ADR 0293 +already captions persisted leftover-map reconstruction `R̂` on grouping +comparison leftover-pair buttons. The strip pair button still does not +name `e`, so a buyer who compares leftover pairs can treat leftover-map +reconstruction `R̂` or leftover-map distance `d` as the leftover the +truncated map explains even after the pair list and graphic already name +`R̂²/R²`. When `R`, `R̂`, `U`, `x`, `s`, and `e` are finite, +`e + s + x = 1`; hiding `e` on the strip lets that identity vanish +without a next action. + +This increment captions persisted leftover-map explained leftover share on +each grouping comparison leftover-pair button through +formatLeftoverMapExplainedShare. It does not add columns. It does not +recompute `e` from `R̂` and `R`, leftover-map distance, plotted +coordinates, leftover-map reconstruction, or leftover residual. 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 **0294** so it does not collide with +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 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 explained leftover share formatter as the +pair-row `R̂²/R²` badge, next to leftover-map reconstruction `R̂` and +leftover-map distance `d`. Use the distinct accessible name +`Leftover map comparison explained leftover share` so the strip badge is +not the graphic caption (`leftover-map explained leftover share {label}`). +A missing or non-finite `e` omits that leftover-map comparison explained +leftover share badge and keeps 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 +`R̂²/R² 0.00` when that persisted share is finite. A finite share greater +than 1 is shown, never clamped. Do not invent `e` from `R̂` 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. 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, and persisted leftover-map +explained leftover share `e` 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 inner product, +leftover-map cosine, leftover-map length, leftover-map graphic display, +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. Explained leftover +share of the leftover cell is `e = R̂² / R²` after two-axis Gabriel +reconstruction. Grouping comparison explained leftover share names that +persisted square share on the strip pair row only when +formatLeftoverMapExplainedShare returns a usable badge.) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 3482516f4..2b7e593ee 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,77 @@ # Product & Technical Gap Baseline +> 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 +> (`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`. 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 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 explained leftover share +> `e` on grouping comparison leftover-pair buttons (ADR 0294 / v2.51.0) +> delivered locally on `feat/leftover-map-compare-explained-share-v2510`. +> Caption each strip leftover-pair button through +> formatLeftoverMapExplainedShare with distinct aria-label +> `Leftover map comparison explained leftover share` next to leftover-map +> reconstruction `R̂` and leftover-map distance `d`. UI-only; no new +> columns. Missing or non-finite `e` omits that leftover-map comparison +> explained leftover share badge independently of `R̂`, `d`, and coverage +> notes. Do not invent `e` from `R̂` 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. Do not caption the leftover-map graphic on the +> strip. Do not invent leftover scores. Stack onto leftover branch +> `feat/leftover-map-compare-reconstruction-v2500` / #826; 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:20 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 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 ed531af9c..ef5398028 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -933,6 +933,7 @@ describe("App, authenticated", () => { leftover_distance: 0.12, leftover_residual: 0.4, leftover_map_reconstruction: 0.248, + leftover_map_explained_share: 0.76, }, ], leftover_map_coverage: { @@ -4349,6 +4350,21 @@ describe("App, authenticated", () => { "Leftover map comparison reconstruction", ), ).toHaveLength(1); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + /leftover-map explained leftover share/, + ), + ).not.toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getByLabelText( + "Leftover map comparison explained leftover share", + ), + ).toHaveTextContent("R̂²/R² 0.76"); + expect( + within(screen.getByLabelText("Grouping comparison")).getAllByLabelText( + "Leftover map comparison explained leftover share", + ), + ).toHaveLength(1); expect( screen.getByRole("button", { name: /open leftover closest pair from comparison: specification revision requested/i, @@ -4359,6 +4375,11 @@ describe("App, authenticated", () => { name: /open leftover closest pair from comparison: specification revision requested/i, }), ).not.toHaveTextContent("R̂"); + expect( + screen.getByRole("button", { + name: /open leftover closest pair from comparison: specification revision requested/i, + }), + ).not.toHaveTextContent("R̂²/R²"); expect( screen.getByRole("button", { name: "Compare Business unit (PU): Demo Report High, mean θ 0.81" }), ).toHaveTextContent("mean θ 0.81"); @@ -4383,6 +4404,11 @@ describe("App, authenticated", () => { name: /open leftover closest pair from comparison: public post/i, }), ).toHaveTextContent("R̂ +0.25"); + expect( + screen.getByRole("button", { + name: /open leftover closest pair from comparison: public post/i, + }), + ).toHaveTextContent("R̂²/R² 0.76"); 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 af3b76d56..3be880042 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -141,6 +141,10 @@ import { formatLeftoverMapReconstruction, LEFTOVER_MAP_COMPARE_RECONSTRUCTION_LABEL, } from "./leftoverMapReconstruction"; +import { + formatLeftoverMapExplainedShare, + LEFTOVER_MAP_COMPARE_EXPLAINED_SHARE_LABEL, +} from "./leftoverMapExplainedShare"; import "./App.css"; const AdminPanel = lazy(() => import("./components/AdminPanel").then((module) => ({ default: module.AdminPanel }))); @@ -4066,6 +4070,9 @@ function ReportsPanel({ const reconstruction = formatLeftoverMapReconstruction( pair.leftover_map_reconstruction, ); + const explainedShare = formatLeftoverMapExplainedShare( + pair.leftover_map_explained_share, + ); return (
  • ) : null} + {explainedShare ? ( + + {explainedShare} + + ) : null}
  • ); diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index 71b98b914..3cef9e167 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -89,7 +89,8 @@ export type LeftoverPairListProps = { * grouping comparison strip, not this pair list. ADR 0292 names persisted leftover-map * 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. Every badge still + * 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 * 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 20d72b60c..6fdf4645e 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -82,6 +82,7 @@ describe("i18n", () => { "Leftover map comparison incomplete posts", "Leftover map comparison incomplete items", "Leftover map comparison reconstruction", + "Leftover map comparison explained leftover share", "Leftover-map graphic item coverage", "Leftover map item coverage", "Leftover map incomplete posts", @@ -435,6 +436,19 @@ describe("i18n", () => { expect(t("Leftover map comparison reconstruction")).toBe(expected); }); + it.each([ + ["ko", "잔여 지도 비교 설명 잔여 점유율"], + ["zh", "残差地图比较已解释残差份额"], + ["ja", "残差マップの比較説明済み残差割合"], + ["vi", "Phần dư giải thích so sánh bản đồ phần dư"], + ] as const)( + "formats leftover map comparison explained leftover share label in %s", + (locale, expected) => { + setLocale(locale); + expect(t("Leftover map comparison explained leftover share")).toBe(expected); + }, + ); + it.each([ ["ko", "잔여 지도 그림 기준 포함 범위"], ["zh", "残差图图形准则覆盖范围"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 5559de700..0383de625 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -273,6 +273,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison incomplete posts": "잔여 지도 비교 불완전 글", "Leftover map comparison incomplete items": "잔여 지도 비교 불완전 기준", "Leftover map comparison reconstruction": "잔여 지도 비교 재구성", + "Leftover map comparison explained leftover share": "잔여 지도 비교 설명 잔여 점유율", "Leftover map item coverage": "잔여 지도 기준 포함 범위", "Leftover map incomplete posts": "잔여 지도 불완전 글", "Leftover map incomplete items": "잔여 지도 불완전 기준", @@ -884,6 +885,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison incomplete posts": "残差地图比较不完整帖文", "Leftover map comparison incomplete items": "残差地图比较不完整准则", "Leftover map comparison reconstruction": "残差地图比较重建", + "Leftover map comparison explained leftover share": "残差地图比较已解释残差份额", "Leftover map item coverage": "残差地图准则覆盖范围", "Leftover map incomplete posts": "残差地图不完整帖文", "Leftover map incomplete items": "残差地图不完整准则", @@ -1516,6 +1518,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison incomplete posts": "残差マップの比較不完全投稿", "Leftover map comparison incomplete items": "残差マップの比較不完全基準", "Leftover map comparison reconstruction": "残差マップの比較再構成", + "Leftover map comparison explained leftover share": "残差マップの比較説明済み残差割合", "Leftover map item coverage": "残差マップの基準対象範囲", "Leftover map incomplete posts": "残差マップの不完全投稿", "Leftover map incomplete items": "残差マップの不完全基準", @@ -2127,6 +2130,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison incomplete posts": "Bài không đầy đủ so sánh trên bản đồ phần dư", "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 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/leftoverMapExplainedShare.test.ts b/frontend/src/leftoverMapExplainedShare.test.ts index 74033a6f6..e1199aac3 100644 --- a/frontend/src/leftoverMapExplainedShare.test.ts +++ b/frontend/src/leftoverMapExplainedShare.test.ts @@ -1,5 +1,8 @@ import { describe, expect, it } from "vitest"; -import { formatLeftoverMapExplainedShare } from "./leftoverMapExplainedShare"; +import { + formatLeftoverMapExplainedShare, + LEFTOVER_MAP_COMPARE_EXPLAINED_SHARE_LABEL, +} from "./leftoverMapExplainedShare"; describe("formatLeftoverMapExplainedShare", () => { it("names leftover-map explained leftover share without inventing a leftover score", () => { @@ -15,4 +18,13 @@ describe("formatLeftoverMapExplainedShare", () => { expect(formatLeftoverMapExplainedShare(Number.POSITIVE_INFINITY)).toBeNull(); expect(formatLeftoverMapExplainedShare(Number.NEGATIVE_INFINITY)).toBeNull(); }); + + it("keeps the grouping comparison explained leftover share label distinct from the graphic explained leftover share label", () => { + expect(LEFTOVER_MAP_COMPARE_EXPLAINED_SHARE_LABEL).toBe( + "Leftover map comparison explained leftover share", + ); + expect(LEFTOVER_MAP_COMPARE_EXPLAINED_SHARE_LABEL).not.toBe( + "leftover-map explained leftover share {label}", + ); + }); }); diff --git a/frontend/src/leftoverMapExplainedShare.ts b/frontend/src/leftoverMapExplainedShare.ts index 9f2c2215e..a1eba63cb 100644 --- a/frontend/src/leftoverMapExplainedShare.ts +++ b/frontend/src/leftoverMapExplainedShare.ts @@ -3,6 +3,9 @@ export const LEFTOVER_MAP_EXPLAINED_SHARE_ACTION = "Leftover map leaves explained leftover share {value} of raw residual after IRT main effects. Open this post to read {criterion}."; +export const LEFTOVER_MAP_COMPARE_EXPLAINED_SHARE_LABEL = + "Leftover map comparison explained leftover share"; + export function formatLeftoverMapExplainedShare( value: number | null | undefined, ): string | null { diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index 1106e38cc..a93617c05 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -20,8 +20,9 @@ * (ADR 0289), grouping comparison item complete-case coverage * (ADR 0290), grouping comparison incomplete post coverage * (ADR 0291), grouping comparison incomplete item coverage - * (ADR 0292), and grouping comparison reconstruction - * (ADR 0293) caption the pair list or the grouping comparison strip, + * (ADR 0292), grouping comparison reconstruction + * (ADR 0293), and grouping comparison explained leftover share + * (ADR 0294) caption the pair list or the grouping comparison strip, * not this graphic layout. */ diff --git a/pyproject.toml b/pyproject.toml index 1ed881f7d..33c2ed641 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.50.0" +version = "2.51.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" }