Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
1bd45fc
feat: persist leftover observed Y and expected E (v2.12.20)
seonghobae Aug 23, 2026
a30bb28
Repair inherited login build boundary
seonghobae Aug 23, 2026
df18ed6
Move leftover evidence contract to ADR 0177
seonghobae Aug 23, 2026
e05d63b
feat: name leftover-map rank on leftover pairs (v2.12.21)
seonghobae Aug 23, 2026
6fcf603
Move leftover-map rank contract to ADR 0172
seonghobae Aug 23, 2026
1834e7d
feat: name leftover residual on period-report pair rows (v2.12.22)
seonghobae Aug 23, 2026
2ca0974
fix: restore frontend build on leftover residual disclosure
seonghobae Aug 23, 2026
54f3f69
fix: restore frontend build on leftover-map PR
seonghobae Aug 23, 2026
cb5b21f
Merge remote-tracking branch 'origin/feat/board-source-detail-state-f…
seonghobae Aug 23, 2026
435992d
fix: show leftover Y and E after stacking onto the workspace branch
seonghobae Aug 23, 2026
dc508ae
Merge remote-tracking branch 'origin/feat/leftover-pair-observed-expe…
seonghobae Aug 23, 2026
a988ca6
fix: keep leftover-map rank after stacking onto leftover Y/E
seonghobae Aug 23, 2026
9b0e47e
Merge remote-tracking branch 'origin/feat/leftover-map-rank-v21221' i…
seonghobae Aug 23, 2026
13d95e1
Merge remote-tracking branch 'origin/feat/oidc-return-remember-login-…
seonghobae Aug 23, 2026
de4ff43
Merge remote-tracking branch 'origin/feat/leftover-pair-observed-expe…
seonghobae Aug 23, 2026
9d4ccf3
Merge remote-tracking branch 'origin/feat/leftover-map-rank-v21221' i…
seonghobae Aug 23, 2026
e1d2442
Merge remote-tracking branch 'origin/feat/oidc-return-remember-login-…
seonghobae Aug 24, 2026
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
7 changes: 7 additions & 0 deletions CHANGELOG.d/2.12.22-leftover-residual-disclosure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## 2.12.22 — Leftover residual disclosure

- Name signed leftover residual `R = Y − E[Y|θ, item]` on leftover
post–criterion pair rows (ADR 0178). After `make seed`, closest and
farthest leftover pairs sit above the member list with `R` next to
leftover-map distance `d`; click opens that post. A non-finite
residual is an em dash, never a fabricated leftover score.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ All notable changes to this project are documented here. Format follows
shell. The production frontend build type-checks again.


## [2.12.22] - 2026-08-24

### Added

- Period leftover pair rows now name signed leftover residual
`R = Y − E[Y|θ, item]` next to leftover-map distance `d`, then open
that post (Jeon et al., 2021, eq. 3; ADR 0178). A non-finite residual
is an em dash rather than a fabricated leftover score.

## [2.12.21] - 2026-08-24

### Added
Expand Down
67 changes: 67 additions & 0 deletions docs/adr/0178-leftover-residual-disclosure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# ADR 0178 — Disclose leftover residual on period-report pair rows

**Decision status:** Accepted
**Date:** 2026-08-24

Amends [ADR 0049](0049-leftover-pair-report-ui.md).

## Context

ADR 0048 already persists leftover-map distance and leftover residual
`R = Y − E[Y|θ, item]` on `report_leftover_pair`. ADR 0049 already
renders closest and farthest pairs above the member list and opens the
named post. The pair button showed only `d`, so a buyer could not tell
a large leftover response from a merely distant map pair.

Jeon et al. (2021, eq. 3) leftover interaction is
`−γ‖ξ_p − ζ_i‖`. Distance is that map gap. Residual is the observed
leftover *after IRT main effects* that entered the biplot. They are
different quantities. Hiding residual would keep the persisted column
as an unpublished measurement.

This increment does not persist leftover-map coordinates, does not name
observed `Y` / expected `E`, does not name leftover-map rank, and does
not land Post quality on the leftover criterion. No schema change:
`leftover_residual` already exists from ADR 0048 / migration `0012`.

The unprotected-stack ADR for the same buyer fact was 0162. This
protected-main reconstruction uses **0178** so it does not collide with
two-axis leftover-map distance (0166), leftover coverage (0168),
leftover-map axis share (0148), leftover-map rank (0172), leftover
observed Y / expected E (0177), or analysis-run status same clock
(0171).

## Decision

Each leftover pair button shows signed leftover residual `R` with two
decimal places next to leftover-map distance `d`. Next action: leftover
residual `R` after IRT main effects; open this post to read the named
criterion. A non-finite residual renders an em dash rather than a
fabricated leftover score. Click still uses the same post-open handler
as ADR 0049.

## Consequences

`GET /api/reports/{grouping}/{period}` already returns
`leftover_residual`. The frontend now names that value. After
`make seed`, closest and farthest leftover pairs sit above the member
list with `R` next to `d`; click opens that post.

## Related

Independent of leftover interaction-map persistence, leftover-criterion
evaluation landing, leftover-map complete-case coverage, leftover-map
axis share, leftover pairs on the grouping comparison strip, two-axis
leftover-map distance, leftover observed `Y` / expected `E`, and
leftover-map rank.

## 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
11 changes: 11 additions & 0 deletions frontend/src/leftoverResidual.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { describe, expect, it } from "vitest";
import { formatLeftoverResidual } from "./leftoverResidual";

describe("formatLeftoverResidual", () => {
it("keeps a signed residual without inventing a leftover score", () => {
expect(formatLeftoverResidual(0.4)).toBe("+0.40");
expect(formatLeftoverResidual(-1.1)).toBe("\u22121.10");
expect(formatLeftoverResidual(0)).toBe("0.00");
expect(formatLeftoverResidual(Number.NaN)).toBe("—");
});
});
18 changes: 18 additions & 0 deletions frontend/src/leftoverResidual.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/** Signed leftover residual ``R = Y − E[Y|θ, item]`` after IRT main effects. */

export const LEFTOVER_RESIDUAL_ACTION =
"Leftover residual R {residual} after IRT main effects. Open this post to read {criterion}.";

export function formatLeftoverResidual(value: number): string {
if (!Number.isFinite(value)) {
return "—";
}
const magnitude = Math.abs(value).toFixed(2);
if (value > 0) {
return `+${magnitude}`;
}
if (value < 0) {
return `\u2212${magnitude}`;
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
return magnitude;
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Comment on lines +3 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Signed residual R never rendered on pair rows

formatLeftoverResidual and LEFTOVER_RESIDUAL_ACTION are never imported or called. LeftoverPairButton still renders only observed/expected and rank, and App.tsx passes no residual prop, so the promised R badge next to d never appears.

Prompt for agents
The new frontend/src/leftoverResidual.ts exports formatLeftoverResidual and LEFTOVER_RESIDUAL_ACTION, but neither is wired into the UI. frontend/src/components/LeftoverPairButton.tsx renders observed/expected (formatLeftoverObservedExpected) and rank (formatLeftoverMapRank) badges next to the distance badge, but never renders the residual. frontend/src/App.tsx (around line 4197) passes observedResponse, expectedResponse, and leftoverMapRank props to LeftoverPairButton but no residual. To deliver the feature described in ADR 0178 and the CHANGELOG, add a leftoverResidual prop to LeftoverPairButton, format it with formatLeftoverResidual, and render it as a post-badge next to the d badge; pass pair.leftover_residual from App.tsx.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

1 change: 1 addition & 0 deletions tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ def test_leftover_pair_names_leftover_map_rank_column(schema_db) -> None:
columns = dict(cur.fetchall())
assert columns["leftover_map_rank"] == "YES"


def test_corporate_hierarchy_recursive_query_returns_correct_shape(schema_db) -> None:
"""The real product requirement: 'Acme Group -> Acme Electronics Korea
-> Acme Electronics Gwangju Plant' must be walkable with one query,
Expand Down
Loading