|

v0.3 scope reduction: drop --hermes flag and Project.hermes field

v0.3 scope reduction: drop --hermes flag and Project.hermes field

The --hermes/--no-hermes/--purge-hermes flag plumbing and per-container
hermes provisioning are removed. Smoke testing exposed fundamental
problems with the bind-mount-the-host-runtime architecture: hermes'
Python venv pins to a uv-vendored host-only path, and uid-mapping for
file binds doesn't generalize. v0.4 will revisit via pre-built incus
images.

What v0.3 still ships: setup wizard, llm-share profile (ollama wiring),
schema-2 migration, hermes module helpers as a library for v0.4.
Author: Chris Tusa <chris.tusa@leafscale.com>
Date: May 08, 2026 13:32
Node: 1bacbaae1c2fcf5cbff002855d3a0b0b86ae2276
Branch: default
Tags: v0.3.0

Diff

diff -r a8ec2564f3fe -r 1bacbaae1c2f README.md
--- a/README.md	Fri May 08 02:41:29 2026 +0000
+++ b/README.md	Fri May 08 13:32:26 2026 +0000
@@ -42,11 +42,11 @@
 
 | Subcommand | Description |
 |---|---|
-| `new <name> [--repo <dirname>] [--image <image>]` | Launch an Incus container and bind a local repo into it. |
+| `new <name> [--repo <dirname>] [--image <image>]` | Launch an Incus container and bind a local repo into it. (Use `repoman setup --with-llm` first if you want LLM stack wiring.) |
 | `sync [name] [--no-delete] [--dry-run]` | Mirror local repos to NFS backup via rsync. |
 | `list` | Print a table of all registered projects (read-only). |
 | `status [name]` | Show project state from registry + live incus query. |
-| `remove <name> --yes [--keep-incus]` | Delete the incus container and registry entry. --yes required. |
+| `remove <name> --yes [--keep-incus]` | Delete the incus container and registry entry. `--yes` required. |
 | `shell <name> [--cwd <path>]` | Open a bash login shell inside the project's container (replaces repoman). |
 | `--help` / `-h` / `help` | Show usage and subcommand list. |
 | `--version` / `-V` | Print version string. |
@@ -72,18 +72,8 @@
   container environment.
 - Registry default `[defaults].llm.enabled = true`.
 
-Per-project hermes data directories (opt-in):
-
-    repoman new myapp --hermes
-
-This selectively seeds your host's `~/.hermes/` (credentials, config, skills, hooks,
-runtime symlinks) into `~/.local/share/repoman/hermes/myapp/` and bind-mounts that
-into the container as `~/.hermes`. Per-container sessions, memories, and SQLite state
-stay isolated — never share a hermes data dir between two running instances.
-
-To delete the data dir alongside the container:
-
-    repoman remove myapp --purge-hermes
+Containers created after `--with-llm` setup automatically inherit the `llm-share`
+profile and can reach the host's ollama daemon at the configured LAN address.
 
 ## Smoke test (requires Incus + NFS)
 
diff -r a8ec2564f3fe -r 1bacbaae1c2f docs/superpowers/plans/2026-05-06-repoman-v0.3-llm-and-setup.md
--- a/docs/superpowers/plans/2026-05-06-repoman-v0.3-llm-and-setup.md	Fri May 08 02:41:29 2026 +0000
+++ b/docs/superpowers/plans/2026-05-06-repoman-v0.3-llm-and-setup.md	Fri May 08 13:32:26 2026 +0000
@@ -1,5 +1,18 @@
 # repoman v0.3 — Setup wizard + LLM stack Implementation Plan
 
+## Scope reduction (2026-05-08)
+
+The `--hermes`/`--no-hermes`/`--purge-hermes` flag-based provisioning was **removed
+during smoke testing** and does not ship in v0.3. Smoke testing exposed fundamental
+problems with the bind-mount-the-host-runtime architecture: hermes' Python venv pins
+to a uv-vendored host-only path, and uid-mapping for file binds does not generalize.
+v0.4 will revisit via pre-built incus images.
+
+v0.3 ships: setup wizard, llm-share profile (ollama wiring), schema-2 migration,
+hermes module helpers as a library for v0.4.
+
+---
+
 > **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:** Ship `repoman setup` (idempotent host-bootstrap wizard) plus per-container hermes data-dir provisioning (`repoman new --hermes`, `repoman remove --purge-hermes`), with a repoman-managed `llm-share` Incus profile that wires containers to the host's ollama daemon over LAN.
diff -r a8ec2564f3fe -r 1bacbaae1c2f docs/superpowers/specs/2026-05-06-repoman-v0.3-llm-and-setup.md
--- a/docs/superpowers/specs/2026-05-06-repoman-v0.3-llm-and-setup.md	Fri May 08 02:41:29 2026 +0000
+++ b/docs/superpowers/specs/2026-05-06-repoman-v0.3-llm-and-setup.md	Fri May 08 13:32:26 2026 +0000
@@ -1,5 +1,24 @@
 # repoman v0.3 — Setup wizard + LLM stack integration
 
+## Scope reduction (2026-05-08)
+
+The `--hermes`/`--no-hermes`/`--purge-hermes` flag-based provisioning described in
+this spec was **removed during smoke testing** and will not ship in v0.3.
+
+Root cause: the bind-mount-the-host-runtime architecture does not survive Python venv
+portability constraints. Hermes' venv pins to a uv-vendored host-only path; bind-
+mounting it into a container where that path doesn't exist fails at import time. Uid
+mapping for the bind also does not generalize cleanly. Copying the venv breaks shebang
+paths.
+
+v0.4 will revisit per-container hermes provisioning via pre-built incus images that
+embed a self-contained hermes install rather than sharing the host runtime.
+
+**v0.3 still ships:** `repoman setup` wizard, `llm-share` profile (ollama client
+wiring), schema-2 migration, and the `hermes` module helpers as a library for v0.4.
+
+---
+
 **Status:** v0.3 design, under review
 **Date:** 2026-05-06
 **Implementation language:** reef-lang 0.5.20 (no new stdlib requirements vs v0.2)
diff -r a8ec2564f3fe -r 1bacbaae1c2f src/cli.reef
--- a/src/cli.reef	Fri May 08 02:41:29 2026 +0000
+++ b/src/cli.reef	Fri May 08 13:32:26 2026 +0000
@@ -11,13 +11,11 @@
 import sys.args as args
 import sys.process as p
 import config
-import hermes
 import incus
 import setup
 import sync
 import paths
 import log
-import io.dir as iodir
 
 export
     fn cmd_new(argv: [string]): int
@@ -39,9 +37,6 @@
     let _r2 = flag.string_flag(parser, "image", '\0', "", "container image (overrides default)")
     let _v  = flag.bool_flag(parser, "verbose", 'v', false, "show subprocess output (incus probes)")
     let _q  = flag.bool_flag(parser, "quiet", 'q', false, "force quiet mode even if config sets verbose")
-    let _f3 = flag.bool_flag(parser, "hermes",     '\0', false, "provision a per-container hermes data dir (overrides default)")
-    let _f4 = flag.bool_flag(parser, "no-hermes",  '\0', false, "skip per-container hermes data dir (overrides default)")
-
     if not flag.parse(parser)
         console.printErr("repoman: error: " + flag.error(parser))
         return 2
@@ -92,24 +87,6 @@
         verbose = false
     end if
 
-    let cli_hermes: bool = flag.get_bool(parser, "hermes")
-    let cli_no_hermes: bool = flag.get_bool(parser, "no-hermes")
-    if cli_hermes and cli_no_hermes
-        console.printErr("repoman: error: --hermes and --no-hermes are mutually exclusive")
-        return 2
-    end if
-    mut want_hermes: bool = reg.defaults.llm.hermes_default
-    if cli_hermes
-        want_hermes = true
-    end if
-    if cli_no_hermes
-        want_hermes = false
-    end if
-    if want_hermes and not reg.defaults.llm.enabled
-        console.printErr("repoman: error: --hermes requires LLM stack enabled. Run 'repoman setup --with-llm' first.")
-        return 3
-    end if
-
     // Reject duplicate name
     let pn: int = reg.projects.length()
     mut i: int = 0
@@ -219,69 +196,6 @@
         return 1
     end if
 
-    // Seed per-container hermes data dir + add disk device (opt-in)
-    if want_hermes
-        let dest: string = hermes.state_dir_for(home, name)
-        if iodir.dir_exists(dest)
-            log.write("repoman: error: hermes data dir already exists at " + dest)
-            log.write("hint: incus delete --project " + reg.defaults.incus_project + " " + name + " ; rm -rf " + dest)
-            return 4
-        end if
-        let source: string = paths.join(home, ".hermes")
-        log.write("==> hermes seed " + source + " -> " + dest)
-        let sr = hermes.seed_data_dir(source, dest, reg.defaults.llm.hermes_seed)
-        if rg.is_err(sr)
-            log.write("repoman: error: " + rg.unwrap_err(sr))
-            return 1
-        end if
-        let user: string = env.get_env_or("USER", "")
-        let in_path: string = "/home/" + user + "/.hermes"
-        log.write("==> incus device add " + name + " hermes-state " + dest + " -> " + in_path)
-        let dr2 = incus.device_add_disk_opts(reg.defaults.incus_project, name, "hermes-state", dest, in_path, ["shift=true"])
-        if rg.is_err(dr2)
-            log.write("repoman: error: " + rg.unwrap_err(dr2))
-            log.write("hint: incus delete --project " + reg.defaults.incus_project + " " + name + " ; rm -rf " + dest)
-            return 1
-        end if
-        // Bind the host's runtime dirs into the container so the Python venv
-        // and other runtime symlinks resolve through to the same host paths
-        // they were created against. (Copying breaks venv shebangs.)
-        let host_hermes: string = paths.join(home, ".hermes")
-        let host_hermes_agent: string = paths.join(host_hermes, "hermes-agent")
-        let host_hermes_node:  string = paths.join(host_hermes, "node")
-        let host_hermes_bin:   string = paths.join(host_hermes, "bin")
-        let in_hermes_agent: string = paths.join(in_path, "hermes-agent")
-        let in_hermes_node:  string = paths.join(in_path, "node")
-        let in_hermes_bin:   string = paths.join(in_path, "bin")
-        log.write("==> incus device add " + name + " hermes-agent (readonly bind)")
-        let dr3 = incus.device_add_disk_opts(reg.defaults.incus_project, name, "hermes-agent", host_hermes_agent, in_hermes_agent, ["shift=true", "readonly=true"])
-        if rg.is_err(dr3)
-            log.write("repoman: error: " + rg.unwrap_err(dr3))
-            log.write("hint: incus delete --project " + reg.defaults.incus_project + " " + name + " ; rm -rf " + dest)
-            return 1
-        end if
-        log.write("==> incus device add " + name + " hermes-node (readonly bind)")
-        let dr4 = incus.device_add_disk_opts(reg.defaults.incus_project, name, "hermes-node", host_hermes_node, in_hermes_node, ["shift=true", "readonly=true"])
-        if rg.is_err(dr4)
-            log.write("repoman: error: " + rg.unwrap_err(dr4))
-            log.write("hint: incus delete --project " + reg.defaults.incus_project + " " + name + " ; rm -rf " + dest)
-            return 1
-        end if
-        log.write("==> incus device add " + name + " hermes-runtime-bin (readonly bind)")
-        let dr5 = incus.device_add_disk_opts(reg.defaults.incus_project, name, "hermes-runtime-bin", host_hermes_bin, in_hermes_bin, ["shift=true", "readonly=true"])
-        if rg.is_err(dr5)
-            log.write("repoman: error: " + rg.unwrap_err(dr5))
-            log.write("hint: incus delete --project " + reg.defaults.incus_project + " " + name + " ; rm -rf " + dest)
-            return 1
-        end if
-        // Restart again so the new devices are mounted
-        let rr2 = incus.restart(reg.defaults.incus_project, name)
-        if rg.is_err(rr2)
-            log.write("repoman: error: " + rg.unwrap_err(rr2))
-            return 1
-        end if
-    end if
-
     // Build new project entry and write registry
     let now: string = time.time_format_iso(time.time_now())
     let new_p: config.Project = config.Project {
@@ -291,8 +205,7 @@
         profiles:  eff.profiles,
         created:   now,
         last_sync: "",
-        backup:    true,
-        hermes:    want_hermes
+        backup:    true
     }
     let reg2_r = config.add_project(reg, new_p)
     if rg.is_err(reg2_r)
@@ -527,8 +440,7 @@
         + str.pad_right("IMAGE", 33, ' ')
         + str.pad_right("CREATED", 21, ' ')
         + str.pad_right("LAST_SYNC", 21, ' ')
-        + str.pad_right("BACKUP", 8, ' ')
-        + "HERMES"
+        + "BACKUP"
     )
 
     mut i: int = 0
@@ -538,18 +450,13 @@
         if p.backup
             backup_str = "yes"
         end if
-        mut hermes_str: string = "no"
-        if p.hermes
-            hermes_str = "yes"
-        end if
         println(
             str.pad_right(p.name, 12, ' ')
             + str.pad_right(p.repo, 18, ' ')
             + str.pad_right(p.image, 33, ' ')
             + str.pad_right(p.created, 21, ' ')
             + str.pad_right(p.last_sync, 21, ' ')
-            + str.pad_right(backup_str, 8, ' ')
-            + hermes_str
+            + backup_str
         )
         i = i + 1
     end while
@@ -625,16 +532,6 @@
     end if
     println("backup:      " + backup_str)
     println("state:       " + state)
-    mut hstr: string = "no"
-    if proj.hermes
-        hstr = "yes"
-    end if
-    println("hermes:      " + hstr)
-    if proj.hermes
-        let home: string = env.get_env_or("HOME", "")
-        let dest: string = hermes.state_dir_for(home, name)
-        println("  data dir:  " + dest)
-    end if
     return 0
 end cmd_status_one
 
@@ -651,8 +548,7 @@
         + str.pad_right("STATE", 10, ' ')
         + str.pad_right("CREATED", 21, ' ')
         + str.pad_right("LAST_SYNC", 21, ' ')
-        + str.pad_right("BACKUP", 8, ' ')
-        + "HERMES"
+        + "BACKUP"
     )
 
     mut i: int = 0
@@ -667,17 +563,12 @@
         if p.backup
             backup_str = "yes"
         end if
-        mut hermes_str: string = "no"
-        if p.hermes
-            hermes_str = "yes"
-        end if
         println(
             str.pad_right(p.name, 12, ' ')
             + str.pad_right(state, 10, ' ')
             + str.pad_right(p.created, 21, ' ')
             + str.pad_right(p.last_sync, 21, ' ')
-            + str.pad_right(backup_str, 8, ' ')
-            + hermes_str
+            + backup_str
         )
         i = i + 1
     end while
@@ -690,7 +581,6 @@
     flag.description(parser, "Remove a project: delete its container and registry entry")
     let _y   = flag.bool_flag(parser, "yes", 'y', false, "confirm removal (required)")
     let _k   = flag.bool_flag(parser, "keep-incus", '\0', false, "leave the incus container; only remove from registry")
-    let _fph = flag.bool_flag(parser, "purge-hermes", '\0', false, "also delete the per-container hermes data dir")
 
     if not flag.parse(parser)
         console.printErr("repoman: error: " + flag.error(parser))
@@ -775,17 +665,6 @@
         return 1
     end if
 
-    let purge: bool = flag.get_bool(parser, "purge-hermes")
-    if purge
-        let dest: string = hermes.state_dir_for(home, name)
-        log.write("==> purge hermes data dir: " + dest)
-        let pr = hermes.purge_data_dir(dest)
-        if rg.is_err(pr)
-            log.write("repoman: warning: " + rg.unwrap_err(pr))
-            // Don't fail the overall remove on purge failure — container is gone.
-        end if
-    end if
-
     log.write("==> removed '" + name + "'")
     return 0
 end cmd_remove
@@ -888,9 +767,8 @@
     console.printErr("  setup [--non-interactive] [--with-llm] [--without-llm]")
     console.printErr("      First-time host bootstrap: incus project, profiles, registry.")
     console.printErr("")
-    console.printErr("  new <name> [--repo <dirname>] [--image <image>] [--hermes | --no-hermes]")
+    console.printErr("  new <name> [--repo <dirname>] [--image <image>]")
     console.printErr("      Launch a container in the 'repoman' Incus project; bind ~/repos/<dirname>.")
-    console.printErr("      --hermes seeds a per-container hermes data dir (requires LLM stack).")
     console.printErr("")
     console.printErr("  sync [name] [--no-delete] [--dry-run]")
     console.printErr("      Mirror local repos to NFS backup (rsync --delete by default).")
@@ -901,9 +779,8 @@
     console.printErr("  status [name]")
     console.printErr("      Show project state from registry + live incus query.")
     console.printErr("")
-    console.printErr("  remove <name> --yes [--keep-incus] [--purge-hermes]")
+    console.printErr("  remove <name> --yes [--keep-incus]")
     console.printErr("      Delete the incus container and registry entry. --yes required.")
-    console.printErr("      --purge-hermes also deletes the per-container hermes data dir.")
     console.printErr("")
     console.printErr("  shell <name> [--cwd <path>]")
     console.printErr("      Open a bash login shell inside the project's container.")
diff -r a8ec2564f3fe -r 1bacbaae1c2f src/config.reef
--- a/src/config.reef	Fri May 08 02:41:29 2026 +0000
+++ b/src/config.reef	Fri May 08 13:32:26 2026 +0000
@@ -55,7 +55,6 @@
     created: string
     last_sync: string
     backup: bool
-    hermes: bool
 end Project
 
 type Mount = struct
@@ -188,8 +187,7 @@
             profiles:  parse_string_array(toml.toml_array_get(doc.keys, doc.values, doc.count, "project", i, "profiles")),
             created:   toml.toml_array_get(doc.keys, doc.values, doc.count, "project", i, "created"),
             last_sync: toml.toml_array_get(doc.keys, doc.values, doc.count, "project", i, "last_sync"),
-            backup:    toml.toml_array_get(doc.keys, doc.values, doc.count, "project", i, "backup") != "false",
-            hermes:    toml.toml_array_get(doc.keys, doc.values, doc.count, "project", i, "hermes") == "true"
+            backup:    toml.toml_array_get(doc.keys, doc.values, doc.count, "project", i, "backup") != "false"
         }
         projects[i] = p
         i = i + 1
@@ -237,7 +235,6 @@
         toml.toml_set_string(b, "created", p.created)
         toml.toml_set_string(b, "last_sync", p.last_sync)
         toml.toml_set_bool(b, "backup", p.backup)
-        toml.toml_set_bool(b, "hermes", p.hermes)
         i = i + 1
     end while
 
@@ -393,8 +390,7 @@
             new_projects[k] = Project {
                 name: old.name, repo: old.repo, image: old.image,
                 profiles: old.profiles, created: old.created,
-                last_sync: ts, backup: old.backup,
-                hermes: old.hermes
+                last_sync: ts, backup: old.backup
             }
         else
             new_projects[k] = reg.projects[k]
diff -r a8ec2564f3fe -r 1bacbaae1c2f src/setup.reef
--- a/src/setup.reef	Fri May 08 02:41:29 2026 +0000
+++ b/src/setup.reef	Fri May 08 13:32:26 2026 +0000
@@ -19,7 +19,7 @@
     type Environment
     fn detect_environment(home_dir: string): Environment
     fn detect_host_lan_ip(): string
-    fn render_llm_share_template(host_lan_ip: string, user: string, hermes_binary: string): string
+    fn render_llm_share_template(host_lan_ip: string, user: string): string
     fn template_contains_placeholder(s: string): bool
     type Stage
     fn plan_stages(env: Environment, with_llm: bool): [Stage]
@@ -149,11 +149,11 @@
     return ""
 end detect_host_lan_ip
 
-fn render_llm_share_template(host_lan_ip: string, user: string, hermes_binary: string): string
+fn render_llm_share_template(host_lan_ip: string, user: string): string
     let base: string =
         "name: llm-share\n" +
         "description: |\n" +
-        "  Local LLM client tools (ollama client + hermes runtime) and host-daemon wiring.\n" +
+        "  Local LLM client tools (ollama client) and host-daemon wiring.\n" +
         "  Created by repoman setup; do not hand-edit (changes will be overwritten).\n" +
         "config:\n" +
         "  environment.OLLAMA_HOST: \"http://{HOST_LAN_IP}:11434\"\n" +
@@ -164,17 +164,6 @@
         "    path: /usr/local/bin/ollama\n" +
         "    readonly: \"true\"\n"
 
-    mut hermes_segment: string = ""
-    if str.length(hermes_binary) > 0
-        hermes_segment =
-            "  hermes-bin:\n" +
-            "    type: disk\n" +
-            "    source: " + hermes_binary + "\n" +
-            "    path: /usr/local/bin/hermes\n" +
-            "    readonly: \"true\"\n" +
-            "    shift: \"true\"\n"
-    end if
-
     let tail: string =
         "  ollama-state:\n" +
         "    type: disk\n" +
@@ -182,7 +171,7 @@
         "    path: /home/{USER}/.ollama\n" +
         "    shift: \"true\"\n"
 
-    let combined: string = base + hermes_segment + tail
+    let combined: string = base + tail
     let s1: string = str.replace(combined, "{HOST_LAN_IP}", host_lan_ip)
     let s2: string = str.replace(s1,       "{USER}",        user)
     return s2
@@ -273,7 +262,7 @@
                 "Add OLLAMA_HOST=" + env.host_lan_ip + ":11434 to your systemd unit and restart ollama."
             )
         end if
-        let yaml: string = render_llm_share_template(env.host_lan_ip, env.user, env.hermes_binary)
+        let yaml: string = render_llm_share_template(env.host_lan_ip, env.user)
         let r = incus.profile_create_or_edit("repoman", "llm-share", yaml)
         if rg.is_err(r)
             return @Result[config.Registry, string].Err(rg.unwrap_err(r))
@@ -430,9 +419,6 @@
     println("setup complete.")
     println("")
     println("  next: repoman new <name>")
-    if reg.defaults.llm.enabled
-        println("        repoman new <name> --hermes")
-    end if
     println("        repoman list")
     return 0
 end cmd_setup
diff -r a8ec2564f3fe -r 1bacbaae1c2f tests/test_config_llm_parse.reef
--- a/tests/test_config_llm_parse.reef	Fri May 08 02:41:29 2026 +0000
+++ b/tests/test_config_llm_parse.reef	Fri May 08 13:32:26 2026 +0000
@@ -6,7 +6,7 @@
     let runner = new framework.TestRunner()
 
     // Schema 2 with [defaults.llm] populated
-    let toml: string = "[repoman]\nschema = 2\noutput = \"quiet\"\n\n[defaults]\nrepos_root = \"~/repos\"\nbackup_root = \"/nfs/repos\"\nlogdir = \"~/.local/state/repoman\"\nincus_project = \"repoman\"\ndefault_image = \"images:ubuntu/26.04/cloud\"\nprofiles = [\"default\", \"claude-share\", \"llm-share\"]\n\n[defaults.llm]\nenabled = true\nhermes_default = false\nollama_url = \"http://192.168.168.42:11434\"\nhermes_seed = [\".env\", \"config.yaml\", \"skills/\"]\n\n[[project]]\nname = \"isurus\"\nrepo = \"isurus\"\nimage = \"images:ubuntu/26.04/cloud\"\nprofiles = [\"default\", \"claude-share\", \"llm-share\"]\ncreated = \"2026-05-06T00:00:00Z\"\nlast_sync = \"\"\nbackup = true\nhermes = true\n"
+    let toml: string = "[repoman]\nschema = 2\noutput = \"quiet\"\n\n[defaults]\nrepos_root = \"~/repos\"\nbackup_root = \"/nfs/repos\"\nlogdir = \"~/.local/state/repoman\"\nincus_project = \"repoman\"\ndefault_image = \"images:ubuntu/26.04/cloud\"\nprofiles = [\"default\", \"claude-share\", \"llm-share\"]\n\n[defaults.llm]\nenabled = true\nhermes_default = false\nollama_url = \"http://192.168.168.42:11434\"\nhermes_seed = [\".env\", \"config.yaml\", \"skills/\"]\n\n[[project]]\nname = \"isurus\"\nrepo = \"isurus\"\nimage = \"images:ubuntu/26.04/cloud\"\nprofiles = [\"default\", \"claude-share\", \"llm-share\"]\ncreated = \"2026-05-06T00:00:00Z\"\nlast_sync = \"\"\nbackup = true\n"
 
     let r = config.parse_registry(toml)
     runner.assert_eq_bool(rg.is_ok(r), true, "schema 2 parses ok")
@@ -18,7 +18,6 @@
         runner.assert_eq_string(reg.defaults.llm.ollama_url, "http://192.168.168.42:11434", "llm.ollama_url")
         runner.assert_eq_int(reg.defaults.llm.hermes_seed.length(), 3, "llm.hermes_seed has 3 entries")
         runner.assert_eq_int(reg.projects.length(), 1, "one project")
-        runner.assert_eq_bool(reg.projects[0].hermes, true, "project.hermes = true")
     end if
 
     // Schema 2 with [defaults.llm] missing — should default to disabled
diff -r a8ec2564f3fe -r 1bacbaae1c2f tests/test_config_llm_serialize.reef
--- a/tests/test_config_llm_serialize.reef	Fri May 08 02:41:29 2026 +0000
+++ b/tests/test_config_llm_serialize.reef	Fri May 08 13:32:26 2026 +0000
@@ -28,8 +28,7 @@
         profiles:  ["default", "claude-share", "llm-share"],
         created:   "2026-05-06T00:00:00Z",
         last_sync: "",
-        backup:    true,
-        hermes:    true
+        backup:    true
     }
     let reg = config.Registry {
         schema:   2,
@@ -47,7 +46,6 @@
     runner.assert_contains_string(s, "ollama_url = \"http://192.168.168.42:11434\"", "writes llm.ollama_url")
     runner.assert_contains_string(s, "hermes_seed = [",             "writes llm.hermes_seed array")
     runner.assert_contains_string(s, "\".env\"",                    "hermes_seed contains .env")
-    runner.assert_contains_string(s, "hermes = true",               "writes project.hermes")
 
     // Round-trip check
     let r2 = config.parse_registry(s)
@@ -57,7 +55,6 @@
         runner.assert_eq_int(reg2.schema, 2, "round-trip schema")
         runner.assert_eq_bool(reg2.defaults.llm.enabled, true, "round-trip llm.enabled")
         runner.assert_eq_int(reg2.defaults.llm.hermes_seed.length(), 3, "round-trip hermes_seed length")
-        runner.assert_eq_bool(reg2.projects[0].hermes, true, "round-trip project.hermes")
     end if
 
     runner.report()
diff -r a8ec2564f3fe -r 1bacbaae1c2f tests/test_config_migrate_v1.reef
--- a/tests/test_config_migrate_v1.reef	Fri May 08 02:41:29 2026 +0000
+++ b/tests/test_config_migrate_v1.reef	Fri May 08 13:32:26 2026 +0000
@@ -27,7 +27,6 @@
         runner.assert_eq_bool(reg.defaults.llm.enabled, false, "migrated llm.enabled = false")
         runner.assert_eq_int(reg.defaults.llm.hermes_seed.length(), 0, "migrated hermes_seed empty")
         runner.assert_eq_int(reg.projects.length(), 1, "project preserved")
-        runner.assert_eq_bool(reg.projects[0].hermes, false, "migrated project.hermes = false")
         runner.assert_eq_string(reg.projects[0].name, "isurus", "project name preserved")
     end if
 
diff -r a8ec2564f3fe -r 1bacbaae1c2f tests/test_config_mutate.reef
--- a/tests/test_config_mutate.reef	Fri May 08 02:41:29 2026 +0000
+++ b/tests/test_config_mutate.reef	Fri May 08 13:32:26 2026 +0000
@@ -24,8 +24,7 @@
 
     let p1: config.Project = config.Project {
         name: "isurus", repo: "isurus", image: "img",
-        profiles: new [string](0), created: "t", last_sync: "", backup: true,
-        hermes: false
+        profiles: new [string](0), created: "t", last_sync: "", backup: true
     }
     let r1 = config.add_project(reg0, p1)
     runner.assert_eq_bool(rg.is_ok(r1), true, "add new project ok")
diff -r a8ec2564f3fe -r 1bacbaae1c2f tests/test_config_roundtrip.reef
--- a/tests/test_config_roundtrip.reef	Fri May 08 02:41:29 2026 +0000
+++ b/tests/test_config_roundtrip.reef	Fri May 08 13:32:26 2026 +0000
@@ -27,8 +27,7 @@
         profiles:  ["default"],
         created:   "2026-04-28T15:00:00Z",
         last_sync: "",
-        backup:    true,
-        hermes:    false
+        backup:    true
     }
     projects[1] = config.Project {
         name:      "tools",
@@ -37,8 +36,7 @@
         profiles:  ["default", "claude-share"],
         created:   "2026-04-29T10:00:00Z",
         last_sync: "2026-04-29T11:00:00Z",
-        backup:    false,
-        hermes:    false
+        backup:    false
     }
     let reg: config.Registry = config.Registry {
         schema: 1, output: "quiet", defaults: defaults, projects: projects
diff -r a8ec2564f3fe -r 1bacbaae1c2f tests/test_config_serialize.reef
--- a/tests/test_config_serialize.reef	Fri May 08 02:41:29 2026 +0000
+++ b/tests/test_config_serialize.reef	Fri May 08 13:32:26 2026 +0000
@@ -26,8 +26,7 @@
         profiles:  ["default"],
         created:   "2026-04-28T15:00:00Z",
         last_sync: "",
-        backup:    true,
-        hermes:    false
+        backup:    true
     }
     let reg: config.Registry = config.Registry {
         schema:   1,
diff -r a8ec2564f3fe -r 1bacbaae1c2f tests/test_setup_template.reef
--- a/tests/test_setup_template.reef	Fri May 08 02:41:29 2026 +0000
+++ b/tests/test_setup_template.reef	Fri May 08 13:32:26 2026 +0000
@@ -5,7 +5,7 @@
 proc main()
     let runner = new framework.TestRunner()
 
-    let yaml = setup.render_llm_share_template("192.168.168.42", "ctusa", "/home/ctusa/.local/bin/hermes")
+    let yaml = setup.render_llm_share_template("192.168.168.42", "ctusa")
 
     runner.assert_contains_string(yaml, "name: llm-share",                "name line")
     runner.assert_contains_string(yaml, "OLLAMA_HOST",                    "ollama env key")
@@ -14,15 +14,9 @@
     runner.assert_contains_string(yaml, "/home/ctusa/.ollama",            "user path substitution")
     runner.assert_contains_string(yaml, "shift: \"true\"",                "shift opt set")
     runner.assert_contains_string(yaml, "readonly: \"true\"",             "readonly opt on bin")
-    runner.assert_contains_string(yaml, "hermes-bin:",                    "hermes-bin device key")
-    runner.assert_contains_string(yaml, "/home/ctusa/.local/bin/hermes",  "hermes launcher source path")
 
     // No placeholders should remain
     runner.assert_eq_bool(false, setup.template_contains_placeholder(yaml), "no {HOST_LAN_IP} or {USER} left")
 
-    // When hermes_binary is empty, hermes-bin device must be absent
-    let yaml_no_hermes = setup.render_llm_share_template("192.168.168.42", "ctusa", "")
-    runner.assert_eq_bool(false, str.contains(yaml_no_hermes, "hermes-bin:"), "no hermes-bin device when hermes_binary empty")
-
     runner.report()
 end main