From 9ba8535327bb2d42d5305d84017e6cd08a110c68 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 31 Aug 2026 00:14:37 +0000 Subject: [PATCH] feat(reports): leftover-map distance on grouping comparison graphic (v2.76.0) Caption persisted leftover-map distance d on the grouping comparison leftover-map graphic from already-named leftover_distance (ADR 0319). Comparison copy uses leftover map comparison graphic leftover-map distance {label} so it stays distinct from leftover-map distance {label}. A missing or non-finite d omits that caption and keeps leftover-map rank, leftover expected, leftover observed, leftover residual, leftover-map unexplained leftover, leftover-map reconstruction, and comparison graphic coverage notes. Rank-0 origin cells still name d 0.00 when that leftover-map distance is finite. Do not caption leftover-map coordinate ticks this increment. Do not invent leftover-map distance from plotted coordinates. Never invent a leftover score. Never invent a theta. No new columns. --- AGENTS.md | 12 ++ ARCHITECTURE.md | 6 +- ...76.0-leftover-map-compare-plot-distance.md | 15 ++ CHANGELOG.md | 15 ++ CLAUDE.md | 2 +- .../0318-leftover-map-compare-plot-rank.md | 8 +- ...0319-leftover-map-compare-plot-distance.md | 94 +++++++++++ docs/product-technical-gap-baseline.md | 17 ++ frontend/package.json | 2 +- frontend/src/App.test.tsx | 10 ++ .../src/components/LeftoverMapPlot.test.tsx | 158 ++++++++++++++++-- frontend/src/components/LeftoverMapPlot.tsx | 13 +- frontend/src/components/LeftoverPairList.tsx | 3 +- frontend/src/i18n.test.ts | 18 ++ frontend/src/i18n.ts | 4 + frontend/src/leftoverMapPlotLayout.test.ts | 16 ++ frontend/src/leftoverMapPlotLayout.ts | 8 +- pyproject.toml | 2 +- uv.lock | 2 +- 19 files changed, 378 insertions(+), 27 deletions(-) create mode 100644 CHANGELOG.d/2.76.0-leftover-map-compare-plot-distance.md create mode 100644 docs/adr/0319-leftover-map-compare-plot-distance.md diff --git a/AGENTS.md b/AGENTS.md index d571f5d26..5046d3148 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -620,6 +620,15 @@ and leftover-map comparison graphic incomplete items when those helpers return u leftover-map rank from plotted coordinates, leftover-map distance, leftover expected, leftover observed, leftover residual, leftover-map unexplained leftover, leftover-map reconstruction, leftover-map axis share, leftover-map post coverage, leftover-map item coverage, leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover pair count, or the count of unused axes. +ADR 0319 captions leftover-map distance on that grouping comparison leftover-map +graphic when formatLeftoverMapDistance returns a usable badge; a missing or +non-finite `d` omits that leftover-map comparison graphic leftover-map distance caption and keeps +leftover-map rank, leftover expected `E`, leftover observed `Y`, leftover residual `R`, leftover-map unexplained leftover `U`, leftover-map cross share `x`, leftover-map unexplained leftover share `s`, leftover-map explained leftover share `e`, leftover-map reconstruction `R̂`, leftover-map comparison graphic coverage, +leftover-map comparison graphic item coverage, leftover-map comparison graphic incomplete posts, +and leftover-map comparison graphic incomplete items when those helpers return usable integers. Do not invent +leftover-map distance from plotted coordinates, leftover-map rank, leftover expected, leftover observed, leftover residual, leftover-map unexplained leftover, leftover-map reconstruction, leftover-map axis share, leftover-map post coverage, leftover-map item coverage, +leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover pair +count, or the count of unused axes. When `R`, `R̂`, `U`, `x`, `s`, and `e` are finite, `e + s + x = 1`. When `Y`, `E`, and `R` are finite, `Y − E = R`. When `R`, `R̂`, and `U` are @@ -709,6 +718,9 @@ that grouping comparison leftover-map graphic with persisted leftover expected Grouping comparison leftover-map rank (ADR 0318) captions that grouping comparison leftover-map graphic with persisted leftover-map rank when formatLeftoverMapRank returns a usable badge. +Grouping comparison leftover-map distance (ADR 0319) captions +that grouping comparison leftover-map graphic with persisted leftover-map distance +`d` when formatLeftoverMapDistance 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 a800e9bd2..ab7bf2425 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -743,6 +743,9 @@ badge. ADR 0318 captions leftover-map rank on that grouping comparison leftover-map graphic when formatLeftoverMapRank returns a usable badge. +ADR 0319 captions leftover-map distance on that grouping +comparison leftover-map graphic when formatLeftoverMapDistance 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 @@ -790,7 +793,8 @@ graphic (ADR 0314), leftover residual on the grouping comparison leftover-map graphic (ADR 0315), leftover observed on the grouping comparison leftover-map graphic (ADR 0316), leftover expected on the grouping comparison leftover-map graphic (ADR 0317), leftover-map rank on the grouping comparison leftover-map -graphic (ADR 0318); +graphic (ADR 0318), leftover-map distance on the grouping comparison leftover-map +graphic (ADR 0319); `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.76.0-leftover-map-compare-plot-distance.md b/CHANGELOG.d/2.76.0-leftover-map-compare-plot-distance.md new file mode 100644 index 000000000..52c2cc8ea --- /dev/null +++ b/CHANGELOG.d/2.76.0-leftover-map-compare-plot-distance.md @@ -0,0 +1,15 @@ +## 2.76.0 — Leftover-map distance on grouping comparison leftover-map graphic + +- Caption leftover-map distance on the grouping comparison leftover-map + graphic (ADR 0319). After `make seed`, closest and farthest leftover pairs + sit above the member list with leftover-map distance `d` on that grouping + comparison leftover-map graphic when formatLeftoverMapDistance returns a usable + badge; click a post marker or a pair button opens that post. A missing or + non-finite `d` omits that leftover-map comparison graphic leftover-map + distance caption and keeps leftover-map rank. Rank-0 origin + cells still name `d 0.00` when that persisted leftover-map distance is finite. + Comparison copy uses `leftover map comparison graphic leftover-map distance {label}` + so it stays distinct from `leftover-map distance {label}`. This increment does not + caption leftover-map coordinate ticks on the comparison graphic with a distinct name. + Do not invent leftover-map distance from plotted coordinates. 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 50d1ad9c6..c9c68f5ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,21 @@ All notable changes to this project are documented here. Format follows ### Added +- Period leftover pairs now name leftover-map distance on the grouping + comparison leftover-map graphic (ADR 0319 / v2.76.0). After `make seed`, + closest and farthest leftover pairs sit above the member list with leftover-map + distance `d` on that grouping comparison leftover-map graphic when + formatLeftoverMapDistance returns a usable badge; click a post marker or a pair + button opens that post. A missing or non-finite `d` omits that leftover-map + comparison graphic leftover-map distance caption and keeps leftover-map rank. + Rank-0 origin cells still name `d 0.00` when that persisted leftover-map distance + is finite. Comparison copy uses + `leftover map comparison graphic leftover-map distance {label}` so it stays + distinct from `leftover-map distance {label}`. This increment does not caption leftover-map + coordinate ticks on the comparison graphic with a distinct name. Do not invent leftover-map + distance from plotted coordinates. A finite negative leftover is shown, never clamped. + Never invent a leftover score or a theta. No new columns. + - Period leftover pairs now name leftover-map rank on the grouping comparison leftover-map graphic (ADR 0318 / v2.75.0). After `make seed`, closest and farthest leftover pairs sit above the member list with leftover-map diff --git a/CLAUDE.md b/CLAUDE.md index 7a7e3b8c3..5a57c0253 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,7 +49,7 @@ cutoff. Global Ask optional `knowledge_cutoff` uses the same cover Create/start endpoint rules (ADR 0017 / 0021), tie-vs-miss similarity (ADR 0026), R&R catalog ids (ADR 0019 / 0027), leftover pairs -(ADR 0048–0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290 / 0291 / 0292 / 0293 / 0294 / 0295 / 0296 / 0297 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315 / 0316 / 0317 / 0318), occupational construct catalog search +(ADR 0048–0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290 / 0291 / 0292 / 0293 / 0294 / 0295 / 0296 / 0297 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315 / 0316 / 0317 / 0318 / 0319), 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/0318-leftover-map-compare-plot-rank.md b/docs/adr/0318-leftover-map-compare-plot-rank.md index 7120ba08c..0a9d8407d 100644 --- a/docs/adr/0318-leftover-map-compare-plot-rank.md +++ b/docs/adr/0318-leftover-map-compare-plot-rank.md @@ -3,6 +3,9 @@ **Decision status:** Accepted **Date:** 2026-08-31 +**Amended by:** [ADR 0319](0319-leftover-map-compare-plot-distance.md) +(leftover-map distance on the grouping comparison leftover-map graphic) + Amends leftover-map rank on graphic-display pair segments ([ADR 0280](0280-leftover-map-segment-rank.md)), leftover-map rank on grouping comparison strip pair rows @@ -75,8 +78,9 @@ Leftover-map rank omits independently of leftover expected captions and leftover A missing leftover-map rank omits leftover-map comparison graphic leftover-map rank and keeps a usable leftover expected caption and a usable leftover observed caption. -This increment does not caption leftover-map distance on the comparison graphic -with a distinct comparison-graphic name. That leftover-map distance already sits +This increment does not caption leftover-map coordinate ticks on the comparison graphic +with a distinct comparison-graphic name. Leftover-map distance on that comparison +graphic is [ADR 0319](0319-leftover-map-compare-plot-distance.md). That leftover-map distance already sits on the period-report graphic through ADR 0271. Do not add SQL migrations. Do not edit shipped migrations. Do not invent a leftover diff --git a/docs/adr/0319-leftover-map-compare-plot-distance.md b/docs/adr/0319-leftover-map-compare-plot-distance.md new file mode 100644 index 000000000..546a2b3c4 --- /dev/null +++ b/docs/adr/0319-leftover-map-compare-plot-distance.md @@ -0,0 +1,94 @@ +# ADR 0319 — Name leftover-map distance on the grouping comparison leftover-map graphic + +**Decision status:** Accepted +**Date:** 2026-08-31 + +Amends leftover-map distance on graphic-display pair segments +([ADR 0271](0271-leftover-map-segment-distance.md)), leftover-map rank +on the grouping comparison leftover-map graphic +([ADR 0318](0318-leftover-map-compare-plot-rank.md)), leftover expected +on the grouping comparison leftover-map graphic +([ADR 0317](0317-leftover-map-compare-plot-expected.md)), leftover-map graphic +display on the grouping comparison strip +([ADR 0304](0304-leftover-map-compare-graphic.md)), leftover-map coordinates +([ADR 0267](0267-leftover-map-coordinates.md)). Independent of leftover-map +coordinate ticks on the grouping comparison leftover-map graphic. + +## Context + +ADR 0267 already persists leftover-map distance `d = ‖ξ_{1:2} − ζ_{1:2}‖`. +ADR 0271 already captions period-report leftover-map pair segments with +`leftover-map distance {label}` when that leftover-map distance is finite. +ADR 0318 already captions leftover-map rank on the grouping comparison leftover-map +graphic. The comparison graphic still reuses `leftover-map distance {label}`, so a +buyer who compares leftover pairs can treat the period-report graphic caption as +the comparison graphic leftover-map distance even after the strip names coordinates. +Hiding a distinct comparison-graphic leftover-map distance caption lets leftover-map +rank or leftover-map reconstruction `R̂` be read as leftover-map distance without a +next action. When coordinates, reconstruction, and distance are finite, +`R̂ = ξ · ζ` and `d = ‖ξ − ζ‖`; the comparison graphic must name the same persisted +`d` the pair row already shows. A finite negative leftover is shown, never clamped. + +This increment captions leftover-map distance on the grouping comparison leftover-map +graphic from already-named leftover-map distance through formatLeftoverMapDistance. +Comparison copy uses the accessible name +`leftover map comparison graphic leftover-map distance {label}` so it stays +distinct from `leftover-map distance {label}` on the period-report graphic. It does +not add columns. It does not recompute leftover-map distance from plotted pixel +length or from coordinates. Do not invent a leftover score. Do not invent a theta. + +This protected increment uses **0319** so it does not collide with leftover-map +rank on the grouping comparison leftover-map graphic (0318), leftover expected on +that graphic (0317), leftover-map distance on pair segments (0271), leftover-map +coordinates (0267), leftover-map graphic display (0268), leftover-map coordinate +ticks (0270), or the dashboard stacks. + +## Decision + +On the grouping comparison leftover-map graphic, caption each pair segment +with persisted leftover-map distance when formatLeftoverMapDistance returns a usable +badge, next to leftover-map rank. Use the distinct accessible name +`leftover map comparison graphic leftover-map distance {label}` so the graphic +caption is not the period-report graphic caption (`leftover-map distance {label}`). +A missing or non-finite `d` omits that leftover-map comparison graphic leftover-map +distance caption and keeps leftover-map rank when formatLeftoverMapRank returns a +usable badge, leftover expected `E`, leftover observed `Y`, leftover residual `R`, leftover-map unexplained leftover `U`, leftover-map cross share `x`, leftover-map unexplained leftover share `s`, leftover-map explained leftover share `e`, leftover-map reconstruction `R̂`, leftover-map comparison graphic coverage notes, leftover-map rank, plus the pair-row leftover-map distance badge. +Rank-0 origin cells still name `d 0.00` when that persisted leftover-map distance is +finite. A finite negative leftover is shown, never clamped. Do not invent leftover-map +distance from plotted coordinates, leftover-map rank, leftover expected, leftover observed, leftover residual, leftover-map reconstruction, leftover-map unexplained leftover, leftover-map axis share, leftover-map post coverage, leftover-map item coverage, leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover pair count, or the count of unused axes. Click a post marker to open that post. + +Leftover-map distance omits independently of leftover-map rank captions. +A missing leftover-map distance omits leftover-map comparison graphic leftover-map distance and keeps +a usable leftover-map rank caption. + +This increment does not caption leftover-map coordinate ticks on the comparison graphic +with a distinct comparison-graphic name. Those ticks already sit on the period-report +graphic through ADR 0270. + +Do not add SQL migrations. Do not edit shipped migrations. Do not invent a leftover +score. Do not invent a theta. + +## Consequences + +After `make seed`, grouping comparison leftover pairs that already show +the leftover-map graphic of persisted `ξ` / `ζ` also name leftover-map +distance on that graphic when formatLeftoverMapDistance returns a usable badge. +Rank-0 unused axes still plot at the origin and still name `d 0.00` when that +persisted leftover-map distance is finite. Click a post marker or a +pair button opens that post. When coordinates, reconstruction, and distance are +finite, `R̂ = ξ · ζ` and `d = ‖ξ − ζ‖`. + +## Related + +Independent of leftover-map coordinate ticks on the grouping comparison leftover-map graphic. + +## References + +Jeon, M., Jin, I. H., Schweinberger, M., & Baugh, S. (2021). Mapping +unobserved item–respondent interactions: A latent space item response +model with interaction map. *Psychometrika, 86*(2), 378–403. +https://doi.org/10.1007/s11336-021-09762-5 + +Gabriel, K. R. (1971). The biplot graphic display of matrices with +application to principal component analysis. *Biometrika, 58*(3), +453–467. https://doi.org/10.1093/biomet/58.3.453 diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 14a20d511..c64dab990 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,22 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-08-31 09:25 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not +> self-approve. #858 leftover-map rank on the grouping comparison leftover-map +> graphic (v2.75.0 / ADR 0318) is `4b9184f`, stacked on #857 @ `9f34d2c`. Independent +> APPROVE is missing. Next buyer increment on this +> cycle: leftover-map distance on the grouping comparison leftover-map +> graphic (ADR 0319 / v2.76.0) delivered locally on +> `feat/leftover-map-compare-plot-distance-v2760`. Caption persisted leftover-map +> distance `d` on that comparison graphic from already-named leftover_distance. +> Distinct accessible name `leftover map comparison graphic leftover-map +> distance {label}`. Omit when formatLeftoverMapDistance fail-closes. +> Do not caption leftover-map coordinate ticks on that comparison graphic with a distinct name this +> increment. Do not invent leftover-map distance from plotted coordinates. +> Stack onto #858. Leave open for independent review. Do not squash-merge. Do not +> self-approve. Issues #79 and #87 stay open. Do not merge #808 without independent APPROVE. +> Never self-approve. + > Exact-head loop overlay: 2026-08-31 09:19 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not > self-approve. #857 leftover expected on the grouping comparison leftover-map diff --git a/frontend/package.json b/frontend/package.json index bf600eb93..3b01a52da 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.75.0", + "version": "2.76.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 3efd4e53a..7aa7adf09 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -4653,6 +4653,16 @@ describe("App, authenticated", () => { "Leftover map comparison rank", ), ).toHaveLength(1); + expect( + within(screen.getByLabelText("Grouping comparison")).getByLabelText( + "leftover map comparison graphic leftover-map distance d 0.12", + ), + ).toHaveTextContent("d 0.12"); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + "leftover-map distance d 0.12", + ), + ).not.toBeInTheDocument(); expect( within(screen.getByLabelText("Grouping comparison")).getAllByLabelText( /leftover-map axis \d tick/, diff --git a/frontend/src/components/LeftoverMapPlot.test.tsx b/frontend/src/components/LeftoverMapPlot.test.tsx index a86a35c39..890df2bb8 100644 --- a/frontend/src/components/LeftoverMapPlot.test.tsx +++ b/frontend/src/components/LeftoverMapPlot.test.tsx @@ -1275,8 +1275,8 @@ describe("LeftoverMapPlot", () => { ); expect(screen.queryByLabelText(/leftover map comparison graphic reconstruction/)).not.toBeInTheDocument(); expect(screen.queryByLabelText(/leftover-map reconstruction/)).not.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 leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover map comparison graphic 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)", ); @@ -1391,8 +1391,8 @@ describe("LeftoverMapPlot", () => { 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 leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover map comparison graphic 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)", ); @@ -1517,8 +1517,8 @@ describe("LeftoverMapPlot", () => { 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 leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover map comparison graphic 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)", ); @@ -1653,8 +1653,8 @@ describe("LeftoverMapPlot", () => { 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 leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover map comparison graphic 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)", ); @@ -1799,8 +1799,8 @@ describe("LeftoverMapPlot", () => { 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 leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover map comparison graphic 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)", ); @@ -1955,8 +1955,8 @@ describe("LeftoverMapPlot", () => { 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 leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover map comparison graphic 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)", ); @@ -2102,7 +2102,7 @@ describe("LeftoverMapPlot", () => { expect( screen.getByLabelText("leftover map comparison graphic reconstruction R̂ +0.25"), ).toBeInTheDocument(); - expect(screen.getByLabelText("leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover map comparison graphic leftover-map distance d 0.12")).toBeInTheDocument(); expect(screen.getByLabelText("Leftover map comparison graphic coverage")).toHaveTextContent( "Leftover map used 2 of 3 scored posts (complete-case)", ); @@ -2233,7 +2233,7 @@ describe("LeftoverMapPlot", () => { expect( screen.getByLabelText("leftover map comparison graphic leftover residual R +0.40"), ).toHaveTextContent("R +0.40"); - expect(screen.getByLabelText("leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover map comparison graphic leftover-map distance d 0.12")).toBeInTheDocument(); expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); }); @@ -2362,7 +2362,7 @@ describe("LeftoverMapPlot", () => { expect( screen.getByLabelText("leftover map comparison graphic leftover residual R +0.40"), ).toHaveTextContent("R +0.40"); - expect(screen.getByLabelText("leftover-map distance d 0.12")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover map comparison graphic leftover-map distance d 0.12")).toBeInTheDocument(); expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); }); @@ -2420,4 +2420,132 @@ describe("LeftoverMapPlot", () => { ).toHaveTextContent("R 0.00"); expect(screen.getByText("leftover map comparison axis 1 (0%)")).toBeInTheDocument(); }); + + it("names leftover-map distance on the comparison graphic with a distinct accessible name", () => { + render( + , + ); + expect( + screen.getByLabelText("leftover map comparison graphic leftover-map distance d 0.12"), + ).toHaveTextContent("d 0.12"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover-map distance d 1.84"), + ).toHaveTextContent("d 1.84"); + expect(screen.queryByLabelText("leftover-map distance d 0.12")).not.toBeInTheDocument(); + expect(screen.queryByLabelText("leftover-map distance d 1.84")).not.toBeInTheDocument(); + expect( + screen.getAllByLabelText("leftover map comparison graphic leftover-map rank rank 1"), + ).toHaveLength(2); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 2.40"), + ).toHaveTextContent("Y 2.40"); + expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); + }); + + it("omits leftover-map comparison graphic leftover-map distance when d is missing or not usable", () => { + render( + , + ); + expect(screen.queryByLabelText(/leftover map comparison graphic leftover-map distance/)).not.toBeInTheDocument(); + expect(screen.queryByLabelText(/^leftover-map distance/)).not.toBeInTheDocument(); + expect( + screen.getAllByLabelText("leftover map comparison graphic leftover-map rank rank 1"), + ).toHaveLength(2); + expect( + screen.getByLabelText("leftover map comparison graphic leftover observed Y 2.40"), + ).toHaveTextContent("Y 2.40"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover residual R +0.40"), + ).toHaveTextContent("R +0.40"); + expect(screen.getByText("leftover map comparison axis 1 (82%)")).toBeInTheDocument(); + }); + + it("names rank-0 origin leftover-map distance on the comparison graphic when that persisted value is finite", () => { + render( + , + ); + expect( + screen.getByLabelText("leftover map comparison graphic leftover-map distance d 0.00"), + ).toHaveTextContent("d 0.00"); + expect(screen.queryByLabelText("leftover-map distance d 0.00")).not.toBeInTheDocument(); + expect( + screen.getByLabelText("leftover map comparison graphic leftover-map rank rank 0"), + ).toHaveTextContent("rank 0"); + expect( + screen.getByLabelText("leftover map comparison graphic leftover expected E 0.00"), + ).toHaveTextContent("E 0.00"); + expect(screen.getByText("leftover map comparison axis 1 (0%)")).toBeInTheDocument(); + }); }); diff --git a/frontend/src/components/LeftoverMapPlot.tsx b/frontend/src/components/LeftoverMapPlot.tsx index a2ee23e3f..692d4ba1f 100644 --- a/frontend/src/components/LeftoverMapPlot.tsx +++ b/frontend/src/components/LeftoverMapPlot.tsx @@ -42,6 +42,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_DISTANCE, LEFTOVER_MAP_PLOT_CAPTION, LEFTOVER_MAP_PLOT_POST_ACTION, LEFTOVER_MAP_PLOT_SEGMENT_CROSS_SHARE, @@ -173,6 +174,9 @@ function leftoverMapPlotAxisText( * labels. ADR 0318 captions leftover-map rank on that * comparison graphic from already-named leftover-map rank * with distinct leftover map comparison graphic leftover-map rank + * labels. ADR 0319 captions leftover-map distance on that + * comparison graphic from already-named leftover-map distance + * with distinct leftover map comparison graphic leftover-map distance * labels. * Never invent a leftover score. */ @@ -329,9 +333,12 @@ export function LeftoverMapPlot({ x={segment.labelX} y={segment.labelY} textAnchor="middle" - aria-label={tf(LEFTOVER_MAP_PLOT_SEGMENT_DISTANCE, { - label: segment.distanceLabel, - })} + aria-label={tf( + variant === "comparison" + ? LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_DISTANCE + : LEFTOVER_MAP_PLOT_SEGMENT_DISTANCE, + { label: segment.distanceLabel }, + )} > {segment.distanceLabel} diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index 9ec439bff..6c8ee1e07 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -128,7 +128,8 @@ export type LeftoverPairListProps = { * comparison leftover-map graphic, not this pair list. ADR 0317 captions leftover * expected on that grouping comparison leftover-map graphic, not this pair list. ADR 0318 * captions leftover-map rank on that grouping comparison leftover-map graphic, not this - * pair list. Every badge still + * pair list. ADR 0319 captions leftover-map distance 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 19c2964d9..5453188b5 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -103,6 +103,7 @@ describe("i18n", () => { "Leftover map comparison expected", "leftover map comparison graphic leftover-map rank {label}", "Leftover map comparison rank", + "leftover map comparison graphic leftover-map distance {label}", "Leftover map comparison coordinates", "Leftover map comparison graphic", "Leftover map comparison", @@ -651,6 +652,23 @@ describe("i18n", () => { }, ); + it.each([ + ["ko", "잔여 지도 비교 그림 거리 d 0.12"], + ["zh", "残差地图比较图形距离 d 0.12"], + ["ja", "残差マップの比較図距離 d 0.12"], + ["vi", "khoảng cách đồ họa so sánh bản đồ phần dư d 0.12"], + ] as const)( + "formats leftover map comparison graphic leftover-map distance in %s", + (locale, expected) => { + setLocale(locale); + expect( + tf("leftover map comparison graphic leftover-map distance {label}", { + label: "d 0.12", + }), + ).toBe(expected); + }, + ); + it.each([ ["ko", "잔여 지도 비교 설명 잔여 점유율"], ["zh", "残差地图比较已解释残差份额"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 1c7b9525d..8bc25651c 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -295,6 +295,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison expected": "잔여 지도 비교 기대", "leftover map comparison graphic leftover-map rank {label}": "잔여 지도 비교 그림 순위 {label}", "Leftover map comparison rank": "잔여 지도 비교 순위", + "leftover map comparison graphic leftover-map distance {label}": "잔여 지도 비교 그림 거리 {label}", "Leftover map comparison coordinates": "잔여 지도 비교 좌표", "Leftover map comparison graphic": "잔여 지도 비교 그림", "Leftover map comparison": "잔여 지도 비교", @@ -936,6 +937,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison expected": "残差地图比较期望", "leftover map comparison graphic leftover-map rank {label}": "残差地图比较图形秩 {label}", "Leftover map comparison rank": "残差地图比较秩", + "leftover map comparison graphic leftover-map distance {label}": "残差地图比较图形距离 {label}", "Leftover map comparison coordinates": "残差地图比较坐标", "Leftover map comparison graphic": "残差地图比较图形", "Leftover map comparison": "残差地图比较", @@ -1598,6 +1600,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison expected": "残差マップの比較期待", "leftover map comparison graphic leftover-map rank {label}": "残差マップの比較図階数 {label}", "Leftover map comparison rank": "残差マップの比較階数", + "leftover map comparison graphic leftover-map distance {label}": "残差マップの比較図距離 {label}", "Leftover map comparison coordinates": "残差マップの比較座標", "Leftover map comparison graphic": "残差マップの比較図", "Leftover map comparison": "残差マップの比較", @@ -2239,6 +2242,7 @@ const TRANSLATIONS: Partial>> = { "Leftover map comparison expected": "Kỳ vọng so sánh bản đồ phần dư", "leftover map comparison graphic leftover-map rank {label}": "hạng đồ họa so sánh bản đồ phần dư {label}", "Leftover map comparison rank": "Hạng so sánh bản đồ phần dư", + "leftover map comparison graphic leftover-map distance {label}": "khoảng cách đồ họa so sánh bản đồ phần dư {label}", "Leftover map comparison coordinates": "Tọa độ so sánh bản đồ phần dư", "Leftover map comparison graphic": "Đồ họa so sánh bản đồ phần dư", "Leftover map comparison": "So sánh bản đồ phần dư", diff --git a/frontend/src/leftoverMapPlotLayout.test.ts b/frontend/src/leftoverMapPlotLayout.test.ts index 958adc229..fdc08a435 100644 --- a/frontend/src/leftoverMapPlotLayout.test.ts +++ b/frontend/src/leftoverMapPlotLayout.test.ts @@ -16,6 +16,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_OBSERVED, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED, LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK, + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_DISTANCE, LEFTOVER_MAP_COMPARE_PLOT_SVG, PLOT_HEIGHT, PLOT_PADDING, @@ -1191,8 +1192,11 @@ describe("formatLeftoverMapDistance", () => { it("formats persisted leftover-map distance without inventing a leftover score", () => { expect(formatLeftoverMapDistance(0.12)).toBe("d 0.12"); expect(formatLeftoverMapDistance(0)).toBe("d 0.00"); + expect(formatLeftoverMapDistance(1.84)).toBe("d 1.84"); + expect(formatLeftoverMapDistance(-0.05)).toBe("d -0.05"); expect(formatLeftoverMapDistance(null)).toBeNull(); expect(formatLeftoverMapDistance(Number.NaN)).toBeNull(); + expect(formatLeftoverMapDistance(Number.POSITIVE_INFINITY)).toBeNull(); }); }); @@ -1336,4 +1340,16 @@ describe("leftover map comparison graphic labels", () => { LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_EXPECTED, ); }); + + it("stays distinct from leftover-map distance copy", () => { + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_DISTANCE).toBe( + "leftover map comparison graphic leftover-map distance {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_DISTANCE).not.toBe( + "leftover-map distance {label}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_DISTANCE).not.toBe( + LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_RANK, + ); + }); }); diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index 1bcafbc94..103fa63c8 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -46,7 +46,8 @@ * residual on that graphic (ADR 0315), and grouping comparison leftover * observed on that graphic (ADR 0316), and grouping comparison leftover * expected on that graphic (ADR 0317), and grouping comparison leftover-map - * rank on that graphic (ADR 0318) caption the pair list or the grouping comparison + * rank on that graphic (ADR 0318), and grouping comparison leftover-map + * distance on that graphic (ADR 0319) 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 @@ -72,6 +73,8 @@ * already-named leftover expected. * ADR 0318 captions leftover-map rank on that comparison graphic from * already-named leftover-map rank. + * ADR 0319 captions leftover-map distance on that comparison graphic from + * already-named leftover-map distance. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; @@ -107,6 +110,9 @@ export const LEFTOVER_MAP_PLOT_TICK = export const LEFTOVER_MAP_PLOT_SEGMENT_DISTANCE = "leftover-map distance {label}"; +export const LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_DISTANCE = + "leftover map comparison graphic leftover-map distance {label}"; + export const LEFTOVER_MAP_PLOT_SEGMENT_RECONSTRUCTION = "leftover-map reconstruction {label}"; diff --git a/pyproject.toml b/pyproject.toml index 9d6139217..a373b486b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.75.0" +version = "2.76.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 39b7853f1..09bd29321 100644 --- a/uv.lock +++ b/uv.lock @@ -685,7 +685,7 @@ wheels = [ [[package]] name = "lineageweave" -version = "2.75.0" +version = "2.76.0" source = { editable = "." } dependencies = [ { name = "certifi" },