docs: add improvement roadmap, research notes, and solution docs
- Add 2026-04-24 ROADMAP with 5 phases / 17 items - Add detailed implementation plans for P1-001 through P4-005 - Add research artifacts and solution docs from ledger merge - Add test for SVD component 1 compass alignment
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
---
|
||||
module: svd
|
||||
date: 2026-04-16
|
||||
category: docs/solutions/logic-errors
|
||||
problem_type: logic_error
|
||||
component: rails_view
|
||||
severity: medium
|
||||
symptoms:
|
||||
- "SVD tab displayed VVD component 1 score of 0.108"
|
||||
- "Compass displayed VVD component 1 score of 0.335"
|
||||
- "Significant numerical discrepancy between two views showing same data"
|
||||
root_cause: scope_issue
|
||||
resolution_type: code_fix
|
||||
tags:
|
||||
- svd
|
||||
- voting-analysis
|
||||
- filter-scope
|
||||
- party-scores
|
||||
---
|
||||
|
||||
# SVD Tab Party Scores Don't Match Compass
|
||||
|
||||
## Problem
|
||||
|
||||
The SVD tab displayed VVD component 1 score as 0.108 while the compass visualization showed 0.335 — a 3x discrepancy caused by including inactive/historical MPs in the SVD calculation.
|
||||
|
||||
## Symptoms
|
||||
|
||||
- **SVD tab**: VVD comp1 score = 0.108 (incorrect)
|
||||
- **Compass**: VVD comp1 score = 0.335 (correct, verified against compass reference)
|
||||
- **Discrepancy**: ~3x difference between views
|
||||
- **Scope**: Affects all parties but most visible for VVD (82 historical MPs vs ~50 active)
|
||||
|
||||
## What Didn't Work
|
||||
|
||||
N/A — straightforward fix once root cause identified through comparison with compass implementation at `explorer.py:1473`.
|
||||
|
||||
## Solution
|
||||
|
||||
The `_get_aligned_party_scores()` function in `views/svd.py` was missing an `active_MP` filter when calculating party means for the current parliament window.
|
||||
|
||||
**Before (buggy code):**
|
||||
|
||||
```python
|
||||
def _get_aligned_party_scores(party_id: str, dimension: str, ...) -> list:
|
||||
raw_scores = execute_query(score_query, ...)
|
||||
# Missing: no active_MP filter
|
||||
return raw_scores
|
||||
```
|
||||
|
||||
**After (fixed code):**
|
||||
|
||||
```python
|
||||
def get_aligned_party_scores(party_id: str, dimension: str, ...) -> list:
|
||||
raw_scores = execute_query(score_query, ...)
|
||||
|
||||
# Filter to only active MPs (matches compass behavior at explorer.py:1473)
|
||||
active_mps = {m[0] for m in active_mp_query if m[0] is not None}
|
||||
scores = [s for s in raw_scores if s[0] in active_mps]
|
||||
|
||||
return scores
|
||||
```
|
||||
|
||||
Key changes:
|
||||
1. Extracted function to module-level for testability
|
||||
2. Added active MP filtering using the same query pattern as compass (`explorer.py:1473`)
|
||||
3. Filter ensures only MPs in current parliament window are included
|
||||
|
||||
**Verification:**
|
||||
- Without filter: VVD comp1 = 0.1083
|
||||
- With filter: VVD comp1 = 0.3366 (matches compass reference of 0.3350)
|
||||
- Test suite: 169/169 tests passing
|
||||
|
||||
## Why This Works
|
||||
|
||||
The root cause was including all 82 historical VVD MPs instead of only the active ones. The database (`data/motions.db`) contains MPs from multiple parliaments, and the `_get_aligned_party_scores()` function wasn't filtering by `active_MP`. The compass correctly applied this filter, explaining the discrepancy.
|
||||
|
||||
## Prevention
|
||||
|
||||
1. **Test suite**: Comprehensive tests in `tests/svd_test.py` covering alignment calculations with active MP filtering
|
||||
2. **Cross-view validation**: Compare SVD and compass scores for each party — assert values match within tolerance
|
||||
3. **Query pattern documentation**: All score queries must include `active_MP` filter when calculating party means
|
||||
4. **Code review checklist**: Require active_MP filter for any new score calculation queries
|
||||
5. **Automated regression**: Add CI check that runs comparison between SVD tab and compass for all parties
|
||||
|
||||
## Related Issues
|
||||
|
||||
- `docs/solutions/logic-errors/svd-theme-divergence-from-party-positions.md` — Related domain issue: SVD scores not matching actual party positions
|
||||
- `docs/solutions/logic-errors/svd-component-labels-mismatch.md` — Related theme: Labels/data alignment mismatches
|
||||
- `docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md` — Core principle: SVD captures voting patterns, verify against actual voting data
|
||||
Reference in New Issue
Block a user