You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
motief/docs/superpowers/specs/2026-04-05-svd-axis-labels-...

4.6 KiB

SVD Axis Labels: Enforce Canonical Left/Right Orientation

Problem

SVD axis labels do not consistently reflect the repository's political convention: right-wing parties (PVV, FVD, JA21, SGP) must appear on the RIGHT side of all axes in visualizations. Current code uses inconsistent inline party sets across files (svd_labels.py has 9 right parties, political_axis.py has 6), and no automated validation exists to enforce the convention.

Goals

  • Guarantee that canonical right-wing parties (PVV, FVD, JA21, SGP) appear on the RIGHT side of all SVD axes.
  • Centralize canonical party sets in a single source of truth.
  • Automatically flip axis orientation when canonical parties appear on the wrong side.
  • Add unit tests and CI validation to prevent regressions.
  • Maintain backward compatibility for existing callers.

Architecture

New Module: analysis/config.py

"""Canonical political party sets for axis orientation validation."""

CANONICAL_RIGHT = frozenset({"PVV", "FVD", "JA21", "SGP"})
CANONICAL_LEFT = frozenset({
    "SP", "PvdA", "GL", "GroenLinks", "GroenLinks-PvdA",
    "DENK", "PvdD", "Volt"
})

Updated Module: analysis/svd_labels.py

  • Import canonical sets from analysis.config.
  • Replace inline RIGHT_PARTIES / LEFT_PARTIES with config values.
  • Export backward-compat aliases:
    from analysis.config import CANONICAL_RIGHT, CANONICAL_LEFT
    RIGHT_PARTIES = CANONICAL_RIGHT
    LEFT_PARTIES = CANONICAL_LEFT
    
  • Update compute_flip_direction() to use CANONICAL_RIGHT for flip decisions.

Data Flow

  1. Compute raw component scores/centroids (existing logic).
  2. Project canonical right party vectors onto the component.
  3. Determine where canonical right parties lie along the component axis.
  4. If majority of CANONICAL_RIGHT items are on the left side, set flip = True.
  5. When flip is True, swap/negate the axis and flip label semantics before returning to callers.
  6. Numerical PCs remain intact; only orientation + labels change at presentation boundary.

Error Handling & Fallbacks

  • No canonical parties present: Log warning, fall back to existing behavior (no flip), optionally surface UI notice.
  • Ambiguous placement: Use majority rule with deterministic tie-breaking (tie → do not flip, log warning).
  • Backward compatibility: Preserve public functions (get_svd_label, get_fallback_labels, compute_flip_direction); only orientation may flip.

Testing Strategy

Unit Tests (tests/test_svd_labels.py)

  • Synthetic flip test: Create synthetic vectors where canonical right parties have negative component values; assert compute_flip_direction returns True and labels map correctly.
  • Synthetic no-flip test: Canonical right parties on positive side; assert compute_flip_direction returns False.
  • Real window test: Load a representative window and assert every item from CANONICAL_RIGHT appears on the right side after flip logic.
  • Backward compat test: Existing tests in test_svd_labels.py continue to pass after import swap.

CI Validation

  • Add pytest test group enforcing the rule:
    pytest -q tests/test_svd_labels.py::test_canonical_right_on_right
    
  • Test loads at least one representative window and asserts PVV/FVD/JA21/SGP appear on the right side of the principal political axis.
  • Prevents regressions in PR pipeline.

Migration Plan

  1. Create analysis/config.py with canonical sets.
  2. Update analysis/svd_labels.py imports and export aliases.
  3. Update compute_flip_direction to use canonical sets.
  4. Add/update unit tests in tests/test_svd_labels.py.
  5. Run full test suite to verify backward compatibility.
  6. Commit changes locally (no push until user approves).

Out of Scope (Follow-up)

  • analysis/political_axis.py currently uses different party sets for PCA centroid orientation. Recommended follow-up: align with config.py in separate PR.
  • UI notice for missing canonical parties (non-blocking enhancement).

Success Criteria

  • CANONICAL_RIGHT and CANONICAL_LEFT defined in analysis/config.py.
  • svd_labels.py imports from config; exports aliases for backward compat.
  • All existing tests pass after changes.
  • New test test_canonical_right_on_right passes.
  • Running pytest tests/test_svd_labels.py -q shows no regressions.
  • Right-wing parties (PVV, FVD, JA21, SGP) consistently appear on the RIGHT side of all axes in visualizations.
  • analysis/config.py (new)
  • analysis/svd_labels.py (update)
  • tests/test_svd_labels.py (update)
  • docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md (existing convention doc)
  • AGENTS.md (convention reference)