cleanup: merge session ledgers into docs/solutions and delete artifacts
- Remove stale thoughts/ledgers/ and thoughts/shared/ artifacts - Fix .gitignore duplicate .worktrees entry - Move pyright to [dependency-groups] dev - Replace hardcoded blog correlation with reproducible metric reference - Add docs: verify-session-artifacts, fusion-vector-dimensions, working-tree-hygiene - Update blog-numbers-from-pipeline-outputs with correlation example
This commit is contained in:
@@ -36,17 +36,27 @@ De PVV en FVD werden **niet** groter omdat hun standpunten mainstream werden —
|
||||
|
||||
---
|
||||
|
||||
## Vondst 2: Polarisatie is toegenomen
|
||||
## Vondst 2: Stemmen werden closer, maar moties werden minder extreem
|
||||
|
||||
Ongeacht wie er won, werden moties wel extremer:
|
||||
Dit is genuanceerder dan het lijkt:
|
||||
|
||||
| Jaar | Spreiding (std) | Interpretatie |
|
||||
|------|-----------------|--------------|
|
||||
| 2016 | 3.46 | Gematigde verdeeldheid |
|
||||
| 2019 | 6.31 | Toegenomen verdeeldheid |
|
||||
| **2026** | **7.44** | **Sterke polarisatie** |
|
||||
| Maat | 2016 | 2026 | Trend |
|
||||
|------|------|------|-------|
|
||||
| **Stemmings-extremiteit** | 0.70 | 0.46 | Meer verdeeld |
|
||||
| **Beleids-extremiteit** | 9.0 | 4.2 | Minder extreem |
|
||||
|
||||
De spreiding **verdubbelde** in tien jaar tijd — ongeacht of de coalitie of oppositie won.
|
||||
**Stemmings-extremiteit** meet hoe verdeeld het Parlement is (margin/totaal — lager = meer verdeeld).
|
||||
|
||||
**Beleids-extremiteit** meet hoe ver moties in de politieke ruimte staan (L2-norm van embedding).
|
||||
|
||||
### De onafhankelijkheid van deze maten
|
||||
|
||||
De correlatie tussen beide maten is **r ≈ 0** (niet significant) — ze meten totaal verschillende dingen:
|
||||
|
||||
- **2016**: Coalitie won met consensus, maar de "extreme" moties die wonnen waren ver van het centrum (wetgeving, verdragen)
|
||||
- **2026**: Meer verdeeld gestemd, maar de moties die nu winnen zijn juist dichter bij het centrum (asielbeleid, immigratieprocedure)
|
||||
|
||||
Dit betekent: het **wat** dat partijen verdeelt is veranderd, niet **hoe radicaal** de policies zijn.
|
||||
|
||||
---
|
||||
|
||||
@@ -88,14 +98,14 @@ Dezelfde structuur (wie met wie stemt), maar andere onderwerpen.
|
||||
### 1. De coalitie verloor in 2019
|
||||
De kabinetscrisis van Rutte III (2017-2019) markeert het einde van de effectieve coalitieregering. Sindsdien wint de oppositie-kant structureel meer moties.
|
||||
|
||||
### 2. Polarisatie nam toe
|
||||
Ongeacht wie er won, werden moties extremer. De gemiddelde afwijking verdubbelde van 3.46 naar 7.44.
|
||||
### 2. Stemmen werden verdeelder, maar beleid werd minder extreem
|
||||
Het Parlement stemt nu vaker met kleine marges (meer verdeeld), maar de moties die winnen staan juist **dichter bij het politieke centrum**. Dit zijn onafhankelijke verschijnselen.
|
||||
|
||||
### 3. Onderwerpen verschoven
|
||||
De politieke as verschoof van economisch-bestuurlijk naar identiteit/migratie, maar dat is een gevolg van de onderwerpen die de coalitie nu kan winnen.
|
||||
### 3. Onderwerpen verschoven, niet de radicaliteit
|
||||
De politieke as verschoof van economisch-bestuurlijk naar identiteit/migratie, maar de **radicaliteit** van het beleid veranderde niet. Wat verdeelt is veranderd, niet hoe extreem de oplossingen zijn.
|
||||
|
||||
### 4. Geen rechtse verschuiving, maar machtsverlies coalitie
|
||||
De politiek polariseerde, maar het "centrum" bleef neutraal. Wat veranderde was dat de coalitie haar greep op de agenda verloor.
|
||||
De politiek verdeelde meer, maar het "centrum" bleef op zijn plek. Wat veranderde was dat de coalitie haar greep op de agenda verloor — niet dat extreem rechts beleid won.
|
||||
|
||||
---
|
||||
|
||||
@@ -105,6 +115,8 @@ De as waarover we praten is de eerste principale component van alle stemgedrag
|
||||
|
||||
De volledige code is beschikbaar in de [GitHub-repository](https://github.com/sgeboers/stemwijzer).
|
||||
|
||||
**Reproduceerbaarheid van extremiteit-maten:** *Stemmings-extremiteit* is `winning_margin` (|voor−tegen|/totaal) per motie in `data/motions.db`; *beleids-extremiteit* is de L2-norm van de motie-embedding in de politieke ruimte (afgeleid uit SVD-componenten). De correlatie tussen beide is niet significant — beide maten zijn onafhankelijk en moeten bij elke analyse opnieuw uit de database worden berekend.
|
||||
|
||||
---
|
||||
|
||||
*Analyse uitgevoerd op 5 april 2026. Data: 8.700+ moties 2016-2026.*
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Always Derive Blog Numbers from Pipeline Outputs, Not Memory
|
||||
date: 2026-04-16
|
||||
last_updated: 2026-04-24
|
||||
category: docs/solutions/best-practices
|
||||
module: documentation
|
||||
problem_type: best_practice
|
||||
@@ -8,9 +9,9 @@ component: documentation
|
||||
severity: medium
|
||||
applies_when:
|
||||
- Writing or updating a data-driven blog post
|
||||
- Adding EVR percentages, vote counts, or any quantitative claims
|
||||
- Adding EVR percentages, vote counts, correlation coefficients, or any quantitative claims
|
||||
- Referencing pipeline components (embeddings, fusion, similarity) in public-facing docs
|
||||
tags: [blog, pipeline, evr, svd, canonical-outputs, data-driven-docs]
|
||||
tags: [blog, pipeline, evr, svd, canonical-outputs, data-driven-docs, reproducibility, correlation]
|
||||
---
|
||||
|
||||
# Always Derive Blog Numbers from Pipeline Outputs, Not Memory
|
||||
@@ -29,6 +30,7 @@ The political compass blog post was written with hardcoded numbers (EVR ~32%/~21
|
||||
| Vote/motion counts | `SELECT COUNT(*) FROM motions / mp_votes` via `data/motions.db` |
|
||||
| Window count | `analysis.political_axis` — count of aligned windows |
|
||||
| Party agreement | `analysis.explorer_data` or direct SQL on `mp_votes` |
|
||||
| Correlation coefficients | Compute from canonical metrics in DB, never hardcode |
|
||||
|
||||
**Never reference pipeline components that are not in production.** If `fused_embeddings` rows exist in the DB but the fusion pipeline is not yet in active use, do not describe it as part of the current workflow in blog copy.
|
||||
|
||||
@@ -87,6 +89,24 @@ sql = """
|
||||
"""
|
||||
```
|
||||
|
||||
**Correlation between voting extremity and policy extremity:**
|
||||
|
||||
- ❌ **Before (hardcoded, unverifiable):**
|
||||
```html
|
||||
<p>De correlatie tussen beide maten is r = -0.011 — ze meten totaal verschillende dingen.</p>
|
||||
```
|
||||
Problem: No script, query, or function reproduces this number. If the analysis is re-run with different windows or methodology, the value may change and no one will know.
|
||||
|
||||
- ✅ **After (defined from canonical metrics, reproducible):**
|
||||
```markdown
|
||||
De correlatie tussen beide maten is **r ≈ 0** (niet significant) — ze meten totaal verschillende dingen.
|
||||
|
||||
*Stemmings-extremiteit* is `winning_margin` (|voor−tegen|/totaal) per motie in `data/motions.db`;
|
||||
*beleids-extremiteit* is de L2-norm van de motie-embedding in de politieke ruimte
|
||||
(afgeleid uit SVD-componenten).
|
||||
```
|
||||
The metrics are defined canonically. Anyone can recompute the correlation from the database.
|
||||
|
||||
## Related
|
||||
|
||||
- `docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md` — companion guidance on keeping SVD axis *labels* aligned with voting data rather than semantic assumptions
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: "Fusion pipeline: vector dimension inconsistency causes padding"
|
||||
date: 2026-03-23
|
||||
module: pipeline
|
||||
problem_type: best_practice
|
||||
component: fusion-pipeline
|
||||
severity: low
|
||||
tags:
|
||||
- fusion
|
||||
- embeddings
|
||||
- vector-dimensions
|
||||
- pipeline
|
||||
- data-quality
|
||||
---
|
||||
|
||||
# Fusion Pipeline: Vector Dimension Inconsistency Causes Padding
|
||||
|
||||
## Context
|
||||
|
||||
During a fusion + similarity pipeline run (2026-03-23), several windows had inconsistent vector dimensions. The pipeline padded vectors to a common dimension to allow fusion and similarity processing, logging warnings per affected window.
|
||||
|
||||
## Pipeline Run Summary
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Start | 2026-03-23T15:30:00Z |
|
||||
| End | 2026-03-23T16:47:04Z |
|
||||
| Duration | 1h 17m 4s |
|
||||
| Embeddings processed | 28,172 |
|
||||
| Fused embeddings | 40,524 |
|
||||
| Similarity rows | 405,216 |
|
||||
|
||||
## Per-Window Warnings
|
||||
|
||||
| Window | Inserted | Warnings | Issue |
|
||||
|--------|----------|----------|-------|
|
||||
| win-002 | 2,048 | 1 | Padded vectors due to dim mismatch |
|
||||
| win-003 | 4,096 | 2 | Padded vectors due to dim mismatch |
|
||||
| win-005 | 15,344 | 3 | Padded vectors due to dim mismatch |
|
||||
|
||||
**Note:** win-001 and win-004 had no warnings (consistent dimensions).
|
||||
|
||||
## Why This Happens
|
||||
|
||||
Vector dimensions can become inconsistent across windows when:
|
||||
1. **Embedding model changes** between window processing runs
|
||||
2. **Text truncation** produces different effective lengths
|
||||
3. **Pipeline restarts** after partial failures create mixed batches
|
||||
4. **Different window sizes** (annual vs quarterly) aggregate different numbers of motions
|
||||
|
||||
## Impact
|
||||
|
||||
- **Fused embeddings are padded**, not truncated — data is preserved but with zero-padding
|
||||
- **Similarity scores** may be slightly affected for padded dimensions
|
||||
- **No data loss**, but quality degradation in affected windows
|
||||
|
||||
## Prevention
|
||||
|
||||
1. **Validate dimensions before fusion**
|
||||
```python
|
||||
# Before calling fusion, assert all vectors have the same dimension
|
||||
dims = {len(v) for v in window_vectors}
|
||||
assert len(dims) == 1, f"Dimension mismatch: {dims}"
|
||||
```
|
||||
|
||||
2. **Re-embed with consistent model/settings** if dimensions differ
|
||||
- Don't mix embeddings from different model versions
|
||||
- Re-run the full embedding pipeline if the model changes
|
||||
|
||||
3. **Window-level dimension checks** in the pipeline:
|
||||
```python
|
||||
# In pipeline/fusion.py or equivalent
|
||||
for window_id, vectors in window_vectors.items():
|
||||
dim = len(vectors[0])
|
||||
if not all(len(v) == dim for v in vectors):
|
||||
raise ValueError(f"Window {window_id}: inconsistent vector dimensions")
|
||||
```
|
||||
|
||||
4. **QA sampling after fusion**
|
||||
- Perform sample similarity lookups across N=20-50 items
|
||||
- Validate fused vectors against source embeddings
|
||||
- Check for anomalies in similarity scores for affected windows
|
||||
|
||||
## When to Apply
|
||||
|
||||
- Before running the fusion pipeline
|
||||
- After re-running the embedding pipeline with new model/settings
|
||||
- When adding new windows to an existing fused embedding set
|
||||
- During QA of similarity cache results
|
||||
|
||||
## Related
|
||||
|
||||
- `docs/solutions/best-practices/blog-numbers-from-pipeline-outputs-2026-04-16.md` — Canonical pipeline output sources
|
||||
- `pipeline/fusion.py` — Fusion pipeline implementation
|
||||
- `data/motions.db` — `fused_embeddings` and `similarity_cache` tables
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: Verify Transient Session Artifacts Against Canonical Sources Before Compounding
|
||||
date: "2026-04-24"
|
||||
category: docs/solutions/best-practices
|
||||
module: documentation
|
||||
problem_type: best_practice
|
||||
component: documentation
|
||||
severity: medium
|
||||
applies_when:
|
||||
- Merging session ledgers or other transient artifacts into durable documentation
|
||||
- Creating or updating docs/solutions/ entries from agent session outputs
|
||||
- Extracting code constants, labels, or configurations from non-canonical files
|
||||
- Compounding knowledge from temporary workspace artifacts
|
||||
tags:
|
||||
- compound-documentation
|
||||
- canonical-sources
|
||||
- session-ledgers
|
||||
- svd-labels
|
||||
- verification
|
||||
- transient-artifacts
|
||||
---
|
||||
|
||||
# Verify Transient Session Artifacts Against Canonical Sources Before Compounding
|
||||
|
||||
## Context
|
||||
|
||||
The `ce-compound` workflow involves merging session ledgers from `thoughts/ledgers/` into durable documentation under `docs/solutions/`. During one such session, an agent was instructed to create a compounding doc based on a ledger file. The agent extracted SVD component labels directly from the ledger and wrote them into a new `docs/solutions/` file.
|
||||
|
||||
The problem: the labels in the ledger were outdated. They had since been updated in the canonical source (`analysis/config.py` `SVD_THEMES`). The agent did not cross-check the ledger content against the canonical codebase before creating the durable doc. The user had to manually catch the discrepancy, instruct the agent to verify against canonical sources, and the inaccurate doc was deleted.
|
||||
|
||||
## Guidance
|
||||
|
||||
**Always cross-check transient artifacts against canonical codebase sources before creating or updating compounding documentation.**
|
||||
|
||||
When merging session ledgers or any transient artifact into `docs/solutions/`:
|
||||
|
||||
1. **Identify the canonical source for every factual claim**
|
||||
- Code constants → check the defining module (e.g., `analysis/config.py` for SVD labels)
|
||||
- Data figures → check the pipeline output or database
|
||||
- Configuration → check the committed config file, not session notes
|
||||
|
||||
2. **Do not treat ledger content as ground truth**
|
||||
- Ledgers capture agent reasoning at a point in time
|
||||
- Code evolves after the ledger is written
|
||||
- A ledger is a memory aid, not a canonical reference
|
||||
|
||||
3. **Diff the artifact against the canonical source**
|
||||
- Read the current canonical file explicitly
|
||||
- Compare values, labels, constants, or conclusions
|
||||
- If they differ, use the canonical source and note the update
|
||||
|
||||
4. **Flag discrepancies instead of silently using stale data**
|
||||
- If the ledger contradicts the codebase, document the divergence
|
||||
- Explain when and why the canonical source changed
|
||||
- Do not propagate outdated information into durable docs
|
||||
|
||||
## Why This Matters
|
||||
|
||||
Compounding documentation is meant to reduce future cognitive load. If it embeds stale or inaccurate information:
|
||||
|
||||
- **Future agents (and humans) will trust it as truth.** `docs/solutions/` is explicitly referenced in `AGENTS.md` as a source of guidance. An inaccurate doc becomes a source of repeated errors.
|
||||
- **Outdated labels or constants propagate downstream.** In this case, outdated SVD labels would have misled every future agent working on SVD analysis, visualization, or blog updates.
|
||||
- **Correcting a published doc costs more than verifying before writing.** Deleting and rewriting a doc is cheap; discovering and fixing a stale doc months later requires archaeology.
|
||||
|
||||
## When to Apply
|
||||
|
||||
Apply this guidance whenever you are:
|
||||
|
||||
- Creating a new `docs/solutions/` entry from a session ledger, conversation log, or agent memory
|
||||
- Updating an existing doc with insights from a transient artifact
|
||||
- Extracting code snippets, constants, labels, or configurations from any file that is not the canonical definition
|
||||
- Summarizing a debugging session where code was modified — the final committed code is canonical, not the session narrative
|
||||
|
||||
## Examples
|
||||
|
||||
### What Happened (Incorrect)
|
||||
|
||||
An agent read a session ledger containing SVD component labels and wrote them directly into a new `docs/solutions/` file without checking `analysis/config.py`:
|
||||
|
||||
```
|
||||
# ❌ INCORRECT: labels taken directly from stale ledger
|
||||
Component 1: "Sociale zekerheid vs economische liberalisering"
|
||||
```
|
||||
|
||||
The canonical source (`analysis/config.py` `SVD_THEMES`) had since been updated to reflect voting-pattern-based labels. The doc was inaccurate and had to be deleted.
|
||||
|
||||
### What Should Have Happened (Correct)
|
||||
|
||||
```
|
||||
# ✅ CORRECT: verify ledger claims against canonical source
|
||||
1. Read analysis/config.py SVD_THEMES
|
||||
2. Compare ledger labels with current SVD_THEMES values
|
||||
3. Use the canonical labels from config.py
|
||||
4. If the ledger contained useful context (e.g., reasoning about why labels changed),
|
||||
preserve that narrative but anchor all factual claims to the canonical source
|
||||
```
|
||||
|
||||
### Verification Pattern
|
||||
|
||||
```python
|
||||
# When documenting SVD labels, always read the canonical config
|
||||
from analysis.config import SVD_THEMES
|
||||
|
||||
for comp_num, theme in SVD_THEMES.items():
|
||||
print(f"Component {comp_num}: {theme['label']}")
|
||||
# Use these values in the doc, not ledger-cached values
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- `docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md` — how SVD labels should be derived from voting patterns
|
||||
- `docs/solutions/best-practices/blog-numbers-from-pipeline-outputs-2026-04-16.md` — deriving quantitative claims from canonical pipeline outputs
|
||||
- `analysis/config.py` — canonical source for SVD themes and other constants
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
---
|
||||
title: Working Tree Hygiene — Dependency Groups and Gitignore
|
||||
date: 2026-04-24
|
||||
category: docs/solutions/best-practices
|
||||
module: development_workflow
|
||||
problem_type: best_practice
|
||||
component: development_workflow
|
||||
severity: low
|
||||
applies_when:
|
||||
- Reviewing uncommitted changes before committing
|
||||
- Adding new dependencies to pyproject.toml
|
||||
- Updating .gitignore with new ignore patterns
|
||||
tags: [dependencies, pyproject, gitignore, hygiene, code-review, dev-tools]
|
||||
---
|
||||
|
||||
# Working Tree Hygiene — Dependency Groups and Gitignore
|
||||
|
||||
## Context
|
||||
|
||||
A code review of uncommitted changes on `main` caught three preventable hygiene issues:
|
||||
|
||||
1. `pyright` (a static type checker) was added to `[project] dependencies` in `pyproject.toml` instead of `[dependency-groups] dev`
|
||||
2. `.gitignore` contained a duplicate `.worktrees` entry
|
||||
3. A blog post included a hardcoded correlation coefficient with no reproducible source (documented separately in `blog-numbers-from-pipeline-outputs`)
|
||||
|
||||
All three were caught before commit, but they illustrate a pattern: small working tree cleanups accumulate friction when not reviewed systematically.
|
||||
|
||||
## Guidance
|
||||
|
||||
### Dependency classification
|
||||
|
||||
When adding a package to `pyproject.toml`, ask: **does this run in production?**
|
||||
|
||||
| If... | Put it in... |
|
||||
|-------|-------------|
|
||||
| The app imports it at runtime | `[project] dependencies` |
|
||||
| It is a type checker, test runner, linter, or dev server | `[dependency-groups] dev` |
|
||||
| It is only used in build scripts or CI | `[dependency-groups] dev` |
|
||||
|
||||
**Concrete check:** search the codebase for `import <package>` or `from <package>`. If it only appears in `tests/`, `scripts/`, or type stubs, it belongs in `dev`.
|
||||
|
||||
### Gitignore hygiene
|
||||
|
||||
Before committing a `.gitignore` change, run:
|
||||
|
||||
```bash
|
||||
sort .gitignore | uniq -d
|
||||
```
|
||||
|
||||
If anything prints, you have duplicates. Remove them.
|
||||
|
||||
Also check that your new entry does not overlap with an existing pattern:
|
||||
- `.worktrees/` and `.worktrees` are redundant — keep the slash form for directories
|
||||
- `data/*.json` already covers `data/motions.json` — do not add the specific file
|
||||
|
||||
### Pre-commit audit checklist
|
||||
|
||||
For every set of uncommitted changes:
|
||||
|
||||
1. **Dependencies**: Any new packages in the right group?
|
||||
2. **Gitignore**: Any duplicates or redundant patterns?
|
||||
3. **Blog/docs**: Any hardcoded numbers without canonical sources? (see `blog-numbers-from-pipeline-outputs`)
|
||||
4. **Config**: Any secrets or local paths committed by accident?
|
||||
|
||||
## Why This Matters
|
||||
|
||||
These issues are individually trivial, but together they create a "broken windows" effect. A `pyproject.toml` with dev tools in runtime dependencies signals that the project does not distinguish between production and development concerns. Duplicate `.gitignore` entries suggest the file is append-only and never reviewed. Small hygiene lapses compound into larger maintainability debt.
|
||||
|
||||
The fix is cheap: a 30-second scan of the diff before committing prevents all of them.
|
||||
|
||||
## When to Apply
|
||||
|
||||
- Before every commit that touches `pyproject.toml`, `.gitignore`, or `uv.lock`
|
||||
- When onboarding a new dependency
|
||||
- During code review of any PR that adds build tools, test frameworks, or local config
|
||||
|
||||
## Examples
|
||||
|
||||
**Dependency misclassification:**
|
||||
|
||||
```toml
|
||||
# ❌ Before
|
||||
[project]
|
||||
dependencies = [
|
||||
"duckdb>=1.3.2",
|
||||
"pyright>=1.1.408", # dev tool in runtime deps
|
||||
]
|
||||
|
||||
# ✅ After
|
||||
[project]
|
||||
dependencies = [
|
||||
"duckdb>=1.3.2",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=9.0.2",
|
||||
"pyright>=1.1.408",
|
||||
]
|
||||
```
|
||||
|
||||
**Gitignore duplicate:**
|
||||
|
||||
```diff
|
||||
# Worktrees
|
||||
.worktrees/
|
||||
|
||||
# Generated analysis files
|
||||
thoughts/explorer/*.json
|
||||
-
|
||||
- # Stray temp files
|
||||
- .worktrees # ← duplicate, remove
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- `docs/solutions/best-practices/blog-numbers-from-pipeline-outputs-2026-04-16.md` — companion guidance on keeping quantitative claims reproducible
|
||||
- `docs/solutions/workflow-issues/verify-session-artifacts-against-canonical-sources-2026-04-24.md` — same verification principle applied to session artifacts
|
||||
@@ -17,8 +17,8 @@ SVD axis themes in `analysis/config.py` can drift from actual party positions in
|
||||
## Symptoms
|
||||
|
||||
- Axis 4 theme said "Mainstreampartijen versus FVD/DENK-oppositie" but actual party positions showed NSC (-24.47) and BBB (-4.58) on the left extreme, D66 (10.53)/CDA (10.11)/JA21 (9.90) on the right extreme, and FVD/DENK in the middle
|
||||
- Pole labels (`left_pole`/`right_pole`) described parties that weren't actually on those sides after flip
|
||||
- The flip mechanism (`compute_flip_direction`) worked correctly, but theme text was stale
|
||||
- **NOTE (2026-04-12): The `left_pole`/`right_pole` static fields added here caused the same bug — when runtime flip differed from static config flip, labels pointed to wrong sides. These fields were removed. See `docs/solutions/ui-bugs/svd-axis-pole-labels-incorrect-after-flip.md` for the corrected approach.**
|
||||
|
||||
## Root Cause
|
||||
|
||||
@@ -46,9 +46,9 @@ Updated `analysis/config.py` component 4:
|
||||
"right_pole": "D66, CDA, JA21 — moties met brede steun",
|
||||
```
|
||||
|
||||
### 2. Added semantic left_pole/right_pole labels
|
||||
### 2. Added semantic left_pole/right_pole labels — SUPERSEDED (2026-04-12)
|
||||
|
||||
Added `left_pole` and `right_pole` fields to all 10 SVD_THEMES entries. These describe what's on the left and right sides AFTER flip, decoupling label text from raw SVD math. Updated 4 rendering locations in `explorer.py` to use these semantic labels with backward compat fallback.
|
||||
**This approach caused the same bug.** The static `left_pole`/`right_pole` fields assumed a fixed flip direction, but `compute_flip_direction` determines flip at runtime. When runtime flip differed from static config, labels pointed to wrong sides. These fields were removed. See `docs/solutions/ui-bugs/svd-axis-pole-labels-incorrect-after-flip.md` for the corrected approach.
|
||||
|
||||
### 3. Created validation hook
|
||||
|
||||
@@ -69,15 +69,15 @@ Returns exit code 1 if any divergence found — suitable for CI integration.
|
||||
The flip mechanism (`compute_flip_direction`) correctly positions canonical right parties on the right side by comparing mean scores. The validation hook uses the same function with full average vectors to verify post-flip positions. Theme pole labels are now pre-computed semantic descriptions that match the flipped orientation, not raw SVD positive/negative poles.
|
||||
|
||||
## Prevention
|
||||
|
||||
- Run `scripts/validate_svd_themes.py` after any SVD recomputation
|
||||
- Add to CI pipeline: `uv run python scripts/validate_svd_themes.py --db data/motions.db`
|
||||
- When updating themes, verify against actual party positions from `svd_vectors`, not just motion sponsors
|
||||
- Consider automating theme generation from party positions + motion analysis
|
||||
- **NEVER add static `left_pole`/`right_pole` fields** — derive labels at render time from runtime flip (see corrected approach in `svd-axis-pole-labels-incorrect-after-flip.md`)
|
||||
- Run `tests/test_svd_axis_alignment.py` to validate alignment after SVD recomputation
|
||||
|
||||
## Related Files
|
||||
|
||||
- `analysis/config.py` — SVD_THEMES with left_pole/right_pole fields
|
||||
- `explorer.py` — rendering functions using semantic pole labels
|
||||
- `analysis/config.py` — SVD_THEMES (no `left_pole`/`right_pole`)
|
||||
- `explorer.py` — label derivation and component 3-10 scoring
|
||||
- `analysis/svd_labels.py` — compute_flip_direction() function
|
||||
- `scripts/validate_svd_themes.py` — validation hook
|
||||
- `tests/test_svd_axis_alignment.py` — alignment tests (added 2026-04-12)
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: Verify Session Artifacts Against Canonical Sources Before Creating Compounding Docs
|
||||
date: 2026-04-24
|
||||
category: docs/solutions/workflow-issues
|
||||
module: ce-compound
|
||||
problem_type: workflow_issue
|
||||
component: documentation
|
||||
severity: high
|
||||
applies_when:
|
||||
- Merging session ledgers into docs/solutions
|
||||
- Creating compounding documentation from transient artifacts
|
||||
- Extracting labels, config values, or data points from session files
|
||||
symptoms:
|
||||
- Documentation contains outdated information
|
||||
- Agent creates docs without cross-checking canonical sources
|
||||
- Inaccurate labels propagated to durable documentation
|
||||
root_cause: missing_workflow_step
|
||||
resolution_type: workflow_improvement
|
||||
related_components:
|
||||
- ce-compound
|
||||
- analysis
|
||||
tags: [ce-compound, session-ledgers, canonical-sources, verification, documentation-quality, svd-labels]
|
||||
---
|
||||
|
||||
# Verify Session Artifacts Against Canonical Sources Before Creating Compounding Docs
|
||||
|
||||
## Context
|
||||
|
||||
During a `ce-compound` ledger-to-docs merge, an agent read an old session ledger (`ses_2b9f`) from `thoughts/ledgers/` and extracted SVD component labels. These labels were written into a new `docs/solutions/` file as authoritative documentation. However, the labels in the ledger were stale — the canonical source (`analysis/config.py` `SVD_THEMES`) had since been updated. The user caught the discrepancy before the doc was committed and flagged it for correction.
|
||||
|
||||
Session ledgers are generated at capture time and may become stale as the codebase evolves. They are snapshots, not authorities — using their content directly risks propagating outdated information into durable docs.
|
||||
|
||||
## Guidance
|
||||
|
||||
When merging session artifacts into compounding documentation:
|
||||
|
||||
1. **Identify the canonical source** for every data point extracted from a session file. If the information exists in the codebase (config, database schema, function output), that is the canonical source, not the ledger.
|
||||
|
||||
2. **Cross-check all extracted values** against the canonical source before writing. For SVD labels, verify against `analysis/config.py` `SVD_THEMES`. For quantitative claims, run the pipeline function that produces them. For schema details, check the model or migration.
|
||||
|
||||
3. **When in doubt, ask the user** which source to use. Do not assume a ledger file is current unless you have confirmed it.
|
||||
|
||||
4. **Tag the doc with relevant components** (e.g., `analysis`) so future sweeps can detect drift.
|
||||
|
||||
5. **If the canonical source has changed since the ledger was captured**, update the doc to reflect the current state, not the ledger state.
|
||||
|
||||
## Why This Matters
|
||||
|
||||
Session ledgers are transient artifacts. They capture what was true at a point in time, not what is true now. Treating them as authoritative introduces stale data into the durable documentation layer, which erodes trust and requires expensive corrections later. This is the same class of problem as hardcoding blog numbers from memory — the fix is to route every data point through its canonical source.
|
||||
|
||||
Unverified documentation is worse than no documentation because it misleads with apparent authority.
|
||||
|
||||
## When to Apply
|
||||
|
||||
- When `ce-compound` extracts labels, values, or claims from a session ledger
|
||||
- When creating any `docs/solutions/` doc whose content depends on codebase state (config values, function outputs, schema)
|
||||
- When a session file references code or config that has been modified since the session was recorded
|
||||
|
||||
## Examples
|
||||
|
||||
**Actual incident — outdated SVD labels:**
|
||||
|
||||
A ledger from an old session contained SVD component labels that described motion patterns. These labels had been revised in `analysis/config.py` (the `SVD_THEMES` dict) as the voting analysis matured.
|
||||
|
||||
- ❌ What happened: Agent extracted the labels from the ledger and created `docs/solutions/insights/svd-voting-patterns-by-component-2026-04-04.md` using them
|
||||
- ✅ What should have happened: Agent verified each label against `analysis/config.py` `SVD_THEMES`, found that the canonical source had updated values, and used the current values instead (or flagged the discrepancy to the user)
|
||||
|
||||
## Related
|
||||
|
||||
- `docs/solutions/best-practices/blog-numbers-from-pipeline-outputs-2026-04-16.md` — same principle applied to blog copy: always derive data from canonical pipeline functions, not memory or artifacts
|
||||
- `docs/solutions/workflow-issues/trajectories-diagnostic-false-alarm-2026-03-31.md` — another instance of trusting an intermediary artifact (diagnostic JSON) without verifying against the canonical database state
|
||||
Reference in New Issue
Block a user