abc8a365d2
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>
114 lines
4.9 KiB
Python
114 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
"""pre_tool_call-Guard (Faden 8, 13.07.2026): sperrt Schreibzugriffe auf den LIVE-Checkout
|
|
~/mission-control-v2 und die Hermes-QUELLE ~/.hermes/hermes-agent HART. Loest Fallstrick #2
|
|
(Worker wandert in die falsche Ebene / lag Patches im Live-Checkout ab). Die native
|
|
is_write_denied deckt diese Pfade NICHT ab (nur Credentials/System) -> diese Luecke.
|
|
|
|
UNBEDINGT (kein Task-/Profil-Scope): KEIN legitimer Hermes-Agent-Flow schreibt je in diese
|
|
zwei Pfade — der Werkstatt-Worker arbeitet im WORKSPACE-Klon (<workspace>/mission-control-v2,
|
|
NICHT home-verankert -> erlaubt), Lucy fasst MC2-Quelle nie an, und die Wartungs-Pipeline
|
|
(deploy.sh, Annahme-Runner) laeuft als Shell ueber ssh, NICHT ueber Hermes-Tools. Ein
|
|
Task-Scope waere hier sogar gefaehrlich: Worker tragen als task_id den Session-Zeitstempel
|
|
(z.B. 20260713_224206_267304), NICHT die Kanban-t_-ID — ein "nur t_"-Check liesse also alle
|
|
Worker-Schreibzugriffe durch (Bug 13.07., so entdeckt).
|
|
|
|
WICHTIG: Gesperrt wird NUR der home-verankerte Live-Checkout ($HOME/mission-control-v2), NIE
|
|
der blosse Name "mission-control-v2" (der Workspace-Klon enthaelt ihn) — sonst waere die
|
|
legitime Worker-Arbeit blockiert. Lesen ist erlaubt; geblockt werden nur Schreib-Operationen.
|
|
|
|
Ausgabe {"action":"block","message":...} = Tool geblockt; {} = durchlassen.
|
|
"""
|
|
import sys, json, os, re
|
|
|
|
|
|
def out(obj):
|
|
sys.stdout.write(json.dumps(obj))
|
|
sys.exit(0)
|
|
|
|
|
|
try:
|
|
payload = json.loads(sys.stdin.read() or "{}")
|
|
except Exception:
|
|
out({})
|
|
|
|
tool = payload.get("tool_name") or ""
|
|
ti = payload.get("tool_input") or {}
|
|
home = os.path.realpath(os.path.expanduser("~"))
|
|
LIVE = os.path.join(home, "mission-control-v2")
|
|
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 = (
|
|
"TABU (Faden 8/R2): Schreiben an dieser Stelle ist fuer Worker gesperrt. Der Live-Checkout "
|
|
"~/mission-control-v2, die Hermes-Quelle ~/.hermes/hermes-agent und die systemd-User-Units "
|
|
"~/.config/systemd/user duerfen NIE direkt veraendert werden. Arbeite im FRISCHEN Klon "
|
|
"deines Task-Workspaces; MC2-Aenderungen kommen als Vorschlags-Branch, systemd-Unit-/"
|
|
"Override-Dateien werden nur ueber eine angenommene Karte installiert, Hermes-Verhalten "
|
|
"nur ueber Config/SOUL/Skill/Hook/MCP. Siehe docs/wissen/GRENZEN.md."
|
|
)
|
|
|
|
|
|
def block():
|
|
out({"action": "block", "message": MSG})
|
|
|
|
|
|
def under_tabu(path):
|
|
rp = os.path.realpath(path)
|
|
return any(rp == t or rp.startswith(t + os.sep) for t in (LIVE, SRC, SYSD))
|
|
|
|
|
|
# --- Datei-Tools: Zielpfad kanonisch pruefen (zuverlaessig) -------------------------
|
|
if tool in ("write_file", "patch"):
|
|
fp = ti.get("path") or ti.get("file_path") or ti.get("filepath") or ""
|
|
if not fp:
|
|
out({})
|
|
fp = os.path.expanduser(fp)
|
|
cwd = payload.get("cwd") or ""
|
|
if not os.path.isabs(fp) and cwd:
|
|
fp = os.path.join(cwd, fp)
|
|
if under_tabu(fp):
|
|
block()
|
|
out({})
|
|
|
|
# --- Terminal: nur bei Schreib-Operation, die auf einen Tabu-Pfad zielt -------------
|
|
if tool == "terminal":
|
|
cmd = ti.get("command") or ""
|
|
if not cmd:
|
|
out({})
|
|
# Home-verankerte Tabu-Formen. Der Workspace-Klon
|
|
# ($HOME/.hermes/kanban/.../mission-control-v2) enthaelt KEINE davon als Teilstring.
|
|
tabu_forms = [
|
|
LIVE, "~/mission-control-v2", "$HOME/mission-control-v2", "${HOME}/mission-control-v2",
|
|
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) + ")"
|
|
if not re.search(tabu_alt, cmd):
|
|
out({}) # kein home-verankerter Tabu-Pfad referenziert -> Workspace-Arbeit, durchlassen
|
|
|
|
# (a) git-Schreib-Subkommando (Repo-Kontext ist der referenzierte Tabu-Pfad).
|
|
if re.search(
|
|
r"(?:^|[^\w])git(?:\s+-C\s+\S+)?\s+(?:-[\w-]+\s+)*"
|
|
r"(commit|push|reset|checkout|switch|merge|add|rm|mv|clean|stash|rebase|apply|restore|init|remote|config)\b",
|
|
cmd,
|
|
):
|
|
block()
|
|
# (b) sed -i, das einen Tabu-Pfad anfasst.
|
|
if re.search(r"(?:^|[^\w])sed\s+-i\S*\s+[^|&;]*" + tabu_alt, cmd):
|
|
block()
|
|
# (c) Umleitung > / >> / tee, die auf einen Tabu-Pfad ZIELT (nicht 2>/dev/null o.ae.).
|
|
if re.search(r"(?:>>?|(?:^|[^\w])tee\s+(?:-a\s+)?)\s*" + tabu_alt, cmd):
|
|
block()
|
|
# (d) destruktiv/kopierend mit Tabu-Pfad im selben Kommando-Segment.
|
|
if re.search(
|
|
r"(?:^|[^\w])(rm|rmdir|mv|cp|truncate|dd|chmod|chown|ln|install|mkdir|touch)\s+[^|&;]*" + tabu_alt,
|
|
cmd,
|
|
):
|
|
block()
|
|
out({})
|
|
|
|
out({})
|