From 5218a5c0e6821451efdfac0c1286ef7273081af7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 31 Aug 2026 03:23:59 +0000 Subject: [PATCH] feat(reports): leftover-map singular values on leftover-map graphic leftover-map axis ticks independently of leftover-map axis share (v2.84.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caption leftover-map graphic leftover-map axis ticks with persisted leftover-map singular values σ_k through leftoverMapPlotTickAxisBadge independently of leftover-map axis share (ADR 0327). Tick copy stays leftover-map axis {k} tick {value} σ {singular} when σ_k is finite so it stays distinct from leftover-map axis {k} σ {value}, leftover axis {k} σ {value}, leftover map comparison leftover axis {k} σ {value}, leftover map comparison graphic leftover-map axis {k} σ {value}, and leftover map comparison graphic leftover-map axis {k} tick {value}. Rank-0 unused axes still name σ 0.00. Singular value omits independently of leftover-map axis share. Leftover-map graphic leftover-map axis ticks never name leftover-map axis share. Do not invent σ_k from leftover-map axis share. Never invent a leftover score. Never invent a theta. --- AGENTS.md | 12 +- ARCHITECTURE.md | 6 +- ....84.0-leftover-map-plot-tick-axis-badge.md | 24 ++++ CHANGELOG.md | 23 ++++ CLAUDE.md | 2 +- docs/adr/0148-leftover-map-axis-share.md | 4 +- ...26-leftover-map-compare-plot-axis-badge.md | 3 + .../0327-leftover-map-plot-tick-axis-badge.md | 121 ++++++++++++++++++ docs/product-technical-gap-baseline.md | 21 +++ frontend/package.json | 2 +- .../src/components/LeftoverMapPlot.test.tsx | 22 +++- frontend/src/components/LeftoverMapPlot.tsx | 29 ++++- frontend/src/i18n.test.ts | 17 +++ frontend/src/i18n.ts | 5 + frontend/src/leftoverMapAxisBadge.ts | 2 + .../src/leftoverMapPlotAxisSingular.test.ts | 66 +++++++++- frontend/src/leftoverMapPlotAxisSingular.ts | 24 +++- frontend/src/leftoverMapPlotLayout.test.ts | 1 + frontend/src/leftoverMapPlotLayout.ts | 3 + pyproject.toml | 2 +- 20 files changed, 374 insertions(+), 15 deletions(-) create mode 100644 CHANGELOG.d/2.84.0-leftover-map-plot-tick-axis-badge.md create mode 100644 docs/adr/0327-leftover-map-plot-tick-axis-badge.md diff --git a/AGENTS.md b/AGENTS.md index 825fe408d..c82f0a49c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -273,7 +273,7 @@ stops startup instead of leaving a healthy-looking partial schema, and application code must not compensate for a missing table. Period leftover pairs (ADR 0017 / 0018 / 0048 / 0049 / 0119 / 0158 / 0162 / -0163 / 0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290 / 0291 / 0292 / 0293 / 0294 / 0295 / 0296 / 0297 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315 / 0316 / 0317 / 0318 / 0319 / 0320 / 0321 / 0322 / 0323 / 0324 / 0325 / 0326) are computed in `lineageweave/leftover_pairs.py` from the +0163 / 0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290 / 0291 / 0292 / 0293 / 0294 / 0295 / 0296 / 0297 / 0298 / 0299 / 0300 / 0301 / 0302 / 0303 / 0304 / 0305 / 0306 / 0307 / 0308 / 0309 / 0310 / 0311 / 0312 / 0313 / 0314 / 0315 / 0316 / 0317 / 0318 / 0319 / 0320 / 0321 / 0322 / 0323 / 0324 / 0325 / 0326 / 0327) are computed in `lineageweave/leftover_pairs.py` from the residual after a real GRM/GPCM score, never invented. Distances are Euclidean on the two-dimensional Gabriel leftover map; missing cells stay out of the factorization. Closest and farthest post–criterion pairs @@ -676,6 +676,13 @@ singular value omit independently. Rank-0 unused axes still name leftover-map comparison graphic leftover-map 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. +ADR 0327 captions leftover-map graphic leftover-map axis ticks with persisted leftover-map +singular values `σ_k` when leftoverMapPlotTickAxisBadge returns a usable leftover-map +axis tick caption. Distinct accessible name +`leftover-map axis {axis} tick {value} σ {singular}`. Singular value omits independently +of leftover-map axis share. Leftover-map graphic leftover-map axis ticks never name leftover-map +axis share. Rank-0 unused axes still name leftover-map graphic leftover-map axis tick +`σ 0.00`. Do not invent leftover-map singular values from leftover-map axis share. 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 @@ -789,6 +796,9 @@ Leftover-axis report badge leftover-map singular values independently of leftove Leftover-map comparison graphic leftover-map axis leftover-map singular values as leftoverMapComparePlotAxisBadge (ADR 0326) captions leftover-map comparison graphic leftover-map axes with persisted leftover-map singular values `σ_k` when leftoverMapComparePlotAxisBadge returns a usable leftover-map axis caption. +Leftover-map graphic leftover-map axis tick leftover-map singular values independently of leftover-map axis share +(ADR 0327) captions leftover-map graphic leftover-map axis ticks with persisted leftover-map +singular values `σ_k` when leftoverMapPlotTickAxisBadge returns a usable leftover-map axis tick caption. Item complete-case coverage (ADR 0282) captions the leftover-map graphic with how many scored criteria entered the map. Item complete-case coverage on the pair list (ADR 0285) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 869131453..edd4f3eec 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -761,6 +761,9 @@ singular values `σ_k` independently of leftover-map axis share. ADR 0326 captions leftover-map comparison graphic leftover-map axes with persisted leftover-map singular values `σ_k` when leftoverMapComparePlotAxisBadge returns a usable leftover-map axis caption. +ADR 0327 captions leftover-map graphic leftover-map axis ticks with persisted leftover-map +singular values `σ_k` when leftoverMapPlotTickAxisBadge returns a usable leftover-map +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 @@ -817,7 +820,8 @@ grouping comparison strip (ADR 0323), leftover-map singular values on leftover-m graphic-display axes (ADR 0324), leftover-map singular values on leftover-axis 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); +(ADR 0326), leftover-map graphic leftover-map axis tick leftover-map singular values independently of leftover-map axis share +(ADR 0327); `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.84.0-leftover-map-plot-tick-axis-badge.md b/CHANGELOG.d/2.84.0-leftover-map-plot-tick-axis-badge.md new file mode 100644 index 000000000..34a92f545 --- /dev/null +++ b/CHANGELOG.d/2.84.0-leftover-map-plot-tick-axis-badge.md @@ -0,0 +1,24 @@ +## 2.84.0 — Leftover-map singular values on leftover-map graphic leftover-map axis ticks independently of leftover-map axis share + +- Caption leftover-map graphic leftover-map axis ticks with persisted leftover-map + singular values `σ_k` through leftoverMapPlotTickAxisBadge independently of leftover-map + axis share (ADR 0327). After `make seed`, leftover-map graphic leftover-map axis + ticks name persisted leftover-map singular values when leftoverMapPlotTickAxisBadge + returns a usable leftover-map 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 axis {k} tick {value}`. Rank-0 unused axes still name `σ 0.00`. + Leftover-map graphic leftover-map axis ticks stay + `leftover-map axis {k} tick {value} σ {singular}` when `σ_k` is finite so they + stay distinct 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}`, from comparison graphic + leftover-map axis `leftover map comparison graphic leftover-map axis {k} σ {value}`, + and from comparison graphic leftover-map axis ticks + `leftover map comparison graphic leftover-map axis {k} tick {value}`. Do not invent + `σ_k` from leftover-map axis share. Do not invent leftover-map axis share from + `σ_k`. Leftover-map graphic leftover-map 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 e516a9baa..90e283b3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,29 @@ All notable changes to this project are documented here. Format follows ### Added +- Period leftover pairs now caption leftover-map graphic leftover-map axis ticks + with persisted leftover-map singular values `σ_k` through leftoverMapPlotTickAxisBadge + independently of leftover-map axis share (ADR 0327 / v2.84.0). After `make seed`, + leftover-map graphic leftover-map axis ticks name persisted leftover-map singular + values when leftoverMapPlotTickAxisBadge returns a usable leftover-map 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 axis {k} tick {value}`. Rank-0 unused + axes still name `σ 0.00`. Leftover-map graphic leftover-map axis ticks stay + `leftover-map axis {k} tick {value} σ {singular}` when `σ_k` is finite so they + stay distinct 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}`, from comparison graphic + leftover-map axis `leftover map comparison graphic leftover-map axis {k} σ {value}`, + and from comparison graphic leftover-map axis ticks + `leftover map comparison graphic leftover-map axis {k} tick {value}`. Do not invent + `σ_k` from leftover-map axis share. Do not invent leftover-map axis share from + `σ_k`. Leftover-map graphic leftover-map 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 axes with persisted leftover-map singular values `σ_k` through leftoverMapComparePlotAxisBadge (ADR 0326 / v2.83.0). After `make seed`, diff --git a/CLAUDE.md b/CLAUDE.md index 4b9edef76..ff52172c1 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), 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), 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 ca4f28f95..315ef199d 100644 --- a/docs/adr/0148-leftover-map-axis-share.md +++ b/docs/adr/0148-leftover-map-axis-share.md @@ -13,7 +13,9 @@ [ADR 0325](0325-leftover-map-axis-singular-only.md) (leftover-map singular values on leftover-axis report badges independently of leftover-map axis share); [ADR 0326](0326-leftover-map-compare-plot-axis-badge.md) -(leftover-map comparison graphic leftover-map axis leftover-map singular values as leftoverMapComparePlotAxisBadge) +(leftover-map comparison graphic leftover-map axis leftover-map singular values as leftoverMapComparePlotAxisBadge); +[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) ## Context diff --git a/docs/adr/0326-leftover-map-compare-plot-axis-badge.md b/docs/adr/0326-leftover-map-compare-plot-axis-badge.md index ccd02ce89..93c352492 100644 --- a/docs/adr/0326-leftover-map-compare-plot-axis-badge.md +++ b/docs/adr/0326-leftover-map-compare-plot-axis-badge.md @@ -117,6 +117,9 @@ Independent of leftover-map singular values on leftover-axis report badges ([ADR 0322](0322-leftover-map-axis-singular.md)). Independent of leftover-map singular values on the grouping comparison leftover-map graphic that still inline σ and share ([ADR 0321](0321-leftover-map-compare-plot-singular.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)). ## References diff --git a/docs/adr/0327-leftover-map-plot-tick-axis-badge.md b/docs/adr/0327-leftover-map-plot-tick-axis-badge.md new file mode 100644 index 000000000..b2d570c69 --- /dev/null +++ b/docs/adr/0327-leftover-map-plot-tick-axis-badge.md @@ -0,0 +1,121 @@ +# ADR 0327 — Name leftover-map singular values on leftover-map graphic leftover-map axis ticks independently of leftover-map axis share + +**Decision status:** Accepted +**Date:** 2026-08-31 + +Amends leftover-map coordinate ticks on the leftover-map graphic +([ADR 0270](0270-leftover-map-coordinate-ticks.md)), leftover-map singular +values on leftover-map graphic-display axes +([ADR 0324](0324-leftover-map-plot-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 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)), leftover-map coordinate ticks +on the grouping comparison leftover-map graphic +([ADR 0320](0320-leftover-map-compare-plot-ticks.md)). + +## Context + +ADR 0148 already persists leftover-map singular values `σ_k` and leftover-map +axis share `σ_k² / Σ_j σ_j²`. ADR 0270 already ticks leftover-map graphic leftover-map +axes at persisted `ξ` / `ζ` projections as `leftover-map axis {k} tick {value}`. +ADR 0324 already captions leftover-map graphic-display leftover-map axes with +`σ_k`. Those leftover-map graphic leftover-map axis ticks still omit `σ_k`, so a +buyer who reads 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-map graphic leftover-map axis ticks never name leftover-map +axis share. + +This increment names leftover-map graphic leftover-map axis ticks leftover-map +singular values as leftoverMapPlotTickAxisBadge, matching leftoverMapComparePlotAxisBadge, +leftoverMapPlotAxisBadge, leftoverMapCompareAxisBadge, and leftoverMapAxisBadge. +Leftover-map graphic leftover-map axis ticks stay +`leftover-map axis {k} tick {value} σ {singular}` when `σ_k` is finite so they stay +distinct 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), from comparison graphic leftover-map axis +`leftover map comparison graphic leftover-map axis {k} σ {value}` (ADR 0326), and +from comparison graphic leftover-map axis ticks +`leftover map comparison graphic leftover-map axis {k} tick {value}` (ADR 0320). +It does not add columns. Do not invent a leftover score. Do not invent a theta. + +This protected increment uses **0327** so it does not collide with 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-map graphic-display +axes (0324), leftover-map coordinate ticks on the grouping comparison leftover-map graphic +(0320), leftover-map axis share persistence (0148), or the dashboard stacks. + +## Decision + +On leftover-map graphic leftover-map axis ticks, caption leftover-map axis `k` +with persisted leftover-map singular value `σ_k` when leftoverMapPlotTickAxisBadge +returns a usable leftover-map 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 graphic leftover-map axis ticks: no `σ_k` +keeps `leftover-map axis {k} tick {value}`; `σ_k` only is +`leftover-map axis {k} tick {value} σ {singular}`. A missing, non-finite, or +negative singular value omits that `σ` tick caption and keeps a usable leftover-map +graphic leftover-map axis tick coordinate caption. Rank-0 unused axes still name +`σ 0.00`. Click a post marker to open that post. Criterion markers are not post +buttons. + +Leftover-map graphic leftover-map axis tick leftover-map singular values omit +independently of leftover-map graphic-display leftover-map axis singular values, +leftover-axis report badge leftover-map singular values, leftover-map comparison +leftover-axis singular values, leftover-map comparison graphic leftover-map axis +singular values, and leftover-map comparison graphic leftover-map axis ticks. A +missing leftover-map axis share omits leftover-map axis share and keeps a usable +leftover-map graphic leftover-map axis tick `σ` caption. + +This increment does not change leftover-map comparison graphic leftover-map axis +ticks. Those comparison graphic leftover-map axis ticks stay +`leftover map comparison graphic leftover-map axis {axis} tick {value}` (ADR 0320). +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 graphic leftover-map axis ticks name persisted +leftover-map singular values when leftoverMapPlotTickAxisBadge returns a usable +leftover-map 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 graphic leftover-map axis tick `σ 0.00`. 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), +and grouping comparison leftover-map graphic leftover-map axis ticks (ADR 0320) +remain. + +## Related + +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-map graphic-display axes +([ADR 0324](0324-leftover-map-plot-axis-singular.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 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 c52f1b12a..227ea53ca 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 12:20 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not +> self-approve. #867 leftover-map comparison graphic leftover-map axis leftover-map +> singular values as leftoverMapComparePlotAxisBadge (v2.83.0 / ADR 0326) is +> `cf7bb55`, stacked on #866 @ `593b8d8`. Independent APPROVE is missing. Next +> buyer increment on this cycle: leftover-map graphic leftover-map axis ticks +> naming persisted leftover-map singular values independently of leftover-map axis +> share (ADR 0327 / v2.84.0) delivered locally on +> `feat/leftover-map-plot-tick-axis-badge-v2840`. Caption persisted leftover-map +> singular values `σ_k` on leftover-map graphic leftover-map axis ticks when +> leftoverMapPlotTickAxisBadge returns a usable leftover-map axis tick `σ` +> caption. Distinct accessible name `leftover-map axis {axis} tick {value} σ {singular}`. +> Rank-0 unused axes still name `σ 0.00`. Singular value omits independently of +> leftover-map axis share. Leftover-map graphic leftover-map 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 #867. 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:05 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not > self-approve. #866 leftover-map singular values on leftover-axis report badges diff --git a/frontend/package.json b/frontend/package.json index ca2960c70..68ff7eed2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.83.0", + "version": "2.84.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/LeftoverMapPlot.test.tsx b/frontend/src/components/LeftoverMapPlot.test.tsx index 5adcea2ac..4ab720991 100644 --- a/frontend/src/components/LeftoverMapPlot.test.tsx +++ b/frontend/src/components/LeftoverMapPlot.test.tsx @@ -166,7 +166,8 @@ describe("LeftoverMapPlot", () => { ).toBeInTheDocument(); expect(screen.getByText("leftover-map axis 1 σ 0.00 (0%)")).toBeInTheDocument(); expect(screen.getByText("leftover-map axis 2 σ 0.00 (0%)")).toBeInTheDocument(); - expect(screen.getAllByLabelText("leftover-map axis 1 tick 0.00").length).toBeGreaterThan(0); + expect(screen.getAllByLabelText("leftover-map axis 1 tick 0.00 σ 0.00").length).toBeGreaterThan(0); + expect(screen.queryByLabelText("leftover-map axis 1 tick 0.00")).not.toBeInTheDocument(); expect(screen.queryByLabelText("leftover-map axis 1 tick +1.00")).not.toBeInTheDocument(); expect(screen.getByLabelText("leftover-map distance d 0.00")).toBeInTheDocument(); expect(screen.getByLabelText("leftover-map reconstruction R̂ 0.00")).toBeInTheDocument(); @@ -194,6 +195,25 @@ describe("LeftoverMapPlot", () => { ); expect(screen.getByText("leftover-map axis 1 σ 1.84 (82%)")).toBeInTheDocument(); expect(screen.getByText("leftover-map axis 2 σ 0.86 (18%)")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover-map axis 1 tick +0.50 σ 1.84")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover-map axis 2 tick −0.02 σ 0.86")).toBeInTheDocument(); + expect(screen.queryByLabelText("leftover-map axis 1 tick +0.50")).not.toBeInTheDocument(); + }); + + it("captions leftover-map graphic leftover-map axis ticks with leftover-map singular values independently of leftover-map axis share", () => { + render( + , + ); + expect(screen.getByLabelText("leftover-map axis 1 tick +0.50 σ 1.84")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover-map axis 1 tick 0.00 σ 1.84")).toBeInTheDocument(); + expect(screen.getByLabelText("leftover-map axis 2 tick −0.02")).toBeInTheDocument(); + expect(screen.queryByLabelText("leftover-map axis 1 tick +0.50")).not.toBeInTheDocument(); + expect(screen.queryByText(/leftover-map axis 1 tick \+0\.50 \(82%\)/)).not.toBeInTheDocument(); }); it("captions leftover-map graphic-display singular values when share is missing or non-finite", () => { diff --git a/frontend/src/components/LeftoverMapPlot.tsx b/frontend/src/components/LeftoverMapPlot.tsx index 3fda6999f..0c97ab533 100644 --- a/frontend/src/components/LeftoverMapPlot.tsx +++ b/frontend/src/components/LeftoverMapPlot.tsx @@ -27,6 +27,7 @@ import { import { leftoverMapComparePlotAxisBadge, leftoverMapPlotAxisBadge, + leftoverMapPlotTickAxisBadge, leftoverSingularForAxis, } from "../leftoverMapPlotAxisSingular"; import { @@ -58,7 +59,6 @@ import { LEFTOVER_MAP_PLOT_SEGMENT_RESIDUAL, LEFTOVER_MAP_PLOT_SEGMENT_UNEXPLAINED, LEFTOVER_MAP_PLOT_SEGMENT_UNEXPLAINED_SHARE, - LEFTOVER_MAP_PLOT_TICK, } from "../leftoverMapPlotLayout"; import "./LeftoverMapPlot.css"; @@ -98,6 +98,20 @@ function leftoverMapPlotAxisText( return tf(badge.key, badge.values); } +function leftoverMapPlotTickText( + axisIndex: number, + tickLabel: string, + leftoverMapAxes: LeftoverMapAxis[] | undefined, + variant: LeftoverMapPlotVariant, +): string { + if (variant === "comparison") { + return tf(LEFTOVER_MAP_COMPARE_PLOT_TICK, { axis: axisIndex, value: tickLabel }); + } + const leftoverSingular = leftoverSingularForAxis(leftoverMapAxes, axisIndex); + const badge = leftoverMapPlotTickAxisBadge(axisIndex, tickLabel, leftoverSingular); + return tf(badge.key, badge.values); +} + /** * Gabriel leftover-map graphic display of persisted ``ξ_{1:2}`` / ``ζ_{1:2}``. * @@ -197,6 +211,9 @@ function leftoverMapPlotAxisText( * graphic-display axes with persisted leftover-map singular values. * ADR 0326 fail-closes leftover-map comparison graphic leftover-map axis leftover-map * singular values through leftoverMapComparePlotAxisBadge. + * ADR 0327 fail-closes leftover-map graphic leftover-map axis ticks leftover-map + * singular values through leftoverMapPlotTickAxisBadge independently of leftover-map + * axis share. * Never invent a leftover score. */ export function LeftoverMapPlot({ @@ -324,11 +341,11 @@ export function LeftoverMapPlot({ diff --git a/frontend/src/i18n.test.ts b/frontend/src/i18n.test.ts index a2dd91a00..9df575c21 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -70,6 +70,7 @@ describe("i18n", () => { "leftover-map axis {axis} σ {value}", "leftover-map axis {axis} σ {value} ({share}%)", "leftover-map axis {axis} tick {value}", + "leftover-map axis {axis} tick {value} σ {singular}", "leftover-map distance {label}", "leftover-map reconstruction {label}", "leftover-map explained leftover share {label}", @@ -1010,6 +1011,22 @@ describe("i18n", () => { expect(tf("leftover-map 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 bản đồ phần dư 1 +0.50 σ 1.84"], + ] as const)("formats leftover-map graphic leftover-map axis tick singular values in %s", (locale, expected) => { + setLocale(locale); + expect( + tf("leftover-map axis {axis} tick {value} σ {singular}", { + axis: 1, + value: "+0.50", + singular: "1.84", + }), + ).toBe(expected); + }); + it.each([ ["ko", "잔여 지도 거리 d 0.12"], ["zh", "残差图距离 d 0.12"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 453fe3a19..cd7623eed 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -651,6 +651,7 @@ const TRANSLATIONS: Partial>> = { "leftover-map axis {axis} σ {value}": "잔여 지도 축 {axis} σ {value}", "leftover-map axis {axis} σ {value} ({share}%)": "잔여 지도 축 {axis} σ {value} ({share}%)", "leftover-map axis {axis} tick {value}": "잔여 지도 축 {axis} 눈금 {value}", + "leftover-map axis {axis} tick {value} σ {singular}": "잔여 지도 축 {axis} 눈금 {value} σ {singular}", "leftover-map distance {label}": "잔여 지도 거리 {label}", "leftover-map reconstruction {label}": "잔여 지도 재구성 {label}", "leftover-map explained leftover share {label}": "잔여 지도 설명 잔여 점유율 {label}", @@ -1304,6 +1305,7 @@ const TRANSLATIONS: Partial>> = { "leftover-map axis {axis} σ {value}": "残差图轴 {axis} σ {value}", "leftover-map axis {axis} σ {value} ({share}%)": "残差图轴 {axis} σ {value} ({share}%)", "leftover-map axis {axis} tick {value}": "残差图轴 {axis} 刻度 {value}", + "leftover-map axis {axis} tick {value} σ {singular}": "残差图轴 {axis} 刻度 {value} σ {singular}", "leftover-map distance {label}": "残差图距离 {label}", "leftover-map reconstruction {label}": "残差图重建 {label}", "leftover-map explained leftover share {label}": "残差图已解释残差份额 {label}", @@ -1961,6 +1963,7 @@ const TRANSLATIONS: Partial>> = { "leftover-map axis {axis} σ {value}": "残差マップ軸 {axis} σ {value}", "leftover-map axis {axis} σ {value} ({share}%)": "残差マップ軸 {axis} σ {value} ({share}%)", "leftover-map axis {axis} tick {value}": "残差マップ軸 {axis} 目盛 {value}", + "leftover-map axis {axis} tick {value} σ {singular}": "残差マップ軸 {axis} 目盛 {value} σ {singular}", "leftover-map distance {label}": "残差マップ距離 {label}", "leftover-map reconstruction {label}": "残差マップ再構成 {label}", "leftover-map explained leftover share {label}": "残差マップ説明済み残差割合 {label}", @@ -2621,6 +2624,8 @@ const TRANSLATIONS: Partial>> = { "leftover-map axis {axis} σ {value}": "trục bản đồ phần dư {axis} σ {value}", "leftover-map axis {axis} σ {value} ({share}%)": "trục bản đồ phần dư {axis} σ {value} ({share}%)", "leftover-map axis {axis} tick {value}": "vạch trục bản đồ phần dư {axis} {value}", + "leftover-map axis {axis} tick {value} σ {singular}": + "vạch trục bản đồ phần dư {axis} {value} σ {singular}", "leftover-map distance {label}": "khoảng cách bản đồ phần dư {label}", "leftover-map reconstruction {label}": "tái dựng bản đồ phần dư {label}", "leftover-map explained leftover share {label}": "phần dư giải thích bản đồ phần dư {label}", diff --git a/frontend/src/leftoverMapAxisBadge.ts b/frontend/src/leftoverMapAxisBadge.ts index d33e9ac24..3c74e05ad 100644 --- a/frontend/src/leftoverMapAxisBadge.ts +++ b/frontend/src/leftoverMapAxisBadge.ts @@ -6,6 +6,8 @@ * ADR 0324 captions leftover-map graphic-display axes, not this helper. * ADR 0326 captions leftover-map comparison graphic leftover-map axes as * leftoverMapComparePlotAxisBadge, not this helper. + * ADR 0327 captions leftover-map graphic leftover-map axis ticks as + * leftoverMapPlotTickAxisBadge, not this helper. */ import type { LeftoverMapAxis } from "./api"; diff --git a/frontend/src/leftoverMapPlotAxisSingular.test.ts b/frontend/src/leftoverMapPlotAxisSingular.test.ts index acd29fb9b..028d433a3 100644 --- a/frontend/src/leftoverMapPlotAxisSingular.test.ts +++ b/frontend/src/leftoverMapPlotAxisSingular.test.ts @@ -4,6 +4,7 @@ import { leftoverMapCompareAxisBadge, leftoverMapComparePlotAxisBadge, leftoverMapPlotAxisBadge, + leftoverMapPlotTickAxisBadge, leftoverSingularForAxis, LEFTOVER_MAP_COMPARE_AXIS_CAPTION, LEFTOVER_MAP_COMPARE_AXIS_LABEL, @@ -14,12 +15,13 @@ import { LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR_SHARE, LEFTOVER_MAP_PLOT_AXIS_SINGULAR, LEFTOVER_MAP_PLOT_AXIS_SINGULAR_SHARE, + LEFTOVER_MAP_PLOT_TICK_SINGULAR, } from "./leftoverMapPlotAxisSingular"; import { LEFTOVER_MAP_COMPARE_PLOT_AXIS_SHARE, LEFTOVER_MAP_PLOT_AXIS_SHARE, } from "./leftoverMapPlotAxisShare"; -import { LEFTOVER_MAP_COMPARE_PLOT_TICK } from "./leftoverMapPlotLayout"; +import { LEFTOVER_MAP_COMPARE_PLOT_TICK, LEFTOVER_MAP_PLOT_TICK } from "./leftoverMapPlotLayout"; import { LEFTOVER_MAP_AXIS_BADGE_SINGULAR, LEFTOVER_MAP_AXIS_BADGE_SINGULAR_ONLY } from "./leftoverMapAxisBadge"; describe("leftoverSingularForAxis", () => { @@ -304,3 +306,65 @@ describe("leftover-map comparison graphic leftover-map axis singular badges", () ); }); }); + +describe("leftover-map graphic leftover-map axis tick leftover-map singular values", () => { + it("names persisted leftover-map singular values on leftover-map graphic leftover-map axis ticks", () => { + expect(leftoverMapPlotTickAxisBadge(1, "+0.50", 1.84)).toEqual({ + key: LEFTOVER_MAP_PLOT_TICK_SINGULAR, + values: { axis: 1, value: "+0.50", singular: "1.84" }, + }); + expect(leftoverMapPlotTickAxisBadge(2, "−0.02", 0.86)).toEqual({ + key: LEFTOVER_MAP_PLOT_TICK_SINGULAR, + values: { axis: 2, value: "−0.02", singular: "0.86" }, + }); + }); + + it("names rank-0 zero leftover-map singular values on leftover-map graphic leftover-map axis ticks", () => { + expect(leftoverMapPlotTickAxisBadge(1, "0.00", 0)).toEqual({ + key: LEFTOVER_MAP_PLOT_TICK_SINGULAR, + values: { axis: 1, value: "0.00", singular: "0.00" }, + }); + }); + + it("omits leftover-map singular values independently of leftover-map axis share", () => { + expect(leftoverMapPlotTickAxisBadge(1, "+0.50", Number.NaN)).toEqual({ + key: LEFTOVER_MAP_PLOT_TICK, + values: { axis: 1, value: "+0.50" }, + }); + expect(leftoverMapPlotTickAxisBadge(2, "−0.02", -0.01)).toEqual({ + key: LEFTOVER_MAP_PLOT_TICK, + values: { axis: 2, value: "−0.02" }, + }); + expect(leftoverMapPlotTickAxisBadge(1, "+0.50", 1.84).values.share).toBeUndefined(); + expect(leftoverMapPlotTickAxisBadge(1, "+0.50", null)).toEqual({ + key: LEFTOVER_MAP_PLOT_TICK, + values: { axis: 1, value: "+0.50" }, + }); + }); + + it("does not invent leftover-map singular values from leftover-map axis share", () => { + expect(leftoverMapPlotTickAxisBadge(1, "+0.50", undefined).key).toBe(LEFTOVER_MAP_PLOT_TICK); + expect(leftoverMapPlotTickAxisBadge(1, "+0.50", undefined).values.singular).toBeUndefined(); + }); + + it("does not invent leftover-map axis share from leftover-map singular values", () => { + expect(leftoverMapPlotTickAxisBadge(1, "+0.50", 1.84).key).toBe(LEFTOVER_MAP_PLOT_TICK_SINGULAR); + expect(leftoverMapPlotTickAxisBadge(1, "+0.50", 1.84).values.share).toBeUndefined(); + }); + + it("stays distinct from leftover-axis, leftover-map graphic axis, comparison leftover-axis, and comparison graphic copy", () => { + expect(LEFTOVER_MAP_PLOT_TICK_SINGULAR).toBe("leftover-map axis {axis} tick {value} σ {singular}"); + expect(LEFTOVER_MAP_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_PLOT_TICK); + expect(LEFTOVER_MAP_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_PLOT_AXIS_SINGULAR); + expect(LEFTOVER_MAP_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR); + expect(LEFTOVER_MAP_PLOT_TICK_SINGULAR).not.toBe(LEFTOVER_MAP_COMPARE_AXIS_SINGULAR); + 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(leftoverMapPlotTickAxisBadge(1, "+0.50", 1.84).key).not.toBe( + leftoverMapPlotAxisBadge(1, 1.84, null)?.key, + ); + expect(leftoverMapPlotTickAxisBadge(1, "+0.50", 1.84).key).not.toBe( + leftoverMapComparePlotAxisBadge(1, 1.84, null)?.key, + ); + }); +}); diff --git a/frontend/src/leftoverMapPlotAxisSingular.ts b/frontend/src/leftoverMapPlotAxisSingular.ts index 4e8b8def5..c45153ce1 100644 --- a/frontend/src/leftoverMapPlotAxisSingular.ts +++ b/frontend/src/leftoverMapPlotAxisSingular.ts @@ -3,6 +3,9 @@ * singular values as leftoverMapPlotAxisBadge. * ADR 0326 names leftover-map comparison graphic leftover-map axis leftover-map * singular values as leftoverMapComparePlotAxisBadge. + * ADR 0327 names leftover-map graphic leftover-map axis ticks leftover-map + * singular values as leftoverMapPlotTickAxisBadge independently of leftover-map + * axis share. * ADR 0323 captions leftover-axis report badges on the grouping comparison strip * as leftoverMapCompareAxisBadge, not leftoverMapComparePlotAxisBadge. */ @@ -13,6 +16,7 @@ import { LEFTOVER_MAP_COMPARE_PLOT_AXIS_SHARE, LEFTOVER_MAP_PLOT_AXIS_SHARE, } from "./leftoverMapPlotAxisShare"; +import { LEFTOVER_MAP_PLOT_TICK } from "./leftoverMapPlotLayout"; export const LEFTOVER_MAP_PLOT_AXIS_SINGULAR = "leftover-map axis {axis} σ {value}"; @@ -25,6 +29,9 @@ export const LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR = export const LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR_SHARE = "leftover map comparison graphic leftover-map axis {axis} σ {value} ({share}%)"; +export const LEFTOVER_MAP_PLOT_TICK_SINGULAR = + "leftover-map axis {axis} tick {value} σ {singular}"; + export const LEFTOVER_MAP_COMPARE_AXIS_SINGULAR = "leftover map comparison leftover axis {axis} σ {value}"; @@ -46,7 +53,7 @@ export type LeftoverMapPlotAxisSingular = { export type LeftoverMapCompareAxisBadge = { key: string; - values: { axis: number; value?: string; share?: string }; + values: { axis: number; value?: string; share?: string; singular?: string }; }; export function leftoverSingularForAxis( @@ -142,3 +149,18 @@ export function leftoverMapComparePlotAxisBadge( values: { axis: axisIndex, value: singular as string, share: percent as string }, }; } + +export function leftoverMapPlotTickAxisBadge( + axisIndex: number, + tickLabel: string, + leftoverSingular: number | null | undefined, +): LeftoverMapCompareAxisBadge { + const singular = formatLeftoverMapPlotAxisSingular(leftoverSingular); + if (singular === null) { + return { key: LEFTOVER_MAP_PLOT_TICK, values: { axis: axisIndex, value: tickLabel } }; + } + return { + key: LEFTOVER_MAP_PLOT_TICK_SINGULAR, + values: { axis: axisIndex, value: tickLabel, singular }, + }; +} diff --git a/frontend/src/leftoverMapPlotLayout.test.ts b/frontend/src/leftoverMapPlotLayout.test.ts index 30b8554b0..2c166ec75 100644 --- a/frontend/src/leftoverMapPlotLayout.test.ts +++ b/frontend/src/leftoverMapPlotLayout.test.ts @@ -1371,6 +1371,7 @@ describe("leftover map comparison graphic labels", () => { ); expect(LEFTOVER_MAP_COMPARE_PLOT_TICK).not.toBe("leftover-map axis {axis} σ {value}"); expect(LEFTOVER_MAP_COMPARE_PLOT_TICK).not.toBe("leftover axis {axis} σ {value}"); + expect(LEFTOVER_MAP_COMPARE_PLOT_TICK).not.toBe("leftover-map axis {axis} tick {value} σ {singular}"); expect(LEFTOVER_MAP_COMPARE_PLOT_TICK).not.toBe( "leftover map comparison leftover axis {axis} σ {value}", ); diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index 845f07c6c..af1aac55f 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -91,6 +91,9 @@ * singular values `σ_k`. * ADR 0326 fail-closes leftover-map comparison graphic leftover-map axis leftover-map * singular values through leftoverMapComparePlotAxisBadge. + * ADR 0327 fail-closes leftover-map graphic leftover-map axis ticks leftover-map + * singular values through leftoverMapPlotTickAxisBadge independently of leftover-map + * axis share. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; diff --git a/pyproject.toml b/pyproject.toml index 88797a9cd..b6bed38ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.83.0" +version = "2.84.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" }