U1: JA21 drives moderation effect (+0.203 CS shift, only party with volume+support gains)
U2: Coalition coding split at July 2024 — opposition effect confirmed (d=0.85 vs 0.87)
U3: Voting margin (ρ=0.812 with centrist support) is far superior to pass rate
U4: SVD trajectory confirms spatial divergence — centrists moved left (Δx=-0.30), right stationary
U5: Mechanism classification Cohen's κ=0.41 (moderate) — taxonomy needs revision
U6: Predictive model AUC-ROC=0.81 — submitter party and category are strongest predictors
Material impact declined post-2024 (2.78→2.43, M>=4 share 23.7%→11.3%).
Right-wing strategic moderation: more motions, milder content, better
framing. The Overton window did not expand — right-wing proposals
shifted into the existing window. 'Acceptance through moderation'
replaces 'acceptance without conversion.'
SVD axes capture agreement structure — centrists 'moving left' means
voting patterns diverged from right-wing, not that parties changed
ideology. 'Acceptance without conversion' is a behavioral claim.
Documented as best-practice learning.
- Classified 24 post-2024 right-wing motions with CS>=0.5
- Dominant mechanisms: consensus framing (33%), institutional (21%), welfare (17%)
- Only 1 targeted restriction, zero system dismantling
- Right-wing gains centrist support through repackaging, not conversion
- Confirms acceptance-without-conversion dynamic at the mechanism level
- Pearson r=0.45 between stylistic and material impact (separable)
- Material impact averages 0.85 points above stylistic
- 36.8% of motions mask high-impact policy behind restrained language
- Original single-score conflates language vs substance
- Mark U4 mechanism analysis as in progress
- Project-local skill .opencode/skills/score-extremity/ for subagent dispatch
- Orchestrator extremity_rescore_2d.py with load_skill/sample/format/validate/store
- 16 TDD tests covering all orchestrator functions
- 117 motions scored by deepseek v4 flash subagents (12 parallel batches)
- Pearson r=0.45 between stylistic and material dimensions — separable
- Key finding: 36.8% of motions use restrained language for consequential policies
- 2d_extremity_correlation_report.md documents distribution, divergence patterns,
and implications for the Overton acceptance-without-conversion narrative
Captures the 7-step methodology developed through multiple analysis
iterations: strict centrist definition, Procrustes-aligned SVD with
anchor-party sign validation, centrist support fraction over pass rate,
acceptance-without-conversion test, opposition-only filter as coalition
control, extremity-stratified tolerance analysis, and LLM score auditing.
- Reclassified centrist to {D66, CDA, CU, NSC} — removing VVD/BBB
which are center-right coalition partners
- Added centrist_support_strict (0.251→0.507, d=+0.65), center_right_support,
and left_support_mp columns via migration script
- Figure 1 now shows center-right (VVD/BBB) support as orange dashed line
- New Figure 3: bar chart of left-party support for right-wing motions
(0.268→0.202, left opposition hardened)
- New report Section 6 covering left-wing support trends
- All analysis now uses strict centrist definition throughout
Extremity Scorer (U4 enhanced):
- Now scores BOTH original motion text AND layman explanation separately
- Schema: text_score, text_explanation, layman_score, layman_explanation
- Text scores: 1→7, 2→33, 3→5, 4→5 (mild-to-moderate)
- Layman scores: 1→12, 2→20, 3→17, 4→1 (slightly milder)
Sentiment Analysis (U5 enhanced):
- Now scores BOTH original motion text AND layman explanation separately
- Schema: text_score, text_explanation, layman_score, layman_explanation
- Text sentiment avg: 0.294 (slightly positive)
- Layman sentiment avg: 0.416 (more positive - summaries tone down hostility)
Category Derivation (new):
- Two-phase LLM approach: derive taxonomy from sample, then apply to all
- Discovered 7 categories from 30-motion sample:
veiligheid/justitie, corona/pandemie, economie/belasting, klimaat/milieu,
defensie/buitenland, asiel/vreemdelingen, overig
- Applied to 50 motions with distribution shown in DB
- Adds category + category_explanation columns to right_wing_motions
Implements U5: sentiment_analysis.py uses LLM batch calls (fallback when no
local Dutch sentiment model is available) to score motion sentiment on [-1, 1]
scale.
Design:
- Prompt asks for sentiment from -1 (hostile/aggressive) to 1 (constructive)
- JSON schema enforces numeric score + Dutch explanation
- Batch size 10, max_workers 5 for parallel API calls
- Stores results in table
- Updates with avg_sentiment, sentiment_std,
pct_strongly_negative per year
Sample validation (50 motions): good variance across [-0.9, 1.0] range.
Implements U3: temporal_analysis.py computes yearly_summary from the
right_wing_motions table (U2 output).
Metrics per year:
- total_right_wing, pct_of_total, total_motions
- avg_right_support, avg_left_opposition, centrist_support
- avg_right_keyword_matches, extremity_index (U4 placeholder)
- yoy_right_wing_delta, yoy_pct_delta
Key finding: right-wing motions grew from ~4% (2018) to ~12% (2024-2025)
of all motions, with rising centrist support over time.
Removes embedding-heavy search and generic browser tabs from the Explorer.
The project does not currently use embeddings meaningfully, so the similarity
search and browser features were dead weight.
Changes:
- explorer.py: Remove search and browser tabs, keep compass/trajectories/SVD
- explorer.py: Remove fused_embeddings and similarity_cache stats from sidebar
- Home.py: Update Explorer description to match new focused layout
- analysis/tabs/__init__.py: Remove search and browser exports
- tests: Update decomposition and import tests for new tab set
Result: Explorer now has 3 focused analytical tabs instead of 5.
- compute_svd_for_window now computes explained variance ratio (s²/sum(s²))
and appends it as a metadata row (entity_type='metadata',
entity_id='explained_variance') to motion_rows
- load_scree_data reads this metadata row from svd_vectors instead of
querying the non-existent sv_metadata column
- run_svd_for_window counts only entity_type='motion' rows in stored_motion
so metadata rows don't inflate the count
- Added 5 TDD tests covering load, compute, store, and round-trip
All 227 tests pass.
- load_scree_data: return [] with TODO until schema stores EVR metadata
- load_party_axis_scores: compute from vectors instead of missing table
- load_party_axis_scores_for_window: same vector-based fallback
- load_party_scores_all_windows[_aligned]: check table existence,
fall back to computing from load_positions when absent
All functions predated decomposition (5afbad1, 2026-04-05) and relied on
party_axis_scores / sv_metadata columns that were never created.
- Add verify-lint-rule-scope-before-relying-on-it: guidance on
confirming lint rule coverage before trusting it for enforcement.
Documents the P2-002 incident where ruff BLE only catches bare
not .
- Update working-tree-hygiene: add dev-tool-in-venv check and
ruff dependency example.
- Add pytest-benchmark to dev dependencies
- Benchmark SVD decomposition on synthetic vote matrix
- Benchmark cosine similarity at small/medium/large scales
P5-003: Benchmark suite
- Wrap duckdb.connect() in try/except with specific duckdb.Error
- Replace bare with in _init_database
- Replace broad with for ALTER TABLE
- Add ruff BLE (blind except) lint rule to prevent regressions
- Add tests verifying graceful error handling for connect, insert, query
P2-002: Fix broad exception handling
- 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
- Enable T20 (flake8-print) for all Python files
- Exclude scripts/, tools/, and .mindmodel/examples/ where
stdout output is acceptable
Completes U6 of P2-001 (replace print with logging)
Replace all print() calls with logger.info/logger.error using
lazy % formatting. Add test verifying error path emits ERROR log.
U2 of P2-001 (replace print with logging)
- Create logging_config.py with configure_logging() helper
- Add tests for level setting, format, idempotency, and inheritance
U1 of P2-001 (replace print with logging)