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/plans/2026-04-05-002-refactor-svd...

7.4 KiB

title type status date origin
Enforce left-right orientation across all SVD axis labels refactor active 2026-04-05 docs/superpowers/specs/2026-04-05-svd-axis-labels-design.md

Enforce Left-Right Orientation Across All SVD Axis Labels

Overview

Update SVD component labels in analysis/config.py so all 10 axes consistently reflect left-right positioning, and add validation tests to ensure canonical right-wing parties (PVV, FVD, JA21, SGP) appear on the right side after flip computation. The flip mechanism already works; this plan focuses on label consistency and test coverage.

Problem Frame

SVD axis labels do not consistently reflect left-right positioning. Some axes describe dimensions like "populist vs mainstream" or "pragmatism vs ideology" without framing how right/conservative and left/progressive parties cluster on each pole. The repo convention (AGENTS.md) requires right-wing parties to appear on the RIGHT side of all axes, and labels should reflect this orientation.

Requirements Trace

  • R1. All 10 SVD component labels consistently frame the dimension in left-right terms
  • R2. Canonical right-wing parties (PVV, FVD, JA21, SGP) appear on the right side after flip computation
  • R3. Backward compatibility preserved for existing callers of get_svd_label, get_fallback_labels, compute_flip_direction
  • R4. Unit tests validate flip behavior and label correctness

Scope Boundaries

  • In scope: analysis/config.py SVD_THEMES labels, tests/test_svd_labels.py additions
  • Out of scope: analysis/political_axis.py party sets (follow-up), UI changes, flip logic changes (already works)

Context & Research

Relevant Code and Patterns

  • analysis/config.py — defines SVD_THEMES with 10 components, each with label, explanation, positive_pole, negative_pole, flip
  • analysis/svd_labels.py — imports CANONICAL_RIGHT/CANONICAL_LEFT from config, exports aliases, compute_flip_direction uses them
  • explorer.py:2680-2690 — dynamically computes flip for all 10 components at runtime, overwrites static flip values

Key Technical Decisions

  • Keep flip mechanism as-is: compute_flip_direction already uses canonical party sets to force right-wing parties to the right. No changes needed.
  • Update labels, not flip logic: The work is in SVD_THEMES label text — reframing each component's label to reflect left-right positioning while preserving the underlying voting pattern description.
  • Preserve explanation text: The explanation field can remain detailed and nuanced; only the label and pole descriptions need left-right framing.

Implementation Units

  • Unit 1: Update SVD_THEMES labels for left-right consistency

Goal: Reframe all 10 SVD component labels to consistently reflect left-right positioning.

Requirements: R1, R3

Dependencies: None

Files:

  • Modify: analysis/config.py

Approach:

  • For each component (1-10), update the label field to frame the dimension in left-right terms
  • Update positive_pole and negative_pole to explicitly mention which parties cluster on each side and their left/right positioning
  • Preserve the explanation text (it's already detailed and accurate)
  • Keep flip values as-is (they're overwritten at runtime anyway)

Patterns to follow:

  • Component 1 label pattern: "Rechts kabinetsbeleid versus links oppositiebeleid" — this is the model
  • Component 3 label pattern: "Verzorgingsstaat versus bezuinigingen en marktwerking" — economic left-right
  • Component 6 label pattern: "Migratie en cultuur versus klimaat en progressieve inclusie" — cultural left-right (GAL-TAN)

Test scenarios:

  • Test expectation: none — this is a label text update, no behavioral change. Verification is manual review of label text.

Verification:

  • All 10 component labels explicitly reference left/right positioning or conservative/progressive framing

  • positive_pole and negative_pole descriptions mention party clusters and their left/right orientation

  • Unit 2: Add validation test for canonical right-on-right

Goal: Add a test that verifies canonical right-wing parties appear on the right side after flip computation.

Requirements: R2, R4

Dependencies: Unit 1 (labels updated, flip logic unchanged)

Files:

  • Modify: tests/test_svd_labels.py

Approach:

  • Add test_canonical_right_on_right that:
    1. Creates synthetic party scores where canonical right parties have negative values (on the left)
    2. Asserts compute_flip_direction returns True for all components 1-10
    3. Creates synthetic scores where canonical right parties have positive values (on the right)
    4. Asserts compute_flip_direction returns False for all components
  • Add test_all_canonical_parties_used that verifies CANONICAL_RIGHT and CANONICAL_LEFT from config contain the expected parties (PVV, FVD, JA21, SGP for right; SP, PvdA, GL, etc. for left)

Execution note: Test-first — write failing test, then verify it passes after Unit 1.

Patterns to follow:

  • Existing test style in tests/test_svd_labels.py (synthetic dict-based party scores, assert on boolean flip result)
  • test_auto_flip_computation_for_all_components already tests flip for all 10 components — new test should follow same pattern but explicitly use CANONICAL_RIGHT/CANONICAL_LEFT from config

Test scenarios:

  • Happy path: Canonical right parties on right side → compute_flip_direction returns False for all components
  • Happy path: Canonical right parties on left side → compute_flip_direction returns True for all components
  • Edge case: Mixed placement (some right parties on left, some on right) → flip based on majority mean
  • Edge case: No canonical parties present → returns False (existing behavior, verify unchanged)

Verification:

  • pytest tests/test_svd_labels.py -q passes with no regressions
  • New tests explicitly validate canonical right-on-right behavior

System-Wide Impact

  • Interaction graph: explorer.py dynamically computes flip at runtime — no changes needed there. Labels flow from config.pysvd_labels.py → UI rendering.
  • Unchanged invariants: compute_flip_direction logic unchanged. Public API (get_svd_label, get_fallback_labels, compute_flip_direction) unchanged. Static flip values in SVD_THEMES still overwritten at runtime.
  • API surface parity: Labels change text but not structure. Callers expecting string labels continue to work.

Risks & Dependencies

Risk Mitigation
Label changes may not capture nuance of non-left-right axes Preserve detailed explanation text; labels are shorthand, explanations carry full context
Tests may pass but labels still feel off Manual review of all 10 labels before committing
political_axis.py still uses different party sets Document as follow-up; out of scope for this plan

Documentation / Operational Notes

  • Update or reference docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md if label convention changes materially
  • No rollout or monitoring impacts — label text change only

Sources & References