From 2238777a2f69fae69fdefd5e3190b6634c91c6e0 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 28 Sep 2026 20:48:49 +0000 Subject: [PATCH] fix(alignment): resolve /root/ hardcoding and stale tanko-DSH references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F1: agent-health-check.py now resolves the home directory from the agent's user field via a shared helper (get_user_home) instead of hardcoding /root/. This fixes the false-positive wrapper-missing:tanko report — tanko has a working wrapper at /home/jerome/.local/bin/hermes, but the check was looking in /root/.local/bin/. F2: Updated stale references that described tanko as DSH-only: - hermes-zulip-restore.prose.md: tanko excluded — hybrid (DSH + Hermes) - hermes-zulip-plugin.prose.md: tanko excluded — hybrid (DSH + Hermes) - infrastructure-control.prose.md: tanko is hybrid (DSH + Hermes) agent - docs/probe-drift-round2-evidence.md: marked as historical record with dated note explaining that the DSH-only observations reflected the /root/ hardcoding bug, not the underlying truth Refs: fix/agent-health-root-hardcoding-20260928 --- docs/probe-drift-round2-evidence.md | 9 +++++++++ hermes-zulip-plugin.prose.md | 6 +++--- hermes-zulip-restore.prose.md | 4 ++-- infrastructure-control.prose.md | 2 +- scripts/agent-health-check.py | 25 +++++++++++++++++++------ 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/docs/probe-drift-round2-evidence.md b/docs/probe-drift-round2-evidence.md index 827abbb..a041ead 100644 --- a/docs/probe-drift-round2-evidence.md +++ b/docs/probe-drift-round2-evidence.md @@ -1,5 +1,14 @@ # Probe-drift round 2 — per-leg before/after evidence +> **Historical record** — 2026-09-28: The lines below that describe tanko as +> "DSH (DeepSeek Harness)" only reflect what the check reported when it was +> running. Tanko's runtime was later found to be **hybrid (DSH + Hermes)** — +> the check had a `/root/` hardcoding bug that made it probe the wrong home +> directory and report `wrapper-missing:tanko` for an agent with a working +> wrapper. This document records the observed output, not the underlying +> truth; see `fix/agent-health-root-hardcoding-20260928` for the correction. + + **Date:** 2026-09-10 **Worktree (absolute execution path):** `/root/.treehouse/prose-contracts-9ce5f3/3/prose-contracts` **Branch:** `fm/probe-drift-round2-20260909` diff --git a/hermes-zulip-plugin.prose.md b/hermes-zulip-plugin.prose.md index 71f1312..1d330b7 100644 --- a/hermes-zulip-plugin.prose.md +++ b/hermes-zulip-plugin.prose.md @@ -28,7 +28,7 @@ connectivity recovery including end-to-end DM validation. | Param | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `target` | string | yes | — | Agent name: `mumuni`, `koby`, or `shumba` (Tanko excluded — on DSH since 2026-08-27, no Hermes plugin) | +| `target` | string | yes | — | Agent name: `mumuni`, `koby`, or `shumba` (Tanko excluded — hybrid (DSH + Hermes) since 2026-08-27, no Hermes plugin) | | `branch` | string | no | `master` | Git branch to pull (overridable for pinning) | ## Maintains @@ -55,7 +55,7 @@ connectivity recovery including end-to-end DM validation. | Host | CT | Proxmox | IP (direct) | Hermes Home | User | |------|-----|---------|-------------|-------------|------| -| Tanko | CT112 | minipve | 192.168.68.122 | /home/jerome/.hermes | jerome | *(DSH since 2026-08-27 — historical, plugin retired on this host)* | +| Tanko | CT112 | minipve | 192.168.68.122 | /home/jerome/.hermes | jerome | *(hybrid (DSH + Hermes) since 2026-08-27 — historical, plugin retired on this host)* | | Koby | CT111 | storepve | 192.168.68.129 | /root/.hermes | root | | Shumba | — | — | 192.168.68.119 | /home/lucky/.hermes | lucky | @@ -121,7 +121,7 @@ cp plugins/platforms/zulip/adapter.py \ {{hermes_home}}/hermes-agent/plugins/platforms/zulip/ # Fix ownership (was Tanko-only, runs as jerome user) -# RETIRED 2026-08-27: tanko no longer uses the Hermes Zulip plugin (DSH). +# RETIRED 2026-08-27: tanko no longer uses the Hermes Zulip plugin (hybrid: DSH + Hermes). [ "{{target}}" = "tanko" ] && chown -R jerome:jerome \ {{hermes_home}}/hermes-agent/plugins/platforms/zulip/ diff --git a/hermes-zulip-restore.prose.md b/hermes-zulip-restore.prose.md index c51887a..ca7c8cc 100644 --- a/hermes-zulip-restore.prose.md +++ b/hermes-zulip-restore.prose.md @@ -24,7 +24,7 @@ gateway restart, and connection validation. | Param | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `target` | string | yes | — | Agent name: `mumuni`, `koby`, or `shumba` (Tanko excluded — DSH since 2026-08-27) | +| `target` | string | yes | — | Agent name: `mumuni`, `koby`, or `shumba` (Tanko excluded — hybrid (DSH + Hermes) since 2026-08-27) | ## Maintains @@ -93,7 +93,7 @@ cp zulip-platform-plugins/plugins/platforms/zulip/adapter.py \ zulip-platform-plugins/plugins/platforms/zulip/plugin.yaml \ /hermes-agent/plugins/platforms/zulip/ -# Fix ownership (was Tanko-only; RETIRED 2026-08-27 — tanko on DSH, no Hermes plugin) +# Fix ownership (was Tanko-only; RETIRED 2026-08-27 — tanko on hybrid (DSH + Hermes), no Hermes plugin) chown -R jerome:jerome /hermes-agent/plugins/platforms/zulip/ # Tanko only (historical) # Clean up diff --git a/infrastructure-control.prose.md b/infrastructure-control.prose.md index 1f8dc27..d94ded4 100644 --- a/infrastructure-control.prose.md +++ b/infrastructure-control.prose.md @@ -682,7 +682,7 @@ ssh root@192.168.68.110 "systemctl restart llama-server" | 109 | docker-vm | storepve | .7 | Docker host | ❌ | | 110 | gitea | minipve | **.17** | Git | ❌ | | 111 | tdunna | storepve | .129 | Hermes agent — ⛔ REPORT-ONLY (Theo's box, no GC) | ✅ | -| 112 | tanko | minipve | .122 | DSH (DeepSeek Harness) agent | ✅ | +| 112 | tanko | minipve | .122 | hybrid (DSH + Hermes) agent | ✅ | | 113 | baggy | amdpve | .114 | Hermes agent | ✅ | | 115 | scottdenya | amdpve | .75 | Denya OneCare | ❌ | | 116 | syslog-api | minipve | .116 | LiteLLM + Grafana | ❌ | diff --git a/scripts/agent-health-check.py b/scripts/agent-health-check.py index 2d413ec..cf8575d 100755 --- a/scripts/agent-health-check.py +++ b/scripts/agent-health-check.py @@ -152,6 +152,18 @@ def ssh(host, cmd, user="root"): except: return None +def get_user_home(user): + """Resolve the home directory for a user. + + For 'root', returns '/root'. For any other user, returns '/home/'. + This is used to construct paths that reference a user's home directory + (e.g., ~/.local/bin/hermes, ~/.hermes/config.yaml) instead of hardcoding /root/. + """ + if user == "root": + return "/root" + else: + return f"/home/{user}" + def http_get(url, headers=None, timeout=5): """Return HTTP status code as string.""" try: @@ -521,8 +533,8 @@ def check_config_integrity(): # Check YAML parses yaml_ok = ssh(host, - "python3 -c " - '"import yaml; yaml.safe_load(open(\'/root/.hermes/config.yaml\')); print(\'OK\')" ' + f"python3 -c " + f"'import yaml; yaml.safe_load(open(\'{home}/.hermes/config.yaml\')); print(\'OK\')" ' "2>&1 || echo 'FAIL'", user=user) if not yaml_ok: @@ -576,7 +588,8 @@ def check_wrapper_integrity(): continue # Check wrapper exists - wrapper = ssh(host, "ls -la /root/.local/bin/hermes 2>/dev/null", user=user) + home = get_user_home(user) + wrapper = ssh(host, f"ls -la {home}/.local/bin/hermes 2>/dev/null", user=user) if not wrapper: # Check alternate wrapper locations wrapper = ssh(host, "which hermes 2>/dev/null; command -v hermes 2>/dev/null", user=user) @@ -599,7 +612,7 @@ def check_wrapper_integrity(): # a removed path (litellm-api-keys.prose.md documents # `rm -f /usr/local/bin/infisical`) must neither produce a dangling path # nor trigger the PATH check — it is not an invocation. - wrapper_body = ssh(host, "cat /root/.local/bin/hermes 2>/dev/null", user=user) or "" + wrapper_body = ssh(host, f"cat {home}/.local/bin/hermes 2>/dev/null", user=user) or "" wrapper_code = "\n".join(line.split("#", 1)[0] for line in wrapper_body.splitlines()) invoked_paths = _infisical_invocation_paths(wrapper_body) if "infisical" in wrapper_code: @@ -635,7 +648,7 @@ def check_wrapper_integrity(): # Check hermes-real exists hermes_real = ssh(host, - "ls -la /root/.local/bin/hermes-real 2>/dev/null || echo MISS", + f"ls -la {home}/.local/bin/hermes-real 2>/dev/null || echo MISS", user=user) if not hermes_real or hermes_real.strip() == "MISS": # Check venv path @@ -650,7 +663,7 @@ def check_wrapper_integrity(): # Check the .env file has the key env_has_key = ssh(host, - "grep -c 'LITELLM_API_KEY' /root/.hermes/.env 2>/dev/null || echo 0", + f"grep -c 'LITELLM_API_KEY' {home}/.hermes/.env 2>/dev/null || echo 0", user=user) if env_has_key and env_has_key.strip() not in ("", "0"): print(f" ✅ {name}: wrapper + .env key present")