Spec-Driven Development
Spec-Driven Development mit Polycrate: Specs planen, implementieren, dokumentieren und versionieren. Kanonischer Flow für Workspace- und Block-Releases, exakte Gate-Tabelle, Agent-Integration.
Spec-Driven Development (SDD) ist der Prozess, mit dem Änderungen an Polycrate-Workspaces und Blocks geplant, umgesetzt, dokumentiert und versioniert werden. Polycrate liefert dafür ein standardisiertes .specs/-Layout, ein maschinenlesbares Spec-Format mit ID-basiertem Lifecycle und die CLI-Commands polycrate spec * und polycrate release *. SDD ist für die Zusammenarbeit mit Coding Agents (Cursor, Claude Code, Codex, …) ausgelegt, funktioniert aber genauso ohne.
Diese Seite beschreibt genau das, was die CLI prüft — nicht mehr. Die Single Source of Truth ist polycrate spec manual (Sektion CANONICAL FLOW); diese Seite, die generierte .cursor/rules/sdd.mdc und Entwickeln und deployen spiegeln sie.
Warum Spec-Driven Development?
| Frage | SDD-Antwort |
|---|---|
| Warum wurde etwas geändert? | Die Spec archiviert Kontext, Analyse und Lösung — dauerhaft, neben dem Code |
| Was ist der Stand? | Live-Scan von .specs/: Ordnerposition + Frontmatter sind der Status |
| Wie kommt eine Änderung zum Release? | draft → done → release create → release update → release finalize |
| Was ist offen? | Backlog in .specs/.next/; offene Entscheidungen in ## Open Questions |
| Was darf ein Agent nicht entscheiden? | Alles, was als Open Question steht — die CLI verweigert done, solange welche offen sind |
| Wie sieht die Ziel-Architektur aus? | Optional .specs/ARCHITECTURE.md, frei editierbar |
Der kanonische Flow
Es gibt genau einen Flow. Alles Weitere auf dieser Seite erklärt seine Bausteine.
A. Workspace-Änderung (Spec → Release)
W=/abs/pfad/zum/workspace # -w muss absolut sein — oder weglassen, wenn cwd der Workspace ist
# 1. Planen — ein Problem oder Feature pro Spec
polycrate spec create --name "Hub-Token rotieren" --type feat -w $W
# → .specs/.next/<id>-<slug>.md, status: draft (IDs beginnen bei 1)
# 2. Schreiben — Sektionskörper direkt editieren (nur H3+) oder per Flag
polycrate spec update <id> --section-file solution=/tmp/.polycrate/spec-sections/spec-<id>-solution.md -w $W
# 3. Struktur prüfen — Drift blockiert jede Status- und Release-Operation
polycrate spec lint <id> -w $W # melden
polycrate spec format <id> -w $W # reparieren, Inhalte bleiben erhalten
# 4. Implementieren. Informative Checks (blockieren nie):
polycrate spec verify <id> -w $W # Acceptance-Checkboxen + @pfad:ZEILE-Anker
polycrate spec review <id> --no-pager -w $W
# 5. Abschließen — Open Questions MÜSSEN leer sein (in Solution/Context auflösen, nicht löschen)
polycrate spec update <id> --set status=done -w $W
# 6. Release — Specs optional; ein reiner Versions-Bump mit Release Note ist gültig
polycrate release create 1.4.0 --spec <id> -w $W
polycrate release update 1.4.0 --set type=feat --set message="Hub-Token-Rotation" \
--changelog-item "Feature: Hub-Token werden automatisch rotiert" \
--note-section highlights="- Hub-Token-Rotation ohne Downtime" -w $W
polycrate release status 1.4.0 --no-pager -w $W # zeigt, was finalize prüfen wird
# 7. Finalisieren — setzt workspace.poly version, führt pre_actions/post_actions aus. Kein Git.
polycrate release finalize 1.4.0 -w $W
B. Block-Änderung (Blocks haben keine Specs)
Blocks tragen kein .specs/-Verzeichnis. Ein Block-Release besteht aus dem CHANGELOG.poly-Eintrag, der block.poly-Version und dem Registry-Tag.
- Logikänderung im Block → eine Workspace-Spec im Repository, das den Block hostet, mit Label
block:<name>. Ihr Lifecycle ist A.1–A.5. - Reiner Versions-Bump (Dependency, Image-Tag, kein Verhaltensunterschied) → keine Spec; der Changelog-Eintrag dokumentiert ihn.
# 0. Nur bei Logikänderung: Spec im Host-Workspace, Label setzen
polycrate spec update <id> --add-label block:hardening -w $W
# 1. Changelog-Eintrag (idempotent; legt den Eintrag an, falls er fehlt)
polycrate release --block hardening update 0.5.6 --set type=fix \
--set message="Konfigurierbare OpenSSH PerSourcePenalties" \
--changelog-item "Fix: exempt list für PerSourcePenalties" \
[--set app_version=A.B.C] -w $W
# 2. Finalisieren — setzt block.poly version, validiert, pusht in die Registry (idempotent)
polycrate release block-finalize hardening 0.5.6 [--app-version A.B.C] [--no-push] -w $W
# 3. Downstream konsumieren — schreibt from: auf jede Instanz dieses Blocks im Workspace
polycrate block pull cargo.ayedo.cloud/ayedo/linux/hardening:0.5.6 -w /abs/pfad/zum/downstream-workspace
polycrate run hardening install -w /abs/pfad/zum/downstream-workspace
release --block <name> create, spec --block, release --block update --spec und --note-section* mit --block existieren nicht mehr und brechen mit einem Hinweis auf diesen Flow ab.
C. Gates — was tatsächlich fehlschlägt (Exit ≠ 0)
| Operation | Hard Fail wenn | Fix |
|---|---|---|
| jeder Command | -w ist nicht absolut |
vollen Pfad übergeben oder -w weglassen |
spec update --set status=done |
struktureller Drift (Überschriften, Reihenfolge, Frontmatter) | spec format <id> |
spec update --set status=done |
## Open Questions nicht leer |
in Solution/Context auflösen |
spec update --section* |
H1/H2 im Sektionskörper | H3+ verwenden |
release create/update --spec <id> |
Spec ist nicht done oder cancelled |
erst status=done |
release finalize (Workspace) |
Drift in einer zugewiesenen Spec | spec format |
release finalize (Workspace) |
Open Questions in einer zugewiesenen Spec | auflösen, dann status=done |
release finalize (Workspace) |
CHANGELOG.poly-Eintrag ohne message |
release update --set message= |
release finalize (Workspace) |
RELEASENOTE.md highlights leer |
release update --note-section highlights= |
release block-finalize |
Changelog-Eintrag fehlt, message/description leer |
release --block <name> update |
release block-finalize |
app_version leer, obwohl block.poly eines führt |
--app-version oder --set app_version= |
release delete / spec delete |
interaktive Bestätigung abgelehnt | --force überspringt den Prompt |
--force ist eine auditierte Notbremse (--force-reason Pflicht, Protokoll in .specs/.force-audit.log): bei status=done umgeht sie Drift und Open Questions; bei release finalize umgeht sie nur highlights — message, Drift und Open Questions zugewiesener Specs bleiben hart.
Keine Gates (Warnung oder stumm): Ergebnisse von spec verify, fehlendes spec review, unerfüllte needs:, 0 Specs an einem Release, der Git-Zustand (Dirty Tree, vorhandenes Tag, ungepushte Commits).
D. Git ist kein Prozessschritt
Ob ein Workspace oder Block released werden kann, hängt nicht vom Zustand eines Git-Repositories ab. release finalize erzeugt kein Tag und keinen Commit; nichts prüft den Working Tree. Released heißt: CHANGELOG.poly-Eintrag + Versionsdatei (workspace.poly/block.poly) bzw. Registry-Tag (Block). Agents führen git commit/tag/push/stash/reset oder polycrate workspace sync nie als Spec- oder Release-Schritt aus. Wer Git-Historie will, committet selbst — siehe Git-Integration.
Konzepte
Specs
Eine Spec ist ein Markdown-Dokument mit YAML-Frontmatter, das eine Änderung von der Idee bis zur Implementierung begleitet:
- ID — ganzzahlig, stabil, nie wiederverwendet;
polycrate spec inspect <id>findet sie unabhängig vom Speicherort - Status —
draft,done,cancelled - Typ — Conventional Commits:
fixperffeatrefactorchoredocscibuildstyletestbreakinganalysis - Sektionen — festes Schema (siehe unten)
- Labels — frei, z. B.
block:<name> - needs / relates-to — Abhängigkeiten (Warnung bei
done, kein Gate) bzw. thematische Links
Ordnerposition ist der Zustand
Es gibt kein index.yml. Specs und Releases werden live aus dem Dateisystem gelesen:
| Pfad | Bedeutung |
|---|---|
.specs/.next/<id>-<slug>.md |
Backlog — in Arbeit, keinem Release zugewiesen |
.specs/<version>/<id>-<slug>.md |
Release <version> zugewiesen |
.specs/<version>/RELEASENOTE.md |
Release Note (Scaffold durch release create) |
mein-workspace/
├── .specs/
│ ├── ARCHITECTURE.md # Referenzarchitektur — frei editierbar
│ ├── .rules.md # Projektregeln für Agents — von spec init geschrieben, repo-lokal erweiterbar
│ ├── .invariants.md # Harte, prüfbare Repo-Regeln — eine pro Zeile
│ ├── .next/
│ │ ├── 7-hub-token-rotieren.md
│ │ └── 8-login-timeout.md
│ └── 1.4.0/
│ ├── RELEASENOTE.md
│ └── 5-s3-bucket-provisioning.md
├── .cursor/rules/sdd.mdc # generiert: polycrate spec rules generate
├── blocks/<name>/ # block.poly + CHANGELOG.poly — kein .specs/
├── workspace.poly
└── CHANGELOG.poly
Was direkt editiert werden darf
| Element | Erlaubt | Werkzeug |
|---|---|---|
Sektionskörper (Text zwischen zwei ##-Überschriften) |
ja | Editor / Agent-Dateiwerkzeuge |
.specs/ARCHITECTURE.md |
ja | Editor |
| YAML-Frontmatter (Status, Typ, Labels, …) | nein | polycrate spec update --set |
##-Sektionsüberschriften, Reihenfolge |
nein | polycrate spec format <id> |
.specs/<v>/RELEASENOTE.md |
nein | polycrate release update --note-section |
CHANGELOG.poly (Workspace und Block) |
nein | polycrate release update / release --block update |
version in workspace.poly / block.poly, app_version |
nein | release finalize / release block-finalize |
Nach direkten Body-Edits polycrate spec lint <id> ausführen; spec format repariert Drift und erhält die Inhalte.
Sektionsschema
| Sektion | Inhalt | Gate |
|---|---|---|
context |
Hintergrund, Anforderungen, Analyse — für fix/perf/analysis: Symptom, Analyse, Root Cause |
— |
solution |
Konkreter Plan mit @pfad/datei.ext:ZEILE-Ankern; darf nur bei analysis leer sein |
— |
affected-files |
Ein Pfad pro Zeile | — |
acceptance-criteria |
- [x] <Bedingung> - verify: @pfad:ZEILE — spec verify prüft Checkboxen und Anker |
informativ |
migration |
Upgrade-Schritte; Pflicht bei type=breaking |
spec validate Error |
open-questions |
Offene Entscheidungen | leer vor status=done |
review |
Self-Review: Finding N [minor\|major\|blocker] @pfad:ZEILE: … - resolved: yes\|no |
informativ |
Innerhalb der Sektionen sind nur H3 (###) und tiefer erlaubt; H1/H2 werden beim Schreiben abgelehnt, weil H2 die Sektionsgrenze ist.
.specs/.rules.md und .specs/.invariants.md
.rules.md— Prozessregeln in Prosa.polycrate spec init --forceschreibt den aktuellen Default neu (Specs bleiben unberührt); repo-lokale Zusätze als eigene##-Sektion anhängen und nach dem Neuschreiben wieder ergänzen..invariants.md— eine harte, prüfbare Regel pro Zeile (max. 25). Steht im generiertensdd.mdcan erster Stelle: Verstöße sind Bugs, keine Konventionsfragen.
# Repo invariants — my-project
- LANG: all user-facing output in English
- DB: use ORM only; no raw SQL
- SDD: in .specs/ only section bodies and ARCHITECTURE.md are hand-editable
- SDD: git is not a process step — never commit/tag/stash/reset/sync as part of spec or release work
- SDD: blocks have no .specs/ — block-related specs live in the workspace with label block:<name>
Referenzarchitektur (.specs/ARCHITECTURE.md)
Kein Spec-Dokument, wird nicht gescannt, frei editierbar. Empfohlen als kompaktes, lebendes Architekturdokument (Überschriften, Tabellen, Code-Verweise). polycrate spec arch list|search <query>|show <slug> liefert gezielt einzelne Abschnitte — Agents lesen vor dem Planen nur die relevanten.
Spec-Lifecycle
draft ◄──────────────► done Gates: kein Drift, Open Questions leer
└──────────┬──────────┘
cancelled jederzeit, kein Gate (cancellation_reason setzen)
polycrate spec update 7 --set status=done
polycrate spec update 7 --set status=draft # wieder öffnen
polycrate spec update 7 --set status=cancelled --set cancellation_reason="Ersetzt durch Spec 9"
spec listblendetcancelledstandardmäßig aus (--allzeigt sie);spec validateundrelease diffignorieren sie.- Deprecated Statusnamen werden beim Lesen gemappt:
planned/pending-approval/approved/pending-verification/in-progress/ready→draft,implemented→done. spec submit/spec finalizesind deprecated Aliase für--set status=done.
Open Questions — das eine Workflow-Gate
Eine offene Frage ist eine fehlende Entscheidung. Damit ein Agent sie nicht still „während der Implementierung“ trifft, verweigert die CLI status=done, solange ## Open Questions Text enthält (HTML-Kommentare zählen als leer). Beantwortete Fragen gehören in solution/context — die Sektion wird aufgelöst, nicht geleert.
● spec 7: Open Questions is not empty — resolve or clear the section before status=done
Abhängigkeiten und Links
polycrate spec link 5 --needs 3 --needs 4 # 5 setzt 3 und 4 voraus — Warnung bei done, kein Gate
polycrate spec link 5 --relates-to 8 # thematischer Link, ohne Wirkung auf den Lifecycle
polycrate spec update 5 --remove-needs 3
polycrate spec list --blocked # Specs mit nicht-done needs
Selbstreferenzen und Zyklen werden abgelehnt.
Informative Checks
| Command | Prüft | Wirkung |
|---|---|---|
spec lint <id> |
Strukturellen Drift (blockierend bei Status/Release) und Grounding-Hinweise (Anker in Context/Solution, existierende Affected Files) | Drift → Error; Grounding → Warnung |
spec review <id> |
Stempelt reviewed_at; status=done warnt, wenn der Stempel fehlt oder älter als die Datei ist |
Warnung |
spec verify <id> |
Acceptance-Checkboxen und @pfad:ZEILE-Anker; --format json, --with-agent (Prompt für einen Verifier-Subagenten) |
Warnung; dieselbe Tabelle druckt status=done auf stdout |
| needs-check | nicht-done Abhängigkeiten bei status=done |
Warnung |
Quick-Capture
polycrate spec note "S3-Upload schlägt bei 403 still fehl" --type fix --label s3
# → draft-Spec sofort angelegt, ID zurückgegeben, aktueller Task geht weiter
Überblick
polycrate spec list # kompakte Tabelle / TUI
polycrate spec list --open # nicht done, nicht cancelled
polycrate spec list --version .next --status done # Backlog: fertig, aber ohne Release
polycrate spec list --label block:hardening
polycrate spec list --format json # Feld heißt "version" (leer = Backlog), nicht "release"
polycrate spec inspect 7 --section solution
Release-Lifecycle
Workspace-Release
polycrate release diff 1.4.0 # Vorschau: welche Specs sind done und unzugewiesen?
polycrate release create 1.4.0 --spec 7 --spec 8 # verschiebt die Dateien nach .specs/1.4.0/, scaffoldet RELEASENOTE.md
polycrate release create 1.4.0 --spec-status done # Bulk: alle done-Specs aus .next/
polycrate release create 1.4.1 # ohne Specs — reiner Bump
polycrate release update 1.4.0 --set type=feat --set message="Kurzfassung" \
--changelog-item "Feature: …" --changelog-item "Fix: …" \
--set docs_url=https://ayedo.de/docs/polycrate/releases/
polycrate release update 1.4.0 --note-section highlights="- …" --note-section-file changes=/tmp/.polycrate/spec-sections/changes.md
polycrate release status 1.4.0 --no-pager # Guards, Warnungen, Checkliste
polycrate release finalize 1.4.0 # Version setzen, pre_actions → post_actions
release finalize setzt workspace.poly → version, führt die in workspace.poly unter config.release.pre_actions / post_actions konfigurierten Actions aus (z. B. Build, Image-Push, Hub-Upload) und schreibt nichts nach Git. Schlägt eine pre_action fehl, stoppt finalize — auch mit --force.
Release Notes (RELEASENOTE.md)
| Sektion | Pflicht für finalize |
|---|---|
highlights |
ja |
changes, fixes, migration |
nein |
polycrate release note status 1.4.0
polycrate release note inspect 1.4.0 --section highlights
Auch hier: nur H3 und tiefer innerhalb der Sektionen.
Block-Release
Siehe Flow B. Zwei Versionen in block.poly, beide CLI-only:
| Feld | Bedeutet | Setzt |
|---|---|---|
version |
Block-Logik (Playbooks, Templates, Actions) | release block-finalize |
app_version |
Upstream-Anwendung, die der Block installiert | --app-version oder release --block update --set app_version= |
Erneutes block-finalize mit gleichem Stand ist ein sicherer No-Op: existiert das Tag in der Registry, wird der Push übersprungen und der Zustand bestätigt (--force pusht erneut).
polycrate release --block hardening status 0.5.6 --no-pager # Changelog, Versionsdatei, Registry-Tag
Validierung und Recovery
polycrate spec validate # alle Specs: Frontmatter, Sektionen, needs, Migration bei breaking
polycrate release validate 1.4.0 # Specs im Verzeichnis done, Changelog-Eintrag, Versionsdatei
polycrate spec format <id> # Drift reparieren
polycrate spec force-audit # alle --force-Nutzungen
Legacy-Repos mit index.yml und 5-Status-Lifecycle: einmalig polycrate spec index migrate, dann polycrate spec lifecycle migrate-v2.
Agent-Integration
Polycrate generiert genau eine Agent-Datei: .cursor/rules/sdd.mdc. Sie enthält die Invariants, die absoluten Regeln, den kanonischen Flow mit Gate-Liste, die Key Commands und den Inhalt von .specs/.rules.md. AGENTS.md, CLAUDE.md oder andere IDE-Dateien werden nicht geschrieben.
polycrate spec init -w $W # legt .specs/ und .specs/.rules.md an (einmalig)
polycrate spec init --force -w $W # schreibt nur .rules.md auf den aktuellen Default neu
polycrate spec rules generate --force -w $W # schreibt .cursor/rules/sdd.mdc
polycrate spec rules show --no-pager -w $W # zeigt .specs/.rules.md
Beides nach jedem CLI-Upgrade in jedem Repo mit .specs/ ausführen. Repo-lokale Prozessregeln gehören in .specs/.rules.md, prüfbare Konventionen in .specs/.invariants.md; fachliche Coding-Standards bleiben in eigenen .cursor/rules/*.mdc. Für IDEs ohne Cursor-Rules reicht polycrate spec manual --no-pager als Einstieg.
Session-Start
polycrate spec manual --no-pager -w $W # einmal pro Session, nicht pro Turn
polycrate spec list --open --format tsv -w $W
polycrate spec arch list -w $W # dann search/show für die berührten Themen
Agent-Disziplin
| Regel | Inhalt |
|---|---|
| Spec Handoff | Die Chat-Antwort ist der Handoff: Spec-Identität, Zusammenfassung (Warum + Was/Wie), Open Questions ausformuliert oder „No open questions.“, nächster Schritt. Nicht annehmen, dass der Nutzer .specs/ öffnet. |
| Soft-Stop | Bei offenen Fragen pausieren: nicht implementieren, nicht status=done. Die CLI stützt das — done scheitert bei nicht-leeren Open Questions. |
| Nicht erfinden | Eine fehlende Tatsache ist eine Open Question in Spec und Chat. |
| Architektur vor Vermutung | spec arch search/show für jedes berührte Thema, bevor geplant oder implementiert wird. |
| Self-Review | Findings mit @pfad:ZEILE und [minor\|major\|blocker]; „alles OK“ zählt nicht. |
| Staging-Dateien | Für --section-file unter /tmp/.polycrate/spec-sections/, nie unter .specs/. |
| Code-Kommentare | // Spec: polycrate spec inspect 7 — nie den Dateipfad referenzieren, er ändert sich beim Release. |
Multi-Agent
Vier Rollen pro Spec, sequenziell: Grounding (spec arch), Author (spec create/update, spec lint), Reviewer (spec review), Verifier (spec verify --with-agent). Specs ohne offene needs sind unabhängig und können parallel bearbeitet werden; spec list --version .next zeigt [BLOCKED] für wartende Specs.
Sandbox und Runtime-Verzeichnis
polycrate spec * und polycrate release * starten keinen Container. In eingeschränkten Umgebungen:
polycrate spec list --runtime-dir /tmp/polycrate-run -w $W
Alle Commands im Überblick
polycrate spec
| Command | Beschreibung |
|---|---|
spec init [--force] |
.specs/ + .rules.md anlegen; --force schreibt nur .rules.md neu |
spec create --name <name> [--type <t>] |
Neue Spec (draft) |
spec note "<text>" [--type] [--label] |
Quick-Capture |
spec list [--open\|--status\|--label\|--type\|--blocked\|--all\|--version\|--format] |
Übersicht |
spec inspect <id> [--section <name>] |
Spec anzeigen |
spec update <id> --set k=v |
Frontmatter: status, type, name, cancellation_reason, reviewed_at |
spec update <id> --section k="…" \| --section-file k=pfad \| --section-stdin k |
Sektionskörper schreiben |
spec update <id> --add-label\|--remove-label\|--add-needs\|--remove-needs\|--add-relates-to |
Labels, Abhängigkeiten |
spec update <id> --force --force-reason "…" |
Drift- und Open-Questions-Gate umgehen (auditiert) |
spec link <id> --needs <id> \| --relates-to <id> |
Alias für Abhängigkeiten |
spec lint <id> / spec format <id> |
Drift melden / reparieren |
spec review <id> |
Self-Review, stempelt reviewed_at |
spec verify <id> [--format json] [--with-agent] |
Acceptance Criteria prüfen |
spec arch list \| search <q> \| show <slug> |
Referenzarchitektur gezielt lesen |
spec validate |
Konsistenz aller Specs |
spec force-audit |
Force-Log anzeigen |
spec delete <id> [--force] |
Spec löschen |
spec manual [--no-pager] |
Vollständiges Manual (SSOT) |
spec rules generate [--force] / spec rules show |
.cursor/rules/sdd.mdc schreiben / .rules.md zeigen |
spec index migrate, spec lifecycle migrate-v2 |
Legacy-Migration |
polycrate release
| Command | Beschreibung |
|---|---|
release diff <version> |
Vorschau der zuweisbaren Specs |
release create <version> [--spec <id>…] [--spec-status done] [--spec-type <t>] |
Workspace-Release anlegen; zugewiesene Specs müssen done/cancelled sein |
release update <version> --set type= --set message= --set docs_url= --changelog-item "…" |
CHANGELOG.poly-Eintrag |
release update <version> --note-section k="…" \| --note-section-file k=pfad |
RELEASENOTE.md |
release update <version> --spec <id> |
Weitere Spec zuweisen |
release status <version> / release list / release validate <version> |
Lesen |
release finalize <version> [--force --force-reason] |
Workspace finalisieren (Gates siehe oben) |
release --block <name> update <version> --set type= --set message= --changelog-item [--set app_version=] |
Block-Changelog-Eintrag |
release block-finalize <name> <version> [--app-version] [--no-push] |
Block finalisieren + pushen |
release --block <name> status <version> |
Block-Release lesen |
release note create\|inspect\|status <version> |
Release Note |
release unassign <version> <id> / release delete <version> |
Spec zurück nach .next/ / Release entfernen |
Siehe auch
- Entwickeln und deployen — der Flow als Prozesskarte inkl. MCP-Ops
- MCP Server — die sechs Tool-Familien für Agents
- CLI-Referenz
- Blöcke · Workspaces
- Git-Integration — optional, kein Teil des Prozesses