# 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 `) trivial. ### 1.2 Naming-Convention für Feature-Branches ``` phase-/ ``` 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-/`. 4. Implementierung gegen die Definition of Done des Issues. 5. Commits folgen Conventional-Commits (§5). 6. Push: `git push -u origin phase-/`. 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= ``` **Labels anlegen**: `POST /api/v1/repos/{owner}/{repo}/labels` ```bash 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` ```bash 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` ```bash 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` ```bash 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: ```bash 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. ```markdown ## Ziel <1–2 Sätze, was erreicht werden soll.> ## Kontext Referenz: `docs/development-plan.md` §, ggf. `docs/ai-engineer-analyse.md` §. ## Scope - **In**: - **Out**: ## Definition of Done - [ ] - [ ] - [ ] 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/` — - `tests/` — ## Branch `phase-/` ## Abhängigkeiten - Blockiert durch: # (falls vorhanden) - Blockiert: # (falls vorhanden) ``` --- ## 4. PR-Template Datei: `.forgejo/PULL_REQUEST_TEMPLATE.md` (Forgejo liest dieses Verzeichnis; Fallback `.gitea/PULL_REQUEST_TEMPLATE.md` wird ebenfalls erkannt). ```markdown ## Summary <1–3 Sätze: was ändert dieser PR und warum.> ## Closes Closes # ## Changes - ## Test plan - [ ] `npm run build` läuft grün - [ ] `npm test` läuft grün - [ ] Manueller Smoke-Test: - [ ] Neue Dateien haben Unit-Tests (falls anwendbar) ## Screenshots / Logs ## Breaking Changes - [ ] Keine - [ ] Ja: ## 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: ``` (): ``` **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**: ```bash git fetch origin git checkout main git pull --ff-only git checkout -b phase-/ ``` 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**: ```bash npm run build npm test ``` Beide müssen grün sein, bevor gepusht wird. 7. **Pushen**: `git push -u origin phase-/`. 8. **PR öffnen**: Via `tea pulls create` oder Forgejo-Web-UI. PR-Template ausfüllen, `Closes #` 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 ` auf einem neuen Branch `phase-/revert-`, normaler PR-Flow. Niemals `git reset` auf `main`. - **PR ist vor Merge kaputt**: Branch bleibt offen, `In Review` → `In 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**: ```bash 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/.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-/` | | 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) |