From 1511166c53c16fef65f3330eee58d3a5664b1583 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 30 Aug 2026 16:26:44 +0000 Subject: [PATCH] feat(reports): leftover-map unexplained leftover on grouping comparison strip (v2.54.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caption persisted leftover-map unexplained leftover U = R − R̂ on grouping comparison leftover-pair buttons through formatLeftoverMapUnexplained (ADR 0297). Distinct aria-label Leftover map comparison unexplained leftover. Missing or non-finite U omits that badge and keeps x, s, e, R̂, d, and coverage notes. Do not invent U from R and R̂. Rank-0 origin cells still name U 0.00 when finite. Finite negative leftover is shown, never clamped. UI-only; no new columns. --- AGENTS.md | 10 +- ARCHITECTURE.md | 7 +- CHANGELOG.md | 49 ++++ CLAUDE.md | 2 +- ...ftover-map-compare-unexplained-leftover.md | 209 ++++++++++++++++++ docs/product-technical-gap-baseline.md | 41 ++-- frontend/package.json | 2 +- frontend/src/App.test.tsx | 26 +++ frontend/src/App.tsx | 15 ++ frontend/src/i18n.test.ts | 14 ++ frontend/src/i18n.ts | 4 + frontend/src/leftoverMapPlotLayout.ts | 5 +- frontend/src/leftoverMapUnexplained.test.ts | 13 ++ frontend/src/leftoverMapUnexplained.ts | 3 + pyproject.toml | 2 +- uv.lock | 2 +- 16 files changed, 376 insertions(+), 28 deletions(-) create mode 100644 docs/adr/0297-leftover-map-compare-unexplained-leftover.md diff --git a/AGENTS.md b/AGENTS.md index a767bd321..d58c5bdf2 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 / 0295 / 0296) 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 / 0296 / 0297) 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 @@ -440,6 +440,12 @@ compares leftover pairs can match the pair-row `2R̂U/R²` badge; a missing or n `x` omits that leftover-map comparison cross share badge and does not invent `x` from `R̂`, `U`, and `R` or from leftover-map distance. The strip does not gain the leftover-map graphic. +ADR 0297 captions grouping comparison leftover-pair buttons with persisted leftover-map +unexplained leftover `U` through formatLeftoverMapUnexplained so a buyer who +compares leftover pairs can match the pair-row `U` badge; a missing or non-finite +`U` omits that leftover-map comparison unexplained leftover badge and does not invent +`U` 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 @@ -470,6 +476,8 @@ Grouping comparison unexplained leftover share (ADR 0295) captions grouping comp buttons with persisted leftover-map unexplained leftover share `s`. Grouping comparison leftover-map cross share (ADR 0296) captions grouping comparison leftover-pair buttons with persisted leftover-map cross share `x`. +Grouping comparison leftover-map unexplained leftover (ADR 0297) captions grouping comparison leftover-pair +buttons with persisted leftover-map unexplained leftover `U`. 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 d8a47c972..ece6e3992 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -683,6 +683,8 @@ ADR 0295 captions grouping comparison leftover-pair buttons with persisted lefto unexplained leftover share `s` through formatLeftoverMapUnexplainedShare. ADR 0296 captions grouping comparison leftover-pair buttons with persisted leftover-map cross share `x` through formatLeftoverMapCrossShare. +ADR 0297 captions grouping comparison leftover-pair buttons with persisted leftover-map +unexplained leftover `U` through formatLeftoverMapUnexplained. 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 @@ -706,8 +708,9 @@ coverage (ADR 0290), leftover-map incomplete post coverage (ADR 0291), leftover- incomplete item coverage (ADR 0292), leftover-map reconstruction `R̂` (ADR 0293), leftover-map explained leftover share `e` (ADR 0294), leftover-map unexplained leftover share `s` -(ADR 0295), and leftover-map cross share `x` -(ADR 0296); +(ADR 0295), leftover-map cross share `x` +(ADR 0296), and leftover-map unexplained leftover `U` +(ADR 0297); `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.md b/CHANGELOG.md index c57a69e90..961e4f418 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,55 @@ 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 `U` through + formatLeftoverMapUnexplained (ADR 0297 / v2.54.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, grouping + comparison leftover-pair buttons that match leftover-map unexplained + leftover share `s` on the pair row, grouping comparison leftover-pair + buttons that match leftover-map cross share `x` on the pair row, and + grouping comparison leftover-pair buttons that match leftover-map + unexplained leftover `U` on the pair row; click a post marker or a pair + button opens that post. A missing or non-finite `U` omits that + leftover-map comparison unexplained leftover badge and keeps leftover-map + cross share `x` when finite, leftover-map unexplained leftover share `s` + when finite, 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 `U` 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. + Rank-0 origin cells still name `U 0.00` when that persisted leftover is + finite. A finite negative leftover is shown, never clamped. 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 cross share `x` through formatLeftoverMapCrossShare (ADR 0296 / v2.53.0). After `make seed`, diff --git a/CLAUDE.md b/CLAUDE.md index 6360d511e..81123ac0a 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 / 0295 / 0296), 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 / 0296 / 0297), 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/0297-leftover-map-compare-unexplained-leftover.md b/docs/adr/0297-leftover-map-compare-unexplained-leftover.md new file mode 100644 index 000000000..e1daa0320 --- /dev/null +++ b/docs/adr/0297-leftover-map-compare-unexplained-leftover.md @@ -0,0 +1,209 @@ +# ADR 0297 — Name leftover-map unexplained leftover 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 persistence +([ADR 0182](0182-leftover-map-unexplained.md)), leftover-map unexplained leftover +on pair segments +([ADR 0276](0276-leftover-map-segment-unexplained-leftover.md)), leftover-map +cross share on grouping comparison strip pair rows +([ADR 0296](0296-leftover-map-compare-cross-share.md)), leftover-map +unexplained leftover share on grouping comparison strip pair rows +([ADR 0295](0295-leftover-map-compare-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 +cross share persistence +([ADR 0185](0185-leftover-map-cross-share.md)), leftover-map cross share on +pair segments +([ADR 0275](0275-leftover-map-segment-cross-share.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 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 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 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 0182 already persists leftover-map unexplained leftover +`U = R − R̂` after two-axis Gabriel reconstruction. ADR 0049 already names +that leftover on the period-report pair list. ADR 0276 already names it on +leftover-map graphic-display pair segments. ADR 0296 already captions +persisted leftover-map cross share `x` on grouping comparison leftover-pair +buttons. The strip pair button still does not name `U`, so a buyer who +compares leftover pairs can treat leftover-map cross share `x`, leftover-map +unexplained leftover share `s`, 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`. When `R`, `R̂`, and `U` are finite, `U + R̂ = R`; hiding +`U` on the strip lets that identity vanish without a next action. + +This increment captions persisted leftover-map unexplained leftover on each +grouping comparison leftover-pair button through formatLeftoverMapUnexplained. +It does not add columns. It does not recompute `U` from `R` and `R̂`, leftover-map +distance, plotted coordinates, leftover-map reconstruction, leftover residual, +leftover-map explained leftover share, leftover-map unexplained leftover share, +or leftover-map cross 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 **0297** so it does not collide with +leftover-map cross share on grouping comparison strip pair rows (0296), +leftover-map unexplained leftover share on grouping comparison strip pair +rows (0295), 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 on pair +segments (0276), leftover-map unexplained leftover persistence (0182), leftover-map +cross share on pair segments (0275), leftover-map cross share persistence +(0185), 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 formatter as the pair-row +`U` badge, next to leftover-map cross share `x`, leftover-map unexplained +leftover share `s`, 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` so the strip badge is not +the graphic caption (`leftover-map unexplained leftover {label}`) and is not +leftover-map comparison unexplained leftover share. A missing or non-finite `U` +omits that leftover-map comparison unexplained leftover badge and keeps leftover-map +cross share `x` when finite, leftover-map unexplained leftover share `s` when +finite, 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 0.00` when that persisted leftover is finite. A finite negative leftover +is shown, never clamped. Do not invent `U` from `R` and `R̂`, leftover-map +distance, plotted coordinates, leftover-map reconstruction, leftover residual, +leftover-map explained leftover share, leftover-map unexplained leftover share, +leftover-map cross 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, persisted leftover-map unexplained +leftover share `s` when finite, persisted leftover-map cross share `x` +when finite, and persisted leftover-map unexplained leftover `U` 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̂`, and `U` are finite, `U + R̂ = R`. +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 unexplained leftover +share on grouping comparison strip pair rows, leftover-map cross 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 on pair segments, leftover-map unexplained +leftover persistence, leftover-map cross share on pair segments, leftover-map +cross share persistence, 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 +of the leftover cell is `U = R − R̂` after two-axis Gabriel reconstruction. +Grouping comparison leftover-map unexplained leftover names that persisted +remainder on the strip pair row only when formatLeftoverMapUnexplained returns +a usable badge. When finite, `U + R̂ = R`. When finite, `e + s + x = 1`.) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 21642a618..983fa4eb1 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,6 +1,6 @@ # Product & Technical Gap Baseline -> Exact-head loop overlay: 2026-08-31 00:48 KST. Protected `main` is +> Exact-head loop overlay: 2026-08-31 01:06 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 @@ -44,8 +44,9 @@ > share `e` on grouping comparison leftover-pair buttons (v2.51.0 / > ADR 0294) is `ead81bd`. #829 leftover-map unexplained leftover share > `s` on grouping comparison leftover-pair buttons (v2.52.0 / ADR 0295) -> is `4d82c28`. 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/#829 open for +> is `4d82c28`. #831 leftover-map cross share `x` on grouping comparison +> leftover-pair buttons (v2.53.0 / ADR 0296) is `2cefc0b`. 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/#829/#831 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 @@ -55,25 +56,27 @@ > closed. Only collaborator is `seonghobae`; no independent reviewer can > be requested from this token. > -> Next buyer increment on this cycle: leftover-map cross share -> `x` on grouping comparison leftover-pair buttons (ADR 0296 / v2.53.0) -> delivered locally on `feat/leftover-map-compare-cross-share-v2530`. +> Next buyer increment on this cycle: leftover-map unexplained leftover +> `U` on grouping comparison leftover-pair buttons (ADR 0297 / v2.54.0) +> delivered locally on `feat/leftover-map-compare-unexplained-leftover-v2540`. > Caption each strip leftover-pair button through -> formatLeftoverMapCrossShare with distinct aria-label -> `Leftover map comparison cross share` next to leftover-map unexplained -> leftover share `s`, leftover-map explained leftover share `e`, leftover-map -> reconstruction `R̂`, and leftover-map distance `d`. UI-only; no new -> columns. Missing or non-finite `x` omits that leftover-map comparison -> cross share badge independently of `s`, `e`, `R̂`, `d`, and coverage -> notes. Do not invent `x` from `R̂`, `U`, and `R`, leftover-map distance, -> plotted coordinates, leftover-map reconstruction, leftover residual, -> leftover-map explained leftover share, leftover-map unexplained 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. A finite negative share is +> formatLeftoverMapUnexplained with distinct aria-label +> `Leftover map comparison unexplained leftover` next to leftover-map cross +> share `x`, leftover-map unexplained leftover share `s`, leftover-map +> explained leftover share `e`, leftover-map reconstruction `R̂`, and +> leftover-map distance `d`. UI-only; no new columns. Missing or non-finite +> `U` omits that leftover-map comparison unexplained leftover badge +> independently of `x`, `s`, `e`, `R̂`, `d`, and coverage notes. Do not invent +> `U` from `R` and `R̂`, leftover-map distance, plotted coordinates, +> leftover-map reconstruction, leftover residual, leftover-map explained +> leftover share, leftover-map unexplained leftover share, leftover-map +> cross 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. Rank-0 origin +> cells still name `U 0.00` when finite. A finite negative leftover is > shown, never clamped. Do not caption the leftover-map graphic on the > strip. Do not invent leftover scores. Stack onto leftover branch -> `feat/leftover-map-compare-unexplained-share-v2520` / #829; leave the PR +> `feat/leftover-map-compare-cross-share-v2530` / #831; 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. diff --git a/frontend/package.json b/frontend/package.json index ff2d6a69f..52298d5f8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.53.0", + "version": "2.54.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 5b43c2657..ba5ac8600 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -936,6 +936,7 @@ describe("App, authenticated", () => { leftover_map_explained_share: 0.76, leftover_map_unexplained_share: 0.02, leftover_map_cross_share: 0.12, + leftover_map_unexplained: 0.05, }, ], leftover_map_coverage: { @@ -4397,6 +4398,21 @@ describe("App, authenticated", () => { "Leftover map comparison cross share", ), ).toHaveLength(1); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + "leftover-map unexplained leftover {label}", + ), + ).not.toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getByLabelText( + "Leftover map comparison unexplained leftover", + ), + ).toHaveTextContent("U +0.05"); + expect( + within(screen.getByLabelText("Grouping comparison")).getAllByLabelText( + "Leftover map comparison unexplained leftover", + ), + ).toHaveLength(1); expect( screen.getByRole("button", { name: /open leftover closest pair from comparison: specification revision requested/i, @@ -4422,6 +4438,11 @@ describe("App, authenticated", () => { name: /open leftover closest pair from comparison: specification revision requested/i, }), ).not.toHaveTextContent("2R̂U/R²"); + expect( + screen.getByRole("button", { + name: /open leftover closest pair from comparison: specification revision requested/i, + }), + ).not.toHaveTextContent("U +"); expect( screen.getByRole("button", { name: "Compare Business unit (PU): Demo Report High, mean θ 0.81" }), ).toHaveTextContent("mean θ 0.81"); @@ -4461,6 +4482,11 @@ describe("App, authenticated", () => { name: /open leftover closest pair from comparison: public post/i, }), ).toHaveTextContent("2R̂U/R² 0.12"); + expect( + screen.getByRole("button", { + name: /open leftover closest pair from comparison: public post/i, + }), + ).toHaveTextContent("U +0.05"); 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 02afbab9c..0bb2c3f88 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -153,6 +153,10 @@ import { formatLeftoverMapCrossShare, LEFTOVER_MAP_COMPARE_CROSS_SHARE_LABEL, } from "./leftoverMapCrossShare"; +import { + formatLeftoverMapUnexplained, + LEFTOVER_MAP_COMPARE_UNEXPLAINED_LABEL, +} from "./leftoverMapUnexplained"; import "./App.css"; const AdminPanel = lazy(() => import("./components/AdminPanel").then((module) => ({ default: module.AdminPanel }))); @@ -4087,6 +4091,9 @@ function ReportsPanel({ const crossShare = formatLeftoverMapCrossShare( pair.leftover_map_cross_share, ); + const unexplainedLeftover = formatLeftoverMapUnexplained( + pair.leftover_map_unexplained, + ); return (
  • ) : null} + {unexplainedLeftover ? ( + + {unexplainedLeftover} + + ) : null}
  • ); diff --git a/frontend/src/i18n.test.ts b/frontend/src/i18n.test.ts index 8ecc22dce..52c8415f9 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -85,6 +85,7 @@ describe("i18n", () => { "Leftover map comparison explained leftover share", "Leftover map comparison unexplained leftover share", "Leftover map comparison cross share", + "Leftover map comparison unexplained leftover", "Leftover-map graphic item coverage", "Leftover map item coverage", "Leftover map incomplete posts", @@ -477,6 +478,19 @@ describe("i18n", () => { }, ); + it.each([ + ["ko", "잔여 지도 비교 미설명 잔여"], + ["zh", "残差地图比较未解释残差"], + ["ja", "残差マップの比較未説明残差"], + ["vi", "Phần dư chưa giải thích so sánh"], + ] as const)( + "formats leftover map comparison unexplained leftover label in %s", + (locale, expected) => { + setLocale(locale); + expect(t("Leftover map comparison unexplained leftover")).toBe(expected); + }, + ); + it.each([ ["ko", "잔여 지도 그림 기준 포함 범위"], ["zh", "残差图图形准则覆盖范围"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 6bf6b50ae..2f64fb582 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -276,6 +276,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison explained leftover share": "잔여 지도 비교 설명 잔여 점유율", "Leftover map comparison unexplained leftover share": "잔여 지도 비교 미설명 잔여 점유율", "Leftover map comparison cross share": "잔여 지도 비교 교차 점유율", + "Leftover map comparison unexplained leftover": "잔여 지도 비교 미설명 잔여", "Leftover map item coverage": "잔여 지도 기준 포함 범위", "Leftover map incomplete posts": "잔여 지도 불완전 글", "Leftover map incomplete items": "잔여 지도 불완전 기준", @@ -890,6 +891,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison explained leftover share": "残差地图比较已解释残差份额", "Leftover map comparison unexplained leftover share": "残差地图比较未解释残差份额", "Leftover map comparison cross share": "残差地图比较交叉份额", + "Leftover map comparison unexplained leftover": "残差地图比较未解释残差", "Leftover map item coverage": "残差地图准则覆盖范围", "Leftover map incomplete posts": "残差地图不完整帖文", "Leftover map incomplete items": "残差地图不完整准则", @@ -1525,6 +1527,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison explained leftover share": "残差マップの比較説明済み残差割合", "Leftover map comparison unexplained leftover share": "残差マップの比較未説明残差割合", "Leftover map comparison cross share": "残差マップの比較交差割合", + "Leftover map comparison unexplained leftover": "残差マップの比較未説明残差", "Leftover map item coverage": "残差マップの基準対象範囲", "Leftover map incomplete posts": "残差マップの不完全投稿", "Leftover map incomplete items": "残差マップの不完全基準", @@ -2139,6 +2142,7 @@ const TRANSLATIONS: Partial>> = { "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 comparison cross share": "Phần giao so sánh bản đồ phần dư", + "Leftover map comparison unexplained leftover": "Phần dư chưa giải thích so sánh", "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 598545847..a71251029 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -23,8 +23,9 @@ * (ADR 0292), grouping comparison reconstruction * (ADR 0293), grouping comparison explained leftover share * (ADR 0294), grouping comparison unexplained leftover share - * (ADR 0295), and grouping comparison leftover-map cross share - * (ADR 0296) caption the pair list or the grouping comparison strip, + * (ADR 0295), grouping comparison leftover-map cross share + * (ADR 0296), and grouping comparison leftover-map unexplained leftover + * (ADR 0297) caption the pair list or the grouping comparison strip, * not this graphic layout. */ diff --git a/frontend/src/leftoverMapUnexplained.test.ts b/frontend/src/leftoverMapUnexplained.test.ts index aa23a125b..11eee7b37 100644 --- a/frontend/src/leftoverMapUnexplained.test.ts +++ b/frontend/src/leftoverMapUnexplained.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import { formatLeftoverMapUnexplained, formatSignedLeftoverValue, + LEFTOVER_MAP_COMPARE_UNEXPLAINED_LABEL, } from "./leftoverMapUnexplained"; describe("formatLeftoverMapUnexplained", () => { @@ -19,4 +20,16 @@ describe("formatLeftoverMapUnexplained", () => { expect(formatLeftoverMapUnexplained(Number.NaN)).toBeNull(); expect(formatLeftoverMapUnexplained(Number.POSITIVE_INFINITY)).toBeNull(); }); + + it("keeps the grouping comparison unexplained leftover label distinct from the graphic unexplained leftover label", () => { + expect(LEFTOVER_MAP_COMPARE_UNEXPLAINED_LABEL).toBe( + "Leftover map comparison unexplained leftover", + ); + expect(LEFTOVER_MAP_COMPARE_UNEXPLAINED_LABEL).not.toBe( + "leftover-map unexplained leftover {label}", + ); + expect(LEFTOVER_MAP_COMPARE_UNEXPLAINED_LABEL).not.toBe( + "Leftover map comparison unexplained leftover share", + ); + }); }); diff --git a/frontend/src/leftoverMapUnexplained.ts b/frontend/src/leftoverMapUnexplained.ts index 4fa23c935..c5ae46bc3 100644 --- a/frontend/src/leftoverMapUnexplained.ts +++ b/frontend/src/leftoverMapUnexplained.ts @@ -3,6 +3,9 @@ export const LEFTOVER_MAP_UNEXPLAINED_ACTION = "Leftover map leaves unexplained U {value} after IRT main effects. Open this post to read {criterion}."; +export const LEFTOVER_MAP_COMPARE_UNEXPLAINED_LABEL = + "Leftover map comparison unexplained leftover"; + export function formatSignedLeftoverValue(value: number): string | null { if (!Number.isFinite(value)) { return null; diff --git a/pyproject.toml b/pyproject.toml index 38d8858ff..eb1f2076d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.53.0" +version = "2.54.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" } diff --git a/uv.lock b/uv.lock index 0e7d2ce9d..d5f0e8e22 100644 --- a/uv.lock +++ b/uv.lock @@ -685,7 +685,7 @@ wheels = [ [[package]] name = "lineageweave" -version = "2.37.0" +version = "2.54.0" source = { editable = "." } dependencies = [ { name = "certifi" },