doku: Phase 2b nachgezogen, tote Doku-Verweise im Code, Morgenmeldung verweist auf den echten Stapel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-24 17:47:16 +02:00
co-authored by Claude Opus 5.5
parent 8eb2fec2ea
commit 97ba6420e2
9 changed files with 44 additions and 20 deletions
+2 -3
View File
@@ -1,7 +1,6 @@
""" """
Hermes-Agent-Status (Control-Plane-Read). MC betreibt Hermes NICHT — es zeigt nur Hermes-Agent: Status und Hirn-Umstellung. MC betreibt Hermes NICHT — Werkzeuge und MCP werden in
Status + verlinkt das standalone hermes-webui. Voller Zugriff + Tools/MCP werden in Hermes' eigener Config verdrahtet (siehe docs/ARCHITEKTUR.md, „Wer schreibt was").
Hermes' eigener Config verdrahtet (siehe docs/HERMES_SETUP.md).
""" """
import logging import logging
+1 -1
View File
@@ -1,7 +1,7 @@
""" """
Voll-Zustands-Backup (Hermes-Configs/Secrets + llama-swap config). Voll-Zustands-Backup (Hermes-Configs/Secrets + llama-swap config).
Delegiert an deploy/backup.sh (eine Quelle der Wahrheit, identisch zum systemd-Timer); Delegiert an deploy/backup.sh (eine Quelle der Wahrheit, identisch zum systemd-Timer);
Restore läuft bewusst nur per CLI (deploy/restore.sh) — siehe docs/BACKUP.md. Restore läuft bewusst nur per CLI (deploy/restore.sh) — siehe docs/BETRIEB.md (Sicherung).
""" """
import subprocess import subprocess
+1 -1
View File
@@ -309,7 +309,7 @@ def register_model(model_path: str, role: str | None = None, ctx: int = 8192,
if "--cache-reuse" not in cmd and "--mmproj" not in cmd: if "--cache-reuse" not in cmd and "--mmproj" not in cmd:
cmd += " --cache-reuse 256 -cram 16384" cmd += " --cache-reuse 256 -cram 16384"
# IDE-Coding profitiert von Nebenläufigkeit; sonst Default 1 Slot = voller Kontext/Anfrage # IDE-Coding profitiert von Nebenläufigkeit; sonst Default 1 Slot = voller Kontext/Anfrage
# (--parallel teilt den Kontext HART auf die Slots auf, s. docs/OPTIMIZATION_PLAN.md §9.4 V6). # (--parallel teilt den Kontext HART auf die Slots auf, s. docs/archiv/2026-06-30-optimierungsplan.md §9.4 V6).
if role_lower == "coder" and "--parallel" not in cmd: if role_lower == "coder" and "--parallel" not in cmd:
cmd += " --parallel 2" cmd += " --parallel 2"
# Vocab-kompatiblen Draft automatisch anhängen — klassisch (DRAFTS_DIR) ODER MTP-Kopf neben # Vocab-kompatiblen Draft automatisch anhängen — klassisch (DRAFTS_DIR) ODER MTP-Kopf neben
+1 -1
View File
@@ -1,6 +1,6 @@
[Unit] [Unit]
Description=MC2 Zustands-Backup (Hermes-Configs/Secrets + llama-swap config) Description=MC2 Zustands-Backup (Hermes-Configs/Secrets + llama-swap config)
Documentation=file:%h/mission-control-v2/docs/BACKUP.md Documentation=file:%h/mission-control-v2/docs/BETRIEB.md
[Service] [Service]
Type=oneshot Type=oneshot
+1 -1
View File
@@ -35,7 +35,7 @@ ANZAHL="$(grep -c '^- ' "$ARBEIT" || true)"
TEXT="$(head -c "$MAX_ZEICHEN" "$ARBEIT")" TEXT="$(head -c "$MAX_ZEICHEN" "$ARBEIT")"
if [ "$(wc -c < "$ARBEIT")" -gt "$MAX_ZEICHEN" ]; then if [ "$(wc -c < "$ARBEIT")" -gt "$MAX_ZEICHEN" ]; then
TEXT="$TEXT TEXT="$TEXT
… gekürzt. Alle Meldungen stehen im Cockpit unter Meldungen." … gekürzt. Den vollen Stapel hat die Box unter ~/.hermes/night-queue.txt.zuletzt-gesendet."
fi fi
NACHRICHT="Guten Morgen, Commander. Heute Nacht gab es $ANZAHL $WORT: NACHRICHT="Guten Morgen, Commander. Heute Nacht gab es $ANZAHL $WORT:
$TEXT" $TEXT"
+20 -1
View File
@@ -50,7 +50,7 @@ Client an MC2 vorbei direkt auf `:8080`. Nur den Radar-Kandidaten startet der Ra
| Prozess (Unit) | Einstieg | Aufgabe | Eigener Zustand | | Prozess (Unit) | Einstieg | Aufgabe | Eigener Zustand |
|---|---|---|---| |---|---|---|---|
| `mission-control-2` (`:9001`) | `backend/app.py` | Oberfläche aus `frontend/dist`; 62 `/api`-Routen; reicht `/v1` roh an den Gateway weiter (`MC_V1_UPSTREAM`); reicht das Hermes-Dashboard unter `/hermes-ui/` durch (HTTP und WebSocket); Erinnerungen; Update- und Download-Jobs als Kindprozesse (`services/jobengine.py`) | Briefkasten, Erinnerungen, Routing-Policy, Hugging-Face-Zugang, ausgeblendete Hinweise | | `mission-control-2` (`:9001`) | `backend/app.py` | Oberfläche aus `frontend/dist`; 62 `/api`-Routen; reicht `/v1` roh an den Gateway weiter (`MC_V1_UPSTREAM`); reicht das Hermes-Dashboard unter `/hermes-ui/` durch (HTTP und WebSocket); Erinnerungen; startet Update- und Download-Aufträge als eigene systemd-Einheiten `mc2-job-<id>` (`services/jobengine.py`) | Briefkasten, Erinnerungen, Routing-Policy, Hugging-Face-Zugang, ausgeblendete Hinweise |
| `mc2-gateway` (`127.0.0.1:9010`) | `backend/gateway_app.py` | `/v1` mit `model: auto` und Bild-Weiche, Kontext-Warnung, `/gw/health` | Token-Zähler (`~/.hermes/token_stats.json`) | | `mc2-gateway` (`127.0.0.1:9010`) | `backend/gateway_app.py` | `/v1` mit `model: auto` und Bild-Weiche, Kontext-Warnung, `/gw/health` | Token-Zähler (`~/.hermes/token_stats.json`) |
| `mc2-steward` | `backend/steward.py` | Re-Warm (alle 90 s, lädt das Hirn nach, wenn nichts geladen ist), Config-Watch (5 s), Wächter (jede Minute) | `mc2-waechter.json` (einziger Schreiber) | | `mc2-steward` | `backend/steward.py` | Re-Warm (alle 90 s, lädt das Hirn nach, wenn nichts geladen ist), Config-Watch (5 s), Wächter (jede Minute) | `mc2-waechter.json` (einziger Schreiber) |
| `mc2-radar` (Timer 00:30) | `backend/radar_lauf.py` | Suche und Nachttest neuer Modelle | `mc2-radar.json`, Baseline, `/srv/models/radar/` | | `mc2-radar` (Timer 00:30) | `backend/radar_lauf.py` | Suche und Nachttest neuer Modelle | `mc2-radar.json`, Baseline, `/srv/models/radar/` |
@@ -90,6 +90,24 @@ Fremde Dienste, die der Box-Wart nur steuert oder überwacht: `llama-swap` (Syst
- Die Units setzen weder `MC_ROLLE` noch `MC_PARTNER_URL`: Die Box läuft in der Rolle `box` und ohne Partner, bis die - Die Units setzen weder `MC_ROLLE` noch `MC_PARTNER_URL`: Die Box läuft in der Rolle `box` und ohne Partner, bis die
zweite Instanz eingerichtet ist. zweite Instanz eingerichtet ist.
## Aufträge (seit 24.09., Phase 2b)
- **Eigene Einheiten:** `services/jobengine.py` startet jeden Auftrag (Update, Modell-Download) per `systemd-run`
als Einheit `mc2-job-<id>` des Nutzer-Managers (als root: des System-Managers). `RuntimeMaxSec` ist das
Zeitlimit, „Abbrechen" stoppt die Einheit samt Kindern. Ein Neustart von MC2 (Deploy, Absturz) würgt den
Auftrag nicht mehr ab.
- **Akten:** `<Datenordner>/mc2-jobs/<id>.json` (Zustand), `.log` (Ausgabe), `.exit` (Exit-Code, atomar von einer
Bash-Hülle geschrieben). MC2 liest die Akten beim Start (`wiederaufnehmen()` im Lebenszyklus der App) und
beobachtet laufende Aufträge weiter; endet eine Einheit ohne Exit-Code, gilt der Auftrag als gescheitert bzw.
abgebrochen oder als Zeitlimit. Beendete Aufträge verschwinden nach einem Tag bzw. ab 40 Stück.
- **Geheimnisse** (z. B. `HF_TOKEN`) gehen über eine nur für den Nutzer lesbare Umgebungsdatei `<id>.env`, die der
Auftrag beim Start liest und löscht — nicht über Befehlszeile oder Einheit (auf der Box nachgeprüft).
- **Nacharbeiten** sind benannt (`@jobengine.nacharbeit`): `wartung:nach_update` (Zwischenspeicher leeren) und
`modell:rolle` (Rolle nach dem Download setzen). Sie stehen mit ihren Daten in der Akte, laufen also auch nach
einem Neustart, und zwar vor dem Endzustand: Wer „done" sieht, sieht auch ihre Wirkung.
- **Ohne systemd** (PC, Tests, `MC_JOBS_ART=prozess`) läuft der Auftrag als Kindprozess und überlebt keinen
Neustart. Ein Probelauf daneben (`MC_PROBELAUF=1`) nimmt keine Aufträge auf.
## Modelle und Modell-Rollen ## Modelle und Modell-Rollen
- **Rollen-Aliase statt Namen:** Clients fragen `hermes`/`fast` (Hirn), `coder`/`heavy` (Coder), `vision` und - **Rollen-Aliase statt Namen:** Clients fragen `hermes`/`fast` (Hirn), `coder`/`heavy` (Coder), `vision` und
@@ -117,6 +135,7 @@ Fremde Dienste, die der Box-Wart nur steuert oder überwacht: `llama-swap` (Syst
| `/srv/models/mc2-waechter.json` | nur der Steward | MC2 liest und führt Knöpfe aus; der nächste Takt sieht das Ergebnis. Der Ordner folgt `MC_DATEN_DIR`. | | `/srv/models/mc2-waechter.json` | nur der Steward | MC2 liest und führt Knöpfe aus; der nächste Takt sieht das Ergebnis. Der Ordner folgt `MC_DATEN_DIR`. |
| `/srv/models/mc2-quittiert.json` | nur MC2 (Knopf „Ausblenden bis zum nächsten Lauf") | Der Steward liest; ein ausgeblendeter Werkzeugfehler-Hinweis kommt wieder, wenn der nächste Lauf des Jobs erneut Fehler hat. | | `/srv/models/mc2-quittiert.json` | nur MC2 (Knopf „Ausblenden bis zum nächsten Lauf") | Der Steward liest; ein ausgeblendeter Werkzeugfehler-Hinweis kommt wieder, wenn der nächste Lauf des Jobs erneut Fehler hat. |
| `/srv/models/mc2-radar.json` (+ `mc2-radar-baseline.json`) | Radar-Lauf und MC2 | jede Änderung unter `flock`, atomar | | `/srv/models/mc2-radar.json` (+ `mc2-radar-baseline.json`) | Radar-Lauf und MC2 | jede Änderung unter `flock`, atomar |
| `/srv/models/mc2-jobs/` | nur MC2 (Akten) und die Hülle des Auftrags (`.log`, `.exit`) | Akten atomar; Protokoll und Exit-Code schreibt der Auftrag selbst, MC2 hängt nur `[mc]`-Zeilen an. |
| `/srv/models/mc2-pins.json` | `autoupdate.sh` hält fest; MC2 gibt frei | Format: Baustein → `pinned`, `version`, `grund`, `datum` | | `/srv/models/mc2-pins.json` | `autoupdate.sh` hält fest; MC2 gibt frei | Format: Baustein → `pinned`, `version`, `grund`, `datum` |
| `/srv/models/mc2-announce.json` (Briefkasten, 200 Einträge) | nur MC2 | Steward und Gateway liefern per HTTP (`MC_ANNOUNCE_HTTP`), `notify.sh` per `POST /api/voice/announce` | | `/srv/models/mc2-announce.json` (Briefkasten, 200 Einträge) | nur MC2 | Steward und Gateway liefern per HTTP (`MC_ANNOUNCE_HTTP`), `notify.sh` per `POST /api/voice/announce` |
| `/srv/models/mc2-reminders.json` | nur MC2 | Erinnerungs-Schleife und `/api/reminders` im selben Prozess | | `/srv/models/mc2-reminders.json` | nur MC2 | Erinnerungs-Schleife und `/api/reminders` im selben Prozess |
+9 -3
View File
@@ -105,8 +105,10 @@ bash ~/mission-control-v2/deploy/deploy.sh
``` ```
**Stufe 1** (die bisherige Fassung des Skripts): Sperre gegen einen zweiten Deploy (`flock` auf **Stufe 1** (die bisherige Fassung des Skripts): Sperre gegen einen zweiten Deploy (`flock` auf
`$XDG_RUNTIME_DIR/mc2-deploy.lock`). Laufen Update- oder Download-Jobs (`/api/jobs`), bricht der Deploy ab. Dann den `$XDG_RUNTIME_DIR/mc2-deploy.lock`). Läuft ein Update-Auftrag (Gruppe `maintenance` in `/api/jobs`), bricht der
alten Stand merken, `git fetch` und `git merge --ff-only origin/main` und die neue Fassung als Stufe 2 starten. Deploy ab — das Update führt Skripte aus diesem Checkout aus. Downloads laufen weiter: Aufträge sind eigene
systemd-Einheiten und überleben den Neustart von MC2. Dann den alten Stand merken, `git fetch` und
`git merge --ff-only origin/main` und die neue Fassung als Stufe 2 starten.
**Stufe 2** (die neue Fassung): **Stufe 2** (die neue Fassung):
@@ -129,7 +131,7 @@ Erfolg: eine Zeile in `/srv/models/mc2-deploy.log` und „Deploy <commit> ist li
`git reset --hard` auf den alten Stand, llama-swap-Config zurück (falls ersetzt), Dienste neu, Zeile `GESCHEITERT` im `git reset --hard` auf den alten Stand, llama-swap-Config zurück (falls ersetzt), Dienste neu, Zeile `GESCHEITERT` im
Log und die dringende Meldung „[Alarm] Deploy". Log und die dringende Meldung „[Alarm] Deploy".
Schalter: `MC_DEPLOY_TROTZDEM=1` (trotz laufender Jobs), `MC_DEPLOY_OHNE_TESTS=1` (Prüftor überspringen, nur im Schalter: `MC_DEPLOY_TROTZDEM=1` (trotz laufendem Update-Auftrag), `MC_DEPLOY_OHNE_TESTS=1` (Prüftor überspringen, nur im
Notfall), `MC_DEPLOY_SKIP_SWAP_CONFIG=1` (llama-swap-Config nie anfassen). Notfall), `MC_DEPLOY_SKIP_SWAP_CONFIG=1` (llama-swap-Config nie anfassen).
Nicht Teil des Deploys: das Frontend bauen (`frontend/dist` kommt fertig aus Git), die Root-Kopie von `warmup.sh`, Nicht Teil des Deploys: das Frontend bauen (`frontend/dist` kommt fertig aus Git), die Root-Kopie von `warmup.sh`,
@@ -141,6 +143,10 @@ Neustarts von llama-swap und Hermes.
`gateway_app`, `steward` und `radar_lauf`, dann `pytest backend/tests`. Die Frontend-Prüfungen (`npm run lint`, `gateway_app`, `steward` und `radar_lauf`, dann `pytest backend/tests`. Die Frontend-Prüfungen (`npm run lint`,
`npm test`, `npm run build`) laufen nur am PC. `npm test`, `npm run build`) laufen nur am PC.
**Probelauf auf der Box** `deploy/probelauf-box.sh` (am PC, vor dem Merge nach `main`): schickt den lokalen HEAD
als Abzug nach `/tmp` auf der Box und lässt dort das Prüftor mit dem Python des Box-Checkouts laufen. Ändert auf
der Box sonst nichts. Grund: Unter Linux zeigen sich Wettläufe, die am Windows-PC durchgehen (24.09.).
## Sicherung ## Sicherung
- **Wann:** `mc2-backup.timer` täglich 03:30 (+≤5 min); außerdem vor jedem Hermes-Update, vor jedem Zurückspielen und - **Wann:** `mc2-backup.timer` täglich 03:30 (+≤5 min); außerdem vor jedem Hermes-Update, vor jedem Zurückspielen und
+7 -2
View File
@@ -14,8 +14,13 @@ und Symptom; Erledigtes streichen (die Git-Historie behält es)._
die letzten 5). Vorher `diff /etc/llama-swap/config.yaml deploy/llama-swap.config.yaml` und den lebenden Stand ins die letzten 5). Vorher `diff /etc/llama-swap/config.yaml deploy/llama-swap.config.yaml` und den lebenden Stand ins
Repo holen. `MC_DEPLOY_SKIP_SWAP_CONFIG=1` lässt die Config ganz in Ruhe. Bis 24.09. überschrieb jeder Deploy sie Repo holen. `MC_DEPLOY_SKIP_SWAP_CONFIG=1` lässt die Config ganz in Ruhe. Bis 24.09. überschrieb jeder Deploy sie
bei jeder Abweichung und startete den Motor neu. bei jeder Abweichung und startete den Motor neu.
- **Ein Neustart von MC2 bricht laufende Update- und Download-Jobs ab (24.09.).** Sie leben im MC2-Prozess. - **Aufträge überleben einen Neustart von MC2, aber keinen der Box (seit 24.09., Phase 2b).** Jeder Auftrag
`deploy.sh` verschiebt den Deploy deshalb, solange ein Job läuft (`MC_DEPLOY_TROTZDEM=1` erzwingt). läuft als eigene systemd-Einheit `mc2-job-<id>`. Ein Deploy wartet trotzdem, solange ein Update-Auftrag
(Gruppe `maintenance`) läuft, weil der Skripte aus dem Checkout ausführt; Downloads laufen weiter
(`MC_DEPLOY_TROTZDEM=1` erzwingt). Bis 24.09. waren es Kindprozesse von MC2, jeder Neustart brach sie ab.
- **Am PC grün heißt nicht auf der Box grün (24.09.).** Ein Test mit Wettlauf lief unter Windows durch und
scheiterte im Deploy-Prüftor auf der Box (der Rückweg griff). Vor dem Merge nach `main`:
`bash deploy/probelauf-box.sh` lässt das Prüftor mit dem lokalen HEAD auf der Box laufen.
- **`deploy.sh` schaltet alles in `AKTIV` wieder ein.** Wer einen Dienst oder Timer dauerhaft aus haben will, nimmt - **`deploy.sh` schaltet alles in `AKTIV` wieder ein.** Wer einen Dienst oder Timer dauerhaft aus haben will, nimmt
ihn dort heraus, sonst dreht der nächste Deploy den User-Entscheid zurück (mit `mc2-autoupdate` schon passiert). ihn dort heraus, sonst dreht der nächste Deploy den User-Entscheid zurück (mit `mc2-autoupdate` schon passiert).
Schlafende Units (`box-console`, `voice-service`) stehen nur in `UNITS`. Schlafende Units (`box-console`, `voice-service`) stehen nur in `UNITS`.
+2 -7
View File
@@ -28,16 +28,11 @@ Hintergrund zur Homelab-Technik: [ARCHITEKTUR.md](../ARCHITEKTUR.md), Abschnitt
1. **Ein Modelltausch schreibt die llama-swap-Config mehrfach.** „Übernehmen" im Radar trägt das Modell unter einer 1. **Ein Modelltausch schreibt die llama-swap-Config mehrfach.** „Übernehmen" im Radar trägt das Modell unter einer
Sperre ein; Hirn-Umstellung, Aliase `fast`/`heavy` und ttl folgen als eigene Schreibvorgänge. Jeder lädt Sperre ein; Hirn-Umstellung, Aliase `fast`/`heavy` und ttl folgen als eigene Schreibvorgänge. Jeder lädt
llama-swap neu und entlädt alle Modelle. Ziel: ein Schreibvorgang je Tausch. llama-swap neu und entlädt alle Modelle. Ziel: ein Schreibvorgang je Tausch.
2. **Jobs überleben noch keinen MC2-Neustart.** Update- und Download-Jobs sind Kindprozesse von MC2; `deploy.sh` 2. **Session-Token des Hermes-Dashboards (nur mit User-Ja).** Das Drop-in
verschiebt deshalb. Verschwindet der Hermes-Job, wartet `autoupdate.sh` 20 Minuten und geht dann in
Selbstreparatur und Rückweg. Lösung in Phase 2 (Worker).
3. **Session-Token des Hermes-Dashboards (nur mit User-Ja).** Das Drop-in
`hermes-builtin-ui.service.d/session-token.conf` aus der früheren Desktop-Anbindung ist weiter gesetzt; `backup.sh` `hermes-builtin-ui.service.d/session-token.conf` aus der früheren Desktop-Anbindung ist weiter gesetzt; `backup.sh`
und `restore.sh` sichern es samt Kopie `desktop-gateway-token`. Entfernen ist Security-Config. und `restore.sh` sichern es samt Kopie `desktop-gateway-token`. Entfernen ist Security-Config.
4. **Rote Wächter-Hinweise warten nachts bis 07:00.** Der Wächter meldet mit Betreff „[Box-Problem]" ohne `-d`; zwischen 3. **Rote Wächter-Hinweise warten nachts bis 07:00.** Der Wächter meldet mit Betreff „[Box-Problem]" ohne `-d`; zwischen
00:00 und 06:59 landet das in der Morgenmeldung. Klären, ob Rot sofort raus soll. 00:00 und 06:59 landet das in der Morgenmeldung. Klären, ob Rot sofort raus soll.
5. **Die gekürzte Morgenmeldung verweist auf „Cockpit unter Meldungen".** Diesen Bereich gibt es nicht. Der volle
Stapel liegt in `~/.hermes/night-queue.txt.zuletzt-gesendet`, jede Meldung zusätzlich in Lucys Briefkasten.
6. **Code verweist auf verschobene Doku:** `deploy/mc2-backup.service` (`Documentation=` auf `docs/BACKUP.md`), 6. **Code verweist auf verschobene Doku:** `deploy/mc2-backup.service` (`Documentation=` auf `docs/BACKUP.md`),
`backend/services/backup.py` (`docs/BACKUP.md`), `backend/services/maintenance.py` (`docs/BEDIENUNG.md`, `backend/services/backup.py` (`docs/BACKUP.md`), `backend/services/maintenance.py` (`docs/BEDIENUNG.md`,
sudoers-Hinweis), `backend/services/agent.py` (`docs/HERMES_SETUP.md`), `backend/services/llamaswap.py` sudoers-Hinweis), `backend/services/agent.py` (`docs/HERMES_SETUP.md`), `backend/services/llamaswap.py`