Prozessmodelle versioniert unter prozesse/<name>/ ablegen

Jedes Modell bekommt einen eigenen Ordner mit Steckbrief (README.md,
Status Entwurf|abgenommen), der maßgeblichen Arbeitsschritt-.md und der
daraus erzeugten .bpmn (ADR 0003).

- scripts/pruefe_prozesse.py prüft Ordnername, Dateien, Status und ob
  jede .bpmn byte-gleich mit der Umwandlung ihrer .md ist; --erzeugen
  erzeugt alle .bpmn neu. md_to_bpmn.py arbeitet deterministisch.
- tests/test_pruefe_prozesse.py: Bestandsprüfung plus 11 Fälle für die
  Prüflogik; pytest.ini sammelt .agents/skills und tests.
- Die in Anleitung.md genannten, nie committeten Beispiele liegen jetzt
  unter .agents/skills/sn-bpmn/beispiele/ und werden mitgeprüft.
- Skill sn-bpmn: Entwürfe nach entwuerfe/, neuer Schritt 5 „Ablegen“.
- Vorlage docs/vorlagen/prozess_readme.md, Doku und Fallstricke ergänzt.
- Tests: python -m pytest -q -> 44 bestanden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 13:02:48 +02:00
co-authored by Claude Opus 5.5
parent 012509b6d1
commit 55c024dbc9
25 changed files with 835 additions and 35 deletions
@@ -36,7 +36,7 @@ git switch -c feature/<issue-nr>-<kurzname> # oder bugfix/… ; klein, ASCII,
```
## 4. Umsetzen
- Kleine Schritte, jeder für sich prüfbar. Nach jedem Schritt `python -m pytest -q .agents/skills`.
- 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,
@@ -45,7 +45,7 @@ git switch -c feature/<issue-nr>-<kurzname> # oder bugfix/… ; klein, ASCII,
- Ändert sich die Markdown-Syntax oder ein Element: `Anleitung.md` bzw. `Elementkatalog.md` mitziehen.
## 5. Nachweis
- `python -m pytest -q .agents/skills` → alles grün.
- `python -m pytest -q` → alles grün.
- `python scripts/pruefe_umlaute.py` → keine Funde.
- Ändert sich die erzeugte BPMN-Datei: ein Beispiel erzeugen und in PICTURE importieren. Kannst du das nicht
selbst, im PR als offenen Punkt eintragen. **Nicht als erledigt ausgeben.**
+14 -5
View File
@@ -25,15 +25,19 @@ stehen in `Modellierregeln.md`.
## Aufruf
```bash
python3 md_to_bpmn.py <prozess.md> <ausgabe.bpmn>
python .agents/skills/sn-bpmn/md_to_bpmn.py <prozess.md> <ausgabe.bpmn>
```
Beispiel:
Beispiel (aus dem Workspace-Root):
```bash
python3 md_to_bpmn.py beispiel_prozess.md beispiel_prozess.bpmn
python .agents/skills/sn-bpmn/md_to_bpmn.py entwuerfe/antrag-pruefen.md entwuerfe/antrag-pruefen.bpmn
```
Versionierte Modelle unter `prozesse/` werden nicht einzeln, sondern gesammelt
erzeugt: `python scripts/pruefe_prozesse.py --erzeugen` (siehe
`prozesse/README.md`).
Die erzeugte `.bpmn`-Datei kann anschließend über den BPMN-Import-Dialog
in PICTURE geladen werden.
@@ -185,8 +189,13 @@ umformulieren.
### Vollständige Beispiele
- Ohne Lanes: `beispiel_prozess.md` / `beispiel_prozess.bpmn`
- Mit Lanes: `gewannenvermessung_lanes.md` / `gewannenvermessung_lanes.bpmn`
In `beispiele/` neben dieser Datei, reine Syntaxbeispiele (fachlich nicht geprüft):
- Ohne Lanes: `beispiele/beispiel_prozess.md` / `beispiele/beispiel_prozess.bpmn`
- Mit Lanes und externem Beteiligten: `beispiele/beispiel_lanes.md` / `beispiele/beispiel_lanes.bpmn`
Ein Test hält die `.bpmn` der Beispiele aktuell; nach einer Änderung am
Konverter `python scripts/pruefe_prozesse.py --erzeugen` ausführen.
## Einschränkungen der aktuellen Version
+19 -4
View File
@@ -19,13 +19,17 @@ schreibst oder bearbeitest.
## Ablauf
1. **Quelle ermitteln.** Frage nach der Rohtext-, Word- oder PDF-Quelle,
falls keine angegeben oder angehängt wurde.
falls keine angegeben oder angehängt wurde. Gibt es den Prozess schon
unter `prozesse/<name>/`, ist dessen `<name>.md` der Ausgangspunkt:
dann auf einem Arbeitsbranch direkt dort ändern statt in `entwuerfe/`,
und nach Schritt 3 mit Schritt 5 weitermachen.
2. **Entwurf erzeugen.** Lies die Quelle und leite daraus eine
strukturierte Arbeitsschritt-`.md` nach der Syntax aus `Anleitung.md`
ab. Triff nachvollziehbare fachliche Annahmen (Schrittgrenzen,
Verzweigungen, Verantwortliche wo erkennbar) und schreibe das Ergebnis
nach `entwuerfe/<name>.md` (Verzeichnis bei Bedarf anlegen). Gleiche den
nach `entwuerfe/<name>.md` (Verzeichnis bei Bedarf anlegen; `<name>` in
Kleinbuchstaben mit Bindestrichen, z. B. `antrag-pruefen`). Gleiche den
Entwurf gegen `Modellierregeln.md` ab und behebe Verstöße direkt im
Entwurf. Fasse kurz zusammen, welche Annahmen getroffen wurden, und
weise auf Stellen hin, die fachlich geprüft werden sollten.
@@ -38,9 +42,20 @@ schreibst oder bearbeitest.
4. **BPMN erzeugen.** Erst nach ausdrücklicher Freigabe (z. B. "ist
angepasst", "passt so", "wandle um"):
```
python .agents/skills/sn-bpmn/md_to_bpmn.py entwuerfe/<name>.md <name>.bpmn
python .agents/skills/sn-bpmn/md_to_bpmn.py entwuerfe/<name>.md entwuerfe/<name>.bpmn
```
Ergebnis kurz melden (Anzahl Schritte/Kanten/Lanes, Pfad der Datei).
Ergebnis kurz melden (Anzahl Schritte/Kanten/Lanes, Pfad der Datei) und
anbieten, das Modell im Repository abzulegen (Schritt 5).
5. **Ablegen (nur auf Wunsch).** Ablauf und Regeln: `prozesse/README.md`
(ADR 0003). Kurz: Branch `feature/<issue-nr>-<name>` von `main`, Ordner
`prozesse/<name>/` mit `<name>.md` und Steckbrief `README.md` aus
`docs/vorlagen/prozess_readme.md` (Status `Entwurf`, Annahmen aus
Schritt 2 unter „Annahmen und offene Punkte“), dann
`python scripts/pruefe_prozesse.py --erzeugen` und `python -m pytest -q`.
Vorher prüfen, dass keine Namen, Aktenzeichen oder Verfahrensdaten in der
`.md` stehen. Commit und Pull-Request-Text vorbereiten; pushen macht der
Mensch. Den Status `abgenommen` setzt nie der Agent von sich aus.
Wenn der Nutzer direkt eine bereits fertige Arbeitsschritt-`.md` angibt
und ausdrücklich nur die Umwandlung möchte, Schritt 2-3 überspringen
@@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://picture-tool.local/bpmn" exporter="md_to_bpmn.py">
<bpmn:collaboration id="Collaboration_1">
<bpmn:participant id="Participant_1" name="Antrag prüfen mit Zuständigkeiten (Beispiel)" processRef="Process_1"/>
<bpmn:participant id="Participant_Ext_1" name="Antragsteller"/>
<bpmn:messageFlow id="MsgFlow_1" sourceRef="Participant_Ext_1" targetRef="StartEvent_S1"/>
<bpmn:messageFlow id="MsgFlow_2" sourceRef="SendTask_S4" targetRef="Participant_Ext_1"/>
<bpmn:messageFlow id="MsgFlow_3" sourceRef="SendTask_S7" targetRef="Participant_Ext_1"/>
</bpmn:collaboration>
<bpmn:process id="Process_1" name="Antrag prüfen mit Zuständigkeiten (Beispiel)" isExecutable="false">
<bpmn:laneSet id="LaneSet_1">
<bpmn:lane id="Lane_1" name="Posteingang">
<bpmn:flowNodeRef>StartEvent_S1</bpmn:flowNodeRef>
<bpmn:flowNodeRef>SendTask_S7</bpmn:flowNodeRef>
<bpmn:flowNodeRef>EndEvent_S8</bpmn:flowNodeRef>
</bpmn:lane>
<bpmn:lane id="Lane_2" name="Sachbearbeitung">
<bpmn:flowNodeRef>Task_S2</bpmn:flowNodeRef>
<bpmn:flowNodeRef>Gateway_S3</bpmn:flowNodeRef>
<bpmn:flowNodeRef>SendTask_S4</bpmn:flowNodeRef>
<bpmn:flowNodeRef>Task_S5</bpmn:flowNodeRef>
</bpmn:lane>
<bpmn:lane id="Lane_3" name="Teamleitung">
<bpmn:flowNodeRef>Task_S6</bpmn:flowNodeRef>
</bpmn:lane>
</bpmn:laneSet>
<bpmn:startEvent id="StartEvent_S1" name="Antrag geht ein">
<bpmn:outgoing>Flow_1</bpmn:outgoing>
<bpmn:messageEventDefinition id="StartEvent_S1_ed"/>
</bpmn:startEvent>
<bpmn:task id="Task_S2" name="Antrag erfassen">
<bpmn:incoming>Flow_1</bpmn:incoming>
<bpmn:incoming>Flow_5</bpmn:incoming>
<bpmn:outgoing>Flow_2</bpmn:outgoing>
</bpmn:task>
<bpmn:exclusiveGateway id="Gateway_S3" name="Antrag vollständig?">
<bpmn:incoming>Flow_2</bpmn:incoming>
<bpmn:outgoing>Flow_3</bpmn:outgoing>
<bpmn:outgoing>Flow_4</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sendTask id="SendTask_S4" name="Unterlagen nachfordern">
<bpmn:incoming>Flow_4</bpmn:incoming>
<bpmn:outgoing>Flow_5</bpmn:outgoing>
</bpmn:sendTask>
<bpmn:task id="Task_S5" name="Entscheidung vorbereiten">
<bpmn:incoming>Flow_3</bpmn:incoming>
<bpmn:outgoing>Flow_6</bpmn:outgoing>
</bpmn:task>
<bpmn:task id="Task_S6" name="Entscheidung zeichnen">
<bpmn:incoming>Flow_6</bpmn:incoming>
<bpmn:outgoing>Flow_7</bpmn:outgoing>
</bpmn:task>
<bpmn:sendTask id="SendTask_S7" name="Bescheid versenden">
<bpmn:incoming>Flow_7</bpmn:incoming>
<bpmn:outgoing>Flow_8</bpmn:outgoing>
</bpmn:sendTask>
<bpmn:endEvent id="EndEvent_S8" name="Antrag entschieden">
<bpmn:incoming>Flow_8</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1" sourceRef="StartEvent_S1" targetRef="Task_S2"/>
<bpmn:sequenceFlow id="Flow_2" sourceRef="Task_S2" targetRef="Gateway_S3"/>
<bpmn:sequenceFlow id="Flow_3" sourceRef="Gateway_S3" targetRef="Task_S5" name="Ja"/>
<bpmn:sequenceFlow id="Flow_4" sourceRef="Gateway_S3" targetRef="SendTask_S4" name="Nein"/>
<bpmn:sequenceFlow id="Flow_5" sourceRef="SendTask_S4" targetRef="Task_S2"/>
<bpmn:sequenceFlow id="Flow_6" sourceRef="Task_S5" targetRef="Task_S6"/>
<bpmn:sequenceFlow id="Flow_7" sourceRef="Task_S6" targetRef="SendTask_S7"/>
<bpmn:sequenceFlow id="Flow_8" sourceRef="SendTask_S7" targetRef="EndEvent_S8"/>
</bpmn:process>
<bpmndi:BPMNDiagram id="Diagram_1">
<bpmndi:BPMNPlane id="Plane_1" bpmnElement="Collaboration_1">
<bpmndi:BPMNShape id="Participant_1_di" bpmnElement="Participant_1" isHorizontal="true">
<dc:Bounds x="0" y="0" width="1366" height="720"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Lane_1_di" bpmnElement="Lane_1" isHorizontal="true">
<dc:Bounds x="0" y="0" width="1366" height="190"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Lane_2_di" bpmnElement="Lane_2" isHorizontal="true">
<dc:Bounds x="0" y="190" width="1366" height="340"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Lane_3_di" bpmnElement="Lane_3" isHorizontal="true">
<dc:Bounds x="0" y="530" width="1366" height="190"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Participant_Ext_1_di" bpmnElement="Participant_Ext_1" isHorizontal="true">
<dc:Bounds x="0" y="780" width="1366" height="100"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="StartEvent_S1_di" bpmnElement="StartEvent_S1">
<dc:Bounds x="110" y="77" width="36" height="36"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_S2_di" bpmnElement="Task_S2">
<dc:Bounds x="300" y="320" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_S3_di" bpmnElement="Gateway_S3">
<dc:Bounds x="490" y="335" width="50" height="50"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="SendTask_S4_di" bpmnElement="SendTask_S4">
<dc:Bounds x="680" y="235" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_S5_di" bpmnElement="Task_S5">
<dc:Bounds x="680" y="405" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_S6_di" bpmnElement="Task_S6">
<dc:Bounds x="870" y="585" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="SendTask_S7_di" bpmnElement="SendTask_S7">
<dc:Bounds x="1060" y="55" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="EndEvent_S8_di" bpmnElement="EndEvent_S8">
<dc:Bounds x="1250" y="77" width="36" height="36"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1_di" bpmnElement="Flow_1">
<di:waypoint x="146" y="95"/>
<di:waypoint x="223" y="95"/>
<di:waypoint x="223" y="360"/>
<di:waypoint x="300" y="360"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_2_di" bpmnElement="Flow_2">
<di:waypoint x="400" y="360"/>
<di:waypoint x="400" y="310"/>
<di:waypoint x="515" y="310"/>
<di:waypoint x="515" y="335"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_3_di" bpmnElement="Flow_3">
<di:waypoint x="515" y="385"/>
<di:waypoint x="515" y="410"/>
<di:waypoint x="680" y="410"/>
<di:waypoint x="680" y="445"/>
<bpmndi:BPMNLabel>
<dc:Bounds x="523" y="389.5" width="30" height="14"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_4_di" bpmnElement="Flow_4">
<di:waypoint x="540" y="360"/>
<di:waypoint x="565" y="360"/>
<di:waypoint x="565" y="275"/>
<di:waypoint x="680" y="275"/>
<bpmndi:BPMNLabel>
<dc:Bounds x="537.5" y="340" width="30" height="14"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_5_di" bpmnElement="Flow_5">
<di:waypoint x="713.333" y="315"/>
<di:waypoint x="713.333" y="770"/>
<di:waypoint x="350" y="770"/>
<di:waypoint x="350" y="400"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_6_di" bpmnElement="Flow_6">
<di:waypoint x="780" y="445"/>
<di:waypoint x="825" y="445"/>
<di:waypoint x="825" y="625"/>
<di:waypoint x="870" y="625"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_7_di" bpmnElement="Flow_7">
<di:waypoint x="970" y="625"/>
<di:waypoint x="1015" y="625"/>
<di:waypoint x="1015" y="95"/>
<di:waypoint x="1060" y="95"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_8_di" bpmnElement="Flow_8">
<di:waypoint x="1160" y="95"/>
<di:waypoint x="1250" y="95"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="MsgFlow_1_di" bpmnElement="MsgFlow_1">
<di:waypoint x="341.5" y="780"/>
<di:waypoint x="128" y="113"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="MsgFlow_2_di" bpmnElement="MsgFlow_2">
<di:waypoint x="746.667" y="315"/>
<di:waypoint x="683" y="780"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="MsgFlow_3_di" bpmnElement="MsgFlow_3">
<di:waypoint x="1110" y="135"/>
<di:waypoint x="1024.5" y="780"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
@@ -0,0 +1,20 @@
# Syntaxbeispiel mit Lanes und externem Beteiligten – fachlich nicht geprüft, nur zur Veranschaulichung.
Prozess: Antrag prüfen mit Zuständigkeiten (Beispiel)
Extern: Antragsteller
S1 Start Nachricht: Posteingang: Antrag geht ein
<~ Antragsteller
-> S2
S2 Task: Sachbearbeitung: Antrag erfassen
S3 Gateway XOR: Sachbearbeitung: Antrag vollständig?
Ja -> S5
Nein -> S4
S4 Sendeaufgabe: Sachbearbeitung: Unterlagen nachfordern
~> Antragsteller
-> S2
S5 Task: Sachbearbeitung: Entscheidung vorbereiten
S6 Task: Teamleitung: Entscheidung zeichnen
S7 Sendeaufgabe: Posteingang: Bescheid versenden
~> Antragsteller
-> S8
S8 Ende: Posteingang: Antrag entschieden
@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://picture-tool.local/bpmn" exporter="md_to_bpmn.py">
<bpmn:process id="Process_1" name="Antrag prüfen (Beispiel)" isExecutable="false">
<bpmn:startEvent id="StartEvent_S1" name="Antrag geht ein">
<bpmn:outgoing>Flow_1</bpmn:outgoing>
<bpmn:messageEventDefinition id="StartEvent_S1_ed"/>
</bpmn:startEvent>
<bpmn:task id="Task_S2" name="Antrag erfassen">
<bpmn:incoming>Flow_1</bpmn:incoming>
<bpmn:incoming>Flow_5</bpmn:incoming>
<bpmn:outgoing>Flow_2</bpmn:outgoing>
</bpmn:task>
<bpmn:exclusiveGateway id="Gateway_S3" name="Antrag vollständig?">
<bpmn:incoming>Flow_2</bpmn:incoming>
<bpmn:outgoing>Flow_3</bpmn:outgoing>
<bpmn:outgoing>Flow_4</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sendTask id="SendTask_S4" name="Unterlagen nachfordern">
<bpmn:incoming>Flow_4</bpmn:incoming>
<bpmn:outgoing>Flow_5</bpmn:outgoing>
</bpmn:sendTask>
<bpmn:task id="Task_S5" name="Antrag fachlich prüfen">
<bpmn:incoming>Flow_3</bpmn:incoming>
<bpmn:outgoing>Flow_6</bpmn:outgoing>
</bpmn:task>
<bpmn:exclusiveGateway id="Gateway_S6" name="Antrag begründet?">
<bpmn:incoming>Flow_6</bpmn:incoming>
<bpmn:outgoing>Flow_7</bpmn:outgoing>
<bpmn:outgoing>Flow_8</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:task id="Task_S7" name="Bewilligung vorbereiten">
<bpmn:incoming>Flow_7</bpmn:incoming>
<bpmn:outgoing>Flow_9</bpmn:outgoing>
</bpmn:task>
<bpmn:task id="Task_S8" name="Ablehnung vorbereiten">
<bpmn:incoming>Flow_8</bpmn:incoming>
<bpmn:outgoing>Flow_10</bpmn:outgoing>
</bpmn:task>
<bpmn:sendTask id="SendTask_S9" name="Bescheid versenden">
<bpmn:incoming>Flow_9</bpmn:incoming>
<bpmn:incoming>Flow_10</bpmn:incoming>
<bpmn:outgoing>Flow_11</bpmn:outgoing>
</bpmn:sendTask>
<bpmn:endEvent id="EndEvent_S10" name="Antrag entschieden">
<bpmn:incoming>Flow_11</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1" sourceRef="StartEvent_S1" targetRef="Task_S2"/>
<bpmn:sequenceFlow id="Flow_2" sourceRef="Task_S2" targetRef="Gateway_S3"/>
<bpmn:sequenceFlow id="Flow_3" sourceRef="Gateway_S3" targetRef="Task_S5" name="Ja"/>
<bpmn:sequenceFlow id="Flow_4" sourceRef="Gateway_S3" targetRef="SendTask_S4" name="Nein"/>
<bpmn:sequenceFlow id="Flow_5" sourceRef="SendTask_S4" targetRef="Task_S2"/>
<bpmn:sequenceFlow id="Flow_6" sourceRef="Task_S5" targetRef="Gateway_S6"/>
<bpmn:sequenceFlow id="Flow_7" sourceRef="Gateway_S6" targetRef="Task_S7" name="Ja"/>
<bpmn:sequenceFlow id="Flow_8" sourceRef="Gateway_S6" targetRef="Task_S8" name="Nein"/>
<bpmn:sequenceFlow id="Flow_9" sourceRef="Task_S7" targetRef="SendTask_S9"/>
<bpmn:sequenceFlow id="Flow_10" sourceRef="Task_S8" targetRef="SendTask_S9"/>
<bpmn:sequenceFlow id="Flow_11" sourceRef="SendTask_S9" targetRef="EndEvent_S10"/>
</bpmn:process>
<bpmndi:BPMNDiagram id="Diagram_1">
<bpmndi:BPMNPlane id="Plane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="StartEvent_S1_di" bpmnElement="StartEvent_S1">
<dc:Bounds x="165" y="80" width="36" height="36"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_S2_di" bpmnElement="Task_S2">
<dc:Bounds x="165" y="250" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_S3_di" bpmnElement="Gateway_S3">
<dc:Bounds x="165" y="420" width="50" height="50"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="SendTask_S4_di" bpmnElement="SendTask_S4">
<dc:Bounds x="80" y="590" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_S5_di" bpmnElement="Task_S5">
<dc:Bounds x="250" y="590" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_S6_di" bpmnElement="Gateway_S6">
<dc:Bounds x="165" y="760" width="50" height="50"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_S7_di" bpmnElement="Task_S7">
<dc:Bounds x="80" y="930" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_S8_di" bpmnElement="Task_S8">
<dc:Bounds x="250" y="930" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="SendTask_S9_di" bpmnElement="SendTask_S9">
<dc:Bounds x="165" y="1100" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="EndEvent_S10_di" bpmnElement="EndEvent_S10">
<dc:Bounds x="165" y="1270" width="36" height="36"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1_di" bpmnElement="Flow_1">
<di:waypoint x="183" y="116"/>
<di:waypoint x="183" y="183"/>
<di:waypoint x="215" y="183"/>
<di:waypoint x="215" y="250"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_2_di" bpmnElement="Flow_2">
<di:waypoint x="215" y="330"/>
<di:waypoint x="215" y="395"/>
<di:waypoint x="190" y="395"/>
<di:waypoint x="190" y="420"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_3_di" bpmnElement="Flow_3">
<di:waypoint x="190" y="470"/>
<di:waypoint x="190" y="495"/>
<di:waypoint x="300" y="495"/>
<di:waypoint x="300" y="590"/>
<bpmndi:BPMNLabel>
<dc:Bounds x="198" y="474.5" width="30" height="14"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_4_di" bpmnElement="Flow_4">
<di:waypoint x="215" y="445"/>
<di:waypoint x="240" y="445"/>
<di:waypoint x="240" y="590"/>
<di:waypoint x="130" y="590"/>
<bpmndi:BPMNLabel>
<dc:Bounds x="212.5" y="425" width="30" height="14"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_5_di" bpmnElement="Flow_5">
<di:waypoint x="80" y="630"/>
<di:waypoint x="20" y="630"/>
<di:waypoint x="20" y="290"/>
<di:waypoint x="165" y="290"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_6_di" bpmnElement="Flow_6">
<di:waypoint x="300" y="670"/>
<di:waypoint x="300" y="735"/>
<di:waypoint x="190" y="735"/>
<di:waypoint x="190" y="760"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_7_di" bpmnElement="Flow_7">
<di:waypoint x="190" y="810"/>
<di:waypoint x="190" y="835"/>
<di:waypoint x="130" y="835"/>
<di:waypoint x="130" y="930"/>
<bpmndi:BPMNLabel>
<dc:Bounds x="198" y="814.5" width="30" height="14"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_8_di" bpmnElement="Flow_8">
<di:waypoint x="215" y="785"/>
<di:waypoint x="240" y="785"/>
<di:waypoint x="240" y="930"/>
<di:waypoint x="300" y="930"/>
<bpmndi:BPMNLabel>
<dc:Bounds x="212.5" y="765" width="30" height="14"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_9_di" bpmnElement="Flow_9">
<di:waypoint x="130" y="1010"/>
<di:waypoint x="130" y="1055"/>
<di:waypoint x="198.333" y="1055"/>
<di:waypoint x="198.333" y="1100"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_10_di" bpmnElement="Flow_10">
<di:waypoint x="300" y="1010"/>
<di:waypoint x="300" y="1055"/>
<di:waypoint x="231.667" y="1055"/>
<di:waypoint x="231.667" y="1100"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_11_di" bpmnElement="Flow_11">
<di:waypoint x="215" y="1180"/>
<di:waypoint x="215" y="1225"/>
<di:waypoint x="183" y="1225"/>
<di:waypoint x="183" y="1270"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
@@ -0,0 +1,19 @@
# Syntaxbeispiel ohne Lanes – fachlich nicht geprüft, nur zur Veranschaulichung der Markdown-Syntax.
Prozess: Antrag prüfen (Beispiel)
S1 Start Nachricht: Antrag geht ein
S2 Task: Antrag erfassen
S3 Gateway XOR: Antrag vollständig?
Ja -> S5
Nein -> S4
S4 Sendeaufgabe: Unterlagen nachfordern
-> S2
S5 Task: Antrag fachlich prüfen
S6 Gateway XOR: Antrag begründet?
Ja -> S7
Nein -> S8
S7 Task: Bewilligung vorbereiten
-> S9
S8 Task: Ablehnung vorbereiten
S9 Sendeaufgabe: Bescheid versenden
S10 Ende: Antrag entschieden
+1
View File
@@ -38,6 +38,7 @@
"Bash(python scripts/gitea.py:*)",
"Bash(python scripts/setup_workspace.py:*)",
"Bash(python scripts/pruefe_umlaute.py:*)",
"Bash(python scripts/pruefe_prozesse.py:*)",
"Bash(python -m pytest:*)"
]
}
+8 -4
View File
@@ -33,7 +33,7 @@ Sie bleibt bewusst kurz: Details liegen in `docs/` und werden nur bei Bedarf gel
### Tests
- Vor jedem Commit: `python -m pytest -q .agents/skills` – alle Tests grün.
- 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.
- **Jeder neue oder geänderte Test bekommt einen deutschen Docstring**, erste Zeile in der Form `"""Prüft, dass …"""`.
@@ -45,6 +45,9 @@ Sie bleibt bewusst kurz: Details liegen in `docs/` und werden nur bei Bedarf gel
- **Keine personenbezogenen Daten, keine Zugangsdaten, keine internen Originalunterlagen** im Repository, in der Doku
oder in Prompts. Quelldokumente (Word/PDF) bleiben lokal unter `referenz/` (nicht versioniert); ins Repository kommen
nur die daraus abgeleiteten, bereinigten Arbeitsschritt-`.md` und BPMN-Dateien.
- **Prozessmodelle liegen in `prozesse/<name>/`** (ADR 0003, Regeln in `prozesse/README.md`). Die `.md` ist
maßgeblich, die `.bpmn` wird nie von Hand bearbeitet, sondern mit `python scripts/pruefe_prozesse.py --erzeugen`
neu erzeugt. Den Status `abgenommen` setzt der Agent nie von sich aus.
- Rollen und Verantwortliche in Prozessmodellen als Funktion benennen („Sachbearbeitung Flurbereinigung"),
nie als Person.
- Keine Python-Abhängigkeiten außer der Standardbibliothek und `pytest` ohne Issue-Absprache und Eintrag in
@@ -63,7 +66,7 @@ Sie bleibt bewusst kurz: Details liegen in `docs/` und werden nur bei Bedarf gel
vorbereiten. Ziel und Abnahmekriterien klären, bevor etwas geändert wird.
3. Branch `feature/<issue-nr>-<kurzname>` bzw. `bugfix/<issue-nr>-<kurzname>` von `main`.
4. In kleinen Schritten umsetzen; nur betroffene Dateien und Abschnitte lesen.
5. `python -m pytest -q .agents/skills` und `python scripts/pruefe_umlaute.py`. Erzeugte BPMN-Dateien zusätzlich
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`).
**Push und Pull Request macht der Mensch.**
@@ -80,10 +83,11 @@ Sie bleibt bewusst kurz: Details liegen in `docs/` und werden nur bei Bedarf gel
- Sitzungsbeginn: `python scripts/hallo.py` (Claude Code: `/hallo`)
- Tickets: `python scripts/gitea.py tickets` | `python scripts/gitea.py ticket Projektwissen 12` | `… suche "<text>"`
- Tests: `python -m pytest -q .agents/skills`
- Tests: `python -m pytest -q`
- Umlaute prüfen: `python scripts/pruefe_umlaute.py`
- Prozessmodelle prüfen bzw. alle `.bpmn` neu erzeugen: `python scripts/pruefe_prozesse.py [--erzeugen]`
- Workspace einrichten oder aktualisieren: `python scripts/setup_workspace.py` (mit `--pull` für fast-forward)
- BPMN erzeugen: `python .agents/skills/sn-bpmn/md_to_bpmn.py entwuerfe/<name>.md <name>.bpmn`
- Entwurf umwandeln: `python .agents/skills/sn-bpmn/md_to_bpmn.py entwuerfe/<name>.md entwuerfe/<name>.bpmn`
## Skills
+6 -3
View File
@@ -33,7 +33,7 @@ cd AG_Prozesse
python scripts/setup_workspace.py
# 3. Prüfen, dass alles läuft (Sekunden)
python -m pytest -q .agents/skills
python -m pytest -q
```
```bash
@@ -62,6 +62,8 @@ Du prüfst und änderst den Entwurf fachlich, dann: *„passt so, wandle um"*. H
Import in PICTURE. Syntax des Entwurfs: [`.agents/skills/sn-bpmn/Anleitung.md`](.agents/skills/sn-bpmn/Anleitung.md).
`referenz/` und `entwuerfe/` werden nicht versioniert — Quelldokumente bleiben auf deinem Rechner.
Soll das Modell für alle verfügbar sein, legt der Agent es auf Wunsch unter [`prozesse/`](prozesse/README.md) ab —
mit Steckbrief und Status `Entwurf`, per Pull Request. Dort liegen auch die fertigen `.bpmn` zum Herunterladen.
## Am Werkzeug mitarbeiten
@@ -71,7 +73,7 @@ Import in PICTURE. Syntax des Entwurfs: [`.agents/skills/sn-bpmn/Anleitung.md`](
| 2 | Offene Tickets ansehen | `python scripts/gitea.py tickets` |
| 3 | Ein Ticket im Detail lesen | `python scripts/gitea.py ticket Projektwissen 12` |
| 4 | Aufgabe an den Agenten geben | Skill `sn-aufgabe-bearbeiten`, Muster in [`03_arbeitsweise.md`](docs/leitfaden/03_arbeitsweise.md) |
| 5 | Nachweis erbringen | `python -m pytest -q .agents/skills` |
| 5 | Nachweis erbringen | `python -m pytest -q` |
| 6 | Pull Request | Vorlage [`docs/vorlagen/pull_request.md`](docs/vorlagen/pull_request.md) |
## Die Regeln, die immer gelten
@@ -94,7 +96,8 @@ Die vollständige, für Agenten verbindliche Fassung steht in [`AGENTS.md`](AGEN
|---|---|
| [`AGENTS.md`](AGENTS.md) / `CLAUDE.md` | Anweisungen für Agenten (kanonisch ist `AGENTS.md`) |
| [`workspace.toml`](workspace.toml) | Gitea-Organisation und Verzeichnis weiterer Repositories |
| `scripts/` | `hallo.py`, `setup_workspace.py`, `gitea.py`, `pruefe_umlaute.py` |
| [`prozesse/`](prozesse/README.md) | Versionierte Prozessmodelle, ein Ordner je Prozess (ADR 0003) |
| `scripts/` | `hallo.py`, `setup_workspace.py`, `gitea.py`, `pruefe_umlaute.py`, `pruefe_prozesse.py` |
| `.agents/skills/` | Skills, gespiegelt nach `.claude/skills/`; `sn-bpmn` enthält den Konverter |
| `.claude/commands/` | Slash-Befehle für Claude Code (`/hallo`) |
| `docs/status/` | **Aktueller Stand** — immer zuerst lesen |
+2 -2
View File
@@ -9,10 +9,10 @@
| `leitfaden/03_arbeitsweise.md` | stabil | Einstieg, Arbeitszyklus, Checklisten |
| `leitfaden/04_werkzeuge_einrichten.md` | stabil | Einrichtung: Claude Code als CLI und in VS Code; von null zur ersten Aufgabe |
| `leitfaden/05_git_arbeitsweise.md` | **verbindlich** | Branches, Pull Requests, was der Agent darf |
| `vorlagen/` | Vorlagen | Issue, PR, Übergabe, Repo-`AGENTS.md` |
| `vorlagen/` | Vorlagen | Issue, PR, Übergabe, Repo-`AGENTS.md`, Steckbrief eines Prozesses |
Die Fachdoku des Konverters (Markdown-Syntax, Elementkatalog, Modellierregeln) liegt beim Skill in
`.agents/skills/sn-bpmn/`.
`.agents/skills/sn-bpmn/`, die versionierten Prozessmodelle in `prozesse/` (Regeln: `prozesse/README.md`).
Regel: **Lebende** Dokumente werden mit jeder Änderung gepflegt, **Momentaufnahmen** nicht nachträglich geändert
(neue Analyse = neue Datei mit Datum).
@@ -0,0 +1,33 @@
# 0003 – Prozessmodelle liegen versioniert in `prozesse/<name>/`, die `.md` ist maßgeblich
Status: vorgeschlagen · Datum: 2026-09-23 · Beteiligte: AG Prozesse
## Kontext
Der Skill `sn-bpmn` legt Entwürfe in `entwuerfe/` ab; der Ordner ist nicht versioniert. Fertige Modelle hatten damit
keinen gemeinsamen Ort: Wer ein Modell ändern wollte, musste die letzte Fassung bei der Kollegin erfragen, und niemand
konnte nachvollziehen, welcher Stand abgenommen war. PICTURE ist Zielsystem des Imports, aber kein Ort für Review
und Versionsgeschichte.
## Entscheidung
Jedes Prozessmodell liegt in einem eigenen Ordner `prozesse/<name>/` mit genau drei Dateien: Steckbrief `README.md`,
Arbeitsschritt-Beschreibung `<name>.md` und die daraus erzeugte `<name>.bpmn`.
- Die **`.md` ist die maßgebliche Quelle**. Die `.bpmn` wird mit `python scripts/pruefe_prozesse.py --erzeugen`
erzeugt und mitcommittet, damit sie ohne Python aus Gitea heruntergeladen und in PICTURE importiert werden kann.
- Ein **Test prüft**, dass jede `.bpmn` byte-gleich mit der Umwandlung ihrer `.md` ist. Das geht, weil
`md_to_bpmn.py` deterministisch arbeitet (keine Zufalls-IDs, keine Zeitstempel).
- Der **Steckbrief** trägt den Status `Entwurf` oder `abgenommen`. Entwürfe dürfen auf `main`, damit mehrere
Personen daran arbeiten können; die Abnahme ist ein eigener Pull Request, der nur den Status ändert.
- Im Ordner liegen **keine weiteren Dateien**, insbesondere keine Originalunterlagen.
## Folgen
Modelle sind reviewbar (Diff der `.md` statt XML), versioniert und mit Status versehen. Eine Änderung am Konverter,
die die Ausgabe verändert, macht alle abgelegten `.bpmn` rot, bis sie neu erzeugt sind — gewollt: Der Pull Request
zeigt dann, welche Modelle sich im Bild ändern, und sie werden gemeinsam neu erzeugt. Nacharbeiten, die nur in
PICTURE gemacht werden (Layout, Zusatzfelder), gehen bei der nächsten Erzeugung verloren und gehören deshalb in die
`.md` oder in den Steckbrief.
## Verworfene Alternativen
Nur die `.md` versionieren (Fachleute ohne Python kämen nicht an die `.bpmn`); nur die `.bpmn` versionieren (XML ist
nicht reviewbar, die Quelle ginge verloren); Status in Kopfzeilen der `.md` (der Parser würde Zeilen wie
„Verantwortliche Stelle: …“ als Schritt deuten); Ablage nur in PICTURE (keine Versionsgeschichte, kein Review).
+1
View File
@@ -27,3 +27,4 @@ Kurz, mit Grund.
|---|---|---|
| 0001 | Arbeitsweise aus AG_QGIS/Projektwissen übernehmen, ohne Historie | vorgeschlagen |
| 0002 | Branchmodell: `main` plus Arbeitsbranches | vorgeschlagen |
| 0003 | Prozessmodelle liegen versioniert in `prozesse/<name>/`, die `.md` ist maßgeblich | vorgeschlagen |
+11 -4
View File
@@ -14,7 +14,7 @@ Einrichtung Schritt für Schritt: `04_werkzeuge_einrichten.md`.
Wer nur modelliert, braucht Git kaum: Claude Code im Ordner `AG_Prozesse` starten, Quelle nennen, Entwurf prüfen,
umwandeln. Git und Pull Requests kommen erst ins Spiel, wenn sich am Konverter, an den Regeln oder an der Doku
etwas ändern soll — oder wenn ein abgenommenes Modell im Repository abgelegt wird (STATUS, Baustelle 2).
etwas ändern soll — oder wenn ein Modell unter `prozesse/` abgelegt oder geändert wird (Abschnitt 2).
## 2. Einen Prozess modellieren
@@ -26,6 +26,8 @@ flowchart TD
C -->|"passt so, wandle um"| D["&lt;name&gt;.bpmn"]
D --> E["Import in PICTURE"]
E -->|"Darstellungsfehler"| F["Issue anlegen<br/><i>Skill sn-ticket</i>"]
E -->|"für alle ablegen"| G["prozesse/&lt;name&gt;/<br/>Status Entwurf, Pull Request"]
G -->|"fachlich bestätigt"| H["Pull Request: Status abgenommen"]
```
- **Der Agent wartet nach dem Entwurf.** Er wandelt erst um, wenn du ausdrücklich zustimmst. Das ist Absicht: die
@@ -35,6 +37,10 @@ flowchart TD
- **Rollen statt Personen.** Verantwortliche immer als Funktion („Sachbearbeitung"), nie mit Namen.
- **Darstellungsfehler in PICTURE** (Kanten durch Elemente, überlappende Lanes) sind Fehler des Konverters, nicht
deines Entwurfs. Issue anlegen, mit dem Entwurf als Beispiel (bereinigt).
- **Ablegen unter `prozesse/`** macht das Modell für alle sichtbar und versioniert (ADR 0003, Regeln in
`prozesse/README.md`). Die `.md` ist maßgeblich: Was nur in PICTURE nachgebessert wird, ist bei der nächsten
Erzeugung weg. **Abnahme** heißt: ein eigener Pull Request, der im Steckbrief den Status auf `abgenommen` setzt;
das Review dieses Pull Requests ist die fachliche Bestätigung.
## 3. Wie man einem Agenten einen Auftrag gibt
@@ -90,6 +96,7 @@ Werkzeugs (Claude Auto-Memory) ist ein Notizzettel; teilbares Wissen gehört in
| `.claude/commands/` | Slash-Befehle, z. B. `/hallo` |
| `.claude/settings.json` | Berechtigungen – sperrt `git push`, `merge`, `tag` |
| `docs/` | auf Abruf, nicht automatisch |
| `prozesse/` | auf Abruf; versionierte Modelle mit Steckbrief |
| `referenz/`, `entwuerfe/` | nur, wenn du darauf verweist; nicht versioniert |
| `CLAUDE.local.md` | ja, lokal, nicht versioniert |
@@ -114,15 +121,15 @@ kleine PRs machen.
**Vor dem PR**
- [ ] Issue verlinkt, Branch von `main`
- [ ] `python -m pytest -q .agents/skills` grün, `python scripts/pruefe_umlaute.py` ohne Funde
- [ ] Bei geänderter BPMN-Ausgabe: PICTURE-Import dokumentiert
- [ ] `python -m pytest -q` grün, `python scripts/pruefe_umlaute.py` ohne Funde
- [ ] 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
- [ ] Diff gelesen: nur, was das Issue verlangt
**Review**
- [ ] Fachlich richtig? (BPMN-Regeln, Begriffe)
- [ ] Fachlich richtig? (BPMN-Regeln, Begriffe; bei Modellen: Diff der `.md` lesen, nicht der `.bpmn`)
- [ ] Tests vorhanden und sinnvoll (nicht nur angepasst)?
- [ ] Umfang überschaubar (< 400 Zeilen)?
- [ ] Nach dem Merge: `STATUS.md`
+5 -5
View File
@@ -72,7 +72,7 @@ Die Skripte (`hallo.py`, `gitea.py`, `setup_workspace.py`, `pruefe_umlaute.py`)
git clone https://entwicklung.flurneuordnung-sachsen.de/AG_Prozesse/Projektwissen.git AG_Prozesse
cd AG_Prozesse
python scripts/setup_workspace.py # spiegelt die Skills, holt ggf. weitere Repos
python -m pytest -q .agents/skills # erwartet: alle Tests grün
python -m pytest -q # erwartet: alle Tests grün
```
Der Zielordner `AG_Prozesse` ist bewusst anders benannt als das Repository: darin können später weitere
@@ -116,8 +116,8 @@ Die Auto-Memory ist lokal und wird nicht geteilt; Teilbares gehört nach `docs/`
| Python | `ms-python.python` | Syntax, Interpreterwahl, Debugger |
| Markdown Preview Mermaid Support | `bierner.markdown-mermaid` | zeigt Mermaid-Diagramme in der Vorschau |
Ein BPMN-Betrachter als Erweiterung ist nicht nötig: `md_to_bpmn.py` kann eine Vorschau ohne PICTURE erzeugen,
siehe `.agents/skills/sn-bpmn/Anleitung.md`.
Zum Ansehen einer `.bpmn` ohne PICTURE eignet sich der Camunda Modeler (Desktop, offline) oder `demo.bpmn.io`,
siehe `.agents/skills/sn-bpmn/Anleitung.md` Abschnitt „Diagramm vorab prüfen“.
### 4.2 Persönliche Einstellungen
@@ -152,7 +152,7 @@ keinen `.vscode/`-Ordner. Wer sie haben will, kopiert sie sich in die eigene `se
## 6. Von null zur ersten Aufgabe (etwa eine Stunde)
1. Abschnitt 2 durcharbeiten; `python -m pytest -q .agents/skills` ist grün.
1. Abschnitt 2 durcharbeiten; `python -m pytest -q` ist grün.
2. Variante A oder B einrichten. Prüfung: der Agent nennt auf Nachfrage die Regeln aus `AGENTS.md`.
3. `/hallo` ausführen — holt den Stand.
4. Zum Kennenlernen einen kleinen, unkritischen Prozess modellieren lassen (Skill `sn-bpmn`), den Entwurf
@@ -171,7 +171,7 @@ keinen `.vscode/`-Ordner. Wer sie haben will, kopiert sie sich in die eigene `se
- [ ] `git --version` und `python --version` (≥ 3.11) laufen, `python -m pytest --version` ohne Fehler
- [ ] `git config user.email` im Ordner `AG_Prozesse` liefert die Adresse des Gitea-Kontos
- [ ] `python scripts/setup_workspace.py` ohne Fehler, `.claude/skills/` vorhanden
- [ ] `python -m pytest -q .agents/skills` grün
- [ ] `python -m pytest -q` grün
- [ ] `python scripts/hallo.py` läuft und zeigt den Stand
- [ ] `python scripts/gitea.py tickets` läuft
- [ ] Claude Code im Ordner `AG_Prozesse` gestartet; `/context` zeigt `CLAUDE.md` und `AGENTS.md`
+1 -1
View File
@@ -33,7 +33,7 @@ git add .agents/skills/sn-bpmn/md_to_bpmn.py .agents/skills/sn-bpmn/test_md_to_b
git commit -m "Lane-Beschriftung bricht lange Rollennamen um (#12)"
# 4. Prüfen
python -m pytest -q .agents/skills
python -m pytest -q
python scripts/pruefe_umlaute.py
# 5. Push — durch den Menschen, mit dessen Zugangsdaten
+6 -2
View File
@@ -18,8 +18,7 @@ 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).
2. **Ablageort für abgenommene Prozessmodelle festlegen.** Entwürfe liegen unversioniert in `entwuerfe/`.
Vorschlag: `prozesse/<name>/<name>.md` plus `<name>.bpmn`, versioniert, Änderungen per Pull Request. → ADR.
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.
4. **Leitfäden vom Team gegenlesen lassen.** `docs/leitfaden/03–05` sind für die Prozessarbeit neu gefasst;
@@ -29,6 +28,11 @@ Skills sind übernommen, alles QGIS- und Plugin-Spezifische ist entfernt. Kern i
## Erledigt
- 23.09.2026 **Ablage der Prozessmodelle** in `prozesse/<name>/` mit Steckbrief, `.md` und erzeugter `.bpmn`
(ADR 0003). `scripts/pruefe_prozesse.py` prüft Struktur, Status und Aktualität der `.bpmn`, auch als Test.
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`),
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 -2
View File
@@ -2,7 +2,7 @@
<Was soll am Ende möglich sein? Ein Satz aus Nutzersicht.>
## Betroffen
Bereich: Prozessmodell | Konverter `md_to_bpmn.py` | Skill | Skripte | Doku
Bereich: Prozessmodell (`prozesse/<name>/`) | Konverter `md_to_bpmn.py` | Skill | Skripte | Doku
Dateien (falls bekannt):
## Fachlicher Hintergrund
@@ -11,7 +11,7 @@ Dateien (falls bekannt):
## Abnahmekriterien
- [ ] <beobachtbares Verhalten 1>
- [ ] <beobachtbares Verhalten 2>
- [ ] Bei Änderungen am Konverter: Test vorhanden, `python -m pytest -q .agents/skills` grün
- [ ] Bei Änderungen am Konverter: Test vorhanden, `python -m pytest -q` grün
- [ ] PICTURE-Import: <nötig / nicht nötig>
## Nicht Teil dieses Issues
+19
View File
@@ -0,0 +1,19 @@
# <Name des Prozesses>
| | |
|---|---|
| Status | Entwurf |
| Stand | JJJJ-MM-TT |
| Zuständig (Funktion) | <z. B. Sachbearbeitung Flurbereinigung – nie eine Person> |
| Rechtsgrundlage | <z. B. § 41 FlurbG, oder „–“> |
| Quelle | <Art und Stand der Vorlage, z. B. „Dienstanweisung, Fassung 2025“ – nicht die Datei selbst> |
| PICTURE-Import geprüft | <JJJJ-MM-TT, PICTURE-Version> \| noch nicht |
## Zweck
<Zwei bis drei Sätze: Was leistet der Prozess, wann beginnt und wann endet er.>
## Annahmen und offene Punkte
<Was bei der Modellierung angenommen wurde und fachlich bestätigt werden muss. Leer, sobald abgenommen.>
## Änderungen
<Neueste oben: Datum – was – Pull Request.>
+2 -1
View File
@@ -2,8 +2,9 @@
<Zwei Sätze. Issue: #…>
## Wie geprüft
- `python -m pytest -q .agents/skills`: <Ergebniszeile>
- `python -m pytest -q`: <Ergebniszeile>
- `python scripts/pruefe_umlaute.py`: <Ergebniszeile>
- Bei Modellen unter `prozesse/`: `.bpmn` mit `python scripts/pruefe_prozesse.py --erzeugen` erzeugt
- PICTURE-Import: <PICTURE-Version, Datei, Ergebnis> | nicht nötig, weil …
## Übergabenotiz
+12
View File
@@ -5,6 +5,18 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
## BPMN und PICTURE (Skill sn-bpmn, md_to_bpmn.py)
- **Jede Änderung an der Ausgabe von `md_to_bpmn.py` macht die abgelegten `.bpmn` rot.** Der Test in
`tests/test_pruefe_prozesse.py` vergleicht jede `.bpmn` unter `prozesse/` und in `beispiele/` byte-genau mit der
Umwandlung ihrer `.md`. Nach einer Konverteränderung `python scripts/pruefe_prozesse.py --erzeugen` ausführen und
die neu erzeugten Dateien mit in den Pull Request nehmen. Das setzt voraus, dass der Konverter deterministisch
bleibt: keine Zufalls-IDs, keine Zeitstempel, keine Reihenfolge aus `set`-Iteration. (ADR 0003, 23.09.2026)
- **Metadaten gehören nicht als Kopfzeile in die Arbeitsschritt-`.md`.** `parse_markdown()` liest eine Zeile wie
`Verantwortliche Stelle: …` als Schritt mit ID `Verantwortliche` und Typ `Stelle` und bricht ab. Status und Stand
stehen deshalb im Steckbrief `README.md` des Prozessordners. (23.09.2026)
- **Zuständigkeit gilt auch für Gateways und Ereignisse.** Ohne `Rolle:`-Präfix landen sie in der Lane
„Nicht zugeordnet“. Ein `?` stört nur vor dem Doppelpunkt: `Sachbearbeitung: Antrag vollständig?` funktioniert.
(23.09.2026, beim Erstellen der Beispiele)
- **Ein Gateway hat in PICTURE nur vier feste Anschlusspunkte (N/O/S/W auf der Rautenkontur), keinen
variablen Rand wie ein Rechteck.** Eine Kante, die diesen Punkt verlässt bzw. dort ankommt, darf sich
danach **nicht** direkt zur Ziel-/Quellkoordinate drehen, wenn diese auf der anderen Achse liegt – sonst
+39
View File
@@ -0,0 +1,39 @@
# Prozessmodelle
Hier liegen die versionierten Prozessmodelle der AG Prozesse, ein Ordner je Prozess (ADR 0003).
Arbeitsentwürfe gehören nicht hierher, sondern nach `entwuerfe/` (nicht versioniert).
```
prozesse/
antrag-pruefen/
README.md Steckbrief: Status, Stand, Zuständigkeit, offene Punkte
antrag-pruefen.md Arbeitsschritt-Beschreibung – die maßgebliche Quelle
antrag-pruefen.bpmn daraus erzeugt, nie von Hand bearbeiten
```
## Regeln
- **Ordnername:** Kleinbuchstaben, Ziffern, Bindestriche (`antrag-pruefen`). Die beiden Modelldateien heißen wie
der Ordner.
- **Genau drei Dateien.** Keine Originalunterlagen (Word, PDF), keine Bilder — die bleiben lokal in `referenz/`.
- **Die `.md` ist maßgeblich.** Die `.bpmn` wird nach jeder Änderung neu erzeugt und mitcommittet:
`python scripts/pruefe_prozesse.py --erzeugen`. Änderungen, die in PICTURE nachgetragen werden, fließen nicht
zurück — wer etwas dauerhaft ändern will, ändert die `.md`.
- **Status im Steckbrief:** `Entwurf` (liegt zur gemeinsamen Bearbeitung hier) oder `abgenommen` (fachlich
bestätigt). Den Wechsel auf `abgenommen` macht ein eigener Pull Request, dessen Review die Abnahme ist.
- **Keine personenbezogenen Daten.** Zuständigkeiten als Funktion, keine Namen, Aktenzeichen oder Verfahrensdaten.
## Prüfen
```bash
python scripts/pruefe_prozesse.py # Struktur, Status, .bpmn aktuell?
python -m pytest -q # enthält dieselbe Prüfung
```
## Einen Prozess ablegen
1. Entwurf mit dem Skill `sn-bpmn` erstellen und fachlich prüfen.
2. Branch `feature/<issue-nr>-<prozessname>` von `main`.
3. Ordner anlegen, Entwurf als `<name>.md` hineinkopieren, Steckbrief aus `docs/vorlagen/prozess_readme.md`.
4. `python scripts/pruefe_prozesse.py --erzeugen`, dann Tests.
5. Pull Request — der Agent bereitet vor, der Mensch pusht.
+2
View File
@@ -0,0 +1,2 @@
[pytest]
testpaths = .agents/skills tests
+155
View File
@@ -0,0 +1,155 @@
#!/usr/bin/env python3
"""Prüft die versionierten Prozessmodelle unter prozesse/ und die Beispiele des Skills sn-bpmn.
Jeder Prozess liegt in einem eigenen Ordner prozesse/<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
<name>.bpmn daraus erzeugt, nie von Hand bearbeitet
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.
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
Exit-Code: 0 = in Ordnung, 1 = mindestens ein Befund.
"""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SKILL = ROOT / ".agents" / "skills" / "sn-bpmn"
PROZESSE = ROOT / "prozesse"
BEISPIELE = SKILL / "beispiele"
sys.path.insert(0, str(SKILL))
import md_to_bpmn # noqa: E402 (liegt im Skill-Ordner, nicht als Paket installiert)
ORDNERNAME = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
STATUSWERTE = ("Entwurf", "abgenommen")
STATUSZEILE = re.compile(r"^\|\s*Status\s*\|\s*(?P<wert>[^|]*?)\s*\|", re.MULTILINE)
def erzeuge_bpmn(md_pfad: Path) -> bytes:
"""Wandelt eine Arbeitsschritt-.md genau so um wie md_to_bpmn.py auf der Kommandozeile."""
text = md_pfad.read_text(encoding="utf-8")
meta, steps, order, externals = md_to_bpmn.parse_markdown(text)
return md_to_bpmn.prettify(md_to_bpmn.build_bpmn(meta, steps, order, externals))
def _relativ(pfad: Path) -> str:
try:
return pfad.relative_to(ROOT).as_posix()
except ValueError:
return pfad.as_posix()
def pruefe_modell(md_pfad: Path) -> list[str]:
"""Befunde zum Paar <name>.md / <name>.bpmn; leer, wenn die .bpmn aktuell ist."""
bpmn_pfad = md_pfad.with_suffix(".bpmn")
try:
erwartet = erzeuge_bpmn(md_pfad)
except ValueError as fehler:
return [f"{_relativ(md_pfad)}: lässt sich nicht umwandeln: {fehler}"]
if not bpmn_pfad.is_file():
return [f"{_relativ(bpmn_pfad)}: fehlt – erzeugen mit python scripts/pruefe_prozesse.py --erzeugen"]
vorhanden = bpmn_pfad.read_bytes().replace(b"\r\n", b"\n")
if vorhanden != erwartet.replace(b"\r\n", b"\n"):
return [f"{_relativ(bpmn_pfad)}: passt nicht zu {md_pfad.name} (von Hand geändert oder nicht neu "
f"erzeugt) – erzeugen mit python scripts/pruefe_prozesse.py --erzeugen"]
return []
def pruefe_ordner(ordner: Path) -> list[str]:
"""Befunde zu Name, Dateien und Steckbrief eines Prozessordners."""
name = ordner.name
ort = _relativ(ordner)
befunde: list[str] = []
if not ORDNERNAME.match(name):
befunde.append(f"{ort}: Ordnername nur aus Kleinbuchstaben, Ziffern und Bindestrichen (z. B. antrag-pruefen)")
erwartet = {"README.md", f"{name}.md", f"{name}.bpmn"}
vorhanden = {p.name for p in ordner.iterdir()}
for fehlend in sorted(erwartet - vorhanden):
befunde.append(f"{ort}: {fehlend} fehlt")
for fremd in sorted(vorhanden - erwartet):
befunde.append(f"{ort}: {fremd} gehört nicht hierher (nur README.md, {name}.md, {name}.bpmn; "
f"Originalunterlagen nach referenz/)")
readme = ordner / "README.md"
if readme.is_file():
treffer = STATUSZEILE.search(readme.read_text(encoding="utf-8"))
if not treffer:
befunde.append(f"{ort}/README.md: Zeile '| Status | … |' fehlt (Vorlage docs/vorlagen/prozess_readme.md)")
elif treffer.group("wert") not in STATUSWERTE:
befunde.append(f"{ort}/README.md: Status '{treffer.group('wert')}' unbekannt, erlaubt: "
+ " | ".join(STATUSWERTE))
md_pfad = ordner / f"{name}.md"
if md_pfad.is_file():
befunde += pruefe_modell(md_pfad)
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 beispiele(wurzel: Path = BEISPIELE) -> list[Path]:
return sorted(wurzel.glob("*.md")) if wurzel.is_dir() else []
def pruefe_alles() -> list[str]:
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():
befunde += pruefe_ordner(ordner)
for md_pfad in beispiele():
befunde += pruefe_modell(md_pfad)
return befunde
def erzeuge_alles() -> None:
quellen = [o / f"{o.name}.md" for o in prozessordner()] + beispiele()
for md_pfad in quellen:
if not md_pfad.is_file():
continue
try:
md_pfad.with_suffix(".bpmn").write_bytes(erzeuge_bpmn(md_pfad))
print(f"erzeugt: {_relativ(md_pfad.with_suffix('.bpmn'))}")
except ValueError as fehler:
print(f"übersprungen: {_relativ(md_pfad)}: {fehler}")
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--erzeugen", action="store_true", help="alle .bpmn aus ihren .md neu erzeugen, dann prüfen")
args = ap.parse_args()
if args.erzeugen:
erzeuge_alles()
print()
befunde = pruefe_alles()
for befund in befunde:
print(befund)
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).")
return 1 if befunde else 0
if __name__ == "__main__":
raise SystemExit(main())
+109
View File
@@ -0,0 +1,109 @@
"""Tests für scripts/pruefe_prozesse.py und für den Bestand unter prozesse/ – reines Python/pytest."""
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "scripts"))
import pruefe_prozesse as pp # noqa: E402
MODELL = """Prozess: Testprozess
S1 Start: Beginn
S2 Task: Etwas tun
S3 Ende: Fertig
"""
STECKBRIEF = """# Testprozess
| | |
|---|---|
| Status | {status} |
| Stand | 2026-09-23 |
"""
def lege_prozess_an(wurzel, name="test-prozess", status="Entwurf"):
ordner = wurzel / name
ordner.mkdir()
md = ordner / f"{name}.md"
md.write_text(MODELL, encoding="utf-8")
(ordner / f"{name}.bpmn").write_bytes(pp.erzeuge_bpmn(md))
(ordner / "README.md").write_text(STECKBRIEF.format(status=status), encoding="utf-8")
return ordner
def test_bestand_unter_prozesse_und_beispiele_ist_konsistent():
"""Prüft, dass alle Prozessordner und Skill-Beispiele im Repository ohne Befund sind."""
assert pp.pruefe_alles() == []
def test_es_gibt_mindestens_ein_beispiel():
"""Prüft, dass der Abgleich .md/.bpmn wenigstens an den Skill-Beispielen tatsächlich greift."""
assert pp.beispiele(), "keine Beispiele in .agents/skills/sn-bpmn/beispiele/"
def test_gueltiger_prozessordner_hat_keine_befunde(tmp_path):
"""Prüft, dass ein vollständiger Ordner mit aktueller .bpmn und gültigem Status durchgeht."""
assert pp.pruefe_ordner(lege_prozess_an(tmp_path)) == []
def test_status_abgenommen_ist_gueltig(tmp_path):
"""Prüft, dass der Status 'abgenommen' akzeptiert wird."""
assert pp.pruefe_ordner(lege_prozess_an(tmp_path, status="abgenommen")) == []
def test_unbekannter_status_wird_gemeldet(tmp_path):
"""Prüft, dass ein Status außerhalb von Entwurf/abgenommen gemeldet wird."""
befunde = pp.pruefe_ordner(lege_prozess_an(tmp_path, status="fertig"))
assert any("Status 'fertig' unbekannt" in b for b in befunde)
def test_fehlende_statuszeile_wird_gemeldet(tmp_path):
"""Prüft, dass ein Steckbrief ohne Statuszeile gemeldet wird."""
ordner = lege_prozess_an(tmp_path)
(ordner / "README.md").write_text("# Testprozess\n", encoding="utf-8")
assert any("Status | … |' fehlt" in b for b in pp.pruefe_ordner(ordner))
def test_ungueltiger_ordnername_wird_gemeldet(tmp_path):
"""Prüft, dass Großbuchstaben oder Unterstriche im Ordnernamen gemeldet werden."""
befunde = pp.pruefe_ordner(lege_prozess_an(tmp_path, name="Antrag_Pruefen"))
assert any("Ordnername" in b for b in befunde)
def test_fehlende_bpmn_wird_gemeldet(tmp_path):
"""Prüft, dass ein Ordner ohne erzeugte .bpmn gemeldet wird."""
ordner = lege_prozess_an(tmp_path)
(ordner / "test-prozess.bpmn").unlink()
befunde = pp.pruefe_ordner(ordner)
assert any("test-prozess.bpmn fehlt" in b for b in befunde)
def test_fremde_datei_im_prozessordner_wird_gemeldet(tmp_path):
"""Prüft, dass zusätzliche Dateien (etwa eine Original-PDF) im Prozessordner gemeldet werden."""
ordner = lege_prozess_an(tmp_path)
(ordner / "quelle.pdf").write_bytes(b"%PDF")
assert any("quelle.pdf gehört nicht hierher" in b for b in pp.pruefe_ordner(ordner))
def test_veraltete_bpmn_wird_gemeldet(tmp_path):
"""Prüft, dass eine .bpmn gemeldet wird, die nach einer Änderung der .md nicht neu erzeugt wurde."""
ordner = lege_prozess_an(tmp_path)
md = ordner / "test-prozess.md"
md.write_text(MODELL.replace("Etwas tun", "Etwas anderes tun"), encoding="utf-8")
assert any("passt nicht zu test-prozess.md" in b for b in pp.pruefe_ordner(ordner))
def test_zeilenenden_der_bpmn_spielen_keine_rolle(tmp_path):
"""Prüft, dass eine unter Windows mit CRLF ausgecheckte .bpmn nicht als veraltet gilt."""
ordner = lege_prozess_an(tmp_path)
bpmn = ordner / "test-prozess.bpmn"
bpmn.write_bytes(bpmn.read_bytes().replace(b"\n", b"\r\n"))
assert pp.pruefe_ordner(ordner) == []
def test_nicht_umwandelbare_md_wird_gemeldet(tmp_path):
"""Prüft, dass eine .md mit Syntaxfehler (doppelte Schritt-ID) als Befund statt als Absturz endet."""
ordner = lege_prozess_an(tmp_path)
(ordner / "test-prozess.md").write_text(MODELL + "S2 Task: Noch einmal S2\n", encoding="utf-8")
assert any("lässt sich nicht umwandeln" in b for b in pp.pruefe_ordner(ordner))