Skip to content
Merged
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
9 changes: 6 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,12 @@ out of the factorization. Closest and farthest post–criterion pairs
persist to `report_leftover_pair` with signed residual `R`, observed
`Y`, and expected `E[Y|θ, item]` so `R = Y − E` remains auditable,
plus leftover-map rank so rank 0 is not read as structure,
unexplained leftover, and the ADR 0201 reconstruction evidence. ADR 0201
is the sole normative reconstruction formula, storage, and audit contract;
do not duplicate or reinterpret it here. The pairs sit above the member
unexplained leftover, ADR 0201 reconstruction evidence, leftover-map
cross share `x`, and leftover-map explained share `e = R̂² / R²` of
raw residual (ADR 0232). Unexplained leftover share `s` is not
persisted. ADR 0201 is the sole normative reconstruction formula,
storage, and audit contract; do not duplicate or reinterpret it here.
The pairs sit above the member
list so a click opens that post with the leftover criterion current
in Post quality (ADR 0158). Leftover-map axis share (ADR 0148) is Gabriel inertia of
residual SVD axes 1 and 2 and persists to `report_leftover_map_axis`.
Expand Down
5 changes: 3 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,9 +621,10 @@ 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) persist to `report_leftover_pair` with signed residual `R`,
0182 / 0185 / 0201 / 0232) 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, and ADR 0185 cross-share evidence.
leftover, ADR 0201 reconstruction evidence, ADR 0185 cross-share evidence,
and leftover-map explained share `e = R̂² / R²` of raw residual (ADR 0232).
Those ADRs are the normative mathematical and storage 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
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.d/2.19.0-leftover-map-explained-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## 2.19.0 — Leftover-map explained share

- Persist leftover-map explained share `e = R̂² / R²` of raw residual
on leftover post–criterion pairs (ADR 0232). After `make seed`,
closest and farthest leftover pairs sit above the member list with
`R̂²/R²` next to leftover-map distance `d`; click opens that post.
Omit the badge when the share is missing. A finite share greater than
1 is shown, never clamped. Never invent a leftover score. Do not
introduce leftover-map unexplained share `s`; ADR 0185 remains
authoritative for `x` and ADR 0201 for `R̂`.
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ cutoff.

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 / 0201), the text-channel embedding swap and cosine
(ADR 0048–0164 / 0182 / 0185 / 0201 / 0232), the text-channel embedding swap and cosine
clamp (ADR 0190), per-edge channel-score persistence (ADR 0195),
migration replay (ADR 0166), docstring coverage, and the measurement
boundary are all stated in [AGENTS.md](AGENTS.md) -- read it before
Expand Down
26 changes: 22 additions & 4 deletions backend/app/report_ingestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,8 @@ async def persist_period_report(
pair_kind, post_id, criterion_code, leftover_distance, leftover_residual,
observed_response, expected_response, leftover_map_rank,
leftover_map_unexplained, leftover_map_cross_share,
leftover_map_reconstruction
) values ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
leftover_map_reconstruction, leftover_map_explained_share
) values ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)
""",
grouping_kind,
grouping_key,
Expand All @@ -465,6 +465,7 @@ async def persist_period_report(
pair.leftover_map_unexplained,
pair.leftover_map_cross_share,
pair.leftover_map_reconstruction,
pair.leftover_map_explained_share,
)
for axis in report.leftover_map_axes:
await conn.execute(
Expand Down Expand Up @@ -652,7 +653,8 @@ async def fetch_period_reports(
lp.leftover_distance, lp.leftover_residual,
lp.observed_response, lp.expected_response, lp.leftover_map_rank,
lp.leftover_map_unexplained, lp.leftover_map_cross_share,
lp.leftover_map_reconstruction, p.post_title,
lp.leftover_map_reconstruction, lp.leftover_map_explained_share,
p.post_title,
p.visibility_code, p.corporate_entity_id, p.process_unit_id,
({_SOURCE_CONTEXT_PRESENT_SQL}) as has_real_source_context
from report_leftover_pair lp
Expand Down Expand Up @@ -809,6 +811,11 @@ async def fetch_period_reports(
if row["leftover_map_reconstruction"] is None
else float(row["leftover_map_reconstruction"])
),
"leftover_map_explained_share": (
None
if row["leftover_map_explained_share"] is None
else float(row["leftover_map_explained_share"])
),
"visibility_code": row["visibility_code"],
"corporate_entity_id": str(row["corporate_entity_id"]),
"process_unit_id": (
Expand Down Expand Up @@ -1010,7 +1017,8 @@ async def fetch_period_comparison(
f"""
select lp.grouping_kind, lp.grouping_key, lp.pair_kind, lp.post_id,
lp.criterion_code, lp.leftover_distance, lp.leftover_residual,
Comment thread
seonghobae marked this conversation as resolved.
lp.leftover_map_reconstruction,
lp.leftover_map_cross_share, lp.leftover_map_reconstruction,
lp.leftover_map_explained_share,
p.post_title, p.visibility_code, p.corporate_entity_id,
({_SOURCE_CONTEXT_PRESENT_SQL}) as has_real_source_context
from report_leftover_pair lp
Expand Down Expand Up @@ -1065,6 +1073,16 @@ async def fetch_period_comparison(
if pair["leftover_map_reconstruction"] is None
else float(pair["leftover_map_reconstruction"])
),
"leftover_map_cross_share": (
None
if pair["leftover_map_cross_share"] is None
else float(pair["leftover_map_cross_share"])
),
"leftover_map_explained_share": (
None
if pair["leftover_map_explained_share"] is None
else float(pair["leftover_map_explained_share"])
),
Comment thread
seonghobae marked this conversation as resolved.
"visibility_code": pair["visibility_code"],
"corporate_entity_id": str(pair["corporate_entity_id"]),
"has_real_source_context": bool(pair["has_real_source_context"]),
Expand Down
29 changes: 28 additions & 1 deletion backend/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@
/ "migrations"
/ "0206_report_leftover_map_reconstruction.sql"
)
_LEFTOVER_MAP_EXPLAINED_SHARE_MIGRATION = (
Path(__file__).resolve().parents[2]
/ "migrations"
/ "0236_report_leftover_map_explained_share.sql"
)
_GLOBAL_ASK_JOB_MIGRATION = (
Path(__file__).resolve().parents[2]
/ "migrations"
Expand Down Expand Up @@ -458,6 +463,7 @@ def seeded_db(demo_analyst_token):
cur.execute(_LEFTOVER_MAP_UNEXPLAINED_MIGRATION.read_text())
cur.execute(_LEFTOVER_MAP_CROSS_SHARE_MIGRATION.read_text())
cur.execute(_LEFTOVER_MAP_RECONSTRUCTION_MIGRATION.read_text())
cur.execute(_LEFTOVER_MAP_EXPLAINED_SHARE_MIGRATION.read_text())
cur.execute(
"insert into common_lookup_value (lookup_category, lookup_code, lookup_label) values "
"('corporate_entity_level', 'group', 'Group'), "
Expand Down Expand Up @@ -6117,9 +6123,14 @@ def test_seed_period_report_surfaces_on_get_reports(client, demo_analyst_token,
if share is not None:
assert not math.isnan(share)
assert not math.isinf(share)
explained = pair.get("leftover_map_explained_share")
assert "leftover_map_explained_share" in pair
assert explained is None or isinstance(explained, (int, float))
if explained is not None:
assert not math.isnan(explained)
assert not math.isinf(explained)
if unexplained is not None and reconstruction is not None:
assert unexplained + reconstruction == pytest.approx(pair["leftover_residual"])
assert "leftover_map_explained_share" not in pair
assert "leftover_map_unexplained_share" not in pair
leftover_axes = high_report.get("leftover_map_axes", [])
assert [axis["axis_index"] for axis in leftover_axes] == [1, 2]
Expand Down Expand Up @@ -6183,6 +6194,22 @@ def test_seed_period_report_surfaces_on_get_reports(client, demo_analyst_token,
or isinstance(pair["leftover_map_reconstruction"], (int, float))
for pair in leftover_thread.get("leftover_pairs", [])
)
assert all(
"leftover_map_cross_share" in pair
and (
pair["leftover_map_cross_share"] is None
or isinstance(pair["leftover_map_cross_share"], (int, float))
)
for pair in leftover_thread.get("leftover_pairs", [])
)
assert all(
"leftover_map_explained_share" in pair
and (
pair["leftover_map_explained_share"] is None
or isinstance(pair["leftover_map_explained_share"], (int, float))
)
for pair in leftover_thread.get("leftover_pairs", [])
)


def test_seed_period_report_includes_fixture_event_lineage_posts(
Expand Down
7 changes: 4 additions & 3 deletions docs/adr/0003-fast-mlsirm-report-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ than one large PR:
public Rust-backed prediction API (upstream PR #1279); LineageWeave must
not reproduce GRM/GPCM parameter conventions locally.
8. **Leftover evidence extensions:** unexplained leftover shipped in 2.12.26
(ADR 0182), cross-share evidence shipped in 2.12.29 (ADR 0185), and
reconstruction evidence is Unreleased for 2.12.31 (ADR 0201). Do not
persist explained share, unexplained share, or another unsupported alias.
(ADR 0182), cross-share evidence shipped in 2.12.29 (ADR 0185),
reconstruction evidence shipped in 2.12.31 (ADR 0201), and leftover-map
explained share shipped in 2.19.0 (ADR 0232). Do not persist unexplained
leftover share `s` or another unsupported alias.
9. **Leftover-map axis-share slice** (ADR 0148): persist Gabriel inertia
`σ_k² / Σ_j σ_j²` of leftover-map axes 1 and 2 on the same residual
SVD. Rank-0 residuals emit two zero-share axes. Do not invent a
Expand Down
4 changes: 3 additions & 1 deletion docs/adr/0048-persist-lsirm-leftover-pairs.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ read as leftover residual `R`, leftover-map distance `d`, explained
leftover share `e`, or unexplained leftover share `s` (ADR 0185).
ADR 0201 now persists that same signed reconstruction on the pair row so
`U + R̂ = R` remains directly auditable; it does not change this selection or
distance contract.
distance contract. ADR 0232 persists leftover-map explained share
`e = R̂² / R²` of raw residual so `e + s + x = 1` is not read from `x`
alone; unexplained leftover share `s` is still not persisted.

Cascade the rows with `report_period_score`. A leftover post must
also be a `report_member_score` row, and the leftover criterion
Expand Down
19 changes: 11 additions & 8 deletions docs/adr/0049-leftover-pair-report-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
[ADR 0182](0182-leftover-map-unexplained.md) (unexplained leftover U);
[ADR 0158](0158-leftover-criterion-evaluation-landing.md) (criterion evaluation landing);
[ADR 0185](0185-leftover-map-cross-share.md) (leftover-map cross share);
[ADR 0201](0201-leftover-map-reconstruction.md) (signed reconstruction R̂)
[ADR 0201](0201-leftover-map-reconstruction.md) (signed reconstruction R̂);
[ADR 0232](0232-leftover-map-explained-share.md) (leftover-map explained share)

## Context

Expand All @@ -26,16 +27,17 @@ On each period-report group, render leftover pairs **above** the
member list. Each pair is a button: closest or farthest label, post
title, criterion short label, signed residual `R`, two-axis leftover-map
distance, full map rank, observed `Y`, expected `E` when finite,
unexplained leftover `U`, signed reconstruction `R̂` when finite, and
leftover-map cross share next to distance when finite. The next action names every available
unexplained leftover `U`, signed reconstruction `R̂` when finite,
leftover-map cross share next to distance when finite, and leftover-map
explained share `e = R̂² / R²` of raw residual when finite. The next action names every available
measurement before opening the post; no amendment hides another, rank 0
explicitly names no leftover structure, and unexplained leftover names
"leftover map leaves unexplained `U` after IRT main effects; open this
post to read the named criterion" when present. When leftover-map cross
share is also present, the next action instead names the identity
remainder `x` two leftover-map axes leave in raw residual after
IRT main effects. A missing or non-finite value falls back in order —
cross share, then reconstruction, then unexplained leftover, then the existing
post to read the named criterion" when present. When leftover-map
explained share is also present, the next action names how much of the
raw residual two leftover-map axes explain after IRT main effects.
A missing or non-finite value falls back in order —
explained share, then cross share, then reconstruction, then unexplained leftover, then the existing
closest/farthest next action. Clicking the button opens that post with
leftover focus so Post quality marks the named criterion current
(ADR 0158). Residual naming is
Expand All @@ -45,6 +47,7 @@ is [ADR 0164](0164-leftover-map-rank.md), unexplained leftover naming
is [ADR 0182](0182-leftover-map-unexplained.md), leftover-map cross
share naming is [ADR 0185](0185-leftover-map-cross-share.md).
Reconstruction naming is [ADR 0201](0201-leftover-map-reconstruction.md).
Explained-share naming is [ADR 0232](0232-leftover-map-explained-share.md).

After `make seed`, closest and farthest leftover pairs sit above the
member list. Click a pair to open that post with the leftover
Expand Down
1 change: 1 addition & 0 deletions docs/adr/0185-leftover-map-cross-share.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

**Decision status:** Draft
**Date:** 2026-08-24
**Amended by:** [ADR 0232](0232-leftover-map-explained-share.md) (leftover-map explained share `e = R̂² / R²`)

Amends [ADR 0048](0048-persist-lsirm-leftover-pairs.md) and
[ADR 0049](0049-leftover-pair-report-ui.md).
Expand Down
1 change: 1 addition & 0 deletions docs/adr/0201-leftover-map-reconstruction.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

**Decision status:** Accepted
**Date:** 2026-08-25
**Amended by:** [ADR 0232](0232-leftover-map-explained-share.md) (leftover-map explained share `e = R̂² / R²`)

Amends [ADR 0048](0048-persist-lsirm-leftover-pairs.md),
[ADR 0049](0049-leftover-pair-report-ui.md), and
Expand Down
Loading