1 line
71 KiB
JSON
1 line
71 KiB
JSON
{"config":{"lang":["de"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"GW\u00d6-Antragspr\u00fcfer \u2014 Dokumentation","text":"<p>Diese Dokumentation folgt dem Di\u00e1taxis-Framework und ist nach Funktion (nicht nach Themen) organisiert. Drift-immun durch klare Trennung: Reference wird aus dem Code generiert, ADRs sind immutable, Tutorials/How-to sind manuell gepflegt aber knapp.</p>"},{"location":"#struktur","title":"Struktur","text":"<pre><code>docs/\n\u251c\u2500\u2500 README.md \u2190 du bist hier\n\u251c\u2500\u2500 adr/ Architecture Decision Records (immutable)\n\u2502 \u251c\u2500\u2500 README.md ADR-Index + Workflow\n\u2502 \u251c\u2500\u2500 template.md Template f\u00fcr neue ADRs\n\u2502 \u2514\u2500\u2500 NNNN-titel.md Eine Datei pro Entscheidung\n\u2514\u2500\u2500 archive/ Historische Snapshots, nicht autoritativ\n</code></pre> <p>Geplant f\u00fcr sp\u00e4ter (siehe Issue #62):</p> <pre><code>docs/\n\u251c\u2500\u2500 reference/ \u2190 mkdocs autodoc-Output (nicht eingecheckt)\n\u251c\u2500\u2500 tutorials/ \u2190 Erst-Schritt-Anleitungen\n\u251c\u2500\u2500 how-to/ \u2190 Aufgaben-orientiert, \"wie deploye ich\"\n\u2514\u2500\u2500 explanation/ \u2190 Hintergr\u00fcnde, Konzepte\n</code></pre>"},{"location":"#nicht-hier-dokumentiert-bewusst","title":"Nicht hier dokumentiert (bewusst)","text":"Was Wo API-Reference (Endpoints, Models, Schemas) Auto-generiert aus FastAPI/Pydantic, nicht in docs/ einchecken Code-Reference (Klassen, Funktionen) mkdocstrings aus den Docstrings (geplant Phase 2) Aktueller Projekt-Stand / Issues Gitea Issues \u2014 <code>repo.toppyr.de/tobias/gwoe-antragspruefer/issues</code> Onboarding f\u00fcr KI-assisted Coder <code>CLAUDE.md</code> im Repo-Root Live-System-Status <code>https://gwoe.toppyr.de/auswertungen</code> (dynamisches Dashboard) Memory der KI-Sessions <code>~/.claude/projects/<projekt>/memory/</code> (privat)"},{"location":"#gegen-drift","title":"Gegen Drift","text":"<ul> <li>ADRs sind immutable: nie \u00fcberschreiben, sondern bei \u00c4nderung mit einem neuen ADR superseden, der den alten in seinem Header referenziert.</li> <li>Reference wird aus dem Code generiert, nie von Hand gepflegt.</li> <li><code>docs/archive/</code> enth\u00e4lt historische Status-Files. Werden gelesen aber nicht aktualisiert. Wenn etwas davon noch wahr ist, geh\u00f6rt es in einen ADR oder in die generierte Reference, nicht in einen neuen Status-Snapshot.</li> </ul>"},{"location":"adr/","title":"Architecture Decision Records (ADRs)","text":"<p>ADRs dokumentieren signifikante Architektur-Entscheidungen mit Kontext, Optionen und Konsequenzen. Format inspiriert von Michael Nygard.</p>"},{"location":"adr/#workflow","title":"Workflow","text":"<ol> <li>Neue Entscheidung steht an \u2192 Kopie von <code>template.md</code> mit n\u00e4chster freier Nummer (<code>NNNN-kebap-titel.md</code>).</li> <li>Status <code>proposed</code> \u2192 diskutiert in Issue/PR \u2192 bei Akzeptanz auf <code>accepted</code>.</li> <li>Niemals editieren nach <code>accepted</code>. Wenn eine Entscheidung sich \u00e4ndert, neuer ADR mit <code>Supersedes: NNNN-\u2026</code> im Header und der alte ADR bekommt <code>Superseded by: MMMM-\u2026</code>.</li> <li>Status <code>deprecated</code> f\u00fcr Entscheidungen, die ohne Nachfolger auslaufen.</li> </ol>"},{"location":"adr/#index","title":"Index","text":"ID Titel Status Datum 0001 LLM-Citations server-seitig binden statt prompt-seitig accepted 2026-04-10 0002 Adapter-Pattern mit ParlamentAdapter-Basisklasse + Registry accepted 2026-04-10 0003 Sub-D Property-Verification: Zitate als Substring der zitierten PDF-Seite accepted 2026-04-10 0004 Docker Compose Deploy mit DB-/Reports-Volume und SN-XML-Sonderpfad accepted 2026-04-10"},{"location":"adr/#wann-adr-wann-nicht","title":"Wann ADR, wann nicht","text":"ADR-w\u00fcrdig nicht ADR-w\u00fcrdig Wahl zwischen mehreren plausiblen Architekturen mit Trade-offs Bug-Fix Strukturelle Konsequenzen f\u00fcr mehrere Module Refactoring innerhalb eines Moduls Reverse-Engineering-Findings die andere Adapter beeinflussen Stil\u00e4nderungen, Linting-Konventionen Neue externe Abh\u00e4ngigkeiten oder APIs Dependency-Bumps ohne API-\u00c4nderung Workflow-Konventionen die mehrere Sessions \u00fcberdauern m\u00fcssen Tagesgesch\u00e4ft, Issue-Tracking <p>Faustregel: Wenn ein neuer Kollege (oder eine neue Session) die Entscheidung sonst r\u00fcckg\u00e4ngig machen w\u00fcrde, geh\u00f6rt sie in einen ADR.</p>"},{"location":"adr/0001-llm-citation-binding/","title":"0001 \u2014 LLM-Citations server-seitig binden statt prompt-seitig","text":"Status accepted Datum 2026-04-10 Refs Issue #60, Commits eb045d0/ed64399/db3ada9/6ced7ae, Sub-D Tests"},{"location":"adr/0001-llm-citation-binding/#kontext","title":"Kontext","text":"<p>Der GW\u00d6-Antragspr\u00fcfer l\u00e4sst ein LLM (Qwen Plus via DashScope) parlamentarische Antr\u00e4ge bewerten. Pro Antrag werden via Embedding-Retrieval relevante Chunks aus den Wahl- und Grundsatzprogrammen der involvierten Parteien zugespielt. Das LLM gibt im JSON-Output <code>wahlprogramm_scores[*].wahlprogramm.zitate[*]</code> mit jeweils <code>text</code>, <code>quelle</code> (Source-Label, z.B. \"GR\u00dcNE NRW Wahlprogramm 2022, S. 58\") und <code>url</code> zur\u00fcck.</p> <p>Beim ersten Live-Lauf des Sub-D Citation-Property-Tests (siehe ADR 0003) gegen die Prod-DB wurden drei Halluzinations-Cases gefunden: das LLM hatte Snippets erfunden und unter realen Source-Labels zitiert. Issue #60 wurde ge\u00f6ffnet.</p>"},{"location":"adr/0001-llm-citation-binding/#optionen","title":"Optionen","text":"<p>Drei strukturell unterschiedliche Wege, das LLM zu binden:</p>"},{"location":"adr/0001-llm-citation-binding/#option-a-scharferer-prompt","title":"Option A \u2014 Sch\u00e4rferer Prompt","text":"<p>Die ZITATEREGEL im User-Prompt wird versch\u00e4rft. Konkret:</p> <ol> <li>Jeder retrievte Chunk wird mit einer ENUM-ID <code>[Q1]</code>, <code>[Q2]</code>, \u2026 getaggt.</li> <li>Das LLM wird angewiesen, jedes Zitat per <code>[Qn]</code>-Tag an einen Chunk zu binden, den <code>text</code> w\u00f6rtlich aus diesem Chunk zu kopieren und das Source-Label exakt zu \u00fcbernehmen.</li> <li>Top-K wird von 2 \u2192 5 erh\u00f6ht, damit die \"richtige\" Seite h\u00e4ufiger im Retrieval-Window landet.</li> </ol> <p>Vorteile: prompt-only, kleiner Diff, keine Server-Pipeline-\u00c4nderung.</p> <p>Nachteile: ENUM-Anker im Prompt ist ein weicher Hint. Das LLM darf zwar den Snippet aus Chunk Qn nehmen, aber es gibt nichts, was es daran hindert, die Seite aus Chunk Qm in <code>quelle</code> zu schreiben. Cross-Mix bleibt m\u00f6glich.</p>"},{"location":"adr/0001-llm-citation-binding/#option-b-server-seitige-quellen-rekonstruktion","title":"Option B \u2014 Server-seitige Quellen-Rekonstruktion","text":"<p>Nach dem LLM-Call werden die emittierten Zitate Server-seitig nachgearbeitet:</p> <ol> <li>F\u00fcr jedes Zitat im Output-JSON wird der <code>text</code> per Substring oder 5-Wort-Anker gegen alle retrievten Chunks gematcht.</li> <li>Bei Match: <code>quelle</code> und <code>url</code> werden aus dem matchenden Chunk konstruiert und der LLM-Output f\u00fcr diese Felder verworfen.</li> <li>Bei kein-Match: das ganze Zitat wird verworfen.</li> </ol> <p>Vorteile: strukturell \u2014 der LLM hat keine M\u00f6glichkeit mehr, eine falsche Quelle anzugeben. Die einzigen zwei Outcomes sind \"korrekt zitiert\" oder \"verworfen\".</p> <p>Nachteile: zus\u00e4tzlicher Server-seitiger Schritt nach <code>json.loads</code> und vor Pydantic-Validation. Match-Logik muss konsistent mit Sub-D bleiben.</p>"},{"location":"adr/0001-llm-citation-binding/#option-c-schema-anderung-mit-quote_id-feld","title":"Option C \u2014 Schema-\u00c4nderung mit <code>quote_id</code>-Feld","text":"<p>Statt <code>quelle</code> direkt zu emittieren, soll das LLM ein neues Feld <code>quote_id</code> (z.B. <code>\"Q3\"</code>) liefern, aus dem der Server <code>quelle</code>/<code>url</code> rekonstruiert.</p> <p>Vorteile: explizit schema-modelliert, keine Heuristik.</p> <p>Nachteile: invasiv (Pydantic-Modell, JSON-Schema, Frontend, DB-Migration); verl\u00e4sst sich darauf, dass das LLM die ID korrekt setzt \u2014 wenn es l\u00fcgt, ist die Bindung futsch.</p>"},{"location":"adr/0001-llm-citation-binding/#entscheidung","title":"Entscheidung","text":"<p>A + B kombiniert. Option A liefert die Vorab-H\u00e4rtung im Prompt \u2014 sie f\u00e4ngt den Gro\u00dfteil der Cases (etwa 80%). Option B ist die strukturelle Backstop, die die restlichen 20% (echter Snippet aus Qn, falsche Seite aus Qm) verl\u00e4sslich abdeckt. Option C wurde verworfen, weil der Schema- Eingriff den Aufwand nicht rechtfertigt, solange B die LLM-Eingabe ohnehin ignoriert.</p> <p>Konkret in dieser Reihenfolge implementiert:</p> <ol> <li><code>db3ada9</code> \u2014 Option A: ENUM-Anker <code>[Q1]/[Q2]/\u2026</code> im Prompt + Top-K 2\u21925. Live-Verifikation der drei Original-Cases aus #60: 13/13 ok.</li> <li>Sub-D Live-Run gegen Prod-DB: 45/46 gr\u00fcn, ein neuer Case (BB 8/673) gefunden mit \"echter Snippet, falsche Seite\". Beweis dass A allein nicht reicht.</li> <li><code>6ced7ae</code> \u2014 Option B: <code>embeddings.reconstruct_zitate(data, semantic_quotes)</code> nach <code>json.loads</code> und vor Pydantic-Validation. Helpers <code>find_chunk_for_text</code> und <code>_normalize_for_match</code> mit identischer Logik wie Sub-D. Bei Match wird <code>_chunk_source_label</code> und <code>_chunk_pdf_url</code> angewendet, bei No-Match wird das Zitat gedroppt.</li> <li>Sub-D Re-Run nach B: 52/52 gr\u00fcn \u00fcber NRW/LSA/BE/MV/BB/BUND.</li> </ol>"},{"location":"adr/0001-llm-citation-binding/#konsequenzen","title":"Konsequenzen","text":""},{"location":"adr/0001-llm-citation-binding/#positiv","title":"Positiv","text":"<ul> <li>Strukturelle Garantie: nach <code>reconstruct_zitate</code> kann ein im DB gespeichertes Zitat nur noch eines von zwei Dingen sein: korrekt zitiert aus einem real retrievten Chunk, oder gar nicht da.</li> <li>Sub-D als CI-Gate: das Property-Test-Pattern f\u00e4ngt jede k\u00fcnftige Regression dieser Bug-Klasse.</li> <li>Server vertraut LLM nicht mehr f\u00fcr <code>quelle</code>/<code>url</code>. K\u00fcnftige Modell- Wechsel oder Prompt-Drift k\u00f6nnen die Quellen-Korrektheit nicht mehr brechen.</li> </ul>"},{"location":"adr/0001-llm-citation-binding/#negativ","title":"Negativ","text":"<ul> <li>Fehlende Match-Heuristik = stilles Verschwinden: Wenn der LLM einen echten Chunk leicht paraphrasiert (z.B. Komma anders gesetzt), verwirft Option B das Zitat. Die 5-Wort-Anker-Fallback f\u00e4ngt das in den meisten F\u00e4llen, aber Edge-Cases bleiben. Mitigation: doctest-style Sub-D-Run pro Deploy + Monitor der <code>wahlprogramm_scores[*].zitate</code>-Counts pro Assessment.</li> <li>Code-Pfad-Komplexit\u00e4t: drei Schichten (Prompt-ENUM, Server-Postprocess, Sub-D-Test) statt einer. Daf\u00fcr ist jede Schicht isoliert testbar.</li> <li><code>embeddings.py</code> hat jetzt mehr Verantwortung als nur Embedding-Retrieval \u2014 auch Match-Helpers und Source-Label-Konstruktion. Akzeptabel solange das Modul thematisch geschlossen bleibt; bei weiterem Wachstum als <code>citations.py</code> extrahieren.</li> </ul>"},{"location":"adr/0001-llm-citation-binding/#folgen-fur-andere-adrs","title":"Folgen f\u00fcr andere ADRs","text":"<ul> <li>ADR 0003 (Sub-D Citation-Property-Tests) ist die Test-S\u00e4ule dieser Entscheidung \u2014 wenn 0003 jemals supersedet wird, bricht 0001 ohne den Test-Backstop m\u00f6glicherweise still.</li> <li>Neue Adapter (zuk\u00fcnftige Bundesl\u00e4nder) m\u00fcssen sich nicht extra um Citation-Korrektheit k\u00fcmmern; der Server-Postprocess ist adapter-agnostisch.</li> </ul>"},{"location":"adr/0002-adapter-architecture/","title":"0002 \u2014 Adapter-Pattern mit ParlamentAdapter-Basisklasse + Registry","text":"Status accepted Datum 2026-04-10 Refs Issues #2, #3, #4, #19, #21, #23, #24, #26, #56, parlamente.py"},{"location":"adr/0002-adapter-architecture/#kontext","title":"Kontext","text":"<p>Der GW\u00d6-Antragspr\u00fcfer soll Antr\u00e4ge aus 16 Bundesl\u00e4ndern + dem Bundestag analysieren k\u00f6nnen. Jedes Parlament hat ein eigenes Doku-System mit unterschiedlichen Endpoints, Schemas, Auth-Anforderungen und Quirks:</p> Familie Mitglieder Charakter portala/eUI LSA, BE, BB, RP JSON-Tree-Search mit <code>parsed</code>-Strings, HTML-Hits in efxRecordRepeater StarWeb (eUI v2) HE <code>browse.tt.json</code> 2-step + Perl-Dump-Comments PARiS (Java-Servlet) HB \u00e4lterer StarWeb-Vorg\u00e4nger, eigenes Hit-Format ParlDok 8.x MV, HH, TH SPA mit Fulltext/Search + Resultpage Pagination StarFinder-CGI SH <code>lissh.lvn.parlanet.de/cgi-bin/starfinder/0</code> OPAL native NRW \u00e4lteres Eigensystem PARLIS (eUI-Variante) BW polling, JSON-in-HTML-Comments Eigensysteme BY (TYPO3-Solr), SL (Umbraco/.NET), SN (ASP.NET-Webforms), BUND (DIP-API) jeweils komplett unique"},{"location":"adr/0002-adapter-architecture/#optionen","title":"Optionen","text":""},{"location":"adr/0002-adapter-architecture/#option-a-eine-mega-klasse-mit-strategy-pattern","title":"Option A \u2014 Eine Mega-Klasse mit Strategy-Pattern","text":"<p>Eine <code>ParlamentAdapter</code>-Klasse mit Strategy-Object pro Doku-System, gew\u00e4hlt \u00fcber Konfiguration.</p> <p>Vorteile: zentral, ein Eintrittspunkt f\u00fcr gemeinsame Logik wie Fraktion-Normalisierung.</p> <p>Nachteile: kompliziert sich schnell, wenn jede Strategy ihre eigenen Quirks hat (z.B. BB hat Datum-vor-Drucksachennummer im h6, BE hat 730d-Window-Hack, HE hat Perl-Hex-Decoder). Die Trennung w\u00e4re \"Strategy mit Subclasses\" \u2014 und damit sind wir bei B.</p>"},{"location":"adr/0002-adapter-architecture/#option-b-abstract-base-eine-konkrete-klasse-pro-familie","title":"Option B \u2014 Abstract Base + Eine konkrete Klasse pro Familie","text":"<p><code>ParlamentAdapter(ABC)</code> mit <code>search/get_document/download_text</code>-Methoden. Pro Doku-System eine konkrete Klasse: <code>PortalaAdapter</code>, <code>ParLDokAdapter</code>, <code>StarFinderCGIAdapter</code>, <code>StarWebHEAdapter</code>, <code>PARiSHBAdapter</code>, <code>PARLISAdapter</code>, <code>BayernAdapter</code>, <code>SaarlandAdapter</code>, <code>BundestagAdapter</code>, <code>SNEdasXmlAdapter</code>, <code>NRWAdapter</code>.</p> <p>Wo m\u00f6glich, wird die Klasse via Konstruktor-Parameter parametrisiert (<code>PortalaAdapter</code> nimmt 6 BLs auf, <code>ParLDokAdapter</code> 3) statt jede Wiederverwendung als Subklasse.</p> <p>Registriert in einem <code>ADAPTERS: dict[str, ParlamentAdapter]</code> am Datei-Ende, indexiert per BL-Code (<code>\"NRW\"</code>, <code>\"BUND\"</code>, \u2026).</p> <p>Vorteile: - Klare Verantwortlichkeit: ein Test-Lauf gegen einen LT trifft genau eine Klasse. - Reverse-Engineering-Findings k\u00f6nnen als Class-Docstring dokumentiert werden, wo sie hingeh\u00f6ren. - Parametrisierung erlaubt Wiederverwendung ohne Vererbungs-Hierarchie. - <code>get_adapter(bundesland)</code> ist O(1).</p> <p>Nachteile: - Cross-Cutting-Concerns (Fraktion-Normalisierung) m\u00fcssen separat gel\u00f6st werden \u2014 siehe ADR-Folge zur <code>parteien</code>-Zentralisierung in #55.</p>"},{"location":"adr/0002-adapter-architecture/#option-c-separates-modul-pro-adapter","title":"Option C \u2014 Separates Modul pro Adapter","text":"<p>Ein Python-File pro Adapter (z.B. <code>app/adapters/lsa.py</code>).</p> <p>Vorteile: maximale Datei-Granularit\u00e4t.</p> <p>Nachteile: Import-Overhead, der Registry-Eintrag muss in einer dritten Datei liegen, kein nennenswerter Vorteil gegen\u00fcber B solange die Adapter-Klassen unter ~500 Zeilen bleiben (PortalaAdapter ist mit ~520 Zeilen der gr\u00f6\u00dfte).</p>"},{"location":"adr/0002-adapter-architecture/#entscheidung","title":"Entscheidung","text":"<p>Option B. Eine <code>parlamente.py</code> mit einer Klasse pro Doku-System, parametrisierbar wo sinnvoll, registriert in <code>ADAPTERS</code>. Cross-Cutting-Logik wie Fraktion-Normalisierung wandert in separate Module (<code>parteien.py</code>, <code>bundeslaender.py</code>) und wird per Import oder Funktions-Argument injiziert.</p>"},{"location":"adr/0002-adapter-architecture/#konsequenzen","title":"Konsequenzen","text":""},{"location":"adr/0002-adapter-architecture/#positiv","title":"Positiv","text":"<ul> <li>17 Adapter sauber separierbar in ~3000 Zeilen <code>parlamente.py</code>. Jeder Adapter kommt mit seinem Reverse-Engineering-Kommentar als Docstring, was bei Re-Reads nach Wochen die Wiedereinarbeit auf Minuten bringt.</li> <li>Tests parametrisieren \u00fcber <code>ADAPTERS</code>-Registry: ein einzelner Test-Helper iteriert alle aktiven Adapter und pr\u00fcft die Akzeptanz- Kriterien (search liefert \u22653 Hits, get_document funktioniert, download_text extrahiert Text).</li> <li>Neue Bundesl\u00e4nder kommen als 100-300-Zeilen-Klasse + 1 Eintrag in <code>ADAPTERS</code> rein, ohne Ber\u00fchrung anderer Adapter.</li> </ul>"},{"location":"adr/0002-adapter-architecture/#negativ","title":"Negativ","text":"<ul> <li><code>parlamente.py</code> ist gro\u00df (~3000 Zeilen, 17 Klassen). Code-Navigation via Grep statt Auto-Imports.</li> <li>Reverse-Engineering-Findings sind in Kommentaren, nicht in einer reference-Sektion. Wenn sich das System \u00e4ndert, kann der Kommentar veralten \u2014 Mitigation: jede Reverse-Engineering-Session legt einen HAR-Trace in <code>TEMP/</code> und referenziert ihn im Class-Docstring.</li> <li>Adapter-Bugs leak nicht in andere Adapter, aber gemeinsame Fraktion-Disambiguation muss manuell konsistent gehalten werden (siehe #55-Refactor und seine Regression in Issue #60-Side-Befund: ein Refactor-Rest in <code>embeddings.py:528</code> wurde durch ein anderes Modul verbreitet).</li> </ul>"},{"location":"adr/0002-adapter-architecture/#folgen-fur-andere-adrs","title":"Folgen f\u00fcr andere ADRs","text":"<ul> <li>ADR 0001 (LLM-Citation-Binding) ist davon unabh\u00e4ngig \u2014 der Postprocess l\u00e4uft adapter-agnostisch nach jedem <code>analyze_antrag</code>.</li> <li>Eine k\u00fcnftige Module-Splitter-Entscheidung (<code>adapters/</code> als Folder) w\u00fcrde den ADR superseden, wenn <code>parlamente.py</code> \u00fcber ~5000 Zeilen w\u00e4chst.</li> </ul>"},{"location":"adr/0003-citation-property-tests/","title":"0003 \u2014 Sub-D Property-Verification: Zitate als Substring der zitierten PDF-Seite","text":"Status accepted Datum 2026-04-10 Refs Issues #50, #54, #60; tests/integration/test_citations_substring.py"},{"location":"adr/0003-citation-property-tests/#kontext","title":"Kontext","text":"<p>Der LLM-Output enth\u00e4lt pro Assessment N Zitate, jedes mit <code>text</code>, <code>quelle</code> (z.B. \"GR\u00dcNE NRW Wahlprogramm 2022, S. 58\") und <code>url</code>. Wahrscheinlich korrekt \u2014 aber wie verifizieren wir das, ohne jedes einzeln h\u00e4ndisch nachzuschlagen?</p> <p>Die naheliegenden Test-Optionen sind alle unbefriedigend:</p> <ul> <li>Mock-LLM-Tests: pr\u00fcfen das Schema, sagen aber nichts \u00fcber die inhaltliche Korrektheit.</li> <li>Snapshot-Tests der LLM-Outputs: drift mit jedem Modell-Update.</li> <li>Manuelles Stichprobenchecken: skaliert nicht \u00fcber mehrere BLs.</li> </ul>"},{"location":"adr/0003-citation-property-tests/#optionen","title":"Optionen","text":""},{"location":"adr/0003-citation-property-tests/#option-a-schema-only-tests-was-wir-vorher-hatten","title":"Option A \u2014 Schema-only Tests (was wir vorher hatten)","text":"<p>Pydantic validiert dass jedes Zitat die Felder <code>text</code>, <code>quelle</code>, <code>url</code> hat und <code>url</code> mit <code>/static/referenzen/</code> beginnt. Erkennt syntaktische Korruption, aber keine Halluzinationen.</p>"},{"location":"adr/0003-citation-property-tests/#option-b-property-test-gegen-die-echten-pdfs","title":"Option B \u2014 Property-Test gegen die echten PDFs","text":"<p>Pro Zitat in der Prod-DB: 1. <code>quelle</code> per Token-Coverage-Match auf den <code>PROGRAMME</code>-Eintrag mappen. 2. Seitennummer aus <code>quelle</code> extrahieren. 3. Per <code>fitz</code> die PDF-Seite lesen, Whitespace + Soft-Hyphen normalisieren. 4. <code>text</code> muss als Substring (oder 5-Wort-Anker) in der Seite vorkommen. 5. Bug-Klasse 17 (Cross-Bundesland-Zitat): das aufgel\u00f6ste Programm muss zum Bundesland des Antrags passen, oder ein Grundsatzprogramm sein.</p> <p>Vorteile: pr\u00fcft die einzige Eigenschaft die wirklich z\u00e4hlt \u2014 \"war das was zitiert wird auch wirklich da\". Findet Halluzinationen direkt.</p> <p>Nachteile: braucht eine lokale Kopie der <code>gwoe-antraege.db</code> und der Wahlprogramm-PDFs. Test ist Pydantic-Schema-\u00fcbergreifend (Integration, nicht Unit). Skipped sauber wenn DB nicht gemounted ist.</p>"},{"location":"adr/0003-citation-property-tests/#option-c-online-verifikation-pro-assessment-insert","title":"Option C \u2014 Online-Verifikation pro Assessment-Insert","text":"<p>Im <code>analyze_antrag</code>-Flow direkt nach LLM-Call jedes Zitat verifizieren und bei Failure abbrechen oder retry.</p> <p>Vorteile: kein \"stale data in DB\"-Risiko.</p> <p>Nachteile: f\u00fcgt Latenz und Komplexit\u00e4t in den Hot-Path. Die Verifikation ist O(N\u00d7M), wo N=Zitate und M=Wahlprogramm-Pages.</p>"},{"location":"adr/0003-citation-property-tests/#entscheidung","title":"Entscheidung","text":"<p>Option B als pytest-Integration-Test \u2014 <code>tests/integration/test_citations_substring.py</code>, parametrisiert per <code>_load_recent_assessments(limit_per_bl=5)</code> \u00d7 <code>_flat_zitate()</code>.</p> <p>Strict substring als Default-Match (Whitespace + Soft-Hyphen normalisiert, LLM-Truncation-Marker <code>...</code> toleriert), 5-Wort-Anker als Fallback f\u00fcr geringf\u00fcgige Wort-Drift wie \"LLM hat mittendrin gek\u00fcrzt\". Min-Length-Guard von 20 Zeichen verhindert false-positive Matches auf trivialen Snippets.</p> <p>Marker <code>pytestmark = pytest.mark.integration</code> \u2014 der Test l\u00e4uft nicht in der Default-Suite, sondern explizit per <code>pytest -m integration</code>. Skipped wenn <code>webapp/data/gwoe-antraege.db</code> nicht existiert (Dev-Setup ohne DB-Kopie).</p> <p>Match-Helpers (<code>_normalize</code>, <code>_is_substring</code>, <code>_resolve_quelle_to_programm_id</code>, <code>_extract_page_number</code>) sind eigene Unit-Tests in <code>TestHelpers</code> \u2014 die Match- Logik selbst ist nicht-trivial und braucht ihre Eigenkontrolle.</p>"},{"location":"adr/0003-citation-property-tests/#konsequenzen","title":"Konsequenzen","text":""},{"location":"adr/0003-citation-property-tests/#positiv","title":"Positiv","text":"<ul> <li>Findet Halluzinationen direkt: Issue #60 wurde durch den ersten Live-Lauf dieses Tests entdeckt (3 von 36 Citations failed), ohne dass ein Mensch Wahlprogramm-PDFs aufmachen musste.</li> <li>Re-runnable als Regression-Gate: nach jedem Deploy einmal <code>pytest -m integration</code> gegen die DB \u2192 0 Failures = OK.</li> <li>Test-Logik = Production-Logik: ADR 0001 Option B (<code>reconstruct_zitate</code>) nutzt identische Match-Heuristiken (<code>find_chunk_for_text</code>, <code>_normalize_for_match</code>). Damit kann der Test nichts fangen, was die Production nicht auch fangen w\u00fcrde, und umgekehrt \u2014 kein Test-/Prod-Drift.</li> </ul>"},{"location":"adr/0003-citation-property-tests/#negativ","title":"Negativ","text":"<ul> <li>Lokale DB-Kopie n\u00f6tig: vor jedem Sub-D-Run muss <code>data/gwoe-antraege.db</code> vom Container gepullt werden. CI-Integration steht aus.</li> <li>Test ist langsam-ish: ~50 Citations \u00d7 ein PDF-Open pro Programm ist bei den ~30 indexierten Programmen ~250ms im Ganzen, nicht trivial aber nicht prohibitiv.</li> <li>Token-Coverage-Heuristik f\u00fcr Quelle-zu-Programm-Mapping kann false- positive bei sehr \u00e4hnlichen Programmen werden (z.B. CDU NRW 2022 vs. CDU Niedersachsen 2022 \u2014 w\u00fcrde durch Bundesland-Bonus-Check abgefangen).</li> </ul>"},{"location":"adr/0003-citation-property-tests/#folgen-fur-andere-adrs","title":"Folgen f\u00fcr andere ADRs","text":"<ul> <li>ADR 0001 ist von ADR 0003 abh\u00e4ngig \u2014 wenn dieser Test entfernt w\u00fcrde, h\u00e4tte der LLM-Citation-Postprocess keinen Backstop und neue Halluzinations- Bug-Klassen w\u00fcrden still durchrutschen.</li> </ul>"},{"location":"adr/0004-deployment-workflow/","title":"0004 \u2014 Docker Compose Deploy mit DB-/Reports-Volume und SN-XML-Sonderpfad","text":"Status accepted Datum 2026-04-10 Refs CLAUDE.md \"Deployment\", <code>docker-compose.yml</code>, Issue #5, project_sn_xml_export"},{"location":"adr/0004-deployment-workflow/#kontext","title":"Kontext","text":"<p>Der GW\u00d6-Antragspr\u00fcfer l\u00e4uft als Docker-Container <code>gwoe-antragspruefer</code> auf einem VServer hinter Traefik (Let's Encrypt SSL, Domain <code>gwoe.toppyr.de</code>). Code wird via Git aus <code>repo.toppyr.de</code> gezogen.</p> <p>Drei Subsysteme haben unterschiedliche Lebenszyklen:</p> <ol> <li>Code (<code>app/</code>) \u2014 wird bei jedem Deploy neu kopiert.</li> <li>SQLite-Daten (<code>data/gwoe-antraege.db</code>, <code>data/embeddings.db</code>) \u2014 Source of Truth, MUSS persistent \u00fcber Deploys hinweg.</li> <li>PDF-Reports (<code>reports/*.pdf</code>) \u2014 sind generierte Artefakte, k\u00f6nnten theoretisch regeneriert werden, sind aber teuer (LLM-Calls + WeasyPrint), also auch persistent.</li> </ol> <p>Issue #5 hatte einen fr\u00fchen Schmerzpunkt: der Container-Build hat die DB \u00fcberschrieben, weil <code>data/</code> nicht aus dem Build-Context exkludiert war.</p>"},{"location":"adr/0004-deployment-workflow/#optionen","title":"Optionen","text":""},{"location":"adr/0004-deployment-workflow/#option-a-alles-im-image-manuelle-backups","title":"Option A \u2014 Alles im Image, manuelle Backups","text":"<p>Code, DB, Reports zusammen im Image. Backups via <code>docker cp</code> vor jedem Deploy.</p> <p>Nachteile: Image wird gigantisch, jeder Deploy ist ein Risk-Event, kein automatischer Persistenz-Mechanismus.</p>"},{"location":"adr/0004-deployment-workflow/#option-b-docker-volumes-fur-db-und-reports","title":"Option B \u2014 Docker-Volumes f\u00fcr DB und Reports","text":"<p><code>docker-compose.yml</code> mountet <code>./data</code> und <code>./reports</code> vom Host als Volumes. Build kopiert nur <code>app/</code> und <code>requirements.txt</code>.</p> <p>Vorteile: Host-Volumes \u00fcberleben Container-Restart und Image-Rebuild. Backups via Standard-Linux-Tools auf dem Host.</p> <p>Nachteile: Build-Context muss <code>data/</code>, <code>reports/</code>, <code>.env</code> exkludieren (siehe <code>.dockerignore</code>), sonst werden sie versehentlich ins Image kopiert und \u00fcberschreiben das gemountete Volume bei Container-Start.</p>"},{"location":"adr/0004-deployment-workflow/#option-c-externe-db-postgres","title":"Option C \u2014 Externe DB (Postgres)","text":"<p>Postgres als separater Container, gwoe-antragspruefer verbindet per asyncpg.</p> <p>Vorteile: standard, robust, Backups via pg_dump.</p> <p>Nachteile: Migration aller existierenden Queries, neue Abh\u00e4ngigkeit, mehr Operational Surface. SQLite reicht f\u00fcr die aktuelle Last (~30 Antr\u00e4ge, selten parallele Writes).</p>"},{"location":"adr/0004-deployment-workflow/#entscheidung","title":"Entscheidung","text":"<p>Option B. Docker-Compose mit Host-Volumes f\u00fcr <code>data/</code> und <code>reports/</code>. <code>.dockerignore</code> exkludiert beide aus dem Build-Context.</p>"},{"location":"adr/0004-deployment-workflow/#standard-deploy","title":"Standard-Deploy","text":"<pre><code>ssh vserver 'cd /opt/gwoe-antragspruefer && git pull && docker compose up -d --build'\n</code></pre>"},{"location":"adr/0004-deployment-workflow/#manueller-tar-upload-falls-git-workflow-blockiert-ist","title":"Manueller Tar-Upload (falls Git-Workflow blockiert ist)","text":"<pre><code>cd webapp\ntar czf /tmp/gwoe-webapp.tar.gz \\\n --exclude='venv' --exclude='__pycache__' \\\n --exclude='data' --exclude='reports' --exclude='.env' .\nscp /tmp/gwoe-webapp.tar.gz vserver:/tmp/\nssh vserver 'cd /opt/gwoe-antragspruefer && tar xzf /tmp/gwoe-webapp.tar.gz && docker compose up -d --build'\n</code></pre> <p>Beachte: <code>--exclude='data'</code> ist zwingend, sonst \u00fcberschreibt der Tar die Live-DB.</p>"},{"location":"adr/0004-deployment-workflow/#sn-xml-sonderpfad","title":"SN-XML-Sonderpfad","text":"<p>Sachsen hat keinen scrape-baren Endpoint und liest stattdessen w\u00f6chentlich manuell exportierte XML-Dumps aus EDAS. Workflow:</p> <ol> <li>User exportiert XML aus EDAS (manuell, im Browser).</li> <li><code>cp dokumente_export.xml gwoe-antragspruefer:/app/data/sn-edas/</code></li> <li><code>scp</code> aus dem Container ins Host-Volume \u2014 kein Container-Restart n\u00f6tig.</li> <li>SNEdasXmlAdapter liest die XML beim n\u00e4chsten Search-Call.</li> </ol> <p>Details in <code>~/.claude/projects/<projekt>/memory/project_sn_xml_export.md</code>.</p>"},{"location":"adr/0004-deployment-workflow/#container-zeitzone","title":"Container-Zeitzone","text":"<p>Der Container l\u00e4uft UTC, nicht CEST. DB-Timestamps in <code>assessments.created_at</code> sind UTC (kein TZ-Suffix, aber UTC). Beim Korrelieren mit Commit-Zeiten (lokal CEST = UTC+2) muss konvertiert werden, sonst flie\u00dfen falsche Schlussfolgerungen ein. Detailliert in <code>~/.claude/projects/<projekt>/memory/reference_container_utc.md</code>.</p>"},{"location":"adr/0004-deployment-workflow/#konsequenzen","title":"Konsequenzen","text":""},{"location":"adr/0004-deployment-workflow/#positiv","title":"Positiv","text":"<ul> <li>DB \u00fcberlebt jeden Deploy \u2014 verifiziert seit Issue #5 (Fix in <code>.dockerignore</code>-Update).</li> <li>Backups sind trivial: <code>tar czf gwoe-data-$(date +%F).tar.gz data/</code> auf dem Host.</li> <li>Build ist schnell (~30s f\u00fcr nicht-cached Layers), weil das Image nur Code + Dependencies enth\u00e4lt.</li> </ul>"},{"location":"adr/0004-deployment-workflow/#negativ","title":"Negativ","text":"<ul> <li><code>.dockerignore</code> ist ein Foot-Gun \u2014 wenn jemand vergisst, <code>data/</code> neu hinzuzuf\u00fcgen nach einem Refactor, kann es passieren dass der Build die Live-DB \u00fcberschreibt. Mitigation: ein dedicated Sub-D-style Test, der nach dem Build pr\u00fcft, dass die DB die erwarteten Tabellen hat \u2014 steht noch aus.</li> <li>SN-XML braucht manuelle Pflege w\u00f6chentlich. Akzeptiert weil es kein scrape-baren Endpoint gibt.</li> <li>SQLite skaliert nicht \u00fcber parallele Writes \u2014 bei mehr als ~5 gleichzeitigen Analyses w\u00fcrde es Lock-Contention geben. Aktuell l\u00e4uft alles seriell durch den Background-Task-Mechanismus, also kein Problem. Bei Wachstum auf >50 Analyses/Tag w\u00e4re ein Postgres-Migration ein separater ADR.</li> </ul>"},{"location":"adr/0004-deployment-workflow/#folgen-fur-andere-adrs","title":"Folgen f\u00fcr andere ADRs","text":"<ul> <li>ADR 0002 (Adapter-Architektur) ist davon unabh\u00e4ngig \u2014 Adapter sind reine Code-Klassen ohne State.</li> <li>Ein zuk\u00fcnftiger Postgres-Migration-ADR w\u00fcrde diesen ADR partial superseden (DB-Persistenz, nicht Reports).</li> </ul>"},{"location":"adr/template/","title":"NNNN \u2014 Titel der Entscheidung","text":"Status proposed / accepted / deprecated / superseded Datum YYYY-MM-DD Supersedes (optional) link auf vorherigen ADR Superseded by (optional) link auf neueren ADR Refs Issues, PRs, Commits"},{"location":"adr/template/#kontext","title":"Kontext","text":"<p>Welches Problem hat zu der Entscheidung gef\u00fchrt? Was ist der Stand vorher, welche Constraints sind im Spiel, welche Stakeholder sind betroffen?</p>"},{"location":"adr/template/#optionen","title":"Optionen","text":"<p>Welche Alternativen wurden ernsthaft erwogen? Mindestens 2, gerne mehr.</p>"},{"location":"adr/template/#option-a","title":"Option A \u2014 \u2026","text":"<p>Beschreibung. Vor- und Nachteile.</p>"},{"location":"adr/template/#option-b","title":"Option B \u2014 \u2026","text":"<p>Beschreibung. Vor- und Nachteile.</p>"},{"location":"adr/template/#entscheidung","title":"Entscheidung","text":"<p>Welche Option wurde gew\u00e4hlt und warum? Konkret und unmissverst\u00e4ndlich, sodass ein neuer Kollege ohne Diskussion weitermachen kann.</p>"},{"location":"adr/template/#konsequenzen","title":"Konsequenzen","text":"<p>Was wird leichter / schwerer durch diese Entscheidung? Welche Folge-Arbeiten fallen an? Welche Teile des Systems werden ber\u00fchrt?</p>"},{"location":"adr/template/#positiv","title":"Positiv","text":"<ul> <li>\u2026</li> </ul>"},{"location":"adr/template/#negativ","title":"Negativ","text":"<ul> <li>\u2026</li> </ul>"},{"location":"adr/template/#folgen-fur-andere-adrs","title":"Folgen f\u00fcr andere ADRs","text":"<ul> <li>\u2026</li> </ul>"},{"location":"archive/","title":"Archiv","text":"<p>Hier liegen historische Doku-Snapshots, die nicht mehr autoritativ sind. Sie beschreiben jeweils einen Zustand zu einem bestimmten Zeitpunkt; vieles davon ist heute \u00fcberholt.</p>"},{"location":"archive/#warum-nicht-loschen","title":"Warum nicht l\u00f6schen?","text":"<ul> <li>Sie sind als historische Quelle n\u00fctzlich, wenn man verstehen will, wie das Projekt zu einem fr\u00fcheren Zeitpunkt strukturiert war.</li> <li>Manche Findings sind in die ADRs (<code>../adr/</code>) \u00fcbernommen, andere bewusst nicht \u2014 weil sie tagesaktueller Stand waren und nie ADR-w\u00fcrdig.</li> </ul>"},{"location":"archive/#inhalt","title":"Inhalt","text":"Datei Stand Beschreibung <code>DOKUMENTATION-2026-03-24.md</code> 2026-03-24 Fr\u00fche Skript-basierte Architektur, vor dem Webapp-Migrate. Beschreibt <code>scripts/process_single.sh</code>-Workflow, der heute durch FastAPI-Backend ersetzt ist. <code>STATUS-2026-03-28.md</code> 2026-03-28 Tagesstand-Snapshot. Beschreibt v5-Prompt, Persistenz-Volume, Security-Headers \u2014 alle drei sind heute Standard. <code>README-2026-03-28.md</code> 2026-03-28 Erste Repo-README. Listet nur den NRW-Adapter (vor #2/#3/#4 und 13 weiteren BLs). Architektur-Diagramm ist veraltet."},{"location":"archive/#wenn-etwas-davon-noch-wahr-ist","title":"Wenn etwas davon noch wahr ist\u2026","text":"<p>\u2026geh\u00f6rt es in einen ADR (<code>../adr/</code>) oder die generierte Reference (geplant f\u00fcr #62 Phase 2). Bitte nicht in einen neuen Status-Snapshot ziehen. Status-Snapshots sind die Drift-Falle, die wir hier vermeiden wollen.</p>"},{"location":"archive/DOKUMENTATION-2026-03-24/","title":"GW\u00d6-Antragspr\u00fcfer \u2014 Dokumentation","text":"<p>Stand: 24. M\u00e4rz 2026, 22:15</p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#0-datenbank","title":"0. Datenbank","text":"<p>Alle Bewertungen werden in einer SQLite-Datenbank gespeichert: <code>gwoe-antraege.db</code></p> <pre><code># DB initialisieren (einmalig)\nsqlite3 gwoe-antraege.db < scripts/init-db.sql\n\n# \u00dcbersicht aller Antr\u00e4ge\nsqlite3 -header -column gwoe-antraege.db \"SELECT * FROM v_antraege_uebersicht;\"\n\n# Statistik nach Fraktion\nsqlite3 -header -column gwoe-antraege.db \"SELECT * FROM v_statistik_fraktionen;\"\n\n# Themen-Ranking\nsqlite3 -header -column gwoe-antraege.db \"SELECT * FROM v_themen_ranking;\"\n</code></pre> <p>Schema: siehe <code>scripts/init-db.sql</code> und <code>prompt-gwoe-antragspruefer-v4.md</code></p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#1-batch-workflow","title":"1. Batch-Workflow","text":""},{"location":"archive/DOKUMENTATION-2026-03-24/#vollautomatisiert","title":"Vollautomatisiert","text":"<pre><code># Einzelner Antrag\n./scripts/process_single.sh 18-18102\n\n# Batch aus Datei\n./scripts/gwoe-batch.sh --batch neue-antraege.txt\n\n# Alle neu rendern\n./scripts/gwoe-batch.sh --render-all\n</code></pre>"},{"location":"archive/DOKUMENTATION-2026-03-24/#manuell-schritt-fur-schritt","title":"Manuell (Schritt f\u00fcr Schritt)","text":"<pre><code># 1. PDF herunterladen\ncurl -sL \"https://www.landtag.nrw.de/portal/WWW/dokumentenarchiv/Dokument/MMD18-18102.pdf\" \\\n -o antraege/18-18102.pdf\n\n# 2. Text extrahieren\npdftotext -layout antraege/18-18102.pdf antraege/18-18102.txt\n\n# 3. LLM analysieren (manuell oder via API)\n# \u2192 JSON in assessments.json einf\u00fcgen\n\n# 4. data.ts aktualisieren\ncat assessments.json > quarto-report/src/data.ts # vereinfacht\n\n# 5. Quarto rendern\ncd quarto-report && npm run generate\ncd reports && quarto render gwoe-18-18102.qmd --to pdf\n\n# 6. Mit Original mergen\npdftk quarto-report/reports/gwoe-18-18102.pdf antraege/18-18102.pdf \\\n cat output output/gwoe-bericht-18-18102.pdf\n</code></pre>"},{"location":"archive/DOKUMENTATION-2026-03-24/#api-aufruf-claude","title":"API-Aufruf (Claude)","text":"<pre><code>curl -sS https://api.anthropic.com/v1/messages \\\n -H \"Content-Type: application/json\" \\\n -H \"x-api-key: $ANTHROPIC_API_KEY\" \\\n -H \"anthropic-version: 2023-06-01\" \\\n -d '{\n \"model\": \"claude-sonnet-4-20250514\",\n \"max_tokens\": 4096,\n \"system\": \"<SYSTEM_PROMPT>\",\n \"messages\": [{\"role\": \"user\", \"content\": \"<USER_PROMPT>\"}]\n }'\n</code></pre>"},{"location":"archive/DOKUMENTATION-2026-03-24/#1-uberblick","title":"1. \u00dcberblick","text":"<p>Der GW\u00d6-Antragspr\u00fcfer bewertet parlamentarische Antr\u00e4ge (aktuell: NRW Landtag) systematisch nach drei Dimensionen:</p> <ol> <li>GW\u00d6-Treue (0-10): \u00dcbereinstimmung mit der Gemeinwohl-\u00d6konomie Matrix 2.0</li> <li>Wahlprogrammtreue (0-10): Konsistenz mit dem NRW-Wahlprogramm 2022 der einreichenden Fraktion(en) und der Fraktionen in Regierungsverantwortung</li> <li>Parteiprogrammtreue (0-10): Konsistenz mit dem Grundsatzprogramm der einreichenden Fraktion(en) und der Fraktionen in Regierungsverantwortung</li> </ol> <p>Das Tool generiert professionelle PDF-Berichte mit dem offiziellen ECOnGOOD-Branding.</p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#2-gewahlte-matrix-v20-fur-gemeinden","title":"2. Gew\u00e4hlte Matrix: V2.0 f\u00fcr Gemeinden","text":""},{"location":"archive/DOKUMENTATION-2026-03-24/#warum-nicht-matrix-51","title":"Warum nicht Matrix 5.1?","text":"<p>Die GW\u00d6-Matrix 5.1 ist f\u00fcr Unternehmen konzipiert mit Ber\u00fchrungsgruppen wie \"Kund:innen\", \"Eigent\u00fcmer:innen\", \"Mitarbeitende\". Diese passen nicht zu parlamentarischen Antr\u00e4gen.</p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#warum-nicht-matrix-21a","title":"Warum nicht Matrix 2.1.A?","text":"<p>Die Matrix 2.1.A ist f\u00fcr die \"\u00f6ffentliche Hand\" allgemein. Die Matrix 2.0 f\u00fcr Gemeinden bietet einen etwas weiteren Blick und ist kompakter formuliert \u2014 ideal f\u00fcr parlamentarische Analyse.</p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#die-matrix-20-fur-gemeinden","title":"Die Matrix 2.0 f\u00fcr Gemeinden","text":"Aspekt Beschreibung Struktur 5\u00d75 (25 Felder) Fokus Kommunale Gebietsk\u00f6rperschaften Zielgruppe Kommunalpolitik, Landesantr\u00e4ge Staatsprinzipien ja (verfassungsrechtlich verankert)"},{"location":"archive/DOKUMENTATION-2026-03-24/#die-55-struktur","title":"Die 5\u00d75-Struktur","text":"<p>Spalten (Werte des Gemeinwohls + Staatsprinzipien):</p> Nr Wert Staatsprinzip 1 Menschenw\u00fcrde Rechtsstaatsprinzip 2 Solidarit\u00e4t Gemeinnutz 3 \u00d6kologische Nachhaltigkeit Umwelt-Verantwortung 4 Soziale Gerechtigkeit Sozialstaatsprinzip 5 Transparenz & Mitbestimmung Demokratie <p>Zeilen (Ber\u00fchrungsgruppen):</p> Code Gruppe Beschreibung A Ausgelagerte Betriebe, Lieferant:innen, Dienstleister:innen Externe Beschaffung, Lieferketten B Finanzpartner:innen, Geldgeber:innen, Steuerzahler:innen Haushalt, Finanzpolitik C Politische F\u00fchrung, Verwaltung, Ehrenamtliche Mandatstr\u00e4ger:innen, Mitarbeitende D B\u00fcrger:innen und Wirtschaft Interne Wirkung, Daseinsvorsorge E Staat, Gesellschaft und Natur \u00dcberregionale/langfristige Wirkung"},{"location":"archive/DOKUMENTATION-2026-03-24/#relevanz-fur-landesantrage","title":"Relevanz f\u00fcr Landesantr\u00e4ge","text":"<p>Die meisten Parlamentsantr\u00e4ge betreffen: - D-Zeile: Wirkung auf B\u00fcrger:innen und Wirtschaft im Land - E-Zeile: \u00dcberregionale oder langfristige Auswirkungen</p> <p>Prinzip: D (intern) hat Vorrang vor E (extern).</p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#3-programmtreue-bewertung","title":"3. Programmtreue-Bewertung","text":""},{"location":"archive/DOKUMENTATION-2026-03-24/#zwei-dimensionen","title":"Zwei Dimensionen","text":"<p>Antr\u00e4ge werden sowohl gegen das Wahlprogramm als auch gegen das Grundsatzprogramm der Fraktionen gepr\u00fcft:</p> Partei Wahlprogramm Grundsatzprogramm CDU NRW 2022 \"In Freiheit leben\" (2024) SPD NRW 2022 Hamburger Programm (2007) GR\u00dcNE NRW 2022 \"...zu achten und zu sch\u00fctzen...\" (2020) FDP NRW 2022 \"Verantwortung f\u00fcr die Freiheit\" (2012) AfD NRW 2022 \"Programm f\u00fcr Deutschland\" (2016)"},{"location":"archive/DOKUMENTATION-2026-03-24/#bewertungsskala","title":"Bewertungsskala","text":"Score Bedeutung 9-10 Vollst\u00e4ndige \u00dcbereinstimmung 7-8 Hohe \u00dcbereinstimmung 5-6 Partielle \u00dcbereinstimmung 3-4 Geringe \u00dcbereinstimmung 1-2 Widerspricht Teilaspekten 0 Vollst\u00e4ndiger Widerspruch"},{"location":"archive/DOKUMENTATION-2026-03-24/#4-bewertungsskala-gwo","title":"4. Bewertungsskala GW\u00d6","text":"Punkte Stufe Beschreibung 7-10 Vorbildlich Innovative Ma\u00dfnahmen, weitreichende Verbesserungen 4-6 Erfahren Erkennbare Verbesserungen, gute Ergebnisse 2-3 Fortgeschritten Erste Ma\u00dfnahmen, erste Erfolge 1 Erste Schritte Erstes Engagement 0 Basislinie Nur gesetzliche Anforderungen negativ Widerspruch Aktiver Widerspruch zu GW\u00d6-Werten"},{"location":"archive/DOKUMENTATION-2026-03-24/#matrix-feldwertung","title":"Matrix-Feldwertung","text":"<p>F\u00fcr jedes ber\u00fchrte Feld im Antrag: - <code>++</code> (+2/+3): Stark f\u00f6rdernd - <code>+</code> (+1): F\u00f6rdernd - <code>\u25cb</code> (0): Neutral - <code>\u2212</code> (-1): Widersprechend - <code>\u2212\u2212</code> (-2/-3): Stark widersprechend</p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#5-econgood-corporate-design","title":"5. ECOnGOOD Corporate Design","text":""},{"location":"archive/DOKUMENTATION-2026-03-24/#branding-aus-cd-manual-2024","title":"Branding (aus CD Manual 2024)","text":"<p>Das Layout folgt strikt dem offiziellen ECOnGOOD Corporate Design Manual (Juni 2024).</p> <p>Farbpalette (Prim\u00e4rfarben aus Logo):</p> Farbe Hex CMYK Pantone Verwendung Dunkelgrau <code>#5a5a5a</code> 0-0-0-80 425 U Flie\u00dftext, H1-\u00dcberschriften Gr\u00fcn <code>#889e33</code> 40-10-90-20 583 U Positive Werte, H3, Akzente Blau <code>#009da5</code> 100-10-40-00 320 U Hauptfarbe, H2, Links, Linien Hellgrau <code>#bfbfbf</code> 0-0-0-30 421 U Hintergr\u00fcnde, Fu\u00dfzeilen <p>Typografie: - Prim\u00e4r: Avenir (light, roman, black) - Fallback: Arial - Web: Avenir - \u00dcberschriften: - H1: Avenir black, 22pt, Dunkelgrau - H2: Avenir black, 16pt, Blau oder Gr\u00fcn - H3: Avenir black, 12pt, Blau oder Gr\u00fcn - Flie\u00dftext: Avenir roman, 10pt, Dunkelgrau - Hervorhebungen: Avenir black</p> <p>Logo: ECOnGOOD-Logo (<code>econgood-logo.png</code>) \u2014 zwei stilisierte Pusteblumen (Blau + Gr\u00fcn)</p> <p>Boxen (CD-konform): - Fl\u00e4chig farbig (Gr\u00fcn oder Blau) mit wei\u00dfem Text - Oder mit farbiger Outline und farbigem Text - Keine abgerundeten Ecken (arc=0pt)</p> <p>Tabellen: - Titelleiste in Markenfarbe (Blau) - Wei\u00dfer Text in Titelleiste, fett, Gro\u00dfbuchstaben - Innere horizontale Linien max. 1pt - 3mm Abstand zwischen Text und Linie</p> <p>Layout: - Offenes, leichtes Design - Reinwei\u00dfer Hintergrund - Kr\u00e4ftige Farben (aktiv) - Genug Wei\u00dfraum zwischen Elementen</p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#empfehlungs-symbole","title":"Empfehlungs-Symbole","text":"Empfehlung Symbol Farbe Hex Uneingeschr\u00e4nkt unterst\u00fctzen <code>[++]</code> Gr\u00fcn <code>#889e33</code> Unterst\u00fctzen mit \u00c4nderungen <code>[+]</code> Blau <code>#009da5</code> \u00dcberarbeiten <code>[!]</code> Orange <code>#F7941D</code> Ablehnen <code>[X]</code> Rot <code>#d00000</code>"},{"location":"archive/DOKUMENTATION-2026-03-24/#6-technische-umsetzung","title":"6. Technische Umsetzung","text":""},{"location":"archive/DOKUMENTATION-2026-03-24/#stack","title":"Stack","text":"<ul> <li>TypeScript \u2014 Datentypen, Generator</li> <li>Quarto \u2014 Markdown \u2192 PDF (via LuaLaTeX)</li> <li>TikZ \u2014 Grafische Elemente (Score-Balken)</li> <li>pdftk \u2014 PDF-Merge (Bewertung + Original-Antrag)</li> </ul>"},{"location":"archive/DOKUMENTATION-2026-03-24/#projektstruktur","title":"Projektstruktur","text":"<pre><code>quarto-report/\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 types.ts # Datentypen, Matrix-Labels\n\u2502 \u251c\u2500\u2500 data.ts # Testbewertungen\n\u2502 \u2514\u2500\u2500 generate-qmd.ts # Quarto-Generator\n\u251c\u2500\u2500 reports/ # Generierte .qmd + .pdf\n\u251c\u2500\u2500 econgood-logo.png # Logo (aus CD Manual)\n\u2514\u2500\u2500 package.json\n</code></pre>"},{"location":"archive/DOKUMENTATION-2026-03-24/#workflow","title":"Workflow","text":"<ol> <li>LLM analysiert Antrag \u2192 JSON-Ausgabe</li> <li>JSON \u2192 TypeScript Assessment-Objekt</li> <li>Generator erzeugt <code>.qmd</code> (Quarto Markdown)</li> <li>Quarto rendert \u2192 PDF</li> <li>pdftk merged Bewertung + Original-Antrag</li> </ol>"},{"location":"archive/DOKUMENTATION-2026-03-24/#7-kontext-dateien","title":"7. Kontext-Dateien","text":"<p>Im Ordner <code>kontext/</code>:</p> Datei Beschreibung <code>gwoe-matrix-2.0.md</code> Matrix 2.0 f\u00fcr Gemeinden \u2014 vollst\u00e4ndige Dokumentation <code>gwoe-matrix-2.0-gemeinden.pdf</code> Offizielles Matrix-PDF <code>gwoe-arbeitsbuch-gemeinden-2.0.pdf</code> Arbeitsbuch zur Matrix <code>parteiprogramme.md</code> Kurzreferenz Wahlprogramme + Grundsatzprogramme <code>cdu-grundsatzprogramm-2024.pdf</code> CDU \"In Freiheit leben\" <code>spd-hamburger-programm-2007.pdf</code> SPD Hamburger Programm <code>gruene-grundsatzprogramm-2020.pdf</code> Gr\u00fcne Grundsatzprogramm <code>fdp-grundsatzprogramm-2012.pdf</code> FDP \"Verantwortung f\u00fcr die Freiheit\" <code>afd-grundsatzprogramm-2016.pdf</code> AfD \"Programm f\u00fcr Deutschland\" <code>wahlprogramme-nrw-2022.md</code> NRW Wahlprogramme 2022"},{"location":"archive/DOKUMENTATION-2026-03-24/#8-getestete-antrage","title":"8. Getestete Antr\u00e4ge","text":"Drucksache Titel GW\u00d6 Empfehlung 18/18081 Schlechte-Kita-Gesetz stoppen (SPD) 8/10 Uneingeschr\u00e4nkt unterst\u00fctzen 18/18088 Iran-Solidarit\u00e4t (alle) 9/10 Uneingeschr\u00e4nkt unterst\u00fctzen 18/18094 Versorgungssicherheit/Fracking (AfD) 1/10 Ablehnen 18/18099 Kultur-Schecks (CDU/Gr\u00fcne) 6/10 Unterst\u00fctzen mit \u00c4nderungen 18/18104 KI-Korrektur (FDP) 6/10 Unterst\u00fctzen mit \u00c4nderungen"},{"location":"archive/DOKUMENTATION-2026-03-24/#9-gendering","title":"9. Gendering","text":"<p>Standard: <code>:</code> (Doppelpunkt) statt <code>*</code> (Sternchen)</p> <p>Beispiele: - B\u00fcrger:innen - Lieferant:innen - Mitarbeiter:innen</p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#10-quellen","title":"10. Quellen","text":"<ul> <li>GW\u00d6-Matrix 2.0: https://germany.econgood.org/wp-content/uploads/sites/8/2024/04/Matrix-Gemeinwohl-Bilanzierung-Gemeinden-V2.0.pdf</li> <li>Arbeitsbuch 2.0: https://austria.econgood.org/wp-content/uploads/sites/7/2022/01/Arbeitsbuch-Gemeinden_2.pdf</li> <li>ECOnGOOD CD Manual: Corporate Design/2024_ECOnGOOD_Manual_DE_Juni_02.pdf</li> <li>GW\u00d6 Deutschland: https://germany.econgood.org</li> <li>Kontakt: Gemeinde@ecogood.org</li> </ul>"},{"location":"archive/DOKUMENTATION-2026-03-24/#11-changelog","title":"11. Changelog","text":"Datum Version \u00c4nderung 24.03.2026 v4 Matrix 2.0 statt 2.1.A, Parteiprogrammtreue erg\u00e4nzt, <code>:</code> beim Gendern, ECOnGOOD-Logo 23.03.2026 v3 Umstellung auf Matrix 2.1.A (5\u00d75), GW\u00d6-Branding 23.03.2026 v2 Vollst\u00e4ndiger Kontext (Matrix + Wahlprogramme) 23.03.2026 v1 Initiale Version mit Matrix 5.1 <p>Projektordner: <code>~/Nextcloud/dotty/projekte/2026-03-23 GW\u00d6-Antragspr\u00fcfer _WIP_/</code> Telegram: https://t.me/c/3823618505/4247</p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#session-28032026-nachmittagabend","title":"Session 28.03.2026 (Nachmittag/Abend)","text":""},{"location":"archive/DOKUMENTATION-2026-03-24/#security","title":"Security","text":"<ul> <li>CSP, X-Frame-Options, X-XSS-Protection etc. als Middleware</li> <li>/docs deaktiviert</li> </ul>"},{"location":"archive/DOKUMENTATION-2026-03-24/#v5-prompt-mit-verbesserungsvorschlagen","title":"v5-Prompt mit Verbesserungsvorschl\u00e4gen","text":"<p>Neues Format: <pre><code>{\n \"original\": \"Zitat aus Antrag\",\n \"vorschlag\": \"Verbesserter Text mit **Markierungen**\",\n \"begruendung\": \"GW\u00d6-Begr\u00fcndung\"\n}\n</code></pre></p>"},{"location":"archive/DOKUMENTATION-2026-03-24/#ui-erweiterungen","title":"UI-Erweiterungen","text":"<ul> <li>Partei-Filter (Dropdown)</li> <li>Tag-Wolke mit Multi-Select</li> <li>Partei-Durchschnitte in Stats-Bar</li> <li>Kombinierte Filter (Score + Partei)</li> </ul>"},{"location":"archive/DOKUMENTATION-2026-03-24/#infrastruktur-fixes","title":"Infrastruktur-Fixes","text":"<ul> <li>DB-Persistenz: Dockerfile kopiert data/ nicht mehr</li> <li>JSON-Import deaktiviert</li> <li>Retry-Logik f\u00fcr LLM (3 Versuche)</li> </ul>"},{"location":"archive/DOKUMENTATION-2026-03-24/#repository","title":"Repository","text":"<ul> <li>https://repo.toppyr.de/tobias/gwoe-antragspruefer</li> <li>MIT License</li> <li>Vollst\u00e4ndige Dokumentation</li> </ul>"},{"location":"archive/README-2026-03-28/","title":"GW\u00d6-Antragspr\u00fcfer \u2014 Projektdokumentation","text":"<p>Stand: 28.03.2026, 23:58 Uhr Telegram-Topic: \ud83c\udf31 GW\u00d6-Antragspr\u00fcfer (thread_id 4247)</p>"},{"location":"archive/README-2026-03-28/#projektziel","title":"\ud83c\udfaf Projektziel","text":"<p>Automatische Bewertung von Parlamentsantr\u00e4gen nach der Gemeinwohl-\u00d6konomie (GW\u00d6) Matrix 2.0 f\u00fcr Gemeinden. Das Tool analysiert Antr\u00e4ge aus Landesparlamenten und bewertet sie nach GW\u00d6-Kriterien, vergleicht mit Wahl- und Parteiprogrammen und schl\u00e4gt konkrete Textverbesserungen vor.</p>"},{"location":"archive/README-2026-03-28/#live-system","title":"\ud83c\udf10 Live-System","text":"Was URL Webapp https://gwoe.toppyr.de Git-Repository https://repo.toppyr.de/tobias/gwoe-antragspruefer Server VServer 152.53.119.77 Container <code>/opt/gwoe-antragspruefer/</code>"},{"location":"archive/README-2026-03-28/#architektur","title":"\ud83c\udfd7\ufe0f Architektur","text":"<pre><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Frontend \u2502\n\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n\u2502 \u2502 Durchsuchen \u2502 \u2502 \ud83c\udff7\ufe0f Tags \u2502 \u2502 Pr\u00fcfen \u2502 \u2502\n\u2502 \u2502 (Liste) \u2502 \u2502 (Wolke) \u2502 \u2502 (Upload/Analyse) \u2502 \u2502\n\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 FastAPI Backend \u2502\n\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n\u2502 \u2502 /api/ \u2502 \u2502 /api/ \u2502 \u2502 /api/ \u2502 \u2502 /api/ \u2502 \u2502\n\u2502 \u2502assessments\u2502 \u2502search- \u2502 \u2502analyze- \u2502 \u2502assessment/ \u2502 \u2502\n\u2502 \u2502 \u2502 \u2502landtag \u2502 \u2502drucksache\u2502 \u2502pdf \u2502 \u2502\n\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502 \u2502 \u2502\n \u25bc \u25bc \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 SQLite \u2502 \u2502 OPAL API \u2502 \u2502 DashScope (Qwen) \u2502\n\u2502 gwoe-antraege\u2502 \u2502 (Landtag \u2502 \u2502 qwen-plus-latest \u2502\n\u2502 .db \u2502 \u2502 NRW) \u2502 \u2502 (LLM Analyse) \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n</code></pre>"},{"location":"archive/README-2026-03-28/#projektstruktur","title":"\ud83d\udcc1 Projektstruktur","text":"<pre><code>webapp/\n\u251c\u2500\u2500 app/\n\u2502 \u251c\u2500\u2500 main.py # FastAPI-Endpoints + Security Middleware\n\u2502 \u251c\u2500\u2500 analyzer.py # LLM-Analyse mit v5-Prompt + Retry-Logik\n\u2502 \u251c\u2500\u2500 database.py # SQLite (aiosqlite) Persistenz\n\u2502 \u251c\u2500\u2500 models.py # Pydantic Assessment-Model\n\u2502 \u251c\u2500\u2500 parlamente.py # OPAL-Adapter f\u00fcr NRW Landtag\n\u2502 \u251c\u2500\u2500 report.py # PDF-Generierung (WeasyPrint)\n\u2502 \u251c\u2500\u2500 config.py # Settings + Environment\n\u2502 \u251c\u2500\u2500 embeddings.py # Wahlprogramm-Embeddings (optional)\n\u2502 \u251c\u2500\u2500 kontext/\n\u2502 \u2502 \u251c\u2500\u2500 gwoe-matrix-2.0.md # GW\u00d6-Matrix Referenz\n\u2502 \u2502 \u251c\u2500\u2500 wahlprogramme-nrw-2022.md # Zusammenfassungen\n\u2502 \u2502 \u251c\u2500\u2500 parteiprogramme.md # Grundsatzprogramme\n\u2502 \u2502 \u2514\u2500\u2500 *-nrw-2022-paged.txt # Volltext (paginiert)\n\u2502 \u251c\u2500\u2500 templates/\n\u2502 \u2502 \u251c\u2500\u2500 index.html # Haupt-UI (Jinja2)\n\u2502 \u2502 \u2514\u2500\u2500 quellen.html # Quellen\u00fcbersicht\n\u2502 \u2514\u2500\u2500 static/\n\u2502 \u2514\u2500\u2500 referenzen/ # Original-PDFs der Programme\n\u251c\u2500\u2500 data/ # SQLite-DBs (Docker Volume)\n\u2502 \u2514\u2500\u2500 gwoe-antraege.db # Haupt-Datenbank\n\u251c\u2500\u2500 reports/ # Generierte PDFs (Docker Volume)\n\u251c\u2500\u2500 docker-compose.yml # Traefik + Let's Encrypt\n\u251c\u2500\u2500 Dockerfile # Python 3.12 + WeasyPrint\n\u251c\u2500\u2500 requirements.txt\n\u251c\u2500\u2500 .env.example\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 .dockerignore\n\u251c\u2500\u2500 LICENSE (MIT)\n\u2514\u2500\u2500 README.md\n</code></pre>"},{"location":"archive/README-2026-03-28/#features-stand-28032026","title":"\u2728 Features (Stand 28.03.2026)","text":""},{"location":"archive/README-2026-03-28/#analyse","title":"Analyse","text":"<ul> <li>[x] GW\u00d6-Score (0-10) mit Matrix-Zuordnung</li> <li>[x] Bewertungssymbole (++/+/\u25cb/\u2212/\u2212\u2212) pro Matrix-Feld</li> <li>[x] Wahlprogrammtreue (0-10) pro Fraktion</li> <li>[x] Parteiprogrammtreue (0-10) pro Fraktion</li> <li>[x] Verbesserungsvorschl\u00e4ge im Redline-Format</li> <li>Original-Zitat aus dem Antrag</li> <li>Konkreter Verbesserungsvorschlag</li> <li>GW\u00d6-Begr\u00fcndung</li> <li>[x] Themen-Tags f\u00fcr Kategorisierung</li> <li>[x] Retry-Logik (3 Versuche) bei JSON-Parse-Fehlern</li> </ul>"},{"location":"archive/README-2026-03-28/#ui","title":"UI","text":"<ul> <li>[x] Score-Filter (Alle / 8-10 / 5-7 / 0-4)</li> <li>[x] Partei-Filter (Dropdown)</li> <li>[x] Durchschnittswerte pro Partei (kompakte Stats-Bar)</li> <li>[x] Tag-Wolke mit Multi-Select (Schnittmenge)</li> <li>[x] Landtag-Suche via OPAL-API</li> <li>[x] \"Jetzt pr\u00fcfen\"-Button f\u00fcr neue Antr\u00e4ge</li> <li>[x] Detail-Ansicht mit allen Bewertungen</li> <li>[x] PDF-Download pro Antrag</li> </ul>"},{"location":"archive/README-2026-03-28/#security","title":"Security","text":"<ul> <li>[x] Content-Security-Policy</li> <li>[x] X-Frame-Options: DENY</li> <li>[x] X-Content-Type-Options: nosniff</li> <li>[x] X-XSS-Protection</li> <li>[x] Referrer-Policy</li> <li>[x] Permissions-Policy</li> <li>[x] /docs, /redoc, /openapi.json deaktiviert</li> </ul>"},{"location":"archive/README-2026-03-28/#infrastruktur","title":"Infrastruktur","text":"<ul> <li>[x] Docker Compose mit Traefik Reverse Proxy</li> <li>[x] Let's Encrypt SSL</li> <li>[x] Persistente SQLite-DB (Volume, \u00fcberlebt Container-Neustarts)</li> <li>[x] Git-Repository auf repo.toppyr.de</li> </ul>"},{"location":"archive/README-2026-03-28/#konfiguration","title":"\ud83d\udd27 Konfiguration","text":""},{"location":"archive/README-2026-03-28/#environment-variablen-env","title":"Environment-Variablen (.env)","text":"<pre><code>DASHSCOPE_API_KEY=sk-... # Alibaba DashScope API\nKEYCLOAK_URL=https://sso.toppyr.de # Optional: SSO\nKEYCLOAK_REALM=collaboration\nKEYCLOAK_CLIENT_ID=gwoe-antragspruefer\n</code></pre>"},{"location":"archive/README-2026-03-28/#llm-modelle","title":"LLM-Modelle","text":"Modell Verwendung Kosten <code>qwen-plus-latest</code> Standard (Free Tier) Kostenlos <code>qwen-plus</code> Fallback (Paid) ~$0.80/MTok <code>qwen-max</code> Premium ~$2.40/MTok"},{"location":"archive/README-2026-03-28/#api-endpoints","title":"\ud83d\udcca API-Endpoints","text":"Methode Pfad Beschreibung GET <code>/</code> Web-UI GET <code>/health</code> Health Check GET <code>/quellen</code> Quellen\u00fcbersicht GET <code>/api/assessments</code> Alle Bewertungen (JSON) GET <code>/api/assessment?drucksache=18/12345</code> Einzelne Bewertung GET <code>/api/search?q=Klima</code> Interne DB-Suche GET <code>/api/search-landtag?q=Klima</code> Landtag OPAL-Suche POST <code>/api/analyze-drucksache</code> Neue Analyse starten GET <code>/api/assessment/pdf?drucksache=18/12345</code> PDF-Download GET <code>/api/bundeslaender</code> Verf\u00fcgbare Bundesl\u00e4nder GET <code>/status/{job_id}</code> Job-Status abfragen"},{"location":"archive/README-2026-03-28/#prompt-v5-kern-features","title":"\ud83e\udde0 Prompt v5 \u2014 Kern-Features","text":"<p>Der Analyse-Prompt fordert:</p> <ol> <li>GW\u00d6-Treue (0-10) mit Matrix-Zuordnung:</li> <li>Matrix-Feld (z.B. \"D1 Menschenw\u00fcrde\")</li> <li>Symbol (++/+/\u25cb/\u2212/\u2212\u2212)</li> <li> <p>Kurzbegr\u00fcndung</p> </li> <li> <p>Programmtreue f\u00fcr Antragsteller UND Regierungsfraktionen:</p> </li> <li>Wahlprogramm-Score (0-10)</li> <li>Parteiprogramm-Score (0-10)</li> <li> <p>Begr\u00fcndungen</p> </li> <li> <p>Verbesserungsvorschl\u00e4ge (max. 3): <pre><code>{\n \"original\": \"Zitat aus dem Antrag\",\n \"vorschlag\": \"Konkret **verbesserter** Text\",\n \"begruendung\": \"St\u00e4rkt GW\u00d6-Wert X durch Y\"\n}\n</code></pre></p> </li> <li> <p>Themen-Tags f\u00fcr Kategorisierung</p> </li> </ol>"},{"location":"archive/README-2026-03-28/#deployment","title":"\ud83d\ude80 Deployment","text":""},{"location":"archive/README-2026-03-28/#erstinstallation","title":"Erstinstallation","text":"<pre><code>ssh vserver\ncd /opt\ngit clone https://repo.toppyr.de/tobias/gwoe-antragspruefer.git\ncd gwoe-antragspruefer\n\n# .env erstellen\ncp .env.example .env\nnano .env # DASHSCOPE_API_KEY eintragen\n\n# Starten\ndocker compose up -d\n</code></pre>"},{"location":"archive/README-2026-03-28/#update","title":"Update","text":"<pre><code># Lokal\ncd ~/Nextcloud/dotty/projekte/2026-03-23\\ GW\u00d6-Antragspr\u00fcfer\\ _WIP_/webapp\ngit add . && git commit -m \"Update\" && git push\n\n# Auf Server\nssh vserver 'cd /opt/gwoe-antragspruefer && git pull && docker compose up -d --build'\n</code></pre>"},{"location":"archive/README-2026-03-28/#manuelles-deploy-ohne-git-auf-server","title":"Manuelles Deploy (ohne Git auf Server)","text":"<pre><code>cd ~/Nextcloud/dotty/projekte/2026-03-23\\ GW\u00d6-Antragspr\u00fcfer\\ _WIP_/webapp\ntar czf /tmp/gwoe-webapp.tar.gz --exclude='venv' --exclude='__pycache__' --exclude='data' --exclude='reports' --exclude='.env' .\nscp /tmp/gwoe-webapp.tar.gz vserver:/tmp/\nssh vserver 'cd /opt/gwoe-antragspruefer && tar xzf /tmp/gwoe-webapp.tar.gz && docker compose up -d --build'\n</code></pre>"},{"location":"archive/README-2026-03-28/#statistiken-28032026","title":"\ud83d\udcc8 Statistiken (28.03.2026)","text":"Metrik Wert Analysierte Antr\u00e4ge 20 \u00d8 GW\u00d6-Score gesamt 4.6 \u00d8 SPD 7.7 \u00d8 GR\u00dcNE 6.0 \u00d8 CDU 6.0 \u00d8 FDP 4.8 \u00d8 AfD 1.5"},{"location":"archive/README-2026-03-28/#bekannte-issues-todos","title":"\ud83d\udc1b Bekannte Issues / TODOs","text":""},{"location":"archive/README-2026-03-28/#offen","title":"Offen","text":"<ul> <li>[ ] Keycloak SSO-Integration aktivieren</li> <li>[ ] Weitere Bundesl\u00e4nder (BY, BW) anbinden</li> <li>[ ] Batch-Analyse f\u00fcr viele Antr\u00e4ge</li> <li>[ ] Wahlprogramm-Zitate mit Seitenzahlen</li> <li>[ ] Export als CSV/Excel</li> </ul>"},{"location":"archive/README-2026-03-28/#gelost-28032026","title":"Gel\u00f6st (28.03.2026)","text":"<ul> <li>[x] JSON-Parse-Fehler bei LLM-Output \u2192 Retry-Logik</li> <li>[x] DB nicht persistent \u2192 Dockerfile gefixt, data/ als Volume</li> <li>[x] Alte Assessments \u00fcberschreiben neue \u2192 JSON-Import deaktiviert</li> <li>[x] Partei-Filter zeigt \"Suchfehler\" \u2192 JS-Bug gefixt</li> </ul>"},{"location":"archive/README-2026-03-28/#changelog","title":"\ud83d\udcdd Changelog","text":""},{"location":"archive/README-2026-03-28/#v100-28032026","title":"v1.0.0 (28.03.2026)","text":"<ul> <li>Initial Release</li> <li>GW\u00d6-Matrix 2.0 Analyse f\u00fcr NRW</li> <li>Verbesserungsvorschl\u00e4ge im Redline-Format</li> <li>Tag-Wolke mit Multi-Select</li> <li>Partei-Filter + Durchschnittswerte</li> <li>Security Headers</li> <li>Docker Deployment</li> </ul>"},{"location":"archive/README-2026-03-28/#quellen","title":"\ud83d\udcda Quellen","text":"<ul> <li>GW\u00d6-Matrix 2.0 f\u00fcr Gemeinden (Arbeitsbuch)</li> <li>ECOnGOOD Corporate Design Manual 2024</li> <li>NRW OPAL Parlamentsdokumentation</li> <li>Wahlprogramme NRW 2022 (CDU, GR\u00dcNE, SPD, FDP, AfD)</li> <li>Grundsatzprogramme der Parteien</li> </ul> <p>Entwickelt von Tobias R\u00f6del mit Unterst\u00fctzung von Dotty \ud83d\udc7b</p>"},{"location":"archive/STATUS-2026-03-28/","title":"GW\u00d6-Antragspr\u00fcfer \u2014 Status 28.03.2026 (Final)","text":""},{"location":"archive/STATUS-2026-03-28/#heute-erledigt-28032026","title":"\u2705 Heute erledigt (28.03.2026)","text":""},{"location":"archive/STATUS-2026-03-28/#vormittag","title":"Vormittag","text":"<ul> <li>[x] \"Jetzt pr\u00fcfen\" aus Landtag-Suche implementiert</li> <li>[x] Zitat-System mit Wahlprogramm-Seitenreferenzen</li> <li>[x] 5 Original-Wahlprogramme integriert (CDU, SPD, Gr\u00fcne, FDP, AfD)</li> </ul>"},{"location":"archive/STATUS-2026-03-28/#nachmittagabend","title":"Nachmittag/Abend","text":"<ul> <li>[x] Security Headers eingebaut (CSP, X-Frame-Options, etc.)</li> <li>[x] /docs, /redoc, /openapi.json deaktiviert</li> <li>[x] v5-Prompt mit Verbesserungsvorschl\u00e4gen im Redline-Format</li> <li>[x] Retry-Logik (3 Versuche) bei JSON-Parse-Fehlern</li> <li>[x] Partei-Filter (Dropdown)</li> <li>[x] Tag-Wolke mit Multi-Select (Schnittmenge-Filter)</li> <li>[x] Partei-Durchschnittswerte in kompakter Stats-Bar</li> <li>[x] Persistente DB \u2014 Dockerfile gefixt, data/ als Volume</li> <li>[x] JSON-Import deaktiviert (DB ist Source of Truth)</li> <li>[x] 20 Test-Antr\u00e4ge neu analysiert mit v5-Prompt</li> <li>[x] Git-Repository auf repo.toppyr.de gepusht</li> <li>[x] Dokumentation komplett</li> </ul>"},{"location":"archive/STATUS-2026-03-28/#live-system","title":"\ud83c\udf10 Live-System","text":"Was URL Webapp https://gwoe.toppyr.de Repository https://repo.toppyr.de/tobias/gwoe-antragspruefer Server VServer 152.53.119.77 (<code>/opt/gwoe-antragspruefer/</code>)"},{"location":"archive/STATUS-2026-03-28/#aktueller-stand","title":"\ud83d\udcca Aktueller Stand","text":"Metrik Wert Analysierte Antr\u00e4ge 20 Alle mit Verbesserungsvorschl\u00e4gen \u2705 \u00d8 GW\u00d6-Score gesamt 4.6 \u00d8 SPD 7.7 \u00d8 CDU/GR\u00dcNE 6.0 \u00d8 FDP 4.8 \u00d8 AfD 1.5"},{"location":"archive/STATUS-2026-03-28/#technische-anderungen-heute","title":"\ud83d\udd27 Technische \u00c4nderungen heute","text":""},{"location":"archive/STATUS-2026-03-28/#security-mainpy","title":"Security (main.py)","text":"<pre><code># Middleware hinzugef\u00fcgt:\n- X-Content-Type-Options: nosniff\n- X-Frame-Options: DENY\n- X-XSS-Protection: 1; mode=block\n- Content-Security-Policy\n- Referrer-Policy: strict-origin-when-cross-origin\n- Permissions-Policy: geolocation=(), microphone=(), camera=()\n\n# FastAPI Docs deaktiviert:\ndocs_url=None, redoc_url=None, openapi_url=None\n</code></pre>"},{"location":"archive/STATUS-2026-03-28/#retry-logik-analyzerpy","title":"Retry-Logik (analyzer.py)","text":"<pre><code># 3 Versuche bei JSON-Parse-Fehlern\n# Temperatur steigt pro Versuch (0.3 \u2192 0.4 \u2192 0.5)\nmax_retries = 3\nfor attempt in range(max_retries):\n # ... LLM Call ...\n try:\n data = json.loads(content)\n return Assessment.model_validate(data)\n except json.JSONDecodeError:\n continue # Retry\n</code></pre>"},{"location":"archive/STATUS-2026-03-28/#persistente-db-dockerfile","title":"Persistente DB (Dockerfile)","text":"<pre><code># VORHER (kaputt):\nCOPY data/ ./data/\nCOPY reports/ ./reports/\n\n# NACHHER (korrekt):\n# data/ und reports/ werden als Volumes gemountet\nRUN mkdir -p /app/data /app/reports\n</code></pre>"},{"location":"archive/STATUS-2026-03-28/#deploy-workflow","title":"Deploy-Workflow","text":"<pre><code># .tarignore und --exclude verhindern \u00dcberschreiben der Server-DB\ntar czf ... --exclude='data' --exclude='reports' ...\n</code></pre>"},{"location":"archive/STATUS-2026-03-28/#repository-struktur","title":"\ud83d\udcc1 Repository-Struktur","text":"<pre><code>gwoe-antragspruefer/\n\u251c\u2500\u2500 app/\n\u2502 \u251c\u2500\u2500 main.py # FastAPI + Security\n\u2502 \u251c\u2500\u2500 analyzer.py # LLM + Retry\n\u2502 \u251c\u2500\u2500 database.py # SQLite\n\u2502 \u251c\u2500\u2500 models.py # Pydantic\n\u2502 \u251c\u2500\u2500 parlamente.py # OPAL-Adapter\n\u2502 \u251c\u2500\u2500 report.py # PDF\n\u2502 \u251c\u2500\u2500 kontext/ # GW\u00d6-Matrix, Programme\n\u2502 \u251c\u2500\u2500 templates/ # Jinja2 UI\n\u2502 \u2514\u2500\u2500 static/referenzen/ # Original-PDFs\n\u251c\u2500\u2500 docker-compose.yml\n\u251c\u2500\u2500 Dockerfile\n\u251c\u2500\u2500 requirements.txt\n\u251c\u2500\u2500 .env.example\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 LICENSE (MIT)\n\u2514\u2500\u2500 README.md\n</code></pre>"},{"location":"archive/STATUS-2026-03-28/#offene-todos","title":"\ud83d\udccb Offene TODOs","text":""},{"location":"archive/STATUS-2026-03-28/#prio-1","title":"Prio 1","text":"<ul> <li>[ ] Keycloak SSO aktivieren</li> <li>[ ] Batch-Analyse f\u00fcr viele Antr\u00e4ge optimieren</li> </ul>"},{"location":"archive/STATUS-2026-03-28/#prio-2","title":"Prio 2","text":"<ul> <li>[ ] Weitere Bundesl\u00e4nder (BY, BW)</li> <li>[ ] CSV/Excel-Export</li> <li>[ ] Zitat-Highlighting in PDFs</li> </ul>"},{"location":"archive/STATUS-2026-03-28/#nice-to-have","title":"Nice to have","text":"<ul> <li>[ ] Historische Trend-Analyse</li> <li>[ ] Newsletter-Integration</li> <li>[ ] API-Rate-Limiting f\u00fcr \u00f6ffentliche Nutzung</li> </ul>"},{"location":"archive/STATUS-2026-03-28/#deployment-befehle","title":"\ud83d\ude80 Deployment-Befehle","text":""},{"location":"archive/STATUS-2026-03-28/#standard-update","title":"Standard-Update","text":"<pre><code># Lokal committen\ncd ~/Nextcloud/dotty/projekte/2026-03-23\\ GW\u00d6-Antragspr\u00fcfer\\ _WIP_/webapp\ngit add . && git commit -m \"...\" && git push\n\n# Server aktualisieren\nssh vserver 'cd /opt/gwoe-antragspruefer && git pull && docker compose up -d --build'\n</code></pre>"},{"location":"archive/STATUS-2026-03-28/#manuell-ohne-server-git","title":"Manuell (ohne Server-Git)","text":"<pre><code>cd ~/Nextcloud/dotty/projekte/2026-03-23\\ GW\u00d6-Antragspr\u00fcfer\\ _WIP_/webapp\ntar czf /tmp/gwoe-webapp.tar.gz --exclude='venv' --exclude='__pycache__' --exclude='data' --exclude='reports' --exclude='.env' .\nscp /tmp/gwoe-webapp.tar.gz vserver:/tmp/\nssh vserver 'cd /opt/gwoe-antragspruefer && tar xzf /tmp/gwoe-webapp.tar.gz && docker compose up -d --build'\n</code></pre>"},{"location":"archive/STATUS-2026-03-28/#logs-prufen","title":"Logs pr\u00fcfen","text":"<pre><code>ssh vserver 'docker logs gwoe-antragspruefer --tail 50'\n</code></pre>"},{"location":"archive/STATUS-2026-03-28/#db-status","title":"DB-Status","text":"<pre><code>ssh vserver 'docker exec gwoe-antragspruefer python -c \"\nimport sqlite3\nconn = sqlite3.connect(\\\"/app/data/gwoe-antraege.db\\\")\ncur = conn.cursor()\ncur.execute(\\\"SELECT COUNT(*) FROM assessments\\\")\nprint(f\\\"Assessments: {cur.fetchone()[0]}\\\")\n\"'\n</code></pre> <p>Projekt-Status: FUNKTIONSF\u00c4HIG \u2705</p> <p>Dokumentiert von Dotty, 28.03.2026, 23:58 Uhr</p>"}]} |