Two independent features landed together in this checkout: - Isolated per-job git worktrees for the flake repo (only) so Builder jobs stop serializing on one shared checkout, plus a second dedicated builder instance/inbox for the board's "leicht" column (cheap OpenRouter model) alongside the existing "schwer" one. Fixes bundled in: correct -C/prompt path once a worktree is in play, git+ssh in the deploy runner's PATH, ProtectHome write access for its own sync-to-dev step, and per-(role,sender,board_slug) scoping for the Builder's carried-forward context so concurrent jobs sharing the board's fixed owner sender no longer bleed context into each other. - A new Operator role and deterministic scheduler between planning and Builder execution (docs/intent/board-intent-operator-orchestration.md): moving a planned intent into the Operator column analyzes it read-only and automatically dispatches either a direct Builder run or a validated split into dependency-aware, wave-scheduled child intents, capped at two concurrent Operator-originated Builder jobs. Synthesis only fires once every child has passed review. Also fixes a live incident hit while deploying this: board-agent-worker and board-agent-completion-watcher gained ReadWritePaths entries for the new leicht-lane inbox/processing dirs without a matching tmpfiles rule, so the directories not existing yet crashed the service (start-limit-hit) the first time a job needed them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GurGgq83aNurjWbaNQ71Jr
13 KiB
Intent: Automatisierte Intent-Operator- und Builder-Orchestrierung
Ziel
Zwischen Planung und Umsetzung einen neuen Agenten namens Operator einführen. Wird ein geplanter Parent-Intent in die Operator-Spalte verschoben, beginnt seine Analyse sofort und ohne weitere Freigabe. Der Operator entscheidet, ob der Intent direkt in eine Builder-Pipeline gehen kann oder vorher in kleinere, eigenständig umsetzbare Builder-Unter-Intents zerlegt werden muss.
Eine erfolgreiche Operator-Entscheidung startet die empfohlenen Builder- Läufe automatisch. Eine deterministische Scheduling-Schicht setzt dabei Abhängigkeiten und die begrenzte VPS-Kapazität durch. So bleibt die inhaltlich anspruchsvolle Zerlegung bei einem dafür geeigneten Modell, während kleinere, klar begrenzte Unter-Intents mit günstigeren Profilen bearbeitet werden können.
Umfang
Operator-Rolle und Vertrauensgrenze
- Eine neue, eigenständige Rolle
operatormit eigener Rollendatei, Runtime-Konfiguration und eigenem Katalogeintrag einführen. - Der Operator liest den vollständigen Parent-Intent sowie den für seine Entscheidung nötigen Repository-Kontext read-only.
- Der Operator schreibt keine Dateien, verändert keinen Board-State, queued keine Jobs, committet und deployt nicht.
- Der Operator liefert ausschließlich eine streng strukturierte Antwort. Ein vertrauenswürdiger Board-Wrapper validiert sie vollständig und führt erst danach die vorgesehenen Datei-, State- und Queue-Änderungen aus.
Board-Phasen und Entscheidungen
- Neue, eindeutig sichtbare Phasen einführen:
operator_angefordertoperator_laeuftwartet_auf_kinderdurch_kinder_blockiert
- Das Verschieben einer Karte von
geplantnachoperator_angefordertqueued unmittelbar den Operator-Job. Es gibt danach kein zusätzliches Bestätigungs- oder Freigabe-Gate. - Der Operator liefert genau eine der folgenden Entscheidungen:
direct_builder: Der Parent-Intent ist klein und präzise genug für einen direkten Builder-Lauf.split: Der Parent-Intent wird in mehrere Builder-Unter-Intents zerlegt.needs_human: Eine sichere automatische Entscheidung ist wegen Unklarheit, notwendiger Recherche, zu starker Kopplung oder eines nicht unterstützten Falls nicht möglich.
- Bei
direct_builderwählt der Operator eine empfohlene Builder-Lane und ein Modell-/Reasoning-Profil. Der Wrapper queued den ursprünglichen Parent-Intent danach automatisch in der empfohlenen Builder-Pipeline. - Bei
spliterzeugt der Wrapper atomar höchstens zehn vollständige, eigenständig umsetzbare Builder-Intent-Dateien. Entweder werden alle Kinder und ihre Beziehungen angelegt oder keine davon. - Bei
needs_humanoder einer ungültigen Operator-Antwort entstehen keine Kinder und kein Builder-Job. Die Parent-Karte zeigt den Grund und einen klaren manuellen Recovery-Pfad. - In dieser ersten Version erzeugt der Operator ausschließlich Builder-
Kinder. Benötigt der Auftrag zuerst Research, muss er
needs_humanwählen.
Ausführungsplan und Parent-/Child-Modell
- Das vorhandene Parent-/Child-Modell aus
fanout.pywiederverwenden und für jedes Kind mindestens folgende Metadaten speichern:parent_idblocked_bywaverecommended_lane- empfohlenes Runtime-/Modell-/Reasoning-Profil
- erwarteter Schreibumfang beziehungsweise Zielpfade
- kindspezifische Akzeptanzkriterien
blocked_bybildet echte Ausführungsabhängigkeiten ab. Die bereits vorhandene Parent-Listedepends_onbleibt die kanonische Liste aller Kinder und wird nicht gleichzeitig als Kind-zu-Kind-Abhängigkeitsgraph überladen.- Ausführungsnummern stellen Wellen dar: Mehrere unabhängige Kinder dürfen
dieselbe Welle
W1besitzen und parallel laufen. Eine spätere Welle darf erst starten, wenn ihre explizit genannten Voraussetzungen erfolgreich abgeschlossen sind. - Der Operator muss konkurrierende Schreibumfänge erkennen und betroffene
Kinder entweder über
blocked_byserialisieren oderneeds_humanwählen. Die Angabe von Zielpfaden ist eine Planungs- und Scheduling-Hilfe, keine vollständige technische Garantie semantischer Unabhängigkeit. - Rekursives automatisches Zerlegen von Operator-Kindern ist in der ersten Version nicht zulässig.
Deterministischer Scheduler und Kapazität
- Eine deterministische Scheduling-Schicht entscheidet, wann ein vom Operator geplanter Builder-Job starten darf. Das LLM entscheidet nicht über Locks, aktive Slots oder Queue-Kapazität.
- Der Scheduler berücksichtigt nicht nur die Board-Agent-Inbox, sondern auch nachgelagerte Builder-Inboxen, Processing-Verzeichnisse und aktive Jobs, damit bereits übergebene Arbeit weiterhin gegen das Limit zählt.
- Der Scheduler startet ausschließlich Kinder, deren
blocked_by- Abhängigkeiten erfolgreich abgeschlossen sind. - Kinder mit überlappendem deklariertem Schreibumfang dürfen nicht gleichzeitig gestartet werden.
- Eine konfigurierbare globale Obergrenze begrenzt alle gleichzeitig
laufenden Operator-veranlassten Builder-Jobs. Der Anfangswert ist
2. - Lane-spezifische Limits dürfen zusätzlich gelten, aber niemals die globale Obergrenze umgehen.
- Derselbe Intent darf weder durch konkurrierende Scheduler-Läufe noch nach einem Service-Neustart doppelt gequeued werden.
- Nach Builder-Abschluss, Fehlerbehebung oder Service-Neustart bewertet der Scheduler wartende Parents erneut und startet neu freigewordene Kinder.
- Kinder starten nach einer vollständig validierten
split-Entscheidung automatisch. Es gibt keine Einzelbestätigung pro Kind.
Modellprofile und Eskalation
- Die Zerlegung selbst läuft mit einem für Planungsqualität geeigneten Profil, weil Fehler an dieser Stelle alle nachgelagerten Jobs betreffen.
- Kleine, klar begrenzte Kinder dürfen mit günstigeren, vom Operator empfohlenen Builder-Profilen starten.
- Modellwahl ist eine Empfehlung innerhalb einer serverseitig erlaubten Konfiguration; frei erfundene Runtime-, Modell- oder Reasoning-Werte aus der Modellausgabe werden abgelehnt.
- Bereits vorhandene, begrenzte Retry- oder Korrekturmechanismen dürfen für ein Kind genutzt werden. Dieser Intent führt keinen unbegrenzten Retry- oder automatischen Rekursionsloop ein.
- Ein günstigeres Profil gilt erst dann als erfolgreich, wenn sein Ergebnis den normalen Reviewpfad bestanden hat. Die bloße Fertigmeldung des Builder-Prozesses reicht nicht aus.
Synthese, Fehler und Parent-Abschluss
- Ausschließlich erfolgreich reviewte Builder-Kinder zählen als erfolgreich abgeschlossen.
- Ein fehlgeschlagenes, am Limit beendetes oder negativ reviewtes Kind blockiert die Synthese. Das heutige Fan-out-Verhalten, auch nach terminalen Fehlschlägen sofort eine Synthese zu erzeugen, wird für Operator-Parents bewusst nicht verwendet.
- Bei einem solchen Kind wechselt der Parent nach
durch_kinder_blockiertund zeigt die blockierenden Kinder sowie deren Fehler- oder Feedbackstatus an. - Der Parent bleibt blockiert, bis jedes fehlgeschlagene Kind über die vorhandenen Recovery-/Retry-Wege erfolgreich abgeschlossen wurde. Danach wird die Orchestrierung automatisch fortgesetzt.
- Erst wenn alle Kinder erfolgreich sind, erzeugt das Board genau einen Synthese-Intent. Die vorhandene idempotente Parent-/Child-Fan-in-Logik soll dafür wiederverwendet und um die strengere Erfolgsbedingung ergänzt werden.
- Der Synthese-Intent wird ohne zusätzliche Freigabe automatisch gestartet und durchläuft Builder und Review.
- Die Synthese bewertet und vervollständigt das kombinierte Ergebnis gegen Ziel und Akzeptanzkriterien des ursprünglichen Parent-Intents. Sie darf nicht lediglich Kindberichte zusammenfassen.
- Der Parent gilt erst nach einem erfolgreichen Review des Synthese-Ergebnisses als abgeschlossen. Ein fehlgeschlagenes oder abgelehntes Synthese-Ergebnis führt in einen sichtbaren Blockierungs- beziehungsweise Feedbackzustand und niemals stillschweigend zum Abschluss.
Board-Darstellung
- Parent und Kinder erhalten eine deterministisch aus der Parent-ID abgeleitete, gemeinsame und ausreichend kontrastreiche visuelle Gruppierung.
- Farbe kennzeichnet nur die Familie. Agententyp, Builder-Lane, Ausführungswelle und Status werden als getrennte Text-Badges dargestellt und dürfen nicht ausschließlich über Farbe vermittelt werden.
- Parent-Karten zeigen Fortschritt, beispielsweise
2/5 Kinder erfolgreich, aktuell laufende Wellen und blockierende Kinder. - Kind-Karten zeigen Parent, Welle,
blocked_by, empfohlenes Profil und tatsächlichen Laufstatus. - Die Darstellung bleibt auch für bestehende Einzelkarten ohne Parent-Beziehung kompatibel.
Explizit ausgeschlossen
- Keine Research-Kinder und keine gemischten Research-/Builder-Graphen in der ersten Version.
- Kein menschliches Approval zwischen Operator-Analyse, Kind-Erzeugung und automatischem Dispatch.
- Kein neuer Security- oder Risikoklassifikations-Gate; bestehende Sandbox-, Builder-, Review-, Deployment- und Systemgrenzen bleiben unverändert bestehen.
- Kein rekursives automatisches Splitten von Kindern.
- Keine unbegrenzten automatischen Retries oder Korrekturschleifen.
- Keine dynamische CPU-/RAM-Messung oder lastabhängige automatische Veränderung der Parallelitätsgrenze.
- Keine neue Merge-Engine für semantisch überlappende Änderungen.
- Kein automatisches Training oder selbstständiges Umschreiben von Rollen und Modellprofilen aufgrund einzelner Ergebnisse.
Annahmen
- Die in Arbeit befindlichen Builder-Lanes und isolierten Worktrees werden
stabilisiert und verifiziert, bevor die produktive Parallelitätsgrenze
über
1erhöht wird. - Builder-Kinder bleiben normale
docs/intent/*.md-Artefakte und durchlaufen dieselben Builder-/Review-Grundregeln wie manuell angelegte Intents. - Orchestrierungsmetadaten und der strukturierte Operator-Bericht werden im Board-State gespeichert; die Intent-Dokumente bleiben die Quelle der Wahrheit für den fachlichen Auftragsinhalt.
- Der konfigurierte Anfangswert von zwei parallelen Jobs ist eine vorsichtige Startannahme und muss anhand realer VPS-Beobachtung bestätigt werden.
- Kleinere Intents ermöglichen den Einsatz günstigerer Modelle, garantieren aber für sich allein weder geringere Gesamtkosten noch dieselbe Qualität wie ein einzelner großer Lauf.
Offene Risiken
- Eine fachlich schlechte Zerlegung kann versteckte Kopplungen erzeugen, obwohl deklarierte Zielpfade nicht überlappen.
- Automatisch bis zu zehn Kinder zu erzeugen und zu starten kann bei mittelgroßen Intents teurer sein als ein einzelner stärkerer Builder- Lauf.
- Im privaten Board-State gespeicherte Operator-Entscheidungen besitzen eine schwächere Git-Historie als versionierte Intent-Dokumente.
- Ein nicht erfolgreich repariertes Kind kann seinen Parent dauerhaft blockieren; dieser Zustand muss im Board deutlich sichtbar bleiben.
- Zwei parallele Nix-Evaluierungen oder Builds können den VPS bereits überlasten. Die Softwaregrenze ersetzt keinen realen Belastungstest.
- Modell- und Lane-Empfehlungen sind heuristisch. Ohne einen kleinen repräsentativen Eval-Korpus kann nicht belegt werden, dass die günstigeren Pipelines dieselbe Ergebnisqualität wie ein großer Modelllauf erreichen.
- Automatischer Start ohne Freigabe erhöht den möglichen Ressourcenverbrauch und die Wirkung einer Fehlentscheidung des Operators. Diese bewusste Entscheidung muss in UI und Dokumentation sichtbar sein, auch wenn ein zusätzlicher Security-Gate erst später ergänzt wird.
Erfolgskriterien
- Das Verschieben eines geplanten Intents in die Operator-Spalte startet den Operator unmittelbar und ohne weitere Bestätigung.
- Der Operator ist eine neue, eigenständige read-only Rolle namens
operator. - Eine vollständig validierte
direct_builder-Entscheidung queued den ursprünglichen Intent automatisch mit einem erlaubten empfohlenen Builder-Profil. - Eine vollständig validierte
split-Entscheidung erzeugt atomar höchstens zehn vollständige, miteinander verknüpfte Builder-Karten und startet alle aktuell ausführbaren Kinder automatisch. - Ungültige oder unvollständige Operator-Ausgaben erzeugen weder Teilmengen von Kind-Dateien noch Builder-Jobs.
- Standardmäßig laufen höchstens zwei Operator-veranlasste Builder-Jobs gleichzeitig; nachgelagerte Queues und aktive Jobs werden korrekt mitgezählt.
- Abhängigkeiten, überlappende deklarierte Schreibumfänge und Ausführungswellen werden vor jedem Start berücksichtigt.
- Parent-/Child-Familie, Welle, Lane, Fortschritt und Blockierungsgrund sind auf dem Board sichtbar und nicht allein farblich codiert.
- Ein Service-Neustart verliert keine wartende Arbeit und erzeugt keine doppelten Builder-Jobs.
- Jeder fehlgeschlagene oder negativ reviewte Kind-Lauf verhindert die Synthese zuverlässig.
- Wird das letzte blockierende Kind später erfolgreich abgeschlossen, setzt der Scheduler die Orchestrierung ohne erneute Freigabe fort.
- Die Synthese wird genau einmal und erst nach erfolgreichem Review aller Kinder erzeugt und automatisch gestartet.
- Der Parent wird erst nach erfolgreichem Review des Synthese-Ergebnisses abgeschlossen.
- Bestehende Intents ohne Operator- oder Parent-/Child-Metadaten funktionieren unverändert weiter.