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
25 changes: 21 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) are computed in `lineageweave/leftover_pairs.py` from the
0163 / 0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271) 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 All @@ -282,12 +282,29 @@ persist to `report_leftover_pair` with signed residual `R`, observed
plus leftover-map rank so rank 0 is not read as structure,
unexplained leftover, the ADR 0201 reconstruction evidence, ADR 0185
cross-share evidence, ADR 0233 unexplained leftover share
`s = U² / R²`, and ADR 0266 explained leftover share `e = R̂² / R²`.
`s = U² / R²`, ADR 0266 explained leftover share `e = R̂² / R²`, and
ADR 0267 leftover-map coordinates `ξ_{1:2}` / `ζ_{1:2}`.
ADR 0201 is the sole normative reconstruction formula,
storage, and audit contract; do not duplicate or reinterpret it here.
ADR 0233 is the sole unexplained leftover share contract. ADR 0266 is
the sole explained leftover share contract. When `R`, `R̂`, `U`, `x`,
`s`, and `e` are finite, `e + s + x = 1`. The pairs sit above the member
the sole explained leftover share contract. ADR 0267 is the sole
leftover-map coordinate contract. ADR 0268 is the leftover-map
graphic-display contract: draw persisted `ξ` and `ζ` above the pair
buttons; omit the plot when coordinates are missing; click a post
marker to open that post. ADR 0269 captions those leftover-map axes
with persisted leftover-map axis share `σ_k² / Σ_j σ_j²` when finite,
including rank-0 zero-share axes; a missing or non-finite share omits
that axis badge and keeps the existing leftover-map axis text. ADR 0270
ticks leftover-map axes at the origin and at each unique finite
persisted `ξ` / `ζ` coordinate so the pair-row badge matches the
plot; rank-0 unused axes name only `0` and do not invent drawing-scale
ticks. ADR 0271 captions leftover-map pair segments with persisted
leftover-map distance `d` so the pair-row badge matches the graphic;
a missing or non-finite `d` omits that segment caption and does not
invent `d` from plotted coordinates. When `R`, `R̂`, `U`, `x`,
`s`, and `e` are finite, `e + s + x = 1`. When coordinates,
reconstruction, and distance are finite, `R̂ = ξ · ζ` and
`d = ‖ξ − ζ‖`. 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
18 changes: 13 additions & 5 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -637,12 +637,19 @@ 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) persist to `report_leftover_pair` with signed residual `R`,
0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271) 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`, and ADR 0266 explained leftover
share `e`.
Those ADRs are the normative mathematical and storage contracts. Leftover-map axis share
ADR 0233 unexplained leftover share `s`, ADR 0266 explained leftover
share `e`, and ADR 0267 leftover-map coordinates `ξ_{1:2}` / `ζ_{1:2}`.
ADR 0268 draws those persisted coordinates as the leftover-map graphic
display above the pair buttons. ADR 0269 captions leftover-map axes 1
and 2 with persisted leftover-map axis share. ADR 0270 ticks those
leftover-map axes at persisted `ξ` / `ζ` coordinates so the pair-row
badge matches the plot. ADR 0271 captions leftover-map pair segments
with persisted leftover-map distance `d`. 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
Expand All @@ -660,7 +667,8 @@ through opens those DAG posts. Report members include the earliest
open ticket title, status lookup label, and due date when one exists. The home page renders
the actual mean θ, the FIPC delta, the CAT-selected item, leftover
closest/farthest pairs (signed residual `R`, observed `Y`, expected
`E`, full rank, and two-axis leftover-map distance `d` after IRT main
`E`, full rank, two-axis leftover-map distance `d`, and leftover-map
coordinates `ξ` / `ζ` after IRT main
effects) above the member list, leftover-map axis share for residual
SVD axes 1 and 2, and complete-case coverage captions (map used N of M
scored posts), plus the
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.d/2.24.0-leftover-map-coordinates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## 2.24.0 — Leftover-map coordinates

- Persist leftover-map coordinates `ξ_{1:2}` and `ζ_{1:2}` on leftover
post–criterion pairs (ADR 0267). After `make seed`, closest and
farthest leftover pairs sit above the member list with `ξ (x, y)
ζ (x, y)` next to leftover-map distance `d`; click opens that post.
Omit all four coordinate values when any coordinate is missing or non-finite,
including `NaN` and positive or negative infinity. Rank-0 unused axes
store `0.0`. When coordinates, reconstruction, and distance are
finite, `R̂ = ξ · ζ` and `d = ‖ξ − ζ‖`. Never invent a leftover
score. Never invent a theta.
11 changes: 11 additions & 0 deletions CHANGELOG.d/2.25.0-leftover-map-graphic-display.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## 2.25.0 — Leftover-map graphic display

- Draw the leftover-map graphic display of persisted `ξ_{1:2}` and
`ζ_{1:2}` above leftover post–criterion pairs (ADR 0268). After
`make seed`, closest and farthest leftover pairs sit above the
member list with the Gabriel biplot of already-named coordinates;
click a post marker or a pair button opens that post. Omit the plot
when no pair has four finite leftover-map coordinates. Rank-0
unused axes plot at the origin. When coordinates, reconstruction,
and distance are finite, `R̂ = ξ · ζ` and `d = ‖ξ − ζ‖`. Never
invent a leftover score. Never invent a theta. No new columns.
11 changes: 11 additions & 0 deletions CHANGELOG.d/2.26.0-leftover-map-axis-share-plot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## 2.26.0 — Leftover-map axis share on the graphic display

- Caption leftover-map graphic-display axes with persisted leftover-map
axis share `σ_k² / Σ_j σ_j²` (ADR 0269). After `make seed`, closest
and farthest leftover pairs sit above the member list with the
Gabriel biplot of already-named coordinates, and each leftover-map
axis names its inertia share; click a post marker or a pair button
opens that post. Rank-0 zero-share axes still name 0%. A missing or
non-finite share omits that axis badge and keeps the existing axis
text. Never invent a leftover score. Never invent a theta. No new
columns.
11 changes: 11 additions & 0 deletions CHANGELOG.d/2.27.0-leftover-map-coordinate-ticks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## 2.27.0 — Leftover-map coordinate ticks on the graphic display

- Caption leftover-map graphic-display axes with persisted leftover-map
coordinate ticks (ADR 0270). 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, and
axis ticks that match `ξ (x, y) ζ (x, y)` on the pair row; click a
post marker or a pair button opens that post. Origin is always named
`0`. Rank-0 unused axes do not invent `−1` / `+1` drawing-scale
ticks. Never invent a leftover score. Never invent a theta. No new
columns.
12 changes: 12 additions & 0 deletions CHANGELOG.d/2.28.0-leftover-map-segment-distance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## 2.28.0 — Leftover-map distance on graphic-display pair segments

- Caption leftover-map graphic-display pair segments with persisted
leftover-map distance `d` (ADR 0271). 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,
and pair segments that match `d` on the pair row; click a post
marker or a pair button opens that post. A missing or non-finite
`d` omits that segment caption. Do not invent `d` from plotted
coordinates. Never invent a leftover score. Never invent a theta.
No new columns.
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,56 @@ All notable changes to this project are documented here. Format follows

### Added

- Period leftover pairs now caption leftover-map graphic-display pair
segments with persisted leftover-map distance `d` (ADR 0271 /
v2.28.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, and pair segments that
match `d` on the pair row; click a post marker or a pair button
opens that post. A missing or non-finite `d` omits that segment
caption. Do not invent `d` from plotted coordinates. Never invent a
leftover score or a theta. No new columns.

- Period leftover pairs now caption leftover-map graphic-display axes
with persisted leftover-map coordinate ticks (ADR 0270 / v2.27.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, and axis ticks that match
`ξ (x, y) ζ (x, y)` on the pair row; click a post marker or a pair
button opens that post. Origin is always named `0`. Rank-0 unused
axes do not invent `−1` / `+1` drawing-scale ticks. Never invent a
leftover score or a theta. No new columns.

- Period leftover pairs now caption leftover-map graphic-display axes
with persisted leftover-map axis share `σ_k² / Σ_j σ_j²` (ADR 0269 /
v2.26.0). After `make seed`, closest and farthest leftover pairs sit
above the member list with the Gabriel biplot of already-named
coordinates, and each leftover-map axis names its inertia share;
click a post marker or a pair button opens that post. Rank-0
zero-share axes still name 0%. A missing or non-finite share omits
that axis badge and keeps the existing leftover-map axis text. Never
invent a leftover score or a theta. No new columns.

- Period leftover pairs now draw the leftover-map graphic display of
persisted `ξ_{1:2}` and `ζ_{1:2}` (ADR 0268 / v2.25.0). After
`make seed`, closest and farthest leftover pairs sit above the
member list with the Gabriel biplot of already-named coordinates;
click a post marker or a pair button opens that post. Omit the plot
when no pair has four finite leftover-map coordinates. Rank-0 unused
axes plot at the origin. When coordinates, reconstruction, and
distance are finite, `R̂ = ξ · ζ` and `d = ‖ξ − ζ‖`. Never invent
a leftover score or a theta. No new columns.

- Period leftover pair rows now name leftover-map coordinates `ξ_{1:2}`
and `ζ_{1:2}` after two-axis Gabriel reconstruction (ADR 0267 /
v2.24.0). After `make seed`, closest and farthest leftover pairs sit
above the member list with `ξ (x, y) ζ (x, y)` next to leftover-map
distance `d`; click opens that post. Rank-0 unused axes store `0.0`.
When coordinates, reconstruction, and distance are finite,
`R̂ = ξ · ζ` and `d = ‖ξ − ζ‖`. Never invent a leftover score or a
theta.

- Period leftover pair rows now name leftover-map explained leftover share
`e = R̂² / R²` of raw residual after two-axis Gabriel reconstruction
(ADR 0266 / v2.23.0). After `make seed`, closest and farthest leftover
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), occupational construct catalog search
(ADR 0048–0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271), 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
34 changes: 31 additions & 3 deletions backend/app/report_ingestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,10 @@ async def persist_period_report(
observed_response, expected_response, leftover_map_rank,
leftover_map_unexplained, leftover_map_cross_share,
leftover_map_reconstruction, leftover_map_unexplained_share,
leftover_map_explained_share
) values ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17)
leftover_map_explained_share, leftover_map_person_axis_1,
leftover_map_person_axis_2, leftover_map_item_axis_1,
leftover_map_item_axis_2
) values ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21)
""",
grouping_kind,
grouping_key,
Expand All @@ -468,6 +470,10 @@ async def persist_period_report(
pair.leftover_map_reconstruction,
pair.leftover_map_unexplained_share,
pair.leftover_map_explained_share,
pair.leftover_map_person_axis_1,
pair.leftover_map_person_axis_2,
pair.leftover_map_item_axis_1,
pair.leftover_map_item_axis_2,
)
for axis in report.leftover_map_axes:
await conn.execute(
Expand Down Expand Up @@ -656,7 +662,9 @@ async def fetch_period_reports(
lp.observed_response, lp.expected_response, lp.leftover_map_rank,
lp.leftover_map_unexplained, lp.leftover_map_cross_share,
lp.leftover_map_reconstruction, lp.leftover_map_unexplained_share,
lp.leftover_map_explained_share, p.post_title,
lp.leftover_map_explained_share, lp.leftover_map_person_axis_1,
lp.leftover_map_person_axis_2, lp.leftover_map_item_axis_1,
lp.leftover_map_item_axis_2, 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 @@ -823,6 +831,26 @@ async def fetch_period_reports(
if row["leftover_map_explained_share"] is None
else float(row["leftover_map_explained_share"])
),
"leftover_map_person_axis_1": (
None
if row["leftover_map_person_axis_1"] is None
else float(row["leftover_map_person_axis_1"])
),
"leftover_map_person_axis_2": (
None
if row["leftover_map_person_axis_2"] is None
else float(row["leftover_map_person_axis_2"])
),
"leftover_map_item_axis_1": (
None
if row["leftover_map_item_axis_1"] is None
else float(row["leftover_map_item_axis_1"])
),
"leftover_map_item_axis_2": (
None
if row["leftover_map_item_axis_2"] is None
else float(row["leftover_map_item_axis_2"])
),
"visibility_code": row["visibility_code"],
"corporate_entity_id": str(row["corporate_entity_id"]),
"process_unit_id": (
Expand Down
38 changes: 38 additions & 0 deletions backend/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,21 @@
/ "migrations"
/ "0206_report_leftover_map_reconstruction.sql"
)
_LEFTOVER_MAP_UNEXPLAINED_SHARE_MIGRATION = (
Path(__file__).resolve().parents[2]
/ "migrations"
/ "0233_report_leftover_map_unexplained_share.sql"
)
_LEFTOVER_MAP_EXPLAINED_SHARE_MIGRATION = (
Path(__file__).resolve().parents[2]
/ "migrations"
/ "0244_report_leftover_map_explained_share.sql"
)
_LEFTOVER_MAP_COORDINATES_MIGRATION = (
Path(__file__).resolve().parents[2]
/ "migrations"
/ "0245_report_leftover_map_coordinates.sql"
)
_GLOBAL_ASK_JOB_MIGRATION = (
Path(__file__).resolve().parents[2]
/ "migrations"
Expand Down Expand Up @@ -407,6 +422,9 @@ 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_UNEXPLAINED_SHARE_MIGRATION.read_text())
cur.execute(_LEFTOVER_MAP_EXPLAINED_SHARE_MIGRATION.read_text())
cur.execute(_LEFTOVER_MAP_COORDINATES_MIGRATION.read_text())
Comment thread
seonghobae marked this conversation as resolved.
cur.execute(
"insert into common_lookup_value (lookup_category, lookup_code, lookup_label) values "
"('corporate_entity_level', 'group', 'Group'), "
Expand Down Expand Up @@ -5731,6 +5749,26 @@ def test_seed_period_report_surfaces_on_get_reports(client, demo_analyst_token,
assert unexplained_share >= 0.0
if explained_share is not None:
assert explained_share >= 0.0
person_axis_1 = pair.get("leftover_map_person_axis_1")
person_axis_2 = pair.get("leftover_map_person_axis_2")
item_axis_1 = pair.get("leftover_map_item_axis_1")
item_axis_2 = pair.get("leftover_map_item_axis_2")
axes = (person_axis_1, person_axis_2, item_axis_1, item_axis_2)
if any(axis is None for axis in axes):
assert axes == (None, None, None, None)
else:
for axis in axes:
assert isinstance(axis, (int, float))
assert not math.isnan(axis)
assert not math.isinf(axis)
reconstruction = pair.get("leftover_map_reconstruction")
if reconstruction is not None:
assert reconstruction == pytest.approx(
person_axis_1 * item_axis_1 + person_axis_2 * item_axis_2
)
assert pair["leftover_distance"] == pytest.approx(
math.hypot(person_axis_1 - item_axis_1, person_axis_2 - item_axis_2)
)
if (
explained_share is not None
and unexplained_share is not None
Expand Down
Loading
Loading