This repository has been archived on 2026-09-17. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
nixos-config/docs/intent/board-intent-operator-orchestration.md
pijukka 1b805c05d5 board-agent: worktree-parallel builder lanes + Operator orchestration
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
2026-09-09 11:23:49 +02:00

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.