From c7a8680f458dbb3c0377654ae01a203552e74c0e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 30 Aug 2026 23:54:19 +0000 Subject: [PATCH 1/3] feat(reports): leftover observed on grouping comparison graphic (v2.73.0) Caption persisted leftover observed Y on the grouping comparison leftover-map graphic from already-named observed_response (ADR 0316). Comparison copy uses leftover map comparison graphic leftover observed {label} so it stays distinct from leftover observed {label} and from strip Leftover map comparison observed. A missing or non-finite Y omits that caption and keeps leftover residual, leftover-map unexplained leftover, leftover-map cross share, leftover-map unexplained leftover share, leftover-map explained leftover share, leftover-map reconstruction, leftover-map distance, and comparison graphic coverage notes. Rank-0 origin cells still name Y 0.00 when that leftover is finite. Do not caption leftover expected this increment. Do not invent Y from R and E or leftover residual. Never invent a leftover score. Never invent a theta. No new columns. --- AGENTS.md | 13 ++ ARCHITECTURE.md | 6 +- ...73.0-leftover-map-compare-plot-observed.md | 39 ++++ CHANGELOG.md | 43 ++++ CLAUDE.md | 2 +- ...0315-leftover-map-compare-plot-residual.md | 9 +- ...0316-leftover-map-compare-plot-observed.md | 184 ++++++++++++++++++ docs/product-technical-gap-baseline.md | 21 ++ frontend/package.json | 2 +- frontend/src/App.test.tsx | 9 +- .../src/components/LeftoverMapPlot.test.tsx | 141 ++++++++++++++ frontend/src/components/LeftoverMapPlot.tsx | 13 +- frontend/src/components/LeftoverPairList.tsx | 3 +- frontend/src/i18n.test.ts | 18 ++ frontend/src/i18n.ts | 4 + frontend/src/leftoverMapPlotLayout.test.ts | 16 ++ frontend/src/leftoverMapPlotLayout.ts | 7 +- frontend/src/leftoverObservedExpected.test.ts | 14 ++ frontend/src/leftoverObservedExpected.ts | 3 + pyproject.toml | 2 +- uv.lock | 2 +- 21 files changed, 536 insertions(+), 15 deletions(-) create mode 100644 CHANGELOG.d/2.73.0-leftover-map-compare-plot-observed.md create mode 100644 docs/adr/0316-leftover-map-compare-plot-observed.md diff --git a/AGENTS.md b/AGENTS.md index ccf5a3160..6fb8835b2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -591,6 +591,16 @@ and leftover-map comparison graphic incomplete items when those helpers return u leftover-map rank, leftover-map axis share, leftover-map post coverage, leftover-map item coverage, leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover pair count, or the count of unused axes. +ADR 0316 captions leftover observed on that grouping comparison leftover-map +graphic when formatLeftoverMapObserved returns a usable badge; a missing or +non-finite `Y` omits that leftover-map comparison graphic leftover observed caption and keeps +leftover residual `R`, leftover-map unexplained leftover `U`, leftover-map cross share `x`, leftover-map unexplained leftover share `s`, leftover-map explained leftover share `e`, leftover-map reconstruction `R̂`, leftover-map distance `d`, leftover-map comparison graphic coverage, +leftover-map comparison graphic item coverage, leftover-map comparison graphic incomplete posts, +and leftover-map comparison graphic incomplete items when those helpers return usable integers. Do not invent +`Y` from `R` and `E`, leftover residual, leftover-map unexplained leftover, leftover-map reconstruction, leftover-map distance, plotted coordinates, +leftover-map rank, leftover-map axis share, leftover-map post coverage, leftover-map item coverage, +leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover pair +count, or the count of unused axes. 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 @@ -671,6 +681,9 @@ that grouping comparison leftover-map graphic with persisted leftover-map unexpl Grouping comparison leftover residual (ADR 0315) captions that grouping comparison leftover-map graphic with persisted leftover residual `R` when formatLeftoverMapResidual returns a usable badge. +Grouping comparison leftover observed (ADR 0316) captions +that grouping comparison leftover-map graphic with persisted leftover observed +`Y` when formatLeftoverMapObserved returns a usable badge. 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 3f2860b71..c0cea1234 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -734,6 +734,9 @@ badge. ADR 0315 captions leftover residual on that grouping comparison leftover-map graphic when formatLeftoverMapResidual returns a usable badge. +ADR 0316 captions leftover observed on that grouping +comparison leftover-map graphic when formatLeftoverMapObserved returns a usable +badge. 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 @@ -778,7 +781,8 @@ graphic (ADR 0311), leftover-map unexplained leftover share on the grouping comp graphic (ADR 0312), leftover-map cross share on the grouping comparison leftover-map graphic (ADR 0313), leftover-map unexplained leftover on the grouping comparison leftover-map graphic (ADR 0314), leftover residual on the grouping comparison leftover-map -graphic (ADR 0315); +graphic (ADR 0315), leftover observed on the grouping comparison leftover-map +graphic (ADR 0316); `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.73.0-leftover-map-compare-plot-observed.md b/CHANGELOG.d/2.73.0-leftover-map-compare-plot-observed.md new file mode 100644 index 000000000..3cd596b5c --- /dev/null +++ b/CHANGELOG.d/2.73.0-leftover-map-compare-plot-observed.md @@ -0,0 +1,39 @@ +## 2.73.0 — Leftover observed on grouping comparison leftover-map graphic + +- Caption leftover observed on the grouping comparison leftover-map + graphic (ADR 0316). 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, leftover-map complete-case + coverage when leftoverMapCoverageCounts returns usable complete-case integers, + leftover-map item complete-case coverage when leftoverMapItemCoverageCounts + returns usable complete-case integers, leftover-map incomplete post coverage + when leftoverMapIncompletePostCount returns a usable dropped integer, + leftover-map incomplete item coverage when leftoverMapIncompleteItemCount + returns a usable dropped integer, leftover-map reconstruction `R̂` when + formatLeftoverMapReconstruction returns a usable signed badge, leftover-map + explained leftover share `e` when formatLeftoverMapExplainedShare returns a + usable badge, leftover-map unexplained leftover share `s` when + formatLeftoverMapUnexplainedShare returns a usable badge, leftover-map + cross share `x` when formatLeftoverMapCrossShare returns a usable badge, leftover-map + unexplained leftover `U` when formatLeftoverMapUnexplained returns a usable badge, + leftover residual `R` when formatLeftoverMapResidual returns a usable badge, + and leftover observed `Y` on that grouping comparison leftover-map graphic; click a + post marker or a pair button opens that post. A missing or non-finite `Y` + omits that leftover-map comparison graphic leftover observed caption and keeps + leftover residual `R`, leftover-map unexplained leftover `U`, leftover-map cross share `x`, leftover-map unexplained leftover share `s`, leftover-map explained leftover + share `e`, leftover-map reconstruction `R̂`, leftover-map distance `d`, leftover-map + comparison graphic coverage, leftover-map comparison graphic item coverage, + leftover-map comparison graphic incomplete posts, and leftover-map comparison + graphic incomplete items when those helpers return usable integers. Rank-0 origin + cells still name `Y 0.00` when that persisted leftover observed is + finite. Comparison copy uses `leftover map comparison graphic leftover observed + {label}` so it stays distinct from `leftover observed + {label}` and from strip `Leftover map comparison observed`. + This increment does not caption leftover expected on the comparison graphic + with a distinct name. Do not invent `Y` from `R` and `E`, leftover residual, leftover-map reconstruction, + leftover-map unexplained leftover, leftover-map distance, plotted coordinates, leftover-map rank, + leftover-map axis share, leftover-map post coverage, leftover-map item coverage, + leftover-map incomplete post coverage, leftover-map incomplete item coverage, + leftover pair count, or the count of unused axes. A finite negative leftover + observed is shown, never clamped. Never invent a leftover score. + Never invent a theta. No new columns. diff --git a/CHANGELOG.md b/CHANGELOG.md index eacbaf474..91213f639 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 name leftover observed on the grouping + comparison leftover-map graphic (ADR 0316 / v2.73.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, leftover-map complete-case coverage when leftoverMapCoverageCounts + returns usable complete-case integers, leftover-map item complete-case + coverage when leftoverMapItemCoverageCounts returns usable complete-case + integers, leftover-map incomplete post coverage when leftoverMapIncompletePostCount + returns a usable dropped integer, leftover-map incomplete item coverage when + leftoverMapIncompleteItemCount returns a usable dropped integer, leftover-map + reconstruction `R̂` when formatLeftoverMapReconstruction returns a usable signed + badge, leftover-map explained leftover share `e` when formatLeftoverMapExplainedShare + returns a usable badge, leftover-map unexplained leftover share `s` when + formatLeftoverMapUnexplainedShare returns a usable badge, leftover-map + cross share `x` when formatLeftoverMapCrossShare returns a usable badge, leftover-map + unexplained leftover `U` when formatLeftoverMapUnexplained returns a usable badge, + leftover residual `R` when formatLeftoverMapResidual returns a usable badge, + and leftover observed `Y` on that grouping comparison leftover-map graphic when + formatLeftoverMapObserved returns a usable badge; click a post marker or a pair + button opens that post. A missing or non-finite `Y` omits that leftover-map + comparison graphic leftover observed caption and keeps leftover residual + `R`, leftover-map unexplained leftover `U`, leftover-map cross share `x`, leftover-map unexplained leftover share `s`, leftover-map explained leftover + share `e`, leftover-map reconstruction `R̂`, leftover-map distance `d`, leftover-map + comparison graphic coverage, leftover-map comparison graphic item coverage, + leftover-map comparison graphic incomplete posts, and leftover-map comparison + graphic incomplete items when those helpers return usable integers. Rank-0 origin + cells still plot at `(0, 0)` and still name `Y 0.00` when that persisted leftover + observed is finite. Comparison copy uses `leftover map comparison graphic + leftover observed {label}` so it stays distinct from + `leftover observed {label}` and from strip `Leftover map comparison observed`. + Leftover observed omits independently of leftover residual, leftover-map unexplained leftover, + leftover-map cross share, leftover-map unexplained leftover share, leftover-map explained leftover share, + leftover-map reconstruction, leftover-map distance, and coverage notes. This + increment does not caption leftover expected on the comparison + graphic with a distinct name. Do not invent `Y` from `R` and `E`, leftover residual, leftover-map + reconstruction, leftover-map unexplained leftover, leftover-map distance, plotted coordinates, + leftover-map rank, leftover-map axis share, leftover expected, + leftover-map post coverage, leftover-map item coverage, leftover-map incomplete + post coverage, leftover-map incomplete item coverage, leftover pair count, or the + count of unused axes. A finite negative leftover observed is shown, + never clamped. Omit the comparison graphic when no leftover pair has four finite + leftover-map coordinates. Never invent a leftover score or a theta. No new columns. + - Period leftover pairs now name leftover residual on the grouping comparison leftover-map graphic (ADR 0315 / v2.72.0). After `make seed`, closest and farthest leftover pairs sit above the member list with the diff --git a/CLAUDE.md b/CLAUDE.md index 6afe7c990..9f8bafce7 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 / 0297 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315), 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 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315 / 0316), 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/0315-leftover-map-compare-plot-residual.md b/docs/adr/0315-leftover-map-compare-plot-residual.md index a770fc040..b14ad095b 100644 --- a/docs/adr/0315-leftover-map-compare-plot-residual.md +++ b/docs/adr/0315-leftover-map-compare-plot-residual.md @@ -3,6 +3,9 @@ **Decision status:** Accepted **Date:** 2026-08-31 +**Amended by:** [ADR 0316](0316-leftover-map-compare-plot-observed.md) +(leftover observed on the grouping comparison leftover-map graphic) + Amends leftover residual on graphic-display pair segments ([ADR 0277](0277-leftover-map-segment-residual.md)), leftover residual on grouping comparison strip pair rows @@ -193,9 +196,9 @@ a usable leftover-map unexplained leftover caption, a usable leftover-map cross distance caption, a usable post caption, a usable item caption, a usable incomplete posts caption, and a usable incomplete items caption. -This increment does not caption leftover observed `Y` on the comparison graphic -with a distinct comparison-graphic name. That leftover observed already sits on -the strip through ADR 0299. A finite negative leftover on neighbouring fields is shown, +This increment does not caption leftover expected `E` on the comparison graphic +with a distinct comparison-graphic name. Leftover observed `Y` on that comparison +graphic is [ADR 0316](0316-leftover-map-compare-plot-observed.md). A finite negative leftover on neighbouring fields is shown, never clamped. Do not add SQL migrations. Do not edit shipped migrations. Do not persist inner diff --git a/docs/adr/0316-leftover-map-compare-plot-observed.md b/docs/adr/0316-leftover-map-compare-plot-observed.md new file mode 100644 index 000000000..627720339 --- /dev/null +++ b/docs/adr/0316-leftover-map-compare-plot-observed.md @@ -0,0 +1,184 @@ +# ADR 0316 — Name leftover observed on the grouping comparison leftover-map graphic + +**Decision status:** Accepted +**Date:** 2026-08-31 + +Amends leftover observed on graphic-display pair segments +([ADR 0278](0278-leftover-map-segment-observed.md)), leftover observed +on grouping comparison strip pair rows +([ADR 0299](0299-leftover-map-compare-observed.md)), leftover residual +on the grouping comparison leftover-map graphic +([ADR 0315](0315-leftover-map-compare-plot-residual.md)), leftover-map +unexplained leftover on the grouping comparison leftover-map graphic +([ADR 0314](0314-leftover-map-compare-plot-unexplained-leftover.md)), leftover-map +cross share on the grouping comparison leftover-map graphic +([ADR 0313](0313-leftover-map-compare-plot-cross-share.md)), leftover-map +unexplained leftover share on the grouping comparison leftover-map graphic +([ADR 0312](0312-leftover-map-compare-plot-unexplained-share.md)), leftover-map +explained leftover share on the grouping comparison leftover-map graphic +([ADR 0311](0311-leftover-map-compare-plot-explained-share.md)), leftover-map +reconstruction on the grouping comparison leftover-map graphic +([ADR 0310](0310-leftover-map-compare-plot-reconstruction.md)), leftover-map +incomplete item coverage on the grouping comparison leftover-map graphic +([ADR 0309](0309-leftover-map-compare-plot-incomplete-item.md)), leftover-map +incomplete post coverage on the grouping comparison leftover-map graphic +([ADR 0308](0308-leftover-map-compare-plot-incomplete-post.md)), leftover-map item +complete-case coverage on the grouping comparison leftover-map graphic +([ADR 0307](0307-leftover-map-compare-plot-item-coverage.md)), leftover-map +complete-case coverage on the grouping comparison leftover-map graphic +([ADR 0306](0306-leftover-map-compare-plot-coverage.md)), leftover-map axis share +on the grouping comparison leftover-map graphic +([ADR 0305](0305-leftover-map-compare-plot-axis-share.md)), leftover-map graphic +display on the grouping comparison strip +([ADR 0304](0304-leftover-map-compare-graphic.md)), leftover observed +disclosure ([ADR 0163](0163-leftover-observed-expected.md)), leftover pairs on +the grouping comparison strip +([ADR 0149](0149-leftover-pairs-on-comparison-strip.md)), leftover residual +on grouping comparison strip pair rows +([ADR 0298](0298-leftover-map-compare-residual.md)), leftover residual +on pair segments ([ADR 0277](0277-leftover-map-segment-residual.md)), leftover-map +graphic display ([ADR 0268](0268-leftover-map-graphic-display.md)), leftover-map +coordinates ([ADR 0267](0267-leftover-map-coordinates.md)). Independent of leftover +expected on the grouping comparison leftover-map graphic. + +## Context + +ADR 0163 already persists leftover observed `Y` and leftover expected +`E[Y|θ, item]`. ADR 0278 already captions period-report leftover-map pair +segments with `leftover observed {label}` when that leftover observed is +finite. ADR 0299 already captions grouping comparison leftover-pair +buttons with that same persisted leftover observed under accessible name +`Leftover map comparison observed`. ADR 0315 already captions leftover +residual on the grouping comparison leftover-map graphic. The comparison +graphic still reuses `leftover observed {label}`, so a buyer who compares leftover +pairs can treat the period-report graphic caption as the comparison graphic +leftover observed even after the strip names `Y`. Hiding a distinct +comparison-graphic leftover observed caption lets leftover residual `R` or leftover expected +`E` be read as leftover observed without a next action. When `Y`, `E`, and `R` are +finite, `Y − E = R`; the comparison graphic must name the same +persisted `Y` the pair row and strip already show. A finite negative leftover is +shown, never clamped. + +This increment captions leftover observed on the grouping +comparison leftover-map graphic from already-named leftover observed +through formatLeftoverMapObserved. Comparison copy uses the accessible +name `leftover map comparison graphic leftover observed {label}` so it stays +distinct from `leftover observed {label}` on the +period-report graphic and from strip `Leftover map comparison observed`. +It does not add columns. It does not recompute leftover observed +from `R` and `E`, leftover residual, leftover-map unexplained leftover, leftover-map reconstruction, leftover-map distance, +plotted coordinates, leftover-map rank, leftover-map axis share, leftover-map post +coverage, leftover-map item coverage, leftover-map incomplete post coverage, +leftover-map incomplete item coverage, leftover pair count, or the count of unused +axes. 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 **0316** so it does not collide with +leftover residual on the grouping comparison leftover-map graphic +(0315), leftover-map unexplained leftover on the grouping comparison leftover-map graphic +(0314), leftover-map cross share on the grouping comparison leftover-map graphic +(0313), leftover residual on grouping comparison strip pair rows +(0298), leftover residual on pair segments (0277), leftover observed +on grouping comparison strip pair rows (0299), leftover observed +on pair segments (0278), leftover observed / expected disclosure (0163), leftover-map +graphic display (0268), leftover-map coordinates (0267), or the dashboard stacks. + +## Decision + +On the grouping comparison leftover-map graphic, caption each pair segment +with persisted leftover observed when formatLeftoverMapObserved +returns a usable badge, next to leftover residual `R`. Use the +distinct accessible name `leftover map comparison graphic leftover observed {label}` +so the graphic caption is not the strip badge (`Leftover map comparison observed`) +and is not the period-report graphic caption (`leftover observed {label}`). +A missing or non-finite `Y` omits that leftover-map comparison graphic +leftover observed caption and keeps leftover residual `R` when +formatLeftoverMapResidual returns a usable badge, leftover-map unexplained leftover `U` when +formatLeftoverMapUnexplained returns a usable badge, leftover-map cross share `x` when +formatLeftoverMapCrossShare returns a usable badge, leftover-map unexplained leftover share `s` when +formatLeftoverMapUnexplainedShare returns a usable badge, leftover-map explained leftover share `e` when +formatLeftoverMapExplainedShare returns a usable badge, leftover-map reconstruction `R̂` when +formatLeftoverMapReconstruction returns a usable signed badge, leftover-map distance `d`, leftover map comparison +axis share when finite, leftover map comparison axis text, leftover-map comparison +graphic coverage when leftoverMapCoverageCounts returns usable complete-case +integers, leftover-map comparison graphic item coverage when leftoverMapItemCoverageCounts +returns usable complete-case integers, leftover-map comparison graphic incomplete +posts when leftoverMapIncompletePostCount returns a usable dropped integer, +leftover-map comparison graphic incomplete items when leftoverMapIncompleteItemCount +returns a usable dropped integer, leftover-map rank when that rank is a non-negative +integer, leftover expected `E` when finite, leftover-map reconstruction `R̂` when +finite, leftover-map distance `d`, plus the strip leftover observed badge. +Rank-0 origin cells still name `Y 0.00` when that persisted leftover +observed is finite. A finite negative leftover is shown, never clamped. Do not invent `Y` from +`R` and `E`, leftover residual, leftover-map reconstruction, leftover-map unexplained leftover, leftover-map distance, +plotted coordinates, leftover-map rank, leftover-map axis share, leftover-map post +coverage, leftover-map item coverage, leftover-map incomplete post coverage, +leftover-map incomplete item coverage, leftover pair count, or the count of unused +axes. Click a post marker to open that post. + +Leftover observed omits independently of leftover residual captions, +leftover-map unexplained leftover captions, +leftover-map cross share captions, +unexplained leftover share captions, explained leftover share captions, reconstruction captions, +coverage notes, and leftover-map distance. A missing leftover +observed omits leftover-map comparison graphic leftover observed and keeps +a usable leftover residual caption, a usable leftover-map unexplained leftover caption, a usable leftover-map cross share caption, a usable unexplained leftover share caption, a usable explained leftover share caption, a usable reconstruction caption, a usable +distance caption, a usable post caption, a usable item caption, a usable incomplete +posts caption, and a usable incomplete items caption. + +This increment does not caption leftover expected `E` on the comparison graphic +with a distinct comparison-graphic name. That leftover expected already sits on +the strip through ADR 0300. A finite negative leftover on neighbouring fields is shown, +never clamped. + +Do not add SQL migrations. 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`, grouping comparison leftover pairs that already show +the leftover-map graphic of persisted `ξ` / `ζ` also name leftover +observed on that graphic when formatLeftoverMapObserved +returns a usable badge. Rank-0 unused axes still plot at +the origin and still name `Y 0.00` when that persisted leftover +observed is finite. Click a post marker or a pair button opens that post. Hidden posts +stay hidden. When `Y`, `E`, and `R` are finite, `Y − E = R`. When `R`, `R̂`, +and `U` are finite, `U + R̂ = R`. When `R`, `R̂`, `U`, `x`, `s`, and `e` are +finite, `e + s + x = 1`. When coordinates, reconstruction, and distance +are finite, `R̂ = ξ · ζ` and `d = ‖ξ − ζ‖`. + +## Related + +Independent of leftover interaction-map persistence, leftover-criterion +evaluation landing, leftover residual disclosure, leftover-map +complete-case coverage persistence, leftover pairs on the grouping comparison +strip, leftover residual on grouping comparison strip pair rows, leftover +expected on grouping comparison strip pair rows, leftover observed +on grouping comparison strip pair rows, leftover residual +on pair segments, leftover observed on pair segments, leftover expected on +pair segments, leftover-map unexplained leftover on the grouping +comparison leftover-map graphic, leftover residual on the grouping +comparison leftover-map graphic, leftover-map graphic display, leftover-map +coordinates, leftover-map coordinate ticks, leftover-map complete-case +coverage on the grouping comparison leftover-map graphic, leftover-map +axis share on the grouping comparison leftover-map graphic, +and leftover expected on the grouping comparison leftover-map graphic. + +## References + +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. Observed +category after IRT main effects is persisted `Y`. Grouping comparison leftover +observed captions that persisted leftover on the grouping comparison leftover-map +graphic only when formatLeftoverMapObserved returns a usable badge. +Rank-0 unused axes still name `Y 0.00` when that leftover +observed is stored. A finite negative leftover is shown, never clamped.) + +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 diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 5594209eb..cba1004cf 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,26 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-08-31 08:50 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not +> self-approve. #855 leftover residual on the grouping comparison leftover-map +> graphic (v2.72.0 / ADR 0315) is `1087b16` (a11y name +> `leftover map comparison graphic leftover residual {label}`), stacked on #854 @ +> `935cdea`. Independent APPROVE is missing. Next buyer increment on this +> cycle: leftover observed on the grouping comparison leftover-map +> graphic (ADR 0316 / v2.73.0) delivered locally on +> `feat/leftover-map-compare-plot-observed-v2730`. Caption persisted leftover +> observed `Y` on that comparison graphic from already-named +> observed_response. Distinct accessible name `leftover map comparison graphic +> leftover observed {label}`. Omit when formatLeftoverMapObserved fail-closes. +> Do not caption leftover expected on that comparison graphic with a distinct name this +> increment. Do not invent `Y` from `R` and `E`, leftover residual, leftover-map reconstruction, leftover-map unexplained leftover, +> leftover-map distance, plotted coordinates, leftover-map rank, leftover-map axis share, +> leftover-map post coverage, leftover-map item coverage, leftover-map incomplete post coverage, +> leftover-map incomplete item coverage, leftover pair count, or the count of unused +> axes. Stack onto #855. Leave open for independent review. Do not squash-merge. Do not +> self-approve. Issues #79 and #87 stay open. Do not merge #808 without independent APPROVE. +> Closed #96 this cycle (weaker duplicate of #91). Never self-approve. + > Exact-head loop overlay: 2026-08-31 08:20 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not > self-approve. #854 leftover-map unexplained leftover on the grouping comparison leftover-map diff --git a/frontend/package.json b/frontend/package.json index 4ac282897..64af26a98 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.72.0", + "version": "2.73.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index f7f212f1f..7e950b3f4 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -4594,10 +4594,15 @@ describe("App, authenticated", () => { )[1], ).toHaveTextContent("R +0.40"); expect( - within(screen.getByLabelText("Grouping comparison")).queryByLabelText( - /leftover observed/, + within(screen.getByLabelText("Grouping comparison")).getByLabelText( + "leftover map comparison graphic leftover observed Y 2.40", ), ).toHaveTextContent("Y 2.40"); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + "leftover observed Y 2.40", + ), + ).not.toBeInTheDocument(); expect( within(screen.getByLabelText("Grouping comparison")).getByLabelText( "Leftover map comparison observed", diff --git a/frontend/src/components/LeftoverMapPlot.test.tsx b/frontend/src/components/LeftoverMapPlot.test.tsx index 23902ab2b..4805aa9e8 100644 --- a/frontend/src/components/LeftoverMapPlot.test.tsx +++ b/frontend/src/components/LeftoverMapPlot.test.tsx @@ -2024,4 +2024,145 @@ describe("LeftoverMapPlot", () => { ); expect(screen.getByText("leftover map comparison axis 1 (0%)")).toBeInTheDocument(); }); + + it("names leftover observed on the comparison graphic with a distinct accessible name", () => { + render( + , + ); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 2.40"), + ).toHaveTextContent("Y 2.40"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 0.90"), + ).toHaveTextContent("Y 0.90"); + expect(screen.queryByLabelText("leftover observed Y 2.40")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("leftover observed Y 0.90")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("Leftover map comparison observed")).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic leftover residual R +0.40"), + ).toHaveTextContent("R +0.40"); + expect( + screen.getByLabelText("leftover map comparison graphic unexplained leftover U +0.05"), + ).toHaveTextContent("U +0.05"); + expect( + screen.getByLabelText("leftover map comparison graphic reconstruction R̂ +0.25"), + ).toHaveTextContent("R̂ +0.25"); + expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); + }); + + it("omits leftover-map comparison graphic leftover observed when Y is missing or not usable", () => { + render( + , + ); + expect(screen.queryByLabelText(/leftover map comparison graphic leftover observed Y/)).not.toBeInTheDocument(); + expect(screen.queryByLabelText(/^leftover observed Y/)).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic leftover residual R +0.40"), + ).toHaveTextContent("R +0.40"); + expect( + screen.getByLabelText("leftover map comparison graphic unexplained leftover U +0.05"), + ).toHaveTextContent("U +0.05"); + expect( + screen.getByLabelText("leftover map comparison graphic reconstruction R̂ +0.25"), + ).toBeInTheDocument(); + expect(screen.getByLabelText("leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByLabelText("Leftover map comparison graphic coverage")).toHaveTextContent( + "Leftover map used 2 of 3 scored posts (complete-case)", + ); + expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); + }); + + it("names rank-0 origin leftover observed on the comparison graphic when that persisted value is finite", () => { + render( + , + ); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 0.00"), + ).toHaveTextContent("Y 0.00"); + expect(screen.queryByLabelText("leftover observed Y 0.00")).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic leftover residual R 0.00"), + ).toHaveTextContent("R 0.00"); + expect( + screen.getByLabelText("leftover map comparison graphic unexplained leftover U 0.00"), + ).toHaveTextContent("U 0.00"); + expect( + screen.getByLabelText("leftover map comparison graphic reconstruction R̂ 0.00"), + ).toHaveTextContent("R̂ 0.00"); + expect(screen.getByLabelText("Leftover map comparison graphic coverage")).toHaveTextContent( + "Leftover map used 0 of 3 scored posts (complete-case)", + ); + expect(screen.getByText("leftover map comparison axis 1 (0%)")).toBeInTheDocument(); + }); }); diff --git a/frontend/src/components/LeftoverMapPlot.tsx b/frontend/src/components/LeftoverMapPlot.tsx index d6f26adb8..5e52e7f49 100644 --- a/frontend/src/components/LeftoverMapPlot.tsx +++ b/frontend/src/components/LeftoverMapPlot.tsx @@ -39,6 +39,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_CROSS_SHARE, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, LEFTOVER_MAP_PLOT_CAPTION, LEFTOVER_MAP_PLOT_POST_ACTION, LEFTOVER_MAP_PLOT_SEGMENT_CROSS_SHARE, @@ -161,6 +162,9 @@ function leftoverMapPlotAxisText( * labels. ADR 0315 captions leftover residual on that * comparison graphic from already-named leftover residual * with distinct leftover map comparison graphic leftover residual + * labels. ADR 0316 captions leftover observed on that + * comparison graphic from already-named leftover observed + * with distinct leftover map comparison graphic leftover observed * labels. * Never invent a leftover score. */ @@ -426,9 +430,12 @@ export function LeftoverMapPlot({ x={segment.observedX} y={segment.observedY} textAnchor="middle" - aria-label={tf(LEFTOVER_MAP_PLOT_SEGMENT_OBSERVED, { - label: segment.observedLabel, - })} + aria-label={tf( + variant === "comparison" + ? LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED + : LEFTOVER_MAP_PLOT_SEGMENT_OBSERVED, + { label: segment.observedLabel }, + )} > {segment.observedLabel} diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index f0e60a47c..6779c9dd6 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -124,7 +124,8 @@ export type LeftoverPairListProps = { * pair list. ADR 0314 captions leftover-map unexplained leftover on that * grouping comparison leftover-map graphic, not this pair list. ADR 0315 * captions leftover residual on that grouping comparison leftover-map graphic, - * not this pair list. Every badge still + * not this pair list. ADR 0316 captions leftover observed on that grouping + * comparison leftover-map graphic, 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 09d6d6115..8f7b69eb4 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -97,6 +97,7 @@ describe("i18n", () => { "Leftover map comparison unexplained leftover", "leftover map comparison graphic leftover residual {label}", "Leftover map comparison residual", + "leftover map comparison graphic leftover observed {label}", "Leftover map comparison observed", "Leftover map comparison expected", "Leftover map comparison rank", @@ -597,6 +598,23 @@ describe("i18n", () => { }, ); + it.each([ + ["ko", "잔여 지도 비교 그림 관측 Y 2.40"], + ["zh", "残差地图比较图形观测 Y 2.40"], + ["ja", "残差マップの比較図観測 Y 2.40"], + ["vi", "quan sát đồ họa so sánh bản đồ phần dư Y 2.40"], + ] as const)( + "formats leftover map comparison graphic leftover observed in %s", + (locale, expected) => { + setLocale(locale); + expect( + tf("leftover map comparison graphic leftover observed {label}", { + label: "Y 2.40", + }), + ).toBe(expected); + }, + ); + it.each([ ["ko", "잔여 지도 비교 설명 잔여 점유율"], ["zh", "残差地图比较已解释残差份额"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 7452be173..0c32ac23c 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -289,6 +289,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison unexplained leftover": "잔여 지도 비교 미설명 잔여", "leftover map comparison graphic leftover residual {label}": "잔여 지도 비교 그림 잔여 {label}", "Leftover map comparison residual": "잔여 지도 비교 잔여", + "leftover map comparison graphic leftover observed {label}": "잔여 지도 비교 그림 관측 {label}", "Leftover map comparison observed": "잔여 지도 비교 관측", "Leftover map comparison expected": "잔여 지도 비교 기대", "Leftover map comparison rank": "잔여 지도 비교 순위", @@ -927,6 +928,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison unexplained leftover": "残差地图比较未解释残差", "leftover map comparison graphic leftover residual {label}": "残差地图比较图形残差 {label}", "Leftover map comparison residual": "残差地图比较残差", + "leftover map comparison graphic leftover observed {label}": "残差地图比较图形观测 {label}", "Leftover map comparison observed": "残差地图比较观测", "Leftover map comparison expected": "残差地图比较期望", "Leftover map comparison rank": "残差地图比较秩", @@ -1586,6 +1588,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison unexplained leftover": "残差マップの比較未説明残差", "leftover map comparison graphic leftover residual {label}": "残差マップの比較図残差 {label}", "Leftover map comparison residual": "残差マップの比較残差", + "leftover map comparison graphic leftover observed {label}": "残差マップの比較図観測 {label}", "Leftover map comparison observed": "残差マップの比較観測", "Leftover map comparison expected": "残差マップの比較期待", "Leftover map comparison rank": "残差マップの比較階数", @@ -2224,6 +2227,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison unexplained leftover": "Phần dư chưa giải thích so sánh", "leftover map comparison graphic leftover residual {label}": "phần dư đồ họa so sánh bản đồ phần dư {label}", "Leftover map comparison residual": "Phần dư so sánh bản đồ phần dư", + "leftover map comparison graphic leftover observed {label}": "quan sát đồ họa so sánh bản đồ phần dư {label}", "Leftover map comparison observed": "Quan sát so sánh bản đồ phần dư", "Leftover map comparison expected": "Kỳ vọng so sánh bản đồ phần dư", "Leftover map comparison rank": "Hạng so sánh bản đồ phần dư", diff --git a/frontend/src/leftoverMapPlotLayout.test.ts b/frontend/src/leftoverMapPlotLayout.test.ts index 44a55a220..a86e909a9 100644 --- a/frontend/src/leftoverMapPlotLayout.test.ts +++ b/frontend/src/leftoverMapPlotLayout.test.ts @@ -13,6 +13,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_CROSS_SHARE, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, LEFTOVER_MAP_COMPARE_PLOT_SVG, PLOT_HEIGHT, PLOT_PADDING, @@ -1288,4 +1289,19 @@ describe("leftover map comparison graphic labels", () => { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED, ); }); + + it("stays distinct from leftover observed copy", () => { + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED).toBe( + "leftover map comparison graphic leftover observed {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED).not.toBe( + "leftover observed {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED).not.toBe( + "Leftover map comparison observed", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED).not.toBe( + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, + ); + }); }); diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index 77728d5ca..e22b53ca4 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -43,7 +43,8 @@ * unexplained leftover share on that graphic (ADR 0312), grouping comparison leftover-map * cross share on that graphic (ADR 0313), and grouping comparison leftover-map * unexplained leftover on that graphic (ADR 0314), and grouping comparison leftover - * residual on that graphic (ADR 0315) caption the pair list or the grouping comparison + * residual on that graphic (ADR 0315), and grouping comparison leftover + * observed on that graphic (ADR 0316) caption the pair list or the grouping comparison * strip. ADR 0304 reuses this graphic layout on the grouping comparison * strip. ADR 0305 captions leftover-map axis share on that comparison * graphic from already-named leftover-map axes. ADR 0306 captions leftover-map @@ -63,6 +64,8 @@ * comparison graphic from already-named leftover-map unexplained leftover. * ADR 0315 captions leftover residual on that comparison graphic from * already-named leftover residual. + * ADR 0316 captions leftover observed on that comparison graphic from + * already-named leftover observed. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; @@ -131,6 +134,8 @@ export { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL } from "./leftoverResidual"; export const LEFTOVER_MAP_PLOT_SEGMENT_OBSERVED = "leftover observed {label}"; +export { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED } from "./leftoverObservedExpected"; + export const LEFTOVER_MAP_PLOT_SEGMENT_EXPECTED = "leftover expected {label}"; diff --git a/frontend/src/leftoverObservedExpected.test.ts b/frontend/src/leftoverObservedExpected.test.ts index 561a599b4..c38c454c8 100644 --- a/frontend/src/leftoverObservedExpected.test.ts +++ b/frontend/src/leftoverObservedExpected.test.ts @@ -5,6 +5,7 @@ import { formatLeftoverObservedExpected, LEFTOVER_MAP_COMPARE_OBSERVED_LABEL, LEFTOVER_MAP_COMPARE_EXPECTED_LABEL, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, } from "./leftoverObservedExpected"; describe("formatLeftoverObservedExpected", () => { @@ -41,6 +42,19 @@ describe("formatLeftoverMapObserved", () => { expect(LEFTOVER_MAP_COMPARE_OBSERVED_LABEL).toBe("Leftover map comparison observed"); expect(LEFTOVER_MAP_COMPARE_OBSERVED_LABEL).not.toBe("leftover observed {label}"); }); + + it("keeps the grouping comparison graphic leftover observed label distinct from the graphic and strip labels", () => { + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED).toBe( + "leftover map comparison graphic leftover observed {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED).not.toBe("leftover observed {label}"); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED).not.toBe( + LEFTOVER_MAP_COMPARE_OBSERVED_LABEL, + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED).not.toBe( + "leftover map comparison graphic leftover residual {label}", + ); + }); }); describe("formatLeftoverMapExpected", () => { diff --git a/frontend/src/leftoverObservedExpected.ts b/frontend/src/leftoverObservedExpected.ts index a647dae15..5d77b7aea 100644 --- a/frontend/src/leftoverObservedExpected.ts +++ b/frontend/src/leftoverObservedExpected.ts @@ -6,6 +6,9 @@ export const LEFTOVER_MAP_COMPARE_OBSERVED_LABEL = export const LEFTOVER_MAP_COMPARE_EXPECTED_LABEL = "Leftover map comparison expected"; +export const LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED = + "leftover map comparison graphic leftover observed {label}"; + export function formatLeftoverObservedExpected( observed: number | null | undefined, expected: number | null | undefined, diff --git a/pyproject.toml b/pyproject.toml index f0016537b..e18874584 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.72.0" +version = "2.73.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 e04d99617..8226b9653 100644 --- a/uv.lock +++ b/uv.lock @@ -685,7 +685,7 @@ wheels = [ [[package]] name = "lineageweave" -version = "2.72.0" +version = "2.73.0" source = { editable = "." } dependencies = [ { name = "certifi" }, From 9f34d2cb97b6e525618347485f3c507f273cfa5b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 31 Aug 2026 00:01:14 +0000 Subject: [PATCH 2/3] feat(reports): leftover expected on grouping comparison graphic (v2.74.0) Caption persisted leftover expected E on the grouping comparison leftover-map graphic from already-named expected_response (ADR 0317). Comparison copy uses leftover map comparison graphic leftover expected {label} so it stays distinct from leftover expected {label} and from strip Leftover map comparison expected. A missing or non-finite E omits that caption and keeps leftover observed, leftover residual, leftover-map unexplained leftover, leftover-map cross share, leftover-map reconstruction, leftover-map distance, and comparison graphic coverage notes. Rank-0 origin cells still name E 0.00 when that leftover is finite. Do not caption leftover-map rank this increment. Do not invent E from Y and R. Never invent a leftover score. Never invent a theta. No new columns. --- AGENTS.md | 13 ++ ARCHITECTURE.md | 6 +- ...74.0-leftover-map-compare-plot-expected.md | 16 +++ CHANGELOG.md | 16 +++ CLAUDE.md | 2 +- ...0316-leftover-map-compare-plot-observed.md | 9 +- ...0317-leftover-map-compare-plot-expected.md | 101 ++++++++++++++ docs/product-technical-gap-baseline.md | 16 +++ frontend/package.json | 2 +- frontend/src/App.test.tsx | 9 +- .../src/components/LeftoverMapPlot.test.tsx | 123 ++++++++++++++++++ frontend/src/components/LeftoverMapPlot.tsx | 13 +- frontend/src/components/LeftoverPairList.tsx | 3 +- frontend/src/i18n.test.ts | 18 +++ frontend/src/i18n.ts | 4 + frontend/src/leftoverMapPlotLayout.test.ts | 16 +++ frontend/src/leftoverMapPlotLayout.ts | 7 +- frontend/src/leftoverObservedExpected.test.ts | 14 ++ frontend/src/leftoverObservedExpected.ts | 3 + pyproject.toml | 2 +- uv.lock | 2 +- 21 files changed, 380 insertions(+), 15 deletions(-) create mode 100644 CHANGELOG.d/2.74.0-leftover-map-compare-plot-expected.md create mode 100644 docs/adr/0317-leftover-map-compare-plot-expected.md diff --git a/AGENTS.md b/AGENTS.md index 6fb8835b2..aeebe60ea 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -601,6 +601,16 @@ and leftover-map comparison graphic incomplete items when those helpers return u leftover-map rank, leftover-map axis share, leftover-map post coverage, leftover-map item coverage, leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover pair count, or the count of unused axes. +ADR 0317 captions leftover expected on that grouping comparison leftover-map +graphic when formatLeftoverMapExpected returns a usable badge; a missing or +non-finite `E` omits that leftover-map comparison graphic leftover expected caption and keeps +leftover observed `Y`, leftover residual `R`, leftover-map unexplained leftover `U`, leftover-map cross share `x`, leftover-map unexplained leftover share `s`, leftover-map explained leftover share `e`, leftover-map reconstruction `R̂`, leftover-map distance `d`, leftover-map comparison graphic coverage, +leftover-map comparison graphic item coverage, leftover-map comparison graphic incomplete posts, +and leftover-map comparison graphic incomplete items when those helpers return usable integers. Do not invent +`E` from `Y` and `R`, leftover observed, leftover residual, leftover-map unexplained leftover, leftover-map reconstruction, leftover-map distance, plotted coordinates, +leftover-map rank, leftover-map axis share, leftover-map post coverage, leftover-map item coverage, +leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover pair +count, or the count of unused axes. 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 @@ -684,6 +694,9 @@ that grouping comparison leftover-map graphic with persisted leftover residual Grouping comparison leftover observed (ADR 0316) captions that grouping comparison leftover-map graphic with persisted leftover observed `Y` when formatLeftoverMapObserved returns a usable badge. +Grouping comparison leftover expected (ADR 0317) captions +that grouping comparison leftover-map graphic with persisted leftover expected +`E` when formatLeftoverMapExpected returns a usable badge. 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 c0cea1234..1df385c57 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -737,6 +737,9 @@ badge. ADR 0316 captions leftover observed on that grouping comparison leftover-map graphic when formatLeftoverMapObserved returns a usable badge. +ADR 0317 captions leftover expected on that grouping +comparison leftover-map graphic when formatLeftoverMapExpected returns a usable +badge. 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 @@ -782,7 +785,8 @@ graphic (ADR 0312), leftover-map cross share on the grouping comparison leftover graphic (ADR 0313), leftover-map unexplained leftover on the grouping comparison leftover-map graphic (ADR 0314), leftover residual on the grouping comparison leftover-map graphic (ADR 0315), leftover observed on the grouping comparison leftover-map -graphic (ADR 0316); +graphic (ADR 0316), leftover expected on the grouping comparison leftover-map +graphic (ADR 0317); `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.74.0-leftover-map-compare-plot-expected.md b/CHANGELOG.d/2.74.0-leftover-map-compare-plot-expected.md new file mode 100644 index 000000000..e9f778ad2 --- /dev/null +++ b/CHANGELOG.d/2.74.0-leftover-map-compare-plot-expected.md @@ -0,0 +1,16 @@ +## 2.74.0 — Leftover expected on grouping comparison leftover-map graphic + +- Caption leftover expected on the grouping comparison leftover-map + graphic (ADR 0317). After `make seed`, closest and farthest leftover pairs + sit above the member list with leftover expected `E` on that grouping + comparison leftover-map graphic when formatLeftoverMapExpected returns a usable + badge; click a post marker or a pair button opens that post. A missing or + non-finite `E` omits that leftover-map comparison graphic leftover expected + caption and keeps leftover observed `Y` and leftover residual `R`. Rank-0 origin + cells still name `E 0.00` when that persisted leftover expected is finite. + Comparison copy uses `leftover map comparison graphic leftover expected {label}` + so it stays distinct from `leftover expected {label}` and from strip + `Leftover map comparison expected`. This increment does not caption leftover-map + rank on the comparison graphic with a distinct name. Do not invent `E` from + `Y` and `R`. A finite negative leftover expected is shown, never clamped. + Never invent a leftover score. Never invent a theta. No new columns. diff --git a/CHANGELOG.md b/CHANGELOG.md index 91213f639..9f2f1753a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,22 @@ All notable changes to this project are documented here. Format follows ### Added +- Period leftover pairs now name leftover expected on the grouping + comparison leftover-map graphic (ADR 0317 / v2.74.0). After `make seed`, + closest and farthest leftover pairs sit above the member list with leftover + expected `E` on that grouping comparison leftover-map graphic when + formatLeftoverMapExpected returns a usable badge; click a post marker or a pair + button opens that post. A missing or non-finite `E` omits that leftover-map + comparison graphic leftover expected caption and keeps leftover observed `Y` + and leftover residual `R`. Rank-0 origin cells still name `E 0.00` when that + persisted leftover expected is finite. Comparison copy uses + `leftover map comparison graphic leftover expected {label}` so it stays + distinct from `leftover expected {label}` and from strip + `Leftover map comparison expected`. This increment does not caption leftover-map + rank on the comparison graphic with a distinct name. Do not invent `E` from + `Y` and `R`. A finite negative leftover expected is shown, never clamped. + Never invent a leftover score or a theta. No new columns. + - Period leftover pairs now name leftover observed on the grouping comparison leftover-map graphic (ADR 0316 / v2.73.0). After `make seed`, closest and farthest leftover pairs sit above the member list with the diff --git a/CLAUDE.md b/CLAUDE.md index 9f8bafce7..8362530c8 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 / 0297 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315 / 0316), 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 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315 / 0316 / 0317), 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/0316-leftover-map-compare-plot-observed.md b/docs/adr/0316-leftover-map-compare-plot-observed.md index 627720339..9a59ec390 100644 --- a/docs/adr/0316-leftover-map-compare-plot-observed.md +++ b/docs/adr/0316-leftover-map-compare-plot-observed.md @@ -3,6 +3,9 @@ **Decision status:** Accepted **Date:** 2026-08-31 +**Amended by:** [ADR 0317](0317-leftover-map-compare-plot-expected.md) +(leftover expected on the grouping comparison leftover-map graphic) + Amends leftover observed on graphic-display pair segments ([ADR 0278](0278-leftover-map-segment-observed.md)), leftover observed on grouping comparison strip pair rows @@ -126,9 +129,9 @@ a usable leftover residual caption, a usable leftover-map unexplained leftover c distance caption, a usable post caption, a usable item caption, a usable incomplete posts caption, and a usable incomplete items caption. -This increment does not caption leftover expected `E` on the comparison graphic -with a distinct comparison-graphic name. That leftover expected already sits on -the strip through ADR 0300. A finite negative leftover on neighbouring fields is shown, +This increment does not caption leftover-map rank on the comparison graphic +with a distinct comparison-graphic name. Leftover expected `E` on that comparison +graphic is [ADR 0317](0317-leftover-map-compare-plot-expected.md). A finite negative leftover on neighbouring fields is shown, never clamped. Do not add SQL migrations. Do not edit shipped migrations. Do not persist inner diff --git a/docs/adr/0317-leftover-map-compare-plot-expected.md b/docs/adr/0317-leftover-map-compare-plot-expected.md new file mode 100644 index 000000000..19da44c0f --- /dev/null +++ b/docs/adr/0317-leftover-map-compare-plot-expected.md @@ -0,0 +1,101 @@ +# ADR 0317 — Name leftover expected on the grouping comparison leftover-map graphic + +**Decision status:** Accepted +**Date:** 2026-08-31 + +Amends leftover expected on graphic-display pair segments +([ADR 0279](0279-leftover-map-segment-expected.md)), leftover expected +on grouping comparison strip pair rows +([ADR 0300](0300-leftover-map-compare-expected.md)), leftover observed +on the grouping comparison leftover-map graphic +([ADR 0316](0316-leftover-map-compare-plot-observed.md)), leftover residual +on the grouping comparison leftover-map graphic +([ADR 0315](0315-leftover-map-compare-plot-residual.md)), leftover observed `Y` / +expected `E` ([ADR 0163](0163-leftover-observed-expected.md)), leftover-map graphic +display on the grouping comparison strip +([ADR 0304](0304-leftover-map-compare-graphic.md)). Independent of leftover-map +rank on the grouping comparison leftover-map graphic. + +## Context + +ADR 0163 already persists leftover expected `E[Y|θ, item]`. ADR 0279 already +captions period-report leftover-map pair segments with +`leftover expected {label}` when that leftover expected is finite. ADR 0300 +already captions grouping comparison leftover-pair buttons with that same +persisted leftover expected under accessible name +`Leftover map comparison expected`. ADR 0316 already captions leftover +observed on the grouping comparison leftover-map graphic. The comparison +graphic still reuses `leftover expected {label}`, so a buyer who compares leftover +pairs can treat the period-report graphic caption as the comparison graphic +leftover expected even after the strip names `E`. Hiding a distinct +comparison-graphic leftover expected caption lets leftover observed `Y` or leftover residual +`R` be read as leftover expected without a next action. When `Y`, `E`, and `R` are +finite, `Y − E = R`; the comparison graphic must name the same +persisted `E` the pair row and strip already show. A finite negative leftover is +shown, never clamped. + +This increment captions leftover expected on the grouping +comparison leftover-map graphic from already-named leftover expected +through formatLeftoverMapExpected. Comparison copy uses the accessible +name `leftover map comparison graphic leftover expected {label}` so it stays +distinct from `leftover expected {label}` on the +period-report graphic and from strip `Leftover map comparison expected`. +It does not add columns. It does not recompute leftover expected +from `Y` and `R`. Do not invent a leftover score. Do not invent a theta. + +This protected increment uses **0317** so it does not collide with leftover +observed on the grouping comparison leftover-map graphic (0316), leftover residual +on that graphic (0315), leftover expected on grouping comparison strip pair rows +(0300), leftover expected on pair segments (0279), leftover observed / expected +disclosure (0163), leftover-map graphic display (0268), or the dashboard stacks. + +## Decision + +On the grouping comparison leftover-map graphic, caption each pair segment +with persisted leftover expected when formatLeftoverMapExpected +returns a usable badge, next to leftover observed `Y`. Use the +distinct accessible name `leftover map comparison graphic leftover expected {label}` +so the graphic caption is not the strip badge (`Leftover map comparison expected`) +and is not the period-report graphic caption (`leftover expected {label}`). +A missing or non-finite `E` omits that leftover-map comparison graphic +leftover expected caption and keeps leftover observed `Y` when +formatLeftoverMapObserved returns a usable badge, leftover residual `R` when +formatLeftoverMapResidual returns a usable badge, leftover-map unexplained leftover `U`, leftover-map cross share `x`, leftover-map unexplained leftover share `s`, leftover-map explained leftover share `e`, leftover-map reconstruction `R̂`, leftover-map distance `d`, leftover-map comparison graphic coverage notes, leftover-map rank, leftover observed `Y`, plus the strip leftover expected badge. +Rank-0 origin cells still name `E 0.00` when that persisted leftover +expected is finite. A finite negative leftover is shown, never clamped. Do not invent `E` from +`Y` and `R`. Click a post marker to open that post. + +Leftover expected omits independently of leftover observed captions and leftover residual captions. +A missing leftover expected omits leftover-map comparison graphic leftover expected and keeps +a usable leftover observed caption and a usable leftover residual caption. + +This increment does not caption leftover-map rank on the comparison graphic +with a distinct comparison-graphic name. That leftover-map rank already sits on +the strip through ADR 0301. + +Do not add SQL migrations. Do not edit shipped migrations. Do not invent a leftover +score. Do not invent a theta. + +## Consequences + +After `make seed`, grouping comparison leftover pairs that already show +the leftover-map graphic of persisted `ξ` / `ζ` also name leftover +expected on that graphic when formatLeftoverMapExpected +returns a usable badge. Rank-0 unused axes still plot at +the origin and still name `E 0.00` when that persisted leftover +expected is finite. Click a post marker or a pair button opens that post. When `Y`, `E`, and `R` are finite, `Y − E = R`. + +## Related + +Independent of leftover-map rank on the grouping comparison leftover-map graphic. + +## References + +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 + +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 diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index cba1004cf..2d63b7f92 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,21 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-08-31 09:05 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not +> self-approve. #856 leftover observed on the grouping comparison leftover-map +> graphic (v2.73.0 / ADR 0316) is `c7a8680`, stacked on #855 @ `1087b16`. +> Independent APPROVE is missing. Next buyer increment on this +> cycle: leftover expected on the grouping comparison leftover-map +> graphic (ADR 0317 / v2.74.0) delivered locally on +> `feat/leftover-map-compare-plot-expected-v2740`. Caption persisted leftover +> expected `E` on that comparison graphic from already-named +> expected_response. Distinct accessible name `leftover map comparison graphic +> leftover expected {label}`. Omit when formatLeftoverMapExpected fail-closes. +> Do not caption leftover-map rank on that comparison graphic with a distinct name this +> increment. Do not invent `E` from `Y` and `R`. Stack onto #856. Leave open for +> independent review. Do not squash-merge. Do not self-approve. Issues #79 and #87 +> stay open. Do not merge #808 without independent APPROVE. Never self-approve. + > Exact-head loop overlay: 2026-08-31 08:50 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not > self-approve. #855 leftover residual on the grouping comparison leftover-map diff --git a/frontend/package.json b/frontend/package.json index 64af26a98..b1cd177f4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.73.0", + "version": "2.74.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 7e950b3f4..544f89488 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -4614,10 +4614,15 @@ describe("App, authenticated", () => { ), ).toHaveLength(1); expect( - within(screen.getByLabelText("Grouping comparison")).queryByLabelText( - /leftover expected/, + within(screen.getByLabelText("Grouping comparison")).getByLabelText( + "leftover map comparison graphic leftover expected E 2.00", ), ).toHaveTextContent("E 2.00"); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + "leftover expected E 2.00", + ), + ).not.toBeInTheDocument(); expect( within(screen.getByLabelText("Grouping comparison")).getByLabelText( "Leftover map comparison expected", diff --git a/frontend/src/components/LeftoverMapPlot.test.tsx b/frontend/src/components/LeftoverMapPlot.test.tsx index 4805aa9e8..93e9da40f 100644 --- a/frontend/src/components/LeftoverMapPlot.test.tsx +++ b/frontend/src/components/LeftoverMapPlot.test.tsx @@ -2165,4 +2165,127 @@ describe("LeftoverMapPlot", () => { ); expect(screen.getByText("leftover map comparison axis 1 (0%)")).toBeInTheDocument(); }); + + it("names leftover expected on the comparison graphic with a distinct accessible name", () => { + render( + , + ); + expect( + screen.getAllByLabelText("leftover map comparison graphic leftover expected E 2.00"), + ).toHaveLength(2); + expect(screen.queryByLabelText("leftover expected E 2.00")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("Leftover map comparison expected")).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 2.40"), + ).toHaveTextContent("Y 2.40"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover residual R +0.40"), + ).toHaveTextContent("R +0.40"); + expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); + }); + + it("omits leftover-map comparison graphic leftover expected when E is missing or not usable", () => { + render( + , + ); + expect(screen.queryByLabelText(/leftover map comparison graphic leftover expected E/)).not.toBeInTheDocument(); + expect(screen.queryByLabelText(/^leftover expected E/)).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 2.40"), + ).toHaveTextContent("Y 2.40"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover residual R +0.40"), + ).toHaveTextContent("R +0.40"); + expect(screen.getByLabelText("leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); + }); + + it("names rank-0 origin leftover expected on the comparison graphic when that persisted value is finite", () => { + render( + , + ); + expect( + screen.getByLabelText("leftover map comparison graphic leftover expected E 0.00"), + ).toHaveTextContent("E 0.00"); + expect(screen.queryByLabelText("leftover expected E 0.00")).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 0.00"), + ).toHaveTextContent("Y 0.00"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover residual R 0.00"), + ).toHaveTextContent("R 0.00"); + expect(screen.getByText("leftover map comparison axis 1 (0%)")).toBeInTheDocument(); + }); }); diff --git a/frontend/src/components/LeftoverMapPlot.tsx b/frontend/src/components/LeftoverMapPlot.tsx index 5e52e7f49..06c412357 100644 --- a/frontend/src/components/LeftoverMapPlot.tsx +++ b/frontend/src/components/LeftoverMapPlot.tsx @@ -40,6 +40,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED, LEFTOVER_MAP_PLOT_CAPTION, LEFTOVER_MAP_PLOT_POST_ACTION, LEFTOVER_MAP_PLOT_SEGMENT_CROSS_SHARE, @@ -165,6 +166,9 @@ function leftoverMapPlotAxisText( * labels. ADR 0316 captions leftover observed on that * comparison graphic from already-named leftover observed * with distinct leftover map comparison graphic leftover observed + * labels. ADR 0317 captions leftover expected on that + * comparison graphic from already-named leftover expected + * with distinct leftover map comparison graphic leftover expected * labels. * Never invent a leftover score. */ @@ -446,9 +450,12 @@ export function LeftoverMapPlot({ x={segment.expectedX} y={segment.expectedY} textAnchor="middle" - aria-label={tf(LEFTOVER_MAP_PLOT_SEGMENT_EXPECTED, { - label: segment.expectedLabel, - })} + aria-label={tf( + variant === "comparison" + ? LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED + : LEFTOVER_MAP_PLOT_SEGMENT_EXPECTED, + { label: segment.expectedLabel }, + )} > {segment.expectedLabel} diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index 6779c9dd6..8b16ec488 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -125,7 +125,8 @@ export type LeftoverPairListProps = { * grouping comparison leftover-map graphic, not this pair list. ADR 0315 * captions leftover residual on that grouping comparison leftover-map graphic, * not this pair list. ADR 0316 captions leftover observed on that grouping - * comparison leftover-map graphic, not this pair list. Every badge still + * comparison leftover-map graphic, not this pair list. ADR 0317 captions leftover + * expected on that grouping comparison leftover-map graphic, 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 8f7b69eb4..4edf38797 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -99,6 +99,7 @@ describe("i18n", () => { "Leftover map comparison residual", "leftover map comparison graphic leftover observed {label}", "Leftover map comparison observed", + "leftover map comparison graphic leftover expected {label}", "Leftover map comparison expected", "Leftover map comparison rank", "Leftover map comparison coordinates", @@ -615,6 +616,23 @@ describe("i18n", () => { }, ); + it.each([ + ["ko", "잔여 지도 비교 그림 기대 E 2.00"], + ["zh", "残差地图比较图形期望 E 2.00"], + ["ja", "残差マップの比較図期待 E 2.00"], + ["vi", "kỳ vọng đồ họa so sánh bản đồ phần dư E 2.00"], + ] as const)( + "formats leftover map comparison graphic leftover expected in %s", + (locale, expected) => { + setLocale(locale); + expect( + tf("leftover map comparison graphic leftover expected {label}", { + label: "E 2.00", + }), + ).toBe(expected); + }, + ); + it.each([ ["ko", "잔여 지도 비교 설명 잔여 점유율"], ["zh", "残差地图比较已解释残差份额"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 0c32ac23c..e793e90dc 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -291,6 +291,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison residual": "잔여 지도 비교 잔여", "leftover map comparison graphic leftover observed {label}": "잔여 지도 비교 그림 관측 {label}", "Leftover map comparison observed": "잔여 지도 비교 관측", + "leftover map comparison graphic leftover expected {label}": "잔여 지도 비교 그림 기대 {label}", "Leftover map comparison expected": "잔여 지도 비교 기대", "Leftover map comparison rank": "잔여 지도 비교 순위", "Leftover map comparison coordinates": "잔여 지도 비교 좌표", @@ -930,6 +931,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison residual": "残差地图比较残差", "leftover map comparison graphic leftover observed {label}": "残差地图比较图形观测 {label}", "Leftover map comparison observed": "残差地图比较观测", + "leftover map comparison graphic leftover expected {label}": "残差地图比较图形期望 {label}", "Leftover map comparison expected": "残差地图比较期望", "Leftover map comparison rank": "残差地图比较秩", "Leftover map comparison coordinates": "残差地图比较坐标", @@ -1590,6 +1592,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison residual": "残差マップの比較残差", "leftover map comparison graphic leftover observed {label}": "残差マップの比較図観測 {label}", "Leftover map comparison observed": "残差マップの比較観測", + "leftover map comparison graphic leftover expected {label}": "残差マップの比較図期待 {label}", "Leftover map comparison expected": "残差マップの比較期待", "Leftover map comparison rank": "残差マップの比較階数", "Leftover map comparison coordinates": "残差マップの比較座標", @@ -2229,6 +2232,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison residual": "Phần dư so sánh bản đồ phần dư", "leftover map comparison graphic leftover observed {label}": "quan sát đồ họa so sánh bản đồ phần dư {label}", "Leftover map comparison observed": "Quan sát so sánh bản đồ phần dư", + "leftover map comparison graphic leftover expected {label}": "kỳ vọng đồ họa so sánh bản đồ phần dư {label}", "Leftover map comparison expected": "Kỳ vọng so sánh bản đồ phần dư", "Leftover map comparison rank": "Hạng so sánh bản đồ phần dư", "Leftover map comparison coordinates": "Tọa độ so sánh bản đồ phần dư", diff --git a/frontend/src/leftoverMapPlotLayout.test.ts b/frontend/src/leftoverMapPlotLayout.test.ts index a86e909a9..0f2704da5 100644 --- a/frontend/src/leftoverMapPlotLayout.test.ts +++ b/frontend/src/leftoverMapPlotLayout.test.ts @@ -14,6 +14,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED, LEFTOVER_MAP_COMPARE_PLOT_SVG, PLOT_HEIGHT, PLOT_PADDING, @@ -1304,4 +1305,19 @@ describe("leftover map comparison graphic labels", () => { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, ); }); + + it("stays distinct from leftover expected copy", () => { + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED).toBe( + "leftover map comparison graphic leftover expected {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED).not.toBe( + "leftover expected {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED).not.toBe( + "Leftover map comparison expected", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED).not.toBe( + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, + ); + }); }); diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index e22b53ca4..582081aa7 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -44,7 +44,8 @@ * cross share on that graphic (ADR 0313), and grouping comparison leftover-map * unexplained leftover on that graphic (ADR 0314), and grouping comparison leftover * residual on that graphic (ADR 0315), and grouping comparison leftover - * observed on that graphic (ADR 0316) caption the pair list or the grouping comparison + * observed on that graphic (ADR 0316), and grouping comparison leftover + * expected on that graphic (ADR 0317) caption the pair list or the grouping comparison * strip. ADR 0304 reuses this graphic layout on the grouping comparison * strip. ADR 0305 captions leftover-map axis share on that comparison * graphic from already-named leftover-map axes. ADR 0306 captions leftover-map @@ -66,6 +67,8 @@ * already-named leftover residual. * ADR 0316 captions leftover observed on that comparison graphic from * already-named leftover observed. + * ADR 0317 captions leftover expected on that comparison graphic from + * already-named leftover expected. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; @@ -139,6 +142,8 @@ export { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED } from "./leftoverObservedEx export const LEFTOVER_MAP_PLOT_SEGMENT_EXPECTED = "leftover expected {label}"; +export { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED } from "./leftoverObservedExpected"; + export const LEFTOVER_MAP_PLOT_SEGMENT_RANK = "leftover-map rank {label}"; diff --git a/frontend/src/leftoverObservedExpected.test.ts b/frontend/src/leftoverObservedExpected.test.ts index c38c454c8..f58e0a6d8 100644 --- a/frontend/src/leftoverObservedExpected.test.ts +++ b/frontend/src/leftoverObservedExpected.test.ts @@ -6,6 +6,7 @@ import { LEFTOVER_MAP_COMPARE_OBSERVED_LABEL, LEFTOVER_MAP_COMPARE_EXPECTED_LABEL, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED, } from "./leftoverObservedExpected"; describe("formatLeftoverObservedExpected", () => { @@ -76,4 +77,17 @@ describe("formatLeftoverMapExpected", () => { expect(LEFTOVER_MAP_COMPARE_EXPECTED_LABEL).toBe("Leftover map comparison expected"); expect(LEFTOVER_MAP_COMPARE_EXPECTED_LABEL).not.toBe("leftover expected {label}"); }); + + it("keeps the grouping comparison graphic leftover expected label distinct from the graphic and strip labels", () => { + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED).toBe( + "leftover map comparison graphic leftover expected {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED).not.toBe("leftover expected {label}"); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED).not.toBe( + LEFTOVER_MAP_COMPARE_EXPECTED_LABEL, + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED).not.toBe( + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, + ); + }); }); diff --git a/frontend/src/leftoverObservedExpected.ts b/frontend/src/leftoverObservedExpected.ts index 5d77b7aea..d9751978a 100644 --- a/frontend/src/leftoverObservedExpected.ts +++ b/frontend/src/leftoverObservedExpected.ts @@ -9,6 +9,9 @@ export const LEFTOVER_MAP_COMPARE_EXPECTED_LABEL = export const LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED = "leftover map comparison graphic leftover observed {label}"; +export const LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED = + "leftover map comparison graphic leftover expected {label}"; + export function formatLeftoverObservedExpected( observed: number | null | undefined, expected: number | null | undefined, diff --git a/pyproject.toml b/pyproject.toml index e18874584..e20074c95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.73.0" +version = "2.74.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 8226b9653..954ed676f 100644 --- a/uv.lock +++ b/uv.lock @@ -685,7 +685,7 @@ wheels = [ [[package]] name = "lineageweave" -version = "2.73.0" +version = "2.74.0" source = { editable = "." } dependencies = [ { name = "certifi" }, From 4b9184fd83f6749e25384d7bcf5d9d7721437b77 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 31 Aug 2026 00:10:25 +0000 Subject: [PATCH 3/3] feat(reports): leftover-map rank on grouping comparison graphic (v2.75.0) Caption persisted leftover-map rank on the grouping comparison leftover-map graphic from already-named leftover_map_rank (ADR 0318). Comparison copy uses leftover map comparison graphic leftover-map rank {label} so it stays distinct from leftover-map rank {label} and from strip Leftover map comparison rank. A missing, negative, or non-integer rank omits that caption and keeps leftover expected, leftover observed, leftover residual, leftover-map unexplained leftover, leftover-map cross share, leftover-map reconstruction, leftover-map distance, and comparison graphic coverage notes. Rank-0 origin cells still name rank 0 when that leftover-map rank is a non-negative integer. Do not caption leftover-map distance this increment. Do not invent leftover-map rank from plotted coordinates or leftover-map distance. Never invent a leftover score. Never invent a theta. No new columns. --- AGENTS.md | 12 ++ ARCHITECTURE.md | 6 +- .../2.75.0-leftover-map-compare-plot-rank.md | 16 +++ CHANGELOG.md | 16 +++ CLAUDE.md | 2 +- ...0317-leftover-map-compare-plot-expected.md | 8 +- .../0318-leftover-map-compare-plot-rank.md | 109 +++++++++++++++ docs/product-technical-gap-baseline.md | 17 +++ frontend/package.json | 2 +- frontend/src/App.test.tsx | 9 +- .../src/components/LeftoverMapPlot.test.tsx | 132 ++++++++++++++++++ frontend/src/components/LeftoverMapPlot.tsx | 13 +- frontend/src/components/LeftoverPairList.tsx | 4 +- frontend/src/i18n.test.ts | 18 +++ frontend/src/i18n.ts | 4 + frontend/src/leftoverMapPlotLayout.test.ts | 16 +++ frontend/src/leftoverMapPlotLayout.ts | 7 +- frontend/src/leftoverMapRank.test.ts | 19 ++- frontend/src/leftoverMapRank.ts | 3 + pyproject.toml | 2 +- uv.lock | 2 +- 21 files changed, 402 insertions(+), 15 deletions(-) create mode 100644 CHANGELOG.d/2.75.0-leftover-map-compare-plot-rank.md create mode 100644 docs/adr/0318-leftover-map-compare-plot-rank.md diff --git a/AGENTS.md b/AGENTS.md index aeebe60ea..d571f5d26 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -611,6 +611,15 @@ and leftover-map comparison graphic incomplete items when those helpers return u leftover-map rank, leftover-map axis share, leftover-map post coverage, leftover-map item coverage, leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover pair count, or the count of unused axes. +ADR 0318 captions leftover-map rank on that grouping comparison leftover-map +graphic when formatLeftoverMapRank returns a usable badge; a missing, +negative, or non-integer rank omits that leftover-map comparison graphic leftover-map rank caption and keeps +leftover expected `E`, leftover observed `Y`, leftover residual `R`, leftover-map unexplained leftover `U`, leftover-map cross share `x`, leftover-map unexplained leftover share `s`, leftover-map explained leftover share `e`, leftover-map reconstruction `R̂`, leftover-map distance `d`, leftover-map comparison graphic coverage, +leftover-map comparison graphic item coverage, leftover-map comparison graphic incomplete posts, +and leftover-map comparison graphic incomplete items when those helpers return usable integers. Do not invent +leftover-map rank from plotted coordinates, leftover-map distance, leftover expected, leftover observed, leftover residual, leftover-map unexplained leftover, leftover-map reconstruction, leftover-map axis share, leftover-map post coverage, leftover-map item coverage, +leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover pair +count, or the count of unused axes. 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 @@ -697,6 +706,9 @@ that grouping comparison leftover-map graphic with persisted leftover observed Grouping comparison leftover expected (ADR 0317) captions that grouping comparison leftover-map graphic with persisted leftover expected `E` when formatLeftoverMapExpected returns a usable badge. +Grouping comparison leftover-map rank (ADR 0318) captions +that grouping comparison leftover-map graphic with persisted leftover-map rank +when formatLeftoverMapRank returns a usable badge. 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 1df385c57..a800e9bd2 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -740,6 +740,9 @@ badge. ADR 0317 captions leftover expected on that grouping comparison leftover-map graphic when formatLeftoverMapExpected returns a usable badge. +ADR 0318 captions leftover-map rank on that grouping +comparison leftover-map graphic when formatLeftoverMapRank returns a usable +badge. 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 @@ -786,7 +789,8 @@ graphic (ADR 0313), leftover-map unexplained leftover on the grouping comparison graphic (ADR 0314), leftover residual on the grouping comparison leftover-map graphic (ADR 0315), leftover observed on the grouping comparison leftover-map graphic (ADR 0316), leftover expected on the grouping comparison leftover-map -graphic (ADR 0317); +graphic (ADR 0317), leftover-map rank on the grouping comparison leftover-map +graphic (ADR 0318); `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.75.0-leftover-map-compare-plot-rank.md b/CHANGELOG.d/2.75.0-leftover-map-compare-plot-rank.md new file mode 100644 index 000000000..b719216dc --- /dev/null +++ b/CHANGELOG.d/2.75.0-leftover-map-compare-plot-rank.md @@ -0,0 +1,16 @@ +## 2.75.0 — Leftover-map rank on grouping comparison leftover-map graphic + +- Caption leftover-map rank on the grouping comparison leftover-map + graphic (ADR 0318). After `make seed`, closest and farthest leftover pairs + sit above the member list with leftover-map rank on that grouping + comparison leftover-map graphic when formatLeftoverMapRank returns a usable + badge; click a post marker or a pair button opens that post. A missing, + negative, or non-integer rank omits that leftover-map comparison graphic leftover-map + rank caption and keeps leftover expected `E` and leftover observed `Y`. Rank-0 origin + cells still name `rank 0` when that persisted leftover-map rank is a non-negative integer. + Comparison copy uses `leftover map comparison graphic leftover-map rank {label}` + so it stays distinct from `leftover-map rank {label}` and from strip + `Leftover map comparison rank`. This increment does not caption leftover-map + distance on the comparison graphic with a distinct name. Do not invent leftover-map + rank from plotted coordinates or leftover-map distance. Never invent a leftover score. + Never invent a theta. No new columns. diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f2f1753a..50d1ad9c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,22 @@ All notable changes to this project are documented here. Format follows ### Added +- Period leftover pairs now name leftover-map rank on the grouping + comparison leftover-map graphic (ADR 0318 / v2.75.0). After `make seed`, + closest and farthest leftover pairs sit above the member list with leftover-map + rank on that grouping comparison leftover-map graphic when + formatLeftoverMapRank returns a usable badge; click a post marker or a pair + button opens that post. A missing, negative, or non-integer rank omits that leftover-map + comparison graphic leftover-map rank caption and keeps leftover expected `E` + and leftover observed `Y`. Rank-0 origin cells still name `rank 0` when that + persisted leftover-map rank is a non-negative integer. Comparison copy uses + `leftover map comparison graphic leftover-map rank {label}` so it stays + distinct from `leftover-map rank {label}` and from strip + `Leftover map comparison rank`. This increment does not caption leftover-map + distance on the comparison graphic with a distinct name. Do not invent leftover-map + rank from plotted coordinates or leftover-map distance. Never invent a leftover score + or a theta. No new columns. + - Period leftover pairs now name leftover expected on the grouping comparison leftover-map graphic (ADR 0317 / v2.74.0). After `make seed`, closest and farthest leftover pairs sit above the member list with leftover diff --git a/CLAUDE.md b/CLAUDE.md index 8362530c8..7a7e3b8c3 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 / 0297 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315 / 0316 / 0317), 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 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315 / 0316 / 0317 / 0318), 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/0317-leftover-map-compare-plot-expected.md b/docs/adr/0317-leftover-map-compare-plot-expected.md index 19da44c0f..10cbe587a 100644 --- a/docs/adr/0317-leftover-map-compare-plot-expected.md +++ b/docs/adr/0317-leftover-map-compare-plot-expected.md @@ -3,6 +3,9 @@ **Decision status:** Accepted **Date:** 2026-08-31 +**Amended by:** [ADR 0318](0318-leftover-map-compare-plot-rank.md) +(leftover-map rank on the grouping comparison leftover-map graphic) + Amends leftover expected on graphic-display pair segments ([ADR 0279](0279-leftover-map-segment-expected.md)), leftover expected on grouping comparison strip pair rows @@ -69,8 +72,9 @@ Leftover expected omits independently of leftover observed captions and leftover A missing leftover expected omits leftover-map comparison graphic leftover expected and keeps a usable leftover observed caption and a usable leftover residual caption. -This increment does not caption leftover-map rank on the comparison graphic -with a distinct comparison-graphic name. That leftover-map rank already sits on +This increment does not caption leftover-map distance on the comparison graphic +with a distinct comparison-graphic name. Leftover-map rank on that comparison +graphic is [ADR 0318](0318-leftover-map-compare-plot-rank.md). That leftover-map rank already sits on the strip through ADR 0301. Do not add SQL migrations. Do not edit shipped migrations. Do not invent a leftover diff --git a/docs/adr/0318-leftover-map-compare-plot-rank.md b/docs/adr/0318-leftover-map-compare-plot-rank.md new file mode 100644 index 000000000..7120ba08c --- /dev/null +++ b/docs/adr/0318-leftover-map-compare-plot-rank.md @@ -0,0 +1,109 @@ +# ADR 0318 — Name leftover-map rank on the grouping comparison leftover-map graphic + +**Decision status:** Accepted +**Date:** 2026-08-31 + +Amends leftover-map rank on graphic-display pair segments +([ADR 0280](0280-leftover-map-segment-rank.md)), leftover-map rank +on grouping comparison strip pair rows +([ADR 0301](0301-leftover-map-compare-rank.md)), leftover expected +on the grouping comparison leftover-map graphic +([ADR 0317](0317-leftover-map-compare-plot-expected.md)), leftover observed +on the grouping comparison leftover-map graphic +([ADR 0316](0316-leftover-map-compare-plot-observed.md)), leftover residual +on the grouping comparison leftover-map graphic +([ADR 0315](0315-leftover-map-compare-plot-residual.md)), leftover-map rank +([ADR 0164](0164-leftover-map-rank.md)), leftover-map graphic +display on the grouping comparison strip +([ADR 0304](0304-leftover-map-compare-graphic.md)). Independent of leftover-map +distance on the grouping comparison leftover-map graphic. + +## Context + +ADR 0164 already persists leftover-map rank — the number of Gabriel +singular values above the leftover singular floor. A rank-0 residual still +emits a stable closest/farthest pair so `make seed` is not empty; the stored +distance is then zero, not a fabricated interaction. ADR 0280 already captions +period-report leftover-map pair segments with `leftover-map rank {label}` when +that leftover-map rank is a non-negative integer. ADR 0301 already captions +grouping comparison leftover-pair buttons with that same persisted leftover-map +rank under accessible name `Leftover map comparison rank`. ADR 0317 already +captions leftover expected on the grouping comparison leftover-map graphic. +The comparison graphic still reuses `leftover-map rank {label}`, so a buyer who +compares leftover pairs can treat the period-report graphic caption as the +comparison graphic leftover-map rank even after the strip names `rank n`. +Hiding a distinct comparison-graphic leftover-map rank caption lets leftover +expected `E` or leftover-map distance `d` be read as leftover-map rank without +a next action. The comparison graphic must name the same persisted rank the +pair row and strip already show. Rank-0 origin cells still name `rank 0` when +that persisted leftover-map rank is a non-negative integer. + +This increment captions leftover-map rank on the grouping comparison leftover-map +graphic from already-named leftover-map rank through formatLeftoverMapRank. +Comparison copy uses the accessible name +`leftover map comparison graphic leftover-map rank {label}` so it stays +distinct from `leftover-map rank {label}` on the period-report graphic and from +strip `Leftover map comparison rank`. It does not add columns. It does not +recompute leftover-map rank from plotted coordinates, leftover-map distance, or +the count of unused axes. Do not invent a leftover score. Do not invent a theta. + +This protected increment uses **0318** so it does not collide with leftover +expected on the grouping comparison leftover-map graphic (0317), leftover observed +on that graphic (0316), leftover residual on that graphic (0315), leftover-map +rank on grouping comparison strip pair rows (0301), leftover-map rank on pair +segments (0280), leftover-map rank persistence (0164), leftover-map graphic +display (0268), or the dashboard stacks. + +## Decision + +On the grouping comparison leftover-map graphic, caption each pair segment +with persisted leftover-map rank when formatLeftoverMapRank returns a usable +badge, next to leftover expected `E`. Use the distinct accessible name +`leftover map comparison graphic leftover-map rank {label}` so the graphic +caption is not the strip badge (`Leftover map comparison rank`) and is not the +period-report graphic caption (`leftover-map rank {label}`). A missing rank, a +negative rank, or a non-integer rank omits that leftover-map comparison graphic +leftover-map rank caption and keeps leftover expected `E` when +formatLeftoverMapExpected returns a usable badge, leftover observed `Y` when +formatLeftoverMapObserved returns a usable badge, leftover residual `R` when +formatLeftoverMapResidual returns a usable badge, leftover-map unexplained leftover `U`, leftover-map cross share `x`, leftover-map unexplained leftover share `s`, leftover-map explained leftover share `e`, leftover-map reconstruction `R̂`, leftover-map distance `d`, leftover-map comparison graphic coverage notes, leftover expected `E`, leftover observed `Y`, plus the strip leftover-map rank badge. +Rank-0 origin cells still name `rank 0` when that persisted leftover-map rank is +a non-negative integer. Do not invent leftover-map rank from plotted coordinates, +leftover-map distance, leftover expected, leftover observed, leftover residual, leftover-map reconstruction, leftover-map unexplained leftover, leftover-map axis share, leftover-map post coverage, leftover-map item coverage, leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover pair count, or the count of unused axes. Click a post marker to open that post. + +Leftover-map rank omits independently of leftover expected captions and leftover observed captions. +A missing leftover-map rank omits leftover-map comparison graphic leftover-map rank and keeps +a usable leftover expected caption and a usable leftover observed caption. + +This increment does not caption leftover-map distance on the comparison graphic +with a distinct comparison-graphic name. That leftover-map distance already sits +on the period-report graphic through ADR 0271. + +Do not add SQL migrations. Do not edit shipped migrations. Do not invent a leftover +score. Do not invent a theta. + +## Consequences + +After `make seed`, grouping comparison leftover pairs that already show +the leftover-map graphic of persisted `ξ` / `ζ` also name leftover-map +rank on that graphic when formatLeftoverMapRank returns a usable badge. +Rank-0 unused axes still plot at the origin and still name `rank 0` when that +persisted leftover-map rank is a non-negative integer. Click a post marker or a +pair button opens that post. When `Y`, `E`, and `R` are finite, `Y − E = R`. +When `R`, `R̂`, and `U` are finite, `U + R̂ = R`. When coordinates, +reconstruction, and distance are finite, `R̂ = ξ · ζ` and `d = ‖ξ − ζ‖`. + +## Related + +Independent of leftover-map distance on the grouping comparison leftover-map graphic. + +## References + +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 + +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 diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 2d63b7f92..14a20d511 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,22 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-08-31 09:19 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not +> self-approve. #857 leftover expected on the grouping comparison leftover-map +> graphic (v2.74.0 / ADR 0317) is `9f34d2c`, stacked on #856. Independent +> APPROVE is missing. Next buyer increment on this +> cycle: leftover-map rank on the grouping comparison leftover-map +> graphic (ADR 0318 / v2.75.0) delivered locally on +> `feat/leftover-map-compare-plot-rank-v2750`. Caption persisted leftover-map +> rank on that comparison graphic from already-named leftover_map_rank. +> Distinct accessible name `leftover map comparison graphic leftover-map +> rank {label}`. Omit when formatLeftoverMapRank fail-closes. +> Do not caption leftover-map distance on that comparison graphic with a distinct name this +> increment. Do not invent leftover-map rank from plotted coordinates or leftover-map +> distance. Stack onto #857. Leave open for independent review. Do not squash-merge. Do not +> self-approve. Issues #79 and #87 stay open. Do not merge #808 without independent APPROVE. +> Never self-approve. + > Exact-head loop overlay: 2026-08-31 09:05 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not > self-approve. #856 leftover observed on the grouping comparison leftover-map diff --git a/frontend/package.json b/frontend/package.json index b1cd177f4..bf600eb93 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.74.0", + "version": "2.75.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 544f89488..3efd4e53a 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -4634,10 +4634,15 @@ describe("App, authenticated", () => { ), ).toHaveLength(1); expect( - within(screen.getByLabelText("Grouping comparison")).queryByLabelText( - /leftover-map rank/, + within(screen.getByLabelText("Grouping comparison")).getByLabelText( + "leftover map comparison graphic leftover-map rank rank 1", ), ).toHaveTextContent("rank 1"); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + "leftover-map rank rank 1", + ), + ).not.toBeInTheDocument(); expect( within(screen.getByLabelText("Grouping comparison")).getByLabelText( "Leftover map comparison rank", diff --git a/frontend/src/components/LeftoverMapPlot.test.tsx b/frontend/src/components/LeftoverMapPlot.test.tsx index 93e9da40f..a86a35c39 100644 --- a/frontend/src/components/LeftoverMapPlot.test.tsx +++ b/frontend/src/components/LeftoverMapPlot.test.tsx @@ -2288,4 +2288,136 @@ describe("LeftoverMapPlot", () => { ).toHaveTextContent("R 0.00"); expect(screen.getByText("leftover map comparison axis 1 (0%)")).toBeInTheDocument(); }); + + it("names leftover-map rank on the comparison graphic with a distinct accessible name", () => { + render( + , + ); + expect( + screen.getAllByLabelText("leftover map comparison graphic leftover-map rank rank 1"), + ).toHaveLength(2); + expect(screen.queryByLabelText("leftover-map rank rank 1")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("Leftover map comparison rank")).not.toBeInTheDocument(); + expect( + screen.getAllByLabelText("leftover map comparison graphic leftover expected E 2.00"), + ).toHaveLength(2); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 2.40"), + ).toHaveTextContent("Y 2.40"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover residual R +0.40"), + ).toHaveTextContent("R +0.40"); + expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); + }); + + it("omits leftover-map comparison graphic leftover-map rank when rank is missing or not usable", () => { + render( + , + ); + expect(screen.queryByLabelText(/leftover map comparison graphic leftover-map rank/)).not.toBeInTheDocument(); + expect(screen.queryByLabelText(/^leftover-map rank/)).not.toBeInTheDocument(); + expect( + screen.getAllByLabelText("leftover map comparison graphic leftover expected E 2.00"), + ).toHaveLength(2); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 2.40"), + ).toHaveTextContent("Y 2.40"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover residual R +0.40"), + ).toHaveTextContent("R +0.40"); + expect(screen.getByLabelText("leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); + }); + + it("names rank-0 origin leftover-map rank on the comparison graphic when that persisted value is a non-negative integer", () => { + render( + , + ); + expect( + screen.getByLabelText("leftover map comparison graphic leftover-map rank rank 0"), + ).toHaveTextContent("rank 0"); + expect(screen.queryByLabelText("leftover-map rank rank 0")).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic leftover expected E 0.00"), + ).toHaveTextContent("E 0.00"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 0.00"), + ).toHaveTextContent("Y 0.00"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover residual R 0.00"), + ).toHaveTextContent("R 0.00"); + expect(screen.getByText("leftover map comparison axis 1 (0%)")).toBeInTheDocument(); + }); }); diff --git a/frontend/src/components/LeftoverMapPlot.tsx b/frontend/src/components/LeftoverMapPlot.tsx index 06c412357..a2ee23e3f 100644 --- a/frontend/src/components/LeftoverMapPlot.tsx +++ b/frontend/src/components/LeftoverMapPlot.tsx @@ -41,6 +41,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK, LEFTOVER_MAP_PLOT_CAPTION, LEFTOVER_MAP_PLOT_POST_ACTION, LEFTOVER_MAP_PLOT_SEGMENT_CROSS_SHARE, @@ -169,6 +170,9 @@ function leftoverMapPlotAxisText( * labels. ADR 0317 captions leftover expected on that * comparison graphic from already-named leftover expected * with distinct leftover map comparison graphic leftover expected + * labels. ADR 0318 captions leftover-map rank on that + * comparison graphic from already-named leftover-map rank + * with distinct leftover map comparison graphic leftover-map rank * labels. * Never invent a leftover score. */ @@ -466,9 +470,12 @@ export function LeftoverMapPlot({ x={segment.rankX} y={segment.rankY} textAnchor="middle" - aria-label={tf(LEFTOVER_MAP_PLOT_SEGMENT_RANK, { - label: segment.rankLabel, - })} + aria-label={tf( + variant === "comparison" + ? LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK + : LEFTOVER_MAP_PLOT_SEGMENT_RANK, + { label: segment.rankLabel }, + )} > {segment.rankLabel} diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index 8b16ec488..9ec439bff 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -126,7 +126,9 @@ export type LeftoverPairListProps = { * captions leftover residual on that grouping comparison leftover-map graphic, * not this pair list. ADR 0316 captions leftover observed on that grouping * comparison leftover-map graphic, not this pair list. ADR 0317 captions leftover - * expected on that grouping comparison leftover-map graphic, not this pair list. Every badge still + * expected on that grouping comparison leftover-map graphic, not this pair list. ADR 0318 + * captions leftover-map rank on that grouping comparison leftover-map graphic, 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 4edf38797..19c2964d9 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -101,6 +101,7 @@ describe("i18n", () => { "Leftover map comparison observed", "leftover map comparison graphic leftover expected {label}", "Leftover map comparison expected", + "leftover map comparison graphic leftover-map rank {label}", "Leftover map comparison rank", "Leftover map comparison coordinates", "Leftover map comparison graphic", @@ -633,6 +634,23 @@ describe("i18n", () => { }, ); + it.each([ + ["ko", "잔여 지도 비교 그림 순위 rank 1"], + ["zh", "残差地图比较图形秩 rank 1"], + ["ja", "残差マップの比較図階数 rank 1"], + ["vi", "hạng đồ họa so sánh bản đồ phần dư rank 1"], + ] as const)( + "formats leftover map comparison graphic leftover-map rank in %s", + (locale, expected) => { + setLocale(locale); + expect( + tf("leftover map comparison graphic leftover-map rank {label}", { + label: "rank 1", + }), + ).toBe(expected); + }, + ); + it.each([ ["ko", "잔여 지도 비교 설명 잔여 점유율"], ["zh", "残差地图比较已解释残差份额"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index e793e90dc..1c7b9525d 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -293,6 +293,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison observed": "잔여 지도 비교 관측", "leftover map comparison graphic leftover expected {label}": "잔여 지도 비교 그림 기대 {label}", "Leftover map comparison expected": "잔여 지도 비교 기대", + "leftover map comparison graphic leftover-map rank {label}": "잔여 지도 비교 그림 순위 {label}", "Leftover map comparison rank": "잔여 지도 비교 순위", "Leftover map comparison coordinates": "잔여 지도 비교 좌표", "Leftover map comparison graphic": "잔여 지도 비교 그림", @@ -933,6 +934,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison observed": "残差地图比较观测", "leftover map comparison graphic leftover expected {label}": "残差地图比较图形期望 {label}", "Leftover map comparison expected": "残差地图比较期望", + "leftover map comparison graphic leftover-map rank {label}": "残差地图比较图形秩 {label}", "Leftover map comparison rank": "残差地图比较秩", "Leftover map comparison coordinates": "残差地图比较坐标", "Leftover map comparison graphic": "残差地图比较图形", @@ -1594,6 +1596,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison observed": "残差マップの比較観測", "leftover map comparison graphic leftover expected {label}": "残差マップの比較図期待 {label}", "Leftover map comparison expected": "残差マップの比較期待", + "leftover map comparison graphic leftover-map rank {label}": "残差マップの比較図階数 {label}", "Leftover map comparison rank": "残差マップの比較階数", "Leftover map comparison coordinates": "残差マップの比較座標", "Leftover map comparison graphic": "残差マップの比較図", @@ -2234,6 +2237,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison observed": "Quan sát so sánh bản đồ phần dư", "leftover map comparison graphic leftover expected {label}": "kỳ vọng đồ họa so sánh bản đồ phần dư {label}", "Leftover map comparison expected": "Kỳ vọng so sánh bản đồ phần dư", + "leftover map comparison graphic leftover-map rank {label}": "hạng đồ họa so sánh bản đồ phần dư {label}", "Leftover map comparison rank": "Hạng so sánh bản đồ phần dư", "Leftover map comparison coordinates": "Tọa độ so sánh bản đồ phần dư", "Leftover map comparison graphic": "Đồ họa so sánh bản đồ phần dư", diff --git a/frontend/src/leftoverMapPlotLayout.test.ts b/frontend/src/leftoverMapPlotLayout.test.ts index 0f2704da5..958adc229 100644 --- a/frontend/src/leftoverMapPlotLayout.test.ts +++ b/frontend/src/leftoverMapPlotLayout.test.ts @@ -15,6 +15,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK, LEFTOVER_MAP_COMPARE_PLOT_SVG, PLOT_HEIGHT, PLOT_PADDING, @@ -1320,4 +1321,19 @@ describe("leftover map comparison graphic labels", () => { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, ); }); + + it("stays distinct from leftover-map rank copy", () => { + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK).toBe( + "leftover map comparison graphic leftover-map rank {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK).not.toBe( + "leftover-map rank {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK).not.toBe( + "Leftover map comparison rank", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK).not.toBe( + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED, + ); + }); }); diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index 582081aa7..1bcafbc94 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -45,7 +45,8 @@ * unexplained leftover on that graphic (ADR 0314), and grouping comparison leftover * residual on that graphic (ADR 0315), and grouping comparison leftover * observed on that graphic (ADR 0316), and grouping comparison leftover - * expected on that graphic (ADR 0317) caption the pair list or the grouping comparison + * expected on that graphic (ADR 0317), and grouping comparison leftover-map + * rank on that graphic (ADR 0318) caption the pair list or the grouping comparison * strip. ADR 0304 reuses this graphic layout on the grouping comparison * strip. ADR 0305 captions leftover-map axis share on that comparison * graphic from already-named leftover-map axes. ADR 0306 captions leftover-map @@ -69,6 +70,8 @@ * already-named leftover observed. * ADR 0317 captions leftover expected on that comparison graphic from * already-named leftover expected. + * ADR 0318 captions leftover-map rank on that comparison graphic from + * already-named leftover-map rank. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; @@ -147,6 +150,8 @@ export { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED } from "./leftoverObservedEx export const LEFTOVER_MAP_PLOT_SEGMENT_RANK = "leftover-map rank {label}"; +export { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK } from "./leftoverMapRank"; + export const PLOT_WIDTH = 480; export const PLOT_HEIGHT = 320; export const PLOT_PADDING = 40; diff --git a/frontend/src/leftoverMapRank.test.ts b/frontend/src/leftoverMapRank.test.ts index 334d24c60..3338e8090 100644 --- a/frontend/src/leftoverMapRank.test.ts +++ b/frontend/src/leftoverMapRank.test.ts @@ -1,5 +1,9 @@ import { describe, expect, it } from "vitest"; -import { formatLeftoverMapRank, LEFTOVER_MAP_COMPARE_RANK_LABEL } from "./leftoverMapRank"; +import { + formatLeftoverMapRank, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK, + LEFTOVER_MAP_COMPARE_RANK_LABEL, +} from "./leftoverMapRank"; describe("formatLeftoverMapRank", () => { it("names leftover-map rank without inventing leftover structure", () => { @@ -20,4 +24,17 @@ describe("formatLeftoverMapRank", () => { expect(LEFTOVER_MAP_COMPARE_RANK_LABEL).toBe("Leftover map comparison rank"); expect(LEFTOVER_MAP_COMPARE_RANK_LABEL).not.toBe("leftover-map rank {label}"); }); + + it("keeps the grouping comparison graphic leftover-map rank label distinct from the graphic and strip labels", () => { + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK).toBe( + "leftover map comparison graphic leftover-map rank {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK).not.toBe("leftover-map rank {label}"); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK).not.toBe( + LEFTOVER_MAP_COMPARE_RANK_LABEL, + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK).not.toBe( + "leftover map comparison graphic leftover expected {label}", + ); + }); }); diff --git a/frontend/src/leftoverMapRank.ts b/frontend/src/leftoverMapRank.ts index d2b706167..3fce5258a 100644 --- a/frontend/src/leftoverMapRank.ts +++ b/frontend/src/leftoverMapRank.ts @@ -7,6 +7,9 @@ export const LEFTOVER_RANK_STRUCTURE_ACTION = export const LEFTOVER_MAP_COMPARE_RANK_LABEL = "Leftover map comparison rank"; +export const LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK = + "leftover map comparison graphic leftover-map rank {label}"; + export function formatLeftoverMapRank(rank: number | null | undefined): string | null { if (rank == null || !Number.isInteger(rank) || rank < 0) { return null; diff --git a/pyproject.toml b/pyproject.toml index e20074c95..9d6139217 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.74.0" +version = "2.75.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 954ed676f..39b7853f1 100644 --- a/uv.lock +++ b/uv.lock @@ -685,7 +685,7 @@ wheels = [ [[package]] name = "lineageweave" -version = "2.74.0" +version = "2.75.0" source = { editable = "." } dependencies = [ { name = "certifi" },