diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..87b309d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,10 @@ +# Agents + +## Documented Solutions + +`docs/solutions/` — documented solutions to past problems (bugs, best practices, workflow patterns), organized by category with YAML frontmatter (`module`, `tags`, `problem_type`). Relevant when implementing or debugging in documented areas. + +## Project Conventions + +- Right-wing parties (PVV, FVD, JA21, SGP) must appear on the RIGHT side of all axes in visualizations +- SVD labels should reflect voting patterns, not semantic content — see `docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md` diff --git a/docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md b/docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md new file mode 100644 index 0000000..63dc0e6 --- /dev/null +++ b/docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md @@ -0,0 +1,140 @@ +--- +title: "SVD Labels Should Reflect Voting Patterns, Not Semantic Content" +date: "2026-04-04" +category: docs/solutions/best-practices +module: stemwijzer +problem_type: best_practice +component: brief_system +severity: medium +applies_when: + - Labeling SVD (Singular Value Decomposition) components in voting analysis + - Interpreting PCA/SVD dimensions in political party voting data + - Creating axis labels for voting compass or similar applications +tags: + - svd + - voting-analysis + - axis-labels + - dimensionality-reduction + - party-voting-patterns +--- + +# SVD Labels Should Reflect Voting Patterns, Not Semantic Content + +## Context + +When labeling SVD components in the Stemwijzer explorer (`explorer.py`), initial labels were based on **semantic analysis** of motion titles — what topics motions appeared to discuss. However, SVD captures **voting patterns**, not semantic content. + +This mismatch led to: +- Labels that didn't match how parties actually voted +- Right-wing parties appearing on the LEFT side of axes (violating the right-wing parties → right side constraint) +- Confusion about what each component actually represents + +## Guidance + +### The Core Principle + +**SVD components represent voting unity patterns, not topic clusters.** + +When a motion appears on a component with a positive loading, it means parties that vote positively on that motion tend to vote similarly. The component captures this behavioral pattern, not the topic's semantic meaning. + +### Example: Component 1 + +| Approach | Label | Why It's Wrong | +|----------|-------|----------------| +| Semantic | "Sociale zekerheid vs economische liberalisering" | Assumes defense + social care = welfare state | +| Voting | "Rechts kabinetsbeleid vs links oppositiebeleid" | Matches actual coalition vs opposition voting | + +**Why Component 1 captures coalition-opposition:** +- 9 coalition + center parties vote one way +- 6 opposition parties vote the other way +- Motion topics can include defense (right votes for) AND social care (left votes for) because they're on opposite sides of the coalition-opposition divide + +### How to Label SVD Components + +1. **Analyze actual voting patterns first** + - Query which parties vote positively vs negatively on each component + - Look for coalition/opposition splits, cross-block alliances, or isolated parties + +2. **Verify right-wing parties on RIGHT side** + - Check PVV, FVD, JA21, SGP positions + - If they vote negatively while left parties vote positively, flip the axis + +3. **Don't assume semantics match voting** + - A "defense" component may include social care motions if right-wing parties vote the same way on both + - Cross-block alliances (e.g., PVV with SP on welfare) create components that don't fit left-right semantics + +4. **Test with sample motions** + - Top positive-loading motions should align with positive-voting parties' priorities + - Top negative-loading motions should align with negative-voting parties' priorities + +## Why This Matters + +Without understanding that SVD captures voting patterns: +- Labels will be misleading to users +- Right-wing parties may appear on the wrong side of axes +- Components may be mislabeled as "left" when they're actually "opposition" +- Users get incorrect information about party positions + +## When to Apply + +Apply this guidance when: +- Creating or updating SVD/PCA component labels +- Interpreting dimensionality reduction results in voting analysis +- Building voting compasses or similar political guidance tools +- Analyzing roll call votes or legislative voting data + +## Examples + +### Wrong Approach (Semantic) + +```python +# ❌ BAD: Based on motion topics, not voting patterns +SVD_THEMES = { + 1: { + "label": "Sociale zekerheid vs economische liberalisering", + # Reality: 9 coalition parties vote same way, 6 opposition vote opposite + } +} +``` + +### Correct Approach (Voting Patterns) + +```python +# ✅ GOOD: Based on actual voting behavior +SVD_THEMES = { + 1: { + "label": "Rechts kabinetsbeleid vs links oppositiebeleid", + "explanation": ( + "Deze as scheidt het kabinetsbeleid van de oppositie. " + "9 coalitiepartijen stemmen aan de positieve kant, " + "6 oppositiepartijen aan de negatieve kant." + ), + } +} +``` + +### Verification Approach + +To verify SVD component labels, check which parties have positive vs negative loadings on each component: + +```python +# From explorer.py - check party loadings for each component +for comp_num in range(1, 11): + component_parties = svd_scores[svd_scores['component'] == comp_num] + positive_parties = component_parties[component_parties['loading'] > 0]['party'].tolist() + negative_parties = component_parties[component_parties['loading'] < 0]['party'].tolist() + + print(f"Component {comp_num}:") + print(f" Positive ({len(positive_parties)}): {positive_parties}") + print(f" Negative ({len(negative_parties)}): {negative_parties}") +``` + +Use this to verify: +- Right-wing parties (PVV, FVD, JA21, SGP) appear on the correct side +- The label matches the voting pattern, not just the topic + +## Related + +- [SVD Label Unification Plan](docs/superpowers/plans/2026-04-02-svd-label-unification.md) +- [SVD Label Unification Design](docs/superpowers/specs/2026-04-02-svd-label-unification-design.md) +- Commits: `33edb33`, `e77f0ec`, `bfe37c6`, `f7fc908`, `92c3c0e`