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

7.5 KiB

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.