From b086d97675586ee83f5478d7b4e3bf84b0a5da8c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 31 Aug 2026 04:25:35 +0000 Subject: [PATCH] feat(reports): leftover-map singular values on leftover-map comparison leftover-axis ticks independently of leftover-map axis share (v2.86.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caption leftover-map comparison leftover-axis ticks with persisted leftover-map singular values σ_k through leftoverMapCompareAxisTickBadge independently of leftover-map axis share (ADR 0329). A missing, non-finite, or negative singular value omits that σ tick caption. Rank-0 unused axes still name σ 0.00. Do not invent σ_k from leftover-map axis share. --- AGENTS.md | 9 ++ ARCHITECTURE.md | 6 +- ....0-leftover-map-compare-axis-tick-badge.md | 24 ++++ CHANGELOG.md | 24 ++++ CLAUDE.md | 2 +- docs/adr/0148-leftover-map-axis-share.md | 4 +- ...ftover-map-compare-plot-tick-axis-badge.md | 4 +- ...29-leftover-map-compare-axis-tick-badge.md | 132 ++++++++++++++++++ docs/product-technical-gap-baseline.md | 21 +++ docs/storybook-inventory.md | 2 +- frontend/package.json | 2 +- frontend/src/App.test.tsx | 46 ++++++ frontend/src/App.tsx | 14 ++ .../src/components/LeftoverMapPlot.test.tsx | 9 ++ frontend/src/i18n.test.ts | 39 ++++++ frontend/src/i18n.ts | 13 ++ frontend/src/leftoverMapAxisBadge.ts | 2 + .../src/leftoverMapPlotAxisSingular.test.ts | 92 ++++++++++++ frontend/src/leftoverMapPlotAxisSingular.ts | 24 ++++ frontend/src/leftoverMapPlotLayout.test.ts | 6 + frontend/src/leftoverMapPlotLayout.ts | 3 + pyproject.toml | 2 +- 22 files changed, 473 insertions(+), 7 deletions(-) create mode 100644 CHANGELOG.d/2.86.0-leftover-map-compare-axis-tick-badge.md create mode 100644 docs/adr/0329-leftover-map-compare-axis-tick-badge.md diff --git a/AGENTS.md b/AGENTS.md index b2e674ecb..5bc1c6054 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -690,6 +690,15 @@ axis tick caption. Distinct accessible name of leftover-map axis share. Leftover-map comparison graphic leftover-map axis ticks never name leftover-map axis share. Rank-0 unused axes still name leftover-map comparison graphic leftover-map axis tick `σ 0.00`. Do not invent leftover-map singular values from leftover-map axis share. +ADR 0329 captions leftover-map comparison leftover-axis ticks with persisted leftover-map +singular values `σ_k` when leftoverMapCompareAxisTickBadge returns a usable leftover-axis +tick caption. Distinct accessible name +`leftover map comparison leftover axis {axis} tick {value} σ {singular}`. Singular value omits independently +of leftover-map axis share. Leftover-map comparison leftover-axis ticks never name leftover-map +axis share. Rank-0 unused axes still name leftover-map comparison leftover-axis tick +`σ 0.00`. Do not invent leftover-map singular values from leftover-map axis share. Do not invent leftover-map +axis share from leftover-map singular values. This increment does not change leftover-map comparison graphic leftover-map +axis ticks. 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 diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 07434cc54..979f60c77 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -767,6 +767,9 @@ axis tick caption independently of leftover-map axis share. ADR 0328 captions leftover-map comparison graphic leftover-map axis ticks with persisted leftover-map singular values `σ_k` when leftoverMapComparePlotTickAxisBadge returns a usable leftover-map axis tick caption independently of leftover-map axis share. +ADR 0329 captions leftover-map comparison leftover-axis ticks with persisted leftover-map +singular values `σ_k` when leftoverMapCompareAxisTickBadge returns a usable leftover-axis +tick caption independently of leftover-map axis share. 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 @@ -825,7 +828,8 @@ report badges independently of leftover-map axis share (ADR 0325), leftover-map comparison graphic leftover-map axis leftover-map singular values as leftoverMapComparePlotAxisBadge (ADR 0326), leftover-map graphic leftover-map axis tick leftover-map singular values independently of leftover-map axis share (ADR 0327), leftover-map comparison graphic leftover-map axis tick leftover-map singular values independently of leftover-map axis share -(ADR 0328); +(ADR 0328), leftover-map comparison leftover-axis tick leftover-map singular values independently of leftover-map axis share +(ADR 0329); `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.86.0-leftover-map-compare-axis-tick-badge.md b/CHANGELOG.d/2.86.0-leftover-map-compare-axis-tick-badge.md new file mode 100644 index 000000000..c06c25508 --- /dev/null +++ b/CHANGELOG.d/2.86.0-leftover-map-compare-axis-tick-badge.md @@ -0,0 +1,24 @@ +## 2.86.0 — Leftover-map singular values on leftover-map comparison leftover-axis ticks independently of leftover-map axis share + +- Caption leftover-map comparison leftover-axis ticks with persisted leftover-map + singular values `σ_k` through leftoverMapCompareAxisTickBadge independently of leftover-map + axis share (ADR 0329). After `make seed`, leftover-map comparison leftover-axis + ticks name persisted leftover-map singular values when leftoverMapCompareAxisTickBadge + returns a usable leftover-axis tick `σ` caption even when leftover-map axis + share is omitted; click a leftover pair opens that post. A missing, non-finite, + or negative singular value omits that `σ` tick caption and keeps + `leftover map comparison leftover axis {k} tick {value}`. Rank-0 unused + axes still name `σ 0.00`. Leftover-map comparison leftover-axis ticks stay + `leftover map comparison leftover axis {k} tick {value} σ {singular}` when + `σ_k` is finite so they stay distinct from leftover-map comparison graphic leftover-map axis ticks + `leftover map comparison graphic leftover-map axis {k} tick {value} σ {singular}`, from leftover-map + graphic leftover-map axis ticks `leftover-map axis {k} tick {value} σ {singular}`, from leftover-map + graphic leftover-map axis `leftover-map axis {k} σ {value}`, from leftover-axis + `leftover axis {k} σ {value}`, from comparison leftover-axis + `leftover map comparison leftover axis {k} σ {value}`, and from comparison graphic leftover-map + axis `leftover map comparison graphic leftover-map axis {k} σ {value}`. Do not invent + `σ_k` from leftover-map axis share. Do not invent leftover-map axis share from + `σ_k`. Leftover-map comparison leftover-axis ticks never name leftover-map axis + share. This increment does not change leftover-map comparison graphic leftover-map axis ticks. + 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 4c2396b72..c4b186be4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,30 @@ All notable changes to this project are documented here. Format follows ### Added +- Period leftover pairs now caption leftover-map comparison leftover-axis + ticks with persisted leftover-map singular values `σ_k` through + leftoverMapCompareAxisTickBadge independently of leftover-map axis share + (ADR 0329 / v2.86.0). After `make seed`, leftover-map comparison leftover-axis + ticks name persisted leftover-map singular values when leftoverMapCompareAxisTickBadge + returns a usable leftover-axis tick `σ` caption even when leftover-map axis share + is omitted; click a leftover pair opens that post. A missing, non-finite, or + negative singular value omits that `σ` tick caption and keeps + `leftover map comparison leftover axis {k} tick {value}`. Rank-0 unused + axes still name `σ 0.00`. Leftover-map comparison leftover-axis ticks stay + `leftover map comparison leftover axis {k} tick {value} σ {singular}` when + `σ_k` is finite so they stay distinct from leftover-map comparison graphic leftover-map axis ticks + `leftover map comparison graphic leftover-map axis {k} tick {value} σ {singular}`, from leftover-map + graphic leftover-map axis ticks `leftover-map axis {k} tick {value} σ {singular}`, from leftover-map + graphic leftover-map axis `leftover-map axis {k} σ {value}`, from leftover-axis + `leftover axis {k} σ {value}`, from comparison leftover-axis + `leftover map comparison leftover axis {k} σ {value}`, and from comparison graphic leftover-map + axis `leftover map comparison graphic leftover-map axis {k} σ {value}`. Do not invent + `σ_k` from leftover-map axis share. Do not invent leftover-map axis share from + `σ_k`. Leftover-map comparison leftover-axis ticks never name leftover-map + axis share. This increment does not change leftover-map comparison graphic leftover-map + axis ticks. A finite negative leftover is shown, never clamped. Never invent a leftover + score or a theta. No new columns. + - Period leftover pairs now caption leftover-map comparison graphic leftover-map axis ticks with persisted leftover-map singular values `σ_k` through leftoverMapComparePlotTickAxisBadge independently of leftover-map axis share diff --git a/CLAUDE.md b/CLAUDE.md index 9c94ffc03..7d9b6f1d8 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 / 0319 / 0320 / 0321 / 0322 / 0323 / 0324 / 0325 / 0326 / 0327 / 0328), 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 / 0320 / 0321 / 0322 / 0323 / 0324 / 0325 / 0326 / 0327 / 0328 / 0329), 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/0148-leftover-map-axis-share.md b/docs/adr/0148-leftover-map-axis-share.md index 7b309040d..4dc548400 100644 --- a/docs/adr/0148-leftover-map-axis-share.md +++ b/docs/adr/0148-leftover-map-axis-share.md @@ -17,7 +17,9 @@ [ADR 0327](0327-leftover-map-plot-tick-axis-badge.md) (leftover-map graphic leftover-map axis tick leftover-map singular values independently of leftover-map axis share); [ADR 0328](0328-leftover-map-compare-plot-tick-axis-badge.md) -(leftover-map comparison graphic leftover-map axis tick leftover-map singular values independently of leftover-map axis share) +(leftover-map comparison graphic leftover-map axis tick leftover-map singular values independently of leftover-map axis share); +[ADR 0329](0329-leftover-map-compare-axis-tick-badge.md) +(leftover-map comparison leftover-axis tick leftover-map singular values independently of leftover-map axis share) ## Context diff --git a/docs/adr/0328-leftover-map-compare-plot-tick-axis-badge.md b/docs/adr/0328-leftover-map-compare-plot-tick-axis-badge.md index 537e91917..f39915f14 100644 --- a/docs/adr/0328-leftover-map-compare-plot-tick-axis-badge.md +++ b/docs/adr/0328-leftover-map-compare-plot-tick-axis-badge.md @@ -110,7 +110,9 @@ singular values on leftover-map graphic-display axes singular values on leftover-axis report badges independently of leftover-map axis share ([ADR 0325](0325-leftover-map-axis-singular-only.md)). Independent of leftover-map coordinate ticks on the grouping comparison leftover-map graphic -([ADR 0320](0320-leftover-map-compare-plot-ticks.md)). +([ADR 0320](0320-leftover-map-compare-plot-ticks.md)). Independent of leftover-map +comparison leftover-axis tick leftover-map singular values independently of leftover-map axis share +([ADR 0329](0329-leftover-map-compare-axis-tick-badge.md)). ## References diff --git a/docs/adr/0329-leftover-map-compare-axis-tick-badge.md b/docs/adr/0329-leftover-map-compare-axis-tick-badge.md new file mode 100644 index 000000000..7ea9e9f5f --- /dev/null +++ b/docs/adr/0329-leftover-map-compare-axis-tick-badge.md @@ -0,0 +1,132 @@ +# ADR 0329 — Name leftover-map singular values on leftover-map comparison leftover-axis ticks independently of leftover-map axis share + +**Decision status:** Accepted +**Date:** 2026-08-31 + +Amends leftover-map singular values on leftover-axis report badges on the grouping +comparison strip ([ADR 0323](0323-leftover-map-compare-axis-singular.md)), leftover-map +axis share persistence ([ADR 0148](0148-leftover-map-axis-share.md)). Independent of leftover-map +comparison graphic leftover-map axis tick leftover-map singular values independently of leftover-map +axis share ([ADR 0328](0328-leftover-map-compare-plot-tick-axis-badge.md)), leftover-map graphic +leftover-map axis tick leftover-map singular values independently of leftover-map axis share +([ADR 0327](0327-leftover-map-plot-tick-axis-badge.md)), leftover-map comparison graphic leftover-map +axis leftover-map singular values as leftoverMapComparePlotAxisBadge +([ADR 0326](0326-leftover-map-compare-plot-axis-badge.md)), leftover-map singular values on leftover-axis +report badges independently of leftover-map axis share +([ADR 0325](0325-leftover-map-axis-singular-only.md)). + +## Context + +ADR 0148 already persists leftover-map singular values `σ_k` and leftover-map +axis share `σ_k² / Σ_j σ_j²`. ADR 0323 already captions leftover-axis report +badges on the grouping comparison strip as leftoverMapCompareAxisBadge. ADR 0320 +already ticks leftover-map comparison graphic leftover-map axes at persisted +`ξ` / `ζ` projections. ADR 0328 already captions leftover-map comparison graphic +leftover-map axis ticks with `σ_k` through leftoverMapComparePlotTickAxisBadge. +Those grouping comparison leftover-axis report badges still omit leftover-axis +ticks, so a buyer who reads leftover-map comparison leftover-axis badges or leftover-map +comparison graphic leftover-map axis ticks can treat leftover-map coordinate ticks or leftover-map +axis share as leftover-map structure without a next action. A missing, non-finite, or +negative singular value is not a leftover score and must omit independently of leftover-map +axis share. Rank-0 unused axes still persist `σ_k = 0`. A finite negative leftover is +shown, never clamped. Do not invent `σ_k` from leftover-map axis share. Do not invent leftover-map +axis share from `σ_k`. Leftover-map comparison leftover-axis ticks never name leftover-map +axis share. + +This increment names leftover-map comparison leftover-axis ticks leftover-map +singular values as leftoverMapCompareAxisTickBadge, matching leftoverMapCompareAxisBadge, +leftoverMapComparePlotTickAxisBadge, leftoverMapPlotTickAxisBadge, leftoverMapComparePlotAxisBadge, +leftoverMapPlotAxisBadge, and leftoverMapAxisBadge. Leftover-map comparison leftover-axis ticks +reuse `layoutLeftoverMapPlot` ticks so origin `0.00` plus unique finite `ξ` / `ζ` +projections match the comparison graphic. Copy stays +`leftover map comparison leftover axis {k} tick {value} σ {singular}` when `σ_k` is finite so they stay +distinct from leftover-map comparison graphic leftover-map axis ticks +`leftover map comparison graphic leftover-map axis {k} tick {value} σ {singular}` (ADR 0328), from leftover-map +graphic leftover-map axis ticks `leftover-map axis {k} tick {value} σ {singular}` (ADR 0327), from leftover-map +graphic leftover-map axis `leftover-map axis {k} σ {value}` (ADR 0324), from leftover-axis +`leftover axis {k} σ {value}` (ADR 0325), from comparison leftover-axis +`leftover map comparison leftover axis {k} σ {value}` (ADR 0323), and from comparison graphic leftover-map +axis `leftover map comparison graphic leftover-map axis {k} σ {value}` (ADR 0326). +It does not add columns. Do not invent a leftover score. Do not invent a theta. + +This protected increment uses **0329** so it does not collide with leftover-map +comparison graphic leftover-map axis tick leftover-map singular values independently of leftover-map axis share +(0328), leftover-map graphic leftover-map axis tick leftover-map singular values independently of leftover-map axis share +(0327), leftover-map comparison graphic leftover-map axis leftover-map singular values as leftoverMapComparePlotAxisBadge +(0326), leftover-map singular values on leftover-axis report badges independently of leftover-map axis share +(0325), leftover-map singular values on leftover-axis report badges on the grouping comparison strip +(0323), leftover-map axis share persistence (0148), or the dashboard stacks. + +## Decision + +On leftover-map comparison leftover-axis ticks on the grouping comparison strip leftover-axis +surface, caption leftover-map axis `k` tick `{value}` with persisted leftover-map singular +value `σ_k` when leftoverMapCompareAxisTickBadge returns a usable leftover-axis tick caption +that includes that singular value. Share and singular value omit independently through that +helper, and leftover-map axis share is never named on leftover-map comparison leftover-axis +ticks: no `σ_k` keeps `leftover map comparison leftover axis {k} tick {value}`; `σ_k` only is +`leftover map comparison leftover axis {k} tick {value} σ {singular}`. A missing, non-finite, or +negative singular value omits that `σ` tick caption and keeps a usable leftover-map +comparison leftover-axis tick coordinate caption. Rank-0 unused axes still name +`σ 0.00`. Origin `0.00` plus each unique finite persisted `ξ` / `ζ` projection still name leftover-map +comparison leftover-axis ticks. If `layoutLeftoverMapPlot` is null, omit leftover-axis ticks. +Click a leftover pair to open that post. + +Leftover-map comparison leftover-axis tick leftover-map singular values omit independently of leftover-map +comparison graphic leftover-map axis tick leftover-map singular values, leftover-map graphic leftover-map +axis tick leftover-map singular values, leftover-map comparison graphic leftover-map axis singular values, +leftover-map graphic-display leftover-map axis singular values, leftover-axis report badge leftover-map +singular values, and leftover-map comparison leftover-axis singular values. A missing leftover-map axis +share omits leftover-map axis share and keeps a usable leftover-map comparison leftover-axis tick `σ` +caption. + +This increment does not change leftover-map comparison graphic leftover-map axis ticks. Those leftover-map +comparison graphic leftover-map axis ticks stay +`leftover map comparison graphic leftover-map axis {axis} tick {value} σ {singular}` +(ADR 0328). This increment does not persist leftover-map inner product, cosine, or length. + +Do not add SQL. Do not edit shipped migrations. Do not invent a leftover +score. Do not invent a theta. + +## Consequences + +After `make seed`, leftover-map comparison leftover-axis ticks name persisted leftover-map +singular values when leftoverMapCompareAxisTickBadge returns a usable leftover-axis tick `σ` +caption even when leftover-map axis share is omitted; click a leftover pair opens that post. +Hidden posts stay hidden. Rank-0 unused axes still name leftover-map comparison leftover-axis +tick `σ 0.00`. Leftover-map comparison graphic leftover-map axis tick leftover-map singular values +independently of leftover-map axis share (ADR 0328), leftover-map graphic leftover-map axis tick leftover-map +singular values independently of leftover-map axis share (ADR 0327), leftover-map comparison graphic leftover-map +axis leftover-map singular values (ADR 0326), leftover-map graphic-display leftover-map singular values (ADR 0324), leftover-axis +report badge leftover-map singular values independently of leftover-map axis share (ADR 0325), leftover-map +singular values on leftover-axis report badges on the grouping comparison strip (ADR 0323), and grouping +comparison leftover-map graphic leftover-map axis ticks (ADR 0320) remain. + +## Related + +Independent of leftover-map comparison graphic leftover-map axis tick leftover-map singular values +independently of leftover-map axis share +([ADR 0328](0328-leftover-map-compare-plot-tick-axis-badge.md)). Independent of leftover-map graphic leftover-map +axis tick leftover-map singular values independently of leftover-map axis share +([ADR 0327](0327-leftover-map-plot-tick-axis-badge.md)). Independent of leftover-map comparison graphic leftover-map +axis leftover-map singular values as leftoverMapComparePlotAxisBadge +([ADR 0326](0326-leftover-map-compare-plot-axis-badge.md)). Independent of leftover-map singular values on leftover-axis +report badges independently of leftover-map axis share +([ADR 0325](0325-leftover-map-axis-singular-only.md)). Independent of leftover-map singular values on leftover-axis +report badges on the grouping comparison strip +([ADR 0323](0323-leftover-map-compare-axis-singular.md)). Independent of leftover-map coordinate ticks on the grouping +comparison leftover-map graphic +([ADR 0320](0320-leftover-map-compare-plot-ticks.md)). + +## References + +Gabriel, K. R. (1971). The biplot graphic display of matrices with +application to principal component analysis. *Biometrika, 58*(3), +453–467. https://doi.org/10.1093/biomet/58.3.453 + +Jeon, M., Jin, I. H., Schweinberger, M., & Baugh, S. (2021). Mapping +unobserved item–respondent interactions: A latent space item response +model with interaction map. *Psychometrika, 86*(2), 378–403. +https://doi.org/10.1007/s11336-021-09762-5 +(LSIRM interaction `−γ‖ξ_j − ζ_i‖` after main effects +`α_j − β_i`; typically `p = 2` for the interaction map.) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 1a80939ce..a49de5cb4 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,26 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-08-31 13:17 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not +> self-approve. #869 leftover-map singular values on leftover-map comparison graphic leftover-map +> axis ticks independently of leftover-map axis share (v2.85.0 / ADR 0328) is +> `98a29e4`, stacked on #868 @ `5218a5c`. Independent APPROVE is missing. Devin +> Review is COMMENTED, not APPROVE. Next buyer increment on this cycle: leftover-map +> comparison leftover-axis ticks naming persisted leftover-map singular +> values independently of leftover-map axis share (ADR 0329 / v2.86.0) delivered +> locally on `feat/leftover-map-compare-axis-tick-badge-v2860`. Caption persisted leftover-map +> singular values `σ_k` on leftover-map comparison leftover-axis ticks when +> leftoverMapCompareAxisTickBadge returns a usable leftover-axis tick `σ` +> caption. Distinct accessible name `leftover map comparison leftover axis {axis} tick {value} σ {singular}`. +> Rank-0 unused axes still name `σ 0.00`. Singular value omits independently of +> leftover-map axis share. Leftover-map comparison leftover-axis ticks never name +> leftover-map axis share. Do not invent `σ_k` from leftover-map axis share. Do not +> invent leftover-map axis share from `σ_k`. Do not persist leftover-map inner +> product, cosine, or length. Do not change leftover-map comparison graphic leftover-map +> axis ticks this increment. Stack onto #869. 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 12:20 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not > self-approve. #868 leftover-map singular values on leftover-map graphic leftover-map diff --git a/docs/storybook-inventory.md b/docs/storybook-inventory.md index d4c5f9fae..fdec8fc8b 100644 --- a/docs/storybook-inventory.md +++ b/docs/storybook-inventory.md @@ -6,7 +6,7 @@ operator-facing control you can click before changing product CSS. | Story | Operator next action | Token / module | |---|---|---| | `Reports/LeftoverMapPlot` | Read the leftover-map graphic display of persisted `ξ` (posts) and `ζ` (criteria), match axis ticks to those coordinates, pair-segment `d` to leftover-map distance, pair-segment `R̂` to leftover-map reconstruction, pair-segment `R̂²/R²` to leftover-map explained leftover share, pair-segment `U²/R²` to leftover-map unexplained leftover share, pair-segment `2R̂U/R²` to leftover-map cross share, pair-segment `U` to leftover-map unexplained leftover, pair-segment `R` to leftover residual, pair-segment `Y` to leftover observed, pair-segment `E` to leftover expected, and pair-segment leftover-map rank, then click a post marker to open that post. Leftover-map axes name persisted leftover-map singular values `σ_k` and leftover-map axis share when finite. The plot names leftover-map complete-case coverage, leftover-map item complete-case coverage, leftover-map incomplete post coverage, and leftover-map incomplete item coverage when persisted. The comparison variant names leftover-map complete-case coverage under `Leftover map comparison graphic coverage` and leftover-map item complete-case coverage under `Leftover map comparison graphic item coverage` and leftover-map incomplete post coverage under `Leftover map comparison graphic incomplete posts` and leftover-map incomplete item coverage under `Leftover map comparison graphic incomplete items`. `ClosestAndFarthest`, `RankZeroOrigin`, `MissingCoordinates`, `MissingAxisShare`, `Comparison`, `ComparisonMissingCoverage`, `ComparisonMissingItemCoverage`, `ComparisonMissingIncompletePosts`, and `ComparisonMissingIncompleteItems` cover two-pair maps, rank-0 origin with 0% share, a `0` tick, `d 0.00`, `R̂ 0.00`, `R̂²/R² 0.00`, `U²/R² 0.00`, `2R̂U/R² 0.00`, `U 0.00`, `R 0.00`, `Y 0.00`, `E 0.00`, and `rank 0`, omitted plots, missing share that keeps leftover-map axis `σ` without share, grouping comparison leftover-map graphic coverage that matches leftover map used N of M scored posts, omitted comparison graphic coverage when leftoverMapCoverageCounts fail-closes, grouping comparison leftover-map graphic item coverage that matches leftover map used N of M scored criteria, omitted comparison graphic item coverage when leftoverMapItemCoverageCounts fail-closes, grouping comparison leftover-map graphic incomplete posts that matches leftover map dropped N incomplete posts, omitted comparison graphic incomplete posts when leftoverMapIncompletePostCount fail-closes, grouping comparison leftover-map graphic incomplete items that matches leftover map dropped N incomplete criteria, and omitted comparison graphic incomplete items when leftoverMapIncompleteItemCount fail-closes. The plot does not invent a leftover score. | `LeftoverMapPlot`, `leftoverMapPlotLayout`, `leftoverMapPlotAxisShare`, `--color-primary`, `--color-palette-blue-mid` | -| `Reports/LeftoverPairList` | Read closest/farthest leftover pairs with named `R`, `Y`/`E`, rank, `U`, `s`, `e`, `x`, `R̂`, `ξ`/`ζ`, and `d`, then open that post. The leftover-map graphic display sits above the pair buttons when coordinates are finite, leftover-map axes name persisted leftover-map singular values and leftover-map axis share, leftover-map axis ticks name persisted coordinates, pair segments name persisted leftover-map distance, pair segments name persisted leftover-map reconstruction, pair segments name persisted leftover-map explained leftover share, pair segments name persisted leftover-map unexplained leftover share, pair segments name persisted leftover-map cross share, pair segments name persisted leftover-map unexplained leftover, pair segments name persisted leftover residual, pair segments name persisted leftover observed, pair segments name persisted leftover expected, and pair segments name persisted leftover-map rank. The pair-list note names leftover-map complete-case coverage only when leftoverMapCoverageCounts returns usable complete-case integers, leftover-map item complete-case coverage, leftover-map incomplete post coverage, and leftover-map incomplete item coverage when persisted. The grouping comparison strip names leftover-map complete-case coverage, leftover-map item complete-case coverage, leftover-map incomplete post coverage, leftover-map incomplete item coverage, and leftover-axis report badges with persisted leftover-map singular values under `leftover map comparison leftover axis {axis} σ {value}` with distinct comparison labels when leftoverMapCoverageCounts / leftoverMapItemCoverageCounts / leftoverMapIncompletePostCount / leftoverMapIncompleteItemCount / leftoverMapCompareAxisBadge return usable values. | `LeftoverPairList`, `LeftoverMapPlot`, `ticket-list`, `post-badge` | +| `Reports/LeftoverPairList` | Read closest/farthest leftover pairs with named `R`, `Y`/`E`, rank, `U`, `s`, `e`, `x`, `R̂`, `ξ`/`ζ`, and `d`, then open that post. The leftover-map graphic display sits above the pair buttons when coordinates are finite, leftover-map axes name persisted leftover-map singular values and leftover-map axis share, leftover-map axis ticks name persisted coordinates, pair segments name persisted leftover-map distance, pair segments name persisted leftover-map reconstruction, pair segments name persisted leftover-map explained leftover share, pair segments name persisted leftover-map unexplained leftover share, pair segments name persisted leftover-map cross share, pair segments name persisted leftover-map unexplained leftover, pair segments name persisted leftover residual, pair segments name persisted leftover observed, pair segments name persisted leftover expected, and pair segments name persisted leftover-map rank. The pair-list note names leftover-map complete-case coverage only when leftoverMapCoverageCounts returns usable complete-case integers, leftover-map item complete-case coverage, leftover-map incomplete post coverage, and leftover-map incomplete item coverage when persisted. The grouping comparison strip names leftover-map complete-case coverage, leftover-map item complete-case coverage, leftover-map incomplete post coverage, leftover-map incomplete item coverage, leftover-axis report badges with persisted leftover-map singular values under `leftover map comparison leftover axis {axis} σ {value}`, and leftover-axis ticks with persisted leftover-map singular values under `leftover map comparison leftover axis {axis} tick {value} σ {singular}` independently of leftover-map axis share with distinct comparison labels when leftoverMapCoverageCounts / leftoverMapItemCoverageCounts / leftoverMapIncompletePostCount / leftoverMapIncompleteItemCount / leftoverMapCompareAxisBadge / leftoverMapCompareAxisTickBadge return usable values. | `LeftoverPairList`, `LeftoverMapPlot`, `ticket-list`, `post-badge` | | `Workspace/OperationsDashboard` | Compare Event and post counts, inspect external-information coverage, then open the cited source behind a claim, handover, or repeat-issue fact. `EvidenceReady`, `NarrowViewport`, `AnalysisPendingAndMissingEvidence`, `AnalysisFailed`, and `LoadError` cover populated, mobile, unavailable-evidence, analysis-pending, retryable failure, and transport-error states. | `--color-dashboard-*`, `OperationsDashboard` | | `Post/SimilarVocPanel` | Compare ontology/semantic similar VOC and prior action evidence, then open the source; unavailable states show no fabricated TEPP theta or weight. | `SimilarVocPanel.css`, `SimilarVocPanel` | | `Post/Recorded perspectives` | Read the imported primary and every evidence-connected additional Voice with its recorded truth state instead of flattening them into one compound category. `CombinedEvidence`, `RejectedEvidence`, and `NarrowViewport` cover desktop, rejected-evidence, and narrow layouts. | `VoicePerspectiveList`, `ticket-list`, `post-meta` | diff --git a/frontend/package.json b/frontend/package.json index e847e1ee5..2f406ae4e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.85.0", + "version": "2.86.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index b2704c933..af0286645 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -4183,6 +4183,12 @@ describe("App, authenticated", () => { expect(screen.queryByText("leftover axis 2 σ 0.86")).not.toBeInTheDocument(); expect(screen.getByText("leftover map comparison leftover axis 1 σ 1.84 82%")).toBeInTheDocument(); expect(screen.getByText("leftover map comparison leftover axis 2 σ 0.86 18%")).toBeInTheDocument(); + expect(screen.getByText("leftover map comparison leftover axis 1 tick +0.50 σ 1.84")).toBeInTheDocument(); + expect(screen.getByText("leftover map comparison leftover axis 2 tick −0.02 σ 0.86")).toBeInTheDocument(); + expect(screen.getByText("leftover map comparison leftover axis 1 tick 0.00 σ 1.84")).toBeInTheDocument(); + expect(screen.getByText("leftover map comparison leftover axis 2 tick 0.00 σ 0.86")).toBeInTheDocument(); + expect(screen.queryByText("leftover axis 1 tick +0.50 σ 1.84")).not.toBeInTheDocument(); + expect(screen.queryByText("leftover-map axis 1 tick +0.50 σ 1.84")).not.toBeInTheDocument(); expect(screen.getByLabelText("Leftover map comparison leftover axis")).toHaveTextContent( "Open a leftover pair to read the post–criterion cell", ); @@ -4359,6 +4365,36 @@ describe("App, authenticated", () => { "leftover map comparison leftover axis 2 σ 0.86 18%", ), ).toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getByText( + "leftover map comparison leftover axis 1 tick +0.50 σ 1.84", + ), + ).toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getByText( + "leftover map comparison leftover axis 2 tick −0.02 σ 0.86", + ), + ).toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getByText( + "leftover map comparison leftover axis 1 tick 0.00 σ 1.84", + ), + ).toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getByText( + "leftover map comparison leftover axis 2 tick 0.00 σ 0.86", + ), + ).toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByText( + "leftover axis 1 tick +0.50 σ 1.84", + ), + ).not.toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByText( + /leftover map comparison leftover axis 1 tick \+0\.50 82%/, + ), + ).not.toBeInTheDocument(); expect( within(screen.getByLabelText("Grouping comparison")).queryByText("leftover axis 1 σ 1.84 82%"), ).not.toBeInTheDocument(); @@ -4707,6 +4743,16 @@ describe("App, authenticated", () => { "leftover-map axis 1 tick +0.50", ), ).not.toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getByText( + "leftover map comparison leftover axis 1 tick +0.50 σ 1.84", + ), + ).toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText( + "leftover map comparison leftover axis 1 tick +0.50 σ 1.84", + ), + ).not.toBeInTheDocument(); expect( within(screen.getByLabelText("Grouping comparison")).getByLabelText( "Leftover map comparison coordinates", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 83cb43c13..5345e157a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -142,11 +142,13 @@ import { } from "./leftoverMapAxisBadge"; import { leftoverMapCompareAxisBadge, + leftoverMapCompareAxisTickBadge, leftoverSingularForAxis, LEFTOVER_MAP_COMPARE_AXIS_CAPTION, LEFTOVER_MAP_COMPARE_AXIS_LABEL, } from "./leftoverMapPlotAxisSingular"; import { leftoverShareForAxis } from "./leftoverMapPlotAxisShare"; +import { layoutLeftoverMapPlot } from "./leftoverMapPlotLayout"; import { formatLeftoverMapReconstruction, LEFTOVER_MAP_COMPARE_RECONSTRUCTION_LABEL, @@ -4139,6 +4141,18 @@ function ReportsPanel({ {t(LEFTOVER_MAP_COMPARE_AXIS_CAPTION)}

) : null} + {layoutLeftoverMapPlot(row.leftover_pairs ?? [], criterionShortLabel)?.ticks.map((tick) => { + const badge = leftoverMapCompareAxisTickBadge( + tick.axis, + tick.label, + leftoverSingularForAxis(row.leftover_map_axes, tick.axis), + ); + return ( + + {tf(badge.key, badge.values)} + + ); + })} {row.leftover_pairs && row.leftover_pairs.length > 0 && ( <> diff --git a/frontend/src/components/LeftoverMapPlot.test.tsx b/frontend/src/components/LeftoverMapPlot.test.tsx index 530639f68..ec3c9e8ca 100644 --- a/frontend/src/components/LeftoverMapPlot.test.tsx +++ b/frontend/src/components/LeftoverMapPlot.test.tsx @@ -2710,6 +2710,12 @@ describe("LeftoverMapPlot", () => { expect(screen.queryByLabelText("leftover-map axis 1 tick +0.50 σ 1.84")).not.toBeInTheDocument(); expect(screen.queryByLabelText("leftover-map axis 2 tick −0.02")).not.toBeInTheDocument(); expect(screen.queryByLabelText("leftover-map axis 1 tick 0.00")).not.toBeInTheDocument(); + expect( + screen.queryByText("leftover map comparison leftover axis 1 tick +0.50 σ 1.84"), + ).not.toBeInTheDocument(); + expect( + screen.queryByLabelText("leftover map comparison leftover axis 1 tick +0.50 σ 1.84"), + ).not.toBeInTheDocument(); expect( screen.getByLabelText("leftover map comparison graphic leftover-map distance d 0.12"), ).toHaveTextContent("d 0.12"); @@ -2858,6 +2864,9 @@ describe("LeftoverMapPlot", () => { expect( screen.queryByText(/leftover map comparison graphic leftover-map axis 1 tick \+0\.50 \(82%\)/), ).not.toBeInTheDocument(); + expect( + screen.queryByText("leftover map comparison leftover axis 1 tick +0.50 σ 1.84"), + ).not.toBeInTheDocument(); }); it("omits leftover-map comparison graphic leftover-map axis tick leftover-map singular values independently of leftover-map axis share", () => { diff --git a/frontend/src/i18n.test.ts b/frontend/src/i18n.test.ts index dd86474dd..32d803298 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -116,6 +116,8 @@ describe("i18n", () => { "leftover map comparison leftover axis {axis} σ {value}", "leftover map comparison leftover axis {axis} σ {value} {share}%", "leftover map comparison leftover axis {axis} {share}%", + "leftover map comparison leftover axis {axis} tick {value}", + "leftover map comparison leftover axis {axis} tick {value} σ {singular}", "Leftover map comparison leftover axis", "Leftover map comparison leftover-axis share is Gabriel inertia of residual SVD axes 1 and 2. Open a leftover pair to read the post–criterion cell. The shares do not invent a leftover score.", "Leftover map comparison coordinates", @@ -797,6 +799,43 @@ describe("i18n", () => { }, ); + it.each([ + ["ko", "잔여 지도 비교 잔차 축 1 눈금 +0.50"], + ["zh", "残差地图比较残差轴 1 刻度 +0.50"], + ["ja", "残差マップの比較残差軸 1 目盛 +0.50"], + ["vi", "vạch trục phần dư so sánh bản đồ phần dư 1 +0.50"], + ] as const)( + "formats leftover map comparison leftover-axis ticks in %s", + (locale, expected) => { + setLocale(locale); + expect( + tf("leftover map comparison leftover axis {axis} tick {value}", { + axis: 1, + value: "+0.50", + }), + ).toBe(expected); + }, + ); + + it.each([ + ["ko", "잔여 지도 비교 잔차 축 1 눈금 +0.50 σ 1.84"], + ["zh", "残差地图比较残差轴 1 刻度 +0.50 σ 1.84"], + ["ja", "残差マップの比較残差軸 1 目盛 +0.50 σ 1.84"], + ["vi", "vạch trục phần dư so sánh bản đồ phần dư 1 +0.50 σ 1.84"], + ] as const)( + "formats leftover map comparison leftover-axis tick singular values in %s", + (locale, expected) => { + setLocale(locale); + expect( + tf("leftover map comparison leftover axis {axis} tick {value} σ {singular}", { + axis: 1, + value: "+0.50", + singular: "1.84", + }), + ).toBe(expected); + }, + ); + it.each([ ["ko", "잔여 지도 비교 잔차 축 1 σ 1.84 82%"], ["zh", "残差地图比较残差轴 1 σ 1.84 82%"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 7ab06a290..006c23e66 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -306,6 +306,9 @@ const TRANSLATIONS: Partial>> = { "leftover map comparison leftover axis {axis} σ {value} {share}%": "잔여 지도 비교 잔차 축 {axis} σ {value} {share}%", "leftover map comparison leftover axis {axis} {share}%": "잔여 지도 비교 잔차 축 {axis} {share}%", + "leftover map comparison leftover axis {axis} tick {value}": "잔여 지도 비교 잔차 축 {axis} 눈금 {value}", + "leftover map comparison leftover axis {axis} tick {value} σ {singular}": + "잔여 지도 비교 잔차 축 {axis} 눈금 {value} σ {singular}", "Leftover map comparison leftover axis": "잔여 지도 비교 잔차 축", "Leftover map comparison leftover-axis share is Gabriel inertia of residual SVD axes 1 and 2. Open a leftover pair to read the post–criterion cell. The shares do not invent a leftover score.": "잔여 지도 비교 잔차 축 비율은 잔차 SVD 축 1과 2의 Gabriel 관성입니다. 글–기준 셀을 읽으려면 잔차 쌍을 여세요. 이 비율은 잔차 점수를 만들어내지 않습니다.", @@ -966,6 +969,9 @@ const TRANSLATIONS: Partial>> = { "leftover map comparison leftover axis {axis} σ {value} {share}%": "残差地图比较残差轴 {axis} σ {value} {share}%", "leftover map comparison leftover axis {axis} {share}%": "残差地图比较残差轴 {axis} {share}%", + "leftover map comparison leftover axis {axis} tick {value}": "残差地图比较残差轴 {axis} 刻度 {value}", + "leftover map comparison leftover axis {axis} tick {value} σ {singular}": + "残差地图比较残差轴 {axis} 刻度 {value} σ {singular}", "Leftover map comparison leftover axis": "残差地图比较残差轴", "Leftover map comparison leftover-axis share is Gabriel inertia of residual SVD axes 1 and 2. Open a leftover pair to read the post–criterion cell. The shares do not invent a leftover score.": "残差地图比较残差轴份额是残差 SVD 轴 1 和 2 的 Gabriel 惯性。打开残差对以阅读帖文–准则单元格。这些份额不编造残差分数。", @@ -1647,6 +1653,9 @@ const TRANSLATIONS: Partial>> = { "leftover map comparison leftover axis {axis} σ {value} {share}%": "残差マップの比較残差軸 {axis} σ {value} {share}%", "leftover map comparison leftover axis {axis} {share}%": "残差マップの比較残差軸 {axis} {share}%", + "leftover map comparison leftover axis {axis} tick {value}": "残差マップの比較残差軸 {axis} 目盛 {value}", + "leftover map comparison leftover axis {axis} tick {value} σ {singular}": + "残差マップの比較残差軸 {axis} 目盛 {value} σ {singular}", "Leftover map comparison leftover axis": "残差マップの比較残差軸", "Leftover map comparison leftover-axis share is Gabriel inertia of residual SVD axes 1 and 2. Open a leftover pair to read the post–criterion cell. The shares do not invent a leftover score.": "残差マップの比較残差軸の割合は残差 SVD 軸 1 と 2 の Gabriel 慣性です。投稿–基準セルを読むには残差ペアを開いてください。この割合は残差スコアを作りません。", @@ -2310,6 +2319,10 @@ const TRANSLATIONS: Partial>> = { "trục phần dư so sánh bản đồ phần dư {axis} σ {value} {share}%", "leftover map comparison leftover axis {axis} {share}%": "trục phần dư so sánh bản đồ phần dư {axis} {share}%", + "leftover map comparison leftover axis {axis} tick {value}": + "vạch trục phần dư so sánh bản đồ phần dư {axis} {value}", + "leftover map comparison leftover axis {axis} tick {value} σ {singular}": + "vạch trục phần dư so sánh bản đồ phần dư {axis} {value} σ {singular}", "Leftover map comparison leftover axis": "Trục phần dư so sánh bản đồ phần dư", "Leftover map comparison leftover-axis share is Gabriel inertia of residual SVD axes 1 and 2. Open a leftover pair to read the post–criterion cell. The shares do not invent a leftover score.": "Tỷ phần trục phần dư so sánh bản đồ phần dư là quán tính Gabriel của trục SVD phần dư 1 và 2. Mở cặp phần dư để đọc ô bài–tiêu chí. Các tỷ phần này không tạo ra điểm phần dư.", diff --git a/frontend/src/leftoverMapAxisBadge.ts b/frontend/src/leftoverMapAxisBadge.ts index 31238556e..0a57f7869 100644 --- a/frontend/src/leftoverMapAxisBadge.ts +++ b/frontend/src/leftoverMapAxisBadge.ts @@ -10,6 +10,8 @@ * leftoverMapPlotTickAxisBadge, not this helper. * ADR 0328 captions leftover-map comparison graphic leftover-map axis ticks as * leftoverMapComparePlotTickAxisBadge, not this helper. + * ADR 0329 captions leftover-map comparison leftover-axis ticks as + * leftoverMapCompareAxisTickBadge, not this helper. */ import type { LeftoverMapAxis } from "./api"; diff --git a/frontend/src/leftoverMapPlotAxisSingular.test.ts b/frontend/src/leftoverMapPlotAxisSingular.test.ts index 25dc7b7e4..4154319a2 100644 --- a/frontend/src/leftoverMapPlotAxisSingular.test.ts +++ b/frontend/src/leftoverMapPlotAxisSingular.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import { formatLeftoverMapPlotAxisSingular, leftoverMapCompareAxisBadge, + leftoverMapCompareAxisTickBadge, leftoverMapComparePlotAxisBadge, leftoverMapComparePlotTickAxisBadge, leftoverMapPlotAxisBadge, @@ -12,6 +13,8 @@ import { LEFTOVER_MAP_COMPARE_AXIS_SHARE, LEFTOVER_MAP_COMPARE_AXIS_SINGULAR, LEFTOVER_MAP_COMPARE_AXIS_SINGULAR_SHARE, + LEFTOVER_MAP_COMPARE_AXIS_TICK, + LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR, LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR, LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR_SHARE, LEFTOVER_MAP_COMPARE_PLOT_TICK_SINGULAR, @@ -363,6 +366,8 @@ describe("leftover-map graphic leftover-map axis tick leftover-map singular valu expect(LEFTOVER_MAP_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_AXIS_BADGE_SINGULAR_ONLY); expect(LEFTOVER_MAP_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_TICK); expect(LEFTOVER_MAP_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_TICK_SINGULAR); + expect(LEFTOVER_MAP_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_AXIS_TICK); + expect(LEFTOVER_MAP_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR); expect(leftoverMapPlotTickAxisBadge(1, "+0.50", 1.84).key).not.toBe( leftoverMapPlotAxisBadge(1, 1.84, null)?.key, ); @@ -435,6 +440,8 @@ describe("leftover-map comparison graphic leftover-map axis tick leftover-map si expect(LEFTOVER_MAP_COMPARE_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR); expect(LEFTOVER_MAP_COMPARE_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_AXIS_SINGULAR); expect(LEFTOVER_MAP_COMPARE_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_AXIS_BADGE_SINGULAR_ONLY); + expect(LEFTOVER_MAP_COMPARE_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR); + expect(LEFTOVER_MAP_COMPARE_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_AXIS_TICK); expect(leftoverMapComparePlotTickAxisBadge(1, "+0.50", 1.84).key).not.toBe( leftoverMapPlotTickAxisBadge(1, "+0.50", 1.84).key, ); @@ -446,3 +453,88 @@ describe("leftover-map comparison graphic leftover-map axis tick leftover-map si ); }); }); + +describe("leftover-map comparison leftover-axis tick leftover-map singular values", () => { + it("names persisted leftover-map singular values on leftover-map comparison leftover-axis ticks", () => { + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84)).toEqual({ + key: LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR, + values: { axis: 1, value: "+0.50", singular: "1.84" }, + }); + expect(leftoverMapCompareAxisTickBadge(2, "−0.02", 0.86)).toEqual({ + key: LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR, + values: { axis: 2, value: "−0.02", singular: "0.86" }, + }); + }); + + it("names rank-0 zero leftover-map singular values on leftover-map comparison leftover-axis ticks", () => { + expect(leftoverMapCompareAxisTickBadge(1, "0.00", 0)).toEqual({ + key: LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR, + values: { axis: 1, value: "0.00", singular: "0.00" }, + }); + }); + + it("omits leftover-map singular values independently of leftover-map axis share", () => { + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", Number.NaN)).toEqual({ + key: LEFTOVER_MAP_COMPARE_AXIS_TICK, + values: { axis: 1, value: "+0.50" }, + }); + expect(leftoverMapCompareAxisTickBadge(2, "−0.02", -0.01)).toEqual({ + key: LEFTOVER_MAP_COMPARE_AXIS_TICK, + values: { axis: 2, value: "−0.02" }, + }); + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84).values.share).toBeUndefined(); + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", null)).toEqual({ + key: LEFTOVER_MAP_COMPARE_AXIS_TICK, + values: { axis: 1, value: "+0.50" }, + }); + }); + + it("does not invent leftover-map singular values from leftover-map axis share", () => { + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", undefined).key).toBe( + LEFTOVER_MAP_COMPARE_AXIS_TICK, + ); + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", undefined).values.singular).toBeUndefined(); + }); + + it("does not invent leftover-map axis share from leftover-map singular values", () => { + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84).key).toBe( + LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR, + ); + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84).values.share).toBeUndefined(); + }); + + it("stays distinct from leftover-axis, leftover-map graphic axis, leftover-map graphic tick, comparison leftover-axis, and comparison graphic copy", () => { + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK).toBe( + "leftover map comparison leftover axis {axis} tick {value}", + ); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).toBe( + "leftover map comparison leftover axis {axis} tick {value} σ {singular}", + ); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_AXIS_TICK); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_TICK_SINGULAR); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_PLOT_TICK_SINGULAR); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_TICK); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_PLOT_TICK); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_PLOT_AXIS_SINGULAR); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_AXIS_SINGULAR); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_AXIS_BADGE_SINGULAR_ONLY); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_TICK); + expect(LEFTOVER_MAP_COMPARE_AXIS_TICK).not.toBe(LEFTOVER_MAP_PLOT_TICK); + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84).key).not.toBe( + leftoverMapComparePlotTickAxisBadge(1, "+0.50", 1.84).key, + ); + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84).key).not.toBe( + leftoverMapPlotTickAxisBadge(1, "+0.50", 1.84).key, + ); + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84).key).not.toBe( + leftoverMapCompareAxisBadge(1, 1.84, null)?.key, + ); + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84).key).not.toBe( + leftoverMapComparePlotAxisBadge(1, 1.84, null)?.key, + ); + expect(leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84).key).not.toBe( + leftoverMapPlotAxisBadge(1, 1.84, null)?.key, + ); + }); +}); diff --git a/frontend/src/leftoverMapPlotAxisSingular.ts b/frontend/src/leftoverMapPlotAxisSingular.ts index e27656b55..955b695f6 100644 --- a/frontend/src/leftoverMapPlotAxisSingular.ts +++ b/frontend/src/leftoverMapPlotAxisSingular.ts @@ -11,6 +11,9 @@ * axis share. * ADR 0323 captions leftover-axis report badges on the grouping comparison strip * as leftoverMapCompareAxisBadge, not leftoverMapComparePlotAxisBadge. + * ADR 0329 names leftover-map comparison leftover-axis ticks leftover-map + * singular values as leftoverMapCompareAxisTickBadge independently of leftover-map + * axis share, not leftoverMapComparePlotTickAxisBadge. */ import type { LeftoverMapAxis } from "./api"; @@ -50,6 +53,12 @@ export const LEFTOVER_MAP_COMPARE_AXIS_SINGULAR_SHARE = export const LEFTOVER_MAP_COMPARE_AXIS_SHARE = "leftover map comparison leftover axis {axis} {share}%"; +export const LEFTOVER_MAP_COMPARE_AXIS_TICK = + "leftover map comparison leftover axis {axis} tick {value}"; + +export const LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR = + "leftover map comparison leftover axis {axis} tick {value} σ {singular}"; + export const LEFTOVER_MAP_COMPARE_AXIS_LABEL = "Leftover map comparison leftover axis"; export const LEFTOVER_MAP_COMPARE_AXIS_CAPTION = @@ -188,3 +197,18 @@ export function leftoverMapComparePlotTickAxisBadge( values: { axis: axisIndex, value: tickLabel, singular }, }; } + +export function leftoverMapCompareAxisTickBadge( + axisIndex: number, + tickLabel: string, + leftoverSingular: number | null | undefined, +): LeftoverMapCompareAxisBadge { + const singular = formatLeftoverMapPlotAxisSingular(leftoverSingular); + if (singular === null) { + return { key: LEFTOVER_MAP_COMPARE_AXIS_TICK, values: { axis: axisIndex, value: tickLabel } }; + } + return { + key: LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR, + values: { axis: axisIndex, value: tickLabel, singular }, + }; +} diff --git a/frontend/src/leftoverMapPlotLayout.test.ts b/frontend/src/leftoverMapPlotLayout.test.ts index 08ab4c3b2..0de38643c 100644 --- a/frontend/src/leftoverMapPlotLayout.test.ts +++ b/frontend/src/leftoverMapPlotLayout.test.ts @@ -1378,6 +1378,12 @@ describe("leftover map comparison graphic labels", () => { expect(LEFTOVER_MAP_COMPARE_PLOT_TICK).not.toBe( "leftover map comparison leftover axis {axis} σ {value}", ); + expect(LEFTOVER_MAP_COMPARE_PLOT_TICK).not.toBe( + "leftover map comparison leftover axis {axis} tick {value}", + ); + expect(LEFTOVER_MAP_COMPARE_PLOT_TICK).not.toBe( + "leftover map comparison leftover axis {axis} tick {value} σ {singular}", + ); expect(LEFTOVER_MAP_COMPARE_PLOT_TICK).not.toBe( LEFTOVER_MAP_COMPARE_PLOT_SEGMENT_DISTANCE, ); diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index 7df3ba874..4e72e247f 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -97,6 +97,9 @@ * ADR 0328 fail-closes leftover-map comparison graphic leftover-map axis ticks leftover-map * singular values through leftoverMapComparePlotTickAxisBadge independently of leftover-map * axis share. + * ADR 0329 fail-closes leftover-map comparison leftover-axis ticks leftover-map + * singular values through leftoverMapCompareAxisTickBadge independently of leftover-map + * axis share, not this graphic. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; diff --git a/pyproject.toml b/pyproject.toml index 60a1c5783..f11e6bdff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.85.0" +version = "2.86.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" }