Files
homelabstack/docs/superpowers/plans/2026-06-27-hermes-ecosystem-integration.md
T
ginnoirandClaude Opus 4.8 98c4a2a02b docs: implementation plan for Hermes ecosystem integrations
Phased, reversible plan. Phase 1 (acp-skill + curator-evolver, report-only)
fully actionable; Phases 2-3 (camofox stack, eagle-eye trial, UI) gated on
the spec's open decisions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 05:18:21 -05:00

443 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Hermes Ecosystem Integration Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Integrate selected Hermes-ecosystem tools into ginnoir's live valhalla deployment — a host-side delegation skill and self-improvement plugin (Phase 1), a stealth-browser homelab stack and a skill pre-filter trial (Phase 2), and an optional UI upgrade (Phase 3) — each reversible and sized for the single-P100 / weak-local-model constraints.
**Architecture:** Two integration classes. **Class A** (host-side Hermes plugins/skills) install into `~/.hermes/` on the valhalla host and are applied by SSH + `hermes` CLI + `sudo systemctl restart hermes-gateway.service`; they are **host-managed, NOT committed to this repo** (tracked in project memory + the Obsidian vault, like the rustdesk/obsidian/xvfb units). **Class B** (Docker services) become `stacks/<name>/` entries deployed via the normal Gitea-poll path, fronted by Caddy `internal_only` + Authentik. The companion spec is `docs/superpowers/specs/2026-06-27-hermes-ecosystem-integration-design.md`.
**Tech Stack:** Hermes Agent v0.17.0 (host systemd), llama-swap/Tesla P100 backend (`gpt-oss-20b`, `--parallel 1`), Python 3.11 (`~/.hermes/hermes-agent/venv`), `uv`, SQLite, Docker Compose + Portainer (Gitea-polled), Caddy, Authentik, Codex + Claude Code CLIs.
---
## How to read this plan (operational, not codebase-TDD)
These are operational integrations against a live host and third-party services, so the TDD rhythm
is adapted: each task is **back up → change → verify with a smoke test → document/commit**. The
"test" is a real verification command with expected output. **Class A (host) changes are not git
commits** — their checkpoint is a backup + smoke test + a memory/vault note. **Class B (repo)
changes do commit** (and push triggers Portainer). Run every step from the Windows workstation;
host steps use `ssh -o BatchMode=yes ginnoir@valhalla "..."`.
**Global guardrails (apply to every task):**
- `hermes` is only on the **login-shell** PATH → over SSH call it by full path: `~/.local/bin/hermes`.
- Gateway restart needs root: `sudo systemctl restart hermes-gateway.service`.
- Always back up `~/.hermes/config.yaml` before editing (`cp ...bak.$(date +%s)`).
- **Never** load a second model onto the P100. Keep curator/eagle-eye semantic layers on CPU or off.
- Read third-party code before running it (curator writes skills; acp-skill spawns external agents; camofox automates a browser).
**Decision gates:** Phase 1 is fully actionable now. **Phases 2 and 3 are gated on the spec §6
decisions** (UI choice; curator autonomy; eagle-eye trial; camofox wiring). Do not start a gated
task group until ginnoir has answered.
---
# PHASE 1 — Host-side, reversible, high-leverage (actionable now)
## Task 1: Pre-flight — capture current Hermes state
**Files:**
- Host only (no repo files).
- [ ] **Step 1: Verify host reachability and Hermes services are up**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "systemctl is-active hermes-gateway.service hermes-dashboard.service hermes-webui.service"
```
Expected: three lines, each `active`.
- [ ] **Step 2: Snapshot config + inventory skills/plugins/sessions dirs**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "cp ~/.hermes/config.yaml ~/.hermes/config.yaml.bak.preflight.$(date +%s) && ls -la ~/.hermes/skills ~/.hermes/plugins ~/.hermes/sessions 2>&1 | head -60 && ~/.local/bin/hermes --version"
```
Expected: a backup is created; directory listings print (note whether `~/.hermes/plugins` exists yet); `hermes` prints a version (≈ v0.17.0). Record the skills-dir path — confirms `~/.hermes/skills` is correct for later tasks.
- [ ] **Step 3: Confirm Codex and Claude Code are invocable from the host as ginnoir**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "bash -lc 'which codex; which claude; codex --version 2>&1 | head -1; claude --version 2>&1 | head -1'"
```
Expected: paths for both `codex` and `claude`, and a version line each. (Uses `-lc` to get the login PATH.) **If either is missing**, the ACP delegation skill (Task 2) can still install but its `codex`/`claude-code` targets will be non-functional until they're on the service PATH — note this for ginnoir.
- [ ] **Step 4: Checkpoint**
No commit (host inventory only). Record findings (skills-dir path, whether `plugins/` exists, Codex/Claude availability) in the session notes for use in Tasks 23.
---
## Task 2: Install `hermes-agent-acp-skill` (multi-agent delegation)
**Files:**
- Host: `~/.hermes/skills/hermes-acp-orchestrator/` (skill files), `~/.hermes/config.yaml` (delegation block).
- Scratch: clone under `/storage1/hermes/workspace/clones/` (never root; see the disk gotcha).
- [ ] **Step 1: Clone and read the skill before installing**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "mkdir -p /storage1/hermes/workspace/clones && git -C /storage1/hermes/workspace/clones clone https://github.com/Rainhoole/hermes-agent-acp-skill && sed -n '1,200p' /storage1/hermes/workspace/clones/hermes-agent-acp-skill/SKILL.md"
```
Expected: repo clones; `SKILL.md` prints. **Read it** to confirm: the skill folder/name, how `delegate_task()` is wired, and whether it expects a specific install path or a config key. The README omits install steps, so the SKILL.md is authoritative — follow whatever placement it documents. If SKILL.md specifies a different mechanism than the manual copy below, use SKILL.md's.
- [ ] **Step 2: Place the skill into the Hermes skills directory**
Run (adjust the destination name to match SKILL.md's declared skill name):
```bash
ssh -o BatchMode=yes ginnoir@valhalla "mkdir -p ~/.hermes/skills/hermes-acp-orchestrator && cp -r /storage1/hermes/workspace/clones/hermes-agent-acp-skill/SKILL.md /storage1/hermes/workspace/clones/hermes-agent-acp-skill/README.md ~/.hermes/skills/hermes-acp-orchestrator/ && ls -la ~/.hermes/skills/hermes-acp-orchestrator/"
```
Expected: `SKILL.md` and `README.md` present in the new skill dir.
- [ ] **Step 3: Add the delegation config block (with a safe backup)**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "cp ~/.hermes/config.yaml ~/.hermes/config.yaml.bak.acp.$(date +%s) && printf '\ndelegation:\n external_timeout_seconds: 900\n external_max_output_chars: 24000\n' >> ~/.hermes/config.yaml && tail -8 ~/.hermes/config.yaml"
```
Expected: a `.bak.acp.*` backup exists; the `delegation:` block is appended and printed. (If SKILL.md says the block belongs under a different key or nesting, edit accordingly instead of this append.)
- [ ] **Step 4: Restart the gateway and confirm the skill registers**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "sudo systemctl restart hermes-gateway.service && sleep 5 && systemctl is-active hermes-gateway.service && ~/.local/bin/hermes skills list 2>&1 | grep -i acp"
```
Expected: gateway `active`; the ACP/orchestrator skill appears in `hermes skills list`. (If the subcommand differs, use `~/.local/bin/hermes skills --help` to find the list command — verify on host.)
- [ ] **Step 5: Smoke-test a trivial delegation to the local hermes subagent first**
Run (a no-external-dependency delegation — routes to `hermes`, not Codex/Claude, to isolate the skill from CLI availability):
```bash
ssh -o BatchMode=yes ginnoir@valhalla "~/.local/bin/hermes run 'Use delegate_task to ask a hermes subagent to reply with exactly the word PONG, then report its output.' 2>&1 | tail -30"
```
Expected: the delegated subagent returns `PONG` and the parent reports it. **This proves the skill mechanics.** (Exact `hermes` one-shot invocation may differ — confirm the non-interactive run command via `~/.local/bin/hermes --help` in Step 1's read-through.)
- [ ] **Step 6: Smoke-test an external delegation (only if Codex/Claude were found in Task 1)**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "~/.local/bin/hermes run 'Use delegate_task with agent=claude-code to print the current working directory and nothing else, then report it.' 2>&1 | tail -40"
```
Expected: Claude Code is spawned within the 900 s timeout, returns the cwd, output is captured under the 24,000-char cap. **If it hangs or auths interactively**, the external CLI needs non-interactive credentials on the service env — note for ginnoir; the `hermes`-target path (Step 5) still works.
- [ ] **Step 7: Checkpoint (host note + reversibility recorded)**
No git commit. Record in session notes: skill installed at `~/.hermes/skills/hermes-acp-orchestrator/`, config backup at `~/.hermes/config.yaml.bak.acp.*`. **Rollback** = `rm -rf ~/.hermes/skills/hermes-acp-orchestrator`, restore the `.bak.acp.*`, restart gateway.
---
## Task 3: Install `hermes-curator-evolver` (self-improvement, report-only)
**Files:**
- Host: `~/.hermes/plugins/curator-evolver/` (plugin + `data/evidence.sqlite`), systemd **user** timer.
- [ ] **Step 1: Read the plugin source before installing (it can write to skills)**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "git -C /storage1/hermes/workspace/clones clone https://github.com/pingchesu/hermes-curator-evolver && sed -n '1,160p' /storage1/hermes/workspace/clones/hermes-curator-evolver/README.md"
```
Expected: repo clones; README prints. Confirm the apply path requires `--approve` (it does per the README) and that `auto-run` without `--apply-low-risk --approve-auto-apply` is **dry-run only**.
- [ ] **Step 2: Install the plugin (no semantic/embedding extras — keep it off the P100)**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "bash -lc '~/.local/bin/hermes plugins install pingchesu/hermes-curator-evolver --enable && uv pip install --python ~/.hermes/hermes-agent/venv/bin/python -e ~/.hermes/plugins/curator-evolver && ~/.hermes/hermes-agent/venv/bin/hermes-curator-evolver bootstrap'"
```
Expected: plugin installs to `~/.hermes/plugins/curator-evolver`; editable pip install succeeds; `bootstrap` configures and installs a **systemd user timer**. **Do NOT pass `--semantic`** (that pulls Qwen/BGE models — CPU/VRAM cost we're avoiding for now; BM25/FTS ranking is the v1 default).
- [ ] **Step 3: Backfill recent sessions and generate the first dry-run report**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "~/.hermes/hermes-agent/venv/bin/hermes-curator-evolver backfill-sessions --sessions-dir ~/.hermes/sessions --days 30 --format json 2>&1 | tail -20 && ~/.hermes/hermes-agent/venv/bin/hermes-curator-evolver report --days 7 --format json 2>&1 | tail -40"
```
Expected: evidence is mined into `~/.hermes/plugins/curator-evolver/data/evidence.sqlite`; `report` prints a JSON summary of candidate skill improvements. **No skill files are modified** (report is read-only).
- [ ] **Step 4: Generate a dry-run proposal for one skill and inspect it**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "~/.hermes/hermes-agent/venv/bin/hermes-curator-evolver auto-run --skills-dir ~/.hermes/skills --format json 2>&1 | tail -60"
```
Expected: a JSON set of **proposed** (not applied) edits. Confirm no files under `~/.hermes/skills` changed:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "find ~/.hermes/skills -newermt '-10 minutes' -type f 2>/dev/null"
```
Expected: empty output (nothing modified) — proves dry-run safety.
- [ ] **Step 5: Confirm the scheduled timer is report-only**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "bash -lc 'systemctl --user list-timers \"hermes-curator-evolver*\" --all --no-pager' && ssh -o BatchMode=yes ginnoir@valhalla \"systemctl --user cat 'hermes-curator-evolver*' 2>&1 | grep -iE 'ExecStart|approve|apply'\""
```
Expected: a timer is listed; its `ExecStart` runs `auto-run` **without** `--apply-low-risk`/`--approve-auto-apply`. **If the bootstrap-installed unit includes those flags, override it** to remove them (the morning decision in spec §6.2 defaults to report-only). If user-lingering isn't enabled the timer won't fire across logout — enable with `sudo loginctl enable-linger ginnoir` (note for ginnoir).
- [ ] **Step 6: Checkpoint (host note + reversibility recorded)**
No git commit. Record: plugin at `~/.hermes/plugins/curator-evolver`, DB at `.../data/evidence.sqlite`, timer name, report-only confirmed. **Rollback** = `systemctl --user disable --now <timer>`, `~/.local/bin/hermes plugins uninstall curator-evolver` (verify exact uninstall verb), `rm -rf ~/.hermes/plugins/curator-evolver`.
---
## Task 4: Document Phase 1 in memory + vault (durable knowledge)
**Files:**
- Memory: `C:\Users\MattC\.claude\projects\C--Users-MattC-Documents-homelabstack\memory\hermes-extensions.md` + `MEMORY.md` pointer.
- Vault: append to the Hermes project note via Obsidian MCP (`mcp__obsidian__*`).
- [ ] **Step 1: Write the memory file**
Create `memory/hermes-extensions.md` (frontmatter `type: project`) recording: acp-skill installed (delegation to hermes/codex/claude-code, 900s/24k caps), curator-evolver installed **report-only** (CPU ranking, no `--semantic`, no auto-apply flags), exact paths and rollback commands, and the host-vs-repo boundary. Link `[[llm-stack-hermes]]`, `[[multi-agent-tool-configs]]`, `[[obsidian-app-on-valhalla]]`.
- [ ] **Step 2: Add the MEMORY.md index pointer**
Append one line to `MEMORY.md`:
`- [Hermes host extensions](hermes-extensions.md) — acp delegation skill + curator-evolver (report-only) on valhalla; host-managed in ~/.hermes, not in the repo`
- [ ] **Step 3: Write back to the Obsidian vault**
Per the global rule, use the Obsidian MCP (never write CouchDB directly) to append a session note to the Hermes project folder summarizing Phase 1 (what, why report-only, rollback). If the MCP is unreachable, tell ginnoir and skip — do not hand-edit.
- [ ] **Step 4: Checkpoint**
No code commit required (memory files live outside the repo). Phase 1 complete and documented.
---
# PHASE 2 — New capability + experiment (GATED on spec §6.3 / §6.4)
> Start only after ginnoir confirms: trial eagle-eye (§6.3) and camofox wiring choice (§6.4).
## Task 5: `camofox-browser` as a homelab stack (Class B)
**Files:**
- Create: `stacks/camofox/docker-compose.yml`, `stacks/camofox/stack.env`.
- Modify: `Caddyfile` (new site block), `bookmarks-domains.html` + `bookmarks-ports.html` (regenerated).
- Host (image): build under `/storage1/hermes/workspace/clones/camofox-browser`.
- [ ] **Step 1: Decide image provenance and build it**
camofox publishes **no registry image** (`make up` builds locally). Recommended default: build on
valhalla and tag `camofox-browser:local`, reference that tag from compose (Watchtower already
disabled for pinned infra). Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "git -C /storage1/hermes/workspace/clones clone https://github.com/jo-inc/camofox-browser && cd /storage1/hermes/workspace/clones/camofox-browser && docker build -t camofox-browser:local . 2>&1 | tail -20 && docker image ls camofox-browser:local"
```
Expected: image builds; `camofox-browser:local` is listed. **Alternative (if a reproducible/Gitea-Actions build is preferred, like famapp):** build + push to `registry.ginnoir.com/ginnoir/camofox-browser` and reference that instead — flag this choice for ginnoir.
- [ ] **Step 2: Write the stack compose**
Create `stacks/camofox/docker-compose.yml`:
```yaml
# camofox stack — stealth headless browser REST API for the Hermes agent.
# No published image: built on-host as camofox-browser:local (see plan Task 5).
# Internal-only; reachable by Caddy over edge and by host-side Hermes.
services:
camofox:
image: camofox-browser:local
container_name: camofox
restart: unless-stopped
labels:
- "com.centurylabs.watchtower.enable=false"
env_file:
- stack.env
networks: [edge, camofox]
volumes:
- /config/camofox/cookies:/home/node/.camofox/cookies
- /config/camofox/profiles:/home/node/.camofox/profiles
ports:
- "172.20.0.1:9377:9377"
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:9377/health"]
interval: 30s
timeout: 10s
retries: 5
start_period: 40s
networks:
edge:
external: true
camofox:
name: camofox
driver: bridge
```
(The `172.20.0.1:9377` host-port mirrors the llm stack's pattern so host-side Hermes can reach it directly; Caddy reaches it over `edge` by container name.)
- [ ] **Step 3: Write `stack.env` (secrets; LF endings)**
Create `stacks/camofox/stack.env` with a generated bearer key (replace the value with a real secret before push):
```
CAMOFOX_ACCESS_KEY=GENERATE_A_LONG_RANDOM_KEY
CAMOFOX_ADMIN_KEY=GENERATE_A_SECOND_RANDOM_KEY
CAMOFOX_PORT=9377
```
Generate the keys: `ssh ... "openssl rand -hex 32"` (run twice). **Ensure LF line endings** (`.gitattributes` enforces this — verify the file isn't CRLF before committing). Leave `CAMOFOX_API_KEY` unset (cookie-import endpoint stays disabled).
- [ ] **Step 4: Add the Caddy site block (internal-only)**
Add to `Caddyfile` (place near other internal admin services). Since camofox enforces its own bearer auth and Hermes calls it machine-to-machine, gate by LAN only (no Authentik forward-auth, which would block the agent's API calls):
```caddy
camofox.ginnoir.com {
import internal_only
reverse_proxy camofox:9377
}
```
- [ ] **Step 5: Create host config dirs, regenerate bookmarks, then deploy**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "sudo mkdir -p /config/camofox/cookies /config/camofox/profiles && sudo chown -R ginnoir:ginnoir /config/camofox"
```
Then regenerate bookmarks and push (Portainer must have the `stacks/camofox` git stack registered — see Step 6):
```powershell
./scripts/gen-bookmarks.ps1
git add stacks/camofox/ Caddyfile bookmarks-domains.html bookmarks-ports.html
git commit -m "feat(camofox): stealth browser stack for the Hermes agent"
git push
```
Expected: commit + push; Gitea Actions reloads Caddy (Caddyfile changed); Portainer redeploys the camofox stack within 5 min.
- [ ] **Step 6: Register the stack in Portainer if new, and verify it runs**
New stacks need one-time Portainer registration (see `portainer-new-stack-registration` memory). After deploy, verify:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "docker ps --filter name=camofox --format '{{.Names}} {{.Status}}' && curl -fsS http://172.20.0.1:9377/health"
```
Expected: container `Up (healthy)`; `/health` returns OK.
- [ ] **Step 7: Smoke-test the browser API end-to-end**
Run (creates a tab, snapshots a page):
```bash
ssh -o BatchMode=yes ginnoir@valhalla "K=\$(grep CAMOFOX_ACCESS_KEY /config/portainer/compose/*/stacks/camofox/stack.env | cut -d= -f2); ID=\$(curl -fsS -H \"Authorization: Bearer \$K\" -H 'Content-Type: application/json' -d '{\"userId\":\"smoke\",\"sessionKey\":\"t1\",\"url\":\"https://example.com\"}' http://172.20.0.1:9377/tabs | python3 -c 'import sys,json;print(json.load(sys.stdin)[\"id\"])'); curl -fsS -H \"Authorization: Bearer \$K\" \"http://172.20.0.1:9377/tabs/\$ID/snapshot?userId=smoke\" | head -20"
```
Expected: a tab id comes back; the snapshot returns accessibility text containing "Example Domain". (Adjust the JSON id field name to match the real response from Step 1's README read.)
- [ ] **Step 8: Wire camofox into Hermes as a minimal tool surface (per §6.4 decision)**
Default recommendation: a **small Hermes skill** (2 high-level tools — `browse(url)` and `search(query)`) that curls camofox, rather than exposing the full REST surface (respects the tool-budget that keeps gpt-oss-20b functional). Create `~/.hermes/skills/camofox-browse/SKILL.md` documenting the two operations against `http://172.20.0.1:9377` with the bearer key, restart the gateway, and smoke-test:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "sudo systemctl restart hermes-gateway.service && sleep 5 && ~/.local/bin/hermes run 'browse https://example.com and tell me the page heading' 2>&1 | tail -20"
```
Expected: Hermes uses the camofox tool and reports "Example Domain". **If §6.4 chose an MCP shim instead**, build/register the MCP server and add it to `mcp_servers:` with a 2-tool `tools.include` allowlist (per the MCP-curation pattern in `llm-stack-hermes`).
- [ ] **Step 9: Checkpoint**
Repo changes are committed (Step 5). Update `memory/hermes-extensions.md` + the vault note with the camofox stack + tool wiring and the bearer-key location.
---
## Task 6: Trial `eagle-eye` skill pre-filter (Class A, behind a switch)
**Files:**
- Host: `~/.hermes/plugins/eagle-eye/` (or skills dir per its README), config toggle in `~/.hermes/config.yaml`.
- [ ] **Step 1: Clone and read; confirm graceful-degradation and the jieba dependency**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "git -C /storage1/hermes/workspace/clones clone https://github.com/willingning-coder/eagle-eye && sed -n '1,200p' /storage1/hermes/workspace/clones/eagle-eye/README.md"
```
Expected: README prints. Confirm the install hook, the on/off switch, and that L2L5 (incl. dense embeddings) are optional. **Plan to run with the dense layer disabled** (CPU/keep off the P100) — lean on L1 (hard triggers) + L2 (BM25) only for the trial.
- [ ] **Step 2: Install with an easy off-switch and minimal deps**
Install per the README (likely `~/.local/bin/hermes plugins install willingning-coder/eagle-eye --enable`), then restart the gateway:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "bash -lc '~/.local/bin/hermes plugins install willingning-coder/eagle-eye --enable' && ssh -o BatchMode=yes ginnoir@valhalla 'sudo systemctl restart hermes-gateway.service && systemctl is-active hermes-gateway.service'"
```
Expected: plugin enabled; gateway `active`. (If install fails on `jieba`, `uv pip install --python ~/.hermes/hermes-agent/venv/bin/python jieba` then retry — note the foreign-language dep for maintenance.)
- [ ] **Step 3: A/B test skill selection on representative prompts**
Pick 5 prompts that should each map to a known skill and 2 that should map to none. Run each with eagle-eye enabled, then disable it (`~/.local/bin/hermes plugins disable eagle-eye` + gateway restart) and run the same 7. Record which skills each surfaced and whether the local model then picked the right one.
```bash
ssh -o BatchMode=yes ginnoir@valhalla "~/.local/bin/hermes run '<representative prompt>' 2>&1 | tail -25"
```
Expected: with eagle-eye on, the prompt's prompt-injected skill candidates are ≤5 and include the right one; the "no skill needed" prompts proceed without forced skill loading.
- [ ] **Step 4: Keep-or-cut decision**
**Keep only if** skill selection measurably improved (right skill surfaced more often AND/OR fewer wrong skills loaded) without regressions. Otherwise disable and uninstall:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "~/.local/bin/hermes plugins uninstall eagle-eye && sudo systemctl restart hermes-gateway.service"
```
Record the verdict + evidence in `memory/hermes-extensions.md`.
- [ ] **Step 5: Checkpoint**
No repo commit (host-side). Document the A/B result and final state (kept/cut) in memory + vault.
---
# PHASE 3 — UI decision (GATED on spec §6.1)
> Start only after ginnoir picks a UI direction. Default recommended first move: **trial `hermes-ui`**
> (Task 7). `hermes-workspace` (Task 8) is the heavier alternative; `mission-control` is skipped.
## Task 7: Trial `hermes-ui` (lightweight, no build)
**Files:**
- Host: clone at `/storage1/hermes/workspace/clones/hermes-ui`; optional `hermes-ui.service` (host unit) or a tiny container; Caddy block if exposed.
- [ ] **Step 1: Clone and run the stdlib proxy against the live gateway**
Run:
```bash
ssh -o BatchMode=yes ginnoir@valhalla "git -C /storage1/hermes/workspace/clones clone https://github.com/pyrate-llama/hermes-ui && cd /storage1/hermes/workspace/clones/hermes-ui && (~/.hermes/hermes-agent/venv/bin/python3 serve_lite.py >/tmp/hermes-ui.log 2>&1 &) && sleep 3 && curl -fsS http://127.0.0.1:3333/hermes-ui.html | head -5"
```
Expected: the proxy starts on :3333 (defaults to gateway `127.0.0.1:8642`, which matches your deployment), and the HTML serves. If your gateway port differs, edit the `HERMES` variable at the top of `serve_lite.py` (no env var exists).
- [ ] **Step 2: Evaluate against the bundled webui**
Browse it from the LAN (port-forward or temporary Caddy block) and compare to the existing `hermes-webui.service`: chat/streaming, tasks/kanban, files, terminal, skills, MCP browser, cron, memory, health. Decide keep-or-revert.
- [ ] **Step 3: If keeping — make it boot-persistent and gate it**
Create a host unit `hermes-ui.service` (host-managed, like `obsidian.service` — NOT in this repo) running `serve_lite.py`, `After=hermes-gateway.service`, `Restart=on-failure`. Add a Caddy block `hermes-ui.ginnoir.com { import internal_only; reverse_proxy 172.20.0.1:3333 }` (publish the proxy on the host IP if exposing via Caddy), regenerate bookmarks, push the Caddyfile change. **If reverting**, kill the proxy (`pkill -f 'serve_lite[.]py'` — bracket trick to avoid self-match) and remove the clone.
- [ ] **Step 4: Checkpoint**
Repo change is only the Caddy block (if exposed) — commit + push that. The service unit is host-managed; document it in memory + vault alongside the `obsidian.service` note.
---
## Task 8: (Alternative) Evaluate `hermes-workspace` as a stack — scoping only
> Only if ginnoir prefers the full command center over `hermes-ui`. This task is a **scoping
> checkpoint**, not a build, because it carries a real caveat to resolve first.
- [ ] **Step 1: Confirm the swarm caveat before investing in a build**
`hermes-workspace`'s Swarm Mode (parallel tmux worker pools) **cannot parallelize inference** on a
single P100 (`--parallel 1`, one model at a time) — swarm workers serialize on the GPU or must
target a different backend. Decide with ginnoir whether the non-swarm features (workspace, ops
dashboards, Conductor missions) alone justify a React/Vite build + a new `stacks/hermes-workspace`
Docker stack behind Caddy + Authentik (gateway :8642 + dashboard :9119). If yes, this becomes its
own full plan (separate spec/plan cycle). If no, stop — `hermes-ui` (Task 7) is the chosen UI.
- [ ] **Step 2: Checkpoint**
Decision recorded in the vault. No implementation here without a dedicated follow-up plan.
---
## Self-Review (completed)
- **Spec coverage:** All 7 repos map to tasks — acp-skill (T2), curator-evolver (T3), camofox (T5),
eagle-eye (T6), hermes-ui (T7), hermes-workspace (T8 scoping), mission-control (explicitly skipped
per spec §2.5/§5, no task — intentional). Phase ordering, single-P100 discipline, host-vs-repo
boundary, provenance/reversibility, and the §6 decision gates are all reflected.
- **Placeholders:** None of the prohibited kinds. Where a third-party command form can't be verified
remotely (e.g. exact `hermes` subcommand spelling, acp-skill install mechanism, response field
names), the plan's **first step is a concrete "clone + read the README/SKILL.md" command** that
resolves it before use — a real action with expected output, not a TBD.
- **Consistency:** Paths and names are consistent throughout (`~/.hermes/skills`, `~/.hermes/plugins/curator-evolver`, `camofox-browser:local`, port 9377, gateway 8642, `172.20.0.1` host-IP publish pattern, `sudo systemctl restart hermes-gateway.service`).
- **Decision gates:** Phases 23 are clearly gated on spec §6 and must not start before ginnoir answers.