feat: persist leftover interaction-map coordinates (v2.12.7) - #481
feat: persist leftover interaction-map coordinates (v2.12.7)#481seonghobae wants to merge 14 commits into
Conversation
Keep Gabriel leftover-map ξ / ζ after IRT main effects, persist complete-case axes, and render a 2D biplot above leftover pairs. Click a post node to open that post. Rank-0/1 maps pad unused axes with zero; missing cells stay out of the factorization (ADR 0121).
|
Warning Review limit reachedNext included review available in 54 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (28)
📝 WalkthroughWalkthrough기간 보고서는 잔차 SVD 결과에서 person 및 item의 2D 좌표를 계산합니다. 좌표를 PostgreSQL에 저장하고 API로 반환합니다. 프론트엔드는 좌표와 leftover pair를 SVG 지도에 표시하며 게시물 선택을 지원합니다. Changes잔여 상호작용 지도
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: 🟡 Moderate · up to The change adds a persisted interactive leftover map, but pair rankings can disagree with the displayed 2D coordinates, API requests can fail when the new tables are missing from test setup, and some interactive nodes may be inaccessible to assistive technology; merge readiness is therefore blocked pending these fixes. Sequence Diagram(s)sequenceDiagram
participant PeriodReport
participant PostgreSQL
participant ReportAPI
participant ReportUI
PeriodReport->>PostgreSQL: person/item 지도 좌표 저장
ReportAPI->>PostgreSQL: 그룹별 지도 데이터 조회
PostgreSQL-->>ReportAPI: 좌표와 게시물 메타데이터 반환
ReportAPI-->>ReportUI: leftover_map_persons/items 반환
ReportUI->>ReportUI: SVG 지도와 leftover pair 렌더링
ReportUI->>ReportAPI: 선택한 게시물 상세 요청
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
migrations/0001_initial_schema.sql is baked into the Postgres image and only replays on fresh installs; the leftover-interaction-map tables were added there directly as well as in the new, correctly idempotent 0104_report_leftover_interaction_map.sql, leaving existing volumes and fresh installs on different schemas. Removes the duplicate from 0001 and keeps 0104 as the single source of truth. Updates tests/test_schema.py's schema_db fixture to apply 0104 explicitly (it lists each schema migration by hand rather than globbing), and fixes a second, unrelated bug the schema test then surfaced: report_ingestion.py's new leftover_map_persons query had the required nosemgrep suppression but was missing its "Safe SQL:" reason comment, and the suppression count constant wasn't bumped for the new site.
|
Independent exact-head APPROVE still required on |
|
cycle-write-probe 2026-08-23T11:24Z: retrying GitHub writes; not a self-approve. |
|
Cycle 2026-08-23T11:30Z: GitHub writes succeeded. Exact head remains |
|
Cycle note (not a review): leftover-map v2.12.7 still needs an independent exact-head APPROVE. I will not self-approve or squash-merge. Checks on 7ff509e are green except Strix in progress. Next independent buyer gap is leftover-pair landing into Post quality (v2.12.8 on main leftover list, not mixed into this map stack). |
Pair-member leftover-map criterion diamonds open the leftover-pair post (closest preferred, then farthest). Non-pair criteria stay non-interactive. Independent of leftover Post-quality landing.
|
Retry write this cycle: still no independent APPROVE. This identity will not self-approve. Squash-merge only with independent APPROVE + exact-head required success. Next independent leftover-map increment is leftover-map length ‖ξ‖/‖ζ‖ on #533 (fresh from main, not mixed into this stack). |
* feat: persist leftover observed Y and expected E (v2.12.20) After seed, leftover closest/farthest pairs sit above the member list with observed Y and expected E[Y|θ, item] next to leftover-map distance d. Click opens that post. Residual stays R = Y − E (Jeon et al., 2021 eq. 3; Gabriel 1971; ADR 0170). Never invent a leftover score or theta. Independent of leftover persist-map (#481), criterion landing (#485), complete-case coverage (#518), leftover-map axis share (#519), comparison-strip leftover pairs (#521), and two-axis distance (#522). Issues #79 and #87 stay open. * Repair inherited login build boundary * Move leftover evidence contract to ADR 0177 * fix: show leftover Y and E after stacking onto the workspace branch Merge #490 without force-push. Keep ADR 0049 next-action copy and name observed Y and expected E on leftover pair buttons. Replay migration 0177 on existing volumes. * fix(leftover-observed-expected): wire missing migration and translations Two genuine gaps in this PR's own diff, unrelated to any merge conflict: 1. backend/tests/test_api.py and tests/test_schema.py never applied migration 0177_report_leftover_observed_expected.sql in their test DB fixtures, even though report_ingestion.py already inserts into the observed_response/expected_response columns it adds. Every period-report test that touches leftover pairs failed with UndefinedColumnError. 2. frontend/src/i18n.ts never got the ko/zh/ja/vi translations for the two leftover-map-rank action strings that frontend/src/leftoverMapRank.ts (added downstream, in the PR that bases on this one) references — t()/tf() fall back to the raw English key for every locale, failing the i18n test suite in the whole downstream chain. Verified: 1035 Python tests pass, 362 frontend tests pass, build/lint clean.
* feat: persist leftover observed Y and expected E (v2.12.20) After seed, leftover closest/farthest pairs sit above the member list with observed Y and expected E[Y|θ, item] next to leftover-map distance d. Click opens that post. Residual stays R = Y − E (Jeon et al., 2021 eq. 3; Gabriel 1971; ADR 0170). Never invent a leftover score or theta. Independent of leftover persist-map (#481), criterion landing (#485), complete-case coverage (#518), leftover-map axis share (#519), comparison-strip leftover pairs (#521), and two-axis distance (#522). Issues #79 and #87 stay open. * Repair inherited login build boundary * Move leftover evidence contract to ADR 0177 * feat: name leftover-map rank on leftover pairs (v2.12.21) After seed, leftover closest/farthest pairs sit above the member list with leftover-map rank next to leftover-map distance d. Click opens that post. Rank is the count of Gabriel singular values above the floor (Jeon et al., 2021 eq. 3; Gabriel 1971; ADR 0171). Rank 0 names no leftover structure. Never invent a leftover score or theta. Independent of leftover persist-map (#481), criterion landing (#485), complete-case coverage (#518), leftover-map axis share (#519), comparison-strip leftover pairs (#521), two-axis distance (#522), and observed Y / expected E (#527). Issues #79 and #87 stay open. * Move leftover-map rank contract to ADR 0172 ADR 0171 is already used by analysis-run status same clock (#524). Keep leftover-map rank independent: ADR 0172 and migration 0172. Shipped 0001 / 0012 stay untouched. * fix: restore frontend build on leftover-map PR Drop unused OIDC import and unauthenticated AdminPanel that fail tsc (TS6192 / TS2322). AdminPanel stays authenticated-only. * fix: show leftover Y and E after stacking onto the workspace branch Merge #490 without force-push. Keep ADR 0049 next-action copy and name observed Y and expected E on leftover pair buttons. Replay migration 0177 on existing volumes. * fix: keep leftover-map rank after stacking onto leftover Y/E Merge stacked #527 without force-push. Persist leftover_map_rank with observed Y and expected E, and name rank on leftover pair buttons.
) * feat: persist leftover observed Y and expected E (v2.12.20) After seed, leftover closest/farthest pairs sit above the member list with observed Y and expected E[Y|θ, item] next to leftover-map distance d. Click opens that post. Residual stays R = Y − E (Jeon et al., 2021 eq. 3; Gabriel 1971; ADR 0170). Never invent a leftover score or theta. Independent of leftover persist-map (#481), criterion landing (#485), complete-case coverage (#518), leftover-map axis share (#519), comparison-strip leftover pairs (#521), and two-axis distance (#522). Issues #79 and #87 stay open. * Repair inherited login build boundary * Move leftover evidence contract to ADR 0177 * feat: name leftover-map rank on leftover pairs (v2.12.21) After seed, leftover closest/farthest pairs sit above the member list with leftover-map rank next to leftover-map distance d. Click opens that post. Rank is the count of Gabriel singular values above the floor (Jeon et al., 2021 eq. 3; Gabriel 1971; ADR 0171). Rank 0 names no leftover structure. Never invent a leftover score or theta. Independent of leftover persist-map (#481), criterion landing (#485), complete-case coverage (#518), leftover-map axis share (#519), comparison-strip leftover pairs (#521), two-axis distance (#522), and observed Y / expected E (#527). Issues #79 and #87 stay open. * Move leftover-map rank contract to ADR 0172 ADR 0171 is already used by analysis-run status same clock (#524). Keep leftover-map rank independent: ADR 0172 and migration 0172. Shipped 0001 / 0012 stay untouched. * feat: name leftover residual on period-report pair rows (v2.12.22) After seed, leftover closest/farthest pairs sit above the member list with signed leftover residual R next to leftover-map distance d. Click opens that post. Residual is R = Y − E[Y|θ, item] (Jeon et al., 2021 eq. 3; Gabriel 1971; ADR 0178). A non-finite residual is an em dash. Never invent a leftover score or theta. Independent of leftover persist-map (#481), criterion landing (#485), complete-case coverage (#518), leftover-map axis share (#519), comparison-strip leftover pairs (#521), two-axis distance (#522), observed Y / expected E (#527), and leftover-map rank (#529). Issues #79 and #87 stay open. * fix: restore frontend build on leftover residual disclosure Drop unused OIDC import and unauthenticated AdminPanel that fail tsc (TS6192 / TS2322). Login matches the workspace branch: AdminPanel is authenticated-only. * fix: restore frontend build on leftover-map PR Drop unused OIDC import and unauthenticated AdminPanel that fail tsc (TS6192 / TS2322). AdminPanel stays authenticated-only. * fix: show leftover Y and E after stacking onto the workspace branch Merge #490 without force-push. Keep ADR 0049 next-action copy and name observed Y and expected E on leftover pair buttons. Replay migration 0177 on existing volumes. * fix: keep leftover-map rank after stacking onto leftover Y/E Merge stacked #527 without force-push. Persist leftover_map_rank with observed Y and expected E, and name rank on leftover pair buttons.
Resolves conflicts across 16 files where main's independent leftover- pair rank/residual work (ADR 0119/0162/0163/0164) and migrate.sh's ADR-0166 generic replay pattern landed alongside this branch's own leftover interaction-map persistence (ADR 0121): LeftoverPair keeps main's observed_response/expected_response/leftover_map_rank fields and candidate-row helpers while leftover_map_from_residual keeps this branch's LeftoverMapPerson/LeftoverMapItem biplot output, migration 0172 runs after 0163/0164, and every test function from both sides is preserved. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B5M79L945DMyMs3sg5yJ14
| persons = tuple( | ||
| LeftoverMapPerson( | ||
| post_id=post_ids[int(person)], | ||
| axis_one=float(person_xy[local, 0]), | ||
| axis_two=float(person_xy[local, 1]), | ||
| ) | ||
| for local, person in enumerate(person_index) | ||
| ) | ||
| items = tuple( | ||
| LeftoverMapItem( | ||
| criterion_code=item_codes[int(item)], | ||
| axis_one=float(item_xy[local, 0]), | ||
| axis_two=float(item_xy[local, 1]), | ||
| ) | ||
| for local, item in enumerate(item_index) | ||
| ) |
There was a problem hiding this comment.
📝 Info: Map coordinates reconcile with pair distances
The persons/items tuples reuse the same person_xy/item_xy arrays and enumeration order as the pair-distance computation, so a persisted coordinate matches the Euclidean distance of any pair naming it. When no complete-case rectangle exists, persons/items stay empty while pairs still form from observed cells, and their member/item foreign keys still hold.
Was this helpful? React with 👍 or 👎 to provide feedback.
The PR's own two feature commits (2.12.7 leftover-interaction-map and
2.12.10 leftover-map-criterion-node) each independently added
"Closest leftover"/"Farthest leftover" translations to every locale
block, producing duplicate object-literal keys that tsc rejects
(TS1117). Drop the later duplicate in each locale, keeping the
zh-Hans variant ("残差") that i18n.test.ts already asserts.
| for person in report.leftover_map_persons: | ||
| await conn.execute( | ||
| """ | ||
| insert into report_leftover_map_person ( | ||
| grouping_kind, grouping_key, period_code, rubric_version, | ||
| post_id, axis_one, axis_two | ||
| ) values ($1,$2,$3,$4,$5,$6,$7) | ||
| """, | ||
| grouping_kind, | ||
| grouping_key, | ||
| period_code, | ||
| RUBRIC_VERSION, | ||
| person.post_id, | ||
| person.axis_one, | ||
| person.axis_two, | ||
| ) | ||
| for item in report.leftover_map_items: | ||
| await conn.execute( | ||
| """ | ||
| insert into report_leftover_map_item ( | ||
| grouping_kind, grouping_key, period_code, rubric_version, | ||
| criterion_code, axis_one, axis_two | ||
| ) values ($1,$2,$3,$4,$5,$6,$7) | ||
| """, | ||
| grouping_kind, | ||
| grouping_key, | ||
| period_code, | ||
| RUBRIC_VERSION, | ||
| item.criterion_code, | ||
| item.axis_one, | ||
| item.axis_two, | ||
| ) |
There was a problem hiding this comment.
📝 Info: Map-row FK integrity relies on insert order and cascade
The new map tables have composite FKs to report_member_score/report_item_information and report_period_score. persist_period_report deletes report_period_score first (cascading) then inserts parents before the map rows. Biplot persons are a subset of the group members and items a subset of CRITERION_CODES (all written to report_item_information), so the FKs hold. The seed path mirrors this order.
Was this helpful? React with 👍 or 👎 to provide feedback.
| persons = tuple( | ||
| LeftoverMapPerson( | ||
| post_id=post_ids[int(person)], | ||
| axis_one=float(person_xy[local, 0]), | ||
| axis_two=float(person_xy[local, 1]), | ||
| ) | ||
| for local, person in enumerate(person_index) | ||
| ) | ||
| items = tuple( | ||
| LeftoverMapItem( | ||
| criterion_code=item_codes[int(item)], | ||
| axis_one=float(item_xy[local, 0]), | ||
| axis_two=float(item_xy[local, 1]), | ||
| ) | ||
| for local, item in enumerate(item_index) | ||
| ) |
There was a problem hiding this comment.
📝 Info: Map coordinates lack the finiteness guard pair distances have
Pair distances that overflow to non-finite are skipped (lineageweave/leftover_pairs.py:162), but person/item coordinates from the Gabriel SVD are persisted with no finiteness check. Pathological extreme residuals could yield inf/nan coordinates a numeric column rejects. Real residuals are bounded (categories 0..4), so this is not reachable with actual data.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Cycle 2026-08-24 18:02 KST: leftover interaction-map persist+UI is being restacked onto current main@b4911f8 as |
|
Cycle 2026-08-24T18:02 KST: leftover interaction-map persist+UI is restacked onto current main@b4911f8 as |
Summary
Period reports already persist closest/farthest leftover pairs (ADR 0048 / 0049) from a Gabriel biplot of
R = Y − E[Y|θ, item]. The person positions ξ and item positions ζ that produce those pairs were computed and discarded. This increment persists the complete-case leftover interaction map and renders it above the leftover pair list.report_leftover_map_personandreport_leftover_map_item(axis_one,axis_two).0172_report_leftover_interaction_map.sql.migrate.shalso replays0103_tenant_settings.sql(now idempotent).Independent of #258, #468, and #480. Author will not self-approve.
Test plan
Exact head:
329449790cc65868063c32a82eac9ed777fe9b2ftests/test_leftover_pairs.py(9 passed) — rank-1 pad, origin map, sparse row excludedtests/test_migration_replay.py—0103_*and0172_*replayed; obsolete0104_*absentmake seed, leftover map sits above leftover pairs; click a post node to open that post.References
Gabriel (1971); Jeon et al. (2021, eq. 3). ADR 0048, ADR 0049, ADR 0003, ADR 0121.
Summary by CodeRabbit
새로운 기능
개선 사항