Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) 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) 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
Expand Down Expand Up @@ -337,7 +337,12 @@ coordinates. ADR 0280 captions leftover-map pair
segments with persisted leftover-map rank so the pair-row `rank` badge
matches the graphic; a missing, negative, or non-integer rank omits that
leftover-map rank caption and does not invent rank from plotted
coordinates, leftover-map distance, or the count of unused axes. When `R`, `R̂`, `U`, `x`,
coordinates, leftover-map distance, or the count of unused axes. ADR 0281
captions the leftover-map graphic display with persisted leftover-map
complete-case coverage so the pair-list `used N of M scored posts` note
matches the plot; a missing, non-integer, negative-used, non-positive-scored,
or used-greater-than-scored coverage omits that leftover-map coverage caption
and does not invent coverage 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
finite, `U + R̂ = R`. When coordinates,
Expand All @@ -348,8 +353,8 @@ in Post quality (ADR 0158). Leftover-map axis share (ADR 0148) is Gabriel inerti
residual SVD axes 1 and 2 and persists to `report_leftover_map_axis`.
Rank-0 residuals emit two zero-share axes; the shares are report-level
and are not a leftover score. Complete-case coverage (ADR 0168) persists to
`report_leftover_map_coverage` and captions the pair list with how
many scored posts entered the map.
`report_leftover_map_coverage` and captions the pair list and the leftover-map
graphic with how many scored posts entered the map.

Authorized occupational construct catalog search (ADR 0257) matches official
O*NET preferred labels or descriptions only when a source-eligible, ABAC-visible
Expand Down
7 changes: 4 additions & 3 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) 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) 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
Expand All @@ -658,13 +658,14 @@ unexplained leftover `U`. ADR 0277 captions leftover-map pair segments with pers
residual `R`. ADR 0278 captions leftover-map pair segments with persisted leftover
observed `Y`. ADR 0279 captions leftover-map pair segments with persisted leftover
expected `E`. ADR 0280 captions leftover-map pair segments with persisted leftover-map
rank. Click a post marker or a pair button
rank. ADR 0281 captions the leftover-map graphic display with persisted leftover-map
complete-case 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
`report_leftover_map_axis`. Complete-case leftover-map coverage (ADR
0168) persists to `report_leftover_map_coverage` so readers see how
many scored posts entered the factorization. Results persist to
many scored posts entered the factorization, including on the graphic. Results persist to
`report_period_score` / `report_member_score`.
`GET /api/reports/{grouping}` lists the trend;
`GET /api/reports/{grouping}/{period}` is ABAC-filtered;
Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.d/2.38.0-leftover-map-plot-coverage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## 2.38.0 — Leftover-map complete-case coverage on the graphic display

- Caption the leftover-map graphic display with persisted leftover-map
complete-case coverage (ADR 0281). 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, and
a plot caption that matches leftover-map complete-case coverage above
the pair list; click a post marker or a pair button opens that post.
A missing, non-integer, negative-used, non-positive-scored, or
used-greater-than-scored coverage omits that leftover-map coverage
caption. Coverage `0 of M` is shown when that persisted used count is
a non-negative integer. Do not invent coverage from plotted marker
count, leftover-map distance, leftover-map rank, or the count of unused
axes. Never invent a leftover score. Never invent a theta. No new
columns.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,28 @@ 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 complete-case coverage (ADR 0281 / v2.38.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, and a plot caption that matches leftover-map
complete-case coverage above the pair list; click a post marker or a
pair button opens that post. A missing, non-integer, negative-used,
non-positive-scored, or used-greater-than-scored coverage omits that
leftover-map coverage caption. Coverage `0 of M` is shown when that
persisted used count is a non-negative integer. Do not invent coverage
from plotted marker count, leftover-map distance, leftover-map rank, or
the count of unused axes. Never invent a leftover score or a theta. No
new columns.

- Period leftover pairs now caption leftover-map graphic-display pair
segments with persisted leftover-map rank (ADR 0280 / v2.37.0). After
`make seed`, closest and farthest leftover pairs sit above the member
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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), 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), 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),
Expand Down
157 changes: 157 additions & 0 deletions docs/adr/0281-leftover-map-plot-coverage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# ADR 0281 — Name leftover-map complete-case coverage on the graphic display

**Decision status:** Accepted
**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 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 one `report_leftover_map_coverage` row per period
report and captions the pair list with `Leftover map used N of M scored
posts (complete-case)`. ADR 0280 already names persisted leftover-map rank
on leftover-map graphic-display pair segments. The graphic still has no
coverage caption, so a buyer who reads two pair markers can treat those
markers as the scored-post census even after the pair-list note names
`2 of 3`. Incomplete rows are dropped from Gabriel factorization; hiding
coverage on the plot lets a sparse post with one missing criterion vanish
without a next action.

This increment names leftover-map complete-case coverage on the leftover-map
graphic display. It does not add columns. It does not recompute coverage from
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 **0281** so it does not collide with
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
complete-case coverage as `Leftover map used N of M scored posts
(complete-case)`, using the same `map_post_count` / `scored_post_count`
integers the pair-list note already shows. A missing coverage row, a
non-integer count, a negative used count, a non-positive scored count, or
used greater than scored omits that leftover-map coverage 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, or leftover-map rank caption. Coverage `0 of M` is shown when that
persisted used count is a non-negative integer and scored is a positive
integer. Do not invent coverage from plotted marker count, leftover-map
distance, leftover-map rank, 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 coverage 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, and the plot names persisted
leftover-map complete-case 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 coverage when that coverage 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, and leftover-map rank on pair
segments.

## 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; coverage names how many scored posts
entered that factorization.)
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
"version": "2.37.0",
"version": "2.38.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4154,6 +4154,9 @@ describe("App, authenticated", () => {
expect(screen.getByLabelText("Leftover map coverage")).toHaveTextContent(
"Leftover map used 2 of 3 scored posts (complete-case)",
);
expect(screen.getByLabelText("Leftover-map graphic coverage")).toHaveTextContent(
"Leftover map used 2 of 3 scored posts (complete-case)",
);
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", {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3869,6 +3869,7 @@ function ReportsPanel({
<LeftoverPairList
pairs={report.leftover_pairs}
leftoverMapAxes={report.leftover_map_axes}
leftoverMapCoverage={report.leftover_map_coverage}
criterionLabel={criterionShortLabel}
onSelectPost={(pair) => {
onSelectPost(pair.post_id, {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/LeftoverMapPlot.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
background: var(--color-background);
}

.leftover-map-plot-caption {
.leftover-map-plot-caption,
.leftover-map-plot-coverage {
margin: 0 0 var(--space-control-gap);
color: var(--color-text);
font-size: 0.85rem;
Expand Down
Loading