--- title: feat: Overton window coherent narrative architecture type: feat status: active date: 2026-06-06 --- # feat: Overton Window Coherent Narrative Architecture ## Summary The Overton window analysis is Stemwijzer's most ambitious analytical output — a multi-indicator answer to "Has the Dutch Overton window shifted?" built on top of the platform's SVD compass, voting records, and 2D extremity scoring. But it landed as 17 fragmented reports with no narrative spine, no connection to the live Explorer dashboards that visualize the same dynamics, and stale public-facing artifacts. This plan weaves the Overton findings into a coherent story (Quarto article + cleaned reports + Explorer integration) while positioning it as a showcase for what the Stemwijzer platform can do — not as a standalone project. --- ## Problem Frame Stemwijzer is a Dutch parliamentary analysis platform with three tracks: data pipeline reliability, analytical depth, and agent-native architecture (see `STRATEGY.md`). The Overton window analysis is a flagship deliverable of track 2 — it demonstrates the platform's SVD compass, voting data, and LLM scoring capabilities in service of a real political science question. But the Overton output landed fragmented: 17 files across `reports/overton_window/` with no reading order, no cross-references, and no connection to the live Explorer dashboards (Kompas, Trajectories, SVD Components) that directly visualize the same dynamics. The blog post uses stale 1D data and the wrong centrist definition. The analysis scripts work but there's no single entry point for a reader or a user. This plan organizes the Overton findings into a coherent multi-surface narrative while ensuring it serves the broader platform — the Explorer integration and Quarto article should make users want to explore the Stemwijzer compass, not just read about Overton findings. --- ## Requirements - R1. A single Quarto article (`overton_window.qmd`) serves as the narrative spine — telling the story from question ("Has the Overton window shifted?") to answer ("Acceptance through moderation"), with embedded interactive Plotly charts - R2. All public-facing outputs use the strict 4-party centrist definition (D66, CDA, CU, NSC) - R3. The Overton narrative drives traffic TO the live Stemwijzer Explorer — readers should finish the article wanting to explore the compass themselves. The 3 live Explorer dashboards connect to the Overton narrative through explanatory text and a dedicated Overton tab - R4. Stale/drifted reports are removed or explicitly archived - R5. All remaining reports cross-reference each other consistently - R6. A `build_all_reports.py` script regenerates all outputs in dependency order - R7. The blog post is replaced with a current-data version - R8. The Overton narrative showcases Stemwijzer's platform capabilities (SVD compass, voting data, 2D scoring) — it should read as both a political science finding AND a demonstration of what the tool can do --- ## Scope Boundaries - No new analytical findings — this is about organization, narrative, and presentation - No backend infrastructure changes (the Streamlit app already works) - No European comparative analysis (deferred) - No mechanism taxonomy revision (deferred) - No forward-looking scenario analysis (deferred) - Install Quarto CLI as a new tool dependency ### Deferred to Follow-Up Work - European comparison (AfD, Meloni, Le Pen, Sweden Democrats) - Mechanism taxonomy revision (κ=0.41) - Forward-looking scenario analysis (permanent vs temporary shift) - Anti-institutional pivot deep-dive (abolition → contestation) --- ## Context & Research ### Relevant Code and Patterns - `analysis/right_wing/` — 19+ analysis scripts that generate the reports - `analysis/explorer_data.py` — data layer feeding the Streamlit Explorer - `analysis/tabs/` — Streamlit tab modules (compass, trajectories, components, browser, search) - `explorer.py` — Explorer orchestration, currently registers 3 tabs - `reports/overton_window/` — 17 output files (14 MD, 2 HTML, 1 synthesis) - `.opencode/skills/score-extremity/SKILL.md` — 2D scoring methodology ### Institutional Learnings - `docs/solutions/best-practices/overton-window-shift-methodology-2026-05-24.md` — 7-step methodology - `docs/solutions/best-practices/overton-narrative-architecture-2026-06-06.md` — narrative structure guidance (just created) - `docs/solutions/best-practices/domain-decomposition-hidden-overton-variance-2026-05-25.md` - `AGENTS.md` — strict 4-party centrist definition, SVD sign convention, right-wing on RIGHT ### External References - Quarto: `quarto.org/docs/get-started/` — standalone CLI, Jupyter engine for Python/Plotly - Plotly 6.6.0 already installed --- ## Key Technical Decisions - **Quarto Jupyter engine** over static HTML: Interactive Plotly charts survive in the output, readers can hover/zoom/filter. Same dependency (plotly) already in pyproject.toml. - **Strict 4-party centrist definition** enforced across all public outputs: D66, CDA, CU, NSC only. The 6-party definition (adding VVD, BBB) survives only in the breakpoint_analysis.md appendix for comparison. - **Three-tier output structure**: Narrative spine (Quarto) → Detailed appendices (Markdown in reports/overton_window/) → Live exploration (Streamlit Explorer tab) - **Remove, don't accumulate**: findings_report.md removed. blog_post.html replaced. Duplicate section content between reports consolidated. - **Master build script** as single-source-of-truth for reproducibility: `analysis/right_wing/build_all_reports.py` runs scripts in dependency order. --- ## Implementation Units - U1. **Clean up stale and drifted reports** **Goal:** Remove superseded artifacts, fix inconsistent content, archive early-draft reports. **Requirements:** R4, R5 **Dependencies:** None **Files:** - Remove: `reports/overton_window/findings_report.md` - Remove: `reports/overton_window/blog_post.html` - Modify: `reports/overton_window/overton_window_synthesis.md` (fix hashline formatting corruption at top) - Modify: `reports/overton_window/breakpoint_analysis.md` (add note at top linking to synthesis as primary narrative) - Modify: `reports/overton_window/overton_report.html` (switch to strict 4-party centrist definition from current 6-party) **Approach:** - Remove findings_report.md — fully superseded by synthesis - Remove blog_post.html — will be recreated as Quarto output (U3) - Fix hashline corruption in synthesis (duplicate `#HL` header lines) - Add cross-reference header to each remaining report: "See also: [overton_window_synthesis.md](...)" with one-sentence relationship - Switch overton_report.html centrist definition from 6-party (VVD/D66/CDA/NSC/BBB/CU) to strict 4-party (D66/CDA/CU/NSC) **Test expectation:** none — editorial/content changes, no behavioral change **Verification:** - findings_report.md and blog_post.html removed - synthesis hashline headers cleaned up - All remaining reports contain cross-reference to synthesis - overton_report.html uses 4-party centrist numbers --- - U2. **Create the Quarto narrative spine** **Goal:** Write `reports/overton_window/overton_window.qmd` — a single self-contained article with embedded interactive Plotly charts that tells the Overton story from question to answer, while showcasing Stemwijzer's platform capabilities. **Requirements:** R1, R2, R8 **Dependencies:** U1 (cleanup), external prerequisite: Quarto CLI installed **Files:** - Create: `reports/overton_window/overton_window.qmd` - Create: `reports/overton_window/_quarto.yml` (project config) - Modify: `pyproject.toml` (add quarto render script if needed) **Approach:** - 9-section narrative arc: 1. **Introduction** — The question, why it matters, Dutch political context (PVV election 2023) 2. **About Stemwijzer** — Brief platform introduction: what it is (data-driven political compass from real voting records), how it works (SVD on 29K+ motions), what readers can do with it. This positions the article as both a finding and a platform demo. 3. **Methodology** — Right-wing motion classification, 2D extremity scoring, strict centrist definition, data sources 4. **Indicator 1: Centrist Voting** — Breakpoint at 2024, opposition-controlled, gravity-stratified 5. **Indicator 2: Spatial Divergence** — SVD compass drift, acceptance without conversion 6. **Indicator 3: Content Moderation** — 2D extremity trajectories, all-motion comparison 7. **Mechanisms** — Consensus framing, institutional appeals, JA21 as driver 8. **Temporal Dynamics** — Electoral jump, 2024-Q4 peak, 2026 reversion signal 9. **Verdict: Acceptance Through Moderation** — What it means, limitations, open questions, call-to-action to explore the live compass - Embedded Plotly charts (not static PNGs): - Yearly centrist_support_strict with CI bands + opposition-only overlay - Gravity-controlled bar chart (M1-M5 centrist support) - SVD trajectory plot (centrist vs right-wing center) - 2D extremity temporal with all-motion reference lines - Mechanism classification bar chart - Quarterly temporal trajectory - Use `plotly.graph_objects` for chart construction (consistent with existing analysis scripts) - YAML header with `jupyter: python3` engine, `embed-resources: true` - Reference back to Explorer dashboard: "Explore this data live at [localhost:8501](http://localhost:8501), Explorer > Kompas" **Execution note:** Write the QMD content before setting up Quarto — the charts can initially be embedded as static PNGs and upgraded to interactive Plotly in a second pass. **Test expectation:** none — content/documentation **Verification:** - `quarto render overton_window.qmd` produces valid HTML - All 9 sections present with embedded charts - Section 2 introduces Stemwijzer as a platform (not just the Overton analysis) - Uses strict 4-party centrist definition throughout - References the live Explorer dashboard - References the detailed appendices for methodology deep-dives - Final section includes a call-to-action to explore the Stemwijzer compass --- - U3. **Wire Explorer with an Overton context panel** **Goal:** Add explanatory Overton context to the existing Explorer tabs so readers of the narrative can drill into the live data, AND ensure the Overton tab drives engagement with the broader Stemwijzer platform (compass quiz, SVD exploration). **Requirements:** R3, R8 **Dependencies:** U2 **Files:** - Modify: `analysis/tabs/compass.py` (add Overton context expander in the sidebar or below the chart) - Modify: `analysis/tabs/trajectories.py` (add Overton annotation showing 2024 breakpoint) - Create: `analysis/tabs/overton.py` (new tab module — motion browser filtered to right-wing, centrist support trends) - Modify: `analysis/tabs/__init__.py` (register overton tab) - Modify: `explorer.py` (wire the tab into `run_app()`) **Approach:** - Add a collapsible "Overton Window Context" expander to the Kompas tab sidebar explaining what the axes show relative to the Overton analysis, with a link to the Quarto narrative. Include a "Try the Stemwijzer quiz" call-to-action linking to the quiz page. - In the Trajectories tab, add a vertical reference line at 2024 with an annotation referencing the breakpoint finding - Create a lightweight "Overton" tab that shows: - Yearly centrist_support_strict trend line (from right_wing_motions) - Right-wing motion count by year - Filterable right-wing motion browser (reusing `browser.py` with a WHERE classified=TRUE filter) - Summary statistics matching the narrative - "Explore further" section linking to Kompas (see party positions), Trajectories (see drift), and SVD Components (see which motions drive the axes) - The Overton tab should make users curious about the underlying data — not present a closed story but an open exploration **Test scenarios:** - Happy path: Opening Explorer > Overton tab loads centrist support chart and motion browser - Happy path: Kompas tab shows the Overton context expander - Happy path: Trajectories tab shows the 2024 breakpoint annotation - Edge case: No right-wing motions in database — empty state message **Verification:** - `streamlit run Home.py` shows 4 Explorer tabs (Kompas, Trajectories, SVD Components, Overton) - Overton tab shows centrist_support_strict trend line - Kompas sidebar has Overton context expander - Trajectories tab has 2024 reference line --- - U4. **Build master report regeneration script** **Goal:** Single script that regenerates all Overton reports in correct dependency order. **Requirements:** R6 **Dependencies:** U1 (cleanup ensures scripts are consistent) **Files:** - Create: `analysis/right_wing/build_all_reports.py` **Approach:** - Phase 1: Database-dependent scripts (no LLM calls): - `overton_breakpoint_analysis.py` - `temporal_trajectory.py` - `causal_timing.py` - `party_differentiation.py` - `voting_margin.py` - `left_wing_response.py` - `success_correlation.py` - `overton_svd_drift.py` - `svd_trajectory_viz.py` - Phase 2: 2D extremity-dependent scripts (no LLM calls): - `extremity_2d_temporal.py` - `predictive_model.py` - `mechanism_classification.py` - Phase 3: LLM-dependent scripts (optional, skip with --skip-llm): - `derive_categories.py` - `mechanism_classification.py` (LLM classification pass) - Phase 4: Synthesis updates (manual, prints reminder) - CLI: `uv run python analysis/right_wing/build_all_reports.py [--skip-llm]` - Runs each script via `subprocess.run`, checks exit code, logs output paths - Verifies all expected output files exist after each phase **Test scenarios:** - Happy path: Running with skip-llm regenerates all DB-dependent reports - Happy path: All expected output files exist after completion - Error path: A sub-script fails — build_all_reports reports which script failed and its stderr **Verification:** - `uv run python analysis/right_wing/build_all_reports.py --skip-llm` exits 0 - All reports in `reports/overton_window/` have fresh timestamps --- - U5. **Document and cross-reference everything** **Goal:** Update README, add reading guide, final compound. Position the Overton analysis within the broader Stemwijzer platform. **Requirements:** R5 **Dependencies:** U1, U2, U3 **Files:** - Modify: `README.md` - Modify: `AGENTS.md` - Create: `reports/overton_window/README.md` (reading guide) **Approach:** - README: Add Quarto article link under a new "Research" section. Reorganize Documentation section to list Overton reports in reading order. Ensure the Overton work is presented as one of Stemwijzer's analytical outputs, not the project's sole purpose. The README should still lead with the platform (voting compass, explorer) and present Overton as a showcase of what the data enables. - AGENTS.md: No changes needed (already has Overton conventions) — verify - Create `reports/overton_window/README.md` as the directory-level reading guide: - First: `overton_window.qmd` (narrative spine) - Then: `overton_window_synthesis.md` (detailed synthesis) - Then: individual appendices with one-sentence descriptions - Also: "Explore live at: Streamlit Explorer > Overton tab" - Mark deprecated: "Historical artifacts: blog_post.html (replaced by Quarto), findings_report.md (removed)" **Test expectation:** none — documentation changes **Verification:** - `reports/overton_window/README.md` lists all reports in reading order - README.md references the Quarto article and reading guide --- ## System-Wide Impact - **Interaction graph:** Streamlit Explorer (`explorer.py`, `analysis/tabs/__init__.py`) gains a new tab module. Existing compass/trajectories tabs get minor additions (expander, annotation). No changes to data pipeline, database, or API client. - **Unchanged invariants:** All existing analysis scripts and tests continue to work. The Streamlit app's existing 3 tabs remain functional and unbroken. The Stemwijzer quiz page is untouched. - **Platform alignment (STRATEGY.md):** - Track 1 (Pipeline reliability): U4 master build script improves reproducibility. No pipeline changes. - Track 2 (Analytical depth): This plan IS the track 2 showcase — organizing the deepest analysis the platform has produced into a coherent, explorable narrative. - Track 3 (Agent-native): The Overton tab uses `agent_tools`-compatible data tables. The `build_all_reports.py` script makes the analysis reproducible by agents. --- ## Documentation / Operational Notes - The Quarto article should be the primary public-facing artifact. It replaces `blog_post.html` and `findings_report.md`. - The Overton analysis is positioned as a Stemwijzer platform showcase — readers should finish the article understanding both the political finding AND what the tool can do. - The live Explorer is the "next step" for engaged readers — the Quarto article and Overton tab both link to it. --- ## Risks & Dependencies | Risk | Mitigation | |------|------------| | Quarto CLI not available — user needs to install | Check availability early; if not, static HTML with embedded Plotly as fallback | | Explorer tab slows down Streamlit load | Lightweight tab — only queries right_wing_motions and extremity_scores_2d, no SVD computation | | Blog post Regeneration diverges from Quarto narrative | Make Quarto the single source — blog post is rendered from same QMD with different styling | --- ## Sources & References - **Gap analysis:** `docs/solutions/best-practices/overton-narrative-architecture-2026-06-06.md` - **Methodology:** `docs/solutions/best-practices/overton-window-shift-methodology-2026-05-24.md` - **Synthesis report:** `reports/overton_window/overton_window_synthesis.md` - **HTML report:** `reports/overton_window/overton_report.html` - **Quarto docs:** https://quarto.org/docs/get-started/