Review-Haertung R1/R2/R4 + Repo-Waechter (Vorfall verlorener Merge 12.07.)

R1: Steckbrief-Hook erinnert Worker an kanban_complete/kanban_block-Pflicht
    (Nacht-Karte 14.07. blockierte trotz fertiger Arbeit am fehlenden Aufruf).
R2: tabu-pfade-guard sperrt ~/.config/systemd/user fuer Worker — Unit-/
    Override-Dateien nur noch ueber angenommene Karte.
R4: Orchestrator-/Wartungs-Worktrees von /tmp nach ~/.hermes/worktrees
    (ueberleben Reboot, keine kaputten Registrierungen).
Neu: self-smoke Check 4 'Repo-Waechter' — Box-main mit Commits, die origin
    fehlen, loest Alarm aus (so ging die angenommene Karte
    wartung/lucy-annahme-fixes am 12.07. still verloren; am 15.07. als
    rescue/-Branch gerettet und wieder gemergt).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-15 10:04:16 +02:00
parent 1cd492eb33
commit abc8a365d2
5 changed files with 49 additions and 20 deletions
@@ -29,6 +29,7 @@ hint=""
ctx="$(cat <<EOF ctx="$(cat <<EOF
[BOX-ORIENTIERUNG — du bist ein Hintergrund-Worker auf der lokalen AI-Box (Linux, Zeit UTC / lokal Europe/Berlin, locale de_DE), NICHT Lucy und kein Dev-Laptop. Du arbeitest EINE Kanban-Aufgabe ab.] [BOX-ORIENTIERUNG — du bist ein Hintergrund-Worker auf der lokalen AI-Box (Linux, Zeit UTC / lokal Europe/Berlin, locale de_DE), NICHT Lucy und kein Dev-Laptop. Du arbeitest EINE Kanban-Aufgabe ab.]
Die Modelle / das "Hirn" laufen ausschliesslich ueber den Router llama-swap auf 127.0.0.1:8080 (startet je Modell einen llama-server) bzw. das OpenAI-kompatible Gateway von MC2 auf 127.0.0.1:9001/v1 — es gibt KEINEN anderen Modell-Server, also nicht danach suchen. Weitere Dienste: hermes-gateway :8642, mem0-Gedaechtnis :8765. Der MC2-Live-Checkout ~/mission-control-v2 ist nur zum LESEN da (Schreiben/committen TABU — Code-Arbeit immer im frischen Klon deines Task-Workspaces). Grenzen (was gehoert wohin): MC2 = Steuerpult (verwalten/klicken/zusehen, KEIN Chat-UI), Lucy = Chat & Stimme (eigenes Repo), Hermes-Quelle ist TABU (Agenten-Verhalten nur ueber Config/SOUL/Skill/Hook/MCP aendern). Feste Entscheide/Verbote + volle Grenzen-Landkarte: ~/mission-control-v2/docs/wissen/ (GRENZEN/VERDIKTE/FALLEN/STACK) + ~/wissens-vault/eigenbau-landkarte.md.${hint} Die Modelle / das "Hirn" laufen ausschliesslich ueber den Router llama-swap auf 127.0.0.1:8080 (startet je Modell einen llama-server) bzw. das OpenAI-kompatible Gateway von MC2 auf 127.0.0.1:9001/v1 — es gibt KEINEN anderen Modell-Server, also nicht danach suchen. Weitere Dienste: hermes-gateway :8642, mem0-Gedaechtnis :8765. Der MC2-Live-Checkout ~/mission-control-v2 ist nur zum LESEN da (Schreiben/committen TABU — Code-Arbeit immer im frischen Klon deines Task-Workspaces). Grenzen (was gehoert wohin): MC2 = Steuerpult (verwalten/klicken/zusehen, KEIN Chat-UI), Lucy = Chat & Stimme (eigenes Repo), Hermes-Quelle ist TABU (Agenten-Verhalten nur ueber Config/SOUL/Skill/Hook/MCP aendern). Feste Entscheide/Verbote + volle Grenzen-Landkarte: ~/mission-control-v2/docs/wissen/ (GRENZEN/VERDIKTE/FALLEN/STACK) + ~/wissens-vault/eigenbau-landkarte.md.${hint}
PROTOKOLL-PFLICHT (15.07.): Beende deine Aufgabe IMMER mit dem Tool-Aufruf kanban_complete (fertig) oder kanban_block (Frage/Sackgasse). Eine Text-Zusammenfassung OHNE diesen Aufruf zaehlt als Absturz (Protokollverstoss) und blockiert die Karte — genau so ging am 14.07. eine fertige Arbeit verloren.
EOF EOF
)" )"
+12 -6
View File
@@ -36,13 +36,18 @@ ti = payload.get("tool_input") or {}
home = os.path.realpath(os.path.expanduser("~")) home = os.path.realpath(os.path.expanduser("~"))
LIVE = os.path.join(home, "mission-control-v2") LIVE = os.path.join(home, "mission-control-v2")
SRC = os.path.join(home, ".hermes", "hermes-agent") SRC = os.path.join(home, ".hermes", "hermes-agent")
# R2 (Review 15.07.): systemd-User-Units. Die Nacht-Werkstatt hat am 14.07. einen Override
# DIREKT scharf geschaltet — ging gut, verletzt aber "jede Code-Zeile ist Klick-pflichtig".
# Unit-/Override-Dateien werden nur noch ueber eine angenommene Karte installiert.
SYSD = os.path.join(home, ".config", "systemd", "user")
MSG = ( MSG = (
"TABU (Faden 8): Schreiben an dieser Stelle ist fuer Worker gesperrt. Der Live-Checkout " "TABU (Faden 8/R2): Schreiben an dieser Stelle ist fuer Worker gesperrt. Der Live-Checkout "
"~/mission-control-v2 und die Hermes-Quelle ~/.hermes/hermes-agent duerfen NIE veraendert " "~/mission-control-v2, die Hermes-Quelle ~/.hermes/hermes-agent und die systemd-User-Units "
"werden. Arbeite im FRISCHEN Klon deines Task-Workspaces; MC2-Aenderungen kommen als " "~/.config/systemd/user duerfen NIE direkt veraendert werden. Arbeite im FRISCHEN Klon "
"Vorschlags-Branch (nie am Live-Checkout), Hermes-Verhalten nur ueber Config/SOUL/Skill/" "deines Task-Workspaces; MC2-Aenderungen kommen als Vorschlags-Branch, systemd-Unit-/"
"Hook/MCP. Siehe docs/wissen/GRENZEN.md." "Override-Dateien werden nur ueber eine angenommene Karte installiert, Hermes-Verhalten "
"nur ueber Config/SOUL/Skill/Hook/MCP. Siehe docs/wissen/GRENZEN.md."
) )
@@ -52,7 +57,7 @@ def block():
def under_tabu(path): def under_tabu(path):
rp = os.path.realpath(path) rp = os.path.realpath(path)
return rp == LIVE or rp == SRC or rp.startswith(LIVE + os.sep) or rp.startswith(SRC + os.sep) return any(rp == t or rp.startswith(t + os.sep) for t in (LIVE, SRC, SYSD))
# --- Datei-Tools: Zielpfad kanonisch pruefen (zuverlaessig) ------------------------- # --- Datei-Tools: Zielpfad kanonisch pruefen (zuverlaessig) -------------------------
@@ -78,6 +83,7 @@ if tool == "terminal":
tabu_forms = [ tabu_forms = [
LIVE, "~/mission-control-v2", "$HOME/mission-control-v2", "${HOME}/mission-control-v2", LIVE, "~/mission-control-v2", "$HOME/mission-control-v2", "${HOME}/mission-control-v2",
SRC, "~/.hermes/hermes-agent", "$HOME/.hermes/hermes-agent", "${HOME}/.hermes/hermes-agent", SRC, "~/.hermes/hermes-agent", "$HOME/.hermes/hermes-agent", "${HOME}/.hermes/hermes-agent",
SYSD, "~/.config/systemd/user", "$HOME/.config/systemd/user", "${HOME}/.config/systemd/user",
] ]
tabu_alt = "(?:" + "|".join(re.escape(t) for t in tabu_forms) + ")" tabu_alt = "(?:" + "|".join(re.escape(t) for t in tabu_forms) + ")"
if not re.search(tabu_alt, cmd): if not re.search(tabu_alt, cmd):
+19
View File
@@ -83,6 +83,25 @@ else
FAILED+=("Voice (Lucys Sprech-Pfad)") FAILED+=("Voice (Lucys Sprech-Pfad)")
fi fi
# ── 4) Repo-Wächter: verlorene Merges (Vorfall 12.15.07.2026) ──────────────
# Der Annahme-Runner merged+pusht nach origin/main; klemmt der Push (Gitea-Auth
# flattert) oder überschreibt eine fremde Session origin/main, trägt Box-main
# Commits, die origin fehlen — der nächste deploy-Reset VERNICHTET sie still
# (so verlor die Box die angenommene Karte 'wartung/lucy-annahme-fixes' vom
# 12.07., bemerkt erst beim Review am 15.07.). Hier täglich gegenprüfen.
LIVE_CO="$HOME/mission-control-v2"
if git -C "$LIVE_CO" fetch -q origin 2>/dev/null; then
LOST=$(git -C "$LIVE_CO" log --oneline origin/main..main 2>/dev/null | head -3 | tr '\n' ' ')
if [ -n "$LOST" ]; then
say "FAIL · Box-main trägt ungepushte Commits (Deploy würde sie vernichten): $LOST"
FAILED+=("Repo (ungepushte Commits auf Box-main: $LOST— vor dem nächsten Deploy nach origin retten)")
else
say "PASS · Repo-Wächter (Box-main == origin/main)"
fi
else
say "SKIP · Repo-Wächter (git fetch fehlgeschlagen)"
fi
# ── Meldung nur bei Rot ────────────────────────────────────────────────────── # ── Meldung nur bei Rot ──────────────────────────────────────────────────────
if [ "${#FAILED[@]}" -eq 0 ]; then if [ "${#FAILED[@]}" -eq 0 ]; then
say "Alle Selbst-Tests grün ($(date '+%F %H:%M'))." say "Alle Selbst-Tests grün ($(date '+%F %H:%M'))."
+13 -11
View File
@@ -86,10 +86,12 @@ Bevor du selbst Tasks aufteilst, befragst du zwingend **`gpt-oss-120b`** nach ei
3. Fuer jeden Teil-Task entscheide: (a) Artefakt, (b) Worker (`Qwen3-Coder-Next` + `build`/`refactor`), (c) Kontext. 3. Fuer jeden Teil-Task entscheide: (a) Artefakt, (b) Worker (`Qwen3-Coder-Next` + `build`/`refactor`), (c) Kontext.
### 2. Worktree anlegen (Slug = kurzer Kebab-Case-Name des Auftrags) ### 2. Worktree anlegen (Slug = kurzer Kebab-Case-Name des Auftrags)
Worktrees liegen unter `~/.hermes/worktrees/` (NICHT /tmp — ueberlebt keinen Reboot und
hinterlaesst kaputte Worktree-Registrierungen, Review-Befund 15.07.):
``` ```
cd ~/mission-control-v2 && git fetch -q origin \ mkdir -p ~/.hermes/worktrees && cd ~/mission-control-v2 && git fetch -q origin \
&& git worktree add /tmp/orch-<slug> -b orchestrator/<slug> origin/main && git worktree add ~/.hermes/worktrees/orch-<slug> -b orchestrator/<slug> origin/main
mkdir -p /tmp/orch-<slug>-work # Ablage fuer rohe Worker-Ausgaben mkdir -p ~/.hermes/worktrees/orch-<slug>-work # Ablage fuer rohe Worker-Ausgaben
``` ```
### 3. Routen — je Teil-Task ein Worker-Ein-Schuss (seriell) ### 3. Routen — je Teil-Task ein Worker-Ein-Schuss (seriell)
@@ -98,19 +100,19 @@ Fuer jeden Bau-Teil-Task:
fertige Artefakte aus frueheren Schritten als Kontext mitgeben, wenn der Worker sie braucht. fertige Artefakte aus frueheren Schritten als Kontext mitgeben, wenn der Worker sie braucht.
2. Ruf den Worker auf und fang die Ausgabe roh ab: 2. Ruf den Worker auf und fang die Ausgabe roh ab:
``` ```
printf '%s' "$eingabe" | WORKER_ROLE=build ~/.hermes/scripts/worker.sh > /tmp/orch-<slug>-work/<n>.out printf '%s' "$eingabe" | WORKER_ROLE=build ~/.hermes/scripts/worker.sh > ~/.hermes/worktrees/orch-<slug>-work/<n>.out
``` ```
(Default-Modell ist `Qwen3-Coder-Next`. Fuer einen bewusst anderen Worker `WORKER_MODEL=<echte-id>`.) (Default-Modell ist `Qwen3-Coder-Next`. Fuer einen bewusst anderen Worker `WORKER_MODEL=<echte-id>`.)
3. **Pruefe die Ausgabe, bevor du sie verwendest:** Beginnt sie mit `FEHLT:`, hast du zu wenig 3. **Pruefe die Ausgabe, bevor du sie verwendest:** Beginnt sie mit `FEHLT:`, hast du zu wenig
Kontext gegeben → nachliefern und erneut rufen. Hat der Worker versehentlich einen ```-Codezaun Kontext gegeben → nachliefern und erneut rufen. Hat der Worker versehentlich einen ```-Codezaun
oder Prosa drumherum gesetzt, entferne ihn. Dann schreibe das saubere Artefakt mit deinen oder Prosa drumherum gesetzt, entferne ihn. Dann schreibe das saubere Artefakt mit deinen
Datei-Tools an seinen Platz im Worktree (`/tmp/orch-<slug>/...`). Der Worker hat KEINE Datei-Tools an seinen Platz im Worktree (`~/.hermes/worktrees/orch-<slug>/...`). Der Worker hat KEINE
Datei-Haende — das Schreiben machst DU. Datei-Haende — das Schreiben machst DU.
4. **Schreibziel-Pflicht (Vorfall 09.07.2026):** JEDES write_file/patch-Ziel MUSS mit 4. **Schreibziel-Pflicht (Vorfall 09.07.2026):** JEDES write_file/patch-Ziel MUSS mit
`/tmp/orch-<slug>/` beginnen — vor dem ersten Schreiben einmal laut pruefen. NIEMALS nach `~/.hermes/worktrees/orch-<slug>/` beginnen — vor dem ersten Schreiben einmal laut pruefen. NIEMALS nach
`~/mission-control-v2/...` schreiben (Live-Checkout!), NIEMALS Work-Dirs/Worktrees FREMDER `~/mission-control-v2/...` schreiben (Live-Checkout!), NIEMALS Work-Dirs/Worktrees FREMDER
Slugs wiederverwenden (beim Doku-Lauf landete ein Artefakt im Live-Checkout, weil das alte Slugs wiederverwenden (beim Doku-Lauf landete ein Artefakt im Live-Checkout, weil das alte
`/tmp/orch-hermes-desktop-work` recycelt wurde). Existiert dein `/tmp/orch-<slug>` noch nicht, `/tmp/orch-hermes-desktop-work` recycelt wurde). Existiert dein `~/.hermes/worktrees/orch-<slug>` noch nicht,
ist das der Beweis, dass Schritt 2 fehlt — erst Worktree anlegen. ist das der Beweis, dass Schritt 2 fehlt — erst Worktree anlegen.
5. **Grosse Artefakte NIE im Klartext in deine Antwort** — sie gehoeren in Dateien im Worktree. 5. **Grosse Artefakte NIE im Klartext in deine Antwort** — sie gehoeren in Dateien im Worktree.
Wer Dateiinhalte in die Antwort kippt, stirbt am Output-Limit mitten im Lauf (Doku-Lauf Wer Dateiinhalte in die Antwort kippt, stirbt am Output-Limit mitten im Lauf (Doku-Lauf
@@ -148,7 +150,7 @@ Grund wie in der Werkstatt („wer seine eigenen Hausaufgaben benotet, stimmt si
**Zuerst ALLES stagen** — sonst fehlen NEUE Dateien im Diff (haeufigste Falle: `git diff` ignoriert **Zuerst ALLES stagen** — sonst fehlen NEUE Dateien im Diff (haeufigste Falle: `git diff` ignoriert
untracked Dateien, die Kritiker bekaemen einen LEEREN Diff und wuerden alles blind ablehnen): untracked Dateien, die Kritiker bekaemen einen LEEREN Diff und wuerden alles blind ablehnen):
``` ```
git -C /tmp/orch-<slug> add -A git -C ~/.hermes/worktrees/orch-<slug> add -A
``` ```
Dann die Eingabe EINMAL bauen (mit `--cached`, damit die neuen Dateien drin sind): Dann die Eingabe EINMAL bauen (mit `--cached`, damit die neuen Dateien drin sind):
``` ```
@@ -159,7 +161,7 @@ ZERLEGUNG (deine Teil-Tasks, je 1 Zeile):
<1..n> <1..n>
GIT-DIFF des Worktrees: GIT-DIFF des Worktrees:
$(git -C /tmp/orch-<slug> diff --cached origin/main)" $(git -C ~/.hermes/worktrees/orch-<slug> diff --cached origin/main)"
``` ```
Ist dieser Diff LEER, hast du nicht gestaged (oder nichts gebaut) → NICHT weiter, erst beheben. Ist dieser Diff LEER, hast du nicht gestaged (oder nichts gebaut) → NICHT weiter, erst beheben.
**Kritik A — Kompetenz** (`Qwen3-Coder-Next`, starker Coder, vom Bauen noch geladen → KEIN Swap): **Kritik A — Kompetenz** (`Qwen3-Coder-Next`, starker Coder, vom Bauen noch geladen → KEIN Swap):
@@ -212,7 +214,7 @@ Zugriff aufs Repo. Er sieht NUR, was du ihm auf stdin gibst. Deshalb:
den Fokus. Ziel: das kleinste, das den Teil-Task eindeutig macht. den Fokus. Ziel: das kleinste, das den Teil-Task eindeutig macht.
## Nach dem Commander-Entscheid (kommt als neuer Auftrag) ## Nach dem Commander-Entscheid (kommt als neuer Auftrag)
- „verwerfen" → `git worktree remove /tmp/orch-<slug> --force && git branch -D orchestrator/<slug>` - „verwerfen" → `git worktree remove ~/.hermes/worktrees/orch-<slug> --force && git branch -D orchestrator/<slug>`
(+ `rm -rf /tmp/orch-<slug>-work`; Remote-Branch loeschen, falls gepusht). (+ `rm -rf ~/.hermes/worktrees/orch-<slug>-work`; Remote-Branch loeschen, falls gepusht).
- „merge" → das Mergen nach main + Deploy macht der PC/Claude (Frontend-Builds gibt es nur dort). - „merge" → das Mergen nach main + Deploy macht der PC/Claude (Frontend-Builds gibt es nur dort).
Du pushst NIE nach main. Du pushst NIE nach main.
+4 -3
View File
@@ -42,7 +42,8 @@ unangenommene Branches aufbauen — erst wenn die Vor-Etappe in main ist, kommt
## Ablauf ## Ablauf
1. **Worktree anlegen** (Slug = kurzer Kebab-Case-Name des Auftrags): 1. **Worktree anlegen** (Slug = kurzer Kebab-Case-Name des Auftrags):
`cd ~/mission-control-v2 && git fetch origin && git worktree add /tmp/wartung-<slug> -b wartung/<slug> origin/main` `mkdir -p ~/.hermes/worktrees && cd ~/mission-control-v2 && git fetch origin && git worktree add ~/.hermes/worktrees/wartung-<slug> -b wartung/<slug> origin/main`
(Worktrees NIE unter /tmp — ueberlebt keinen Reboot, Review-Befund 15.07.)
2. **Patch** nur im Worktree. Minimal-invasiv, Stil der umgebenden Datei übernehmen 2. **Patch** nur im Worktree. Minimal-invasiv, Stil der umgebenden Datei übernehmen
(deutsche Kommentare, bestehende Muster). (deutsche Kommentare, bestehende Muster).
3. **Selbst-Gate** (was zutrifft): 3. **Selbst-Gate** (was zutrifft):
@@ -76,7 +77,7 @@ unangenommene Branches aufbauen — erst wenn die Vor-Etappe in main ist, kommt
<der Auftrag> <der Auftrag>
GIT-DIFF des Worktrees: GIT-DIFF des Worktrees:
<git -C /tmp/wartung-<slug> diff origin/main>" | FREMDBLICK_MODE=code ~/.hermes/scripts/fremdblick.sh <git -C ~/.hermes/worktrees/wartung-<slug> diff origin/main>" | FREMDBLICK_MODE=code ~/.hermes/scripts/fremdblick.sh
``` ```
Das läuft auf `Qwen3-Coder-Next` (128k, code-stark, anderes Modell als der Qwen3.6-Worker, Das läuft auf `Qwen3-Coder-Next` (128k, code-stark, anderes Modell als der Qwen3.6-Worker,
@@ -113,7 +114,7 @@ unangenommene Branches aufbauen — erst wenn die Vor-Etappe in main ist, kommt
## Nach dem User-Entscheid (kommt als neuer Auftrag) ## Nach dem User-Entscheid (kommt als neuer Auftrag)
- „verwerfen" → `git worktree remove /tmp/wartung-<slug> --force && git branch -D wartung/<slug>` - „verwerfen" → `git worktree remove ~/.hermes/worktrees/wartung-<slug> --force && git branch -D wartung/<slug>`
(+ Remote-Branch löschen, falls gepusht: `git push origin --delete wartung/<slug>`) (+ Remote-Branch löschen, falls gepusht: `git push origin --delete wartung/<slug>`)
- „merge" → das Mergen nach main + Deploy macht der PC/Claude (Frontend-Builds gibt es - „merge" → das Mergen nach main + Deploy macht der PC/Claude (Frontend-Builds gibt es
nur dort). Du pushst NIE nach main — auch nicht mit Token. nur dort). Du pushst NIE nach main — auch nicht mit Token.