OpenClaw to Hermes Migration (12/13) — Three Upstream Issues: Env Vars, Toolset, Hallucinated IDs
This post documents three upstream issues observed during the Hermes migration. Two are runtime and governance issues; one is a hallucinated identifier embedded in a decision document itself. The issues appear independent but share a common pattern: plausible-looking output fills the gap where a fact-verification channel is absent.
What this post covers:
- The $HOME isolation behavior when running agent-style processes under macOS launchd, and the plist key that resolves it
- The empty-toolset scenario produced by the main toolset ∩ sub-agent toolset intersection rule in Claude Code-style sub-agent permission models
- A citation rule that constrains external identifiers (PR/issue numbers) in LLM-authored documents to verifiable forms
All three issues are documented as reported/observed at migration time. Whether they have been merged or resolved is not asserted here.
Issue 1 — $HOME Isolation Under launchd
Symptom
When the Hermes agent is registered as a user daemon via a macOS launchd plist, the $HOME visible inside the agent context does not match the ~ of the user's login shell. Libraries that resolve credentials or config files from ~/.config/... paths therefore search the wrong location.
How It Happens
When launchd launches a LaunchAgent, the default environment is constructed independently of any shell session. Unless EnvironmentVariables in the plist explicitly declares the needed variables, HOME is set to the agent sandbox directory launchd assigns — not the user's home directory. Libraries behave correctly according to their own conventions; the mismatch is in the definition of "home" those conventions assume.
The Fix Key
Inject HOME explicitly into the <key>EnvironmentVariables</key> dictionary in the plist. Omitting this single key reproduces the symptom exactly. The plist template as a whole should not be treated as a validated production artifact; store only the required environment variable keys and retain per-environment verification.
Scope Note
This is not a Hermes-specific issue. It is the general case for any user daemon run under launchd. Any toolchain that depends on user credentials or config files should document this key at the installation level.
Issue 2 — Sub-Agent Intersection Rule Producing an Empty Toolset
Rule Structure
In Hermes, the effective tool permissions for a sub-agent are determined by intersection:
effective_tools(sub) = tools(main) ∩ allowed_tools(sub)
The intent is straightforward: if the main agent's toolset is reduced, sub-agents are constrained to at most that reduced set, propagating permission narrowing systematically.
Observed Side Effect
During a toolset cleanup on the main agent, several sub-agents had their intersection collapse to the empty set. A sub-agent with zero tools does not fail on invocation. The model returns a response — there are simply no tools it can query.
The result is that a "no tools available" state surfaces as plausible text output rather than an explicit error. From the caller's perspective the return value looks normal, so downstream logic may proceed based on that output.
Framing
Permission narrowing is superficially a safety measure, but without also auditing the dependency graph — which sub-agents depend on which tools — that narrowing can operate in a direction that amplifies hallucination. Detailed reproduction steps and mitigations will be covered in a separate post.
Issue 3 — Hallucinated PR Numbers in a Plan Document
The third issue is at the document layer, not the code layer. It illustrates a concrete path by which hallucination enters a decision chain.
Where It Appeared
The Plan document capturing Hermes migration decision flow included a "related upstream issues/PRs" section. Numbers entered during initial drafting were PR #110 and PR #109, with a year of 2024. Single-to-three-digit PR numbers are plausible for small projects and passed surface-level review.
Mismatch with the Actual Repository
The upstream repository Hermes depends on has PR numbers in a much larger range. #110 and #109 do not exist there. An LLM had filled in plausible-sounding numbers and years while organizing the Plan document; the output passed through without verification.
How It Was Caught
During a Plan review, opening the PR links returned 404. A separate sub-agent with browser access then queried the repository's PR list directly and recovered three numbers that appeared to address the same topics. These were incorporated into the revised Plan.
Corrected Numbers
- #12497 (unconfirmed) — report related to the launchd HOME environment issue
- #12495 (unconfirmed) — report related to the sub-agent intersection rule side effect
- #12494 (unconfirmed) — report related to the hallucinated identifier case itself
These three are from a source presumed to be the NousResearch/hermes-agent repository. The sub-agent's work log did not retain the source repository URL, so the repository cannot be cited with certainty. The five-digit PR range is consistent with the accumulated PR volume of active OSS projects. Both this post and the Plan maintain "verification required" on these numbers; re-confirmation of repository and number is required before any external citation.
Note: PR #12497, #12495, and #12494 are references in the harness git repository, not in the Hermes or OpenClaw repositories.
Structural Implication
Code hallucinations are substantially blocked by build and test pipelines. Hallucinations in decision documents, however, are re-anchored by downstream decisions that cite them — the deeper they are trusted, the more firmly they are embedded. If the next decision is built on the non-existent PR #110's supposed discussion, the hallucination accumulates in the decision chain rather than in the code.
This is the same pattern as Issue 2: plausible output fills the gap where a fact-verification channel is absent.
Applicable Pattern — Cite External Identifiers Only in Verifiable Form
A rule applicable to Plan documents, retrospectives, and migration notes when citing PRs or issues from external repositories.
- Do not cite identifiers in isolation. Use
org/repo#12497or a full URL as the default form instead of bare#12497. Where possible, include a verifiable secondary identifier such as a merge commit SHA. - Log the source repository URL alongside the retrieval. When tasking a sub-agent with retrieving external identifiers, instruct it to record which repository it queried. If only the number comes back, the repository context is lost — as happened here.
- Gate LLM-populated identifiers behind a link-open check. In any document authoring workflow, open each external identifier link at least once. A single 404 blocks a single hallucination.
- Leave unconfirmed references as prose, not numbers. A statement like "a related PR is presumed to exist" should remain text without a number. If a number cannot be confirmed, mark it "verification required". A blank is safer than a hallucination.
The process overhead is minimal. The core fix is treating one link-open + one source-repository log entry as a gate in the document workflow. The absence of that gate in the initial draft is why #110 and #109 survived.
Limitations and Open Questions
- All three issues are documented at the reported/observed stage. Whether they have been merged or incorporated is not asserted here; tracking outcomes are subject to separate updates.
- PR #12497, #12495, and #12494 remain in presumed status regarding the NousResearch/hermes-agent repository. Repository and number re-confirmation is required before formal citation.
- The launchd
HOMEplist key guidance is limited to the core key known to be generally valid on macOS. It is not a validated, copy-paste-ready template for all deployment environments; per-environment verification applies.
This post intentionally preserves the original incorrect numbers #110 and #109 alongside their corrections rather than erasing them. The complete trajectory — hallucination → detection → correction — is available as reference material for other pipelines that may encounter the same trap.
One open question: when the sub-agent intersection rule produces an empty set, should the runtime promote this to an explicit error, or should "zero tools" be treated as a legal state with the caller bearing defensive responsibility? This choice has implications for the entire hallucination-suppression strategy.
Series overview: Series index
๋๊ธ
๋๊ธ ์ฐ๊ธฐ