Worktree-basierte parallele Loop-Pipeline — Dual-Mode (in-session + detached)
proposed
git worktree mit eigener DDEV-Site pro Worktree (DDEV-Blog-Pattern: name:-Key raus aus committed Config, Auto-Name nach Verzeichnis, DB via ddev snapshot restore). Zwei Run-Modes nebeneinander: (1) In-Session via Claude-Code built-in Agent(isolation: "worktree", run_in_background: true) für kurze Sub-Agent-Loops aus einer Master-Session; (2) Detached via neuem Skill solcom-worktree mit git worktree add + nohup claude --print für langlebige autonome Loops, die das Ende der Master-Session überleben. Shared zwischen beiden Modes: Mini-Skill solcom-ddev-bootstrap kapselt Snapshot-Restore + ddev start im aktuellen Verzeichnis.
Kontext
Bisherige Annahme (aufgezeichnet in Auto-Memory project_sandcastle_host_only_architecture, datiert auf einen geplanten, aber nicht realisierten .sandcastle/-Orchestrator): Branches werden direkt im Hauptverzeichnis ausgecheckt, AFK-Pipeline läuft sequenziell ein Issue zur Zeit, kein git worktree. Begründung damals: .ddev/config.yaml trägt name: solcom, ein paralleler Worktree würde mit demselben Project-Namen die Eltern-Instance abschießen (Port-/Container-Konflikt). Außerdem: vor MVP-Go-Live 2026-07-08 ist eine schlanke Pipeline wichtiger als Container-Isolation.
Beide Annahmen sind heute überholt:
- Der DDEV-Blog-Eintrag „git worktree contributor training" (veröffentlicht 2025-09) zeigt: wird der
name:-Key aus.ddev/config.yamlentfernt, benennt sich jede DDEV-Site automatisch nach ihrem Verzeichnis. Der Port-Konflikt verschwindet — Primarysolcom/bleibtsolcom.ddev.site, Worktree../solcom-worktrees/P2503-127/wirdsolcom-worktrees-p2503-127.ddev.site. - Claude Code hat seit dem aktuellen Release ein eingebautes Worktree-Feature:
Agent(isolation: "worktree", run_in_background: true)erzeugt automatisch einen temporären Worktree für einen Sub-Agent und räumt ihn auf, wenn der Sub-Agent keine Änderungen produziert hat. Sub-Agents können parallel zur Master-Session laufen. - Marc will mehrere Loops parallel im Hintergrund laufen lassen (
/implementfür mehrere Stories,auto-review-fixals Watcher,ralph-loopals generischer Driver). Sequenzielle Pipeline limitiert Throughput vor dem MVP-Termin.
Der Memory-Eintrag verweist zusätzlich auf zwei Files (docs/adr/0028-host-only-sandcastle-orchestrator.html, docs/plans/2026-05-12-001-sandcastle-host-only-pipeline.html), die im Repo nicht existieren. Das .sandcastle/-Verzeichnis fehlt ebenfalls. Die alte Architektur war also Skizze, nie Code — die Wende ist Greenfield, keine Migration.
Die zentrale Frage ist nicht, ob Worktrees genutzt werden (das ist gesetzt), sondern wer den Worktree-Lifecycle steuert: das Claude-Code-Built-in für kurze Sub-Agent-Loops, oder ein eigenständiger Skill, der OS-Background-Prozesse spawnt und Master-Session-Lebenszyklen überlebt? Beide Pattern haben unterschiedliche Use Cases; eine Single-Mode-Wahl bricht den jeweils anderen.
Entscheidung
Dual-Mode-Architektur. In-Session via Claude-Code Agent(isolation: "worktree", run_in_background: true); Detached via neuem Skill solcom-worktree mit git worktree add + nohup claude --print. Geteilte Infrastruktur: name:-Key raus aus committed .ddev/config.yaml und Mini-Skill solcom-ddev-bootstrap (Snapshot-Restore + ddev start).
1 — Geteilte Infrastruktur
.ddev/config.yaml: Zeilename: solcomwird entfernt. Committed-State. Primarysolcom/bekommt Auto-Namesolcom(Verzeichnisname identisch), URL bleibtsolcom.ddev.site. Jeder zukünftige Worktree-Ordner trägt seinen eigenen Auto-Name.- Worktree-Layout: peer zum Repo unter
../solcom-worktrees/<branch-or-issue-key>/. Nicht inside des Repos — DDEV-Nesting würde sonst kollidieren. - Mini-Skill
solcom-ddev-bootstrap: kapseltddev snapshot restore latest+ddev start+ddev drush crim aktuellen Arbeitsverzeichnis. Idempotent (zweiter Aufruf: no-op). Wird von beiden Modes aufgerufen, nicht vom Worktree-Lifecycle selbst. - Snapshot-Pflege: Primary erzeugt periodisch (manuell oder via Hook)
ddev snapshot --name latest-primary. Worktrees restoren diesen Snapshot. Stale-Risk wird akzeptiert — Worktree-Loops laufen typischerweise sub-stündig, Snapshot-Refresh-Kadenz ist Marc-Workflow-Detail.
2 — Mode A: In-Session (Claude-Code-Built-in)
Use Case: Marc orchestriert aus einer Master-Session mehrere parallele Sub-Agents, die jeweils einen Loop in ihrem eigenen Worktree fahren. Lebenszyklus an die Master-Session gebunden.
- Aufruf:
Agent(subagent_type: "general-purpose" | ..., isolation: "worktree", run_in_background: true, prompt: "/implement P2503-127")innerhalb der Master-Session. - Claude Code erstellt den Worktree, der Sub-Agent läuft drin. Sub-Agent ruft als ersten Step
solcom-ddev-bootstrapauf, wenn der Loop DDEV braucht. - Cleanup: automatisch durch Claude Code, wenn der Sub-Agent keine Änderungen produziert hat. Bei Änderungen: Worktree + Branch werden zurückgegeben, Marc entscheidet manuell (Cleanup oder PR-Pickup).
- Konsumenten (Skills, die diesen Mode nutzen sollen):
/implement,auto-review-fix,solcom-reviewin ihrer typischen sub-stündigen Variante. Skill-Anpassung erfolgt in separater Story.
3 — Mode B: Detached (Skill solcom-worktree)
Use Case: autonome Hintergrund-Loops, die die Master-Session überleben sollen — Sandcastle-AFK-Pipeline parallelisiert, Multi-Stunden-ralph-loop, persistente Watcher.
- Skill
solcom-worktreemit drei Sub-Workflows:spawn <key> <skill-or-prompt>:git worktree add ../solcom-worktrees/<key>→solcom-ddev-bootstrapim Worktree →nohup claude --print --output-format stream-json --dangerously-skip-permissions "<prompt>" > .worktree-logs/<key>.log 2>&1 &→ PID festhalten in.worktree-logs/<key>.pid.list: alle Worktrees ausgit worktree listkreuzen mit den PID-Files; pro Eintrag: PID alive? letzte Log-Zeile? Branch-Status?cleanup <key>: Prozess via PID killen →ddev delete -Oim Worktree →git worktree remove --force ../solcom-worktrees/<key>→ Log-Datei archivieren.
- Logs landen in
.worktree-logs/im Primary-Repo (gitignored, dort sammelt Marc Telemetrie). - Master-Session-Ende killt den Prozess nicht (
nohup+ Detachment). Marc musscleanupexplizit aufrufen — bewusste Mehrarbeit gegen versehentliche Hintergrund-Zombies.
4 — Mode-Wahl
| Kriterium | In-Session (A) | Detached (B) |
|---|---|---|
| Loop-Dauer | ≤ ~60 min | Multi-Stunden, langfristig |
| Master-Session-Bindung | endet mit Master | überlebt Master |
| Cleanup | automatisch bei No-Change | explizit via Skill-cleanup |
| Mechanik-Aufwand | built-in, kein Custom-Code | Custom-Skill |
| Telemetrie | Master-Session-Output | Log-Dateien |
| Default | ja | opt-in |
5 — Story-Gate ready-for-agent
Die bisherige Beschränkung „Stories mit DB-/Drupal-Runtime-Bedarf dürfen nicht ready-for-agent" (Memory) fällt weg. Mit eigener DDEV-Site pro Worktree kann der Agent drush-Calls, Kernel-/Functional-Tests und Migrations laufen lassen. Die Memory-Repair-Task aktualisiert diese Regel.
Rejected alternatives
Topology
- Pure In-Session-Mode (kein Detached-Skill): Sub-Agents sterben mit der Master-Session. Langlebige Loops (Sandcastle-AFK-Watcher, Multi-Stunden-Bauten) sind damit nicht möglich. Außerdem teilen sich alle Sub-Agents die Master-Session-API-Quota und das Master-Session-Context-Limit indirekt über die Tool-Result-Pipes.
- Pure Detached-Mode (kein Built-in): Verzichtet auf Claude-Code-Gratis-Infrastruktur. Jeder Skill, der kurz mal in einem Worktree Sub-Agent-Logik laufen lassen will, müsste den Custom-Skill aufrufen statt einfach
Agent(isolation: "worktree"). Mehr Code, mehr Wartung, kein klarer Vorteil für den Sub-Stunden-Use-Case. - Mehrere Repo-Clones statt Worktrees: Disk-Multiplikator (volle
.git-Kopie pro Clone), kein gemeinsamer Object-Store, Branch-Synchronisation manuell. Ist genau das, was Worktrees lösen. - Worktrees inside Repo (
./worktrees/<key>/): DDEV-Config-Nesting (Worktree sieht das Primary-.ddev/als „Parent"-Config), Risiko von versehentlicher Verschachtelung in Composer/Drush-Pfaden. Peer-Layout ist Blog-konform und semantisch sauber.
DDEV-Naming-Strategie
- Per-Worktree
.ddev/config.local.yaml-Override (committed-Config behältname: solcom): Vorteil: kein Touch der committed Config. Nachteil: jeder Worktree braucht einen manuellen Setup-Step, der die lokale Override schreibt — und vergisst Marc das, kollidiert die Worktree-Site mit der Primary-Site. Auto-Naming via Blog-Pattern ist robuster. - Hardcoded
name: solcom-${SLUG}in.ddev/config.local.yaml: Variante des obigen, gleiche Schwäche. Wird zudem nicht-deterministisch beim Verzeichnis-Rename.
DB-Initialisierung
- Empty-DB +
drupal:recipe-unpack+ Recipe-Apply pro Worktree: 3–5 min Setup-Zeit, plus kein editorial Content (kein Pages-, Vacancy-, sf_publication-Stand). Reproduzierbar, aber für Build-/Review-Loops praktisch unbrauchbar — Stories erwarten realistische Daten. - Shared remote DB (Mittwald-Dev oder dedizierter shared Container): Disk-Vorteil, aber Race-Conditions zwischen parallelen Worktrees (zwei Loops
drush crauf demselben Cache, zwei Migrations gegeneinander). Bricht das Worktree-Isolations-Versprechen. - Hybrid Snapshot-default +
--empty-Flag: später ergänzbar, wenn ein konkreter Use Case auftaucht (z.B. Recipe-Reset-Tests). Im MVP nicht eingebaut, kein Verlust.
Background-Prozess-Spawn (Detached-Mode)
- tmux-Session-Pattern: jeder Loop läuft in einem benannten
tmux-Pane. Vorteil: interaktiv reattach-bar, Output-Streaming. Nachteil: zusätzliche Dependency (tmuxnicht in jedem Setup), Pane-Lifecycle separat zu pflegen, Cleanup komplizierter.nohup+ Log-Datei ist robuster und dependency-frei. launchctl-Plist pro Worktree: macOS-native Daemon-Verwaltung. Schwergewichtig (Plist-Generierung,launchctl bootstrap), wenig portabel falls Marc später eine Linux-VM nutzt. Nicht den Aufwand wert.- Docker-/DDEV-Container-basierter Claude-Runner: Claude läuft in einem Container, der dann beendet wird wenn fertig. Containerisierung von Claude Code ist möglich, aber die Vorteile (Reproducibility, Cleanup) gegenüber
nohupsind im single-host Marc-Setup gering, der Overhead deutlich.
Concurrency-Strategie
- Strikte Obergrenze (z.B. max 3 Worktrees) im Skill
solcom-worktreehardcoded: schützt vor RAM-Eskalation, ist aber für Marcs Workflow zu restriktiv. Concurrency-Pacing bleibt Marc-Verantwortung;listzeigt Lauf-Status, Marc reguliert manuell. - Job-Queue mit Worker-Pool: zentrale Queue, N Worker pollen Issues. Bricht den Skill-First-Ansatz und addiert Persistenz-Schicht (Queue-Store, Lock-Mgmt). Out-of-Scope für die MVP-Mechanik.
Consequences
Was muss man wissen
- Committed-Config-Change:
.ddev/config.yamlverliertname: solcom. Alle Devs im Team müssen nach Pull lokalddev poweroff && ddev startausführen — die DDEV-Site wird neu provisioniert (Auto-Name gleich „solcom" wenn das Verzeichnis so heißt). Onboarding-Doku inREADME.md/CLAUDE.mdentsprechend ergänzen. - Disk-Impact: pro Worktree DB-Snapshot (~hundert MB bis >1 GB je nach Content-Volumen) +
web/sites/default/files(mehrere GB bei Bedarf). 5 parallele Worktrees ≈ >25 GB. Marc muss Mac-Disk im Auge behalten; bei Ressourcenknappheit greift Cleanup zuerst. - RAM-Impact: jede DDEV-Site bringt ~500 MB – 1 GB DB-Container + ~500 MB Web-Container. Auf einem 32-GB-Mac sind 3–4 parallele Sites realistisch; mehr drückt das System in Swap.
- Story-Gate-Erweiterung:
ready-for-agentumfasst ab jetzt auch DB-/Drupal-Runtime-Stories. Triage-Label-Doku (docs/agents/triage-labels.html) entsprechend updaten. - Detached-Prozess-Hygiene:
nohup-Prozesse überleben Terminal-Schließen und Reboot-Erinnerungs-Frage (macOS Login-Reload). Marc musssolcom-worktree cleanupaufrufen, sonst sammeln sich Zombies.list-Sub-Workflow ist die Reality-Check-Schnittstelle. - Master-Session-Lifecycle für Mode A:
Agent(run_in_background: true)-Sub-Agents enden, wenn die Master-Session endet. Wer das nicht will, muss explizit Detached-Mode wählen. - Token-Verbrauch im Detached-Mode:
claude --print --dangerously-skip-permissionsläuft autonom — Bauen, Reviewen, Iterieren ohne menschliche Bremse. Token-Eskalation ist real, Kosten-Monitoring ist eine offene Folge-Story (nicht in dieser ADR adressiert).
Bewusste Restrisiken
- DDEV-Snapshot-Drift: Snapshot ist statisch, Primary lebt weiter. Worktree-Loops sehen alten Stand, wenn Primary inzwischen migriert / Recipes appliziert / Content angelegt hat. Mitigation: Snapshot-Refresh-Routine (manuell oder Marc-Cron); Snapshot-Name-Konvention macht das Datum sichtbar.
- Branch-Konvention-Drift: Bisher
feature/<JIRA-KEY>. Worktrees brauchen einen Branch-Namen; Auto-Generierung imspawn-Sub-Workflow setztagent/<key>-<slug>oderfeature/<key>? Skill-Detail, defaultfeature/<key>bleibt repo-konvention-konform. - Parallele Composer-/NPM-Installs: zwei Worktrees, die gleichzeitig
composer installodernpm installlaufen lassen, sind unproblematisch (jeder Worktree hat eigenevendor/bzw.node_modules/), aber: Composer-Cache und NPM-Cache sind global. Cache-Lock-Konflikte werden in der Praxis selten ein Problem, sind aber theoretisch möglich. - Git-Worktree-Branch-Lock: ein Branch kann zu einer Zeit nur in einem Worktree ausgecheckt sein.
spawnmit bereits-aktivem Branch schlägt fehl; Skill muss Branch-Konflikt sauber abfangen und reporten. - Memory-Fallout: Auto-Memory
project_sandcastle_host_only_architecturewird invertiert (siehe Memory-Repair-Task). Wenn die Memory nicht repariert wird, geben zukünftige Sessions weiterhin „kein Worktree"-Empfehlungen.
Folge-Stories (nicht in diesem ADR)
- Anpassung der Skills
/implement,auto-review-fix,solcom-reviewauf Worktree-Sub-Agent-Aufruf (Mode A). - Cost-Monitoring / Token-Budget für Detached-Mode-Loops.
- Snapshot-Refresh-Automation (z.B.
ddev snapshot --name latest-primaryals post-cim-Hook). - Telemetrie-Dashboard für laufende Background-Loops (HTML-Report aus
.worktree-logs/).
Customer-Lens-Reviewer: N/A — Dev-Infrastruktur
Die solcom-adr-workflow-Konvention macht den customer-lens-reviewer-Aufruf bei Komponenten-Topologie-/Architektur-Entscheidungen mit ≥2 Optionen zur Pflicht. Die drei festgeschriebenen Stakeholder-Linsen — Figma-Designer:innen, Drupal-Site-Builder:innen, Theme-Entwickler:innen — orientieren sich an UI-/Frontend-Artefakten.
Diese Entscheidung betrifft keinen Frontend-Touchpoint: kein SDC, kein Canvas, kein Twig, keine Brand-Tokens. Es geht um Dev-Workflow-Infrastruktur (Git-Worktree-Topologie, DDEV-Site-Naming, Background-Prozess-Lifecycle). Die Stakeholder dieser Entscheidung ist Marc als Drupal-Dev, nicht Designer:innen oder Site-Builder:innen.
Konsequenz: Customer-lens-Konsultation entfällt. Begründung wird hier explizit notiert.