From ef30930271a729044af2f690df04f0b89f751ffd Mon Sep 17 00:00:00 2001 From: seonghobae Date: Sun, 30 Aug 2026 12:36:37 +0000 Subject: [PATCH] feat(reports): leftover-map incomplete post coverage on pair list (v2.43.0) Caption the leftover pair list with persisted leftover-map incomplete post coverage (ADR 0286). After make seed, closest and farthest leftover pairs sit above the member list with a pair-list note that matches leftover-map incomplete post coverage; click a post marker or a pair button opens that post. A missing, non-integer, or negative dropped count, or a dropped count that contradicts usable complete-case integers, omits that leftover-map incomplete post note. Dropped 0 is shown when that persisted dropped count is a non-negative integer. Do not invent dropped posts from scored minus used, plotted marker count, leftover-map distance, leftover-map rank, leftover-map post coverage, leftover-map item coverage, or the count of unused axes. Never invent a leftover score or a theta. UI-only; no new columns. --- AGENTS.md | 10 +- ARCHITECTURE.md | 6 +- ....43.0-leftover-map-list-incomplete-post.md | 28 +++ CHANGELOG.md | 28 +++ CLAUDE.md | 2 +- .../0286-leftover-map-list-incomplete-post.md | 183 ++++++++++++++++++ docs/product-technical-gap-baseline.md | 55 ++++++ docs/storybook-inventory.md | 2 +- frontend/package.json | 2 +- frontend/src/App.test.tsx | 6 + frontend/src/App.tsx | 9 + frontend/src/components/LeftoverPairList.tsx | 3 +- frontend/src/i18n.test.ts | 11 ++ frontend/src/i18n.ts | 4 + frontend/src/leftoverMapCoverage.test.ts | 8 + frontend/src/leftoverMapCoverage.ts | 4 +- frontend/src/leftoverMapPlotLayout.ts | 4 +- pyproject.toml | 2 +- 18 files changed, 356 insertions(+), 11 deletions(-) create mode 100644 CHANGELOG.d/2.43.0-leftover-map-list-incomplete-post.md create mode 100644 docs/adr/0286-leftover-map-list-incomplete-post.md diff --git a/AGENTS.md b/AGENTS.md index f47dea06d..c5983c441 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) 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) 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 @@ -367,6 +367,12 @@ the scored-criterion census; a missing, non-integer, negative-used, non-positive-scored, or used-greater-than-scored item coverage omits that leftover-map item coverage note and does not invent item coverage from plotted criterion marker count. +ADR 0286 captions the leftover pair list with persisted leftover-map +incomplete post coverage so a `used N of M scored posts` note is not read as +every scored post entering the map; a missing, non-integer, or negative +dropped count, or a dropped count that contradicts usable complete-case +integers, omits that leftover-map incomplete post note and does not invent +dropped posts from scored minus used or from plotted marker count. 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 @@ -384,6 +390,8 @@ graphic with how many scored posts entered the map. Item complete-case coverage entered the map. Item complete-case coverage on the pair list (ADR 0285) captions the pair list with how many scored criteria entered the map. Incomplete post coverage (ADR 0283) captions the leftover-map graphic with how many scored posts stayed out of the factorization. Incomplete +post coverage on the pair list (ADR 0286) captions the pair list with how many +scored posts stayed out of the factorization. Incomplete item coverage (ADR 0284) captions the leftover-map graphic with how many scored criteria stayed out of the factorization. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 10a849ac3..e5a075d0b 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -637,7 +637,7 @@ information at the group's mean θ (Lord, 1980 max-info CAT). Rankings persist to `report_item_information`. After those IRT main effects, residual SVD leftover pairs on two Gabriel axes (Jeon et al., 2021; ADR 0017 / 0048 / 0049 / 0119 / 0148 / 0158 / 0162 / 0163 / 0164 / 0168 / -0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285) persist to `report_leftover_pair` with signed residual `R`, +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) persist to `report_leftover_pair` with signed residual `R`, observed `Y`, expected `E[Y|θ, item]`, full leftover-map rank, unexplained leftover, ADR 0201 reconstruction evidence, ADR 0185 cross-share evidence, ADR 0233 unexplained leftover share `s`, ADR 0266 explained leftover @@ -664,7 +664,8 @@ persisted leftover-map item complete-case coverage. ADR 0283 captions the leftov graphic display with persisted leftover-map incomplete post coverage. ADR 0284 captions the leftover-map graphic display with persisted leftover-map incomplete item coverage. ADR 0285 captions the leftover pair list with persisted leftover-map -item complete-case coverage. Click a post marker or a pair button +item complete-case coverage. ADR 0286 captions the leftover pair list with persisted leftover-map +incomplete post coverage. 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 (Gabriel inertia of residual SVD axes 1 and 2; ADR 0148) persists to @@ -674,6 +675,7 @@ many scored posts entered the factorization, including on the graphic, and how many scored criteria entered the factorization on the graphic and on the pair list, and how many scored posts stayed incomplete and out of the factorization, +including on the pair list, and how many scored criteria stayed incomplete and out of the factorization. Results persist to `report_period_score` / `report_member_score`. `GET /api/reports/{grouping}` lists the trend; diff --git a/CHANGELOG.d/2.43.0-leftover-map-list-incomplete-post.md b/CHANGELOG.d/2.43.0-leftover-map-list-incomplete-post.md new file mode 100644 index 000000000..49b98114c --- /dev/null +++ b/CHANGELOG.d/2.43.0-leftover-map-list-incomplete-post.md @@ -0,0 +1,28 @@ +## 2.43.0 — Leftover-map incomplete post coverage on the pair list + +- Caption the leftover pair list with persisted leftover-map incomplete + post coverage (ADR 0286). After `make seed`, closest and farthest leftover + pairs sit above the member list with the Gabriel biplot of already-named + coordinates, leftover-map axis share when finite, axis ticks that match + `ξ (x, y) ζ (x, y)` on the pair row, pair segments that match `d` on the + pair row, pair segments that match `R̂` on the pair row, pair segments + that match `R̂²/R²` on the pair row, pair segments that match `U²/R²` on + the pair row, pair segments that match `2R̂U/R²` on the pair row, pair + segments that match `U` on the pair row, pair segments that match `R` on + the pair row, pair segments that match `Y` on the pair row, pair segments + that match `E` on the pair row, pair segments that match leftover-map rank + on the pair row, a plot caption that matches leftover-map complete-case + coverage above the pair list, a plot caption that matches leftover-map + item complete-case coverage, a plot caption that matches leftover-map + incomplete post coverage, a plot caption that matches leftover-map + incomplete item coverage, a pair-list note that matches leftover-map item + complete-case coverage, and a pair-list note that matches leftover-map + incomplete post coverage; click a post marker or a pair button opens + that post. A missing, non-integer, or negative dropped count, or a + dropped count that contradicts usable complete-case integers, omits that + leftover-map incomplete post note. Dropped `0` is shown when that + persisted dropped count is a non-negative integer. Do not invent dropped + posts from scored minus used, plotted marker count, leftover-map + distance, leftover-map rank, leftover-map post coverage, leftover-map + item coverage, or the count of unused axes. Never invent a leftover + score. Never invent a theta. No new columns. diff --git a/CHANGELOG.md b/CHANGELOG.md index 754045060..7b47f6dbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,34 @@ All notable changes to this project are documented here. Format follows ### Added +- Period leftover pairs now caption the leftover pair list with persisted + leftover-map incomplete post coverage (ADR 0286 / v2.43.0). After + `make seed`, closest and farthest leftover pairs sit above the member + list with the Gabriel biplot of already-named coordinates, leftover-map + axis share when finite, axis ticks that match `ξ (x, y) ζ (x, y)` on + the pair row, pair segments that match `d` on the pair row, pair + segments that match `R̂` on the pair row, pair segments that match + `R̂²/R²` on the pair row, pair segments that match `U²/R²` on the pair + row, pair segments that match `2R̂U/R²` on the pair row, pair segments + that match `U` on the pair row, pair segments that match `R` on the + pair row, pair segments that match `Y` on the pair row, pair segments + that match `E` on the pair row, pair segments that match leftover-map + rank on the pair row, a plot caption that matches leftover-map + complete-case coverage above the pair list, a plot caption that matches + leftover-map item complete-case coverage, a plot caption that matches + leftover-map incomplete post coverage, a plot caption that matches + leftover-map incomplete item coverage, a pair-list note that matches + leftover-map item complete-case coverage, and a pair-list note that + matches leftover-map incomplete post coverage; click a post marker or + a pair button opens that post. A missing, non-integer, or negative + dropped count, or a dropped count that contradicts usable complete-case + integers, omits that leftover-map incomplete post note. Dropped `0` is + shown when that persisted dropped count is a non-negative integer. Do + not invent dropped posts from scored minus used, plotted marker count, + leftover-map distance, leftover-map rank, leftover-map post coverage, + leftover-map item coverage, or the count of unused axes. Never invent + a leftover score or a theta. No new columns. + - Period leftover pairs now caption the leftover pair list with persisted leftover-map item complete-case coverage (ADR 0285 / v2.42.0). After `make seed`, closest and farthest leftover pairs sit above the member diff --git a/CLAUDE.md b/CLAUDE.md index 0884fdca8..49709f27e 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), 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), 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/0286-leftover-map-list-incomplete-post.md b/docs/adr/0286-leftover-map-list-incomplete-post.md new file mode 100644 index 000000000..c6ff56faa --- /dev/null +++ b/docs/adr/0286-leftover-map-list-incomplete-post.md @@ -0,0 +1,183 @@ +# ADR 0286 — Name leftover-map incomplete post coverage on the pair list + +**Decision status:** Accepted +**Date:** 2026-08-30 + +Amends [ADR 0049](0049-leftover-pair-report-ui.md) and leftover-map complete-case +coverage ([ADR 0168](0168-leftover-map-complete-case-coverage.md)). Independent of +leftover-map item complete-case coverage on the pair list +([ADR 0285](0285-leftover-map-list-item-coverage.md)), leftover-map incomplete +item coverage on the graphic display +([ADR 0284](0284-leftover-map-plot-incomplete-item.md)), leftover-map incomplete +post coverage on the graphic display +([ADR 0283](0283-leftover-map-plot-incomplete.md)), leftover-map item +complete-case coverage on the graphic display +([ADR 0282](0282-leftover-map-plot-item-coverage.md)), leftover-map complete-case +coverage on the graphic display +([ADR 0281](0281-leftover-map-plot-coverage.md)), leftover-map rank on pair +segments ([ADR 0280](0280-leftover-map-segment-rank.md)), leftover expected on pair +segments ([ADR 0279](0279-leftover-map-segment-expected.md)), leftover observed on +pair segments ([ADR 0278](0278-leftover-map-segment-observed.md)), leftover +residual on pair segments ([ADR 0277](0277-leftover-map-segment-residual.md)), +leftover-map unexplained leftover on pair segments +([ADR 0276](0276-leftover-map-segment-unexplained-leftover.md)), leftover-map +cross share on pair segments ([ADR 0275](0275-leftover-map-segment-cross-share.md)), +leftover-map unexplained leftover share on pair segments +([ADR 0274](0274-leftover-map-segment-unexplained-share.md)), leftover-map +explained leftover share on pair segments +([ADR 0273](0273-leftover-map-segment-explained-share.md)), leftover-map +reconstruction on pair segments +([ADR 0272](0272-leftover-map-segment-reconstruction.md)), leftover-map +distance on pair segments ([ADR 0271](0271-leftover-map-segment-distance.md)), +leftover-map coordinate ticks ([ADR 0270](0270-leftover-map-coordinate-ticks.md)), +leftover-map axis share on the graphic display +([ADR 0269](0269-leftover-map-axis-share-plot.md)), leftover residual +disclosure ([ADR 0162](0162-leftover-residual-disclosure.md)), leftover +observed `Y` / expected `E` ([ADR 0163](0163-leftover-observed-expected.md)), +leftover-map explained leftover share persistence +([ADR 0266](0266-leftover-map-explained-share.md)), leftover-map +unexplained leftover share persistence +([ADR 0233](0233-leftover-map-unexplained-share.md)), leftover-map +reconstruction persistence ([ADR 0201](0201-leftover-map-reconstruction.md)), +leftover-map cross share persistence +([ADR 0185](0185-leftover-map-cross-share.md)), leftover-map unexplained leftover +persistence ([ADR 0182](0182-leftover-map-unexplained.md)), leftover-map rank +persistence ([ADR 0164](0164-leftover-map-rank.md)), and leftover-map axis +share persistence ([ADR 0148](0148-leftover-map-axis-share.md)). + +## Context + +ADR 0168 already persists `incomplete_post_count` on +`report_leftover_map_coverage` and captions the pair list with post +complete-case coverage. ADR 0283 already names persisted leftover-map +incomplete post coverage on the leftover-map graphic display. ADR 0285 already +names item complete-case coverage on the pair list. The pair-list note still +does not name dropped incomplete posts, so a buyer who reads `used N of M +scored posts (complete-case)` above the pair buttons can treat those N posts +as the full scored census even after incomplete rows were dropped. Incomplete +rows are dropped from Gabriel factorization; hiding dropped posts on the pair +list lets a sparse post with one missing criterion vanish without a next +action. + +This increment names leftover-map incomplete post coverage on the pair-list +note. It does not add columns. It does not recompute dropped posts from scored +minus used, plotted marker count, leftover-map distance, or leftover-map rank. +It does not persist leftover-map inner product, cosine, or length as separate +columns. It does not land Post quality on the leftover criterion. +Leftover-map distance stays two-axis Euclidean. Do not invent a leftover +score. Do not invent a theta. + +The dashboard stack already used neighbouring leftover facts under other +numbers. This protected increment uses **0286** so it does not collide with +leftover-map item complete-case coverage on the pair list (0285), leftover-map +incomplete item coverage on the graphic display (0284), leftover-map incomplete +post coverage on the graphic display (0283), leftover-map item complete-case +coverage on the graphic display (0282), leftover-map complete-case coverage on +the graphic display (0281), leftover-map rank on pair segments (0280), leftover +expected on pair segments (0279), leftover observed on pair segments (0278), +leftover residual on pair segments (0277), leftover-map unexplained leftover on +pair segments (0276), leftover-map cross share on pair segments (0275), +leftover-map unexplained leftover share on pair segments (0274), leftover-map +explained leftover share on pair segments (0273), leftover-map reconstruction +on pair segments (0272), leftover-map distance on pair segments (0271), +leftover-map coordinate ticks (0270), leftover-map axis share on the graphic +display (0269), leftover-map graphic display (0268), leftover-map coordinates +(0267 / migration 0245), leftover-map explained leftover share persistence +(0266 / migration 0244), leftover-map unexplained leftover share persistence +(0233 / migration 0233), leftover-map reconstruction persistence (0201 / +migration 0206), leftover-map cross share persistence (0185), leftover-map +unexplained leftover persistence (0182), leftover residual disclosure (0162), +leftover observed `Y` / expected `E` persistence (0163), leftover-map rank +persistence (0164), leftover coverage persistence (0168), two-axis leftover-map +distance persistence, leftover-map axis share persistence (0148), leftover +interaction-map persistence, occupational construct catalog search (0265), or +the dashboard stacks. + +## Decision + +On the leftover pair list, caption persisted leftover-map incomplete post +coverage as `Leftover map dropped {dropped} incomplete posts`, using the +persisted `incomplete_post_count` integer. A missing coverage row, a +non-integer dropped count, a negative dropped count, or a dropped count that +contradicts usable complete-case integers (`dropped !== scored − used` when +`map_post_count` / `scored_post_count` are usable) omits that leftover-map +incomplete post note and keeps the pair-list post coverage note, the pair-list +item coverage note, and any leftover-map distance, reconstruction, explained +leftover share, unexplained leftover share, leftover-map cross share, +unexplained leftover, leftover residual, leftover observed, leftover expected, +leftover-map rank, leftover-map post coverage, leftover-map item coverage, +leftover-map incomplete post, or leftover-map incomplete item caption on the +graphic. Dropped `0` is shown when that persisted dropped count is a +non-negative integer. Do not invent dropped posts from scored minus used, +plotted marker count, leftover-map distance, leftover-map rank, leftover-map +post coverage, leftover-map item coverage, or the count of unused axes. Click +a pair button to open that post. The grouping comparison strip (ADR 0149) +stays on its reduced leftover payload and does not gain this leftover-map +incomplete post caption. + +Do not add SQL. Do not edit shipped migrations. Do not persist inner +product, cosine, or length as separate columns. Do not invent a leftover +score. Do not invent a theta. + +## Consequences + +After `make seed`, closest and farthest leftover pairs sit above the member +list with the leftover-map graphic display of persisted `ξ` and `ζ`, +leftover-map axes name persisted Gabriel inertia share when finite, +leftover-map axis ticks name the same coordinates shown on the pair row, +pair segments name persisted leftover-map distance `d`, persisted leftover-map +reconstruction `R̂`, persisted leftover-map explained leftover share `e`, +persisted leftover-map unexplained leftover share `s`, persisted leftover-map +cross share `x`, persisted leftover-map unexplained leftover `U`, persisted +leftover residual `R`, persisted leftover observed `Y`, persisted leftover +expected `E`, persisted leftover-map rank, the plot names persisted leftover-map +complete-case coverage, the plot names persisted leftover-map item +complete-case coverage, the plot names persisted leftover-map incomplete +post coverage, the plot names persisted leftover-map incomplete item +coverage, the pair list names persisted leftover-map item complete-case +coverage, and the pair list names persisted leftover-map incomplete post +coverage; click a post marker or a pair button opens that post. +Hidden posts stay hidden. Rank-0 unused axes still plot at the origin and still +name incomplete posts on the pair list when that dropped count is persisted. + +## Related + +Independent of leftover interaction-map persistence, leftover-criterion +evaluation landing, leftover residual disclosure, leftover-map +complete-case coverage persistence, leftover-map axis share persistence, +leftover pairs on the grouping comparison strip, two-axis leftover-map +distance persistence, leftover-map rank persistence, leftover-map inner +product, leftover-map cosine, leftover-map length, leftover-map +reconstruction persistence, leftover-map unexplained leftover persistence, +leftover-map cross share persistence, leftover-map unexplained leftover +share persistence, leftover-map explained leftover share persistence, +leftover-map coordinate persistence, leftover-map graphic display, +leftover-map axis share on the graphic display, leftover-map coordinate +ticks, leftover-map distance on pair segments, leftover-map reconstruction +on pair segments, leftover-map explained leftover share on pair segments, +leftover-map unexplained leftover share on pair segments, leftover-map +cross share on pair segments, leftover-map unexplained leftover on pair +segments, leftover residual on pair segments, leftover observed on pair +segments, leftover expected on pair segments, leftover-map rank on pair +segments, leftover-map complete-case coverage on the graphic display, +leftover-map item complete-case coverage on the graphic display, +leftover-map incomplete post coverage on the graphic display, +leftover-map incomplete item coverage on the graphic display, and +leftover-map item complete-case coverage on the pair list. + +## 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. Gabriel +reconstruction of the leftover cell is the two-axis inner product +`R̂ = ξ_{1:2} · ζ_{1:2}`. Incomplete rows are dropped from the +complete-case residual rectangle; pair-list incomplete post coverage names +how many scored posts stayed out of that factorization.) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index bdd4a2817..cc3fe33b2 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,60 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-08-30 21:35 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1` after unauthorized squash +> of leftover-map coordinates (v2.24.0 / #782). Revert #808 +> (`revert-pr782-unauthorized` @ `1af3e53e`) restores `main` toward +> `fc13acaa` (v2.23.0) and still needs independent APPROVE. GitHub +> writes work (comment/close/create-PR/push). Token still has empty +> `X-OAuth-Scopes`; repo permission is ADMIN. Open leftover stack +> still lacks independent APPROVE. Do not self-approve. +> #782 leftover-map coordinates through pair-segment distance +> (v2.24.0–v2.28.0 / ADR 0267–0271) is `9bdd3e4d`. #802 explained +> leftover share on pair segments (v2.30.0 / ADR 0273, includes v2.29 +> reconstruction) is `79ec22f0`. #803 unexplained leftover share on pair +> segments (v2.31.0 / ADR 0274) is `089a1571`. #804 leftover-map cross +> share on pair segments (v2.32.0 / ADR 0275) is `c513002b`. #805 leftover-map +> unexplained leftover `U` on pair segments (v2.33.0 / ADR 0276) is +> `e3fdfd74`. #806 leftover residual `R` on pair segments (v2.34.0 / +> ADR 0277) is `33f6c3dd`. #809 leftover observed `Y` on pair segments +> (v2.35.0 / ADR 0278) is `b334b00e`. #810 leftover expected `E` on pair +> segments (v2.36.0 / ADR 0279) is `6e37757a`. #811 leftover-map rank on +> pair segments (v2.37.0 / ADR 0280) is `e626a1d0`. #812 leftover-map +> complete-case coverage on the graphic (v2.38.0 / ADR 0281) is `64964cb6`. +> #813 leftover-map item complete-case coverage on the graphic (v2.39.0 / +> ADR 0282) is `de2a8a8b`. #814 leftover-map incomplete post coverage on +> the graphic (v2.40.0 / ADR 0283) is `77292872`. #815 leftover-map +> incomplete item coverage on the graphic (v2.41.0 / ADR 0284) is +> `63092de`. #816 leftover-map item complete-case coverage on the pair +> list (v2.42.0 / ADR 0285) is `1e3d13e`. Leave +> #782/#802/#803/#804/#805/#806/#808/#809/#810/#811/#812/#813/#814/#815/#816 open for +> independent review. Do not squash-merge stacked leftover PRs onto an +> unprotected leftover base. Do not merge #808 without independent APPROVE. +> Strix on #782 failed closed at `Run Strix (quick)`; do not weaken +> fail-closed on `Vulnerabilities [1-9]`. Org Strix `ModelBehaviorError` +> classifier is already on ContextualWisdomLab/.github. Copilot review is +> not independent APPROVE. Issues #79 and #87 stay open. #96 is already +> closed. Only collaborator is `seonghobae`; no independent reviewer can +> be requested from this token. +> +> Next buyer increment on this cycle: leftover-map incomplete post +> coverage on the pair list (ADR 0286 / v2.43.0) delivered locally on +> `feat/leftover-map-list-incomplete-post-v2430`. Caption the pair-list +> note with persisted leftover-map incomplete post coverage so a +> `used N of M scored posts` note is not read as every scored post +> entering the map. UI-only; no new columns. Missing, non-integer, or +> negative dropped count, or a dropped count that contradicts usable +> complete-case integers, omits that leftover-map incomplete post note. +> Dropped `0` is shown when persisted. Do not invent dropped posts from +> scored minus used, plotted marker count, leftover-map distance, +> leftover-map rank, leftover-map post coverage, leftover-map item +> coverage, or the count of unused axes. Do not invent leftover scores. +> Stack onto leftover branch `feat/leftover-map-list-item-coverage-v2420` +> / #816; leave the PR open for independent review. Do not squash-merge +> onto the leftover base. Do not persist leftover-map inner product, +> cosine, or length as separate columns. Grouping comparison strip +> (ADR 0149) does not gain this caption. + > Exact-head loop overlay: 2026-08-30 21:22 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1` after unauthorized squash > of leftover-map coordinates (v2.24.0 / #782). Revert #808 diff --git a/docs/storybook-inventory.md b/docs/storybook-inventory.md index 5b595bf07..3a167c760 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 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. `ClosestAndFarthest`, `RankZeroOrigin`, `MissingCoordinates`, and `MissingAxisShare` 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, and missing share that keeps existing leftover-map axis text. 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 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 and leftover-map item complete-case coverage when persisted. | `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 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, leftover-map item complete-case coverage, and leftover-map incomplete post coverage when persisted. | `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 819c3ae04..bf7b7e2f8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.42.0", + "version": "2.43.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 06fc4e489..08a3204a0 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -4157,6 +4157,9 @@ describe("App, authenticated", () => { expect(screen.getByLabelText("Leftover map item coverage")).toHaveTextContent( "Leftover map used 2 of 2 scored criteria (complete-case)", ); + expect(screen.getByLabelText("Leftover map incomplete posts")).toHaveTextContent( + "Leftover map dropped 1 incomplete posts", + ); expect(screen.getByLabelText("Leftover-map graphic coverage")).toHaveTextContent( "Leftover map used 2 of 3 scored posts (complete-case)", ); @@ -4237,6 +4240,9 @@ describe("App, authenticated", () => { expect( within(screen.getByLabelText("Grouping comparison")).queryByLabelText("Leftover map item coverage"), ).not.toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText("Leftover map incomplete posts"), + ).not.toBeInTheDocument(); expect( screen.getByRole("button", { name: "Compare Business unit (PU): Demo Report High, mean θ 0.81" }), ).toHaveTextContent("mean θ 0.81"); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c5a8647e3..5fa9e07da 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -120,8 +120,11 @@ import { useLocale, } from "./i18n"; import { + leftoverMapIncompletePostCount, leftoverMapItemCoverageCounts, + LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL, LEFTOVER_MAP_LIST_ITEM_COVERAGE_LABEL, + LEFTOVER_MAP_PLOT_INCOMPLETE_POST, LEFTOVER_MAP_PLOT_ITEM_COVERAGE, } from "./leftoverMapCoverage"; import "./App.css"; @@ -3818,6 +3821,7 @@ function ReportsPanel({ > {orderedReports.map((report) => { const itemCoverageCounts = leftoverMapItemCoverageCounts(report.leftover_map_coverage); + const incompletePostCount = leftoverMapIncompletePostCount(report.leftover_map_coverage); return (
  • ) : null} + {incompletePostCount !== null ? ( +

    + {tf(LEFTOVER_MAP_PLOT_INCOMPLETE_POST, incompletePostCount)} +

    + ) : null} {report.leftover_map_axes?.map((axis) => ( {tf("leftover axis {axis} {share}%", { diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index 8481b1a44..a026e7954 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -77,7 +77,8 @@ export type LeftoverPairListProps = { * item complete-case coverage on the graphic. ADR 0283 names persisted leftover-map * incomplete post coverage on the graphic. ADR 0284 names persisted leftover-map * incomplete item coverage on the graphic. ADR 0285 names persisted leftover-map - * item complete-case coverage on the pair-list note. Every badge still + * item complete-case coverage on the pair-list note. ADR 0286 names persisted leftover-map + * incomplete post coverage on the pair-list note. Every badge still * renders together before opening the named post. */ export function LeftoverPairList({ diff --git a/frontend/src/i18n.test.ts b/frontend/src/i18n.test.ts index a61a8250d..f77700c93 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -79,6 +79,7 @@ describe("i18n", () => { "Leftover-map graphic coverage", "Leftover-map graphic item coverage", "Leftover map item coverage", + "Leftover map incomplete posts", "Leftover-map graphic incomplete posts", "Leftover-map graphic incomplete items", "Leftover map used {used} of {scored} scored criteria (complete-case)", @@ -398,6 +399,16 @@ describe("i18n", () => { expect(t("Leftover map item coverage")).toBe(expected); }); + it.each([ + ["ko", "잔여 지도 불완전 글"], + ["zh", "残差地图不完整帖文"], + ["ja", "残差マップの不完全投稿"], + ["vi", "Bài không đầy đủ trên bản đồ phần dư"], + ] as const)("formats leftover map incomplete posts label in %s", (locale, expected) => { + setLocale(locale); + expect(t("Leftover map incomplete posts")).toBe(expected); + }); + it.each([ ["ko", "잔여 지도 그림 불완전 글"], ["zh", "残差图图形不完整帖文"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 6218494af..aa1e68248 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -269,6 +269,7 @@ const TRANSLATIONS: Partial>> = { "Period reports": "기간 리포트", "Leftover map coverage": "잔여 지도 포함 범위", "Leftover map item coverage": "잔여 지도 기준 포함 범위", + "Leftover map incomplete posts": "잔여 지도 불완전 글", "Leftover map used {used} of {scored} scored posts (complete-case)": "잔여 지도는 채점된 글 {scored}개 중 {used}개를 사용했습니다(완전사례)", "Leftover map used {used} of {scored} scored criteria (complete-case)": @@ -873,6 +874,7 @@ const TRANSLATIONS: Partial>> = { "Period reports": "周期报告", "Leftover map coverage": "残差地图覆盖范围", "Leftover map item coverage": "残差地图准则覆盖范围", + "Leftover map incomplete posts": "残差地图不完整帖文", "Leftover map used {used} of {scored} scored posts (complete-case)": "残差地图使用了 {scored} 篇已评分帖文中的 {used} 篇(完全案例)", "Leftover map used {used} of {scored} scored criteria (complete-case)": @@ -1498,6 +1500,7 @@ const TRANSLATIONS: Partial>> = { "Period reports": "期間レポート", "Leftover map coverage": "残差マップの対象範囲", "Leftover map item coverage": "残差マップの基準対象範囲", + "Leftover map incomplete posts": "残差マップの不完全投稿", "Leftover map used {used} of {scored} scored posts (complete-case)": "残差マップは採点済み投稿 {scored} 件のうち {used} 件を使いました(完全ケース)", "Leftover map used {used} of {scored} scored criteria (complete-case)": @@ -2102,6 +2105,7 @@ const TRANSLATIONS: Partial>> = { "Period reports": "Báo cáo theo kỳ", "Leftover map coverage": "Phạm vi bản đồ phần dư", "Leftover map item coverage": "Phạm vi tiêu chí bản đồ phần dư", + "Leftover map incomplete posts": "Bài không đầy đủ trên bản đồ phần dư", "Leftover map used {used} of {scored} scored posts (complete-case)": "Bản đồ phần dư dùng {used} trên {scored} bài đã chấm (trường hợp đầy đủ)", "Leftover map used {used} of {scored} scored criteria (complete-case)": diff --git a/frontend/src/leftoverMapCoverage.test.ts b/frontend/src/leftoverMapCoverage.test.ts index 7fb82f653..0055f8dda 100644 --- a/frontend/src/leftoverMapCoverage.test.ts +++ b/frontend/src/leftoverMapCoverage.test.ts @@ -5,7 +5,9 @@ import { leftoverMapIncompleteItemCount, leftoverMapIncompletePostCount, leftoverMapItemCoverageCounts, + LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL, LEFTOVER_MAP_LIST_ITEM_COVERAGE_LABEL, + LEFTOVER_MAP_PLOT_INCOMPLETE_POST_LABEL, LEFTOVER_MAP_PLOT_ITEM_COVERAGE_LABEL, } from "./leftoverMapCoverage"; @@ -137,6 +139,12 @@ describe("leftoverMapIncompletePostCount", () => { dropped: 2, }); }); + + it("keeps the pair-list incomplete post label distinct from the graphic incomplete post label", () => { + expect(LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL).toBe("Leftover map incomplete posts"); + expect(LEFTOVER_MAP_PLOT_INCOMPLETE_POST_LABEL).toBe("Leftover-map graphic incomplete posts"); + expect(LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL).not.toBe(LEFTOVER_MAP_PLOT_INCOMPLETE_POST_LABEL); + }); }); describe("leftoverMapIncompleteItemCount", () => { diff --git a/frontend/src/leftoverMapCoverage.ts b/frontend/src/leftoverMapCoverage.ts index ec0dada46..a99617558 100644 --- a/frontend/src/leftoverMapCoverage.ts +++ b/frontend/src/leftoverMapCoverage.ts @@ -1,4 +1,4 @@ -/** Leftover-map complete-case coverage after IRT main effects (ADR 0168 / ADR 0281 / ADR 0282 / ADR 0283 / ADR 0284 / ADR 0285). */ +/** Leftover-map complete-case coverage after IRT main effects (ADR 0168 / ADR 0281 / ADR 0282 / ADR 0283 / ADR 0284 / ADR 0285 / ADR 0286). */ import type { LeftoverMapCoverage } from "./api"; @@ -14,6 +14,8 @@ export const LEFTOVER_MAP_PLOT_ITEM_COVERAGE = export const LEFTOVER_MAP_LIST_ITEM_COVERAGE_LABEL = "Leftover map item coverage"; +export const LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL = "Leftover map incomplete posts"; + export const LEFTOVER_MAP_PLOT_INCOMPLETE_POST_LABEL = "Leftover-map graphic incomplete posts"; export const LEFTOVER_MAP_PLOT_INCOMPLETE_POST = diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index 58daf960b..52dcb15dd 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -13,8 +13,8 @@ * coverage (ADR 0281), leftover-map item complete-case coverage * (ADR 0282), leftover-map incomplete post coverage (ADR 0283), and * leftover-map incomplete item coverage (ADR 0284). Pair-list item - * complete-case coverage (ADR 0285) captions the pair list, not this - * graphic layout. + * complete-case coverage (ADR 0285) and pair-list incomplete post + * coverage (ADR 0286) caption the pair list, not this graphic layout. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; diff --git a/pyproject.toml b/pyproject.toml index e70306f1e..e3b79802c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.42.0" +version = "2.43.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" }