adScaile-Workflow ersetzt pipeline-crank und ralph-single-story

ADR-0048 · 2026-05-27 · Status: accepted

1. Kontext

Seit ADR-0037 (Ralph-Loop-Architektur, Mai 2026) hat sich der Workflow-Bestand für die autonome Abarbeitung von Jira-P2503-Tickets organisch vergrößert. Drei Skripte standen gleichzeitig im Repo:

  • scripts/ralph.sh — Bash-Original, Mass-Loop über ready-for-agent-Stories, Plan→Contract→Implement→Review→Merge.
  • scripts/workflows/pipeline-crank.mjs — Spalten-Driver (Jira-Status ist die Phase): „Zu erledigen" → „In Arbeit" → „Zu Review" → „Fertig", eine Crank-Umdrehung pro Run. Designed für inkrementelles Vorrücken über Stakeholder-Reviews zwischen den Spalten.
  • scripts/workflows/ralph-single-story.mjs — In-Session-Variante für genau eine Story, mit Contract-Eval-Skeptiker-Schleife und parallelem solcom-review + /code-review --fix in der Review-Phase.

Parallel hat der User darauf gezeigt, dass der mattpocock-.sandcastle/main.ts-Workflow (drei Phasen pro Outer-Iteration: Plan → Execute+Review → Merge, mit echtem Dependency-Graph-Planning statt Status-getriebenem Crank) als Vorbild dienen soll — 1:1-Nachbau, nicht eine weitere Variante daneben. Die existierende Design-Doc docs/agents/adscaile.html (vormals pipeline-sandcastle.html) hatte den Adaptions-Pfad bereits ausgeschrieben.

Drei Workflows nebeneinander sind aus zwei Gründen schädlich: (a) die kognitive Last steigt — jeder Lauf braucht eine Entscheidung „welcher Workflow passt jetzt?", obwohl die drei sich in der Sache überlappen; (b) die Prompts scripts/prompts/*.md sind Source-of-truth für alle drei, was bei jeder Prompt-Änderung Cross-Workflow-Tests erzwingt.

2. Entscheidung

adScaile wird der einzige Workflow-Eintrittspunkt für autonome Story-Abarbeitung. pipeline-crank.mjs und ralph-single-story.mjs werden gelöscht. scripts/ralph.sh bleibt als historisches Bash-Original für Notfall-Recovery-Läufe stehen, ist aber nicht mehr der default Pfad.

2.1 1:1-Choreographie-Mapping mit main.ts

Drei Phasen pro Outer-Iteration, bis zu 10 Iterationen:

  • Phase 1 Plan — Story-Picker mit Dependency-Graph-Heuristik (Datei-Overlap, Parent-Link, blocked-by). Output: <plan>{stories:[...]}</plan> per Regex-Tag-Match (identisch zum Original). Exit wenn Liste leer.
  • Phase 2 Execute — Pro Story sequenziell: Setup (Branch + DB-Snapshot + Jira-Transition) → Implementer (Sub-Task-Loop mit TDD-Discipline) → Reviewer (gated auf commits_count > 0 wie das Original auf result.commits.length).
  • Phase 3 Merge — Batch-Phase: alle Branches mit erfolgreichem Review werden via Azure-DevOps-REST als PR geöffnet, CI-Status gepollt, dann via PATCH /pullrequests/<ID> gemerged.

2.2 Drei bewusste Deviations vom Original

Aspektmain.tsadScaileGrund
MAX_PARALLEL 4 1 DDEV ist Singleton — parallele Stories würden auf der Snapshot-DB kollidieren. ADR-0030 hatte den Worktree-Pfad eröffnet (separate DDEV-Sites pro Worktree); im tatsächlichen Betrieb hat sich gezeigt, dass der Setup-Overhead pro Worktree nicht durch den Parallelism-Gewinn aufgewogen wird, solange die Story-Pipeline überhaupt funktioniert. Wir nehmen die einfachere Sequenziell-Variante und lassen ADR-0030 als optionaler Pfad stehen (nicht widerrufen, nur nicht der Default).
Isolation Docker-Sandbox pro Issue Primary-Workspace, git checkout Folgt aus 1:1 mit MAX_PARALLEL=1 — sequentielle Stories brauchen keine Sandbox-Isolation. Branch-Switch reicht.
Issue-Tracker GitHub-Issues + gh Jira P2503 + acli; Azure-DevOps-REST für Merge Reines Tooling-Swap, keine Choreographie-Änderung. gh issue listacli jira workitem search; gh pr mergePOST/PATCH Azure-DevOps-REST (Memory reference_azure_devops_pr_merge_api).

2.3 Model-Strategie (Audit aus Design-Doc Section 9)

Das Original nutzt einheitlich opus. adScaile differenziert:

  • Plan: sonnet — Story-Pick + Dependency-Graph ist regelbasierte Heuristik, kein Trade-off-Judgement.
  • Setup: haiku — pure Mechanics (git, ddev, acli), kein Judgement.
  • Implementer: sonnet — SOLCOM-Default für Coding-Aufgaben.
  • Reviewer: opus — unverhandelbar, weil Standards/Canvas/Spec-Findings-Detection Judgement und Cross-Reference braucht. Memory feedback_subagent_trust_but_verify warnt explizit vor billigen Reviewern.
  • Merger: sonnet — strukturiertes Tool-Use (PR-Open + CI-Polling + REST-PATCH), kein Judgement.
  • Block-Discipline: haikuacli edit + draft.

Single Opus-Call-Rolle (Reviewer) statt mehreren — der Reviewer-Opus läuft 1× pro Story, alle anderen Calls auf Sonnet oder Haiku.

2.4 Wiederverwendung der existierenden Prompts

Die existierenden scripts/prompts/{plan,implement,review,merge}-prompt.md bleiben unverändert und werden vom adScaile-Sub-Agenten per Read geladen. adScaile schiebt nur Wrapper-Strings als Header vor den Sub-Agent-Call, die die mattpocock-spezifischen Args (<plan>-Tag-Output-Kontrakt, BRANCH, JIRA_KEY, BRANCHES-Liste) liefern. Das hält die Prompts als Source-of-truth zentral und vermeidet Drift.

3. Rejected alternatives

3.1 Drei Workflows nebeneinander erhalten

Hätte den Vorteil, dass die Spalten-Driver-Mechanik aus pipeline-crank (frühe PR-Open für Stakeholder-Sichtbarkeit, dann später Merge nach Approval) weiterhin verfügbar wäre. Verworfen, weil (a) die kognitive Auswahl-Last bei jedem Lauf den Workflow blockiert, (b) Cross-Workflow-Test bei jeder Prompt-Änderung einen 3× Aufwand erzeugt, (c) der Spalten-Crank-Bedarf in der Praxis durch die Block-Discipline (labels-remove ready-for-agent + Draft) und durch Single-Story-Modus (args:'P2503-XXX') bereits abgedeckt ist.

3.2 Worktree-basierte parallele adScaile-Variante

Hätte die mattpocock-MAX_PARALLEL=4 näher am Original abgebildet und den AFK-Throughput vervierfacht. Verworfen, weil der Setup-Overhead pro Worktree (eigenes DDEV, eigene Datenbank, eigenes Snapshot-Restore) den Parallelism-Gewinn in der bisherigen Beobachtung nicht aufwiegt. ADR-0030 bleibt als Option stehen, falls die Sequenziell-Variante in den vollen AFK-Läufen am Durchsatz scheitert — dann ist die parallele Variante eine kontrollierte Eskalation, kein Initial-Default.

3.3 Neue scripts/prompts/sandcastle/-Prompt-Files

Die Design-Doc hatte ursprünglich 4 separate Sandcastle-Prompts vorgesehen (plan-sandcastle-prompt.md etc.), parallel zur mattpocock-Struktur. Verworfen zugunsten von Wiederverwendung der existierenden Prompts plus Wrapper-Header-Strings im .mjs-File. Vorteil: weniger Prompt-Drift, weil Plan/Implement/Review/Merge an einer Stelle gepflegt werden.

3.4 opus für alle Rollen wie im Original

Hätte 1:1-Treue auf der Model-Achse erhalten. Verworfen wegen der Token-Kosten — vor allem Plan-Phase läuft 1× pro Iter (bis zu 10× pro AFK-Lauf), und die Dependency-Graph-Heuristik ist regelbasiert genug, dass Sonnet sie verlässlich liefert. Quality-Risk ist gering, weil ein Sonnet-Planner mit schlechtem Output sofort in Phase 2 als Story-Konflikt oder Implementer-Block auffällt.

4. Consequences

Positiv

  • Ein einziger Workflow-Eintrittspunkt für autonome P2503-Abarbeitung. Eingang in jeden Run: Workflow({scriptPath:'scripts/workflows/adscaile.mjs'}).
  • Single-Story-Modus, Trockenlauf-Modus, AFK-Lauf-Modus über args gewählt — keine Cross-Workflow-Auswahl-Entscheidung.
  • Token-Profil deutlich günstiger als 1:1-Original — nur Reviewer auf opus, Plan und Setup runter auf sonnet bzw. haiku.
  • Prompts scripts/prompts/*.md bleiben Source-of-truth für alle vier Rollen, gepflegt an einer Stelle.

Negativ / Risiko

  • Throughput-Cap: MAX_PARALLEL=1 begrenzt den AFK-Lauf auf eine Story-Pipeline gleichzeitig. Bei 6 Stories pro Iteration und ~30min pro Story landet ein voller 10-Iter-Lauf bei 30 Stunden seriell. Falls das real zum Engpass wird → Eskalations-Pfad ist ADR-0030 (Worktree-basiert parallel).
  • Verlust der Spalten-Driver-Semantik: pipeline-crank's früher PR-Open-für-Stakeholder-Review-Pattern fällt weg. Falls der Use-Case zurückkommt, ist die saubere Reintroduktion ein separater args.openPrAfterImpl=true-Toggle im Implementer.
  • Sonnet-Planner-Quality-Drift: falls Sonnet schlechtere Dependency-Graphen liefert als Opus, fällt das in Phase 2 als Story-Reihenfolge-Problem auf. Rollback ist 1-Line-Change (model:'opus' in der Plan-agent-Call). Quality-Monitoring über die Story-Hub-Worklogs.

Folgeschritte

  • scripts/workflows/pipeline-crank.mjs + ralph-single-story.mjs sind bereits gelöscht (untracked, kein History-Verlust).
  • docs/agents/pipeline-sandcastle.html umbenannt zu docs/agents/adscaile.html mit Model-Strategie-Section + Code-Skelett-Sync.
  • Test-Pfad: Trockenlauf der Plan-Phase (args:{phasesOnly:['plan']}) als erste Validierung, dann Single-Story-Lauf gegen eine kontrollierte Test-Story, dann voller AFK-Lauf.
  • Skill-Inventar in docs/agents/skills.html reviewn — falls dort Skills wie solcom-implement-story auf ralph-single-story.mjs oder ralph.sh verweisen, auf adScaile umstellen.