docs: add SVD axis labels design spec

main
Sven Geboers 4 weeks ago
parent ea6e33fa3f
commit 0a39fa0fe3
  1. 103
      docs/superpowers/specs/2026-04-05-svd-axis-labels-design.md

@ -0,0 +1,103 @@
# 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`
```python
"""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:
```python
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:
```bash
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.
## Related Files
- `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)
Loading…
Cancel
Save