# Intent: agent-codex-State (Sessions, Freigaben) von Dateien auf SQLite umstellen **Status:** Phase 1 umgesetzt, verifiziert (53 Tests, Smoke-Test gegen den echten `AGENT_DB_PATH`) und auf blunix deployed (2026-09-05, Commit fdf3ea9). Phase 2 (Freigabe-Workflows) weiterhin offen, eigener Auftrag. ## Ziel Auslöser: ein per Tailscale zugesandtes PDF ("Bash versus SQL im Agentic Workflow") mit der Kernthese, dass strukturierter Agenten-Zustand (Memory, Freigaben, Relationships) deklarativ in SQL gehört, während Bash/Shell die Ebene für offene Computerarbeit (Filesystem, Git, Prozesse) bleibt. Eine Repo-Recherche hat `hosts/blunix/localServices/agent-codex/` als konkreten, akuten Kandidaten identifiziert: der XMPP-Codex-Agent (`agent@`/`builder@bot.0ff.space`) verwaltet Sessions, Erstkontakt-Marker und beide Freigabe-Workflows (`/annehmen`+`/ablehnen`, `/freigeben`+`/verwerfen`) komplett als JSON-Dateien in Verzeichnissen — das im PDF beschriebene Muster ohne Schema, Transaktionen oder Query-Planner. Das README dokumentiert bereits einen daraus resultierenden Vorfall (30.07.2026): ein `nixos-rebuild switch` killte per SIGTERM einen laufenden Builder-Job, der Datensatz blieb verwaist in `builder-processing/` liegen, bis nachträglich ein SIGTERM-Handler ergänzt wurde. Ziel dieses Auftrags: die Freigabe-/Sitzungs-Datensätze in eine lokale SQLite-Datenbank überführen, damit solche Zustände strukturell konsistent bleiben und per SQL statt `ls`/`cat` einsehbar sind — ohne die Job-Queue- und Ausführungsebene (pfadgetriggerte systemd-Services, Codex-Aufrufe, Git-Operationen) anzutasten, die laut derselben PDF-Logik zu Recht imperativ/dateibasiert bleibt. ## Umfang Zwei Phasen, jede mit eigenem Abschluss und eigenem Deploy: **Phase 1 — Sessions und Erstkontakt-Marker:** - Neues gemeinsames Modul `hosts/blunix/localServices/agent-codex/db.py` (Python-Stdlib `sqlite3`, keine neue Abhängigkeit) mit Tabellen `sessions` und `introductions` (Spalten: `role`, `sender`, `session_id`/`introduced_at`, `updated_at`; Primärschlüssel `(role, sender)`). - `worker.py` und `builder.py`: `session_path()`/`sender_marker()` und die zugehörigen Lese-/Schreibfunktionen (`load_session`, `save_session`, `reset_session`, `add_first_reply_notice`, `mark_introduced`) auf `db.py` umstellen. - `agent-codex.nix`: neue `AGENT_DB_PATH`-Umgebungsvariable (`${stateDir}/agent.db`) in `commonEnvironment`. - `test_agent_codex.py`: betroffene Tests auf eine temporäre SQLite-Datei pro Testfall umstellen statt Tempdir-Dateien. **Phase 2 — Freigabe-Workflows (`builder-start-pending/`, `builder-pending/`):** - Tabellen `start_pending` (job_id, sender, requested_by, body, created_at) und `pending_approval`/`pending_approval_repo` (job_id, requested_by, commit_message, created_at bzw. job_id, repo_dir) in `db.py`. - `builder.py`: `save_start_pending`/`load_start_pending`/ `delete_start_pending` sowie `save_pending_approval`/ `load_pending_approval`/`delete_pending_approval` auf `db.py` umstellen. `find_stash_ref()` (Suche nach dem Marker-String in `git stash list`) bleibt unverändert — der Datensatz selbst wird nur abfragbar statt nur als Datei lesbar. Beide Phasen jeweils mit dem üblichen blunix-Verify-Pfad (`nix flake check` → `drvPath`-Eval → Build → `diff-closures` → `dry-activate` → `switch`) und dem bestehenden manuellen Vorsichtsschritt aus dem README (`systemctl is-active agent-codex-builder.service` vor dem Switch prüfen). ## Explizit ausgeschlossen - Die Job-Queue selbst (`inbox/`, `processing/`, `failed/` und die `builder-*`-Pendants) bleibt dateibasiert — sie ist die pfadgetriggerte (`systemd.paths`, `PathExistsGlob`) Ausführungsebene, keine abzufragende Business-Struktur; eine Umstellung würde laut PDF ("SQL -> Queue -> Shell") nur eine schlechtere Shell hinter SQL verstecken. - Kein automatisiertes Migrationsskript für bestehende Datei-Datensätze. Sessions/Introductions sind verlustlos ersetzbar (nächste Nachricht startet nötigenfalls neu); offene `start_pending`/`pending_approval`-Vorgänge werden vor dem jeweiligen Deploy im Chat manuell aufgelöst (`/annehmen`, `/ablehnen`, `/freigeben`, `/verwerfen`). - Kein `job_events`-Audit-Log (PDF-Muster "Event Sourcing") — separat vormerken, nicht Teil dieses Auftrags. - Keine Änderung an `find_stash_ref()`s String-Matching-Mechanik selbst (robusterer Stash-Bezug über Commit-Hash statt Marker-Text) — eigener, späterer Auftrag, falls gewünscht. - Kein Eingriff in `hosts/pinix/localServices/webhook.nix` oder Vikunja/GoToSocial — laut Recherche bereits korrekt (Bash-only bzw. bereits SQL-gestützt). ## Annahmen - Beide Rollen (`agent-codex-worker`, `agent-codex-builder`) laufen als `Type = "oneshot"`-Services, die von ihrem jeweiligen `systemd.paths` serialisiert getriggert werden -- pro Rolle läuft nie mehr als ein Prozess gleichzeitig gegen `agent.db`, SQLites Single-Writer-Grenze ist damit unkritisch. - Eine gemeinsame Datei `agent.db` für beide Rollen (mit `role`-Spalte) ist einfacher zu betreiben als zwei getrennte Dateien, ohne die bestehende Rollentrennung (getrennte Queues, getrennte Sender-Allowlists) aufzuweichen -- die Trennung bleibt rein logisch über die `role`-Spalte bestehen. ## Offene Risiken - Falls zum Deploy-Zeitpunkt tatsächlich ein `start_pending`- oder `pending_approval`-Vorgang offen ist und nicht rechtzeitig manuell aufgelöst wird, geht dieser Datensatz beim Umschalten verloren (die zugehörigen `git stash`-Einträge selbst bleiben aber erhalten und sind über `git stash list` von Hand wiederfindbar). - Unklar, ob `sqlite3` unter dem bestehenden `systemd`-Sandboxing (`ProtectSystem = "strict"`, `ReadWritePaths`) ohne weitere Anpassung in `${stateDir}` schreiben kann -- vermutlich ja, da `StateDirectory` bereits schreibbar ist, aber nicht verifiziert. ## Erfolgskriterien - Phase 1: `nix flake check` und Build von blunix erfolgreich; `test_agent_codex.py` grün; nach Deploy zeigt eine neue Chat-Nachricht weiterhin korrektes Sitzungsverhalten (Fortsetzung/`/neu`) und die Erstkontakt-Notiz erscheint nur beim ersten Kontakt -- beides jetzt aus `agent.db` gelesen, nicht mehr aus `sessions/`/`senders/`. - Phase 2: ein kompletter `/annehmen`- und ein kompletter `/freigeben`-Durchlauf funktionieren unverändert aus Nutzersicht; `sqlite3 agent.db 'SELECT * FROM pending_approval'` zeigt einen offenen Vorgang korrekt an, solange er nicht abgeschlossen ist. - Alte Zustandsverzeichnisse (`sessions/`, `builder-sessions/`, `senders/`, `builder-senders/`, `builder-start-pending/`, `builder-pending/`) sind nach beiden Phasen in einem Folgecommit entfernt.