From e9e4c2a622219bfbddfe71c74b14483f7b36aa5c Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 31 Aug 2026 04:31:10 +0000
Subject: [PATCH] feat(reports): leftover-map singular values on leftover-axis
ticks independently of leftover-map axis share (v2.87.0)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Caption leftover-axis ticks with persisted leftover-map singular values σ_k through leftoverMapAxisTickBadge independently of leftover-map axis share (ADR 0330). 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 +-
.../2.87.0-leftover-map-axis-tick-badge.md | 22 ++++
CHANGELOG.md | 21 ++++
CLAUDE.md | 2 +-
docs/adr/0148-leftover-map-axis-share.md | 4 +-
...29-leftover-map-compare-axis-tick-badge.md | 4 +-
docs/adr/0330-leftover-map-axis-tick-badge.md | 114 ++++++++++++++++++
docs/product-technical-gap-baseline.md | 19 +++
docs/storybook-inventory.md | 2 +-
frontend/package.json | 2 +-
frontend/src/App.test.tsx | 5 +-
frontend/src/App.tsx | 13 ++
.../src/components/LeftoverMapPlot.test.tsx | 1 +
frontend/src/i18n.test.ts | 28 +++++
frontend/src/i18n.ts | 8 ++
frontend/src/leftoverMapAxisBadge.test.ts | 79 ++++++++++++
frontend/src/leftoverMapAxisBadge.ts | 21 ++++
.../src/leftoverMapPlotAxisSingular.test.ts | 2 +
frontend/src/leftoverMapPlotAxisSingular.ts | 3 +
frontend/src/leftoverMapPlotLayout.test.ts | 2 +
frontend/src/leftoverMapPlotLayout.ts | 3 +
pyproject.toml | 2 +-
23 files changed, 364 insertions(+), 8 deletions(-)
create mode 100644 CHANGELOG.d/2.87.0-leftover-map-axis-tick-badge.md
create mode 100644 docs/adr/0330-leftover-map-axis-tick-badge.md
diff --git a/AGENTS.md b/AGENTS.md
index 5bc1c6054..64d26c5df 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -699,6 +699,15 @@ axis share. Rank-0 unused axes still name leftover-map comparison leftover-axis
`σ 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.
+ADR 0330 captions leftover-axis ticks with persisted leftover-map
+singular values `σ_k` when leftoverMapAxisTickBadge returns a usable leftover-axis
+tick caption. Distinct accessible name
+`leftover axis {axis} tick {value} σ {singular}`. Singular value omits independently
+of leftover-map axis share. Leftover-axis ticks never name leftover-map
+axis share. Rank-0 unused axes still name 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 leftover-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 979f60c77..2d798c8f2 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -770,6 +770,9 @@ 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.
+ADR 0330 captions leftover-axis ticks with persisted leftover-map
+singular values `σ_k` when leftoverMapAxisTickBadge 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
@@ -829,7 +832,8 @@ comparison graphic leftover-map axis leftover-map singular values as leftoverMap
(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), leftover-map comparison leftover-axis tick leftover-map singular values independently of leftover-map axis share
-(ADR 0329);
+(ADR 0329), leftover-axis tick leftover-map singular values independently of leftover-map axis share
+(ADR 0330);
`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.87.0-leftover-map-axis-tick-badge.md b/CHANGELOG.d/2.87.0-leftover-map-axis-tick-badge.md
new file mode 100644
index 000000000..2e0ee36b4
--- /dev/null
+++ b/CHANGELOG.d/2.87.0-leftover-map-axis-tick-badge.md
@@ -0,0 +1,22 @@
+## 2.87.0 — Leftover-map singular values on leftover-axis ticks independently of leftover-map axis share
+
+- Caption leftover-axis ticks with persisted leftover-map singular values `σ_k`
+ through leftoverMapAxisTickBadge independently of leftover-map axis share
+ (ADR 0330). After `make seed`, leftover-axis ticks name persisted leftover-map
+ singular values when leftoverMapAxisTickBadge 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 axis {k} tick {value}`. Rank-0 unused
+ axes still name `σ 0.00`. Leftover-axis ticks stay
+ `leftover axis {k} tick {value} σ {singular}` when `σ_k` is finite so they stay
+ distinct from leftover-map comparison leftover-axis ticks
+ `leftover map comparison leftover axis {k} tick {value} σ {singular}`, 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-axis
+ `leftover axis {k} σ {value}`, and from leftover-map graphic leftover-map axis
+ `leftover-map axis {k} σ {value}`. Do not invent `σ_k` from leftover-map axis
+ share. Do not invent leftover-map axis share from `σ_k`. Leftover-axis ticks
+ never name leftover-map axis share. This increment does not change leftover-map
+ comparison leftover-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 c4b186be4..d9aa3a434 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,27 @@ All notable changes to this project are documented here. Format follows
### Added
+- Period leftover pairs now caption leftover-axis ticks with persisted leftover-map
+ singular values `σ_k` through leftoverMapAxisTickBadge independently of leftover-map
+ axis share (ADR 0330 / v2.87.0). After `make seed`, leftover-axis ticks name
+ persisted leftover-map singular values when leftoverMapAxisTickBadge 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 axis {k} tick {value}`.
+ Rank-0 unused axes still name `σ 0.00`. Leftover-axis ticks stay
+ `leftover axis {k} tick {value} σ {singular}` when `σ_k` is finite so they stay
+ distinct from leftover-map comparison leftover-axis ticks
+ `leftover map comparison leftover axis {k} tick {value} σ {singular}`, 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-axis
+ `leftover axis {k} σ {value}`, and from leftover-map graphic leftover-map axis
+ `leftover-map axis {k} σ {value}`. Do not invent `σ_k` from leftover-map axis
+ share. Do not invent leftover-map axis share from `σ_k`. Leftover-axis ticks
+ never name leftover-map axis share. This increment does not change leftover-map
+ comparison leftover-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 leftover-axis
ticks with persisted leftover-map singular values `σ_k` through
leftoverMapCompareAxisTickBadge independently of leftover-map axis share
diff --git a/CLAUDE.md b/CLAUDE.md
index 7d9b6f1d8..e9bf54c9b 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 / 0329), 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 / 0330), 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 4dc548400..0ee0869db 100644
--- a/docs/adr/0148-leftover-map-axis-share.md
+++ b/docs/adr/0148-leftover-map-axis-share.md
@@ -19,7 +19,9 @@
[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);
[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)
+(leftover-map comparison leftover-axis tick leftover-map singular values independently of leftover-map axis share);
+[ADR 0330](0330-leftover-map-axis-tick-badge.md)
+(leftover-axis tick leftover-map singular values independently of leftover-map axis share)
## Context
diff --git a/docs/adr/0329-leftover-map-compare-axis-tick-badge.md b/docs/adr/0329-leftover-map-compare-axis-tick-badge.md
index 7ea9e9f5f..bd849cf59 100644
--- a/docs/adr/0329-leftover-map-compare-axis-tick-badge.md
+++ b/docs/adr/0329-leftover-map-compare-axis-tick-badge.md
@@ -116,7 +116,9 @@ report badges independently of leftover-map axis share
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)).
+([ADR 0320](0320-leftover-map-compare-plot-ticks.md)). Independent of leftover-axis
+tick leftover-map singular values independently of leftover-map axis share
+([ADR 0330](0330-leftover-map-axis-tick-badge.md)).
## References
diff --git a/docs/adr/0330-leftover-map-axis-tick-badge.md b/docs/adr/0330-leftover-map-axis-tick-badge.md
new file mode 100644
index 000000000..ee425ea24
--- /dev/null
+++ b/docs/adr/0330-leftover-map-axis-tick-badge.md
@@ -0,0 +1,114 @@
+# ADR 0330 — Name leftover-map singular values on 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 independently of leftover-map
+axis share ([ADR 0325](0325-leftover-map-axis-singular-only.md)), leftover-map axis share
+persistence ([ADR 0148](0148-leftover-map-axis-share.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)), 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)).
+
+## Context
+
+ADR 0148 already persists leftover-map singular values `σ_k` and leftover-map
+axis share `σ_k² / Σ_j σ_j²`. ADR 0325 already captions leftover-axis report
+badges as leftoverMapAxisBadge independently of leftover-map axis share. ADR 0270
+already ticks leftover-map graphic leftover-map axes at persisted `ξ` / `ζ`
+projections. ADR 0327 already captions leftover-map graphic leftover-map axis ticks
+with `σ_k` through leftoverMapPlotTickAxisBadge. ADR 0329 already captions leftover-map
+comparison leftover-axis ticks with `σ_k` through leftoverMapCompareAxisTickBadge.
+Those leftover-axis report badges still omit leftover-axis ticks, so a buyer who
+reads leftover-axis badges or leftover-map 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-axis ticks never
+name leftover-map axis share.
+
+This increment names leftover-axis ticks leftover-map singular values as leftoverMapAxisTickBadge,
+matching leftoverMapAxisBadge, leftoverMapCompareAxisTickBadge, leftoverMapPlotTickAxisBadge,
+leftoverMapComparePlotTickAxisBadge, leftoverMapCompareAxisBadge, leftoverMapComparePlotAxisBadge,
+and leftoverMapPlotAxisBadge. Leftover-axis ticks reuse `layoutLeftoverMapPlot` ticks so origin
+`0.00` plus unique finite `ξ` / `ζ` projections match the leftover-map graphic. Copy stays
+`leftover axis {k} tick {value} σ {singular}` when `σ_k` is finite so they stay distinct from leftover-map
+comparison leftover-axis ticks `leftover map comparison leftover axis {k} tick {value} σ {singular}`
+(ADR 0329), 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-axis
+`leftover axis {k} σ {value}` (ADR 0325), and from leftover-map graphic leftover-map axis
+`leftover-map axis {k} σ {value}` (ADR 0324). It does not add columns. Do not invent a leftover score.
+Do not invent a theta.
+
+This protected increment uses **0330** so it does not collide with leftover-map comparison leftover-axis
+tick leftover-map singular values independently of leftover-map axis share (0329), 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 singular values on leftover-axis
+report badges independently of leftover-map axis share (0325), leftover-map axis share persistence (0148), or the dashboard stacks.
+
+## Decision
+
+On leftover-axis ticks on the leftover-pair list leftover-axis surface, caption leftover-map axis `k`
+tick `{value}` with persisted leftover-map singular value `σ_k` when leftoverMapAxisTickBadge 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-axis ticks:
+no `σ_k` keeps `leftover axis {k} tick {value}`; `σ_k` only is
+`leftover axis {k} tick {value} σ {singular}`. A missing, non-finite, or negative singular value
+omits that `σ` tick caption and keeps a usable 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-axis ticks. If `layoutLeftoverMapPlot` is null, omit leftover-axis ticks. Click a leftover
+pair to open that post.
+
+Leftover-axis tick leftover-map singular values omit independently of leftover-map comparison leftover-axis
+tick leftover-map singular values, 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 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-axis
+tick `σ` caption.
+
+This increment does not change leftover-map comparison leftover-axis ticks. Those leftover-map comparison leftover-axis
+ticks stay `leftover map comparison leftover axis {axis} tick {value} σ {singular}` (ADR 0329). 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-axis ticks name persisted leftover-map singular values when leftoverMapAxisTickBadge
+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-axis tick `σ 0.00`.
+Leftover-map comparison leftover-axis tick leftover-map singular values independently of leftover-map axis share
+(ADR 0329), 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-axis report badge leftover-map singular values independently of leftover-map axis share
+(ADR 0325), leftover-map graphic-display leftover-map singular values (ADR 0324), leftover-map singular values on leftover-axis
+report badges on the grouping comparison strip (ADR 0323), and leftover-map graphic leftover-map axis ticks (ADR 0270) remain.
+
+## Related
+
+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)). 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 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 leftover-map graphic ([ADR 0270](0270-leftover-map-coordinate-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 a49de5cb4..be94f06bf 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,5 +1,24 @@
# Product & Technical Gap Baseline
+> Exact-head loop overlay: 2026-08-31 13:28 KST. Protected `main` is
+> `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not
+> self-approve. #870 leftover-map singular values on leftover-map comparison leftover-axis
+> ticks independently of leftover-map axis share (v2.86.0 / ADR 0329) is
+> `b086d97`, stacked on #869 @ `98a29e4`. Independent APPROVE is missing. Next
+> buyer increment on this cycle: leftover-axis ticks naming persisted leftover-map
+> singular values independently of leftover-map axis share (ADR 0330 / v2.87.0)
+> delivered locally on `feat/leftover-map-axis-tick-badge-v2870`. Caption persisted leftover-map
+> singular values `σ_k` on leftover-axis ticks when leftoverMapAxisTickBadge returns
+> a usable leftover-axis tick `σ` caption. Distinct accessible name
+> `leftover axis {axis} tick {value} σ {singular}`. Rank-0 unused axes still name
+> `σ 0.00`. Singular value omits independently of leftover-map axis share. 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 leftover-axis
+> ticks this increment. Stack onto #870. 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 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
diff --git a/docs/storybook-inventory.md b/docs/storybook-inventory.md
index fdec8fc8b..6cf9d9046 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, 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` |
+| `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, leftover-axis ticks name persisted leftover-map singular values independently of leftover-map axis share under `leftover axis {axis} tick {value} σ {singular}`, 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 2f406ae4e..712ee4a66 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
- "version": "2.86.0",
+ "version": "2.87.0",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx
index af0286645..25999ef95 100644
--- a/frontend/src/App.test.tsx
+++ b/frontend/src/App.test.tsx
@@ -4187,7 +4187,10 @@ describe("App, authenticated", () => {
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.getByText("leftover axis 1 tick +0.50 σ 1.84")).toBeInTheDocument();
+ expect(screen.getByText("leftover axis 2 tick −0.02 σ 0.86")).toBeInTheDocument();
+ expect(screen.getByText("leftover axis 1 tick 0.00 σ 1.84")).toBeInTheDocument();
+ expect(screen.getByText("leftover axis 2 tick 0.00 σ 0.86")).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",
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 5345e157a..e4558d4e5 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -139,6 +139,7 @@ import {
} from "./leftoverMapCoverage";
import {
leftoverMapAxisBadge,
+ leftoverMapAxisTickBadge,
} from "./leftoverMapAxisBadge";
import {
leftoverMapCompareAxisBadge,
@@ -3964,6 +3965,18 @@ function ReportsPanel({
)}
) : null}
+ {layoutLeftoverMapPlot(report.leftover_pairs ?? [], criterionShortLabel)?.ticks.map((tick) => {
+ const badge = leftoverMapAxisTickBadge(
+ tick.axis,
+ tick.label,
+ leftoverSingularForAxis(report.leftover_map_axes, tick.axis),
+ );
+ return (
+
+ {tf(badge.key, badge.values)}
+
+ );
+ })}
{report.leftover_pairs && report.leftover_pairs.length > 0 && (
{
expect(
screen.queryByLabelText("leftover map comparison leftover axis 1 tick +0.50 σ 1.84"),
).not.toBeInTheDocument();
+ expect(screen.queryByText("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");
diff --git a/frontend/src/i18n.test.ts b/frontend/src/i18n.test.ts
index 32d803298..00f665876 100644
--- a/frontend/src/i18n.test.ts
+++ b/frontend/src/i18n.test.ts
@@ -45,6 +45,8 @@ describe("i18n", () => {
"leftover axis {axis} {share}%",
"leftover axis {axis} σ {value}",
"leftover axis {axis} σ {value} {share}%",
+ "leftover axis {axis} tick {value}",
+ "leftover axis {axis} tick {value} σ {singular}",
"Leftover-map axis share",
"Leftover-map axis share is Gabriel inertia of residual SVD axes 1 and 2. Leftover-map singular values are the Gabriel scale of those axes. Open a leftover pair to read the post–criterion cell. The shares and singular values do not invent a leftover score.",
"Leftover pairs",
@@ -781,6 +783,32 @@ describe("i18n", () => {
expect(tf("leftover axis {axis} σ {value}", { axis: 1, value: "1.84" })).toBe(expected);
});
+ it.each([
+ ["ko", "잔차 축 1 눈금 +0.50"],
+ ["zh", "残差轴 1 刻度 +0.50"],
+ ["ja", "残差軸 1 目盛 +0.50"],
+ ["vi", "vạch trục phần dư 1 +0.50"],
+ ] as const)("formats leftover-axis ticks in %s", (locale, expected) => {
+ setLocale(locale);
+ expect(tf("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ư 1 +0.50 σ 1.84"],
+ ] as const)("formats leftover-axis tick singular values in %s", (locale, expected) => {
+ setLocale(locale);
+ expect(
+ tf("leftover axis {axis} tick {value} σ {singular}", {
+ axis: 1,
+ value: "+0.50",
+ singular: "1.84",
+ }),
+ ).toBe(expected);
+ });
+
it.each([
["ko", "잔여 지도 비교 잔차 축 1 σ 1.84"],
["zh", "残差地图比较残差轴 1 σ 1.84"],
diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts
index 006c23e66..745722b7f 100644
--- a/frontend/src/i18n.ts
+++ b/frontend/src/i18n.ts
@@ -620,6 +620,8 @@ const TRANSLATIONS: Partial>> = {
"leftover axis {axis} {share}%": "잔차 축 {axis} {share}%",
"leftover axis {axis} σ {value}": "잔차 축 {axis} σ {value}",
"leftover axis {axis} σ {value} {share}%": "잔차 축 {axis} σ {value} {share}%",
+ "leftover axis {axis} tick {value}": "잔차 축 {axis} 눈금 {value}",
+ "leftover axis {axis} tick {value} σ {singular}": "잔차 축 {axis} 눈금 {value} σ {singular}",
"Leftover-map axis share": "잔차 지도 축 비율",
"Leftover-map axis share is Gabriel inertia of residual SVD axes 1 and 2. Leftover-map singular values are the Gabriel scale of those axes. Open a leftover pair to read the post–criterion cell. The shares and singular values do not invent a leftover score.":
"잔차 지도 축 비율은 잔차 SVD 축 1과 2의 Gabriel 관성입니다. 잔차 지도 특이값은 그 축의 Gabriel 척도입니다. 글–기준 셀을 읽으려면 잔차 쌍을 여세요. 이 비율과 특이값은 잔차 점수를 만들어내지 않습니다.",
@@ -1279,6 +1281,8 @@ const TRANSLATIONS: Partial>> = {
"leftover axis {axis} {share}%": "残差轴 {axis} {share}%",
"leftover axis {axis} σ {value}": "残差轴 {axis} σ {value}",
"leftover axis {axis} σ {value} {share}%": "残差轴 {axis} σ {value} {share}%",
+ "leftover axis {axis} tick {value}": "残差轴 {axis} 刻度 {value}",
+ "leftover axis {axis} tick {value} σ {singular}": "残差轴 {axis} 刻度 {value} σ {singular}",
"Leftover-map axis share": "残差图轴占比",
"Leftover-map axis share is Gabriel inertia of residual SVD axes 1 and 2. Leftover-map singular values are the Gabriel scale of those axes. Open a leftover pair to read the post–criterion cell. The shares and singular values do not invent a leftover score.":
"残差图轴占比是残差 SVD 第 1、2 轴的 Gabriel 惯量。残差图奇异值是这些轴的 Gabriel 尺度。打开一个残差配对可查看文章–准则单元格。这些占比和奇异值不会虚构残差分数。",
@@ -1942,6 +1946,8 @@ const TRANSLATIONS: Partial>> = {
"leftover axis {axis} {share}%": "残差軸 {axis} {share}%",
"leftover axis {axis} σ {value}": "残差軸 {axis} σ {value}",
"leftover axis {axis} σ {value} {share}%": "残差軸 {axis} σ {value} {share}%",
+ "leftover axis {axis} tick {value}": "残差軸 {axis} 目盛 {value}",
+ "leftover axis {axis} tick {value} σ {singular}": "残差軸 {axis} 目盛 {value} σ {singular}",
"Leftover-map axis share": "残差マップ軸の比率",
"Leftover-map axis share is Gabriel inertia of residual SVD axes 1 and 2. Leftover-map singular values are the Gabriel scale of those axes. Open a leftover pair to read the post–criterion cell. The shares and singular values do not invent a leftover score.":
"残差マップ軸の比率は、残差 SVD の第1軸と第2軸の Gabriel 慣性です。残差マップ特異値はそれらの軸の Gabriel 尺度です。投稿–基準セルを読むには残差ペアを開いてください。この比率と特異値から残差スコアを作りません。",
@@ -2609,6 +2615,8 @@ const TRANSLATIONS: Partial>> = {
"leftover axis {axis} {share}%": "trục phần dư {axis} {share}%",
"leftover axis {axis} σ {value}": "trục phần dư {axis} σ {value}",
"leftover axis {axis} σ {value} {share}%": "trục phần dư {axis} σ {value} {share}%",
+ "leftover axis {axis} tick {value}": "vạch trục phần dư {axis} {value}",
+ "leftover axis {axis} tick {value} σ {singular}": "vạch trục phần dư {axis} {value} σ {singular}",
"Leftover-map axis share": "Tỷ trọng trục bản đồ phần dư",
"Leftover-map axis share is Gabriel inertia of residual SVD axes 1 and 2. Leftover-map singular values are the Gabriel scale of those axes. Open a leftover pair to read the post–criterion cell. The shares and singular values do not invent a leftover score.":
"Tỷ trọng trục bản đồ phần dư là quán tính Gabriel của các trục SVD phần dư 1 và 2. Giá trị kỳ dị bản đồ phần dư là thang Gabriel của các trục đó. Mở một cặp phần dư để đọc ô bài viết–tiêu chí. Các tỷ trọng và giá trị kỳ dị này không tạo ra điểm phần dư.",
diff --git a/frontend/src/leftoverMapAxisBadge.test.ts b/frontend/src/leftoverMapAxisBadge.test.ts
index cc0c76001..5f3469b10 100644
--- a/frontend/src/leftoverMapAxisBadge.test.ts
+++ b/frontend/src/leftoverMapAxisBadge.test.ts
@@ -3,18 +3,28 @@ import {
leftoverMapAxisBadge,
leftoverMapAxisBadgeShare,
leftoverMapAxisBadgeSingular,
+ leftoverMapAxisTickBadge,
LEFTOVER_MAP_AXIS_BADGE_SHARE,
LEFTOVER_MAP_AXIS_BADGE_SINGULAR,
LEFTOVER_MAP_AXIS_BADGE_SINGULAR_ONLY,
+ LEFTOVER_MAP_AXIS_TICK,
+ LEFTOVER_MAP_AXIS_TICK_SINGULAR,
} from "./leftoverMapAxisBadge";
import {
+ leftoverMapCompareAxisTickBadge,
+ leftoverMapComparePlotTickAxisBadge,
+ leftoverMapPlotTickAxisBadge,
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,
LEFTOVER_MAP_PLOT_AXIS_SINGULAR,
LEFTOVER_MAP_PLOT_AXIS_SINGULAR_SHARE,
+ LEFTOVER_MAP_PLOT_TICK_SINGULAR,
} from "./leftoverMapPlotAxisSingular";
describe("leftoverMapAxisBadgeShare", () => {
@@ -127,3 +137,72 @@ describe("leftover-axis report badge leftover-map singular values", () => {
expect(leftoverMapAxisBadge(1, 1.84, undefined)?.values.share).toBeUndefined();
});
});
+
+describe("leftover-axis leftover-axis tick leftover-map singular values", () => {
+ it("names persisted leftover-map singular values on leftover-axis ticks", () => {
+ expect(leftoverMapAxisTickBadge(1, "+0.50", 1.84)).toEqual({
+ key: LEFTOVER_MAP_AXIS_TICK_SINGULAR,
+ values: { axis: 1, value: "+0.50", singular: "1.84" },
+ });
+ expect(leftoverMapAxisTickBadge(2, "−0.02", 0.86)).toEqual({
+ key: LEFTOVER_MAP_AXIS_TICK_SINGULAR,
+ values: { axis: 2, value: "−0.02", singular: "0.86" },
+ });
+ });
+
+ it("names rank-0 zero leftover-map singular values on leftover-axis ticks", () => {
+ expect(leftoverMapAxisTickBadge(1, "0.00", 0)).toEqual({
+ key: LEFTOVER_MAP_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(leftoverMapAxisTickBadge(1, "+0.50", Number.NaN)).toEqual({
+ key: LEFTOVER_MAP_AXIS_TICK,
+ values: { axis: 1, value: "+0.50" },
+ });
+ expect(leftoverMapAxisTickBadge(2, "−0.02", -0.01)).toEqual({
+ key: LEFTOVER_MAP_AXIS_TICK,
+ values: { axis: 2, value: "−0.02" },
+ });
+ expect(leftoverMapAxisTickBadge(1, "+0.50", 1.84).values.share).toBeUndefined();
+ expect(leftoverMapAxisTickBadge(1, "+0.50", null)).toEqual({
+ key: LEFTOVER_MAP_AXIS_TICK,
+ values: { axis: 1, value: "+0.50" },
+ });
+ });
+
+ it("does not invent leftover-map singular values from leftover-map axis share", () => {
+ expect(leftoverMapAxisTickBadge(1, "+0.50", undefined).key).toBe(LEFTOVER_MAP_AXIS_TICK);
+ expect(leftoverMapAxisTickBadge(1, "+0.50", undefined).values.singular).toBeUndefined();
+ });
+
+ it("does not invent leftover-map axis share from leftover-map singular values", () => {
+ expect(leftoverMapAxisTickBadge(1, "+0.50", 1.84).key).toBe(LEFTOVER_MAP_AXIS_TICK_SINGULAR);
+ expect(leftoverMapAxisTickBadge(1, "+0.50", 1.84).values.share).toBeUndefined();
+ });
+
+ it("stays distinct from leftover-map graphic tick, comparison leftover-axis tick, and leftover-axis badge copy", () => {
+ expect(LEFTOVER_MAP_AXIS_TICK).toBe("leftover axis {axis} tick {value}");
+ expect(LEFTOVER_MAP_AXIS_TICK_SINGULAR).toBe("leftover axis {axis} tick {value} σ {singular}");
+ expect(LEFTOVER_MAP_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_AXIS_TICK);
+ expect(LEFTOVER_MAP_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR);
+ expect(LEFTOVER_MAP_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_PLOT_TICK_SINGULAR);
+ expect(LEFTOVER_MAP_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_TICK_SINGULAR);
+ expect(LEFTOVER_MAP_AXIS_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_AXIS_BADGE_SINGULAR_ONLY);
+ expect(LEFTOVER_MAP_AXIS_TICK).not.toBe(LEFTOVER_MAP_COMPARE_AXIS_TICK);
+ expect(leftoverMapAxisTickBadge(1, "+0.50", 1.84).key).not.toBe(
+ leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84).key,
+ );
+ expect(leftoverMapAxisTickBadge(1, "+0.50", 1.84).key).not.toBe(
+ leftoverMapPlotTickAxisBadge(1, "+0.50", 1.84).key,
+ );
+ expect(leftoverMapAxisTickBadge(1, "+0.50", 1.84).key).not.toBe(
+ leftoverMapComparePlotTickAxisBadge(1, "+0.50", 1.84).key,
+ );
+ expect(leftoverMapAxisTickBadge(1, "+0.50", 1.84).key).not.toBe(
+ leftoverMapAxisBadge(1, 1.84, null)?.key,
+ );
+ });
+});
diff --git a/frontend/src/leftoverMapAxisBadge.ts b/frontend/src/leftoverMapAxisBadge.ts
index 0a57f7869..498f68d9d 100644
--- a/frontend/src/leftoverMapAxisBadge.ts
+++ b/frontend/src/leftoverMapAxisBadge.ts
@@ -12,6 +12,8 @@
* leftoverMapComparePlotTickAxisBadge, not this helper.
* ADR 0329 captions leftover-map comparison leftover-axis ticks as
* leftoverMapCompareAxisTickBadge, not this helper.
+ * ADR 0330 names leftover-map leftover-axis ticks leftover-map singular
+ * values as leftoverMapAxisTickBadge independently of leftover-map axis share.
*/
import type { LeftoverMapAxis } from "./api";
@@ -28,6 +30,10 @@ export const LEFTOVER_MAP_AXIS_BADGE_SINGULAR = "leftover axis {axis} σ {value}
export const LEFTOVER_MAP_AXIS_BADGE_SINGULAR_ONLY = "leftover axis {axis} σ {value}";
+export const LEFTOVER_MAP_AXIS_TICK = "leftover axis {axis} tick {value}";
+
+export const LEFTOVER_MAP_AXIS_TICK_SINGULAR = "leftover axis {axis} tick {value} σ {singular}";
+
export function leftoverMapAxisBadgeShare(
leftoverShare: LeftoverMapAxis["leftover_share"] | null | undefined,
): string | null {
@@ -65,3 +71,18 @@ export function leftoverMapAxisBadge(
values: { axis: axisIndex, value: singular as string, share: percent as string },
};
}
+
+export function leftoverMapAxisTickBadge(
+ axisIndex: number,
+ tickLabel: string,
+ leftoverSingular: number | null | undefined,
+): LeftoverMapCompareAxisBadge {
+ const singular = formatLeftoverMapPlotAxisSingular(leftoverSingular);
+ if (singular === null) {
+ return { key: LEFTOVER_MAP_AXIS_TICK, values: { axis: axisIndex, value: tickLabel } };
+ }
+ return {
+ key: LEFTOVER_MAP_AXIS_TICK_SINGULAR,
+ values: { axis: axisIndex, value: tickLabel, singular },
+ };
+}
diff --git a/frontend/src/leftoverMapPlotAxisSingular.test.ts b/frontend/src/leftoverMapPlotAxisSingular.test.ts
index 4154319a2..520fb2677 100644
--- a/frontend/src/leftoverMapPlotAxisSingular.test.ts
+++ b/frontend/src/leftoverMapPlotAxisSingular.test.ts
@@ -521,6 +521,8 @@ describe("leftover-map comparison leftover-axis tick leftover-map singular value
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(LEFTOVER_MAP_COMPARE_AXIS_TICK_SINGULAR).not.toBe("leftover axis {axis} tick {value} σ {singular}");
+ expect(LEFTOVER_MAP_COMPARE_AXIS_TICK).not.toBe("leftover axis {axis} tick {value}");
expect(leftoverMapCompareAxisTickBadge(1, "+0.50", 1.84).key).not.toBe(
leftoverMapComparePlotTickAxisBadge(1, "+0.50", 1.84).key,
);
diff --git a/frontend/src/leftoverMapPlotAxisSingular.ts b/frontend/src/leftoverMapPlotAxisSingular.ts
index 955b695f6..ab64a2f55 100644
--- a/frontend/src/leftoverMapPlotAxisSingular.ts
+++ b/frontend/src/leftoverMapPlotAxisSingular.ts
@@ -14,6 +14,9 @@
* ADR 0329 names leftover-map comparison leftover-axis ticks leftover-map
* singular values as leftoverMapCompareAxisTickBadge independently of leftover-map
* axis share, not leftoverMapComparePlotTickAxisBadge.
+ * ADR 0330 names leftover-map leftover-axis ticks leftover-map singular
+ * values as leftoverMapAxisTickBadge independently of leftover-map axis share,
+ * not leftoverMapCompareAxisTickBadge.
*/
import type { LeftoverMapAxis } from "./api";
diff --git a/frontend/src/leftoverMapPlotLayout.test.ts b/frontend/src/leftoverMapPlotLayout.test.ts
index 0de38643c..c1b68cffd 100644
--- a/frontend/src/leftoverMapPlotLayout.test.ts
+++ b/frontend/src/leftoverMapPlotLayout.test.ts
@@ -1384,6 +1384,8 @@ describe("leftover map comparison graphic labels", () => {
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 axis {axis} tick {value}");
+ expect(LEFTOVER_MAP_COMPARE_PLOT_TICK).not.toBe("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 4e72e247f..9d460a322 100644
--- a/frontend/src/leftoverMapPlotLayout.ts
+++ b/frontend/src/leftoverMapPlotLayout.ts
@@ -100,6 +100,9 @@
* 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.
+ * ADR 0330 fail-closes leftover-map leftover-axis ticks leftover-map
+ * singular values through leftoverMapAxisTickBadge independently of leftover-map
+ * axis share, not this graphic.
*/
import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates";
diff --git a/pyproject.toml b/pyproject.toml
index f11e6bdff..4cbf8bd6e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "lineageweave"
-version = "2.86.0"
+version = "2.87.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" }