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:
@@ -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.**
|
||||
|
||||
@@ -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,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
|
||||
@@ -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:*)"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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).
|
||||
@@ -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 |
|
||||
|
||||
@@ -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["<name>.bpmn"]
|
||||
D --> E["Import in PICTURE"]
|
||||
E -->|"Darstellungsfehler"| F["Issue anlegen<br/><i>Skill sn-ticket</i>"]
|
||||
E -->|"für alle ablegen"| G["prozesse/<name>/<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`
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
testpaths = .agents/skills tests
|
||||
@@ -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())
|
||||
@@ -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))
|
||||
Reference in New Issue
Block a user