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>
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
# 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.5** → `est ≈ echte prompt_tokens` auf ~1–3 % 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)
|
||||
```bash
|
||||
# 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.5` → `est` 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.
|
||||
Reference in New Issue
Block a user