Regeln für PR- und Issue-Texte an AG_QGIS angleichen (#3) #4

Merged
Daniel merged 1 commits from bugfix/3-regeln-pr-issue-angleichen into main 2026-09-23 11:55:44 +00:00
10 changed files with 165 additions and 14 deletions
Showing only changes of commit fd58d75502 - Show all commits
+11 -4
View File
@@ -29,6 +29,8 @@ Ohne diesen Schritt wird womöglich etwas behoben, das längst behoben ist.
## 3. Branch
Immer von `main`. Vollständig: `docs/leitfaden/05_git_arbeitsweise.md`.
Kein `feature/`- oder `bugfix/`-Branch ohne Issue-Nummer. Gibt es kein Issue, zuerst einen Entwurf vorbereiten
(Skill `sn-ticket`) und warten, bis der Mensch es angelegt hat. Nur `doku/…` kommt ohne Issue aus.
```bash
git switch main && git pull --ff-only
@@ -39,10 +41,13 @@ git switch -c feature/<issue-nr>-<kurzname> # oder bugfix/… ; klein, ASCII,
- Kleine Schritte, jeder für sich prüfbar. Nach jedem Schritt `python -m pytest -q`.
- Regeln aus `AGENTS.md`: nur Standardbibliothek, kein Umbau nebenbei, Stil der Datei beibehalten, Deutsch mit
echten Umlauten.
- **Neues Verhalten des Konverters bekommt einen Test** in `.agents/skills/sn-bpmn/test_md_to_bpmn.py` – pytest,
ohne Netzwerk, ein Test je Verhalten. Bei einem Bugfix zuerst den Test schreiben, der den Fehler zeigt.
- **Neue Logik bekommt einen Test** – Konverter in `.agents/skills/sn-bpmn/test_md_to_bpmn.py`, Skripte in
`tests/test_<skript>.py`. pytest, ohne Netzwerk, ein Test je Verhalten. Bei einem Bugfix zuerst den Test
schreiben, der den Fehler zeigt.
- **Jeder neue oder geänderte Test bekommt einen deutschen Docstring**, erste Zeile `"""Prüft, dass …"""`.
- Ändert sich die Markdown-Syntax oder ein Element: `Anleitung.md` bzw. `Elementkatalog.md` mitziehen.
- Ändert sich die Markdown-Syntax oder ein Element: `Anleitung.md` bzw. `Elementkatalog.md` mitziehen. Syntax und
Ablageformat sind Verträge (`AGENTS.md`): vorher alle Modelle unter `prozesse/` prüfen, danach
`python scripts/pruefe_prozesse.py --erzeugen`.
## 5. Nachweis
- `python -m pytest -q` → alles grün.
@@ -52,5 +57,7 @@ git switch -c feature/<issue-nr>-<kurzname> # oder bugfix/… ; klein, ASCII,
## 6. Abschluss
- Commit-Nachrichten deutsch, Präsens, erste Zeile ≤ 72 Zeichen, Issue-Nummer am Ende.
- Pull-Request-Text nach `docs/vorlagen/pull_request.md` vorbereiten; Übergabenotiz mit Skill `sn-uebergabe`.
- Pull-Request-Text **vollständig** nach `docs/vorlagen/pull_request.md` vorbereiten: jeder Abschnitt, auch
„Änderungen an Syntax und Ablageformat“ (notfalls „keine“) und die Checkliste mit ehrlich gesetzten Haken.
Übergabenotiz mit Skill `sn-uebergabe`.
- **Hier ist Schluss.** Push, Pull Request, Merge und alles auf Gitea macht der Mensch.
+9 -4
View File
@@ -34,8 +34,8 @@ Sie bleibt bewusst kurz: Details liegen in `docs/` und werden nur bei Bedarf gel
### Tests
- Vor jedem Commit: `python -m pytest -q` – alle Tests grün.
- **Neues Verhalten von `md_to_bpmn.py` ohne Test wird nicht gemergt.** Bei einer Fehlerbehebung zuerst der Test,
der den Fehler zeigt.
- **Neue Funktionalität ohne Test wird nicht gemergt** – im Konverter wie in den Skripten. Mindestens ein Test je
neuem beobachtbarem Verhalten, ohne Netzwerk. Bei einer Fehlerbehebung zuerst der Test, der den Fehler zeigt.
- **Jeder neue oder geänderte Test bekommt einen deutschen Docstring**, erste Zeile in der Form `"""Prüft, dass …"""`.
- Tests werden nicht gelöscht, nicht mit `skip` stillgelegt und nicht in ihrer Erwartung angepasst, um grün zu werden –
außer das Verhalten wurde bewusst geändert und der Pull Request begründet es.
@@ -53,6 +53,9 @@ Sie bleibt bewusst kurz: Details liegen in `docs/` und werden nur bei Bedarf gel
- Keine Python-Abhängigkeiten außer der Standardbibliothek und `pytest` ohne Issue-Absprache und Eintrag in
`fallstricke.md`. Einzige eingebettete Fremdbibliothek ist bpmn-js unter `werkzeuge/vorschau/bpmn-js/` –
nicht von Hand ändern, Aktualisierung nach `werkzeuge/vorschau/README.md`.
- **Die Markdown-Syntax und das Ablageformat unter `prozesse/` sind Verträge.** Wer sie ändert, prüft vorher alle
Modelle unter `prozesse/`, zieht `Anleitung.md` bzw. `prozesse/README.md` nach und nennt die Änderung im Pull
Request unter „Änderungen an Syntax und Ablageformat“.
- Kein Umbau „nebenbei". Strukturänderungen nur über eigenes Issue.
### Allgemein
@@ -65,11 +68,13 @@ Sie bleibt bewusst kurz: Details liegen in `docs/` und werden nur bei Bedarf gel
1. `python scripts/hallo.py` – Stand holen.
2. Gitea-Issue lesen (Skill `sn-ticket`, `python scripts/gitea.py ticket Projektwissen <nr>`) oder als Entwurf
vorbereiten. Ziel und Abnahmekriterien klären, bevor etwas geändert wird.
3. Branch `feature/<issue-nr>-<kurzname>` bzw. `bugfix/<issue-nr>-<kurzname>` von `main`.
3. Branch `feature/<issue-nr>-<kurzname>` bzw. `bugfix/<issue-nr>-<kurzname>` von `main`. Ohne Issue kein
solcher Branch: erst den Entwurf, dann legt der Mensch das Issue an (Ausnahme: `doku/…`).
4. In kleinen Schritten umsetzen; nur betroffene Dateien und Abschnitte lesen.
5. `python -m pytest -q` (enthält `pruefe_prozesse.py`) und `python scripts/pruefe_umlaute.py`. Erzeugte BPMN-Dateien zusätzlich
in PICTURE importieren und das Ergebnis im PR nennen.
6. Pull-Request-Text nach `docs/vorlagen/pull_request.md` vorbereiten, inklusive Übergabenotiz (Skill `sn-uebergabe`).
6. Pull-Request-Text vollständig nach `docs/vorlagen/pull_request.md` vorbereiten – alle Abschnitte einschließlich
Checkliste –, mit Übergabenotiz (Skill `sn-uebergabe`).
**Push und Pull Request macht der Mensch.**
7. Neue Erkenntnisse in `docs/wissen/` oder `docs/entscheidungen/` festhalten, nicht nur im lokalen Gedächtnis des Werkzeugs.
@@ -9,7 +9,9 @@ sind Regeln, Skripte, Skills und eine Wissensbasis für die KI-gestützte Zusamm
## Entscheidung
`AG_Prozesse/Projektwissen` wird **neu angelegt** und übernimmt ausgewählte Dateien aus
`AG_QGIS/Projektwissen` (Stand `main` @ `decd2a0`, `sn-bpmn` von `BPMN-Skill` @ `6efcac4`) – **ohne Git-Historie**.
`AG_QGIS/Projektwissen` (Stand `main` @ `a0bb40a`, `sn-bpmn` von `BPMN-Skill` @ `6efcac4`) – **ohne Git-Historie**.
*(Korrigiert am 2026-09-23: Hier stand zunächst `decd2a0`; das ist ein Commit auf dem Branch
`feature/16-test-audit`, kopiert wurde aber von `main`.)*
QGIS- und Plugin-Spezifisches (Testrunner, Testkatalog, QGIS-Profil, ALKIS/WFS-Wissen, Modulkarte, Release)
wird nicht übernommen.
+6 -2
View File
@@ -122,11 +122,15 @@ kleine PRs machen.
**Vor dem PR**
- [ ] Issue verlinkt, Branch von `main`
- [ ] Issue verlinkt, Branch von `main` mit Issue-Nummer im Namen
- [ ] `python -m pytest -q` grün, `python scripts/pruefe_umlaute.py` ohne Funde
- [ ] Neue Funktionalität hat mindestens einen Test je neuem Verhalten
- [ ] Jeder neue oder geänderte Test hat einen deutschen Docstring `"""Prüft, dass …"""`
- [ ] Keine Debug-`print`, kein auskommentierter Code
- [ ] Änderungen an Markdown-Syntax oder Ablageformat im PR benannt (oder „keine“)
- [ ] Bei geänderter BPMN-Ausgabe: PICTURE-Import dokumentiert, `.bpmn` unter `prozesse/` neu erzeugt
- [ ] Keine personenbezogenen Daten, keine Originalunterlagen
- [ ] Übergabenotiz im PR; Wissensbasis nachgezogen, falls nötig
- [ ] PR-Text vollständig nach Vorlage, mit Checkliste und Übergabenotiz; Wissensbasis nachgezogen, falls nötig
- [ ] Diff gelesen: nur, was das Issue verlangt
**Review**
+9 -2
View File
@@ -17,7 +17,9 @@ Skills sind übernommen, alles QGIS- und Plugin-Spezifische ist entfernt. Kern i
## Offene Baustellen (priorisiert)
1. **Ersten Stand auf Gitea bringen** (`main`) und Branch-Schutz für `main` setzen (nur per Pull Request).
1. **Repo `Projektwissen` in Gitea öffentlich schalten** (Repo → Einstellungen → Sichtbarkeit). Die Organisation ist
öffentlich, das Repo nicht; ohne das liest `scripts/gitea.py` keine Tickets, und die Skills `sn-ticket` und
`sn-aufgabe-bearbeiten` laufen ins Leere.
2. **ADR 0003 (Ablage in `prozesse/`) vom Team annehmen lassen**, dann den ersten echten Prozess ablegen.
3. **Glossar um BPMN-/PICTURE-Begriffe ergänzen** und die Flurbereinigungsbegriffe auf das prüfen, was für
Prozesse gebraucht wird.
@@ -28,6 +30,11 @@ Skills sind übernommen, alles QGIS- und Plugin-Spezifische ist entfernt. Kern i
## Erledigt
- 23.09.2026 **Regeln für PR- und Issue-Texte an AG_QGIS angeglichen:** Vertragsabschnitt in der PR-Vorlage
(„Änderungen an Syntax und Ablageformat“), Vertragsfrage in der Issue-Vorlage, Testpflicht wieder für jede neue
Funktionalität, vollständige Checklisten, kein Arbeitsbranch ohne Issue. `gitea.py` meldet ein nicht öffentliches
Repo jetzt als Fehler statt „keine Tickets“ und stürzt bei Zeichen außerhalb von cp1252 nicht mehr ab.
- 23.09.2026 **BPMN-Vorschau** im Browser, offline: `scripts/vorschau.py` und `werkzeuge/vorschau/vorschau.html`
auf Basis von bpmn-js 18.29.1 (lokal eingebettet, MIT mit Wasserzeichen-Auflage). Anzeige direkt in Gitea wäre
Sache der Server-Administration (externer Renderer), Issue-Entwurf liegt vor.
@@ -37,6 +44,6 @@ Skills sind übernommen, alles QGIS- und Plugin-Spezifische ist entfernt. Kern i
Die in `Anleitung.md` genannten, aber nie committeten Beispiele liegen jetzt unter
`.agents/skills/sn-bpmn/beispiele/`.
- 23.09.2026 **Repository aus AG_QGIS/Projektwissen abgeleitet** (Stand `decd2a0` bzw. `BPMN-Skill` @ `6efcac4`),
- 23.09.2026 **Repository aus AG_QGIS/Projektwissen abgeleitet** (Stand `main` @ `a0bb40a` bzw. `BPMN-Skill` @ `6efcac4`),
ohne Historie. Skripte auf die Organisation `AG_Prozesse` umgestellt, Hauptbranch `main`, Test über `pytest`
(32 Tests grün), `pruefe_umlaute.py` prüft jetzt den Workspace selbst.
+2 -1
View File
@@ -4,6 +4,7 @@
## Betroffen
Bereich: Prozessmodell (`prozesse/<name>/`) | Konverter `md_to_bpmn.py` | Skill | Skripte | Doku
Dateien (falls bekannt):
Ändert Markdown-Syntax oder Ablageformat unter `prozesse/`? nein | ja: <welche>
## Fachlicher Hintergrund
<Begriffe, Regeln, Beispiel (keine echten Verfahrensdaten, keine Namen). Verweise auf Glossar.>
@@ -11,7 +12,7 @@ Dateien (falls bekannt):
## Abnahmekriterien
- [ ] <beobachtbares Verhalten 1>
- [ ] <beobachtbares Verhalten 2>
- [ ] Bei Änderungen am Konverter: Test vorhanden, `python -m pytest -q` grün
- [ ] Test je neuem Verhalten vorhanden (Konverter, Skripte), `python -m pytest -q` grün
- [ ] PICTURE-Import: <nötig / nicht nötig>
## Nicht Teil dieses Issues
+5
View File
@@ -7,11 +7,16 @@
- Bei Modellen unter `prozesse/`: `.bpmn` mit `python scripts/pruefe_prozesse.py --erzeugen` erzeugt
- PICTURE-Import: <PICTURE-Version, Datei, Ergebnis> | nicht nötig, weil …
## Änderungen an Syntax und Ablageformat
keine | <welche, mit Verweis auf Issue: Markdown-Syntax (`Anleitung.md`), Ablage unter `prozesse/`
(`prozesse/README.md`, ADR 0003); welche Modelle unter `prozesse/` angepasst oder neu erzeugt wurden>
## Übergabenotiz
<nach docs/vorlagen/uebergabe.md>
## Checkliste
- [ ] Nur Änderungen zum Issue
- [ ] Keine Debug-`print`, kein auskommentierter Code
- [ ] Keine personenbezogenen Daten, keine internen Originalunterlagen
- [ ] Doku des Skills nachgezogen (Anleitung, Elementkatalog, Modellierregeln) oder nicht betroffen
- [ ] Wissensbasis nachgezogen (oder „nichts Neues“)
+7
View File
@@ -50,6 +50,13 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
## Umgebung und Werkzeuge
- **Öffentliche Organisation heißt nicht öffentliches Repo.** In Gitea haben Organisation und Repo getrennte
Sichtbarkeit. `AG_Prozesse` war öffentlich, `Projektwissen` privat: Die Weboberfläche zeigte es angemeldeten
Personen normal, die API ohne Anmeldung lieferte 404. `scripts/gitea.py` liest bewusst ohne Token und braucht deshalb
ein öffentliches Repo; es prüft das jetzt vor jeder Abfrage. Prüfen ohne Anmeldung:
`curl -s -o /dev/null -w "%{http_code}" https://entwicklung.flurneuordnung-sachsen.de/api/v1/repos/AG_Prozesse/Projektwissen`
muss `200` liefern. (23.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. Es gilt die dienstliche
Adresse, die im Gitea-Konto hinterlegt ist. Wer den Rechner auch privat nutzt, setzt sie je Repository mit
+28
View File
@@ -32,6 +32,7 @@ from __future__ import annotations
import argparse
import json
import shutil
import sys
import tomllib
import urllib.error
import urllib.parse
@@ -186,6 +187,26 @@ def hole_issues(basis: str, org: str, repo: str, state: str, art: str = "issues"
) from None
def pruefe_sichtbar(basis: str, org: str, namen: list[str]) -> None:
"""Bricht mit klarer Meldung ab, wenn ein Repo ohne Anmeldung nicht lesbar ist.
Ohne diese Prüfung liefert die Sammelsuche über die Organisation für ein privates
Repo einfach keine Treffer – das sieht aus wie „keine offenen Tickets“.
"""
unsichtbar = []
for name in namen:
try:
hole_json(baue_url(basis, f"/repos/{urllib.parse.quote(org)}/{urllib.parse.quote(name)}"))
except NichtGefunden:
unsichtbar.append(name)
if unsichtbar:
raise NichtGefunden(
f"Ohne Anmeldung nicht lesbar: {', '.join(f'{org}/{n}' for n in unsichtbar)}. "
"Das Repo ist privat oder existiert nicht. scripts/gitea.py liest bewusst ohne Token; "
"das Repo muss in Gitea öffentlich sein (Repo → Einstellungen → Sichtbarkeit)."
)
def hole_issues_org(basis: str, org: str, state: str, art: str = "issues",
suchtext: str | None = None) -> list[dict]:
"""Issues ALLER Repos der Organisation über den Sammelendpunkt.
@@ -549,6 +570,11 @@ def befehl_entwurf(basis: str, org: str, repo: str, art: str, nummer: int | None
# ------------------------------------------------------------------ main ------
def main() -> int:
# Windows-Konsolen und Pipes laufen oft mit cp1252; Zeichen wie „→“ in Tickettexten
# oder Meldungen führten sonst zum Absturz der Ausgabe.
for strom in (sys.stdout, sys.stderr):
if hasattr(strom, "reconfigure"):
strom.reconfigure(encoding="utf-8", errors="replace")
breite_eltern = argparse.ArgumentParser(add_help=False)
breite_eltern.add_argument("--breite", type=int, help="Terminalbreite erzwingen (Standard: automatisch)")
@@ -596,6 +622,8 @@ def main() -> int:
return 2
repo = loese_repo(args.repo, namen) if getattr(args, "repo", None) else None
if not (args.befehl == "entwurf" and args.art == "issue"): # nur der Issue-Entwurf kommt ohne Netz aus
pruefe_sichtbar(basis, org, [repo] if repo else namen)
if args.befehl == "tickets":
return befehl_tickets(basis, org, repo, namen, args)
+85
View File
@@ -0,0 +1,85 @@
"""Tests für scripts/gitea.py – ohne Netzwerk, die Gitea-API wird ersetzt."""
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "scripts"))
import gitea # noqa: E402
BASIS = "https://gitea.example"
MANIFEST = {"workspace": {"gitea": BASIS, "org": "AG_Test", "repo": "Projektwissen"}, "repos": []}
def api_mit_sichtbaren(*sichtbar):
"""Ersatz für hole_json: nur die genannten Repos existieren, Issue-Listen sind leer."""
def hole_json(url):
if "/issues" in url:
return []
name = url.rsplit("/", 1)[-1]
if name in sichtbar:
return {"name": name}
raise gitea.NichtGefunden("Repo/Issue nicht gefunden: " + url)
return hole_json
def starte(monkeypatch, capsys, *argumente):
monkeypatch.setattr(gitea, "lade_manifest", lambda: MANIFEST)
monkeypatch.setattr(sys, "argv", ["gitea.py", *argumente])
code = gitea.main()
return code, capsys.readouterr().out
def test_pruefe_sichtbar_laesst_oeffentliches_repo_durch(monkeypatch):
"""Prüft, dass ein ohne Anmeldung lesbares Repo keinen Fehler auslöst."""
monkeypatch.setattr(gitea, "hole_json", api_mit_sichtbaren("Projektwissen"))
gitea.pruefe_sichtbar(BASIS, "AG_Test", ["Projektwissen"])
def test_pruefe_sichtbar_nennt_nicht_lesbare_repos(monkeypatch):
"""Prüft, dass ein privates Repo mit Namen und Hinweis auf die Sichtbarkeit gemeldet wird."""
monkeypatch.setattr(gitea, "hole_json", api_mit_sichtbaren("Projektwissen"))
with pytest.raises(gitea.NichtGefunden) as fehler:
gitea.pruefe_sichtbar(BASIS, "AG_Test", ["Projektwissen", "Geheim"])
assert "AG_Test/Geheim" in str(fehler.value)
assert "AG_Test/Projektwissen" not in str(fehler.value)
assert "Sichtbarkeit" in str(fehler.value)
def test_tickets_bei_privatem_repo_meldet_fehler_statt_keine_tickets(monkeypatch, capsys):
"""Prüft, dass 'tickets' bei unsichtbarem Repo mit Exit-Code 1 abbricht, statt 'keine Tickets' zu melden."""
monkeypatch.setattr(gitea, "hole_json", api_mit_sichtbaren())
code, ausgabe = starte(monkeypatch, capsys, "tickets")
assert code == 1
assert "Ohne Anmeldung nicht lesbar" in ausgabe
assert "Keine passenden Tickets" not in ausgabe
def test_tickets_bei_oeffentlichem_repo_ohne_tickets(monkeypatch, capsys):
"""Prüft, dass ein sichtbares Repo ohne offene Tickets weiterhin normal mit Exit-Code 0 endet."""
monkeypatch.setattr(gitea, "hole_json", api_mit_sichtbaren("Projektwissen"))
code, ausgabe = starte(monkeypatch, capsys, "tickets")
assert code == 0
assert "Keine passenden Tickets" in ausgabe
def test_issue_entwurf_braucht_kein_netz(monkeypatch, capsys, tmp_path):
"""Prüft, dass 'entwurf issue' ohne jede API-Anfrage eine Entwurfsdatei anlegt."""
def kein_netz(url):
raise AssertionError(f"unerwartete Anfrage: {url}")
monkeypatch.setattr(gitea, "hole_json", kein_netz)
monkeypatch.setattr(gitea, "ROOT", tmp_path)
monkeypatch.setattr(gitea, "ENTWURFSORDNER", tmp_path / "entwuerfe")
code, _ = starte(monkeypatch, capsys, "entwurf", "issue", "Projektwissen")
assert code == 0
assert len(list((tmp_path / "entwuerfe").glob("*-issue-Projektwissen.md"))) == 1
def test_ausgabe_mit_zeichen_ausserhalb_cp1252_stuerzt_nicht_ab(monkeypatch, capsys):
"""Prüft, dass die Fehlermeldung mit '→' auch bei einer cp1252-Konsole ausgegeben wird."""
monkeypatch.setattr(gitea, "hole_json", api_mit_sichtbaren())
code, ausgabe = starte(monkeypatch, capsys, "pulls")
assert code == 1
assert "→" in ausgabe