Gemeinsames Gedächtnis: Dinge, die man **nicht aus dem Code ablesen kann** und die schon einmal Zeit gekostet haben.
Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik. Veraltetes löschen, nicht auskommentieren.
## Umgebung und Werkzeuge
- **QGIS 3.40 ist seit Januar 2026 End of Life.** Aktueller LTR ist 3.44, Stable 4.2 (wird 25.09.2026 LTR). `workspace.toml` und alle `metadata.template` stehen noch auf `3.40`– offene Produktentscheidung, siehe `docs/konzept/07_qgis_tests_plan.md` Abschnitt 2. (04.09.2026, QGIS-Release-Fahrplan)
- **Zwei Qt-Welten.** QGIS 3.40/3.44 laufen mit Qt5/PyQt5, QGIS 4 mit Qt6/PyQt6. `qt_wrapper` lädt PyQt6 zuerst, dann PyQt5, sonst Mock. Enum-Zugriffe unterscheiden sich (`Qt.AlignmentFlag.AlignTop` statt `Qt.AlignTop`), deshalb nur Wrapper-Aliase verwenden. (09/2026, Code)
- **Mock-Modus ist kein QGIS.** `python scripts/run_tests.py` läuft in Sekunden, prüft aber nur Logik gegen Mock-Klassen. Geometrieoperationen, WFS, Layouts und Signale müssen in QGIS geprüft werden (`tests/test_qgis.bat` unter Windows). Manuelle QGIS-Tests im PR dokumentieren. (09/2026)
- **`openpyxl` ist eine harte Abhängigkeit** von `sn_basis` (Excel-Import) und unter Linux-QGIS oft nicht installiert (Issue Verfahrensgebiet #21). `requests` ist nur optionaler Fallback. Es gibt keine `requirements.txt`. (09/2026)
- **QGIS lädt Plugins in nicht garantierter Reihenfolge.** Fachplugins dürfen in `initGui()` nicht davon ausgehen, dass `sn_basis` fertig ist; `sn_basis` ruft `initGui()` aller `sn_*` nach. (Code `main.py`)
- **Exit-Crash unter QGIS 3.40** entstand durch nicht getrennte Projektsignale beim Unload. Regel: jeder Tab/Manager, der Signale verbindet, bietet `cleanup()`/`teardown_...()` und wird beim Schließen aufgerufen. (Basis, `assets/plan-qgis340ExitCrashFix.prompt.md`)
- **`.vscode/` gehört nicht ins Repository.** Im Projektwissen wurde er am 04.09.2026 entfernt; brauchbare Einstellungen stehen jetzt in `docs/leitfaden/04_werkzeuge_einrichten.md` Abschnitt 4.2 zum Kopieren in die Benutzereinstellungen. In `Plugin_SN_Basis`, `Plugin_SN_Plan41`, `Plugin_SN_Verfahrensgebiet` und `Plugin_SN_Widmung` liegt er noch und enthält Pfade eines einzelnen Arbeitsplatzes (OSGeo-Installationspfad, persönliches Benutzerprofil) – nicht als Vorgabe verstehen. (09/2026)
- **`QT_QPA_PLATFORM=offscreen` muss gesetzt sein, bevor die `QgsApplication` erzeugt wird.** Danach wirkt es nicht mehr. (04.09.2026, Recherche)
- **`qgis.utils.iface` ist nach `start_app()``None`.** Entweder `pytest-qgis` verwenden (patcht automatisch) oder selbst `qgis.utils.iface = get_iface()` setzen – und keine Module zu früh importieren. (04.09.2026)
- **`QgsProject.instance()` ist global.** Ohne Aufräumen zwischen den Tests beeinflussen sie sich gegenseitig. (04.09.2026)
- **Abstürze beim Beenden** sind ein bekanntes QGIS-Thema, wenn Layer- oder Canvas-Objekte noch referenziert werden. Gegenmittel: `pytest-qgis` oder `--qgis_disable_exit`. (04.09.2026)
- **`QgisTestCase.render_layout_check()` ist für Plugins nicht nutzbar**: es sucht Referenzbilder in einem einkompilierten Pfad des QGIS-Quellbaums. Nur `QgsRenderChecker` bietet `setControlImagePath()`. Referenzbilder immer im CI-Container erzeugen, nie unter Windows (Font- und Antialiasing-Unterschiede). (04.09.2026)
- **Modale Dialoge blockieren den Test.** Reihenfolge der Gegenmittel: modalen Aufruf per `monkeypatch` ersetzen, `open()` statt `exec()`, erst zuletzt `QTimer.singleShot`. (04.09.2026)
- **Unter Windows nur `python-qgis.bat`/`python-qgis-ltr.bat` verwenden** – ein normales `python -m pytest` findet die QGIS-Bindings nicht. (04.09.2026)
- **Gitea ordnet Commits über die E-Mail-Adresse zu.** Stimmt `user.email` nicht mit einer im Gitea-Konto hinterlegten Adresse überein, erscheinen die Commits ohne Zuordnung zum Benutzer. Für dieses Projekt gilt die dienstliche Adresse (`@lfulg.sachsen.de`). Wer den Rechner auch privat nutzt, setzt sie je Repository mit `git config --local` statt global. Nachträglich nur durch Umschreiben der Historie korrigierbar. (04.09.2026)
- **Der Release-Workflow baut den Kopf des Kanal-Branches**, nicht den getaggten Commit (`git checkout "$CHANNEL"` in `Repository:hidden/workflows`). Tag nur setzen, wenn der Kanal-Branch auf dem gewünschten Stand ist. (09/2026)
- **`metadata.txt` nie von Hand pflegen.** Sie wird beim Release aus `.gitea/templates/metadata.template` erzeugt. Die im Repo liegende Datei ist ein Abbild des letzten Builds und kann irreführende Versionen/URLs zeigen (Issue Verfahrensgebiet #26). (09/2026)
- **Stable-Kanal ist leer**, ein Branch dafür existiert nicht; ein Tag ohne Suffix würde derzeit fehlschlagen. Der Branch muss **exakt `stable`** heißen, weil der Release-Workflow in `Repository:hidden/workflows``git checkout "$CHANNEL"` ausführt und `$CHANNEL` aus dem Tag-Suffix stammt. Anlegen: `docs/konzept/08_repo_einrichten.md` (ADR 0007). (04.09.2026, Workflow gelesen)
- **Repo-Name ≠ Paketname.** `Plugin_SN_Basis` wird als `sn_basis` installiert (Gitea-Variable `ZIP_FOLDER`). Importe im Code verwenden immer den Paketnamen.
- **Bekannte Metadata-Fehler:** Plan41-Template verweist auf `Plugin_Test_Action` als Repository; FLT-Template auf `Plugin_SN_Neu`; Widmung trägt Version `4711-unstable`. (Gesamtüberblick 09/2026)
- **Workspace-Dateien in Release-ZIPs.** Der rsync-Schritt schließt nur `.git`, `.gitea`, `.gitignore`, `__pycache__` aus. `AGENTS.md`, `CLAUDE.md`, `.agents/`, `.claude/`, `docs/`, `tests/` werden mit ausgeliefert, bis die Excludes ergänzt sind (offener Punkt).
- **WFS-Beschränkungen der sächsischen ALKIS-Dienste:** POST/GML-Filter werden blockiert, URL-Länge ist begrenzt, serverseitige Feature-Limits. Deshalb arbeiten Flurstücks- und Grenzpunktabrufe mit BBOX, gemarkungsweisen Batches und Nutzerdialogen. Nicht „vereinfachen“, ohne das gegen den Dienst zu testen. Vollständig mit erprobten Anfragen: `docs/wissen/wfs_dienste.md`, Objektarten/Attribute: `docs/wissen/alkis_objektarten.md`. (Verfahrensgebiet, Code)
- **Zwei Linklisten.** Zentrale `Linkliste/linkliste.csv` (Spalten `id;gruppe;ebene;inhalt;url;provider;stil`) und die vom Plugin genutzte `Plugin_SN_Plan41/assets/Linkliste.xlsx` (`ident, gruppe, kartenebene, inhalt, link, provider, stildatei`) sind nicht verbunden. Änderungen an Diensten in **beiden** nachziehen, bis die Zusammenführung entschieden ist. (09/2026)
- **ArcGIS-REST-Layer nicht nach Nummer deuten.** Gleiche Layer-URLs werden in der Linkliste und im Alternativansatz unterschiedlich benannt; Bedeutung und Geometrie immer gegen die Dienstmetadaten prüfen. (Vergleich Alternativansatz 09/2026)
- **CRS:** Plan41 und VLN-API arbeiten in EPSG:25833. Der Alternativansatz erzeugt EPSG:25832. Für neue Layer 25833 verwenden, bis eine Entscheidung dokumentiert ist.
- **Shape-Import erwartet Feld `FBSchl`** mit `901/10110F` (Neuordnungsgebiet) oder `901/10120F` (Neuvermessungsgebiet). Exporte mit abweichendem Feldnamen (`FBSchl.`) wurden bis 26.8.3 abgewiesen; seit PR Verfahrensgebiet #45 (02.09.2026, 26.8.4-unstable) ist die Feldsuche tolerant. Neue Exportvarianten trotzdem gegen die Erkennung testen.
- **VLN-Upload ersetzt den Serverbestand der VKZ vollständig** (`PUT`). Vor dem Upload sicherstellen, dass alle Teillayer geladen sind.
- **API-Key liegt in `QSettings`** (Registry/Konfigurationsdatei), kein sicherer Schlüsselspeicher. Niemals in Logs oder Tests ausgeben.
## Code-Konventionen, die man kennen muss
- **Projektvariablen-Schlüssel ohne `sn_` übergeben.** `set_variable("sn_abdeckungslayer", ...)` erzeugt `sn_sn_abdeckungslayer` (Altlast in Druckmodulen; funktioniert nur, weil Lesen und Schreiben denselben Fehler machen).
- **Zwei Schreibweisen für den Verfahrensgebiet-Layer:** `verfahrensgebiet_layer` (Manager, Fachplugins) und `verfahrensgebietslayer` (`print_logic.py`). Bis zur Bereinigung nicht auf Gleichheit vertrauen.
- **Layer werden über Anzeigenamen gefunden** (`BROR_LAYER_NAME` usw.). Umbenennungen in der UI brechen Abläufe.
- **`print()` ist im Code verbreitet**, aber kein Logging. Neue Ausgaben über `message_wrapper` (Nutzer) oder gar nicht; Debug-`print` vor dem PR entfernen.
- **Testrunner pro Repo (`tests/run_tests.py`) und Workspace-Runner unterscheiden sich.** Für PR-Nachweise zählt `scripts/run_tests.py --baseline`.
- **Tests der Verfahrensgebiet-Suite sind teils veraltet** (erwarten Einzeldatei-/Einzellayer-Schnittstellen vor der Mehrfachauswahl). Vor dem Fixen prüfen, ob der Test oder der Code falsch ist; siehe Baseline.
- **Tool-Gedächtnis ist lokal.** Die Claude-Auto-Memory sieht niemand sonst. Geteiltes Wissen nur in diesem Ordner (siehe Issue Basis #36 „shared-memory-architektur“, Kai).
- **Issues sind teils Diskussionen**, nicht Aufträge (Label `Diskussion`). Vor der Umsetzung Konsens im Issue abwarten oder im PR als Annahme kennzeichnen.