Plan · 2026-05-17 · ralph-loop

Ralph-Loop — Mass-Implementation autonom über ready-for-agent Jira-Stories

Setzt die Architektur aus ADR-0037 in neun Tracer-Bullet-Slices um. Jede Slice ist end-to-end (Code + Test + Doc-Update) und für sich mergebar. Pilot-Lauf nach Slice 8 auf einer kleinen ready-for-agent Story; Memory-Updates und Skill-Deprecation erfolgen erst nach grünem Pilot.

00

Summary

Strands
scripts/ · .claude/skills/ · docs/
Slices
9 Tracer-Bullets
Pilot-Bedingung
Eine grün durchlaufende ready-for-agent Story bis PR-Open
Trigger-Skill
scripts/ralph.sh

Slices 1–4 bauen die Infrastruktur (DB-Sync, PR-Body, drei Phase-Prompt-Files plan/implement/check, Shell-Orchestrator-Library mit Handoff-File-Mechanik). Slices 5–7 entkoppeln bestehende Skills (auto-review-fix single-shot, solcom-review no-persist, QA-Plan-Refinement-Pflicht). Slice 8 ist das eigentliche ralph.sh-Skript. Slice 9 ist das Single-Story-Skill solcom-implement-story für /goal "implementier P2503-X".

Vor Slice 1: Pre-Flight-Verifikation der Jira-Transition-Namen (acli jira workitem transition --key P2503-108 --list) — Loop-Konstanten JIRA_TRANSITION_IN_PROGRESS, JIRA_TRANSITION_DONE, JIRA_TRANSITION_BLOCKED entsprechend setzen.

01

Tracer-Bullet-Slices

Reihenfolge ist die empfohlene Build-Reihenfolge; Slices 1–4 sind voneinander unabhängig und können parallel angefangen werden, blockieren aber Slice 8.
Slice 1

DB-Sync-Mechanik (PROD-Pull + DDEV-Snapshot)

File scripts/lib/db-sync.sh · Skill mittwald-hosting

Zwei Funktionen in einem POSIX-Shell-Skript: pull_prod_baseline() zieht via mw app exec <app-id> "drush sql-dump --gzip" > /tmp/prod-$(date +%s).sql.gz, importiert via ddev import-db --file=/tmp/...sql.gz, legt DDEV-Snapshot prod-baseline an. restore_prod_baseline() macht ddev snapshot restore prod-baseline && ddev drush cr.

Cleanup-Policy: SQL-Dumps unter /tmp/prod-*.sql.gz werden nach erfolgreichem Snapshot via rm entfernt; DDEV-Snapshot prod-baseline wird vor jedem neuen Pull überschrieben (ddev snapshot --delete=prod-baseline + neu).

Acceptance
  • bash scripts/lib/db-sync.sh pull erzeugt Snapshot prod-baseline ohne Fehler.
  • bash scripts/lib/db-sync.sh restore bringt DB in den gepullten Zustand (verifiziert via Node-Count-Vergleich).
  • Fehlt MITTWALD_API_TOKEN, bricht das Skript mit klarem Fehler < 5 Zeilen ab.
  • Dauer Per-Pull < 5 Min, Per-Restore < 90 s.
Slice 2

PR-Body-Template

File scripts/lib/pr-body-template.md

Markdown-Template mit vier Sektionen und Variablen-Substitution via envsubst:

## Summary
${STORY_SUMMARY}

## Sub-Task-Commits
${COMMIT_LIST}

## Open Findings
${OPEN_FINDINGS}

## QA-Plan
${QA_PLAN}

---
Closes ${JIRA_KEY}
Story-Hub: docs/stories/${JIRA_KEY}-${SLUG}.html

Befüllung: STORY_SUMMARY aus acli jira workitem view ${JIRA_KEY} --json --fields summary,description, COMMIT_LIST aus git log develop..HEAD --pretty=format:"- %s", OPEN_FINDINGS aus dem solcom-review-Output (Markdown-Liste), QA_PLAN aus der Story-Hub-Sektion id="qa-plan" (extrahiert via xmllint --xpath).

Acceptance
  • Template + Variablen-Substitution erzeugt gültigen GitHub-PR-Body (Test: envsubst < template.md mit Mock-Werten).
  • Wenn QA-Plan-Sektion fehlt, gibt das Template QA-Plan: (fehlt — Story-Hub muss QA-Plan-Sektion ergänzen) aus statt zu brechen.
Slice 3

Drei Phase-Prompt-Files (plan / implement / check)

Files scripts/prompts/plan-prompt.md, scripts/prompts/implement-prompt.md, scripts/prompts/check-prompt.md

Drei Plain-Markdown-Prompt-Templates, je eine pro Workflow-Phase. Werte werden via envsubst aus dem Bash-Caller-Environment substituiert. Keine Sub-Agent-Definitionen, keine --agents-JSON — schlanker mattpocock/.sandcastle/-Stil.

scripts/prompts/plan-prompt.md (Skelett):

@${STORY_HUB_PATH}
@docs/adr/0037-ralph-loop-architecture.html

You are the planner for sub-task ${JIRA_KEY} (story ${STORY_KEY}).

Steps:
1. Read the sub-task acceptance criteria:
     acli jira workitem view ${JIRA_KEY} --json --fields description
2. Inventory relevant code files via Grep on AK keywords.
3. Decompose the sub-task into 1-3 TDD slices, each covering ONE
   behaviour with red (test) -> green (impl) -> refactor.
4. Write the handoff file at ${HANDOFF_PATH} as self-contained HTML.

Handoff file MUST contain:
- <meta> tags: ralph:subtask, ralph:story, ralph:branch, ralph:iter (0),
  ralph:status (planned)
- <section id="slices"> with one <tr> per slice:
  number, description, expected file changes, status=todo
- empty <section id="findings-log"> (will be appended to by check-phase)

Do NOT write code outside the handoff file.
End your run with: PLAN_DONE

scripts/prompts/implement-prompt.md (Skelett):

@${HANDOFF_PATH}

You are the implementer for sub-task ${JIRA_KEY}, slice ${SLICE_NUM},
iteration ${ITERATION}.

Steps:
1. Read the handoff file. Locate slice ${SLICE_NUM} in the slices table.
2. If ${ITERATION} > 1: read the most recent findings-log entry
   and address each finding surgically. Do not rewrite unrelated parts.
3. Execute a TDD vertical slice for this slice ONLY:
     a) write a failing test (PHPUnit Kernel/Functional or Twig snapshot)
     b) implement minimal code to make the test green
     c) refactor only if necessary
4. Follow solcom-tdd skill rules. byte_theme/AGENTS.md is non-negotiable.
   Refactor != Rewrite (CLAUDE.md).

Do NOT commit. Do NOT touch other slices.
End your run with: IMPL_DONE

scripts/prompts/check-prompt.md (Skelett):

@${HANDOFF_PATH}

You are the checker for sub-task ${JIRA_KEY}, slice ${SLICE_NUM},
iteration ${ITERATION}. Diff range: ${SLICE_BASE}..HEAD.

Run three sub-stages, each emitting an explicit VERDICT marker:

(1) STANDARDS REVIEW
    - Diff against byte_theme/AGENTS.md (13 canonical rules),
      active ADRs, docs/wiki/ entities,
      four code-reviewer skills.
    - List findings: severity + file:line + rationale.
    - End with: VERDICT: pass  (or fail)

(2) TECH VERIFY
    - Run drupal-pre-merge-gate: PHPCS, PHPStan, PHPUnit, config drift.
    - If diff touches web/themes/custom/** or Canvas composition:
      run agent-browser visual verify against story-hub QA-Plan URLs.
    - Report pass/fail per stage with stack trace on fail.
    - End with: VERDICT: pass  (or fail)

(3) ACCEPTANCE CHECK
    - Read sub-task AKs via acli jira workitem view ${JIRA_KEY}.
    - For each AK: pass/fail/inconclusive plus proof reference
      (test file, URL+step, or drush query).
    - inconclusive counts as fail.
    - End with: VERDICT: pass  (or fail)

Append a new <section> with id="findings-iter-${ITERATION}" inside
<section id="findings-log"> of ${HANDOFF_PATH}. Each sub-stage MUST
contain its VERDICT marker as a recognisable line.

Do NOT write code outside the handoff file.
End your run with: CHECK_DONE
Acceptance
  • Drei Files existieren mit Skelett-Inhalt.
  • Trockenlauf: JIRA_KEY=P2503-127 STORY_KEY=P2503-108 STORY_HUB_PATH=docs/stories/P2503-108-….html HANDOFF_PATH=/tmp/test-handoff.html claude -p "$(envsubst < scripts/prompts/plan-prompt.md)" erzeugt gültiges Handoff-HTML in /tmp/test-handoff.html.
  • Trockenlauf check-prompt.md auf einem präparierten Diff erzeugt drei VERDICT:-Marker im findings-log.
  • Interaktiver Pfad: /goal "@scripts/prompts/plan-prompt.md plane mir P2503-127" funktioniert in-session (ad-hoc-Planning ohne ralph.sh).
Slice 4

Shell-Orchestrator-Library + State-Verzeichnis

Files scripts/lib/subtask-orchestrator.sh, scripts/state/ralph/.gitkeep, .gitignore (Anpassung)

Bash-Library mit den Helper-Funktionen, die ralph.sh für den Sub-Task-Workflow aufruft. Keine LLM-Layer — reine Shell-Logik plus xmlstarlet für Handoff-Mutation.

Zu liefernde Funktionen:

  • next_todo_slice $HANDOFF — gibt die Nummer der ersten Slice mit Status todo aus, leerer Output wenn alle done.
  • mark_slice_doing $HANDOFF $NUM, mark_slice_done $HANDOFF $NUM, mark_slice_blocked $HANDOFF $NUM — Status-Mutation via xmlstarlet ed -L.
  • passes_check $HANDOFF $SLICE_NUM $ITER — liest die zuletzt geschriebene section#findings-iter-$ITER, prüft ob alle drei VERDICT: pass-Marker enthalten sind; gibt 0 (pass) oder 1 (fail) zurück.
  • run_subtask_pipeline $SUB — Top-Level: ruft Plan-Phase, dann Slice-Loop, dann Rework-Loop pro Slice (siehe Skelett unten).

Zusätzlich: Verzeichnis scripts/state/ralph/ existiert mit .gitkeep; .gitignore erweitern um scripts/state/ralph/*-handoff.html (nur Handoff-Files, nicht das Verzeichnis selbst).

scripts/lib/subtask-orchestrator.sh (Skelett, ~120 Zeilen):

run_subtask_pipeline() {
  local SUB="$1"
  export JIRA_KEY="$SUB"
  export STORY_KEY="$STORY_KEY"   # set by caller
  export STORY_HUB_PATH HANDOFF_PATH
  STORY_HUB_PATH="$(ls docs/stories/${STORY_KEY}-*.html | head -1)"
  HANDOFF_PATH="scripts/state/ralph/${SUB}-handoff.html"

  # 1) plan once (skip on recovery)
  if [ ! -f "$HANDOFF_PATH" ]; then
    claude -p --permission-mode acceptEdits \
      --output-format stream-json --verbose \
      "$(envsubst < scripts/prompts/plan-prompt.md)" \
      | tee "/tmp/ralph-${SUB}-plan.jsonl" \
      | jq -r 'select(.type=="assistant") | .message.content[]?.text // empty'
  fi

  # 2) slice loop
  while SLICE_NUM=$(next_todo_slice "$HANDOFF_PATH") && [ -n "$SLICE_NUM" ]; do
    mark_slice_doing "$HANDOFF_PATH" "$SLICE_NUM"
    export SLICE_NUM
    export SLICE_BASE
    SLICE_BASE=$(git rev-parse HEAD)

    # 3) rework loop (max 3)
    for iter in 1 2 3; do
      export ITERATION="$iter"

      claude -p --permission-mode acceptEdits \
        --output-format stream-json --verbose \
        "$(envsubst < scripts/prompts/implement-prompt.md)" \
        | tee "/tmp/ralph-${SUB}-s${SLICE_NUM}-i${iter}-impl.jsonl" \
        | jq -r 'select(.type=="assistant") | .message.content[]?.text // empty'

      claude -p \
        --output-format stream-json --verbose \
        "$(envsubst < scripts/prompts/check-prompt.md)" \
        | tee "/tmp/ralph-${SUB}-s${SLICE_NUM}-i${iter}-check.jsonl" \
        | jq -r 'select(.type=="assistant") | .message.content[]?.text // empty'

      if passes_check "$HANDOFF_PATH" "$SLICE_NUM" "$iter"; then
        git add .
        git commit -m "feat(${SUB}): slice ${SLICE_NUM}"
        mark_slice_done "$HANDOFF_PATH" "$SLICE_NUM"
        break
      fi
    done

    if ! passes_check "$HANDOFF_PATH" "$SLICE_NUM" 3; then
      mark_slice_blocked "$HANDOFF_PATH" "$SLICE_NUM"
      echo "<promise>SUBTASK_BLOCKED:${SUB}:slice${SLICE_NUM}</promise>"
      return 1
    fi
  done

  rm "$HANDOFF_PATH"
  echo "<promise>SUBTASK_DONE:${SUB}</promise>"
}

next_todo_slice() {
  xmlstarlet sel -t -v \
    '//section[@id="slices"]//tr[td[last()]="todo"][1]/td[1]' "$1" 2>/dev/null
}

mark_slice_doing()   { _set_slice_status "$1" "$2" "doing"; }
mark_slice_done()    { _set_slice_status "$1" "$2" "done"; }
mark_slice_blocked() { _set_slice_status "$1" "$2" "blocked"; }

_set_slice_status() {
  xmlstarlet ed -L -u \
    "//section[@id='slices']//tr[td[1]='$2']/td[last()]" -v "$3" "$1"
}

passes_check() {
  local HANDOFF="$1" SLICE_NUM="$2" ITER="$3"
  local SECTION="//section[@id='findings-iter-${ITER}']"
  local VERDICTS
  VERDICTS=$(xmlstarlet sel -t -v "${SECTION}//text()[contains(.,'VERDICT')]" "$HANDOFF" 2>/dev/null)
  local PASS_COUNT FAIL_COUNT
  PASS_COUNT=$(echo "$VERDICTS" | grep -c 'VERDICT: pass' || true)
  FAIL_COUNT=$(echo "$VERDICTS" | grep -c 'VERDICT: fail' || true)
  [ "$PASS_COUNT" -eq 3 ] && [ "$FAIL_COUNT" -eq 0 ]
}
Acceptance
  • Library lässt sich sourcen ohne Fehler: source scripts/lib/subtask-orchestrator.sh.
  • Unit-Test gegen Mock-Handoff-File: passes_check liefert 0 bei drei VERDICT: pass-Markern, 1 bei einem fail.
  • next_todo_slice liefert die richtige Slice-Nummer bei einem Handoff mit gemischten Status.
  • scripts/state/ralph/.gitkeep ist committed; .gitignore-Eintrag scripts/state/ralph/*-handoff.html ist gesetzt.
  • End-to-End-Trockenlauf: run_subtask_pipeline P2503-127 auf einer kleinen ready-for-agent Story endet mit <promise>SUBTASK_DONE...</promise> oder <promise>SUBTASK_BLOCKED...</promise>; Conventional-Commit pro Slice steht im Git-Log.
Slice 5

Auto-Review-Fix Single-Shot-Mode

File .claude/skills/auto-review-fix/SKILL.md

Neues Flag --single-pass (Standard für RALPH_LOOP=1-Environment): keine Worktree-Erzeugung, kein Retry-bis-grün, kein Persistent-Monitor — nur einmaliger Pass über den Review-Report, alles deterministisch Fixbare (Imports, Whitespace, Phpcbf, Twig-Format) wird angewendet, der Rest bleibt unfixed und wird als OPEN_FINDINGS-Liste an stdout zurückgegeben.

Der Worktree-basierte Mode bleibt als Default für manuelle Aufrufe; der single-shot Mode ist additiv.

Acceptance
  • Skill-File hat neuen Header-Block „Single-Pass-Mode (RALPH_LOOP=1)" mit Aufruf-Beispiel.
  • Test im Main-Tree: skill auf einem Mock-Review-Report mit drei Findings, davon zwei fixbar — nach Lauf sind die zwei gefixt + committed, das dritte steht in stdout.
Slice 6

Solcom-Review im ralph-Modus (—no-persist)

File .claude/skills/solcom-review/SKILL.md

Skill respektiert Environment-Var RALPH_LOOP=1 als Override für das --persist-Default (Memory [[feedback_solcom_review_persist_default]]). Im ralph-Modus: Report nur als stdout-Markdown, kein File-Write nach docs/reports/, kein LOG.html-Eintrag.

Begründung: ohne Override entstehen bei 50 Stories pro Run 50 HTML-Reports — operativer Müll. Mass-Run-Reports sind ephemer; Persistierung ist nur für manuelle, ad-hoc Reviews wertvoll.

Acceptance
  • RALPH_LOOP=1 /solcom-review main gibt Report an stdout, schreibt nichts nach docs/reports/.
  • Ohne Env-Var bleibt Default-Verhalten (persist + LOG-Eintrag).
  • Memory [[feedback_solcom_review_persist_default]] mit Override-Klausel erweitert.
Slice 7

QA-Plan-Sektion Refinement-Pflicht

Files .claude/skills/to-prd/SKILL.md, .claude/skills/to-plan/SKILL.md, .claude/skills/grill-with-docs/SKILL.md

Output-Templates der drei Skills um Pflicht-Sektion erweitern:

<section id="qa-plan">
  <h2>QA-Plan</h2>
  <p>Click-Pfade pro Story-AK für Mensch-QA.</p>
  <table>
    <thead><tr><th>AK</th><th>URL</th><th>Action</th><th>Expected</th></tr></thead>
    <tbody>
      <tr><td>…</td><td>…</td><td>…</td><td>…</td></tr>
    </tbody>
  </table>
</section>

Skill-Prompt erzwingt im Grilling: vor Schreiben des Story-Hubs muss QA-Plan-Sektion gefüllt sein. Stories ohne QA-Plan werden vom Ralph-Loop übersprungen und mit Jira-Comment „missing QA plan, refine before ready-for-agent" zurückgesetzt.

Eigenständige Mini-Action: bestehende Stories mit Label ready-for-agent (Stand 2026-05-17: via acli jira workitem search --jql "project=P2503 AND labels=ready-for-agent" ermitteln) müssen QA-Plan-Sektion nachgepflegt werden — Zähl-Aufgabe vor Pilot-Lauf.

Acceptance
  • Drei Skill-Files haben Output-Template mit <section id="qa-plan">.
  • Ein neuer Probe-Run von to-plan auf einer Test-PRD erzeugt Story-Hub mit befüllter QA-Plan-Sektion.
  • Inventory bestehender ready-for-agent Stories mit fehlendem QA-Plan ist erstellt (als Jira-Comment am Parent-Story-Ticket).
Slice 8

scripts/ralph.sh Mass-Loop

File scripts/ralph.sh · Dependencies Slices 1–7

Bash-Skript, ~120 Zeilen mit Kommentaren. Skelett:

#!/usr/bin/env bash
set -euo pipefail

if [ -z "${1:-}" ]; then echo "Usage: $0 <max-stories>"; exit 1; fi

export RALPH_LOOP=1
source scripts/lib/db-sync.sh

# Pre-Loop: PROD-Pull
pull_prod_baseline

for ((i=1; i<=$1; i++)); do
  echo "=== Iteration $i ==="

  # 0. Recovery-Check
  IN_PROGRESS=$(acli jira workitem search --jql \
    'project=P2503 AND status="In Progress" AND assignee=currentUser()' \
    --json | jq -r '.[].key')
  if [ -n "$IN_PROGRESS" ]; then
    STORY_KEY=$(echo "$IN_PROGRESS" | head -1)
    BRANCH="feature/$STORY_KEY"
    echo "Recovery: resume $STORY_KEY on $BRANCH"
    git checkout "$BRANCH" || { echo "branch missing, hard-stop"; exit 1; }
  else
    # 1. Pickup
    # Label sitzt auf Sub-Tasks (Refinement-Granularität).
    # Planner aggregiert Sub-Tasks zu Parent-Stories, picked die höchstrangige.
    STORY_KEY=$(acli jira workitem search --jql \
      'project=P2503 AND labels=ready-for-agent AND issuetype="Sub-Task"
       AND status="Zu erledigen" ORDER BY rank ASC' \
      --json --limit 50 \
      | jq -r '[.[] | .fields.parent.key] | unique | .[0] // empty')
    if [ -z "$STORY_KEY" ]; then
      echo "<promise>NO_AGENT_READY</promise>"
      exit 0
    fi

    # 2. Branch
    git checkout develop && git pull origin develop --rebase
    git checkout -b "feature/$STORY_KEY"

    # 3. DB-Restore
    restore_prod_baseline

    # 4. Jira-Transition: In Progress
    acli jira workitem transition --key "$STORY_KEY" \
      --transition "In Progress" --yes
  fi

  # 5. Sub-Task-Loop — pipeline-Funktion aus scripts/lib/subtask-orchestrator.sh
  source scripts/lib/subtask-orchestrator.sh
  SUBTASKS=$(acli jira workitem view "$STORY_KEY" --json \
    --fields subtasks | jq -r '.subtasks[].key')

  for SUB in $SUBTASKS; do
    SUB_STATUS=$(acli jira workitem view "$SUB" --json --fields status \
      | jq -r '.status.name')
    if [ "$SUB_STATUS" = "Fertig" ]; then continue; fi

    acli jira workitem transition --key "$SUB" --transition "In Progress" --yes

    if ! run_subtask_pipeline "$SUB"; then
      # SUBTASK_BLOCKED — Jira-Transition + Comment, nächste Story
      acli jira workitem transition --key "$SUB" --transition "Blocked" --yes
      acli jira workitem comment create --key "$STORY_KEY" \
        --body-file "scripts/state/ralph/${SUB}-handoff.html"
      git checkout develop
      continue 2  # nächste Story
    fi

    acli jira workitem transition --key "$SUB" --transition "Done" --yes
  done

  # 6. Pre-PR-Tail
  REVIEW_REPORT=$(claude -p "$(cat <<EOF
/solcom-review develop
EOF
)")
  FIX_RESULT=$(claude -p "$(cat <<EOF
/auto-review-fix --single-pass --report-stdin
$REVIEW_REPORT
EOF
)")
  OPEN_FINDINGS=$(echo "$FIX_RESULT" | jq -r '.open_findings')

  # 7. PR open
  git push -u origin "feature/$STORY_KEY"
  PR_BODY=$(STORY_KEY="$STORY_KEY" OPEN_FINDINGS="$OPEN_FINDINGS" \
    bash scripts/lib/pr-body-template.sh)
  gh pr create --base develop --head "feature/$STORY_KEY" \
    --title "feat($STORY_KEY): $(acli jira workitem view "$STORY_KEY" --json \
      --fields summary | jq -r '.summary')" \
    --body "$PR_BODY"

  # 8. Cleanup: lokaler Branch weg, zurück zu develop
  git checkout develop
  git branch -D "feature/$STORY_KEY"

  # 9. Story-Transition: Done (oder „In Review" je nach Workflow)
  acli jira workitem transition --key "$STORY_KEY" \
    --transition "Done" --yes
done
Acceptance (Pilot-Run-Bedingung)
  • ./scripts/ralph.sh 1 läuft End-to-End auf einer Pilot-Story (kleinste verfügbare ready-for-agent Story mit QA-Plan).
  • Ergebnis am Ende: Feature-Branch remote vorhanden, PR offen mit gefülltem Template, Story-Status „Done" in Jira, lokales Repo auf sauberem develop.
  • Trockener Re-Run (gleiche Pilot-Story wiederholen, Story noch nicht „Done"): Recovery-Pfad nimmt den In-Progress-Status korrekt auf.
  • Fail-Pilot (Sub-Task absichtlich blockend gestaltet, z. B. unmöglicher Test): Sub-Task auf „Blocked", Jira-Comment angelegt, Loop kehrt zu develop zurück, exit-code 0.
Slice 9

Single-Story-Skill solcom-implement-story

File .claude/skills/solcom-implement-story/SKILL.md · Dependencies Slices 1–7

Skill ruft dieselbe Phase-Pipeline (plan-prompt.md → implement-prompt.md → check-prompt.md, gesteuert durch scripts/lib/subtask-orchestrator.sh) wie ralph.sh auf — aber in-session, ohne Mass-Pickup-Loop. Eingabe: ein Jira-Story-Key. Output: gleiches Endergebnis (Feature-Branch + PR offen). Ad-hoc-Variante: /goal "@scripts/prompts/plan-prompt.md plane P2503-127" ruft nur die Plan-Phase auf, ohne durchzuiterieren.

Trigger-Phrasen: „implementier P2503-XXX", „bau Story P2503-XXX", „/goal "implementier P2503-XXX"". Damit ist /goal als Single-Story-Driver erschlossen ohne ralph.sh-Wrapper.

Stylistisch entspricht das Skill dem Pattern von backlog-to-jira: schmaler Wrapper über die operativen Bausteine. Keine eigene Choreographie.

Acceptance
  • /goal "implementier P2503-XXX" löst Skill aus, das die gleiche Pipeline wie ralph.sh durchläuft (Branch, DB, Sub-Tasks, Pre-PR-Tail, PR).
  • Memory [[project_implement_pipeline_triple]] ist re-written und referenziert dieses Skill plus ralph.sh.
  • Alter implement-Skill bekommt Deprecation-Header mit Link auf dieses Skill und ralph.sh.
02

Risks & Mitigations

Risiko
Severity
Mitigation
Handoff-File-Mutation via xmlstarlet korrumpiert HTML (XPath schlägt fehl, in-place edit zerstört Datei)
med
Slice 4 Acceptance erzwingt Unit-Test-Coverage für next_todo_slice, _set_slice_status, passes_check gegen Mock-Handoffs. Backup-Strategie: cp $HANDOFF $HANDOFF.bak vor jeder Mutation, Rollback auf Fail.
Check-Phase emittiert nicht alle drei VERDICT-Marker (Sonnet vergisst eine Sub-Stage)
med
passes_check verlangt exakt drei VERDICT: pass-Marker; bei weniger gilt der Check als fail und löst Rework aus. Prompt enthält explizit „each sub-stage MUST contain its VERDICT marker as a recognisable line".
Jira-Transition-Namen abweichen („Erledigt" statt „Done", „Geblockt" statt „Blocked")
med
Pre-Flight-Verifikation via acli jira workitem transition --key P2503-108 --list als allererster Schritt; Konstanten in ralph.sh entsprechend setzen
Bestehende ready-for-agent Stories haben keinen QA-Plan im Story-Hub
med
Slice 7 ergänzt Skip-Mechanik im Loop (Story ohne QA-Plan → zurücksetzen + Jira-Comment); Pre-Pilot-Inventory der betroffenen Stories als eigene Aufgabe
DDEV-Snapshot-Restore kollidiert mit laufender DDEV-Container-Aktivität (z. B. open ssh-Session, Maintenance-Mode)
med
db-sync.sh restore ruft vorab ddev stop && ddev start; falls Container nicht clean stoppt, exit-code-Check und Hard-Stop
PR-Body-Substitution scheitert wenn Story-Summary Sonderzeichen enthält
low
Substitution via envsubst (POSIX-safe); JSON-Outputs vorher mit jq -r entjsonifizieren
Mass-Run kostet mehr als erwartet (>$300 pro Run)
low
<n>-Argument am Aufruf ist harter Cap; Pilot-Run mit n=1, dann n=3, dann frei wählbar je nach Pilot-Cost
Branch-Lifecycle: lokales git branch -D löscht Story-Branch bevor PR-Push remote durch ist
med
Branch-Delete-Step bedingt durch gh pr view --json url-Check (PR muss existieren); bei Push-Fehler bleibt Branch lokal stehen
03

Pilot-Run-Checklist

Bedingung für „Architektur ist validiert" — erst danach werden Memory-Updates und Skill-Deprecation gemerged.
04

Open Questions

Q1 — Conventional-Commit-Type pro Sub-Task
Soll der Sub-Task-Type (feat/fix/refactor/docs/test/chore) aus dem Jira-Sub-Task-Issuetype abgeleitet werden, oder von der implement-Phase aus dem Diff-Kontext bestimmt (in der finalen Commit-Message-Zeile)? Mein Default-Vorschlag: implement-Phase setzt am Ende ihres Runs eine COMMIT_TYPE: feat-Marker-Zeile, die der Shell-Caller parst — zuverlässiger als Issuetype-Mapping.
Q2 — Story-Transition nach PR-Open
Direkt auf „Done" (Story gilt als abgeschlossen mit PR-Open) oder auf „In Review" (Mensch muss PR mergen, dann manuell „Done")? Default-Vorschlag: „In Review" — sauberer Trace, Mensch hat letzte Entscheidung.
Q3 — Cleanup-Policy für DDEV-Snapshots
prod-baseline wird pro Run überschrieben — sollen alte Snapshots automatisch gelöscht werden, oder behalten wir Historie? Default-Vorschlag: automatisches Überschreiben (1 baseline pro Run, kein Lifecycle).
Q4 — Cost-Caps pro Sub-Task
Soll der Loop einen weichen Cost-Cap pro Sub-Task einbauen (z. B. Sonnet-Calls hart limitieren via --max-turns)? Default-Vorschlag: zunächst nicht — <n>-Argument am Top-Level reicht als Cap.
Q5 — Streaming-Output
Slice 8 nutzt --output-format stream-json --verbose plus tee + jq-Filter, damit Token-Stream während des Loops live im Terminal sichtbar ist und der finale Result-Frame strukturiert in $RESULT landet (chrismdp/ralph-Pattern). Per-Sub-Task wird ein /tmp/ralph-${STORY}-${SUB}.jsonl-Log behalten für Post-Mortem; Cleanup-Policy: nach erfolgreichem Story-Done rm /tmp/ralph-${STORY}-*.jsonl, bei Block-Status bleiben Logs für Mensch-Inspektion liegen.
05

Post-Pilot — Memory- und Skill-Wartung

Erst nach grünem Pilot: