Prozessbibliothek als eigenes Repository auslagern (#9)

- ADR 0004 ersetzt ADR 0003: Die Prozessmodelle liegen im Repository
  AG_Prozesse/Prozessbibliothek, geklont nach Prozessbibliothek/ (neuer
  Eintrag in workspace.toml, .gitignore). prozesse/ ist entfernt; das
  Ablageformat (Ordner je Prozess, drei Dateien, Status) bleibt.
- pruefe_prozesse.py prüft den Klon (Ort über setup_workspace.repos_root),
  lässt Punkt-Ordner wie .git aus und erlaubt in der Wurzel nur die
  Repo-Dateien. Fehlt der Klon, gibt es einen Hinweis statt eines Befunds;
  pytest warnt dann.
- einlesen.py sucht den Bestand in der Bibliothek und nennt Treffer als
  Prozessbibliothek/<name>/, auch wenn der Klon woanders liegt.
- Regeln aus prozesse/README.md nach docs/leitfaden/08_prozessbibliothek.md,
  inklusive Ablauf bei Konverteränderungen (zwei Pull Requests).
- Verweise in AGENTS.md, README, Skills, Leitfäden, Vorlagen, Fallstricken
  und STATUS nachgezogen.
- Tests: python -m pytest -q -> 88 bestanden, 1 xfail (#8), 1 Warnung
  (Bibliothek noch nicht geklont).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 09:40:52 +02:00
co-authored by Claude Opus 5.5
parent 23a39fd929
commit 31131cc5c3
26 changed files with 312 additions and 131 deletions
+11 -8
View File
@@ -11,7 +11,8 @@ Mechanische; die fachliche Einschätzung und die Rückfrage an den Menschen mach
geändert .md mit Vermerk, aber der Inhalt hat sich seitdem geändert
eingelesen .md mit Vermerk, Inhalt unverändert – wird übersprungen
Für neue und geänderte Dateien: vorgeschlagener Prozessname, ob die Datei schon eine
gültige Arbeitsschritt-.md ist, und ähnliche vorhandene Prozesse (prozesse/, entwuerfe/).
gültige Arbeitsschritt-.md ist, und ähnliche vorhandene Prozesse
(Prozessbibliothek, entwuerfe/).
vermerk Trägt nach erfolgreichem Einlesen oben in die .md einen Vermerk mit Zeitstempel ein.
Aufrufe (aus dem Workspace-Root):
@@ -39,9 +40,11 @@ import unicodedata
from datetime import datetime
from pathlib import Path
from setup_workspace import repos_root
ROOT = Path(__file__).resolve().parents[1]
EINGANG = ROOT / "input"
PROZESSE = ROOT / "prozesse"
BIBLIOTHEK = repos_root() / "Prozessbibliothek" # eigenes Repository (ADR 0004), wie in pruefe_prozesse.py
ENTWUERFE = ROOT / "entwuerfe"
SKILL = ROOT / ".agents" / "skills" / "sn-bpmn"
@@ -103,7 +106,7 @@ def setze_vermerk(pfad: Path, ergebnis: str, ziel: str, jetzt: datetime | None =
# ------------------------------------------------------------ Einordnung ---
def slug(text: str) -> str:
"""Ordnername nach ADR 0003: Kleinbuchstaben, Ziffern, Bindestriche; Umlaute ausgeschrieben."""
"""Ordnername nach ADR 0004: Kleinbuchstaben, Ziffern, Bindestriche; Umlaute ausgeschrieben."""
text = text.lower()
for alt, neu in (("ä", "ae"), ("ö", "oe"), ("ü", "ue"), ("ß", "ss")):
text = text.replace(alt, neu)
@@ -138,16 +141,16 @@ def ist_arbeitsschritt_md(text: str) -> bool:
def bestand() -> list[dict]:
"""Vorhandene Prozesse (prozesse/) und lokale Entwürfe (entwuerfe/) mit Titel und Wörtern."""
"""Vorhandene Prozesse (Prozessbibliothek) und lokale Entwürfe (entwuerfe/) mit Titel und Wörtern."""
eintraege = []
if PROZESSE.is_dir():
for ordner in sorted(p for p in PROZESSE.iterdir() if p.is_dir()):
if BIBLIOTHEK.is_dir():
for ordner in sorted(p for p in BIBLIOTHEK.iterdir() if p.is_dir() and not p.name.startswith(".")):
md = ordner / f"{ordner.name}.md"
text = md.read_text(encoding="utf-8") if md.is_file() else ""
readme = ordner / "README.md"
titel = titel_von(readme.read_text(encoding="utf-8"), ordner.name) if readme.is_file() else ordner.name
eintraege.append({"art": "Prozess", "name": ordner.name, "titel": titel_von(text, titel),
"pfad": ordner.relative_to(ROOT).as_posix() + "/", "woerter": woerter(text)})
"pfad": f"{BIBLIOTHEK.name}/{ordner.name}/", "woerter": woerter(text)})
if ENTWUERFE.is_dir():
for md in sorted(ENTWUERFE.glob("*.md")):
if re.match(r"^\d{8}-\d{6}-(issue|kommentar)-", md.name):
@@ -261,7 +264,7 @@ def main() -> int:
v = unter.add_parser("vermerk", help="Einlese-Vermerk in eine .md schreiben")
v.add_argument("datei", help="Datei in input/")
v.add_argument("--ergebnis", required=True, choices=ERGEBNISSE)
v.add_argument("--ziel", required=True, help="wo das Ergebnis liegt, z. B. entwuerfe/<name>.md oder prozesse/<name>/")
v.add_argument("--ziel", required=True, help="wo das Ergebnis liegt, z. B. entwuerfe/<name>.md oder Prozessbibliothek/<name>/")
args = ap.parse_args()
if args.befehl == "vermerk":
+49 -15
View File
@@ -1,7 +1,9 @@
#!/usr/bin/env python3
"""Prüft die versionierten Prozessmodelle unter prozesse/ und die Beispiele des Skills sn-bpmn.
"""Prüft die versionierten Prozessmodelle der Prozessbibliothek und die Beispiele des Skills sn-bpmn.
Jeder Prozess liegt in einem eigenen Ordner prozesse/<name>/ mit genau drei Dateien:
Die Prozessbibliothek ist ein eigenes Repository (AG_Prozesse/Prozessbibliothek, ADR 0004). Sie wird von
scripts/setup_workspace.py neben Projektwissen geklont – in den Workspace-Ordner oder nach repos_root aus
workspace.local.toml. Jeder Prozess liegt dort in einem eigenen Ordner <name>/ mit genau drei Dateien:
README.md Steckbrief mit Status, Stand, Zuständigkeit (Vorlage docs/vorlagen/prozess_readme.md)
<name>.md Arbeitsschritt-Beschreibung – die maßgebliche Quelle
@@ -11,12 +13,17 @@ Geprüft wird:
* Ordnername in Kleinbuchstaben, Ziffern und Bindestrichen,
* genau diese drei Dateien, keine weiteren (Originalunterlagen gehören nach referenz/),
* README.md nennt einen gültigen Status (Entwurf | abgenommen),
* <name>.md lässt sich umwandeln, und <name>.bpmn ist byte-gleich mit dem Ergebnis.
* <name>.md lässt sich umwandeln, und <name>.bpmn ist byte-gleich mit dem Ergebnis,
* in der Wurzel der Bibliothek liegen außer den Prozessordnern nur README.md, AGENTS.md, CLAUDE.md,
.gitattributes und .gitignore.
Ist die Bibliothek nicht geklont, ist das kein Befund; es werden nur die Beispiele geprüft, und ein Hinweis
nennt den Befehl zum Klonen.
Die Beispiele in .agents/skills/sn-bpmn/beispiele/ werden auf die letzte Bedingung geprüft.
Aufrufe (aus dem Workspace-Root):
python scripts/pruefe_prozesse.py # alles prüfen
python scripts/pruefe_prozesse.py --erzeugen # alle .bpmn aus ihren .md neu erzeugen, dann prüfen
Die neu erzeugten .bpmn der Bibliothek werden dort auf einem eigenen Branch committet (eigener Pull Request).
Exit-Code: 0 = in Ordnung, 1 = mindestens ein Befund.
"""
@@ -28,9 +35,11 @@ import re
import sys
from pathlib import Path
from setup_workspace import repos_root
ROOT = Path(__file__).resolve().parents[1]
SKILL = ROOT / ".agents" / "skills" / "sn-bpmn"
PROZESSE = ROOT / "prozesse"
BIBLIOTHEK = repos_root() / "Prozessbibliothek"
BEISPIELE = SKILL / "beispiele"
sys.path.insert(0, str(SKILL))
@@ -38,6 +47,8 @@ import md_to_bpmn # noqa: E402 (liegt im Skill-Ordner, nicht als Paket install
ORDNERNAME = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
STATUSWERTE = ("Entwurf", "abgenommen")
# Dateien, die in der Wurzel der Bibliothek neben den Prozessordnern liegen dürfen
REPO_DATEIEN = {"README.md", "AGENTS.md", "CLAUDE.md", ".gitattributes", ".gitignore"}
STATUSZEILE = re.compile(r"^\|\s*Status\s*\|\s*(?P<wert>[^|]*?)\s*\|", re.MULTILINE)
@@ -49,10 +60,13 @@ def erzeuge_bpmn(md_pfad: Path) -> bytes:
def _relativ(pfad: Path) -> str:
try:
return pfad.relative_to(ROOT).as_posix()
except ValueError:
return pfad.as_posix()
"""Pfad für Meldungen: relativ zum Workspace, Bibliotheks-Pfade beginnend mit „Prozessbibliothek/“."""
for basis in (ROOT, BIBLIOTHEK.parent):
try:
return pfad.relative_to(basis).as_posix()
except ValueError:
pass
return pfad.as_posix()
def pruefe_modell(md_pfad: Path) -> list[str]:
@@ -102,21 +116,39 @@ def pruefe_ordner(ordner: Path) -> list[str]:
return befunde
def prozessordner(wurzel: Path = PROZESSE) -> list[Path]:
return sorted(p for p in wurzel.iterdir() if p.is_dir()) if wurzel.is_dir() else []
def prozessordner(wurzel: Path = BIBLIOTHEK) -> list[Path]:
"""Prozessordner der Bibliothek; .git und andere Punkt-Ordner zählen nicht."""
if not wurzel.is_dir():
return []
return sorted(p for p in wurzel.iterdir() if p.is_dir() and not p.name.startswith("."))
def beispiele(wurzel: Path = BEISPIELE) -> list[Path]:
return sorted(wurzel.glob("*.md")) if wurzel.is_dir() else []
def pruefe_alles() -> list[str]:
def pruefe_bibliothek(wurzel: Path = BIBLIOTHEK) -> list[str]:
"""Befunde zur Wurzel der Bibliothek und zu allen Prozessordnern; leer, wenn sie nicht geklont ist."""
befunde: list[str] = []
if PROZESSE.is_dir():
for fremd in sorted(p for p in PROZESSE.iterdir() if p.is_file() and p.name != "README.md"):
befunde.append(f"{_relativ(fremd)}: Dateien gehören in einen Prozessordner prozesse/<name>/")
for ordner in prozessordner():
if not wurzel.is_dir():
return befunde
for fremd in sorted(p for p in wurzel.iterdir() if p.is_file() and p.name not in REPO_DATEIEN):
befunde.append(f"{_relativ(fremd)}: Dateien gehören in einen Prozessordner <name>/")
for ordner in prozessordner(wurzel):
befunde += pruefe_ordner(ordner)
return befunde
def hinweis_bibliothek(wurzel: Path = BIBLIOTHEK) -> str:
"""Hinweis, wenn die Bibliothek nicht geklont ist; sonst leer."""
if wurzel.is_dir():
return ""
return (f"Hinweis: Prozessbibliothek nicht geklont ({wurzel.as_posix()}) – nur die Beispiele geprüft. "
f"Klonen mit: python scripts/setup_workspace.py")
def pruefe_alles() -> list[str]:
befunde = pruefe_bibliothek()
for md_pfad in beispiele():
befunde += pruefe_modell(md_pfad)
return befunde
@@ -145,6 +177,8 @@ def main() -> int:
befunde = pruefe_alles()
for befund in befunde:
print(befund)
if hinweis := hinweis_bibliothek():
print(hinweis)
anzahl = len(prozessordner())
status = "GRÜN" if not befunde else "ROT"
print(f"{status}: {len(befunde)} Befund/e, {anzahl} Prozess(e), {len(beispiele())} Beispiel(e).")
+1 -1
View File
@@ -6,7 +6,7 @@ Das Skript erzeugt daraus eine einzelne, in sich geschlossene HTML-Datei (Biblio
eingebettet) und öffnet sie im Standardbrowser. Es wird nichts übertragen; die Seite lädt nichts aus dem Netz.
Aufrufe (aus dem Workspace-Root):
python scripts/vorschau.py prozesse/antrag-pruefen/antrag-pruefen.bpmn
python scripts/vorschau.py Prozessbibliothek/antrag-pruefen/antrag-pruefen.bpmn
python scripts/vorschau.py entwuerfe/antrag-pruefen.md # .md wird vorher umgewandelt
python scripts/vorschau.py <datei> --ausgabe vorschau.html --nicht-oeffnen
python scripts/vorschau.py --stdin < datei.bpmn > vorschau.html # Filter, z. B. als Gitea-Renderer