disclaw/docs/workflow.md

20 KiB
Raw Permalink Blame History

DisClaw — Projekt- und Git-Workflow

Autor: Projekt- und Git-Manager-Agent Datum: 2026-04-08 Quelle der Wahrheit für Inhalt: docs/development-plan.md. Quelle der Wahrheit für den Backlog: docs/backlog.md.

Dieses Dokument definiert, wie an DisClaw gearbeitet wird: wie Branches heißen, wie Forgejo strukturiert ist, wie Issues geschnitten werden, wie ein Developer-Agent (Claude Sonnet 4.6) pro Issue vorgeht, und wann eine Phase als abgeschlossen gilt.

Forgejo-Remote: http://localhost:3000/dev/disclaw.git (lokale Instanz).


1. Branch-Strategie

1.1 main ist protected

  • main ist protected. Keine direkten Pushes, keine Force-Pushes, kein Rebase.
  • Änderungen kommen ausschließlich über Pull Requests.
  • Jeder PR benötigt mindestens ein Review-Approval und grüne Checks, bevor er gemerged wird.
  • Merge-Strategie: Squash-Merge. Eine Aufgabe = ein Commit auf main. Das hält die Historie linear und macht Rollback (git revert <sha>) trivial.

1.2 Naming-Convention für Feature-Branches

phase-<n>/<kurz-slug>

Beispiele:

  • phase-0/harden-spawn
  • phase-0/sanitize-env
  • phase-1/channel-queue
  • phase-1/split-for-discord

Begründung: Der Phase-Prefix macht sofort sichtbar, an welchem Phasen-Ziel ein Branch arbeitet. Das hilft beim Filtern in Forgejo (refs/heads/phase-0/*), beim Review-Priorisieren und beim mentalen Mapping zu Milestones. Der Kurz-Slug ist imperativ und eindeutig (harden-spawn, nicht fix-stuff). Keine Trailing-Nummern, keine Issue-IDs im Branch-Namen — die Verknüpfung passiert im PR via Closes #N.

1.3 Lifecycle eines Feature-Branches

  1. Issue wird in Forgejo Ready gesetzt und einem Developer-Agent zugewiesen.
  2. Developer-Agent checkt aktuellen main aus (git fetch origin && git checkout main && git pull --ff-only).
  3. Developer-Agent erstellt den Branch: git checkout -b phase-<n>/<slug>.
  4. Implementierung gegen die Definition of Done des Issues.
  5. Commits folgen Conventional-Commits (§5).
  6. Push: git push -u origin phase-<n>/<slug>.
  7. PR gegen main öffnen (Template in .forgejo/PULL_REQUEST_TEMPLATE.md).
  8. Review, Anpassungen, grüne Checks.
  9. Squash-Merge.
  10. Branch wird automatisch gelöscht (Forgejo-Setting: "Delete branch after merge").

1.4 Keine langlebigen Integration-Branches

  • Kein dev, kein next, kein phase-1-integration.
  • Eine Phase gilt als abgeschlossen, wenn alle Issues ihres Milestones gemerged sind und der manuelle Smoke-Test (§8) durch ist.
  • Der einzige geteilte Branch ist main.

2. Forgejo-Projektstruktur

2.1 Labels

Maximal schlank gehalten. Ziel: jeder Developer versteht das Label-Set in < 30 Sekunden.

Name Farbe (hex) Zweck
phase:0 #b71c1c Phase 0 — Härtung
phase:1 #e65100 Phase 1 — Solides MVP
phase:2 #f9a825 Phase 2 — Context-Effizienz (--resume)
phase:3 #2e7d32 Phase 3 — UX-Feinschliff
phase:4 #1565c0 Phase 4 — Skills & Permissions
phase:5 #4527a0 Phase 5 — Streaming
phase:6 #424242 Phase 6+ — Zukunft / Platzhalter
type:feat #0e8a16 Neues Feature
type:fix #d93f0b Bugfix
type:refactor #5319e7 Umstrukturierung ohne Verhaltensänderung
type:test #006b75 Tests oder Test-Infrastruktur
type:docs #1d76db Dokumentation
type:chore #bfbfbf Build, CI, Dependencies, Housekeeping
priority:p0 #b60205 Blocker, muss sofort
priority:p1 #d93f0b Wichtig in aktueller Phase
priority:p2 #fbca04 Kann in späteren Phasen warten
blocked #000000 Hängt an anderem Issue — Body nennt Blocker
needs-review #fbca04 PR wartet auf Review
security #b60205 Sicherheitsrelevant (CVE, Secret-Leak, Injection)

Begründung: Phase-Label + Type-Label + Priority-Label sind orthogonal und genügen, um einen Backlog zu filtern. Zusätzliche Labels wie good-first-issue, help-wanted, discussion lehnen wir ab — sie sind bei einem Zwei-Agent-Setup (PM + Developer) wertlos und erzeugen Noise. security ist ein Sonderfall: separat, weil es automatisierte Eskalations-Policies erlauben soll (z. B. P0 + security → sofortige Zuweisung).

2.2 Milestones

Ein Milestone pro Phase. Titel und Description exakt aus docs/development-plan.md §4 abgeleitet:

Milestone Titel Description (Kurzform der Definition of Done)
M0 Phase 0 — Härtung Kein shell: true, sanitizedEnv, Path-Containment, --bare + --append-system-prompt-file, Workspace-Root außerhalb Repo, CLAUDE.md mit Injection-Klausel, settings.json mit Deny-Liste. Alle P0-Security-Issues geschlossen.
M1 Phase 1 — Solides MVP Pino-Logger, ChannelQueue, Semaphore, splitForDiscord, Zod-Schemas, codeblock-aware Splitting, .env.example, npm test grün, keine console.* im Prod-Code.
M2 Phase 2 — Context-Effizienz claude_session_id in DB, Runner nutzt --resume, Fork-Case abgefangen, Usage-Metriken geloggt, Session überlebt Bot-Neustart.
M3 Phase 3 — UX-Feinschliff Attachment-Download, Reaction-Markers, sendResponse mit Attachment-Fallback, Typing-Refresh 8s.
M4 Phase 4 — Skills & Permissions Profile-Registry (5 Profiles), PreToolUse-Hook guard-tool.cjs, Audit-Log, profile-Option in /new-agent.
M5 Phase 5 — Streaming runAgentStream, NDJSON-Parser defensiv, stream-to-discord mit Rate-Limit, Feature-Flag in disclaw.yaml.
M6 Phase 6+ — Zukunft /list-agents, /delete-agent, /agent-config, Rate-Limits, Budget, Agent-zu-Agent, Docker, Dashboard. Nur Platzhalter, wird bei Phase-Start verfeinert.

Ein Milestone ist closed, sobald alle seine Issues closed sind und der Smoke-Test erfolgreich durchgeführt wurde.

2.3 Kanban-Board (Forgejo Project)

Ein einziges Projekt namens DisClaw Delivery mit fünf Spalten. Kein separates Board pro Phase — Phase wird über Label-Filter dargestellt.

Spalte Kriterium für Einzug
Backlog Issue existiert, wurde vom PM geschnitten, hat Titel, Phase-Label, Type-Label, Milestone. Noch nicht priorisiert für aktuelle Phase.
Ready Issue hat vollständige Definition of Done, alle Dependencies aufgelöst (keine blocked-Labels), Priority gesetzt, für aktuelle Phase priorisiert. Darf sofort von einem Developer-Agent gezogen werden.
In Progress Ein Developer-Agent ist zugewiesen, ein Feature-Branch existiert und wurde nach origin gepusht (mindestens der erste Commit).
In Review PR ist geöffnet, needs-review-Label gesetzt, CI-Checks laufen oder sind grün.
Done PR ist gemerged, Issue ist geschlossen. Kein manuelles Verschieben — Forgejo erledigt das automatisch über Closes #N im PR-Body.

Regel: Eine Karte wird rückwärts verschoben, wenn ein Kriterium verloren geht (z. B. PR wird geschlossen ohne Merge → zurück nach In Progress; neue Abhängigkeit wird entdeckt → zurück nach Backlog mit blocked-Label).

2.4 Bootstrap via Forgejo-API

Die folgenden Endpunkte setzen Labels, Milestones und das Projekt-Board auf. Alle Pfade sind echte Forgejo-API-Pfade (Forgejo spricht die Gitea-API v1 und deckt zusätzlich eigene Project-Routen ab). Das Bootstrap passiert per Script (siehe §2.5); dieses Dokument erstellt nichts direkt.

Umgebungsvariablen:

FORGEJO_BASE=http://localhost:3000
FORGEJO_OWNER=dev
FORGEJO_REPO=disclaw
FORGEJO_TOKEN=<personal-access-token mit repo-scope>

Labels anlegen: POST /api/v1/repos/{owner}/{repo}/labels

curl -sS -X POST \
  -H "Authorization: token $FORGEJO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"phase:0","color":"#b71c1c","description":"Phase 0 — Härtung"}' \
  "$FORGEJO_BASE/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO/labels"

Milestones anlegen: POST /api/v1/repos/{owner}/{repo}/milestones

curl -sS -X POST \
  -H "Authorization: token $FORGEJO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Phase 0 — Härtung","description":"Kein shell:true, sanitizedEnv, ..."}' \
  "$FORGEJO_BASE/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO/milestones"

Issues anlegen: POST /api/v1/repos/{owner}/{repo}/issues

curl -sS -X POST \
  -H "Authorization: token $FORGEJO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"...","body":"...","labels":[1,5,12],"milestone":1}' \
  "$FORGEJO_BASE/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO/issues"

(Label- und Milestone-IDs kommen aus der Response der Create-Calls oben.)

Branch-Protection für main: POST /api/v1/repos/{owner}/{repo}/branch_protections

curl -sS -X POST \
  -H "Authorization: token $FORGEJO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "branch_name":"main",
    "enable_push":false,
    "required_approvals":1,
    "enable_status_check":true,
    "status_check_contexts":["build","test","lint"],
    "block_on_rejected_reviews":true,
    "block_on_outdated_branch":true
  }' \
  "$FORGEJO_BASE/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO/branch_protections"

Projekte (Kanban-Board): Forgejo unterstützt Projekte pro Repo unter POST /api/v1/repos/{owner}/{repo}/projects mit {"title":"DisClaw Delivery","board_type":"basic_kanban"}. Spalten werden nachgelagert angelegt über POST /api/v1/projects/{id}/columns mit {"title":"Ready"} etc.

Alternative: tea CLI (https://gitea.com/gitea/tea). Die relevanten Kommandos:

tea labels create --name "phase:0" --color "#b71c1c" --description "Phase 0 — Härtung"
tea milestones create --title "Phase 0 — Härtung" --description "..."
tea issues create --title "..." --body "..." --labels "phase:0,type:fix,priority:p0" --milestone "Phase 0 — Härtung"

tea authentifiziert sich über tea login add einmalig mit FORGEJO_TOKEN.

2.5 Bootstrap-Script (skizziert, NICHT angelegt)

Ein späteres Script unter scripts/bootstrap-forgejo.ts soll:

  1. docs/backlog.md parsen (jedes Issue als YAML-Frontmatter-Block oder strukturiertes Markdown).
  2. Labels anlegen (idempotent: vorher GET /labels, skip wenn vorhanden).
  3. Milestones anlegen (idempotent).
  4. Projekt + Spalten anlegen (idempotent).
  5. Branch-Protection setzen.
  6. Für jedes Backlog-Issue: POST /issues, DIS-XXX im Backlog-File durch die echte Issue-Nummer ersetzen.
  7. Jede Karte ins Backlog-Kanban-Board einhängen.

Abhängigkeiten: node-fetch (oder Node 20 native), js-yaml, nichts weiteres. Läuft einmalig, ist re-runnable (idempotent). Legt dieses Dokument nicht an.


3. Issue-Template

Jedes Issue folgt exakt diesem Template. Abweichungen werden vom PM vor dem Create korrigiert. Das Template landet zusätzlich unter .forgejo/ISSUE_TEMPLATE/feature.md als Forgejo-Template.

## Ziel
<12 Sätze, was erreicht werden soll.>

## Kontext
Referenz: `docs/development-plan.md` §<Abschnitt>, ggf. `docs/ai-engineer-analyse.md` §<Abschnitt>.
<Kurze Begründung, warum das jetzt gemacht wird und welches Problem es löst.>

## Scope
- **In**: <konkret was gemacht wird>
- **Out**: <konkret was NICHT gemacht wird  damit kein Scope-Creep>

## Definition of Done
- [ ] <konkreter, überprüfbarer Punkt 1>
- [ ] <konkreter, überprüfbarer Punkt 2>
- [ ] Unit-Tests geschrieben und grün (`npm test`)
- [ ] Keine `console.*` im Produktionscode (falls betroffen)
- [ ] PR-Template ausgefüllt, Review bestanden, CI grün

## Dateien (erwartet)
- `src/<pfad1>`<zweck>
- `tests/<pfad2>`<zweck>

## Branch
`phase-<n>/<slug>`

## Abhängigkeiten
- Blockiert durch: #<issue-nr> (falls vorhanden)
- Blockiert: #<issue-nr> (falls vorhanden)

4. PR-Template

Datei: .forgejo/PULL_REQUEST_TEMPLATE.md (Forgejo liest dieses Verzeichnis; Fallback .gitea/PULL_REQUEST_TEMPLATE.md wird ebenfalls erkannt).

## Summary
<13 Sätze: was ändert dieser PR und warum.>

## Closes
Closes #<issue-nr>

## Changes
- <Bullet für jede relevante Datei-Gruppe>

## Test plan
- [ ] `npm run build` läuft grün
- [ ] `npm test` läuft grün
- [ ] Manueller Smoke-Test: <falls sinnvoll; sonst "n/a">
- [ ] Neue Dateien haben Unit-Tests (falls anwendbar)

## Screenshots / Logs
<optional  Log-Snippets bei Runtime-Änderungen>

## Breaking Changes
- [ ] Keine
- [ ] Ja: <Beschreibung + Migrationshinweis>

## Reviewer-Checklist
- [ ] Code-Diff passt zum Issue-Scope (kein Scope-Creep)
- [ ] Definition of Done aus dem Issue ist erfüllt
- [ ] Keine neuen `console.*`-Calls (außer in Tests/Probe-Scripts)
- [ ] Keine Secrets, Tokens oder absoluten Pfade in Logs
- [ ] `shell: false` bei jedem Spawn (falls Spawn betroffen)
- [ ] Sicherheitsrelevanter Code hat mindestens einen negativen Test

5. Commit-Convention

Conventional Commits mit Phase-Scope. Format:

<type>(<scope>): <subject>

<optional body>

<optional footer: Closes #N>

Types (deckungsgleich mit type:*-Labels): feat, fix, refactor, test, docs, chore, perf, build, ci.

Scope: Phase oder Modul. Bevorzugt Phase bei Phase-spezifischen Changes (feat(phase-1): add channel queue), Modul bei Cross-Phase-Infrastruktur (fix(runner): handle ENOENT on claude.cmd).

Beispiele:

feat(phase-0): sanitize child env before spawning claude

Removes DISCORD_BOT_TOKEN and all TOKEN_/SECRET_/API_KEY_ variables
from the environment passed to the claude child process. Adds an
allow-only whitelist helper in src/runtime/env.ts.

Closes #3
fix(runner): resolve claude.cmd via cross-spawn on Windows

CVE-2024-27980 blocks direct .cmd spawning without shell:true on
Node 20.12+. cross-spawn handles the wrapper resolution and arg
quoting transparently.

Closes #2
refactor(router): extract ChannelQueue into runtime/channel-queue.ts

Regel: Subject <= 72 Zeichen, imperativ, kein Punkt am Ende. Body erklärt das Warum, nicht das Was — der Diff zeigt bereits das Was.

Squash-Merge: Beim Merge in main wird der PR-Titel zum finalen Commit-Subject. Der PM achtet deshalb darauf, dass der PR-Titel Conventional-Commits-konform ist.


6. Developer-Agent-Workflow (Claude Sonnet 4.6)

Ein Developer-Agent arbeitet genau ein Issue pro Branch/PR. Nie Issue-Pooling.

6.1 Ablauf pro Issue

  1. Ticket ziehen: Issue aus Ready nehmen, sich selbst zuweisen, Status auf In Progress setzen.
  2. Branch erstellen:
    git fetch origin
    git checkout main
    git pull --ff-only
    git checkout -b phase-<n>/<slug>
    
  3. Implementieren: Strikt entlang der Definition of Done. Keine zusätzlichen Änderungen außerhalb des Dateien (erwartet)-Blocks — wenn eine zusätzliche Datei nötig wird, zuerst das Issue kommentieren, nicht einfach drauflosschreiben.
  4. Tests: Für jede neue reine Funktion ein Unit-Test. Integration-Tests nur, wenn das Issue es explizit verlangt oder ein Mehrkomponenten-Flow betroffen ist.
  5. Commits: Kleine, thematisch fokussierte Commits mit Conventional-Format. Der finale Squash verdichtet sie ohnehin, aber die Branch-Historie hilft beim Review.
  6. Lokale Vor-Checks:
    npm run build
    npm test
    
    Beide müssen grün sein, bevor gepusht wird.
  7. Pushen: git push -u origin phase-<n>/<slug>.
  8. PR öffnen: Via tea pulls create oder Forgejo-Web-UI. PR-Template ausfüllen, Closes #<n> einfügen, Label needs-review setzen. Spalte In Review.
  9. Self-Review-Checklist: Reviewer-Checklist im PR-Body selbst durchgehen, bevor um ein Review gebeten wird. Saves a round-trip.
  10. Auf Review warten. Bei Change-Requests: Anpassungen als neue Commits (kein Force-Push) auf denselben Branch.
  11. Nach Merge: Branch wird automatisch gelöscht, Issue schließt automatisch über Closes #N.

6.2 Rolle des Projekt-/Git-Manager-Agents (dieser Agent)

  • Schneidet Issues gemäß docs/development-plan.md und schreibt sie in docs/backlog.md.
  • Priorisiert innerhalb eines Milestones (priority:p0 > p1 > p2).
  • Weist Developer-Agenten zu (im Ein-Developer-Setup trivial, bei mehreren nach Load).
  • Koordiniert Reviews, triggert bei festhängenden PRs nach.
  • Stellt Phasen-Abschluss fest, führt den Smoke-Test durch (siehe §8).
  • Entscheidet über blocked-Labels und löst Blocker auf, indem fehlende Vor-Issues nachgeschnitten werden.
  • Schreibt keinen Produktionscode. Ausnahme: Bootstrap-Scripts und Dokumentation.

6.3 Rollback bei kaputtem PR

  • PR ist nach Merge kaputt: git revert <merge-sha> auf einem neuen Branch phase-<n>/revert-<slug>, normaler PR-Flow. Niemals git reset auf main.
  • PR ist vor Merge kaputt: Branch bleibt offen, In ReviewIn Progress, Developer-Agent fixt auf demselben Branch. Kein Force-Push. Keine neuen Branches.
  • Feature muss komplett zurückgezogen werden: Issue wieder öffnen, blocked-Label, Begründung als Kommentar, Revert-PR mergen.

7. Test-Gate pro PR

Jeder PR muss vor Merge folgende Gates passieren:

  1. Build: npm run build — TypeScript-Compile ohne Fehler.
  2. Unit-Tests: npm test — Vitest-Suite grün.
  3. Lint (sobald Phase 0 Issue "Add ESLint + shell:false-Rule" gemerged ist): npm run lint grün.
  4. Manueller Smoke-Test: Nur bei Änderungen am Bot-Startup, Runner, oder Discord-Integration. Im PR-Template als separater Haken dokumentiert.

CI wird in Phase 0 als eigenes Issue aufgesetzt (siehe docs/backlog.md Issue DIS-008: Forgejo Actions Workflow mit build, test, lint auf ubuntu-latest und windows-latest).

Lokal ausführbar:

npm install
npm run build
npm test

Ein fehlgeschlagener Test = kein Merge. Keine --skip-Flags, kein xit-Workaround.


8. Phasen-Übergang

8.1 Definition "Phase abgeschlossen"

Eine Phase ist genau dann abgeschlossen, wenn alle folgenden Bedingungen gelten:

  1. Alle Issues des Milestones sind im Status Done (PR gemerged).
  2. Der manuelle Smoke-Test für die Phase ist durchgelaufen (siehe §8.2).
  3. Der PM hat das Milestone in Forgejo manuell auf closed gesetzt.
  4. Der User hat die nächste Phase explizit freigegeben (siehe §8.3).

8.2 Smoke-Test pro Phase

Der PM führt einen kurzen End-to-End-Test auf dem aktuellen main durch. Mindest-Umfang:

  • Phase 0: Bot startet, /new-agent erstellt Workspace unter ~/.disclaw/workspaces/, .claude/settings.json enthält Deny-Liste, ein Test-Prompt mit Eval-Versuch (echo $DISCORD_BOT_TOKEN) kommt leer zurück.
  • Phase 1: Drei Messages schnell hintereinander im gleichen Channel werden in Reihenfolge beantwortet, Logs sind strukturiertes JSON, splitForDiscord schneidet einen langen Codeblock sauber auf.
  • Phase 2: Zweite Message im selben Channel zeigt cache_read_input_tokens > 0 im Log.
  • Phase 3: Bild-Upload im Discord wird vom Agent beschrieben.
  • Phase 4: Agent mit profile:researcher kann nicht schreiben; Audit-Log enthält blockierten Tool-Call.
  • Phase 5: Lange Antwort erscheint inkrementell, finaler Stand stimmt.

Das konkrete Skript wird in Phase 1 als scripts/smoke/<phase>.md-Checkliste gepflegt.

8.3 Keine automatische Phasen-Progression

Der PM startet die nächste Phase nicht automatisch. Der User muss explizit freigeben ("Phase X ist fertig, starte Phase Y"). Ohne diese Freigabe bleibt der Backlog für die nächste Phase im Zustand Backlog (noch nicht Ready).

Grund: Phasen-Übergänge sind der natürliche Moment, um den Plan zu überprüfen, die verbleibenden Epic-Level-Issues der nächsten Phase zu verfeinern, und eventuell gelernte Erkenntnisse zurückzuspielen.


9. Anhang — Konventionen zusammengefasst

Artefakt Ort Format
Branch-Name Git phase-<n>/<slug>
Commit-Message Git Conventional Commits mit Phase-Scope
Issue Forgejo Template §3, Labels aus §2.1, Milestone aus §2.2
PR Forgejo Template §4, squash-merge, Closes #N im Body
PR-Template-Datei Repo .forgejo/PULL_REQUEST_TEMPLATE.md
Issue-Template-Datei Repo .forgejo/ISSUE_TEMPLATE/feature.md
Backlog Repo docs/backlog.md (Source of Truth vor Forgejo-Sync)
Workflow Repo docs/workflow.md (dieses Dokument)
Bootstrap-Script Repo scripts/bootstrap-forgejo.ts (noch nicht angelegt)