Files
mission-control-v2/deploy/governor/README.md
T
Hitonabi c13cfd2bd0 Governor Phase 0: Token-Waechter-Proxy + Aider Session-Hygiene (bewiesen)
Duenner, zustandsloser Proxy (nur stdlib) zwischen Aider und llama-swap :8080.
Schiebt an einer Token-Schwelle "SAVEPOINT.md finalisieren + stoppen" ein (weich)
bzw. antwortet oberhalb eines optionalen Hart-Deckels selbst. Beweist Session-
Hygiene per hartem Schnitt statt Auto-Compaction -- ohne eine Zeile Lucy-Code.

Alle 4 Akzeptanzkriterien gruen: feuert im Log; SAVEPOINT.md gepflegt; ehrlicher
Grenz-Savepoint am ersten Feuern; frische Sitzung liest Savepoint -> baut Tests,
9 unittest gruen, keine Fassade. CPT 3.5 kalibriert (est ~= echte prompt_tokens).
Hart-Deckel nachgeruestet (weicher Schnitt allein erzeugt Fassade bei Weiterarbeit
ueber die Grenze). Streaming-read1-Fix + 4 kleinere aus adversarialer Review.

Laeuft deployt auf der Box unter ~/governor-p0/ (gov-ctl.sh start <soft> <hart>).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 19:17:36 +02:00

7.8 KiB
Raw Blame History

Governor — Phase 0

Dünner, zustandsloser Token-Wächter-Proxy zwischen einem Off-the-shelf-Coding-Agenten (Aider) und dem lokalen Modell-Endpoint (llama-swap :8080). Er erzwingt Session-Hygiene per hartem Schnitt statt Auto-Compaction: wenn die Anfrage (= ganze Sitzungshistorie, die jede Runde mitkommt) eine Schwelle übersteigt, schiebt er eine Anweisung ein, SAVEPOINT.md zu finalisieren und zu stoppen — damit Wissen in SAVEPOINT.md + git lebt, nicht im degradierenden Chat-Kontext.

Das ist Phase 0 des Ablöse-Plans „Lucy IDE-Modus + Governor": den Kern beweisen, ohne eine Zeile Lucy-Code. Kein bespoke Editor, kein Aider-Fork, kein Modelltausch.

Bausteine (dieses Verzeichnis)

Datei Zweck
governor.py Der Proxy. Nur Standardbibliothek (läuft mit System-python3), zustandslos.
CONVENTIONS.md Aiders schreibgeschützte Arbeitsregeln: SAVEPOINT.md zuerst lesen, laufend pflegen, keine Fassade.
SAVEPOINT.template.md Anfangs-Savepoint für ein frisches Projekt.
driver.py Treibt eine akkumulierende Aider-Sitzung über die Scripting-API (eine Zeile = eine Runde).
gov-ctl.sh Governor sauber starten/stoppen/status (detached via setsid).
run-driver.sh run-driver.sh <msgs> [repo] — Aider-Sitzung durch den Governor.
reset-repo.sh Wegwerf-Test-Repo frisch aufsetzen.
hardstop-test.sh Direkte curls für die drei Pfade (passthrough / soft / hart).
msgs-*.txt Nachrichtenskripte für die Testläufe.

Verhalten des Governors

Pro /v1/chat/completions-Anfrage schätzt er die Tokenzahl (Zeichen / GOV_CHARS_PER_TOKEN, kalibriert auf CPT 3.5est ≈ echte prompt_tokens auf ~13 % bei echten Sessions):

  • est < Soft → unverändert durchreichen.
  • est ≥ Soft (GOV_THRESHOLD, Default 25000) → hängt die SAVEPOINT-Stopp-Anweisung als letzte User-Nachricht an, leitet weiter, loggt FIRED. Das Modell schreibt EINEN ehrlichen Abschluss-Savepoint.
  • est ≥ Hart (GOV_HARD_CEILING, Default 0 = aus) → der Governor antwortet SELBST mit einer kurzen Stopp-Nachricht, ohne das Modell zu fragen; loggt HARDSTOP. Verhindert, dass über die Grenze hinaus weitergearbeitet wird (siehe Befund unten).

Alle anderen Pfade (/v1/models etc.) werden roh durchgereicht. Streaming (SSE) wird byteweise durchgereicht; die echten prompt_tokens aus der Antwort werden zur Kalibrierung mitgeloggt.

Umgebungsvariablen

GOV_PORT (8100) · GOV_HOST (0.0.0.0) · GOV_UPSTREAM (http://127.0.0.1:8080) · GOV_THRESHOLD (25000) · GOV_HARD_CEILING (0=aus) · GOV_CHARS_PER_TOKEN (3.5) · GOV_LOG · GOV_DIRECTIVE · GOV_HARDSTOP_MSG.

Auf der Box laufen lassen (wie in P0 aufgesetzt)

# Aider (einmalig, unter isoliertem Python 3.12 — System-Python 3.14 bricht Aiders Pins):
pipx install uv && uv tool install --python 3.12 aider-chat

# Dateien liegen in ~/governor-p0/. Governor starten (Soft 25k, Hart aus):
~/governor-p0/gov-ctl.sh start 25000 0        # oder: start 25000 30000  (Hart an)

# Aider-Sitzung durch den Governor:
~/governor-p0/run-driver.sh ~/governor-p0/msgs-todo.txt

Aider zeigt mit OPENAI_API_BASE=http://127.0.0.1:8100/v1 und Modell openai/Qwen3-Coder-Next auf den Governor.

Wichtig: Aiders eigene Zusammenfassung MUSS aus

Der Treiber setzt coder.summarizer.max_tokens auf ~1e9. Sonst fasst Aider die Historie selbst zusammen (Auto-Compaction) und die Anfrage wächst nie bis zur Schwelle — der Governor wäre ausgehebelt, und man bekäme genau die über-komprimierte Halluzination, die der Plan verwirft. Der Governor soll die alleinige Sitzungsgrenze sein.

Ergebnisse & Befunde (24.07.2026)

Akzeptanz — alle vier Kriterien bewiesen (Box, Qwen3-Coder-Next)

  1. Governor zählt + feuert an der Schwelle — 12-Runden-Todo-Lauf (Soft 8000): Runden 1-6 ok, ab Runde 7 FIRED (est 8546 / echt 8652). Hart-Deckel: Anfrage mit est 11429 ≥ 10000 → HARDSTOP, Governor antwortet selbst (kein Modell-Call). Alles im Log.
  2. Aider pflegt SAVEPOINT.md — über den ganzen Aufbau hinweg strukturiert gehalten (Ziel/Erledigt/Nächster Schritt/Stolpersteine/Dateien) gemäß CONVENTIONS.md.
  3. An der Grenze: ehrlicher Abschluss + Stopp — beim ersten Feuern (Runde 7) schrieb das Modell einen ehrlichen Savepoint (nur real Gebautes unter „Erledigt", Tests als nächster Schritt) und verweigerte neuen Code.
  4. Frische Sitzung macht sauber weiter — keine Fassade — neue Aider-Sitzung las den Grenz-Savepoint, baute test_todo.py (der exakte nächste Schritt), und alle 9 unittest- Tests laufen grün gegen die echte API. Kein Erfinden.

Kalibrierung

CHARS_PER_TOKEN = 3.5est traf die echten prompt_tokens bei realen Sessions auf ~1-3 %. (Nur künstlicher, extrem repetitiver Fülltext bricht die Heuristik — irrelevant für echten Code.) Der Coder läuft mit 128k Kontext (-c 131072), also keine Modell-Kappung bei 25k.

Wichtigster Befund: Soft reicht nicht allein → Hart-Deckel nachgerüstet

Der weiche Schnitt erzeugt genau EINEN ehrlichen Grenz-Savepoint — solange die Grenze respektiert wird. Schickt man aber über die Grenze hinaus weiter Aufträge (wie im Stresstest), verweigert das Modell zwar den Code, schreibt die Absichten aber fortschreitend als „erledigt" in SAVEPOINT — genährt von Aiders irreführenden Commit-Nachrichten (die aus dem SAVEPOINT- Absichtstext geschöpft werden). Ergebnis: eine Fassade (behauptete test_todo.py/README, die es nicht gab). Deshalb der optionale Hart-Deckel (GOV_HARD_CEILING): oberhalb davon antwortet der Governor selbst, das Modell kann keine degradierenden Savepoints mehr schreiben. Empfehlung: Hart-Deckel aktiv (z. B. Soft 25000, Hart 30000 — ein Finalisier-Zug Luft, dann harter Riegel).

Weitere Befunde / Fallen

  • Aiders eigene Zusammenfassung MUSS aus (summarizer.max_tokens hoch) — sonst compactet Aider selbst und der Governor greift nie. Siehe oben.
  • Commit-Nachrichten überzeichnen in der Abschluss-Phase (aus SAVEPOINT-Absicht). Der Code ist die Wahrheit; git-Nachrichten sind es hier nicht. → Für Phase 1: Auto-Commit nahe/nach dem Feuern zügeln oder Savepoint mit ehrlicher fixer Nachricht committen.
  • Python 3.14 auf der Box bricht Aiders Pins (numpy 1.24.3) → Aider via uv unter isoliertem Python 3.12 installiert.
  • Aider-Scripting-API (coder.run) hängt in einer Datei-Hinzufügen-Reflexion (Edits landeten nicht). Für Einzel-Runden aider --message nutzen (sauberer, unterstützt). Der driver.py taugt für Mehr-Runden-Akkumulation (Governor-Test), nicht als Produktions-Treiber.

Bekannte Grenzen des Governors (aus adversarialer Review, für später)

  • Chunked Request-Bodies ohne Content-Length werden verworfen (Aiders httpx sendet immer Content-Length → schlummernd, aber ein Proxy-Hop mit Chunking bräche).
  • Tool-/Function-Calling: der Soft-Einschub als letzte user-Nachricht kann die Nachrichten- reihenfolge stören, wenn Aider ein Tool-Calling-Edit-Format nutzt (Aiders diff/whole sind reiner Text → schlummernd). estimate_tokens zählt tools/tool_calls nicht mit.
  • https://-Upstream wird nicht unterstützt (nur http.client.HTTPConnection). Für den lokalen :8080-Endpoint irrelevant. Behoben aus derselben Review: inkrementelles Streaming (read1() statt read() — vorher puffernd), Config-Crash bei { } in GOV_DIRECTIVE (sichere Substitution), Query-String umging die Erkennung, Socket-Leak im Fehlerpfad, doppelte Date/Server-Header.

Nächste Schritte (Phase 1+)

Terminal in Lucy einbetten (xterm.js + node-pty, MC2-Muster kopieren) → Voice-Hook auf das Governor-Signal → Feinschliff + Aider/Pi-Finalentscheid. Governor evtl. später in mc2-gateway. Kandidat für Phase 1-Härtung: Auto-Commit-Zügelung + Hart-Deckel als Default.