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
263 lines
13 KiB
Markdown
263 lines
13 KiB
Markdown
# 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 `operator` mit 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_angefordert`
|
|
- `operator_laeuft`
|
|
- `wartet_auf_kinder`
|
|
- `durch_kinder_blockiert`
|
|
- Das Verschieben einer Karte von `geplant` nach
|
|
`operator_angefordert` queued 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_builder` wä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 `split` erzeugt 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_human` oder 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_human`
|
|
wählen.
|
|
|
|
### Ausführungsplan und Parent-/Child-Modell
|
|
|
|
- Das vorhandene Parent-/Child-Modell aus `fanout.py` wiederverwenden und
|
|
für jedes Kind mindestens folgende Metadaten speichern:
|
|
- `parent_id`
|
|
- `blocked_by`
|
|
- `wave`
|
|
- `recommended_lane`
|
|
- empfohlenes Runtime-/Modell-/Reasoning-Profil
|
|
- erwarteter Schreibumfang beziehungsweise Zielpfade
|
|
- kindspezifische Akzeptanzkriterien
|
|
- `blocked_by` bildet echte Ausführungsabhängigkeiten ab. Die bereits
|
|
vorhandene Parent-Liste `depends_on` bleibt 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 `W1` besitzen 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_by` serialisieren oder `needs_human`
|
|
wä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_blockiert` und 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 `1` erhö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.
|