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

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 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.