450 lines
16 KiB
Markdown
450 lines
16 KiB
Markdown
# Runbook: BMAD Phase 4 Automation in Paperclip aufsetzen
|
||
|
||
End-to-End-Anleitung, um von einer leeren Paperclip-Instanz zu einem
|
||
laufenden BMAD-Phase-4-Autopilot mit drei Agents zu kommen. Arbeite sie
|
||
in dieser Reihenfolge ab – jeder Schritt hat einen Verifikations-Check
|
||
am Ende.
|
||
|
||
**Zeit-Abschätzung:** 45-90 Min, wenn OpenCode und Paperclip schon
|
||
installiert sind. Plus die Zeit, die BMAD für Phase 1-3 und manuelles
|
||
Sprint Planning gebraucht hat.
|
||
|
||
## Voraussetzungen
|
||
|
||
- BMAD V6 Phase 1-3 abgeschlossen:
|
||
- `_bmad-output/planning-artifacts/PRD.md`
|
||
- `_bmad-output/planning-artifacts/architecture.md`
|
||
- Eine Epic-Quelle (siehe README für unterstützte Varianten)
|
||
- **Sprint Planning manuell durchgeführt:** Du hast `bmad-sprint-planning`
|
||
selbst ausgeführt, und `_bmad-output/implementation-artifacts/sprint-status.yaml`
|
||
existiert. Der Supervisor erwartet diese Datei und weigert sich zu
|
||
starten, wenn sie fehlt.
|
||
- OpenCode CLI installiert, `opencode auth login` durchgeführt, Zugriff
|
||
auf die gewünschten Modelle (das starke Code-Modell für Dev UND ein
|
||
anderes Modell für QA) bestätigt.
|
||
- Paperclip installiert und laufend (`paperclipai` lokal oder remote).
|
||
- `PAPERCLIP_API_URL` und `PAPERCLIP_API_KEY` als Env-Variablen gesetzt.
|
||
- Git-Worktree des Projekts ist clean.
|
||
|
||
## Schritt 1: Paperclip-Adapter-Schema live abfragen
|
||
|
||
Das Schema für `opencode_local` variiert leicht zwischen Paperclip-
|
||
Versionen. Hol dir die kanonische Form für deine Installation:
|
||
|
||
```bash
|
||
curl -sS "$PAPERCLIP_API_URL/llms/agent-configuration.txt" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | less
|
||
|
||
curl -sS "$PAPERCLIP_API_URL/llms/agent-configuration/opencode_local.txt" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||
```
|
||
|
||
Vergleich die Felder mit `config/agents.jsonc`. Besonders relevant:
|
||
|
||
- Wie heißt das Modell-Pinning-Feld? (`env.OPENCODE_MODEL`, `model`,
|
||
`runtimeModel`, …) – beide Worker brauchen ein explizites Pinning.
|
||
- Wird `sessionBehavior: "new"` unterstützt oder heißt es anders
|
||
(`fresh-always`, `no-resume`, …)?
|
||
|
||
**Verifikation:** Du hast Klarheit über die Pflicht- und Optional-Felder.
|
||
|
||
## Schritt 2: Skills in Paperclips Skill-Library einspielen
|
||
|
||
Drei SKILL.md-Dateien müssen in deine Company-Skill-Library:
|
||
|
||
1. `skills/monitor-bmad-progress/SKILL.md`
|
||
2. `skills/execute-bmad-dev-tasks/SKILL.md`
|
||
3. `skills/execute-bmad-qa-tasks/SKILL.md`
|
||
|
||
**Variante A – via Paperclip-UI:** Company → Skills → Create New Skill,
|
||
Inhalt hineinkopieren. Der Skill-Name muss exakt zum `name:` im
|
||
YAML-Frontmatter passen.
|
||
|
||
**Variante B – via API:** Endpoint aus deiner Installation abfragen:
|
||
|
||
```bash
|
||
curl -sS "$PAPERCLIP_API_URL/llms/skills-api.txt" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||
```
|
||
|
||
**Verifikation:**
|
||
|
||
```bash
|
||
curl -sS "$PAPERCLIP_API_URL/api/skills" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||
| jq '.[] | select(.name | startswith("monitor-bmad") or
|
||
startswith("execute-bmad-dev") or
|
||
startswith("execute-bmad-qa")) | .name'
|
||
```
|
||
|
||
Drei Namen müssen zurückkommen.
|
||
|
||
## Schritt 3: Company anlegen
|
||
|
||
Im Paperclip-UI: **+ Company**.
|
||
|
||
- **Name:** "BMAD Phase 4 – <dein-projektname>"
|
||
- **Mission:** "Automatisierte Ausführung der BMAD-V6-Implementation-
|
||
Phase mit Dev/QA-Rollen-Trennung."
|
||
- **Projekt-Pfad (cwd / project_root):** Dein BMAD-Projektverzeichnis.
|
||
|
||
```bash
|
||
curl -sS "$PAPERCLIP_API_URL/api/companies" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||
| jq '.[] | select(.name | contains("BMAD Phase 4"))'
|
||
```
|
||
|
||
Notiere `companyId` – brauchst du für alle folgenden Calls.
|
||
|
||
## Schritt 4: Supervisor hiren
|
||
|
||
```bash
|
||
export COMPANY_ID="<aus Schritt 3>"
|
||
export PROJECT_ROOT="/srv/projects/<dein-projektname>"
|
||
|
||
curl -sS -X POST "$PAPERCLIP_API_URL/api/agents" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d @- <<EOF
|
||
{
|
||
"companyId": "$COMPANY_ID",
|
||
"name": "PM Supervisor",
|
||
"role": "supervisor",
|
||
"icon": "📋",
|
||
"adapterType": "process",
|
||
"adapterConfig": {
|
||
"adapter": "opencode_local",
|
||
"cwd": "$PROJECT_ROOT",
|
||
"sessionBehavior": "resume-or-new",
|
||
"heartbeat": {"enabled": false}
|
||
},
|
||
"runtimeConfig": {"maxTurns": 80, "timeoutMs": 900000},
|
||
"desiredSkills": ["monitor-bmad-progress"],
|
||
"prompt": "<siehe agents.jsonc für den vollen Prompt>",
|
||
"budget": {"monthlyUsd": 20}
|
||
}
|
||
EOF
|
||
```
|
||
|
||
Notiere die `agentId` – Dev und QA referenzieren sie als `reportsTo`.
|
||
|
||
## Schritt 5: Dev-Worker hiren
|
||
|
||
```bash
|
||
curl -sS -X POST "$PAPERCLIP_API_URL/api/agents" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d @- <<EOF
|
||
{
|
||
"companyId": "$COMPANY_ID",
|
||
"name": "Dev Worker",
|
||
"role": "developer",
|
||
"icon": "💻",
|
||
"reportsTo": "<supervisor-agentId>",
|
||
"adapterType": "process",
|
||
"adapterConfig": {
|
||
"adapter": "opencode_local",
|
||
"cwd": "$PROJECT_ROOT",
|
||
"sessionBehavior": "new",
|
||
"env": {"OPENCODE_MODEL": "<starkes-code-modell>"},
|
||
"heartbeat": {"enabled": false}
|
||
},
|
||
"runtimeConfig": {"maxTurns": 300, "timeoutMs": 2700000},
|
||
"desiredSkills": ["execute-bmad-dev-tasks"],
|
||
"prompt": "<siehe agents.jsonc>",
|
||
"budget": {"monthlyUsd": 300, "perTaskUsdMax": 10}
|
||
}
|
||
EOF
|
||
```
|
||
|
||
## Schritt 6: QA-Engineer hiren
|
||
|
||
**Wichtig:** Das Modell MUSS sich nachweislich vom Dev-Modell
|
||
unterscheiden. Sonst verliert das adversariale Review seinen Wert.
|
||
|
||
```bash
|
||
curl -sS -X POST "$PAPERCLIP_API_URL/api/agents" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d @- <<EOF
|
||
{
|
||
"companyId": "$COMPANY_ID",
|
||
"name": "QA Engineer",
|
||
"role": "qa",
|
||
"icon": "🔍",
|
||
"reportsTo": "<supervisor-agentId>",
|
||
"adapterType": "process",
|
||
"adapterConfig": {
|
||
"adapter": "opencode_local",
|
||
"cwd": "$PROJECT_ROOT",
|
||
"sessionBehavior": "new",
|
||
"env": {"OPENCODE_MODEL": "<anderes-modell-als-dev>"},
|
||
"heartbeat": {"enabled": false}
|
||
},
|
||
"runtimeConfig": {"maxTurns": 200, "timeoutMs": 1800000},
|
||
"desiredSkills": ["execute-bmad-qa-tasks"],
|
||
"prompt": "<siehe agents.jsonc>",
|
||
"budget": {"monthlyUsd": 150, "perTaskUsdMax": 5}
|
||
}
|
||
EOF
|
||
```
|
||
|
||
**Verifikation Schritt 4-6:**
|
||
|
||
```bash
|
||
curl -sS "$PAPERCLIP_API_URL/api/companies/$COMPANY_ID/agents" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||
| jq '[.[] | {name, role, reportsTo, model: .adapterConfig.env.OPENCODE_MODEL}]'
|
||
```
|
||
|
||
Drei Agents, Supervisor top-level, Dev und QA beide unter Supervisor,
|
||
mit unterschiedlichen Modellen bei Dev/QA.
|
||
|
||
## Schritt 7: Smoke-Test der beiden Worker
|
||
|
||
Bevor echte Arbeit reinkommt: je ein Dummy-Task an Dev und QA.
|
||
|
||
**Dev-Smoke:**
|
||
|
||
```bash
|
||
curl -sS -X POST "$PAPERCLIP_API_URL/api/issues" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"companyId": "'$COMPANY_ID'",
|
||
"title": "Smoke test Dev: write hello-dev.txt",
|
||
"description": "Erzeuge hello-dev.txt im Projekt-Root mit Inhalt \"dev online\". Dann status=success melden.",
|
||
"assignedAgentId": "<dev-worker-agentId>"
|
||
}'
|
||
```
|
||
|
||
**QA-Smoke:**
|
||
|
||
```bash
|
||
curl -sS -X POST "$PAPERCLIP_API_URL/api/issues" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"companyId": "'$COMPANY_ID'",
|
||
"title": "Smoke test QA: read hello-dev.txt",
|
||
"description": "Lies hello-dev.txt, bestätige Inhalt, KEINE Änderungen. status=success mit dem gelesenen Text melden.",
|
||
"assignedAgentId": "<qa-engineer-agentId>"
|
||
}'
|
||
```
|
||
|
||
Beide Runs mit `exitCode: 0`. Räume `hello-dev.txt` danach auf.
|
||
|
||
**NICHT weitermachen**, bevor beide Smoke-Tests grün sind.
|
||
|
||
## Schritt 8: Bootstrap-Task einspielen
|
||
|
||
Jetzt der eigentliche Start: Supervisor baut die Task-Hierarchie aus den
|
||
BMAD-Planning-Artefakten.
|
||
|
||
```bash
|
||
curl -sS -X POST "$PAPERCLIP_API_URL/api/issues" \
|
||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d @bootstrap/initial-task.json
|
||
```
|
||
|
||
(Vorher in `initial-task.json` die `companyId` und `assignedAgentId`
|
||
eintragen – letzteres ist die Supervisor-ID aus Schritt 4.)
|
||
|
||
**Erwartung (im Paperclip-UI):**
|
||
|
||
- Bootstrap-Task: `ready` → `in-progress` → `completed`
|
||
- Child-Issues entstehen pro Story:
|
||
- `create-story (X.Y)` — Dev-Worker
|
||
- `dev-story (X.Y)` — Dev-Worker
|
||
- `code-review (X.Y)` — QA-Engineer
|
||
- Pro Epic zusätzlich `retrospective (Epic N)` — QA-Engineer
|
||
- Supervisor postet Comment: Epics, Stories, `epic_source_layout`
|
||
- Nur der erste `create-story`-Task ist auf `ready`, alle anderen
|
||
`blocked` mit Dependencies
|
||
|
||
**Mögliche Abbrüche beim Bootstrap:**
|
||
|
||
- `sprint_status_missing` → Sprint Planning nicht vorab ausgeführt.
|
||
Lauf `bmad-sprint-planning` manuell, starte neu.
|
||
- `no_epic_source_found` → keine der vier Epic-Layouts gefunden.
|
||
Prüfe `_bmad-output/planning-artifacts/`.
|
||
- `sprint_status_inconsistent` → Stories im sprint-status.yaml passen
|
||
nicht zur Epic-Quelle.
|
||
- `planning_incomplete` → PRD.md oder architecture.md fehlt.
|
||
|
||
## Schritt 9: Erste Story durchlaufen lassen
|
||
|
||
Der Supervisor setzt automatisch den ersten `create-story`-Task auf
|
||
`ready`, sobald Bootstrap fertig ist. Der Dev-Worker sollte ihn binnen
|
||
Sekunden aufgreifen.
|
||
|
||
**Happy Path für Story 1.1 → 1.2 → …:**
|
||
|
||
1. Dev: `create-story (1.1)` → Story-Datei existiert → completed
|
||
2. Dev: `dev-story (1.1)` → Implementierung, Commits, Tests → completed
|
||
3. QA: `code-review (1.1)` → verdict=approved → completed
|
||
4. ERST JETZT: Dev: `create-story (1.2)` → ...
|
||
|
||
**Keine Parallelität über Story-Grenzen hinweg.** Policy C (Strikte
|
||
Story-Ordnung) verhindert, dass `create-story (1.2)` startet, solange
|
||
Story 1.1 nicht approved ist. Die einzige Gleichzeitigkeit: während
|
||
Dev `dev-story (1.2)` macht, kann QA theoretisch `code-review (1.1)`
|
||
noch am Laufen haben – aber das passiert in der Praxis nicht, weil
|
||
Policy C auch das verhindert (Story 1.2 rührt sich ja erst, wenn Story
|
||
1.1 approved, d.h. code-review completed ist).
|
||
|
||
In der Praxis läuft also zu jedem Zeitpunkt EIN Task. Beide Agents
|
||
sind selten gleichzeitig beschäftigt. Das ist bewusste Verlangsamung
|
||
zugunsten sauberer Sequenzierung.
|
||
|
||
**Bei needs-rework:**
|
||
|
||
1. QA: `code-review (1.1)` → verdict=needs-rework → completed
|
||
2. Supervisor legt an:
|
||
- `dev-story (1.1) retry 1` (Dev) — mit Findings als Metadata
|
||
- `code-review (1.1) retry 1` (QA) — blockiert auf den Retry-Dev
|
||
3. Benachrichtigung an Operator: "Story 1.1 Retry 1/3 — N HIGH, M MEDIUM"
|
||
|
||
Bei Retry-Limit (3) erreicht → Eskalation mit vier Handlungsoptionen.
|
||
|
||
**Epic-Übergang:**
|
||
|
||
Nach `code-review` der letzten Story im Epic:
|
||
1. QA: `retrospective (Epic 1)` → completed
|
||
2. Erst jetzt wird der erste `create-story`-Task des nächsten Epics
|
||
ready gesetzt. Davor: blocked durch Single-Epic-Policy.
|
||
|
||
## Schritt 10: Häufige Fehler
|
||
|
||
**BMAD-Skill fragt interaktiv zurück:**
|
||
Prompt in Dev- oder QA-Skill präzisieren oder `--non-interactive`
|
||
an den BMAD-Skill-Aufruf anhängen.
|
||
|
||
**OpenCode-Session hängt > Timeout:**
|
||
Task-Timeout → SIGTERM. Supervisor markiert als `failed,
|
||
reason=timeout` und versucht Re-Schedule. Nach 3 Timeouts am gleichen
|
||
Task: Eskalation. Bei wiederholten Timeouts an einer Story: Story zu
|
||
groß, manuell splitten.
|
||
|
||
**Dev und QA nutzen versehentlich dasselbe Modell:**
|
||
Supervisor merkt das am `model_used`-Feld in den Agent-Reports und
|
||
informiert einmalig im Status-Report. Kein Abbruch – aber das Review
|
||
ist dadurch schwächer. Korrigiere die `env.OPENCODE_MODEL`-Werte in
|
||
den Agent-Configs.
|
||
|
||
**`concurrent_task`-Fehler:**
|
||
Ein Agent hat zwei Tasks gleichzeitig. Sollte durch Policy B nie
|
||
passieren. Wenn doch: Sofort-Stopp, Paperclip-Audit-Log prüfen, dann
|
||
neu starten. Möglicherweise Supervisor-Bug oder Paperclip-Atomicity-
|
||
Issue.
|
||
|
||
**`sessionBehavior: "new"` wird ignoriert:**
|
||
Schema-Name in deiner Paperclip-Version abweichend. In agents.jsonc
|
||
anpassen basierend auf Schritt 1.
|
||
|
||
**Operator ändert sprint-status.yaml mid-run:**
|
||
Supervisor erkennt das am Timestamp, eskaliert. Besser: Workflow
|
||
stoppen, anpassen, neu bootstrappen.
|
||
|
||
**QA findet NIE etwas:**
|
||
Entweder QA-Modell ist zu permissiv (Modell-Upgrade) oder die Stories
|
||
sind tatsächlich trivial (akzeptabel, dann warning_zero_findings
|
||
regelmäßig). Nach 3-5 approved-ohne-Findings: Stichprobe auf die
|
||
Implementierung werfen.
|
||
|
||
## Schritt 11: Patrol verstehen
|
||
|
||
Der Supervisor läuft nicht nur bei Events (`task_completed`), sondern
|
||
patrouilliert alle 15 Minuten (konfigurierbar in agents.jsonc
|
||
`heartbeat.intervalSec`). Das sorgt für maximale Autonomie: wenn ein
|
||
Agent hängt oder ein Ready-Setting verloren geht, bemerkt und behebt
|
||
der Supervisor das, ohne dass du eingreifen musst.
|
||
|
||
**Was du in Paperclip sehen wirst:**
|
||
|
||
- **Patrol ohne Funde:** Keine Comments. Stille Einträge im
|
||
Audit-Trail ("Patrol OK"). Das ist der Normalfall bei gesundem Lauf.
|
||
- **Patrol mit autonomer Aktion:** Ein Comment am Parent-Issue im
|
||
Format:
|
||
```
|
||
### Patrol-Report – <zeitstempel>
|
||
Graph-Status: 5/20 completed, 1 in-progress, 0 ready, 14 blocked
|
||
Aktuelle Story: Epic 1 Story 1.3, Task dev-story
|
||
Gefundene Blocker: stuck_in_progress_task (story 1.3, 47 min)
|
||
Autonome Aktionen: reset-to-ready für task dev-story (1.3)
|
||
Offene Eskalationen: keine
|
||
```
|
||
- **Patrol mit Eskalation:** Comment mit `attention_required=true` –
|
||
das heißt, du musst ran. Z.B. bei dirty worktree oder Retry-Limit.
|
||
|
||
**Was Patrol autonom auflöst:**
|
||
|
||
| Blocker | Aktion |
|
||
| --- | --- |
|
||
| Task hängt in-progress ohne Heartbeat | als failed markieren, ready reset, normaler Retry-Pfad |
|
||
| Task ready, aber kein Agent zugewiesen | manuelles Re-Assignment an zuständigen Agent |
|
||
| Vorgänger completed, Folge blocked | Ready-Check nachholen, falls passt → ready setzen |
|
||
| Zwei Tasks mit identischem Kompositschlüssel | jüngeren cancellen, älteren behalten |
|
||
| Goal offen, aber kein aktiver Task | nächsten korrekten Task identifizieren und ready setzen |
|
||
| Dirty worktree (uncommitted changes) | WIP-Commit mit erkennbarer Message, Flow weiter (kein Code-Verlust) |
|
||
|
||
**Was Patrol NICHT autonom macht (immer Eskalation):**
|
||
|
||
- Retry-Limit erreicht (inhaltliche Entscheidung nötig)
|
||
- Fehlender Agent (Governance-Entscheidung)
|
||
- Meta-Stall > 1 h trotz Patrol-Aktionen (autonome Mittel erschöpft)
|
||
|
||
**Besonderheit Dirty Worktree:**
|
||
|
||
Wenn der Supervisor uncommittete Changes im Projekt findet (typisch
|
||
nach einem abgebrochenen Dev-Task), macht er einen automatischen
|
||
WIP-Commit mit Message `wip: patrol-auto-commit from {task_type}
|
||
(story {story_id})`. Damit ist der Worktree wieder clean, der
|
||
Workflow läuft weiter, und du behältst die Änderungen vollständig
|
||
im Git-History. Du solltest solche WIP-Commits später manuell
|
||
prüfen – entweder revertst du sie und lässt den Dev-Agent sauber
|
||
neu arbeiten, oder du behältst sie, falls der Inhalt sinnvoll ist.
|
||
|
||
Diese landen als Info-Notiz mit `attention_required=false`. Du wirst
|
||
sie im Log sehen, aber sie stören deinen Schlaf nicht.
|
||
|
||
**Wenn du Patrol vorübergehend abschalten willst:**
|
||
In `agents.jsonc` beim Supervisor `heartbeat.enabled: false` setzen.
|
||
Dann läuft er wieder nur event-basiert – aber rechne damit, dass Hänger
|
||
dann liegen bleiben, bis du sie manuell lösst.
|
||
|
||
## Schritt 12: Reporting einrichten (optional)
|
||
|
||
Der Supervisor postet Status-Reports als Comments auf das Parent-Issue.
|
||
Für Push-Notifications:
|
||
|
||
- Paperclip-UI → Company → Integrations → Webhook auf
|
||
`issue_comment_created` für das Phase-4-Parent-Issue.
|
||
- Alternativ: Im Supervisor-Prompt Hook einbauen, der bei
|
||
`attention_required=true` einen connected MCP (Slack, Email) triggert.
|
||
|
||
## Was danach?
|
||
|
||
Sobald Story 1.1 einmal sauber durchgelaufen ist, ist die Schleife
|
||
bewiesen.
|
||
|
||
- **Cost-Dashboard täglich prüfen:** Retry-Spiralen sind der teuerste
|
||
Failure-Mode. 3-Retry-Limit deckelt, aber früh eingreifen lohnt sich.
|
||
- **Nach 3 Stories manuelle Stichprobe:** Qualität auf deinem Level?
|
||
- **QA-Findings-Muster beobachten:** Wenn eine Finding-Klasse
|
||
immer wiederkehrt → `project-context.md` erweitern, damit der Dev die
|
||
Konvention von Anfang an einhält.
|
||
|
||
## Zweites BMAD-Projekt automatisieren
|
||
|
||
Paperclip unterstützt mehrere Companies pro Installation:
|
||
|
||
1. Neue Company anlegen
|
||
2. Die drei Skills sind instance-weit verfügbar, kein Re-Upload
|
||
3. Drei neue Agents hiren mit neuem `cwd`
|
||
4. Bootstrap wie in Schritt 8
|
||
|
||
Die SKILL.md-Dateien sind projekt-agnostisch. Nur `cwd` der Agents
|
||
ändert sich.
|