Implements docs/intent/board-builder-limit-reached-auto-split.md. A card that hits limit_reached twice (manual trigger + its one automatic retry) can now be dragged into a new "Aufteilen angefordert" column, which runs the Planner role read-only against the original intent plus the stored builder_report and splits it into up to 10 independent child intents (===SPLIT-REPORT===/===CHILD=== marker blocks, wrapper writes every docs/intent/<child-slug>.md itself, all-or-nothing on any malformed or oversized proposal). Children land in "Geplant" with parent_id set, no auto-trigger; the parent moves to the new terminal phase "Aufgeteilt" with child_slugs recorded. Implemented directly in this session rather than via the board's own Builder pipeline: the automated Builder run for this exact intent hit its own 20-iteration limit twice, which is the failure mode this feature exists to handle. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F51W1LEGuSvNroZNj7vXqC
135 lines
7.5 KiB
Markdown
135 lines
7.5 KiB
Markdown
# Intent: Automatisches Zerlegen einer zu großen Karte über eine Board-Spalte
|
|
|
|
## Ziel
|
|
|
|
Für eine Builder-Karte, die auch nach dem automatischen Einzelversuch aus
|
|
`docs/intent/board-builder-limit-reached-fallback.md` erneut am
|
|
Iterationslimit hängen bleibt, eine bewusste, manuell ausgelöste
|
|
Eskalationsstufe anbieten: eine eigene Board-Spalte, in die man die Karte
|
|
zieht, löst einen automatischen Zerlege-Lauf aus, der den zu großen
|
|
Intent in mehrere kleinere, unabhängige Kind-Intents aufteilt. Das
|
|
Zusammenführen der Ergebnisse am Ende ist **kein neuer Mechanismus** —
|
|
dafür wird der bereits entworfene, aber noch nicht gebaute
|
|
Synthese-Schritt aus `docs/intent/board-subintent-fanout-synthesis.md`
|
|
wiederverwendet, indem das Zerlegen genau dessen Datenmodell
|
|
(`parent_id`, Kind-Endzustände) von Anfang an befüllt.
|
|
|
|
## Umfang
|
|
|
|
- Neue Phase `aufteilen_angefordert` in `trigger.py`s `PHASES`, erreichbar
|
|
nur per manuellem Drag einer Karte, deren `last_run_status ==
|
|
"limit_reached"` **und** deren `auto_retry_attempts >= 1` ist (der
|
|
automatische Einzelversuch aus dem Fallback-Intent muss bereits
|
|
verbraucht sein) — verhindert, dass diese teurere Eskalationsstufe vor
|
|
der billigeren automatischen Wiederholung gezogen wird.
|
|
- Der Move queued einen neuen `job_type="split"`-Job in `board-agent`s
|
|
eigener Inbox (analog zu `"review"`/`"research"`).
|
|
- `worker.py` bekommt `run_split()`: ruft die Planner-Rolle read-only auf
|
|
(gleiches Sandbox-Muster wie `run_review()` — keine Schreibrechte),
|
|
Kontext ist der vollständige, unveränderte Original-Intent **plus** der
|
|
gespeicherte `builder_report`-Text aus dem/den gescheiterten
|
|
Builder-Versuch(en) (bereits auf der Karte vorhanden, kein Zugriff auf
|
|
Git-Stashes/Diffs nötig), Auftrag: den Intent in N unabhängige,
|
|
eigenständig umsetzbare Kind-Intents zerlegen.
|
|
- Antwortformat: ein Marker-Block (`===SPLIT-REPORT===` /
|
|
`===END-SPLIT-REPORT===`), der pro Kind einen eigenen Unterblock mit
|
|
Slug und vollständigem Intent-Dokumentinhalt liefert — der Wrapper
|
|
parst das und schreibt jede `docs/intent/<kind-slug>.md`-Datei selbst,
|
|
außerhalb des Modellprozesses (dasselbe „Rolle schlägt vor, Wrapper
|
|
wendet an"-Muster wie bei Review-/Research-/Commit-Marker).
|
|
- Für jedes neu geschriebene Kind: initialer State-Eintrag mit
|
|
`parent_id = <Slug der Elternkarte>`, Phase **„Geplant"** — kein
|
|
automatischer Trigger, jedes Kind durchläuft den normalen manuellen
|
|
„Geplant → In Umsetzung"-Weg, exakt wie in
|
|
`board-subintent-fanout-synthesis.md` für den Synthese-Intent gefordert.
|
|
- **Obergrenze: maximal 10 Kinder pro Zerlege-Vorschlag.** Enthält der
|
|
Marker-Block mehr als 10 Unterblöcke, wird der gesamte Vorschlag
|
|
verworfen (keine einzige Kind-Datei geschrieben) und der Job endet wie
|
|
ein fehlgeschlagener Split — dieselbe Alles-oder-nichts-Validierung,
|
|
die `run_review()` heute schon für die Entscheidungszeile durchsetzt,
|
|
keine partielle Anwendung.
|
|
- Die Elternkarte wechselt nach erfolgreichem Split in eine neue
|
|
Endphase `aufgeteilt` (analog zu `archiviert` behandelt: kein Vorwärts-
|
|
Move erlaubt außer der ohnehin immer erlaubten Rückwärtsbewegung),
|
|
bekommt `child_slugs` (Liste) zur Anzeige gesetzt.
|
|
- Farbliche/visuelle Gruppierung von Eltern-/Kindkarten im Frontend ist
|
|
**nicht Teil dieses Intents**, sondern die bereits in
|
|
`board-subintent-fanout-synthesis.md` als Folgeintent ausgeklammerte
|
|
„Board-UI-Änderung (Parent-/Child-Anzeige, Baumdarstellung)".
|
|
|
|
## Explizit ausgeschlossen
|
|
|
|
- Keine Änderung an der automatischen Einzel-Retry-Logik aus
|
|
`board-builder-limit-reached-fallback.md` — diese Eskalationsstufe
|
|
setzt exakt dort auf, wo jene endet.
|
|
- Keine automatische Weiterverkettung über die Kind-Erzeugung hinaus —
|
|
kein Kind wird automatisch auf „In Umsetzung" gesetzt, kein
|
|
Synthese-Intent wird von diesem Intent selbst erzeugt (das bleibt
|
|
vollständig Aufgabe von `board-subintent-fanout-synthesis.md`, sobald
|
|
dieses umgesetzt ist).
|
|
- Keine Board-UI-Arbeit (Farbgruppierung, Baumdarstellung) — separater,
|
|
bereits ausgeklammerter Folgeintent.
|
|
- Kein Zugriff des Zerlege-Laufs auf Git-Stashes/rohe Diffs vorheriger
|
|
Builder-Versuche — nur der bereits gespeicherte `builder_report`-Text.
|
|
- Keine Bearbeitung/Freigabe des Zerlege-Vorschlags **vor** dem Schreiben
|
|
der Kind-Dateien — die Prüfung findet stattdessen implizit dadurch
|
|
statt, dass jedes Kind zunächst in „Geplant" landet und einen normalen
|
|
manuellen Trigger braucht, nicht durch einen zusätzlichen
|
|
Freigabeschritt direkt nach dem Split.
|
|
- Setzt `board-subintent-fanout-synthesis.md` **nicht** als
|
|
Voraussetzung für die Umsetzung dieses Intents selbst voraus (das
|
|
Schreiben der Kind-Dateien mit `parent_id` funktioniert unabhängig
|
|
davon) — wohl aber für den späteren automatischen Synthese-Schritt, der
|
|
ohne jenes Intent schlicht noch nicht existiert und die Kinder dann
|
|
wie jede andere fertige Karte manuell zusammengeführt werden müssten.
|
|
|
|
## Annahmen
|
|
|
|
- Die Planner-Rolle kann read-only aufgerufen werden, ähnlich der
|
|
Reviewer-Rolle heute (`--sandbox read-only` bzw. deaktivierte
|
|
Schreibwerkzeuge je Runtime).
|
|
- Ein automatisch vorgeschlagener Zerlege-Plan ist grundsätzlich weniger
|
|
durchdacht als ein von Hand erarbeiteter — dieselbe Einschränkung, die
|
|
`board-subintent-fanout-synthesis.md` schon für automatisch erzeugte
|
|
Synthese-Intents benennt. Das Landen in „Geplant" statt eines
|
|
automatischen Triggers ist die bewusste Absicherung dagegen, keine
|
|
zusätzliche Qualitätsprüfung.
|
|
- `docs/intent/<slug>.md` bleibt die einzige Quelle der Wahrheit für den
|
|
Intent-Inhalt; `parent_id`/`child_slugs` leben ausschließlich im
|
|
Runtime-Overlay (`state.json`), nicht im Intent-Dokument selbst.
|
|
|
|
## Offene Risiken
|
|
|
|
- Die Unabhängigkeit der vorgeschlagenen Kind-Intents ist nicht
|
|
automatisch geprüft (gleiche Grundannahme wie bei
|
|
`board-builder-worktree-parallelism.md`: Unabhängigkeit ist
|
|
Planungs-, nicht Werkzeugaufgabe) — ein schlechter Zerlege-Vorschlag
|
|
mit überlappenden Zielen fällt erst beim manuellen Review jedes Kindes
|
|
auf, nicht vorher.
|
|
- Der Zerlege-Lauf selbst könnte bei einem besonders unübersichtlichen
|
|
Original-Intent an sein eigenes Iterationslimit stoßen; da das
|
|
Auslösen ein einzelner, manueller Schritt ist (kein automatischer
|
|
Retry für den Split-Job selbst vorgesehen), bleibt die Karte in diesem
|
|
Fall einfach wie zuvor stehen — kein Rekursionsrisiko.
|
|
- Falls `board-subintent-fanout-synthesis.md` nie umgesetzt wird, bleiben
|
|
automatisch erzeugte Kinder ohne automatischen Synthese-Schritt am
|
|
Ende — funktional weiterhin nutzbar (jedes Kind ist eine normale
|
|
Karte), nur ohne die automatische Zusammenführung.
|
|
|
|
## Erfolgskriterien
|
|
|
|
- Eine Karte mit `last_run_status == "limit_reached"` und bereits
|
|
verbrauchtem automatischem Retry lässt sich in die neue Spalte ziehen
|
|
und löst genau einen Zerlege-Lauf aus.
|
|
- Der Zerlege-Lauf schreibt N (maximal 10) neue `docs/intent/<slug>.md`-
|
|
Dateien mit vollständigem, eigenständigem Intent-Inhalt, jede mit
|
|
`parent_id` auf die Elternkarte in `state.json` verknüpft, Phase
|
|
„Geplant". Bei mehr als 10 vorgeschlagenen Kindern wird keine einzige
|
|
Datei geschrieben, der Split-Job gilt als fehlgeschlagen.
|
|
- Die Elternkarte wechselt nach „aufgeteilt" und referenziert ihre
|
|
Kinder; kein Kind wird automatisch angestoßen.
|
|
- Kinder verhalten sich ansonsten wie jede normale Intent-Karte
|
|
(manueller „Geplant → In Umsetzung"-Trigger, eigener Builder-/Review-
|
|
Durchlauf).
|
|
- Bestehende Tests (`test_board_agent.py`) bleiben grün oder werden
|
|
entsprechend angepasst.
|