diff --git a/AGENTS.md b/AGENTS.md index 4944e8279..d90cee39a 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) 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) 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 @@ -464,6 +464,12 @@ compares leftover pairs can match the pair-row `E` badge; a missing or non-finit `E` omits that leftover-map comparison expected badge and does not invent `E` from `Y` and `R` or from leftover-map distance. The strip does not gain the leftover-map graphic. +ADR 0301 captions grouping comparison leftover-pair buttons with persisted leftover-map +rank through formatLeftoverMapRank so a buyer who +compares leftover pairs can match the pair-row `rank` badge; a missing, negative, or +non-integer rank omits that leftover-map comparison rank badge and does not invent +rank from plotted coordinates, leftover-map distance, or the count of unused axes. +The strip does not gain the leftover-map graphic. When `R`, `R̂`, `U`, `x`, `s`, and `e` are finite, `e + s + x = 1`. When `Y`, `E`, and `R` are finite, `Y − E = R`. When `R`, `R̂`, and `U` are @@ -502,6 +508,8 @@ Grouping comparison leftover observed (ADR 0299) captions grouping comparison le buttons with persisted leftover observed `Y`. Grouping comparison leftover expected (ADR 0300) captions grouping comparison leftover-pair buttons with persisted leftover expected `E`. +Grouping comparison leftover-map rank (ADR 0301) captions grouping comparison leftover-pair +buttons with persisted leftover-map rank. 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 d2e752dc4..d9a09dd66 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -691,6 +691,8 @@ ADR 0299 captions grouping comparison leftover-pair buttons with persisted lefto observed `Y` through formatLeftoverMapObserved. ADR 0300 captions grouping comparison leftover-pair buttons with persisted leftover expected `E` through formatLeftoverMapExpected. +ADR 0301 captions grouping comparison leftover-pair buttons with persisted leftover-map +rank through formatLeftoverMapRank. 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 @@ -718,8 +720,9 @@ incomplete item coverage (ADR 0292), leftover-map reconstruction `R̂` (ADR 0296), leftover-map unexplained leftover `U` (ADR 0297), leftover residual `R` (ADR 0298), leftover observed `Y` -(ADR 0299), and leftover expected `E` -(ADR 0300); +(ADR 0299), leftover expected `E` +(ADR 0300), and leftover-map rank +(ADR 0301); `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.58.0-leftover-map-compare-rank.md b/CHANGELOG.d/2.58.0-leftover-map-compare-rank.md new file mode 100644 index 000000000..c73bbfa1c --- /dev/null +++ b/CHANGELOG.d/2.58.0-leftover-map-compare-rank.md @@ -0,0 +1,18 @@ +## 2.58.0 — Leftover-map rank on grouping comparison strip pair rows + +- Caption leftover-map rank on grouping comparison strip leftover-pair + buttons through formatLeftoverMapRank (ADR 0301). After `make seed`, + closest and farthest leftover pairs sit above the member list with grouping + comparison leftover-pair buttons that match leftover-map rank on the + pair row; click a post marker or a pair button opens that post. A missing, + negative, or non-integer rank omits that leftover-map comparison rank badge + and keeps leftover expected `E` when finite, leftover observed `Y` when + finite, leftover residual `R` when finite, leftover-map unexplained leftover + `U` when finite, leftover-map cross share `x` when finite, leftover-map + unexplained leftover share `s` when finite, leftover-map explained leftover + share `e` when finite, leftover-map reconstruction `R̂` when finite, + leftover-map distance `d`, plus the strip coverage notes. Rank-0 origin + cells still name `rank 0`. Do not invent rank from plotted coordinates, + leftover-map distance, or the count of unused axes. A finite negative leftover + is shown, never clamped. The strip does not gain the leftover-map graphic. + Never invent a leftover score. Never invent a theta. No new columns. diff --git a/CHANGELOG.md b/CHANGELOG.md index f800abb60..e629ee998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,61 @@ All notable changes to this project are documented here. Format follows ### Added +- Period leftover pairs now caption grouping comparison leftover-pair + buttons with persisted leftover-map rank through + formatLeftoverMapRank (ADR 0301 / v2.58.0). After `make seed`, + closest and farthest leftover pairs sit above the member list with the + Gabriel biplot of already-named coordinates, leftover-map axis share when + finite, axis ticks that match `ξ (x, y) ζ (x, y)` on the pair row, pair + segments that match `d` on the pair row, pair segments that match `R̂` on + the pair row, pair segments that match `R̂²/R²` on the pair row, pair + segments that match `U²/R²` on the pair row, pair segments that match + `2R̂U/R²` on the pair row, pair segments that match `U` on the pair row, + pair segments that match `R` on the pair row, pair segments that match + `Y` on the pair row, pair segments that match `E` on the pair row, pair + segments that match leftover-map rank on the pair row, a plot caption + that matches leftover-map complete-case coverage above the pair list, a + plot caption that matches leftover-map item complete-case coverage, a + plot caption that matches leftover-map incomplete post coverage, a plot + caption that matches leftover-map incomplete item coverage, a pair-list + note that matches leftover-map post complete-case coverage, a pair-list + note that matches leftover-map item complete-case coverage, a pair-list + note that matches leftover-map incomplete post coverage, a pair-list + note that matches leftover-map incomplete item coverage, a grouping + comparison note that matches leftover-map post complete-case coverage, + a grouping comparison note that matches leftover-map item + complete-case coverage, a grouping comparison note that matches + leftover-map incomplete post coverage, a grouping comparison note + that matches leftover-map incomplete item coverage, grouping comparison + leftover-pair buttons that match leftover-map reconstruction `R̂` on the + pair row, grouping comparison leftover-pair buttons that match + leftover-map explained leftover share `e` on the pair row, grouping + comparison leftover-pair buttons that match leftover-map unexplained + leftover share `s` on the pair row, grouping comparison leftover-pair + buttons that match leftover-map cross share `x` on the pair row, + grouping comparison leftover-pair buttons that match leftover-map + unexplained leftover `U` on the pair row, grouping comparison leftover-pair + buttons that match leftover residual `R` on the pair row, grouping + comparison leftover-pair buttons that match leftover observed `Y` on the + pair row, grouping comparison leftover-pair buttons that match leftover + expected `E` on the pair row, and grouping comparison leftover-pair buttons + that match leftover-map rank on the pair row; click a post marker or a pair + button opens that post. A missing, negative, or non-integer rank omits that + leftover-map comparison rank badge and keeps leftover expected `E` when + finite, leftover observed `Y` when finite, leftover residual `R` when + finite, leftover-map unexplained leftover `U` when finite, leftover-map + cross share `x` when finite, leftover-map unexplained leftover share `s` + when finite, leftover-map explained leftover share `e` when finite, + leftover-map reconstruction `R̂` when finite, leftover-map distance `d`, + plus the strip coverage notes. Rank-0 origin cells still name `rank 0`. + Do not invent rank from plotted coordinates, leftover-map distance, + leftover expected, leftover observed, leftover residual, leftover-map + reconstruction, leftover-map unexplained leftover, leftover-map post + coverage, leftover-map item coverage, leftover-map incomplete post coverage, + leftover-map incomplete item coverage, or the count of unused axes. A finite + negative leftover is shown, never clamped. The strip does not gain the + leftover-map graphic. Never invent a leftover score or a theta. No new columns. + - Period leftover pairs now caption grouping comparison leftover-pair buttons with persisted leftover expected `E` through formatLeftoverMapExpected (ADR 0300 / v2.57.0). After `make seed`, diff --git a/CLAUDE.md b/CLAUDE.md index 7e85d2649..93024ff65 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), 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), 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/0301-leftover-map-compare-rank.md b/docs/adr/0301-leftover-map-compare-rank.md new file mode 100644 index 000000000..e55d8e797 --- /dev/null +++ b/docs/adr/0301-leftover-map-compare-rank.md @@ -0,0 +1,227 @@ +# ADR 0301 — Name leftover-map rank on grouping comparison strip pair rows + +**Decision status:** Accepted +**Date:** 2026-08-31 + +Amends leftover pairs on the grouping comparison strip +([ADR 0149](0149-leftover-pairs-on-comparison-strip.md)), leftover-map rank +([ADR 0164](0164-leftover-map-rank.md)), leftover-map rank on pair segments +([ADR 0280](0280-leftover-map-segment-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 residual +on grouping comparison strip pair rows +([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 cross share +on grouping comparison strip pair rows +([ADR 0296](0296-leftover-map-compare-cross-share.md)), leftover-map unexplained +leftover share on grouping comparison strip pair rows +([ADR 0295](0295-leftover-map-compare-unexplained-share.md)), leftover-map +explained leftover share on grouping comparison strip pair rows +([ADR 0294](0294-leftover-map-compare-explained-share.md)), leftover-map +reconstruction on grouping comparison strip pair rows +([ADR 0293](0293-leftover-map-compare-reconstruction.md)), leftover-map +incomplete item coverage on the grouping comparison strip +([ADR 0292](0292-leftover-map-compare-incomplete-item.md)), leftover-map +incomplete post coverage on the grouping comparison strip +([ADR 0291](0291-leftover-map-compare-incomplete-post.md)), leftover-map item +complete-case coverage on the grouping comparison strip +([ADR 0290](0290-leftover-map-compare-item-coverage.md)), leftover-map +complete-case coverage on the grouping comparison strip +([ADR 0289](0289-leftover-map-compare-coverage.md)), leftover residual +disclosure ([ADR 0162](0162-leftover-residual-disclosure.md)), leftover +observed `Y` / expected `E` ([ADR 0163](0163-leftover-observed-expected.md)), leftover-map unexplained leftover persistence ([ADR 0182](0182-leftover-map-unexplained.md)), +and leftover-map reconstruction persistence +([ADR 0201](0201-leftover-map-reconstruction.md)). Independent of leftover-map +post complete-case coverage fail-closed on the pair list +([ADR 0288](0288-leftover-map-list-post-coverage-helper.md)), leftover-map +incomplete item coverage on the pair list +([ADR 0287](0287-leftover-map-list-incomplete-item.md)), leftover-map incomplete +post coverage on the pair list +([ADR 0286](0286-leftover-map-list-incomplete-post.md)), leftover-map item +complete-case coverage on the pair list +([ADR 0285](0285-leftover-map-list-item-coverage.md)), leftover-map incomplete +item coverage on the graphic display +([ADR 0284](0284-leftover-map-plot-incomplete-item.md)), leftover-map incomplete +post coverage on the graphic display +([ADR 0283](0283-leftover-map-plot-incomplete.md)), leftover-map item +complete-case coverage on the graphic display +([ADR 0282](0282-leftover-map-plot-item-coverage.md)), leftover-map complete-case +coverage on the graphic display +([ADR 0281](0281-leftover-map-plot-coverage.md)), leftover expected on pair +segments ([ADR 0279](0279-leftover-map-segment-expected.md)), leftover observed +on pair segments ([ADR 0278](0278-leftover-map-segment-observed.md)), leftover +residual on pair segments ([ADR 0277](0277-leftover-map-segment-residual.md)), +leftover-map unexplained leftover on pair segments +([ADR 0276](0276-leftover-map-segment-unexplained-leftover.md)), leftover-map +cross share on pair segments ([ADR 0275](0275-leftover-map-segment-cross-share.md)), +leftover-map unexplained leftover share on pair segments +([ADR 0274](0274-leftover-map-segment-unexplained-share.md)), leftover-map +explained leftover share on pair segments +([ADR 0273](0273-leftover-map-segment-explained-share.md)), leftover-map +reconstruction on pair segments +([ADR 0272](0272-leftover-map-segment-reconstruction.md)), leftover-map +distance on pair segments ([ADR 0271](0271-leftover-map-segment-distance.md)), +leftover-map coordinate ticks ([ADR 0270](0270-leftover-map-coordinate-ticks.md)), +leftover-map axis share on the graphic display +([ADR 0269](0269-leftover-map-axis-share-plot.md)), leftover-map +cross share persistence ([ADR 0185](0185-leftover-map-cross-share.md)), leftover-map +unexplained leftover share persistence +([ADR 0233](0233-leftover-map-unexplained-share.md)), leftover-map explained leftover +share persistence ([ADR 0266](0266-leftover-map-explained-share.md)), and leftover-map +axis share persistence ([ADR 0148](0148-leftover-map-axis-share.md)). + +## Context + +ADR 0164 already persists leftover-map rank — the number of Gabriel +singular values above the leftover singular floor — on leftover pair +rows. A rank-0 residual still emits a stable closest/farthest pair so +`make seed` is not empty; the stored distance is then zero, not a +fabricated interaction. ADR 0280 already names persisted leftover-map +rank on leftover-map graphic-display pair segments. ADR 0300 already +captions persisted leftover expected `E` on grouping comparison leftover-pair +buttons. The strip pair button still does not name leftover-map rank, so a +buyer who compares leftover pairs can treat leftover expected `E`, leftover +observed `Y`, leftover residual `R`, leftover-map unexplained leftover `U`, +leftover-map cross share `x`, leftover-map unexplained leftover share `s`, +leftover-map explained leftover share `e`, leftover-map reconstruction `R̂`, +or leftover-map distance `d` as a two-axis leftover map even after the pair +list and graphic already name `rank 0`. Hiding rank on the strip lets a +rank-0 origin cell with `d 0.00` and `E 0.00` be read as leftover structure +without a next action. + +This increment captions persisted leftover-map rank on each grouping +comparison leftover-pair button through formatLeftoverMapRank. It does not +add columns. It does not recompute rank from plotted coordinates, leftover-map +distance, leftover expected, leftover observed, leftover residual, leftover-map +reconstruction, leftover-map explained leftover share, leftover-map unexplained +leftover share, leftover-map cross share, leftover-map unexplained leftover, or +the count of unused axes. It does not persist leftover-map inner product, +cosine, or length as separate columns. Do not invent a leftover score. Do not +invent a theta. + +The dashboard stack already used neighbouring leftover facts under other +numbers. This protected increment uses **0301** so it does not collide with +leftover expected on grouping comparison strip pair rows (0300), leftover +observed on grouping comparison strip pair rows (0299), leftover residual +on grouping comparison strip pair rows (0298), leftover-map unexplained leftover +on grouping comparison strip pair rows (0297), leftover-map cross share on grouping +comparison strip pair rows (0296), leftover-map unexplained leftover share on grouping +comparison strip pair rows (0295), leftover-map explained leftover share on grouping +comparison strip pair rows (0294), leftover-map reconstruction on grouping comparison +strip pair rows (0293), leftover-map rank on pair segments (0280), leftover expected on +pair segments (0279), leftover observed on pair segments (0278), leftover observed +`Y` / expected `E` (0163), leftover residual on pair segments (0277), leftover residual +disclosure (0162), leftover-map unexplained leftover on pair segments (0276), leftover-map +unexplained leftover persistence (0182), leftover-map reconstruction on pair segments +(0272), leftover-map reconstruction persistence (0201), leftover-map rank persistence +(0164), or the dashboard stacks. + +## Decision + +On the grouping comparison strip, caption each leftover pair button with +the same persisted leftover-map rank formatter as the pair-row `rank` badge, +next to leftover expected `E`, leftover observed `Y`, leftover residual `R`, +leftover-map unexplained leftover `U`, leftover-map cross share `x`, leftover-map +unexplained leftover share `s`, leftover-map explained leftover share `e`, +leftover-map reconstruction `R̂`, and leftover-map distance `d`. Use the distinct +accessible name `Leftover map comparison rank` so the strip badge is not the +graphic caption (`leftover-map rank {label}`). A missing rank, a negative rank, +or a non-integer rank omits that leftover-map comparison rank badge and keeps +leftover expected `E` when finite, leftover observed `Y` when finite, leftover +residual `R` when finite, leftover-map unexplained leftover `U` when finite, +leftover-map cross share `x` when finite, leftover-map unexplained leftover share +`s` when finite, leftover-map explained leftover share `e` when finite, leftover-map +reconstruction `R̂` when finite, leftover-map distance `d`, the strip leftover-map +post coverage note, leftover-map item coverage note, leftover-map incomplete post +note, leftover-map incomplete item note, leftover pairs, and any leftover-map +captions on the pair list and graphic. Rank-0 origin cells still name `rank 0` +when that persisted leftover-map rank is a non-negative integer. A finite negative +leftover on neighbouring badges is shown, never clamped. Do not invent rank from +plotted coordinates, leftover-map distance, leftover expected, leftover observed, +leftover residual, leftover-map reconstruction, leftover-map unexplained leftover, +leftover-map explained leftover share, leftover-map unexplained leftover share, +leftover-map cross share, leftover-map post coverage, leftover-map item coverage, +leftover-map incomplete post coverage, leftover-map incomplete item coverage, or +the count of unused axes. Do not add the leftover-map graphic to the strip. Click +a leftover pair on the strip to open that post. + +Do not add SQL. Do not edit shipped migrations. Do not persist inner +product, cosine, or length as separate columns. Do not invent a leftover +score. Do not invent a theta. + +## Consequences + +After `make seed`, the grouping comparison strip names persisted leftover-map +post complete-case coverage, persisted leftover-map item complete-case +coverage, persisted leftover-map incomplete post coverage, and persisted +leftover-map incomplete item coverage on each grouping row when +leftoverMapCoverageCounts / leftoverMapItemCoverageCounts / +leftoverMapIncompletePostCount / leftoverMapIncompleteItemCount return usable +integers, then names leftover pairs with leftover-map distance `d`, persisted +leftover-map reconstruction `R̂` when finite, persisted leftover-map +explained leftover share `e` when finite, persisted leftover-map unexplained +leftover share `s` when finite, persisted leftover-map cross share `x` +when finite, persisted leftover-map unexplained leftover `U` when finite, +persisted leftover residual `R` when finite, persisted leftover observed +`Y` when finite, persisted leftover expected `E` when finite, and persisted +leftover-map rank when that rank is a non-negative integer. +Closest and farthest leftover pairs still sit above the member +list with the leftover-map graphic display; click a post marker or a pair +button opens that post. +Hidden posts stay hidden. When `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`. + +## Related + +Independent of leftover interaction-map persistence, leftover-criterion +evaluation landing, leftover residual disclosure, leftover-map +complete-case coverage persistence, leftover-map axis share persistence, +leftover pairs on the grouping comparison strip, leftover-map complete-case +coverage on the grouping comparison strip, leftover-map item complete-case +coverage on the grouping comparison strip, leftover-map incomplete post +coverage on the grouping comparison strip, leftover-map incomplete item +coverage on the grouping comparison strip, leftover-map reconstruction on +grouping comparison strip pair rows, leftover-map explained leftover share +on grouping comparison strip pair rows, leftover-map unexplained leftover +share on grouping comparison strip pair rows, leftover-map cross share on +grouping comparison strip pair rows, leftover-map 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 inner product, leftover-map cosine, leftover-map +length, leftover-map graphic display, 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 cross +share on pair segments, leftover-map cross share persistence, leftover-map +unexplained leftover share on pair segments, leftover-map unexplained leftover +share persistence, leftover-map explained leftover share on pair segments, +leftover-map explained leftover share persistence, leftover-map reconstruction +on pair segments, leftover-map reconstruction persistence, leftover-map item +complete-case coverage on the graphic display, leftover-map item complete-case +coverage on the pair list, leftover-map incomplete post coverage on the graphic +display, leftover-map incomplete post coverage on the pair list, leftover-map +incomplete item coverage on the graphic display, leftover-map incomplete item +coverage on the pair list, leftover-map post complete-case coverage fail-closed +on the pair list, and leftover-map rank persistence. + +## 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. Leftover-map rank +is the number of Gabriel singular values above the leftover singular floor +after IRT main effects. Grouping comparison leftover-map rank names that +persisted rank on the strip pair row only when formatLeftoverMapRank returns +a usable badge. Rank-0 origin cells still name `rank 0`.) + +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 ecaecac1d..e61ebedf6 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,23 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-08-31 02:10 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not +> self-approve. #837 leftover expected `E` on grouping comparison +> leftover-pair buttons (v2.57.0 / ADR 0300) is `e6638a8`, stacked on +> #836 @ `ada0c86`. Next buyer increment on this cycle: leftover-map rank +> on grouping comparison leftover-pair buttons (ADR 0301 / v2.58.0) +> delivered locally on `feat/leftover-map-compare-rank-v2580`. Caption +> each strip leftover-pair button through formatLeftoverMapRank with +> distinct aria-label `Leftover map comparison rank`. UI-only; no new +> columns. Missing, negative, or non-integer rank omits that badge +> independently of `E`, `Y`, `R`, `U`, `x`, `s`, `e`, `R̂`, `d`, and +> coverage notes. Rank-0 origin cells still name `rank 0`. Do not invent +> rank from plotted coordinates, leftover-map distance, or the count of +> unused axes. Stack onto #837. Leave open for independent review. Do not +> squash-merge. Issues #79 and #87 stay open. #96 is already closed. Do +> not merge #808 without independent APPROVE. + + > Exact-head loop overlay: 2026-08-31 01:45 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not > self-approve. #836 leftover observed `Y` on grouping comparison diff --git a/frontend/package.json b/frontend/package.json index bc6359902..25c6ec845 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.57.0", + "version": "2.58.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index fbeb990df..6d0be1309 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -939,6 +939,7 @@ describe("App, authenticated", () => { leftover_map_unexplained: 0.05, observed_response: 2.4, expected_response: 2.0, + leftover_map_rank: 1, }, ], leftover_map_coverage: { @@ -4465,6 +4466,21 @@ describe("App, authenticated", () => { "Leftover map comparison expected", ), ).toHaveLength(1); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + /leftover-map rank/, + ), + ).not.toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getByLabelText( + "Leftover map comparison rank", + ), + ).toHaveTextContent("rank 1"); + expect( + within(screen.getByLabelText("Grouping comparison")).getAllByLabelText( + "Leftover map comparison rank", + ), + ).toHaveLength(1); expect( screen.getByRole("button", { name: /open leftover closest pair from comparison: specification revision requested/i, @@ -4510,6 +4526,11 @@ describe("App, authenticated", () => { name: /open leftover closest pair from comparison: specification revision requested/i, }), ).not.toHaveTextContent("E "); + expect( + screen.getByRole("button", { + name: /open leftover closest pair from comparison: specification revision requested/i, + }), + ).not.toHaveTextContent("rank "); expect( screen.getByRole("button", { name: "Compare Business unit (PU): Demo Report High, mean θ 0.81" }), ).toHaveTextContent("mean θ 0.81"); @@ -4569,6 +4590,11 @@ describe("App, authenticated", () => { name: /open leftover closest pair from comparison: public post/i, }), ).toHaveTextContent("E 2.00"); + expect( + screen.getByRole("button", { + name: /open leftover closest pair from comparison: public post/i, + }), + ).toHaveTextContent("rank 1"); await waitFor(() => expect(fetchMock).toHaveBeenCalledWith( expect.stringContaining("/api/reports/thread_group/2026-W02"), diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b89f26cd5..70382f62b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -167,6 +167,10 @@ import { LEFTOVER_MAP_COMPARE_EXPECTED_LABEL, LEFTOVER_MAP_COMPARE_OBSERVED_LABEL, } from "./leftoverObservedExpected"; +import { + formatLeftoverMapRank, + LEFTOVER_MAP_COMPARE_RANK_LABEL, +} from "./leftoverMapRank"; import "./App.css"; const AdminPanel = lazy(() => import("./components/AdminPanel").then((module) => ({ default: module.AdminPanel }))); @@ -4107,6 +4111,7 @@ function ReportsPanel({ const residual = formatLeftoverMapResidual(pair.leftover_residual); const observed = formatLeftoverMapObserved(pair.observed_response); const expected = formatLeftoverMapExpected(pair.expected_response); + const rank = formatLeftoverMapRank(pair.leftover_map_rank); return (