--- date: 2026-03-30 topic: "compass-trajectory-consistency" status: draft --- # Implementation Plan — Compass ↔ Trajectory Consistency This plan implements the validated design (thoughts/shared/designs/2026-03-30-compass-trajectory-consistency-design.md) with the following firm constraints from the user: - Use per-window MP-centroid party coordinates as the canonical source for components 1 & 2 - When a party has no MPs in a window, use the first chronological party vector as fallback - **Update all callers** to the new explicit API; do NOT keep backward compatibility shims ## Goal Make the political compass numeric values identical to trajectory centroids for SVD components 1 and 2 by passing explicit per-party (x,y) coordinates (computed from positions_by_window) to the compass renderer and updating all callers to use that API. ## Micro-tasks (ordered, small, actionable) All tasks assume a development branch and running tests locally. Each task should be one commit. 1) Add explorer_helpers.py (pure helper) - Create compute_party_coords(positions_by_window, party_map, window_id, fallback_party_scores=None) - Returns (party_coords: Dict[str,(x,y)], fallback_used: Set[str]) - Unit tests: tests/test_explorer_helpers.py - Estimate: 2.0h 2) Update explorer.py to the new strict API - Replace _build_party_axis_figure to accept only explicit party_coords for comp_sel 1 & 2. - Remove old polymorphic/legacy path; callers must pass party_coords or raise a clear error. - Update rendering glue to call _build_party_axis_figure with explicit party_coords. - Ensure hover text shows fallback notes for parties where fallback_used contains the party. - Update/clean Streamlit caption behavior when no coords available. - Tests: modify tests/test_explorer_chart.py to supply party_coords shape and assert behavior. - Estimate: 4.5h 3) Update all callers across repo to pass explicit party_coords - Grep for places that previously passed party vectors into _build_party_axis_figure or used load_party_axis_scores for compass rendering. - Update each call site to compute party_coords via compute_party_coords, passing the fallback_party_scores (first-chronological vector) when needed. - Caller list (non-exhaustive — verify with repo search): - explorer.build_svd_components_tab - explorer._render_party_axis_chart (if present) - any scripts or tests that directly call _build_party_axis_figure - Update tests referencing legacy vector shape. - Estimate: 3.0h 4) Add integration consistency test - tests/test_compass_trajectory_consistency.py — synthetic positions_by_window and party_map to assert compute_party_coords equals centroid computations used by trajectories. - Estimate: 1.0h 5) Run full test suite and fix regressions - Run pytest; address failures introduced by strict API change. - If other modules relied on old shape in ways not covered by tests, update them to use compute_party_coords. - Estimate: 1.5h 6) Manual QA - Run streamlit run explorer.py and visually verify compass tooltips and trajectories hover values match (comps 1 & 2) for several parties and windows. - Verify fallback tooltip and logger WARN when a party uses fallback vector. - Estimate: 1.0h 7) Commit and push (or open PR) with description: "feat(explorer): use explicit per-party (x,y) coords from positions_by_window for compass (components 1 & 2); update callers and add tests" - Estimate: 0.5h ## Verification commands - Unit tests: - python -m pytest tests/test_explorer_helpers.py - python -m pytest tests/test_explorer_chart.py - python -m pytest tests/test_compass_trajectory_consistency.py - Full test suite: - python -m pytest - Manual UI: - streamlit run explorer.py ## Rollback and mitigation - If the strict API uncovers many call sites, revert to a temporary feature branch, document call sites, and migrate them in smaller patches. - Keep commits small and self-contained to ease review. ## Notes - This plan follows the user's instruction to update all callers and to use the first chronological party vector as fallback. - The helper is pure Python to keep tests simple; callers may cache if needed.