disclaw/docs/workflow.md

496 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`
```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
<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).
```markdown
## 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**:
```bash
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**:
```bash
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 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/<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) |