diff --git a/AGENTS.md b/AGENTS.md
index a5d5d9996..c4b34cac2 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) 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) 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
@@ -354,6 +354,13 @@ 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 caption and does not invent dropped
posts from scored minus used or from plotted marker count.
+ADR 0284 captions the leftover-map graphic display with persisted leftover-map
+incomplete item coverage so a `used N of M scored criteria` caption is not
+read as every scored criterion entering the map; a missing, non-integer, or
+negative dropped count, or a dropped count that contradicts usable item
+complete-case integers, omits that leftover-map incomplete item caption and
+does not invent dropped criteria from scored minus used or from plotted
+criterion 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
@@ -369,7 +376,9 @@ and are not a leftover score. Complete-case coverage (ADR 0168) persists to
graphic with how many scored posts entered the map. Item complete-case coverage
(ADR 0282) captions the leftover-map graphic 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.
+graphic 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.
Authorized occupational construct catalog search (ADR 0257) matches official
O*NET preferred labels or descriptions only when a source-eligible, ABAC-visible
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index e38514fdd..6ceeeb4b2 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) 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) 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
@@ -661,7 +661,9 @@ expected `E`. ADR 0280 captions leftover-map pair segments with persisted leftov
rank. ADR 0281 captions the leftover-map graphic display with persisted leftover-map
complete-case coverage. ADR 0282 captions the leftover-map graphic display with
persisted leftover-map item complete-case coverage. ADR 0283 captions the leftover-map
-graphic display with persisted leftover-map incomplete post coverage. Click a post marker or a pair button
+graphic display with persisted leftover-map incomplete post coverage. ADR 0284
+captions the leftover-map graphic display with persisted leftover-map incomplete
+item 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
@@ -669,7 +671,8 @@ display contracts. Leftover-map axis share
0168) persists to `report_leftover_map_coverage` so readers see how
many scored posts entered the factorization, including on the graphic,
and how many scored criteria entered the factorization on the graphic,
-and how many scored posts stayed incomplete and out of the factorization. Results persist to
+and how many scored posts stayed incomplete and out of the factorization,
+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;
`GET /api/reports/{grouping}/{period}` is ABAC-filtered;
diff --git a/CHANGELOG.d/2.41.0-leftover-map-plot-incomplete-item.md b/CHANGELOG.d/2.41.0-leftover-map-plot-incomplete-item.md
new file mode 100644
index 000000000..68543243e
--- /dev/null
+++ b/CHANGELOG.d/2.41.0-leftover-map-plot-incomplete-item.md
@@ -0,0 +1,28 @@
+## 2.41.0 — Leftover-map incomplete item coverage on the graphic display
+
+- Caption the leftover-map graphic display with persisted leftover-map
+ incomplete item coverage (ADR 0284). 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, and a plot caption that matches leftover-map incomplete item
+ 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 item complete-case integers, omits that leftover-map
+ incomplete item caption. Dropped `0` is shown when that persisted
+ dropped count is a non-negative integer. Do not invent dropped criteria
+ from scored minus used, plotted criterion marker count, leftover-map
+ distance, leftover-map rank, leftover-map post coverage, leftover-map
+ item coverage, leftover-map incomplete post 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 df3f58696..568249bf9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,33 @@ All notable changes to this project are documented here. Format follows
### Added
+- Period leftover pairs now caption the leftover-map graphic display with
+ persisted leftover-map incomplete item coverage (ADR 0284 / v2.41.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, and a plot caption that matches
+ leftover-map incomplete item 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 item
+ complete-case integers, omits that leftover-map incomplete item caption.
+ Dropped `0` is shown when that persisted dropped count is a non-negative
+ integer. Do not invent dropped criteria from scored minus used, plotted
+ criterion marker count, leftover-map distance, leftover-map rank,
+ leftover-map post coverage, leftover-map item coverage, leftover-map
+ incomplete post 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-map graphic display with
persisted leftover-map incomplete post coverage (ADR 0283 / v2.40.0). After
`make seed`, closest and farthest leftover pairs sit above the member
diff --git a/CLAUDE.md b/CLAUDE.md
index 70e509a8a..f6d2b42aa 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), 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), 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/0284-leftover-map-plot-incomplete-item.md b/docs/adr/0284-leftover-map-plot-incomplete-item.md
new file mode 100644
index 000000000..7adac02e9
--- /dev/null
+++ b/docs/adr/0284-leftover-map-plot-incomplete-item.md
@@ -0,0 +1,173 @@
+# ADR 0284 — Name leftover-map incomplete item coverage on the graphic display
+
+**Decision status:** Proposed
+**Date:** 2026-08-30
+
+Amends [ADR 0268](0268-leftover-map-graphic-display.md),
+[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 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_item_count` on
+`report_leftover_map_coverage`. ADR 0282 already names persisted leftover-map
+item complete-case coverage on the leftover-map graphic display. ADR 0283
+already names persisted leftover-map incomplete post coverage on that
+graphic. The graphic still has no incomplete-item caption, so a buyer who
+reads `used N of M scored criteria (complete-case)` can treat those N
+criteria as the full scored census even after incomplete columns were
+dropped. Incomplete columns are dropped from Gabriel factorization; hiding
+dropped criteria on the plot lets a sparse criterion with one missing post
+vanish without a next action.
+
+This increment names leftover-map incomplete item coverage on the
+leftover-map graphic display. It does not add columns. It does not recompute
+dropped criteria from scored minus used, plotted criterion 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 **0284** so it does not collide with
+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-map graphic display, caption persisted leftover-map incomplete
+item coverage as `Leftover map dropped {dropped} incomplete criteria`, using
+the persisted `incomplete_item_count` integer. A missing coverage row, a
+non-integer dropped count, a negative dropped count, or a dropped count that
+contradicts usable item complete-case integers (`dropped !== scored − used`
+when `map_item_count` / `scored_item_count` are usable) omits that leftover-map
+incomplete item caption and keeps the connecting lines 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, or leftover-map incomplete post caption. Dropped
+`0` is shown when that persisted dropped count is a non-negative integer. Do
+not invent dropped criteria from scored minus used, plotted criterion marker
+count, leftover-map distance, leftover-map rank, leftover-map post coverage,
+leftover-map item coverage, leftover-map incomplete post coverage, or the
+count of unused axes. Click a post marker to open that post. Criterion markers
+are not post buttons. The grouping comparison strip (ADR 0149) stays on its
+reduced leftover payload and does not gain this leftover-map incomplete item
+caption. The pair-list note (ADR 0168) stays post coverage.
+
+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, and the plot names persisted leftover-map incomplete item
+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 criteria 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, and
+leftover-map incomplete post coverage on the graphic display.
+
+## 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 columns are dropped from the
+complete-case residual rectangle; incomplete item coverage names how many
+scored criteria stayed out of that factorization.)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index c86ef43fa..a5d79a0b6 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,5 +1,59 @@
# Product & Technical Gap Baseline
+> Exact-head loop overlay: 2026-08-30 20: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`. Leave
+> #782/#802/#803/#804/#805/#806/#808/#809/#810/#811/#812/#813/#814 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 item
+> coverage on the graphic display (ADR 0284 / v2.41.0) delivered locally on
+> `feat/leftover-map-plot-incomplete-item-v2410`. Caption the leftover-map
+> graphic with persisted leftover-map incomplete item coverage so a
+> `used N of M scored criteria` caption is not read as every scored
+> criterion entering the map. UI-only; no new columns. Missing,
+> non-integer, or negative dropped count, or a dropped count that
+> contradicts usable item complete-case integers, omits that leftover-map
+> incomplete item caption. Dropped `0` is shown when persisted. Do not
+> invent dropped criteria from scored minus used, plotted criterion
+> marker count, leftover-map distance, leftover-map rank, leftover-map
+> post coverage, leftover-map item coverage, leftover-map incomplete
+> post coverage, or the count of unused axes. Do not invent leftover
+> scores. Stack onto leftover branch `feat/leftover-map-plot-incomplete-v2400`
+> / #814; 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. Pair-list note stays post
+> coverage (ADR 0168). Grouping comparison strip (ADR 0149) does not
+> gain this caption.
+
> Exact-head loop overlay: 2026-08-30 20:20 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 2c1d14bfd..78bcc406e 100644
--- a/docs/storybook-inventory.md
+++ b/docs/storybook-inventory.md
@@ -5,7 +5,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. `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/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. | `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` |
diff --git a/frontend/package.json b/frontend/package.json
index 7c43d0863..8dbd0fbda 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
- "version": "2.40.0",
+ "version": "2.41.0",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx
index 8af153295..d151aabe9 100644
--- a/frontend/src/App.test.tsx
+++ b/frontend/src/App.test.tsx
@@ -4163,6 +4163,9 @@ describe("App, authenticated", () => {
expect(screen.getByLabelText("Leftover-map graphic incomplete posts")).toHaveTextContent(
"Leftover map dropped 1 incomplete posts",
);
+ expect(screen.getByLabelText("Leftover-map graphic incomplete items")).toHaveTextContent(
+ "Leftover map dropped 0 incomplete criteria",
+ );
const coverageCaption = screen.getByLabelText("Leftover map coverage");
const closestPair = screen.getByRole("button", { name: /open leftover closest pair: public post/i });
const farthestPair = screen.getByRole("button", {
diff --git a/frontend/src/components/LeftoverMapPlot.css b/frontend/src/components/LeftoverMapPlot.css
index 4f4642701..d6d866ec8 100644
--- a/frontend/src/components/LeftoverMapPlot.css
+++ b/frontend/src/components/LeftoverMapPlot.css
@@ -9,7 +9,8 @@
.leftover-map-plot-caption,
.leftover-map-plot-coverage,
.leftover-map-plot-item-coverage,
-.leftover-map-plot-incomplete-posts {
+.leftover-map-plot-incomplete-posts,
+.leftover-map-plot-incomplete-items {
margin: 0 0 var(--space-control-gap);
color: var(--color-text);
font-size: 0.85rem;
diff --git a/frontend/src/components/LeftoverMapPlot.test.tsx b/frontend/src/components/LeftoverMapPlot.test.tsx
index 39d4b0794..1c6e11c22 100644
--- a/frontend/src/components/LeftoverMapPlot.test.tsx
+++ b/frontend/src/components/LeftoverMapPlot.test.tsx
@@ -61,7 +61,7 @@ describe("LeftoverMapPlot", () => {
expect(screen.getByLabelText("Leftover-map graphic display")).toBeInTheDocument();
expect(
screen.getByText(
- "Leftover map after IRT main effects. Axis ticks name persisted leftover-map coordinates. Pair segments name leftover-map distance d, leftover-map reconstruction R̂, leftover-map explained leftover share e, leftover-map unexplained leftover share s, leftover-map cross share x, leftover-map unexplained leftover U, leftover residual R, leftover observed Y, leftover expected E, and leftover-map rank. The plot names leftover-map complete-case coverage, leftover-map item complete-case coverage, and leftover-map incomplete post coverage when persisted. Click a post marker to open that post. The plot does not invent a leftover score.",
+ "Leftover map after IRT main effects. Axis ticks name persisted leftover-map coordinates. Pair segments name leftover-map distance d, leftover-map reconstruction R̂, leftover-map explained leftover share e, leftover-map unexplained leftover share s, leftover-map cross share x, leftover-map unexplained leftover U, leftover residual R, leftover observed Y, leftover expected E, and leftover-map rank. 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. Click a post marker to open that post. The plot does not invent a leftover score.",
),
).toBeInTheDocument();
expect(screen.getByText("Post ξ")).toBeInTheDocument();
@@ -92,6 +92,7 @@ describe("LeftoverMapPlot", () => {
expect(screen.queryByLabelText("Leftover-map graphic coverage")).not.toBeInTheDocument();
expect(screen.queryByLabelText("Leftover-map graphic item coverage")).not.toBeInTheDocument();
expect(screen.queryByLabelText("Leftover-map graphic incomplete posts")).not.toBeInTheDocument();
+ expect(screen.queryByLabelText("Leftover-map graphic incomplete items")).not.toBeInTheDocument();
const postMarker = screen.getByRole("button", {
@@ -643,6 +644,9 @@ describe("LeftoverMapPlot", () => {
expect(screen.getByLabelText("Leftover-map graphic incomplete posts")).toHaveTextContent(
"Leftover map dropped 1 incomplete posts",
);
+ expect(screen.getByLabelText("Leftover-map graphic incomplete items")).toHaveTextContent(
+ "Leftover map dropped 1 incomplete criteria",
+ );
});
it("omits leftover-map item coverage on the graphic when item coverage is missing or not usable", () => {
@@ -672,6 +676,9 @@ describe("LeftoverMapPlot", () => {
expect(screen.getByLabelText("Leftover-map graphic incomplete posts")).toHaveTextContent(
"Leftover map dropped 1 incomplete posts",
);
+ expect(screen.getByLabelText("Leftover-map graphic incomplete items")).toHaveTextContent(
+ "Leftover map dropped 0 incomplete criteria",
+ );
});
it("names leftover-map item coverage 0 of M on a rank-0 origin when that persisted used count is a non-negative integer", () => {
@@ -720,6 +727,9 @@ describe("LeftoverMapPlot", () => {
expect(screen.getByLabelText("Leftover-map graphic incomplete posts")).toHaveTextContent(
"Leftover map dropped 3 incomplete posts",
);
+ expect(screen.getByLabelText("Leftover-map graphic incomplete items")).toHaveTextContent(
+ "Leftover map dropped 2 incomplete criteria",
+ );
});
it("omits leftover-map incomplete posts on the graphic when the dropped count is missing or not usable", () => {
@@ -746,6 +756,9 @@ describe("LeftoverMapPlot", () => {
expect(screen.getByLabelText("Leftover-map graphic coverage")).toHaveTextContent(
"Leftover map used 2 of 3 scored posts (complete-case)",
);
+ expect(screen.getByLabelText("Leftover-map graphic incomplete items")).toHaveTextContent(
+ "Leftover map dropped 0 incomplete criteria",
+ );
});
it("names leftover-map incomplete posts 0 when that persisted dropped count is a non-negative integer", () => {
@@ -770,5 +783,61 @@ describe("LeftoverMapPlot", () => {
expect(screen.getByLabelText("Leftover-map graphic coverage")).toHaveTextContent(
"Leftover map used 3 of 3 scored posts (complete-case)",
);
+ expect(screen.getByLabelText("Leftover-map graphic incomplete items")).toHaveTextContent(
+ "Leftover map dropped 0 incomplete criteria",
+ );
+ });
+
+ it("omits leftover-map incomplete items on the graphic when the dropped count is missing or not usable", () => {
+ const { rerender } = render(
+
+ {tf(LEFTOVER_MAP_PLOT_INCOMPLETE_ITEM, incompleteItemCount)} +
+ ) : null}