From 4ffed4ae8ca315be372566203781df36027df125 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 30 Aug 2026 23:33:53 +0000 Subject: [PATCH 1/3] feat(reports): leftover residual on grouping comparison graphic (v2.72.0) Name leftover residual R on grouping comparison leftover-map pair segments (ADR 0315). After make seed, closest and farthest leftover pairs sit above the member list with leftover residual R next to leftover-map unexplained leftover U on that comparison graphic; click opens that post. Rank-0 unused axes still name R 0.00 when that persisted leftover residual is finite. A missing or non-finite R omits that leftover-map comparison graphic residual caption and keeps leftover-map unexplained leftover U. Never invent a leftover score. Never invent a theta. --- AGENTS.md | 15 +- ARCHITECTURE.md | 6 +- ...72.0-leftover-map-compare-plot-residual.md | 38 +++ CHANGELOG.md | 42 +++ CLAUDE.md | 2 +- .../adr/0277-leftover-map-segment-residual.md | 2 + .../adr/0298-leftover-map-compare-residual.md | 3 + ...r-map-compare-plot-unexplained-leftover.md | 3 + ...0315-leftover-map-compare-plot-residual.md | 248 ++++++++++++++++++ docs/product-technical-gap-baseline.md | 19 ++ frontend/package.json | 2 +- frontend/src/App.test.tsx | 7 +- .../src/components/LeftoverMapPlot.test.tsx | 159 +++++++++++ frontend/src/components/LeftoverMapPlot.tsx | 13 +- frontend/src/components/LeftoverPairList.tsx | 1 + frontend/src/i18n.test.ts | 18 ++ frontend/src/i18n.ts | 4 + frontend/src/leftoverMapPlotLayout.test.ts | 16 ++ frontend/src/leftoverMapPlotLayout.ts | 9 +- frontend/src/leftoverResidual.test.ts | 20 +- frontend/src/leftoverResidual.ts | 2 + pyproject.toml | 2 +- uv.lock | 2 +- 23 files changed, 620 insertions(+), 13 deletions(-) create mode 100644 CHANGELOG.d/2.72.0-leftover-map-compare-plot-residual.md create mode 100644 docs/adr/0315-leftover-map-compare-plot-residual.md diff --git a/AGENTS.md b/AGENTS.md index a47fa7e97..ffc993e53 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -273,7 +273,7 @@ stops startup instead of leaving a healthy-looking partial schema, and application code must not compensate for a missing table. Period leftover pairs (ADR 0017 / 0018 / 0048 / 0049 / 0119 / 0158 / 0162 / -0163 / 0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290 / 0291 / 0292 / 0293 / 0294 / 0295 / 0296 / 0297 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314) are computed in `lineageweave/leftover_pairs.py` from the +0163 / 0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290 / 0291 / 0292 / 0293 / 0294 / 0295 / 0296 / 0297 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315) are computed in `lineageweave/leftover_pairs.py` from the residual after a real GRM/GPCM score, never invented. Distances are Euclidean on the two-dimensional Gabriel leftover map; missing cells stay out of the factorization. Closest and farthest post–criterion pairs @@ -581,6 +581,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 0315 captions leftover residual on that grouping comparison leftover-map +graphic when formatLeftoverMapResidual returns a usable badge; a missing or +non-finite `R` omits that leftover-map comparison graphic residual caption and keeps +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 +`R` from `Y` and `E`, from `U` and `R̂`, 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. 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 @@ -658,6 +668,9 @@ share `x` when formatLeftoverMapCrossShare returns a usable badge. Grouping comparison leftover-map unexplained leftover (ADR 0314) captions that grouping comparison leftover-map graphic with persisted leftover-map unexplained leftover `U` when formatLeftoverMapUnexplained returns a usable badge. +Grouping comparison leftover residual (ADR 0315) captions +that grouping comparison leftover-map graphic with persisted leftover residual +`R` when formatLeftoverMapResidual 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 e1020af2c..3f2860b71 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -731,6 +731,9 @@ badge. ADR 0314 captions leftover-map unexplained leftover on that grouping comparison leftover-map graphic when formatLeftoverMapUnexplained returns a usable badge. +ADR 0315 captions leftover residual on that grouping +comparison leftover-map graphic when formatLeftoverMapResidual 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 @@ -774,7 +777,8 @@ graphic (ADR 0310), leftover-map explained leftover share on the grouping compar graphic (ADR 0311), leftover-map unexplained leftover share on the grouping comparison leftover-map 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); +graphic (ADR 0314), leftover residual on the grouping comparison leftover-map +graphic (ADR 0315); `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.72.0-leftover-map-compare-plot-residual.md b/CHANGELOG.d/2.72.0-leftover-map-compare-plot-residual.md new file mode 100644 index 000000000..7c77dd7e5 --- /dev/null +++ b/CHANGELOG.d/2.72.0-leftover-map-compare-plot-residual.md @@ -0,0 +1,38 @@ +## 2.72.0 — Leftover residual on grouping comparison leftover-map graphic + +- Caption leftover residual on the grouping comparison leftover-map + graphic (ADR 0315). 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, and leftover residual + `R` on that grouping comparison leftover-map graphic; click a + post marker or a pair button opens that post. A missing or non-finite `R` + omits that leftover-map comparison graphic residual caption and keeps + 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 `R 0.00` when that persisted leftover residual is + finite. Comparison copy uses `leftover map comparison graphic residual + {label}` so it stays distinct from hyphen `leftover residual + {label}` and from strip `Leftover map comparison residual`. + This increment does not caption leftover observed on the comparison graphic + with a distinct name. Do not invent `R` from `Y` and `E`, from `U` and `R̂`, 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 + 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 85b0b5193..d213df718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,48 @@ All notable changes to this project are documented here. Format follows ### Added +- 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 + 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, and leftover residual + `R` on that grouping comparison leftover-map graphic when + formatLeftoverMapResidual returns a usable badge; click a post marker or a pair + button opens that post. A missing or non-finite `R` omits that leftover-map + comparison graphic residual caption and keeps 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 `R 0.00` when that persisted leftover + residual is finite. Comparison copy uses `leftover map comparison graphic + residual {label}` so it stays distinct from hyphen `leftover + residual {label}` and from strip `Leftover map comparison residual`. + Leftover residual omits independently of 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 observed on the comparison + graphic with a distinct name. Do not invent `R` from `Y` and `E`, from `U` and `R̂`, leftover-map + reconstruction, leftover-map unexplained leftover, leftover-map distance, plotted coordinates, + leftover-map rank, leftover-map axis share, leftover expected, leftover observed, + 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 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-map unexplained leftover on the grouping comparison leftover-map graphic (ADR 0314 / v2.71.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 351afc4a8..6afe7c990 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), 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), 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/0277-leftover-map-segment-residual.md b/docs/adr/0277-leftover-map-segment-residual.md index 0f43a4227..7c95f3a58 100644 --- a/docs/adr/0277-leftover-map-segment-residual.md +++ b/docs/adr/0277-leftover-map-segment-residual.md @@ -2,6 +2,8 @@ **Decision status:** Accepted **Date:** 2026-08-30 +**Amended by:** [ADR 0315](0315-leftover-map-compare-plot-residual.md) +(leftover residual on the grouping comparison leftover-map graphic) Amends [ADR 0268](0268-leftover-map-graphic-display.md), [ADR 0049](0049-leftover-pair-report-ui.md), and leftover residual diff --git a/docs/adr/0298-leftover-map-compare-residual.md b/docs/adr/0298-leftover-map-compare-residual.md index 796cad367..ab198643d 100644 --- a/docs/adr/0298-leftover-map-compare-residual.md +++ b/docs/adr/0298-leftover-map-compare-residual.md @@ -3,6 +3,9 @@ **Decision status:** Accepted **Date:** 2026-08-31 +**Amended by:** [ADR 0315](0315-leftover-map-compare-plot-residual.md) +(leftover residual on the grouping comparison leftover-map graphic) + Amends leftover pairs on the grouping comparison strip ([ADR 0149](0149-leftover-pairs-on-comparison-strip.md)), leftover residual disclosure ([ADR 0162](0162-leftover-residual-disclosure.md)), leftover residual diff --git a/docs/adr/0314-leftover-map-compare-plot-unexplained-leftover.md b/docs/adr/0314-leftover-map-compare-plot-unexplained-leftover.md index ee143ecac..617a14eda 100644 --- a/docs/adr/0314-leftover-map-compare-plot-unexplained-leftover.md +++ b/docs/adr/0314-leftover-map-compare-plot-unexplained-leftover.md @@ -3,6 +3,9 @@ **Decision status:** Accepted **Date:** 2026-08-31 +**Amended by:** [ADR 0315](0315-leftover-map-compare-plot-residual.md) +(leftover residual on the grouping comparison leftover-map graphic) + Amends leftover-map unexplained leftover on graphic-display pair segments ([ADR 0276](0276-leftover-map-segment-unexplained-leftover.md)), leftover-map unexplained leftover on grouping comparison strip pair rows diff --git a/docs/adr/0315-leftover-map-compare-plot-residual.md b/docs/adr/0315-leftover-map-compare-plot-residual.md new file mode 100644 index 000000000..885653462 --- /dev/null +++ b/docs/adr/0315-leftover-map-compare-plot-residual.md @@ -0,0 +1,248 @@ +# ADR 0315 — Name leftover residual on the grouping comparison leftover-map graphic + +**Decision status:** Accepted +**Date:** 2026-08-31 + +Amends leftover residual on graphic-display pair segments +([ADR 0277](0277-leftover-map-segment-residual.md)), leftover residual +on grouping comparison strip pair rows +([ADR 0298](0298-leftover-map-compare-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 residual +disclosure ([ADR 0162](0162-leftover-residual-disclosure.md)), leftover pairs on +the grouping comparison strip +([ADR 0149](0149-leftover-pairs-on-comparison-strip.md)), leftover-map +coordinates on the compare leftover-pair payload +([ADR 0303](0303-leftover-map-compare-coordinates-payload.md)), leftover-map +coordinates on grouping comparison strip pair rows +([ADR 0302](0302-leftover-map-compare-coordinates.md)), leftover-map +rank on grouping comparison strip pair rows +([ADR 0301](0301-leftover-map-compare-rank.md)), leftover expected on grouping +comparison strip pair rows +([ADR 0300](0300-leftover-map-compare-expected.md)), leftover observed on +grouping comparison strip pair rows +([ADR 0299](0299-leftover-map-compare-observed.md)), leftover-map unexplained leftover +on grouping comparison strip pair rows +([ADR 0297](0297-leftover-map-compare-unexplained.md)), leftover observed `Y` / +expected `E` ([ADR 0163](0163-leftover-observed-expected.md)), leftover-map unexplained leftover persistence +([ADR 0182](0182-leftover-map-unexplained.md)), leftover-map rank +([ADR 0164](0164-leftover-map-rank.md)), leftover-map rank on pair +segments ([ADR 0280](0280-leftover-map-segment-rank.md)), leftover-map coordinate ticks +([ADR 0270](0270-leftover-map-coordinate-ticks.md)), leftover-map graphic display +([ADR 0268](0268-leftover-map-graphic-display.md)), leftover-map axis share on +the graphic display ([ADR 0269](0269-leftover-map-axis-share-plot.md)), leftover-map +complete-case coverage on the graphic display +([ADR 0281](0281-leftover-map-plot-coverage.md)), leftover-map item complete-case +coverage on the graphic display +([ADR 0282](0282-leftover-map-plot-item-coverage.md)), leftover-map incomplete +post coverage on the graphic display +([ADR 0283](0283-leftover-map-plot-incomplete.md)), leftover-map incomplete +item coverage on the graphic display +([ADR 0284](0284-leftover-map-plot-incomplete-item.md)), leftover-map reconstruction +on pair segments ([ADR 0272](0272-leftover-map-segment-reconstruction.md)), leftover-map +reconstruction persistence ([ADR 0201](0201-leftover-map-reconstruction.md)), leftover-map +explained leftover share persistence ([ADR 0266](0266-leftover-map-explained-share.md)), leftover-map unexplained leftover share persistence +([ADR 0233](0233-leftover-map-unexplained-share.md)), leftover-map unexplained leftover share on pair segments +([ADR 0274](0274-leftover-map-segment-unexplained-share.md)), leftover-map explained leftover share on pair segments +([ADR 0273](0273-leftover-map-segment-explained-share.md)), leftover-map cross share on pair segments +([ADR 0275](0275-leftover-map-segment-cross-share.md)), leftover-map cross share persistence +([ADR 0185](0185-leftover-map-cross-share.md)), leftover-map cross share on grouping comparison strip pair rows +([ADR 0296](0296-leftover-map-compare-cross-share.md)), leftover-map unexplained leftover on pair segments +([ADR 0276](0276-leftover-map-segment-unexplained-leftover.md)), leftover observed on pair segments +([ADR 0278](0278-leftover-map-segment-observed.md)), leftover expected on pair +segments ([ADR 0279](0279-leftover-map-segment-expected.md)), leftover-map distance on pair segments +([ADR 0271](0271-leftover-map-segment-distance.md)), and leftover-map coordinates +([ADR 0267](0267-leftover-map-coordinates.md)). Independent of leftover-map +post complete-case coverage fail-closed on the pair list +([ADR 0288](0288-leftover-map-list-post-coverage-helper.md)), leftover-map +incomplete item coverage on the pair list +([ADR 0287](0287-leftover-map-list-incomplete-item.md)), leftover-map incomplete +post coverage on the pair list +([ADR 0286](0286-leftover-map-list-incomplete-post.md)), leftover-map item +complete-case coverage on the pair list +([ADR 0285](0285-leftover-map-list-item-coverage.md)), and leftover observed +on the grouping comparison leftover-map graphic. + +## Context + +ADR 0162 already persists leftover residual `R = Y − E[Y|θ, item]` after IRT +main effects. ADR 0277 already captions period-report leftover-map pair +segments with `leftover residual {label}` when that leftover residual is +finite. ADR 0298 already captions grouping comparison leftover-pair +buttons with that same persisted leftover residual under accessible name +`Leftover map comparison residual`. ADR 0314 already captions leftover-map +unexplained leftover on the grouping comparison leftover-map graphic. +The comparison graphic still reuses hyphen `leftover residual {label}`, so a +buyer who compares leftover pairs can treat the period-report graphic caption +as the comparison graphic leftover residual even after the strip names `R`. +Hiding a distinct comparison-graphic leftover residual caption lets leftover-map +unexplained leftover `U` or leftover observed `Y` be read as leftover residual +without a next action. When `Y`, `E`, and `R` are finite, `Y − E = R`; the +comparison graphic must name the same persisted `R` the pair row and strip +already show. A finite negative leftover is shown, never clamped. + +This increment captions leftover residual on the grouping comparison leftover-map +graphic from already-named leftover residual through formatLeftoverMapResidual. +Comparison copy uses the accessible name `leftover map comparison graphic residual {label}` +so it stays distinct from hyphen `leftover residual {label}` on the period-report +graphic and from strip `Leftover map comparison residual`. It does not add +columns. It does not recompute leftover residual from `Y` and `E`, from `U` and +`R̂`, 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. 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 **0315** so it does not collide with +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 residual disclosure (0162), 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 residual when formatLeftoverMapResidual returns a usable +badge, next to leftover-map unexplained leftover `U`. Use the distinct accessible +name `leftover map comparison graphic residual {label}` so the graphic caption is +not the strip badge (`Leftover map comparison residual`) and is not the +period-report graphic caption (`leftover residual {label}`). A missing or +non-finite `R` omits that leftover-map comparison graphic residual caption and +keeps 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 observed `Y` when finite, +leftover-map reconstruction `R̂` when finite, leftover-map distance `d`, plus the +strip leftover residual badge. Rank-0 origin cells still name `R 0.00` when that +persisted leftover residual is finite. A finite negative leftover is shown, never +clamped. Do not invent `R` from `Y` and `E`, from `U` and `R̂`, 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 residual omits independently of 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 residual omits leftover-map +comparison graphic residual and keeps 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 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, +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 residual +on that graphic when formatLeftoverMapResidual returns a usable badge. Rank-0 +unused axes still plot at the origin and still name `R 0.00` when that persisted +leftover residual 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-map complete-case coverage on the grouping comparison strip, +leftover-map item complete-case coverage on the grouping comparison strip, +leftover-map incomplete post coverage on the grouping comparison strip, +leftover-map incomplete item coverage on the grouping comparison strip, +leftover-map reconstruction on grouping comparison strip pair rows, +leftover-map explained leftover share on grouping comparison strip pair rows, +leftover-map unexplained leftover share on grouping comparison strip pair rows, +leftover-map unexplained leftover on grouping comparison strip pair rows, +leftover residual on grouping comparison strip pair rows, leftover observed +on grouping comparison strip pair rows, leftover expected on grouping +comparison strip pair rows, leftover-map rank on grouping comparison strip +pair rows, leftover-map coordinates on grouping comparison strip pair rows, +leftover-map coordinates on the compare leftover-pair payload, leftover-map +inner product, leftover-map cosine, leftover-map length, leftover residual +on pair segments, leftover observed on pair segments, leftover expected on +pair segments, leftover-map rank on pair segments, leftover-map unexplained +leftover on pair segments, leftover-map unexplained leftover persistence, +leftover-map unexplained leftover share on pair segments, leftover-map unexplained leftover share +persistence, leftover-map explained leftover +share on pair segments, leftover-map explained leftover share +persistence, leftover-map reconstruction on pair segments, leftover-map +reconstruction persistence, leftover-map reconstruction on the grouping +comparison leftover-map graphic, leftover-map explained leftover share on the grouping +comparison leftover-map graphic, leftover-map unexplained leftover share on the grouping +comparison leftover-map graphic, leftover-map cross share on the grouping +comparison leftover-map graphic, leftover-map unexplained leftover on the grouping +comparison leftover-map graphic, leftover-map item complete-case coverage on the +graphic display, leftover-map item complete-case coverage on the pair list, +leftover-map incomplete post coverage on the graphic display, leftover-map +incomplete post coverage on the pair list, leftover-map incomplete item +coverage on the graphic display, leftover-map incomplete item coverage on +the pair list, leftover-map post complete-case coverage fail-closed on the +pair list, leftover-map rank persistence, leftover-map coordinate +persistence, leftover-map coordinate ticks, leftover-map complete-case +coverage on the graphic display, leftover-map complete-case coverage on the +grouping comparison leftover-map graphic, leftover-map item complete-case +coverage on the grouping comparison leftover-map graphic, leftover-map +incomplete post coverage on the grouping comparison leftover-map graphic, +leftover-map incomplete item coverage on the grouping comparison leftover-map +graphic, leftover-map axis share on the grouping comparison leftover-map graphic, +and leftover observed 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. Residual after IRT +main effects is `R = Y − E[Y|θ, item]`. Grouping comparison leftover residual +captions that persisted leftover on the grouping comparison leftover-map +graphic only when formatLeftoverMapResidual returns a usable badge. +Rank-0 unused axes still name `R 0.00` when that leftover residual 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 b0f188e51..5e45bfecf 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,24 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-08-31 08:19 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not +> self-approve. #854 leftover-map unexplained leftover on the grouping comparison leftover-map +> graphic (v2.71.0 / ADR 0314) is `935cdea`, stacked on #853 @ `c4f79bc`. Independent +> APPROVE is missing; Copilot review requested this cycle. Next buyer increment on this +> cycle: leftover residual on the grouping comparison leftover-map +> graphic (ADR 0315 / v2.72.0) delivered locally on +> `feat/leftover-map-compare-plot-residual-v2720`. Caption persisted leftover residual +> `R` on that comparison graphic from already-named leftover_residual. Distinct +> accessible name `leftover map comparison graphic residual {label}`. Omit when +> formatLeftoverMapResidual fail-closes. Do not caption leftover observed on that +> comparison graphic with a distinct name this increment. Do not invent `R` from +> `Y` and `E`, from `U` and `R̂`, 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 #854. 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. + > Exact-head loop overlay: 2026-08-31 07:21 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not > self-approve. #853 leftover-map cross share on the grouping comparison leftover-map diff --git a/frontend/package.json b/frontend/package.json index f3a706b03..4ac282897 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.71.0", + "version": "2.72.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index ee6444019..b6ea4cf58 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -4570,9 +4570,14 @@ describe("App, authenticated", () => { ).toHaveLength(1); expect( within(screen.getByLabelText("Grouping comparison")).getByLabelText( - "leftover residual R +0.40", + "leftover map comparison graphic residual R +0.40", ), ).toHaveTextContent("R +0.40"); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + "leftover residual R +0.40", + ), + ).not.toBeInTheDocument(); expect( within(screen.getByLabelText("Grouping comparison")).getAllByLabelText( "Leftover map comparison residual", diff --git a/frontend/src/components/LeftoverMapPlot.test.tsx b/frontend/src/components/LeftoverMapPlot.test.tsx index 84102df20..9d864932c 100644 --- a/frontend/src/components/LeftoverMapPlot.test.tsx +++ b/frontend/src/components/LeftoverMapPlot.test.tsx @@ -1865,4 +1865,163 @@ describe("LeftoverMapPlot", () => { ); expect(screen.getByText("leftover map comparison axis 1 (0%)")).toBeInTheDocument(); }); + + it("names leftover residual on the comparison graphic with a distinct accessible name", () => { + render( + , + ); + expect( + screen.getByLabelText("leftover map comparison graphic residual R +0.40"), + ).toHaveTextContent("R +0.40"); + expect( + screen.getByLabelText("leftover map comparison graphic residual R −1.10"), + ).toHaveTextContent("R −1.10"); + expect(screen.queryByLabelText("leftover residual R +0.40")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("leftover residual R −1.10")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("Leftover map comparison residual")).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic unexplained leftover U +0.05"), + ).toHaveTextContent("U +0.05"); + expect( + screen.getByLabelText("leftover map comparison graphic cross share 2R̂U/R² 0.12"), + ).toHaveTextContent("2R̂U/R² 0.12"); + expect( + screen.getByLabelText("leftover map comparison graphic unexplained leftover share U²/R² 0.02"), + ).toHaveTextContent("U²/R² 0.02"); + expect( + screen.getByLabelText("leftover map comparison graphic explained leftover share R̂²/R² 0.76"), + ).toHaveTextContent("R̂²/R² 0.76"); + 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 residual when R is missing or not usable", () => { + render( + , + ); + expect(screen.queryByLabelText(/leftover map comparison graphic residual R/)).not.toBeInTheDocument(); + expect(screen.queryByLabelText(/^leftover residual R/)).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic unexplained leftover U +0.05"), + ).toHaveTextContent("U +0.05"); + expect( + screen.getByLabelText("leftover map comparison graphic cross share 2R̂U/R² 0.12"), + ).toHaveTextContent("2R̂U/R² 0.12"); + expect( + screen.getByLabelText("leftover map comparison graphic unexplained leftover share U²/R² 0.02"), + ).toHaveTextContent("U²/R² 0.02"); + expect( + screen.getByLabelText("leftover map comparison graphic explained leftover share R̂²/R² 0.76"), + ).toHaveTextContent("R̂²/R² 0.76"); + 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 distance d 1.84")).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 residual on the comparison graphic when that persisted value is finite", () => { + render( + , + ); + expect( + screen.getByLabelText("leftover map comparison graphic residual R 0.00"), + ).toHaveTextContent("R 0.00"); + expect(screen.queryByLabelText("leftover residual R 0.00")).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic unexplained leftover U 0.00"), + ).toHaveTextContent("U 0.00"); + expect( + screen.getByLabelText("leftover map comparison graphic cross share 2R̂U/R² 0.00"), + ).toHaveTextContent("2R̂U/R² 0.00"); + expect( + screen.getByLabelText("leftover map comparison graphic unexplained leftover share U²/R² 0.00"), + ).toHaveTextContent("U²/R² 0.00"); + expect( + screen.getByLabelText("leftover map comparison graphic explained leftover share R̂²/R² 0.00"), + ).toHaveTextContent("R̂²/R² 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 a80dcd92d..b94785aad 100644 --- a/frontend/src/components/LeftoverMapPlot.tsx +++ b/frontend/src/components/LeftoverMapPlot.tsx @@ -38,6 +38,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED_SHARE, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_CROSS_SHARE, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, LEFTOVER_MAP_PLOT_CAPTION, LEFTOVER_MAP_PLOT_POST_ACTION, LEFTOVER_MAP_PLOT_SEGMENT_CROSS_SHARE, @@ -157,6 +158,9 @@ function leftoverMapPlotAxisText( * labels. ADR 0314 captions leftover-map unexplained leftover on that * comparison graphic from already-named leftover-map unexplained leftover * with distinct leftover map comparison graphic unexplained leftover + * labels. ADR 0315 captions leftover residual on that + * comparison graphic from already-named leftover residual + * with distinct leftover map comparison graphic residual * labels. * Never invent a leftover score. */ @@ -406,9 +410,12 @@ export function LeftoverMapPlot({ x={segment.residualX} y={segment.residualY} textAnchor="middle" - aria-label={tf(LEFTOVER_MAP_PLOT_SEGMENT_RESIDUAL, { - label: segment.residualLabel, - })} + aria-label={tf( + variant === "comparison" + ? LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL + : LEFTOVER_MAP_PLOT_SEGMENT_RESIDUAL, + { label: segment.residualLabel }, + )} > {segment.residualLabel} diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index 169c63f2d..6d87ff504 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -122,6 +122,7 @@ export type LeftoverPairListProps = { * pair list. ADR 0313 captions leftover-map * cross share on that grouping comparison leftover-map graphic, not this * 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 * renders together before opening the named post. */ diff --git a/frontend/src/i18n.test.ts b/frontend/src/i18n.test.ts index 0c484602b..0bf278ff6 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -95,6 +95,7 @@ describe("i18n", () => { "Leftover map comparison cross share", "leftover map comparison graphic unexplained leftover {label}", "Leftover map comparison unexplained leftover", + "leftover map comparison graphic residual {label}", "Leftover map comparison residual", "Leftover map comparison observed", "Leftover map comparison expected", @@ -579,6 +580,23 @@ describe("i18n", () => { }, ); + it.each([ + ["ko", "잔여 지도 비교 그림 잔여 R +0.40"], + ["zh", "残差地图比较图形残差 R +0.40"], + ["ja", "残差マップの比較図残差 R +0.40"], + ["vi", "phần dư đồ họa so sánh R +0.40"], + ] as const)( + "formats leftover map comparison graphic residual in %s", + (locale, expected) => { + setLocale(locale); + expect( + tf("leftover map comparison graphic residual {label}", { + label: "R +0.40", + }), + ).toBe(expected); + }, + ); + it.each([ ["ko", "잔여 지도 비교 설명 잔여 점유율"], ["zh", "残差地图比较已解释残差份额"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 69597163f..7c7befc08 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -287,6 +287,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison cross share": "잔여 지도 비교 교차 점유율", "leftover map comparison graphic unexplained leftover {label}": "잔여 지도 비교 그림 미설명 잔여 {label}", "Leftover map comparison unexplained leftover": "잔여 지도 비교 미설명 잔여", + "leftover map comparison graphic residual {label}": "잔여 지도 비교 그림 잔여 {label}", "Leftover map comparison residual": "잔여 지도 비교 잔여", "Leftover map comparison observed": "잔여 지도 비교 관측", "Leftover map comparison expected": "잔여 지도 비교 기대", @@ -924,6 +925,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison cross share": "残差地图比较交叉份额", "leftover map comparison graphic unexplained leftover {label}": "残差地图比较图形未解释残差 {label}", "Leftover map comparison unexplained leftover": "残差地图比较未解释残差", + "leftover map comparison graphic residual {label}": "残差地图比较图形残差 {label}", "Leftover map comparison residual": "残差地图比较残差", "Leftover map comparison observed": "残差地图比较观测", "Leftover map comparison expected": "残差地图比较期望", @@ -1582,6 +1584,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison cross share": "残差マップの比較交差割合", "leftover map comparison graphic unexplained leftover {label}": "残差マップの比較図未説明残差 {label}", "Leftover map comparison unexplained leftover": "残差マップの比較未説明残差", + "leftover map comparison graphic residual {label}": "残差マップの比較図残差 {label}", "Leftover map comparison residual": "残差マップの比較残差", "Leftover map comparison observed": "残差マップの比較観測", "Leftover map comparison expected": "残差マップの比較期待", @@ -2219,6 +2222,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison cross share": "Phần giao so sánh bản đồ phần dư", "leftover map comparison graphic unexplained leftover {label}": "phần dư chưa giải thích đồ họa so sánh {label}", "Leftover map comparison unexplained leftover": "Phần dư chưa giải thích so sánh", + "leftover map comparison graphic residual {label}": "phần dư đồ họa so sánh {label}", "Leftover map comparison residual": "Phần dư so sánh bản đồ phần dư", "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ư", diff --git a/frontend/src/leftoverMapPlotLayout.test.ts b/frontend/src/leftoverMapPlotLayout.test.ts index 026810775..c03d9b215 100644 --- a/frontend/src/leftoverMapPlotLayout.test.ts +++ b/frontend/src/leftoverMapPlotLayout.test.ts @@ -12,6 +12,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED_SHARE, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_CROSS_SHARE, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, LEFTOVER_MAP_COMPARE_PLOT_SVG, PLOT_HEIGHT, PLOT_PADDING, @@ -1272,4 +1273,19 @@ describe("leftover map comparison graphic labels", () => { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED_SHARE, ); }); + + it("stays distinct from leftover residual copy", () => { + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).toBe( + "leftover map comparison graphic residual {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).not.toBe( + "leftover residual {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).not.toBe( + "Leftover map comparison residual", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).not.toBe( + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED, + ); + }); }); diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index b34b13cc0..d4466c312 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -41,8 +41,9 @@ * reconstruction on that graphic (ADR 0310), grouping comparison leftover-map * explained leftover share on that graphic (ADR 0311), grouping comparison leftover-map * 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) caption the pair list or the grouping comparison + * cross share on that graphic (ADR 0313), 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 * 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 @@ -60,6 +61,8 @@ * cross share on that comparison graphic from already-named leftover-map * cross share. ADR 0314 captions leftover-map unexplained leftover on that * comparison graphic from already-named leftover-map unexplained leftover. + * ADR 0315 captions leftover residual on that comparison graphic from already-named + * leftover residual. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; @@ -123,6 +126,8 @@ export { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_UNEXPLAINED } from "./leftoverMapUnex export const LEFTOVER_MAP_PLOT_SEGMENT_RESIDUAL = "leftover residual {label}"; +export { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL } from "./leftoverResidual"; + export const LEFTOVER_MAP_PLOT_SEGMENT_OBSERVED = "leftover observed {label}"; diff --git a/frontend/src/leftoverResidual.test.ts b/frontend/src/leftoverResidual.test.ts index d0fa7c9d5..3f07fba6a 100644 --- a/frontend/src/leftoverResidual.test.ts +++ b/frontend/src/leftoverResidual.test.ts @@ -1,5 +1,10 @@ import { describe, expect, it } from "vitest"; -import { formatLeftoverMapResidual, formatLeftoverResidual, LEFTOVER_MAP_COMPARE_RESIDUAL_LABEL } from "./leftoverResidual"; +import { + formatLeftoverMapResidual, + formatLeftoverResidual, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL, + LEFTOVER_MAP_COMPARE_RESIDUAL_LABEL, +} from "./leftoverResidual"; describe("formatLeftoverResidual", () => { it("keeps a signed residual without inventing a leftover score", () => { @@ -28,4 +33,17 @@ describe("formatLeftoverMapResidual", () => { expect(LEFTOVER_MAP_COMPARE_RESIDUAL_LABEL).toBe("Leftover map comparison residual"); expect(LEFTOVER_MAP_COMPARE_RESIDUAL_LABEL).not.toBe("leftover residual {label}"); }); + + it("keeps the grouping comparison graphic residual label distinct from the graphic and strip labels", () => { + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).toBe( + "leftover map comparison graphic residual {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).not.toBe("leftover residual {label}"); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).not.toBe( + LEFTOVER_MAP_COMPARE_RESIDUAL_LABEL, + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).not.toBe( + "leftover map comparison graphic unexplained leftover {label}", + ); + }); }); diff --git a/frontend/src/leftoverResidual.ts b/frontend/src/leftoverResidual.ts index f2d0df35c..bc3015d79 100644 --- a/frontend/src/leftoverResidual.ts +++ b/frontend/src/leftoverResidual.ts @@ -23,3 +23,5 @@ export function formatLeftoverMapResidual(value: number | null | undefined): str export const LEFTOVER_MAP_COMPARE_RESIDUAL_LABEL = "Leftover map comparison residual"; +export const LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL = + "leftover map comparison graphic residual {label}"; diff --git a/pyproject.toml b/pyproject.toml index 5203e4fd9..f0016537b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.71.0" +version = "2.72.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 c39ab5333..e04d99617 100644 --- a/uv.lock +++ b/uv.lock @@ -685,7 +685,7 @@ wheels = [ [[package]] name = "lineageweave" -version = "2.71.0" +version = "2.72.0" source = { editable = "." } dependencies = [ { name = "certifi" }, From 1087b16f2dba743bc0d1d4e08ff50811b5845393 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 30 Aug 2026 23:41:53 +0000 Subject: [PATCH 2/3] fix(a11y): distinct leftover residual name on comparison graphic Keep leftover map comparison graphic leftover residual {label} distinct from leftover residual {label} on the period-report graphic and from strip Leftover map comparison residual (ADR 0315). Vietnamese copy names the leftover map. Do not invent R. Never invent a leftover score. Never invent a theta. No new columns. --- AGENTS.md | 2 +- ...72.0-leftover-map-compare-plot-residual.md | 12 +- CHANGELOG.md | 12 +- ...0315-leftover-map-compare-plot-residual.md | 167 +++++++++++------- docs/product-technical-gap-baseline.md | 15 +- frontend/src/App.test.tsx | 2 +- .../src/components/LeftoverMapPlot.test.tsx | 10 +- frontend/src/components/LeftoverMapPlot.tsx | 2 +- frontend/src/components/LeftoverPairList.tsx | 5 +- frontend/src/i18n.test.ts | 8 +- frontend/src/i18n.ts | 8 +- frontend/src/leftoverMapPlotLayout.test.ts | 2 +- frontend/src/leftoverMapPlotLayout.ts | 6 +- frontend/src/leftoverResidual.test.ts | 4 +- frontend/src/leftoverResidual.ts | 2 +- 15 files changed, 146 insertions(+), 111 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ffc993e53..ccf5a3160 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -583,7 +583,7 @@ leftover-map incomplete post coverage, leftover-map incomplete item coverage, le count, or the count of unused axes. ADR 0315 captions leftover residual on that grouping comparison leftover-map graphic when formatLeftoverMapResidual returns a usable badge; a missing or -non-finite `R` omits that leftover-map comparison graphic residual caption and keeps +non-finite `R` omits that leftover-map comparison graphic leftover residual caption and keeps 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 diff --git a/CHANGELOG.d/2.72.0-leftover-map-compare-plot-residual.md b/CHANGELOG.d/2.72.0-leftover-map-compare-plot-residual.md index 7c77dd7e5..8a59ffcc2 100644 --- a/CHANGELOG.d/2.72.0-leftover-map-compare-plot-residual.md +++ b/CHANGELOG.d/2.72.0-leftover-map-compare-plot-residual.md @@ -15,18 +15,18 @@ 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, and leftover residual - `R` on that grouping comparison leftover-map graphic; click a + unexplained leftover `U` when formatLeftoverMapUnexplained returns a usable badge, + and leftover residual `R` on that grouping comparison leftover-map graphic; click a post marker or a pair button opens that post. A missing or non-finite `R` - omits that leftover-map comparison graphic residual caption and keeps + omits that leftover-map comparison graphic leftover residual caption and keeps 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 `R 0.00` when that persisted leftover residual is - finite. Comparison copy uses `leftover map comparison graphic residual - {label}` so it stays distinct from hyphen `leftover residual + finite. Comparison copy uses `leftover map comparison graphic leftover residual + {label}` so it stays distinct from `leftover residual {label}` and from strip `Leftover map comparison residual`. This increment does not caption leftover observed on the comparison graphic with a distinct name. Do not invent `R` from `Y` and `E`, from `U` and `R̂`, leftover-map reconstruction, @@ -34,5 +34,5 @@ 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 - is shown, never clamped. Never invent a leftover score. + residual 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 d213df718..eacbaf474 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,11 +23,11 @@ All notable changes to this project are documented here. Format follows 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, and leftover residual - `R` on that grouping comparison leftover-map graphic when + unexplained leftover `U` when formatLeftoverMapUnexplained returns a usable badge, + and leftover residual `R` on that grouping comparison leftover-map graphic when formatLeftoverMapResidual returns a usable badge; click a post marker or a pair button opens that post. A missing or non-finite `R` omits that leftover-map - comparison graphic residual caption and keeps leftover-map unexplained leftover + comparison graphic leftover residual caption and keeps 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, @@ -35,8 +35,8 @@ All notable changes to this project are documented here. Format follows graphic incomplete items when those helpers return usable integers. Rank-0 origin cells still plot at `(0, 0)` and still name `R 0.00` when that persisted leftover residual is finite. Comparison copy uses `leftover map comparison graphic - residual {label}` so it stays distinct from hyphen `leftover - residual {label}` and from strip `Leftover map comparison residual`. + leftover residual {label}` so it stays distinct from + `leftover residual {label}` and from strip `Leftover map comparison residual`. Leftover residual omits independently of 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 @@ -46,7 +46,7 @@ All notable changes to this project are documented here. Format follows leftover-map rank, leftover-map axis share, leftover expected, leftover observed, 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 is shown, + count of unused axes. A finite negative leftover residual 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. diff --git a/docs/adr/0315-leftover-map-compare-plot-residual.md b/docs/adr/0315-leftover-map-compare-plot-residual.md index 885653462..a770fc040 100644 --- a/docs/adr/0315-leftover-map-compare-plot-residual.md +++ b/docs/adr/0315-leftover-map-compare-plot-residual.md @@ -41,12 +41,27 @@ rank on grouping comparison strip pair rows comparison strip pair rows ([ADR 0300](0300-leftover-map-compare-expected.md)), leftover observed on grouping comparison strip pair rows -([ADR 0299](0299-leftover-map-compare-observed.md)), leftover-map unexplained leftover +([ADR 0299](0299-leftover-map-compare-observed.md)), leftover residual on grouping comparison strip pair rows -([ADR 0297](0297-leftover-map-compare-unexplained.md)), leftover observed `Y` / -expected `E` ([ADR 0163](0163-leftover-observed-expected.md)), leftover-map unexplained leftover persistence -([ADR 0182](0182-leftover-map-unexplained.md)), leftover-map rank -([ADR 0164](0164-leftover-map-rank.md)), leftover-map rank on pair +([ADR 0298](0298-leftover-map-compare-residual.md)), leftover-map unexplained leftover +on grouping comparison strip pair rows +([ADR 0297](0297-leftover-map-compare-unexplained.md)), leftover-map unexplained +leftover share on grouping comparison strip pair rows +([ADR 0295](0295-leftover-map-compare-unexplained-share.md)), leftover-map +explained leftover share on grouping comparison strip pair rows +([ADR 0294](0294-leftover-map-compare-explained-share.md)), leftover-map +reconstruction on grouping comparison strip pair rows +([ADR 0293](0293-leftover-map-compare-reconstruction.md)), leftover-map +incomplete item coverage on the grouping comparison strip +([ADR 0292](0292-leftover-map-compare-incomplete-item.md)), leftover-map +incomplete post coverage on the grouping comparison strip +([ADR 0291](0291-leftover-map-compare-incomplete-post.md)), leftover-map item +complete-case coverage on the grouping comparison strip +([ADR 0290](0290-leftover-map-compare-item-coverage.md)), leftover-map +complete-case coverage on the grouping comparison strip +([ADR 0289](0289-leftover-map-compare-coverage.md)), leftover observed `Y` / +expected `E` ([ADR 0163](0163-leftover-observed-expected.md)), leftover-map unexplained leftover persistence ([ADR 0182](0182-leftover-map-unexplained.md)), +leftover-map rank ([ADR 0164](0164-leftover-map-rank.md)), leftover-map rank on pair segments ([ADR 0280](0280-leftover-map-segment-rank.md)), leftover-map coordinate ticks ([ADR 0270](0270-leftover-map-coordinate-ticks.md)), leftover-map graphic display ([ADR 0268](0268-leftover-map-graphic-display.md)), leftover-map axis share on @@ -68,10 +83,8 @@ explained leftover share persistence ([ADR 0266](0266-leftover-map-explained-sha ([ADR 0275](0275-leftover-map-segment-cross-share.md)), leftover-map cross share persistence ([ADR 0185](0185-leftover-map-cross-share.md)), leftover-map cross share on grouping comparison strip pair rows ([ADR 0296](0296-leftover-map-compare-cross-share.md)), leftover-map unexplained leftover on pair segments -([ADR 0276](0276-leftover-map-segment-unexplained-leftover.md)), leftover observed on pair segments -([ADR 0278](0278-leftover-map-segment-observed.md)), leftover expected on pair -segments ([ADR 0279](0279-leftover-map-segment-expected.md)), leftover-map distance on pair segments -([ADR 0271](0271-leftover-map-segment-distance.md)), and leftover-map coordinates +([ADR 0276](0276-leftover-map-segment-unexplained-leftover.md)), leftover residual on pair segments +([ADR 0277](0277-leftover-map-segment-residual.md)), leftover-map coordinates ([ADR 0267](0267-leftover-map-coordinates.md)). Independent of leftover-map post complete-case coverage fail-closed on the pair list ([ADR 0288](0288-leftover-map-list-post-coverage-helper.md)), leftover-map @@ -80,58 +93,75 @@ incomplete item coverage on the pair list post coverage on the pair list ([ADR 0286](0286-leftover-map-list-incomplete-post.md)), leftover-map item complete-case coverage on the pair list -([ADR 0285](0285-leftover-map-list-item-coverage.md)), and leftover observed -on the grouping comparison leftover-map graphic. +([ADR 0285](0285-leftover-map-list-item-coverage.md)), leftover expected on pair +segments ([ADR 0279](0279-leftover-map-segment-expected.md)), leftover observed +on pair segments ([ADR 0278](0278-leftover-map-segment-observed.md)), leftover-map +distance on pair segments ([ADR 0271](0271-leftover-map-segment-distance.md)), +and leftover observed on the grouping comparison leftover-map graphic. ## Context -ADR 0162 already persists leftover residual `R = Y − E[Y|θ, item]` after IRT -main effects. ADR 0277 already captions period-report leftover-map pair -segments with `leftover residual {label}` when that leftover residual is +ADR 0162 already persists leftover residual +`R = Y − E[Y|θ, item]` after IRT main effects. ADR 0277 already captions +period-report leftover-map pair segments with +`leftover residual {label}` when that leftover residual is finite. ADR 0298 already captions grouping comparison leftover-pair buttons with that same persisted leftover residual under accessible name -`Leftover map comparison residual`. ADR 0314 already captions leftover-map -unexplained leftover on the grouping comparison leftover-map graphic. -The comparison graphic still reuses hyphen `leftover residual {label}`, so a -buyer who compares leftover pairs can treat the period-report graphic caption -as the comparison graphic leftover residual even after the strip names `R`. -Hiding a distinct comparison-graphic leftover residual caption lets leftover-map +`Leftover map comparison residual`. ADR 0314 already captions +leftover-map unexplained leftover on the grouping comparison leftover-map graphic. +The comparison graphic still reuses +`leftover residual {label}`, so a buyer who compares leftover +pairs can treat the period-report graphic caption as the comparison graphic +leftover residual even after the strip names `R`. Hiding a distinct +comparison-graphic leftover residual caption lets leftover-map unexplained leftover `U` or leftover observed `Y` be read as leftover residual -without a next action. When `Y`, `E`, and `R` are finite, `Y − E = R`; the -comparison graphic must name the same persisted `R` the pair row and strip -already show. A finite negative leftover is shown, never clamped. - -This increment captions leftover residual on the grouping comparison leftover-map -graphic from already-named leftover residual through formatLeftoverMapResidual. -Comparison copy uses the accessible name `leftover map comparison graphic residual {label}` -so it stays distinct from hyphen `leftover residual {label}` on the period-report -graphic and from strip `Leftover map comparison residual`. It does not add -columns. It does not recompute leftover residual from `Y` and `E`, from `U` and -`R̂`, 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. Do not invent a leftover score. Do not invent a theta. +without a next action. When `Y`, `E`, and `R` are +finite, `Y − E = R`; the comparison graphic must name the same +persisted `R` the pair row and strip already show. A finite negative leftover is +shown, never clamped. + +This increment captions leftover residual on the grouping +comparison leftover-map graphic from already-named leftover residual +through formatLeftoverMapResidual. Comparison copy uses the accessible +name `leftover map comparison graphic leftover residual {label}` so it stays +distinct from `leftover residual {label}` on the +period-report graphic and from strip `Leftover map comparison residual`. +It does not add columns. It does not recompute leftover residual +from `Y` and `E`, from `U` and `R̂`, 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. 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 **0315** so it does not collide with 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 residual disclosure (0162), leftover-map -graphic display (0268), leftover-map coordinates (0267), or the dashboard stacks. +(0313), leftover-map unexplained leftover share on the grouping comparison leftover-map graphic +(0312), leftover-map explained leftover share on the grouping comparison leftover-map graphic +(0311), leftover-map reconstruction on the grouping comparison leftover-map graphic +(0310), leftover-map incomplete item coverage on the grouping comparison leftover-map +graphic (0309), leftover-map incomplete post coverage on the grouping comparison leftover-map +graphic (0308), leftover-map item complete-case coverage on the grouping comparison leftover-map +graphic (0307), leftover-map complete-case coverage on the grouping comparison +leftover-map graphic (0306), leftover-map axis share on the grouping comparison leftover-map +graphic (0305), leftover-map graphic display on the grouping comparison strip +(0304), leftover residual on grouping comparison strip pair rows +(0298), leftover residual on pair segments (0277), leftover residual +disclosure (0162), 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 residual when formatLeftoverMapResidual returns a usable -badge, next to leftover-map unexplained leftover `U`. Use the distinct accessible -name `leftover map comparison graphic residual {label}` so the graphic caption is -not the strip badge (`Leftover map comparison residual`) and is not the -period-report graphic caption (`leftover residual {label}`). A missing or -non-finite `R` omits that leftover-map comparison graphic residual caption and -keeps leftover-map unexplained leftover `U` when formatLeftoverMapUnexplained -returns a usable badge, leftover-map cross share `x` when +with persisted leftover residual when formatLeftoverMapResidual +returns a usable badge, next to leftover-map unexplained leftover `U`. Use the +distinct accessible name `leftover map comparison graphic leftover residual {label}` +so the graphic caption is not the strip badge (`Leftover map comparison residual`) +and is not the period-report graphic caption (`leftover residual {label}`). +A missing or non-finite `R` omits that leftover-map comparison graphic +leftover residual caption and keeps 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 @@ -144,20 +174,22 @@ 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 observed `Y` when finite, -leftover-map reconstruction `R̂` when finite, leftover-map distance `d`, plus the -strip leftover residual badge. Rank-0 origin cells still name `R 0.00` when that -persisted leftover residual is finite. A finite negative leftover is shown, never -clamped. Do not invent `R` from `Y` and `E`, from `U` and `R̂`, leftover-map -reconstruction, leftover-map unexplained leftover, leftover-map distance, plotted -coordinates, leftover-map rank, leftover-map axis share, leftover-map post +leftover-map reconstruction `R̂` when +finite, leftover-map distance `d`, plus the strip leftover residual badge. +Rank-0 origin cells still name `R 0.00` when that persisted leftover +residual is finite. A finite negative leftover is shown, never clamped. Do not invent `R` from +`Y` and `E`, from `U` and `R̂`, 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 residual omits independently of 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 residual omits leftover-map -comparison graphic residual and keeps 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 +leftover-map cross share captions, +unexplained leftover share captions, explained leftover share captions, reconstruction captions, +coverage notes, and leftover-map distance. A missing leftover +residual omits leftover-map comparison graphic leftover residual and keeps +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. @@ -173,13 +205,14 @@ 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 residual -on that graphic when formatLeftoverMapResidual returns a usable badge. Rank-0 -unused axes still plot at the origin and still name `R 0.00` when that persisted -leftover residual 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 +the leftover-map graphic of persisted `ξ` / `ζ` also name leftover +residual on that graphic when formatLeftoverMapResidual +returns a usable badge. Rank-0 unused axes still plot at +the origin and still name `R 0.00` when that persisted leftover +residual 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 @@ -236,12 +269,12 @@ 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. Residual after IRT -main effects is `R = Y − E[Y|θ, item]`. Grouping comparison leftover residual -captions that persisted leftover on the grouping comparison leftover-map +`α_j − β_i`; typically `p = 2` for the interaction map. Residual after +IRT main effects is `R = Y − E[Y|θ, item]`. Grouping comparison leftover +residual captions that persisted leftover on the grouping comparison leftover-map graphic only when formatLeftoverMapResidual returns a usable badge. -Rank-0 unused axes still name `R 0.00` when that leftover residual is stored. -A finite negative leftover is shown, never clamped.) +Rank-0 unused axes still name `R 0.00` when that leftover +residual 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), diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 5e45bfecf..5594209eb 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,23 +1,24 @@ # Product & Technical Gap Baseline -> Exact-head loop overlay: 2026-08-31 08:19 KST. Protected `main` is +> 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 > graphic (v2.71.0 / ADR 0314) is `935cdea`, stacked on #853 @ `c4f79bc`. Independent > APPROVE is missing; Copilot review requested this cycle. Next buyer increment on this > cycle: leftover residual on the grouping comparison leftover-map > graphic (ADR 0315 / v2.72.0) delivered locally on -> `feat/leftover-map-compare-plot-residual-v2720`. Caption persisted leftover residual -> `R` on that comparison graphic from already-named leftover_residual. Distinct -> accessible name `leftover map comparison graphic residual {label}`. Omit when -> formatLeftoverMapResidual fail-closes. Do not caption leftover observed on that -> comparison graphic with a distinct name this increment. Do not invent `R` from -> `Y` and `E`, from `U` and `R̂`, leftover-map reconstruction, leftover-map unexplained leftover, +> `feat/leftover-map-compare-plot-residual-v2720`. Caption persisted leftover +> residual `R` on that comparison graphic from already-named +> leftover_residual. Distinct accessible name `leftover map comparison graphic +> leftover residual {label}`. Omit when formatLeftoverMapResidual fail-closes. +> Do not caption leftover observed on that comparison graphic with a distinct name this +> increment. Do not invent `R` from `Y` and `E`, from `U` and `R̂`, 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 #854. 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 07:21 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index b6ea4cf58..f7f212f1f 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -4570,7 +4570,7 @@ describe("App, authenticated", () => { ).toHaveLength(1); expect( within(screen.getByLabelText("Grouping comparison")).getByLabelText( - "leftover map comparison graphic residual R +0.40", + "leftover map comparison graphic leftover residual R +0.40", ), ).toHaveTextContent("R +0.40"); expect( diff --git a/frontend/src/components/LeftoverMapPlot.test.tsx b/frontend/src/components/LeftoverMapPlot.test.tsx index 9d864932c..23902ab2b 100644 --- a/frontend/src/components/LeftoverMapPlot.test.tsx +++ b/frontend/src/components/LeftoverMapPlot.test.tsx @@ -1888,10 +1888,10 @@ describe("LeftoverMapPlot", () => { />, ); expect( - screen.getByLabelText("leftover map comparison graphic residual R +0.40"), + screen.getByLabelText("leftover map comparison graphic leftover residual R +0.40"), ).toHaveTextContent("R +0.40"); expect( - screen.getByLabelText("leftover map comparison graphic residual R −1.10"), + screen.getByLabelText("leftover map comparison graphic leftover residual R −1.10"), ).toHaveTextContent("R −1.10"); expect(screen.queryByLabelText("leftover residual R +0.40")).not.toBeInTheDocument(); expect(screen.queryByLabelText("leftover residual R −1.10")).not.toBeInTheDocument(); @@ -1914,7 +1914,7 @@ describe("LeftoverMapPlot", () => { expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); }); - it("omits leftover-map comparison graphic residual when R is missing or not usable", () => { + it("omits leftover-map comparison graphic leftover residual when R is missing or not usable", () => { render( { variant="comparison" />, ); - expect(screen.queryByLabelText(/leftover map comparison graphic residual R/)).not.toBeInTheDocument(); + expect(screen.queryByLabelText(/leftover map comparison graphic leftover residual R/)).not.toBeInTheDocument(); expect(screen.queryByLabelText(/^leftover residual R/)).not.toBeInTheDocument(); expect( screen.getByLabelText("leftover map comparison graphic unexplained leftover U +0.05"), @@ -2001,7 +2001,7 @@ describe("LeftoverMapPlot", () => { />, ); expect( - screen.getByLabelText("leftover map comparison graphic residual R 0.00"), + screen.getByLabelText("leftover map comparison graphic leftover residual R 0.00"), ).toHaveTextContent("R 0.00"); expect(screen.queryByLabelText("leftover residual R 0.00")).not.toBeInTheDocument(); expect( diff --git a/frontend/src/components/LeftoverMapPlot.tsx b/frontend/src/components/LeftoverMapPlot.tsx index b94785aad..d6f26adb8 100644 --- a/frontend/src/components/LeftoverMapPlot.tsx +++ b/frontend/src/components/LeftoverMapPlot.tsx @@ -160,7 +160,7 @@ function leftoverMapPlotAxisText( * with distinct leftover map comparison graphic unexplained leftover * labels. ADR 0315 captions leftover residual on that * comparison graphic from already-named leftover residual - * with distinct leftover map comparison graphic residual + * with distinct leftover map comparison graphic leftover residual * labels. * Never invent a leftover score. */ diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index 6d87ff504..f0e60a47c 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -122,8 +122,9 @@ export type LeftoverPairListProps = { * pair list. ADR 0313 captions leftover-map * cross share on that grouping comparison leftover-map graphic, not this * 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 + * 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 * 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 0bf278ff6..09d6d6115 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -95,7 +95,7 @@ describe("i18n", () => { "Leftover map comparison cross share", "leftover map comparison graphic unexplained leftover {label}", "Leftover map comparison unexplained leftover", - "leftover map comparison graphic residual {label}", + "leftover map comparison graphic leftover residual {label}", "Leftover map comparison residual", "Leftover map comparison observed", "Leftover map comparison expected", @@ -584,13 +584,13 @@ describe("i18n", () => { ["ko", "잔여 지도 비교 그림 잔여 R +0.40"], ["zh", "残差地图比较图形残差 R +0.40"], ["ja", "残差マップの比較図残差 R +0.40"], - ["vi", "phần dư đồ họa so sánh R +0.40"], + ["vi", "phần dư đồ họa so sánh bản đồ phần dư R +0.40"], ] as const)( - "formats leftover map comparison graphic residual in %s", + "formats leftover map comparison graphic leftover residual in %s", (locale, expected) => { setLocale(locale); expect( - tf("leftover map comparison graphic residual {label}", { + tf("leftover map comparison graphic leftover residual {label}", { label: "R +0.40", }), ).toBe(expected); diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 7c7befc08..7452be173 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -287,7 +287,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison cross share": "잔여 지도 비교 교차 점유율", "leftover map comparison graphic unexplained leftover {label}": "잔여 지도 비교 그림 미설명 잔여 {label}", "Leftover map comparison unexplained leftover": "잔여 지도 비교 미설명 잔여", - "leftover map comparison graphic residual {label}": "잔여 지도 비교 그림 잔여 {label}", + "leftover map comparison graphic leftover residual {label}": "잔여 지도 비교 그림 잔여 {label}", "Leftover map comparison residual": "잔여 지도 비교 잔여", "Leftover map comparison observed": "잔여 지도 비교 관측", "Leftover map comparison expected": "잔여 지도 비교 기대", @@ -925,7 +925,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison cross share": "残差地图比较交叉份额", "leftover map comparison graphic unexplained leftover {label}": "残差地图比较图形未解释残差 {label}", "Leftover map comparison unexplained leftover": "残差地图比较未解释残差", - "leftover map comparison graphic residual {label}": "残差地图比较图形残差 {label}", + "leftover map comparison graphic leftover residual {label}": "残差地图比较图形残差 {label}", "Leftover map comparison residual": "残差地图比较残差", "Leftover map comparison observed": "残差地图比较观测", "Leftover map comparison expected": "残差地图比较期望", @@ -1584,7 +1584,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison cross share": "残差マップの比較交差割合", "leftover map comparison graphic unexplained leftover {label}": "残差マップの比較図未説明残差 {label}", "Leftover map comparison unexplained leftover": "残差マップの比較未説明残差", - "leftover map comparison graphic residual {label}": "残差マップの比較図残差 {label}", + "leftover map comparison graphic leftover residual {label}": "残差マップの比較図残差 {label}", "Leftover map comparison residual": "残差マップの比較残差", "Leftover map comparison observed": "残差マップの比較観測", "Leftover map comparison expected": "残差マップの比較期待", @@ -2222,7 +2222,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison cross share": "Phần giao so sánh bản đồ phần dư", "leftover map comparison graphic unexplained leftover {label}": "phần dư chưa giải thích đồ họa so sánh {label}", "Leftover map comparison unexplained leftover": "Phần dư chưa giải thích so sánh", - "leftover map comparison graphic residual {label}": "phần dư đồ họa so sánh {label}", + "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 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ư", diff --git a/frontend/src/leftoverMapPlotLayout.test.ts b/frontend/src/leftoverMapPlotLayout.test.ts index c03d9b215..44a55a220 100644 --- a/frontend/src/leftoverMapPlotLayout.test.ts +++ b/frontend/src/leftoverMapPlotLayout.test.ts @@ -1276,7 +1276,7 @@ describe("leftover map comparison graphic labels", () => { it("stays distinct from leftover residual copy", () => { expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).toBe( - "leftover map comparison graphic residual {label}", + "leftover map comparison graphic leftover residual {label}", ); expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).not.toBe( "leftover residual {label}", diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index d4466c312..77728d5ca 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -41,7 +41,7 @@ * reconstruction on that graphic (ADR 0310), grouping comparison leftover-map * explained leftover share on that graphic (ADR 0311), grouping comparison leftover-map * unexplained leftover share on that graphic (ADR 0312), grouping comparison leftover-map - * cross share on that graphic (ADR 0313), 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 * strip. ADR 0304 reuses this graphic layout on the grouping comparison @@ -61,8 +61,8 @@ * cross share on that comparison graphic from already-named leftover-map * cross share. ADR 0314 captions leftover-map unexplained leftover on that * comparison graphic from already-named leftover-map unexplained leftover. - * ADR 0315 captions leftover residual on that comparison graphic from already-named - * leftover residual. + * ADR 0315 captions leftover residual on that comparison graphic from + * already-named leftover residual. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; diff --git a/frontend/src/leftoverResidual.test.ts b/frontend/src/leftoverResidual.test.ts index 3f07fba6a..d742b333a 100644 --- a/frontend/src/leftoverResidual.test.ts +++ b/frontend/src/leftoverResidual.test.ts @@ -34,9 +34,9 @@ describe("formatLeftoverMapResidual", () => { expect(LEFTOVER_MAP_COMPARE_RESIDUAL_LABEL).not.toBe("leftover residual {label}"); }); - it("keeps the grouping comparison graphic residual label distinct from the graphic and strip labels", () => { + it("keeps the grouping comparison graphic leftover residual label distinct from the graphic and strip labels", () => { expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).toBe( - "leftover map comparison graphic residual {label}", + "leftover map comparison graphic leftover residual {label}", ); expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).not.toBe("leftover residual {label}"); expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL).not.toBe( diff --git a/frontend/src/leftoverResidual.ts b/frontend/src/leftoverResidual.ts index bc3015d79..69bcd28d2 100644 --- a/frontend/src/leftoverResidual.ts +++ b/frontend/src/leftoverResidual.ts @@ -24,4 +24,4 @@ export const LEFTOVER_MAP_COMPARE_RESIDUAL_LABEL = "Leftover map comparison residual"; export const LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RESIDUAL = - "leftover map comparison graphic residual {label}"; + "leftover map comparison graphic leftover residual {label}"; From e8f0ca2c10584fad578c4cd7e6493025bd565020 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 13:57:53 +0900 Subject: [PATCH 3/3] test(adr): keep draft ADR 0315 proposed --- tests/test_adr_0315_governance.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/test_adr_0315_governance.py diff --git a/tests/test_adr_0315_governance.py b/tests/test_adr_0315_governance.py new file mode 100644 index 000000000..06b8b63e8 --- /dev/null +++ b/tests/test_adr_0315_governance.py @@ -0,0 +1,13 @@ +"""Governance regression for the draft ADR 0315 decision state.""" + +from pathlib import Path + + +ADR_PATH = Path(__file__).parents[1] / "docs/adr/0315-leftover-map-compare-plot-residual.md" + + +def test_draft_adr_0315_remains_proposed() -> None: + """Keep the unmerged decision proposed until independent acceptance exists.""" + text = ADR_PATH.read_text(encoding="utf-8") + assert "**Decision status:** Proposed" in text + assert "**Decision status:** Accepted" not in text