ADR 0015 fixiert die zwei strukturellen Entscheidungen vom 2.0-Cut: - Prod-Deploy ueber sauberen git-Checkout statt Tar-Upload (loest ADR 0004 in Teilen ab) - Reconstruct_zitate-Zwei-Pass: Zitate werden ueber beide Bloecke hinweg klassifiziert, dann erst geschrieben — Cross-Block-Move statt nur quelle-Korrektur scripts/migrate-zitate-blocks.py: idempotentes String-basiertes Migrations-Skript fuer bestehende Records mit altem Bug-Stand. Nicht LLM-abhaengig, deterministisch. Beim 2.0-Cut auf 22 Assessments angewendet (26 Zitate verschoben).
4.5 KiB
0015 — Prod-Deploy als sauberer git-Checkout + Citation-Block-Reklassifikation
| Status | accepted |
| Datum | 2026-05-10 |
| Supersedes | (Anteile von) ADR 0004 (Tar-Upload-Workflow) |
| Refs | v2.0.0-Tag, release/2.0-Branch, Commits 770d890–88211c5 |
Kontext
Beim 1.x → 2.0-Release-Cut wurden zwei strukturelle Probleme sichtbar, die in einer Entscheidung zusammenhängen.
Problem 1 — Prod-Repo-Mess. Der prod-Pfad /opt/gwoe-antragspruefer
wurde seit April per Tar-Upload aktualisiert (siehe ADR 0004). Effekt:
Filesystem-Stand war neuer als der HEAD-Commit (4fbdc15 vom 10. April),
19 Files modifiziert, 30+ untracked. git pull war nicht mehr möglich,
ohne den Stand zu zerstören. Dev wurde parallel sauber per git pull aus
main deployed (Cron auto-deploy.sh). Die zwei Workflows divergierten.
Problem 2 — Citation-Block-Misattribution. reconstruct_zitate (ADR
0001 / Issue #60) hat bei einem Cross-Kind-Fallback-Match nur die
quelle des Zitats korrigiert, das Zitat aber im ursprünglichen Block
belassen. Folge: Im wahlprogramm-Block standen Zitate aus
Grundsatzprogrammen, die quelle stimmte zwar, aber die Block-Zuordnung
suggerierte etwas Falsches. Reproduziert auf Antrag 18/18246 (NRW),
Bewertung GRÜNE.
Optionen
Workflow 1 — Beide Probleme über separate Migrationen lösen
A.1 (Deploy): Einmaliger Cut mit frischem git clone, dann beidseitig
git-pull-basiert. Tar-Upload-Pfad obsolet.
A.2 (Deploy): Tar-Upload retten, indem man HEAD nachzieht und .gitignore erweitert, sodass Tar-überschriebene Files nicht als Diff auftauchen.
B.1 (Citations): Zwei-Pass-Verarbeitung in reconstruct_zitate —
erst klassifizieren über beide Blöcke hinweg, dann schreiben. Plus
einmaliges String-basiertes Migrations-Skript für die bestehenden 117
Records.
B.2 (Citations): Ganze Bewertungen neu generieren (LLM-Call), sobald der Code-Fix lebt.
Entscheidung
Deploy: A.1. Einmaliger sauberer git-clone auf prod, danach beide
Umgebungen identisch via git pull. Begründung: Der Tar-Mess wäre nie
sauber zu reparieren gewesen, ohne irgendwo eine Annahme zu treffen, was
"echt" ist. Ein frischer Clone setzt den Stand definitiv. Alle Volumes
(data/, reports/, backups/) bleiben unangetastet.
Citations: B.1. Code-Fix mit Zwei-Pass plus einmaliges Migrations-Skript. Begründung: B.2 wäre nicht-deterministisch (LLM-Fluktuation), würde Tokens verbrennen und liefert keine bessere Garantie als das deterministische String-Match auf "Grundsatzprogramm" vs. "Wahlprogramm" im quelle-Label. 22 Assessments wurden migriert, 26 Zitate verschoben.
Konsequenzen
Deploy
/opt/gwoe-antragsprueferist seit dem Cut ein sauberer Checkout vonrelease/2.0.- Nächster Standard-Deploy:
./scripts/deploy.sh(Branch-Guard, Pre-flight, Pre-Deploy-Backup, Health-Check, Uptime-Kuma). - Major-Cuts:
./scripts/major-release-cut.sh <tag> <branch>— inkl. Bundle-Fallback bei Gitea-Korruption (war beim 2.0-Cut nötig). - Alter Pfad als
/opt/gwoe-antragspruefer-YYYYMMDD-HHMMSS-archivearchiviert. - ADR 0004 ist in Teilen abgelöst, der Tar-Upload-Pfad gilt nicht mehr.
Citation-Binding
reconstruct_zitateklassifiziert pro Fraktion über beide Blöcke hinweg, schreibt erst danach in die jeweils passenden Blöcke.- Test:
tests/test_embeddings.py::TestReconstructZitate::test_zitat_aus_grundsatzprogramm_landet_im_parteiprogramm_blockreproduziert den 18/18246-Fall. - Migrations-Skript
scripts/migrate-zitate-blocks.pyist idempotent und kann jederzeit re-run werden, falls weitere Records aus älterem Code-Stand reinkommen.
DB-Wipe-Liste beim Major-Cut
scripts/major-release-cut.sh enthält die Liste der Tabellen, die beim
Cut geleert werden:
assessments,assessment_versions— Bewertungen (Schema-Drift möglich)presse_drafts,news_articles— Cache-Datenauto_rate_runs,jobs— Queue-/Cron-Trackingmonitoring_scans,monitoring_daily_summary— Live-Metrikenauth_bypass_uses,comments,merkliste,bookmarks,email_subscriptions,votes
Erhalten bleiben:
plenum_vote_results— kostenlose Re-Ingest-Daten (PDFs werden vom Server geholt, kein LLM nötig)abgeordnetenwatch_votes,abgeordnetenwatch_polls— re-fetchbar viasync_abgeordnetenwatch.py, aber zeitaufwändigembeddings.db— extern als Volume, separat gehandhabt
Beim 3.0-Cut diese Liste prüfen: falls neue User-Daten-Tabellen hinzukommen (z.B. erweiterte Bookmarks), gehört die Wipe-Entscheidung dort explizit gemacht.