-
Notifications
You must be signed in to change notification settings - Fork 1
feat: name leftover-map reconstruction on leftover pairs (v2.12.25) #534
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
02c4396
c8bdb4e
a7ff20c
6a97999
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| ## 2.12.25 — Leftover-map reconstruction | ||
|
|
||
| - Persist two-axis leftover-map reconstruction `R̂ = ξ_{1:2} · ζ_{1:2}` | ||
| on leftover post–criterion pairs (ADR 0181). 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. Omit | ||
| the badge when reconstruction is missing. Never invent a leftover | ||
| score. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -273,6 +273,15 @@ All notable changes to this project are documented here. Format follows | |||||
| Jeon et al., 2021, eq. 3; ADR 0181). A missing length omits the | ||||||
| badge. Never invent a leftover score. | ||||||
|
|
||||||
| ## [2.12.25] - 2026-08-24 | ||||||
|
|
||||||
| ### Added | ||||||
|
|
||||||
| - Period leftover pair rows now name two-axis leftover-map reconstruction | ||||||
| `R̂ = ξ_{1:2} · ζ_{1:2}` next to leftover-map distance `d`, then open | ||||||
| that post (Gabriel, 1971; Jeon et al., 2021, eq. 3; ADR 0181). A missing | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Reconstruction changelog cites wrong ADR The reconstruction entry cites
Suggested change
Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||
| reconstruction omits the badge rather than inventing a leftover score. | ||||||
|
|
||||||
| ## [2.12.6] - 2026-08-20 | ||||||
|
|
||||||
| ### Added | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # ADR 0182 — Name two-axis leftover-map reconstruction on period-report pair rows | ||
|
|
||
| **Decision status:** Accepted | ||
| **Date:** 2026-08-24 | ||
|
|
||
| Amends [ADR 0048](0048-persist-lsirm-leftover-pairs.md) and | ||
| [ADR 0049](0049-leftover-pair-report-ui.md). | ||
|
|
||
| ## Context | ||
|
|
||
| ADR 0048 already persists leftover-map distance `d = ‖ξ_p − ζ_i‖` 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. Distance is the Jeon et al. (2021, | ||
| eq. 3) map gap. Gabriel (1971) reconstructs a matrix from the biplot | ||
| as the inner product of person and item coordinates. The leftover map | ||
| readers read is two-axis: unused axes pad with zero, and hidden SVD | ||
| axes after the second are dropped. Full-rank inner product `ξ·ζ` | ||
| therefore reconstructs the centered residual, not the cell the | ||
| two-axis map shows. Hiding two-axis reconstruction `R̂ = ξ_{1:2} · ζ_{1:2}` | ||
| lets a reader read leftover residual `R` or leftover-map distance `d` | ||
| as the reconstructed leftover cell. | ||
|
|
||
| This increment does not persist leftover-map coordinates, does not | ||
| name leftover-map inner product as a separate full-rank column, does | ||
| not name leftover-map cosine, does not name observed `Y` / expected | ||
| `E`, does not name leftover-map rank, does not split leftover-map | ||
| distance onto two axes, and does not land Post quality on the leftover | ||
| criterion. Leftover-map distance stays full-rank Euclidean. | ||
|
|
||
| The unprotected-stack reconstructions for neighbouring leftover facts | ||
| use 0162–0180. This protected-main increment uses **0182** so it does | ||
| not collide with leftover-map cosine (0180), leftover-map inner product | ||
| (0179), leftover residual disclosure (0178), leftover observed `Y` / | ||
| expected `E` (0177), leftover-map rank (0172), two-axis leftover-map | ||
| distance (0166), leftover coverage (0168), leftover-map axis share | ||
| (0148), or leftover interaction-map persistence (0121). | ||
|
|
||
| ## Decision | ||
|
|
||
| Each leftover pair names `leftover_map_reconstruction` — the two-axis | ||
| Gabriel reconstruction `R̂ = ξ_{1:2} · ζ_{1:2}` of the leftover-map | ||
| person and item coordinates. Migration `0182` is the single source of | ||
| the column on every install path, fresh or existing -- shipped | ||
| migrations (`0001` / `0012`) are never edited after the fact. The | ||
| column is nullable so older leftover rows keep distance and residual | ||
| without fabricating a reconstruction. Fallback pairs that have no | ||
| complete-case leftover map omit the value rather than inventing one. | ||
| A rank-0 origin map stores `0.0` (zeros · zeros), not a missing value. | ||
| A non-finite reconstruction stores null rather than inventing a | ||
| leftover score. | ||
|
|
||
| The pair button shows `R̂ {signed}` next to leftover-map distance `d` | ||
| when the value is finite. Next action: leftover map reconstructs `R̂` | ||
| after IRT main effects; open this post to read the named criterion. | ||
| A missing or non-finite reconstruction omits the badge and keeps the | ||
| existing closest/farthest next action. Do not invent a leftover score. | ||
| Do not invent a theta. | ||
|
|
||
| ## Consequences | ||
|
|
||
| `GET /api/reports/{grouping}/{period}` returns | ||
| `leftover_map_reconstruction`. After `make seed`, closest and farthest | ||
| leftover pairs sit above the member list with named `R̂` next to `d`; | ||
| click opens that post. Hidden posts stay hidden. | ||
|
|
||
| ## Related | ||
|
|
||
| Independent of leftover interaction-map persistence, leftover-criterion | ||
| evaluation landing, leftover residual disclosure, leftover observed | ||
| `Y` / expected `E`, leftover-map complete-case coverage, leftover-map | ||
| axis share, leftover pairs on the grouping comparison strip, two-axis | ||
| leftover-map distance, leftover-map rank, leftover-map inner product, | ||
| and leftover-map cosine. | ||
|
|
||
| ## 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -149,6 +149,11 @@ import { | |
| useLocale, | ||
| } from "./i18n"; | ||
| import { rememberOidcReturnUrl, returnUrlFromLocation } from "./oidcReturnUrl"; | ||
| import { | ||
| formatLeftoverMapReconstruction, | ||
| formatSignedLeftoverValue, | ||
| LEFTOVER_MAP_RECONSTRUCTION_ACTION, | ||
| } from "./leftoverMapReconstruction"; | ||
|
Comment on lines
+152
to
+156
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔍 R̂ badge feature never wired into the UI The reconstruction helpers imported here are never used, and Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| import "./App.css"; | ||
|
|
||
| function orchestratorUnavailableMessage(err: unknown, action: string): string { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { describe, expect, it } from "vitest"; | ||
| import { | ||
| formatLeftoverMapReconstruction, | ||
| formatSignedLeftoverValue, | ||
| } from "./leftoverMapReconstruction"; | ||
|
|
||
| describe("formatLeftoverMapReconstruction", () => { | ||
| it("names two-axis leftover-map reconstruction without inventing a leftover score", () => { | ||
| expect(formatLeftoverMapReconstruction(0.4)).toBe("R\u0302 +0.40"); | ||
| expect(formatLeftoverMapReconstruction(-1.1)).toBe("R\u0302 \u22121.10"); | ||
| expect(formatLeftoverMapReconstruction(0)).toBe("R\u0302 0.00"); | ||
| expect(formatSignedLeftoverValue(0.4)).toBe("+0.40"); | ||
| expect(formatSignedLeftoverValue(-1.1)).toBe("\u22121.10"); | ||
| }); | ||
|
|
||
| it("omits the badge when reconstruction is missing or non-finite", () => { | ||
| expect(formatLeftoverMapReconstruction(null)).toBeNull(); | ||
| expect(formatLeftoverMapReconstruction(undefined)).toBeNull(); | ||
| expect(formatLeftoverMapReconstruction(Number.NaN)).toBeNull(); | ||
| expect(formatLeftoverMapReconstruction(Number.POSITIVE_INFINITY)).toBeNull(); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /** Two-axis leftover-map reconstruction ``R̂ = ξ_{1:2} · ζ_{1:2}``. */ | ||
|
|
||
| export const LEFTOVER_MAP_RECONSTRUCTION_ACTION = | ||
| "Leftover map reconstructs R̂ {value} after IRT main effects. Open this post to read {criterion}."; | ||
|
|
||
| export function formatSignedLeftoverValue(value: number): string | null { | ||
| if (!Number.isFinite(value)) { | ||
| return null; | ||
| } | ||
| const magnitude = Math.abs(value).toFixed(2); | ||
| if (value > 0) { | ||
| return `+${magnitude}`; | ||
| } | ||
| if (value < 0) { | ||
| return `\u2212${magnitude}`; | ||
| } | ||
| return magnitude; | ||
| } | ||
|
|
||
| export function formatLeftoverMapReconstruction(value: number | null | undefined): string | null { | ||
| if (value == null) { | ||
| return null; | ||
| } | ||
| const signed = formatSignedLeftoverValue(value); | ||
| return signed === null ? null : `R\u0302 ${signed}`; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| -- ADR 0182: persist two-axis leftover-map reconstruction R̂ = ξ_{1:2} · ζ_{1:2} | ||
| -- on leftover post–criterion pairs. Distance stays Euclidean leftover-map d. | ||
| -- Upgrade column is nullable so older leftover rows keep distance and | ||
| -- residual without fabricating a reconstruction. This migration is the | ||
| -- single source of the column on fresh and existing installations. | ||
|
|
||
| alter table report_leftover_pair | ||
| add column if not exists leftover_map_reconstruction numeric; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| -- Reverse 0182. Leftover distance and residual stay on the pair row. | ||
|
|
||
| alter table report_leftover_pair | ||
| drop column if exists leftover_map_reconstruction; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Duplicate 2.12.25 changelog version
The new section reuses
## [2.12.25], already used above for the leftover-map lengths increment (CHANGELOG.md:252). Two distinct feature entries now share one version, breaking the sequential order that reaches 2.12.25 just above.Was this helpful? React with 👍 or 👎 to provide feedback.