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/builder-self-deploy-without-self-termination.md
pijukka 05575df408 docs: commit backlog of already-resolved intent/review corpus files
board-agent's own README documents docs/intent/ and docs/research-intent/
as tracked corpus directories, and docs/review/<slug>.md as the reviewer's
persisted report per card - but the pipeline only writes these files to
the checkout, it never commits them itself. This had built up a backlog
of intent/review docs for cards already marked deployed or archiviert in
the live board state, which were sitting uncommitted in the working tree.

Adds that backlog as-is: the originating intents, their reviewer reports,
the research-intent/research pair for the board UX evaluation, and one
standalone process-retrospective report (board-review-pipeline-context-
overhead.md, same genre as the two already-tracked audit reports in
docs/review/). No code changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T7UVrKsttMP9a6FiGFna9b
2026-09-10 12:06:53 +02:00

163 lines
7.5 KiB
Markdown

# Intent: Builder-Deploy ohne Self-Termination und Doppelverarbeitung
## Ziel
Ein vom Board ausgelöster Builder darf beim automatischen Deployment auf
`blunix` weder seinen eigenen Prozess noch das Intent Board in einem
halbaktivierten Zustand zurücklassen. Nach Commit und Push muss das
Deployment außerhalb des Lebenszyklus von `agent-codex-builder.service`
ausgeführt werden und bei Unterbrechungen idempotent fortsetzbar sein.
## Beobachteter Produktionsfehler
Beim Lauf für
`board-keep-cards-visible-during-builder-stash` trat am 07.09.2026
folgender Ablauf auf:
1. Um 12:49:29 startete der Board-Job.
2. Der Builder erzeugte und pushte Commit `62c9ce2`.
3. Um 12:50:56 startete `deploy_blunix()` synchron
`nixos-rebuild switch` innerhalb von `agent-codex-builder.service`.
4. Während der Aktivierung stoppte systemd um 12:51:31
`board-agent-trigger.service` und anschließend den Builder-Prozess mit
`SIGTERM` beziehungsweise Status 143.
5. Der SIGTERM-Handler legte denselben Job wieder in die Queue.
6. Der zweite Modelllauf erzeugte und pushte den zusätzlichen Commit
`90ef0c8` und startete erneut ein Deployment.
7. Derselbe Self-Termination-Ablauf wiederholte sich um 12:53:27.
8. Erst ein dritter Lauf endete ohne weiteres Deployment, weil keine
zusätzlichen Änderungen mehr entstanden waren.
9. `board-agent-trigger.service` blieb `inactive (dead)`, obwohl er
deklarativ aktiviert und mit `Restart=always` konfiguriert ist.
Das war kein Python-Crash des Board-Servers. Der Prozess wurde im Rahmen
einer vom Builder selbst gestarteten, dann unterbrochenen NixOS-Aktivierung
geordnet mit `SIGTERM` beendet.
## Belegte Ursache
- `builder.py:deploy_blunix()` führt `sudo nixos-rebuild switch` synchron
als Kindprozess des Builders aus.
- `agent-codex-builder.service` ist die Unit, aus der dieser Rebuild
gestartet wird. Sobald die neue Konfiguration diese Unit während der
Aktivierung stoppt, werden Builder und dessen Rebuild-Prozess gemeinsam
beendet.
- `builder.py:_handle_sigterm()` requeued jeden aktiven Job unabhängig
davon, ob Modelllauf, Commit und Push bereits erfolgreich abgeschlossen
sind.
- `apply_commit()` führt Commit, Push und Deployment als einen nicht
persistent phasierten Ablauf aus. Nach dem Requeue ist nicht erkennbar,
welche Schritte bereits dauerhaft erfolgt sind.
- Der Retry startet deshalb erneut das Modell und kann einen zweiten
Commit für denselben Intent erzeugen.
- Das unterbrochene `nixos-rebuild switch` hatte den Board-Dienst bereits
gestoppt, erreichte dessen anschließenden Start aber nicht mehr.
## Umfang / Auftrag an den Builder
### Deployment entkoppeln
- `nixos-rebuild switch` nicht mehr synchron im Prozessbaum von
`agent-codex-builder.service` ausführen.
- Einen separaten, eng begrenzten Deployment-Runner mit eigenem
systemd-Lebenszyklus definieren. Er erhält einen persistierten Auftrag
mit Commit-Hash, Host und Flake-Pfad und führt ausschließlich den
freigegebenen `blunix`-Switch aus.
- Der Deployment-Runner muss eine Aktivierung seiner eigenen deklarativen
Umgebung überleben. Seine Unit darf während des Switch nicht als Teil
des Builder-Neustarts beendet werden.
- Builder und Board dürfen ein erfolgreich gestartetes Deployment nicht
mit einem erfolgreich abgeschlossenen Deployment verwechseln.
### Persistente Zustandsmaschine
- Den Ablauf pro stabiler Board-Job-ID atomar mindestens in folgende
Zustände aufteilen:
`building -> committed -> deploying -> deployed` beziehungsweise
`build_failed` oder `deploy_failed`.
- Commit-Hash und Push-Ergebnis unmittelbar nach ihrem Erfolg persistent
speichern, bevor ein Deployment gestartet wird.
- Bei Neustart anhand dieses Zustands fortsetzen: Ein bereits committed
Job darf weder erneut das Modell ausführen noch einen weiteren Commit
erzeugen.
- Requeue im SIGTERM-Handler nur zulassen, solange nachweislich noch kein
Commit für diesen Job gespeichert wurde. Nach `committed` darf höchstens
der separate Deployment-Schritt erneut beziehungsweise kontrolliert
fortgesetzt werden.
- Einen Idempotency-Key aus Board-Job-ID und Intent-Slug verwenden und
doppelte Queue-/Completion-Einträge sicher deduplizieren.
### Fehler- und Retry-Verhalten
- Zwischen Build-, Push- und Deployment-Fehlern unterscheiden und den
Status dem Board explizit melden.
- Ein gepushter Commit bei fehlgeschlagener oder partieller Aktivierung
darf nicht als `deployed` gelten.
- Deployment-Retries begrenzen und mit Backoff versehen. Wiederholte
Self-Termination, SIGTERM oder Aktivierungsfehler müssen in einem
sichtbaren `deploy_failed`-Zustand enden statt den Modelllauf erneut zu
starten.
- Der Completion-Watcher muss doppelte Records sowie bereits verarbeitete
Commit-Hashes ignorieren.
- Nach einem fehlgeschlagenen Deployment muss das Board den Commit-Hash,
den Deployment-Status und einen manuellen Recovery-Hinweis anzeigen.
### Betriebsstabilität
- Sicherstellen, dass `board-agent-trigger.service` nach erfolgreicher
Aktivierung läuft.
- Wird eine Aktivierung unterbrochen, muss der Deployment-Runner den
tatsächlichen Unit-Zustand prüfen und einen klaren Fehler melden. Kein
stilles Zurücklassen eines deklarativ aktivierten, aber faktisch
gestoppten Boards.
- Die Lösung darf keine allgemein nutzbare Root-Shell oder zusätzliche
Modellberechtigung schaffen. Das Modell erzeugt weiterhin nur die
Änderung; der eng definierte Wrapper führt den Deployment-Auftrag aus.
## Tests
- Unit-Test: SIGTERM vor dem Commit requeued genau einmal.
- Unit-Test: SIGTERM nach gespeichertem Commit startet das Modell nicht
erneut und erzeugt keinen zweiten Commit.
- Unit-Test: Doppelte Queue- und Completion-Einträge werden anhand der
Job-ID dedupliziert.
- Unit-Test: Ein gepushter Commit mit fehlgeschlagenem Switch endet in
`deploy_failed`, nicht in `deployed` oder einem neuen Builder-Lauf.
- Integrationstest mit Fake-Deployment-Runner: Builder übergibt den
Auftrag und kann beendet werden, ohne den Runner zu terminieren.
- Integrationstest: Der Runner meldet nach erfolgreicher Aktivierung den
Zustand des Board-Dienstes und den aktivierten Systempfad zurück.
- Bestehende Board-, Builder-, Rollen- und Nix-Evaluierungstests müssen
grün bleiben.
## Ausgeschlossen
- Kein erneuter Modelllauf als allgemeine Recovery-Strategie nach bereits
erfolgreichem Commit.
- Kein Zurücksetzen oder Umschreiben der bereits gepushten Commits
`62c9ce2` und `90ef0c8`.
- Keine Abschaffung der bestehenden Sandbox- und Rollenbegrenzungen.
- Kein automatisches Deployment auf andere Hosts als `blunix`.
- Keine imperative Aktivierung allein durch das Vorhandensein dieses
Intent-Dokuments.
## Erfolgskriterien
- Ein Board-Job erzeugt auch bei einem Dienstneustart höchstens einen
fachlichen Commit.
- Der Builder wird nicht durch seinen eigenen Deployment-Kindprozess in
eine Requeue-Schleife gebracht.
- Ein unterbrochener Switch hinterlässt keinen fälschlich erfolgreichen
Board-Status.
- Nach erfolgreichem Switch antwortet das Board wieder; nach einem Fehler
zeigt es einen eindeutigen, persistenten Recovery-Status.
- Commit-/Push- und Deployment-Ergebnisse sind getrennt nachvollziehbar.
- Der aktuelle Produktionsfehler lässt sich mit einem Test reproduzieren,
der vor der Änderung fehlschlägt und danach besteht.
## Aktueller Recovery-Hinweis
Zum Zeitpunkt der Diagnose ist `board-agent-trigger.service` auf `blunix`
seit 12:51:31 `inactive (dead)`. Eine manuelle Wiederinbetriebnahme oder
ein vollständiger erneuter `nixos-rebuild switch` ist eine separate
betriebliche Aktion und nicht durch diesen Intent freigegeben.