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
3 changes: 2 additions & 1 deletion 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 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289) are computed in `lineageweave/leftover_pairs.py` from the
0163 / 0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290) 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 @@ -395,6 +395,7 @@ singular values `σ_k` so leftover-map axis share is not read as leftover-map
structure without the Gabriel scale that produced it; a missing, non-finite,
or negative singular value omits that `σ` badge independently of leftover-map
axis share and does not invent `σ_k` from leftover-map axis share.
ADR 0290 captions the grouping comparison strip with persisted leftover-map post complete-case coverage only when the caller can see the full persisted grouping population. Partial-visibility groupings omit that aggregate; never reconstruct the psychometric denominator from visible members, leftover pairs, or plotted markers.
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
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 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289) 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 / 0287 / 0288 / 0289 / 0290) 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 Down Expand Up @@ -669,7 +669,8 @@ item complete-case coverage. ADR 0286 captions the leftover pair list with persi
incomplete post coverage. ADR 0287 captions the leftover pair list with persisted leftover-map
incomplete item coverage. ADR 0288 fail-closes leftover-map post complete-case coverage
on the leftover pair list through leftoverMapCoverageCounts. ADR 0289 captions leftover-map
graphic axes with persisted leftover-map singular values `σ_k`. Click a post marker or a pair button
graphic axes with persisted leftover-map singular values `σ_k`. ADR 0290 captions the grouping comparison strip with persisted leftover-map post complete-case coverage under a full-visible-grouping rule; partial-visibility groupings fail closed that aggregate to avoid disclosing a hidden population denominator.
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
Expand All @@ -686,7 +687,7 @@ including on the pair list. Results persist to
`GET /api/reports/{grouping}` lists the trend;
`GET /api/reports/{grouping}/{period}` is ABAC-filtered;
`GET /api/reports/compare/{period}` is the home-page grouping strip
and carries the same ABAC-filtered leftover pairs (ADR 0149);
and carries the same ABAC-filtered leftover pairs (ADR 0149) plus persisted leftover-map post complete-case coverage (ADR 0290) only when the caller-visible member population equals the persisted grouping population;
`POST .../rebuild` scores every grouping kind (post_admin). `make seed`
folds A-100/B-200 Event Lineage fixtures (and the Riverbend calendar
post) that already have constructed IRT cells into the same shared
Expand Down
37 changes: 37 additions & 0 deletions CHANGELOG.d/2.47.0-leftover-map-compare-coverage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## 2.47.0 — Leftover-map complete-case coverage on the grouping comparison strip

- Caption leftover-map post complete-case coverage on the grouping comparison
strip through leftoverMapCoverageCounts (ADR 0290). 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 post complete-case coverage, a pair-list
note that matches leftover-map item complete-case coverage, a pair-list
note that matches leftover-map incomplete post coverage, a pair-list
note that matches leftover-map incomplete item coverage, and a grouping
comparison note that matches leftover-map post complete-case coverage;
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 comparison coverage note. 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 post coverage from leftover
pair count, plotted marker count, leftover-map distance, leftover-map
rank, leftover-map item coverage, leftover-map incomplete post coverage,
leftover-map incomplete item coverage, or the count of unused axes. The
strip does not gain leftover-map item coverage, leftover-map incomplete
post coverage, leftover-map incomplete item coverage, or the leftover-map
graphic. Never invent a leftover score. Never invent a theta. No new
columns.

- Authorization boundary: when ABAC hides any member of the persisted grouping, omit comparison `leftover_map_coverage` instead of exposing the full-group denominator or recomputing coverage from the visible subset.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to this project are documented here. Format follows

### Added

- Grouping comparison rows now expose persisted leftover-map post complete-case coverage through `leftoverMapCoverageCounts` (ADR 0290 / v2.47.0) only for fully visible grouping populations. Partial-visibility rows omit the aggregate instead of leaking the full-group denominator or recomputing psychometric coverage from the visible subset.

- Period leftover pairs now caption leftover-map graphic axes with persisted
leftover-map singular values `σ_k` (ADR 0289 / v2.46.0). After `make seed`,
closest and farthest leftover pairs sit above the member list with the
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 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289), occupational construct catalog search
(ADR 0048–0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289 / 0290), 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
5 changes: 5 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3052,6 +3052,11 @@ async def compare_period_groupings(
**row,
"members": [],
"leftover_pairs": leftover_pairs,
"leftover_map_coverage": (
row.get("leftover_map_coverage")
if len(members) == len(row["members"])
else None
),
"post_count": len(members),
}
)
Expand Down
19 changes: 19 additions & 0 deletions backend/app/report_ingestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,22 @@ async def fetch_period_comparison(
leftover_by_key: dict[tuple[str, str], list[asyncpg.Record]] = defaultdict(list)
for row in leftover:
leftover_by_key[(row["grouping_kind"], row["grouping_key"])].append(row)
leftover_coverage = await conn.fetch(
"""
select grouping_kind, grouping_key, map_post_count, scored_post_count,
map_item_count, scored_item_count,
incomplete_post_count, incomplete_item_count
from report_leftover_map_coverage
where period_code = $1 and rubric_version = $2
and grouping_kind = any($3::text[])
""",
period_code,
RUBRIC_VERSION,
list(GROUPING_KINDS),
)
leftover_coverage_by_key = {
(row["grouping_kind"], row["grouping_key"]): row for row in leftover_coverage
}
payload: list[dict[str, Any]] = []
for row in rows:
label = await resolve_grouping_label(conn, row["grouping_kind"], row["grouping_key"])
Expand Down Expand Up @@ -1113,6 +1129,9 @@ async def fetch_period_comparison(
}
for pair in leftover_by_key.get((row["grouping_kind"], row["grouping_key"]), [])
],
"leftover_map_coverage": _leftover_map_coverage_payload(
leftover_coverage_by_key.get((row["grouping_kind"], row["grouping_key"]))
),
}
)
return payload
Expand Down
13 changes: 13 additions & 0 deletions backend/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5829,6 +5829,19 @@ def test_seed_period_report_surfaces_on_get_reports(client, demo_analyst_token,
assert leftover_kinds <= {"closest", "farthest"}
assert all(pair["post_title"] for pair in leftover_thread.get("leftover_pairs", []))
assert all(pair["leftover_distance"] >= 0 for pair in leftover_thread.get("leftover_pairs", []))
leftover_compare_coverage = leftover_thread.get("leftover_map_coverage")
assert leftover_compare_coverage is not None
assert leftover_compare_coverage["map_post_count"] <= leftover_compare_coverage["scored_post_count"]
assert leftover_compare_coverage["scored_post_count"] > 0
assert leftover_compare_coverage["incomplete_post_count"] == (
leftover_compare_coverage["scored_post_count"] - leftover_compare_coverage["map_post_count"]
)
assert leftover_compare_coverage["map_item_count"] <= leftover_compare_coverage["scored_item_count"]
assert leftover_compare_coverage["map_post_count"] != len(
leftover_thread.get("leftover_pairs", [])
) or leftover_compare_coverage["scored_post_count"] > len(
leftover_thread.get("leftover_pairs", [])
)
assert all(
{"visibility_code", "corporate_entity_id", "process_unit_id"}.isdisjoint(pair)
for pair in leftover_thread.get("leftover_pairs", [])
Expand Down
Loading