[GH-ISSUE #3939] [Bug] Nested HERMES_HOME roots use another root's active profile during setup #1548

Open
opened 2026-10-16 21:06:59 +00:00 by bsakaguchi · 5 comments
Owner

Originally created by @coygeek on GitHub (Oct 15, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3939

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Setup, packaging, or upgrade

Observed behavior

For the first variant, The baseline CLI returned exit status 1 and status: "needs_human", with empty plannedActions and appliedActions. The Hermes registration was invalid at $case_dir/.hermes/sub/profiles/coder. Its remediation said Saved Hermes profile "coder" is missing, deleted, or has no profile identity. The selected other profile exists and has a configuration file.

For the second variant, The baseline CLI returned exit status 0 and status: "planned". The configure_client:hermes action targeted $case_dir/.hermes/sub/profiles/coder/config.yaml. The install_skill:hermes action targeted $case_dir/.hermes/sub/profiles/coder/skills/reverse-engineer-anything. Stderr was empty for both runs. These were dry runs, so the evidence establishes refusal and incorrect planned destinations, not an applied wrong-target write.

Reproduction

From the baseline checkout with dependencies installed and the compiled runtime available, run the following commands. Use the pinned Node.js runtime. The commands isolate all configuration in a disposable home and request dry runs only.

case_dir=$(mktemp -d)
mkdir -p "$case_dir/.hermes/profiles/coder" "$case_dir/.hermes/sub/profiles/other"
printf 'coder' > "$case_dir/.hermes/active_profile"
printf 'model: default-root\n' > "$case_dir/.hermes/profiles/coder/config.yaml"
printf 'other' > "$case_dir/.hermes/sub/active_profile"
printf 'model: nested-root\n' > "$case_dir/.hermes/sub/profiles/other/config.yaml"
env -i PATH="$PATH" HOME="$case_dir" HERMES_HOME="$case_dir/.hermes/sub" node scripts/rea.mjs setup --client hermes --dry-run --json

rm "$case_dir/.hermes/sub/active_profile"
mkdir -p "$case_dir/.hermes/sub/profiles/coder"
printf 'model: nested-coder\n' > "$case_dir/.hermes/sub/profiles/coder/config.yaml"
env -i PATH="$PATH" HOME="$case_dir" HERMES_HOME="$case_dir/.hermes/sub" node scripts/rea.mjs setup --client hermes --dry-run --json
rm -rf "$case_dir"

These fixture states were executed through the supported CLI once each using a Python subprocess driver with an isolated environment. The shell commands express the same inputs and invocations. The first command is expected to fail on the affected baseline; continue to the second variant.

Expected behavior

The documented Hermes setup contract resolves configuration from the explicit override root and follows that root's active-profile selection. For the first variant, setup should return planned and target $case_dir/.hermes/sub/profiles/other/config.yaml, with the skill beneath that same profile. For the second variant, the override root has no named selection, so setup should target $case_dir/.hermes/sub/config.yaml and its root skill directory. The unrelated default home's coder selection should not determine either plan.

Impact and scope

A valid nested override root either blocks setup or plans registration and skill installation in an unselected profile. Both dry-run variants reproduced once. No installation was applied, and no wrong-target write or data loss was observed. Controls using an override outside the default home or an explicit nested profiles/other override selected the intended destinations and provide demonstrated workarounds.

REA version

rea 6.5.0 at source commit 4692bb3330

Runtime and platform

Node 24.18.0; macOS arm64

Target and artifact context

Disposable local Hermes configuration roots and profiles. Dry-run setup only; no analysis provider or target artifact.

Interface

Package or setup

Earliest failure stage

Setup or provider admission

Relevant output or evidence

First variant: exit 1, status: needs_human, empty planned/applied actions, invalid registration at the nested profiles/coder and remediation Saved Hermes profile "coder" is missing, deleted, or has no profile identity. The nested root selected the existing other profile.

Second variant: exit 0, status: planned, configuration and skill destinations under the nested profiles/coder, despite the nested root having no active-profile selection. Both stderr streams were empty.

Expected source: docs/client-configuration.md:265-279 documents override-root resolution and selection, explicit named-profile overrides, and shared configuration/skill destinations; docs/cli.md:366 documents successful dry runs.

Failure source: src/application/HermesClientHome.ts:118-149 and src/application/SupportedClients.ts:648-655. These files are unchanged at current upstream main 22ec8bdb2f7d4626587f23b1eed4a207df600386; intervening cited documentation and launcher changes concern output flags.

Typed outcome or recovery expected

Rerun both fixture variants. The first must return planned, exit 0, and target the nested root's selected other profile for configuration and skills. The second must return planned, exit 0, and target the nested root itself. Retain controls for a default home selecting coder, a separate override root selecting other, and an explicit named-profile override. These criteria describe the required behavior; no repaired build has been tested.

Analysis or suspected cause

Source-derived: for an override physically beneath the default home, HermesClientHome.ts:118-149 reads the default home's selection file, then resolves that profile name beneath the override root. Explicit profile directories bypass this selection branch.

Additional context

Related closed #1521 and its fix #1528 addressed ignoring the default home's sticky profile. The default-home control works here; the remaining failure is the read-root/profile-root mismatch for nested overrides. Real Hermes CLI comparison, doctor, uninstall and applied installation were not executed.

Originally created by @coygeek on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3939 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Setup, packaging, or upgrade ### Observed behavior For the first variant, The baseline CLI returned exit status 1 and `status: "needs_human"`, with empty `plannedActions` and `appliedActions`. The Hermes registration was `invalid` at `$case_dir/.hermes/sub/profiles/coder`. Its remediation said `Saved Hermes profile "coder" is missing, deleted, or has no profile identity.` The selected `other` profile exists and has a configuration file. For the second variant, The baseline CLI returned exit status 0 and `status: "planned"`. The `configure_client:hermes` action targeted `$case_dir/.hermes/sub/profiles/coder/config.yaml`. The `install_skill:hermes` action targeted `$case_dir/.hermes/sub/profiles/coder/skills/reverse-engineer-anything`. Stderr was empty for both runs. These were dry runs, so the evidence establishes refusal and incorrect planned destinations, not an applied wrong-target write. ### Reproduction From the baseline checkout with dependencies installed and the compiled runtime available, run the following commands. Use the pinned Node.js runtime. The commands isolate all configuration in a disposable home and request dry runs only. ```sh case_dir=$(mktemp -d) mkdir -p "$case_dir/.hermes/profiles/coder" "$case_dir/.hermes/sub/profiles/other" printf 'coder' > "$case_dir/.hermes/active_profile" printf 'model: default-root\n' > "$case_dir/.hermes/profiles/coder/config.yaml" printf 'other' > "$case_dir/.hermes/sub/active_profile" printf 'model: nested-root\n' > "$case_dir/.hermes/sub/profiles/other/config.yaml" env -i PATH="$PATH" HOME="$case_dir" HERMES_HOME="$case_dir/.hermes/sub" node scripts/rea.mjs setup --client hermes --dry-run --json rm "$case_dir/.hermes/sub/active_profile" mkdir -p "$case_dir/.hermes/sub/profiles/coder" printf 'model: nested-coder\n' > "$case_dir/.hermes/sub/profiles/coder/config.yaml" env -i PATH="$PATH" HOME="$case_dir" HERMES_HOME="$case_dir/.hermes/sub" node scripts/rea.mjs setup --client hermes --dry-run --json rm -rf "$case_dir" ``` These fixture states were executed through the supported CLI once each using a Python subprocess driver with an isolated environment. The shell commands express the same inputs and invocations. The first command is expected to fail on the affected baseline; continue to the second variant. ### Expected behavior The documented Hermes setup contract resolves configuration from the explicit override root and follows that root's active-profile selection. For the first variant, setup should return `planned` and target `$case_dir/.hermes/sub/profiles/other/config.yaml`, with the skill beneath that same profile. For the second variant, the override root has no named selection, so setup should target `$case_dir/.hermes/sub/config.yaml` and its root skill directory. The unrelated default home's `coder` selection should not determine either plan. ### Impact and scope A valid nested override root either blocks setup or plans registration and skill installation in an unselected profile. Both dry-run variants reproduced once. No installation was applied, and no wrong-target write or data loss was observed. Controls using an override outside the default home or an explicit nested `profiles/other` override selected the intended destinations and provide demonstrated workarounds. ### REA version rea 6.5.0 at source commit 4692bb33300c7cb77b095b6cc0c33da861c1b860 ### Runtime and platform Node 24.18.0; macOS arm64 ### Target and artifact context Disposable local Hermes configuration roots and profiles. Dry-run setup only; no analysis provider or target artifact. ### Interface Package or setup ### Earliest failure stage Setup or provider admission ### Relevant output or evidence First variant: exit 1, `status: needs_human`, empty planned/applied actions, invalid registration at the nested `profiles/coder` and remediation `Saved Hermes profile "coder" is missing, deleted, or has no profile identity.` The nested root selected the existing `other` profile. Second variant: exit 0, `status: planned`, configuration and skill destinations under the nested `profiles/coder`, despite the nested root having no active-profile selection. Both stderr streams were empty. Expected source: `docs/client-configuration.md:265-279` documents override-root resolution and selection, explicit named-profile overrides, and shared configuration/skill destinations; `docs/cli.md:366` documents successful dry runs. Failure source: `src/application/HermesClientHome.ts:118-149` and `src/application/SupportedClients.ts:648-655`. These files are unchanged at current upstream main `22ec8bdb2f7d4626587f23b1eed4a207df600386`; intervening cited documentation and launcher changes concern output flags. ### Typed outcome or recovery expected Rerun both fixture variants. The first must return `planned`, exit 0, and target the nested root's selected `other` profile for configuration and skills. The second must return `planned`, exit 0, and target the nested root itself. Retain controls for a default home selecting `coder`, a separate override root selecting `other`, and an explicit named-profile override. These criteria describe the required behavior; no repaired build has been tested. ### Analysis or suspected cause Source-derived: for an override physically beneath the default home, `HermesClientHome.ts:118-149` reads the default home's selection file, then resolves that profile name beneath the override root. Explicit profile directories bypass this selection branch. ### Additional context Related closed #1521 and its fix #1528 addressed ignoring the default home's sticky profile. The default-home control works here; the remaining failure is the read-root/profile-root mismatch for nested overrides. Real Hermes CLI comparison, doctor, uninstall and applied installation were not executed.
Author
Owner

@XinwangTao commented on GitHub (Oct 15, 2026):

Confirmed: when HERMES_HOME points at a nested root, setup resolves the active profile against the wrong root (the outer one), so planned registration targets a profile the nested root never selected. Since the nested root's own active_profile exists and differs, the resolution should be anchored strictly at HERMES_HOME rather than inheriting the outer root's state. Good that this was caught as a dry-run plan rather than an applied write.

<!-- gh-comment-id:6186112207 --> @XinwangTao commented on GitHub (Oct 15, 2026): Confirmed: when HERMES_HOME points at a nested root, setup resolves the active profile against the wrong root (the outer one), so planned registration targets a profile the nested root never selected. Since the nested root's own active_profile exists and differs, the resolution should be anchored strictly at HERMES_HOME rather than inheriting the outer root's state. Good that this was caught as a dry-run plan rather than an applied write.
Author
Owner

@hichipli commented on GitHub (Oct 15, 2026):

Thanks for spelling out both cases. I checked this against Hermes upstream before changing the resolver and found that the current behavior appears to preserve Hermes CLI compatibility.

At NousResearch/hermes-agent 73b9f2af:

I executed those upstream root/profile resolver functions in isolated fixtures (extracted from the pinned source, without installing or launching Hermes), and compared them with actual REA CLI dry runs on macOS. Both refuse a missing nested coder profile, select sub/profiles/coder when it exists, and use sub itself when the outer selection is absent. The nested active_profile=other does not change those outcomes.

The guide’s phrase “the root’s active_profile” is ambiguous for this layout. PR #4145 clarifies the documentation and adds CLI acceptance coverage for these cases, preserving the compatible resolver behavior. An explicit HERMES_HOME ending in profiles/other remains the direct way to select that profile. Happy to revisit the behavior if a supported Hermes version demonstrates different routing.

PR #4145 is now ready for review. It clarifies the lookup without changing the resolver and adds all three nested-home acceptance controls. The Hermes CLI acceptance suite passes all 17 tests; npm run check and npm run docs:check also pass. The human owner has confirmed review of the final change and validation evidence.

Review follow-up: the guide now qualifies the symlinked-default case when the nested override does not exist yet. An actual CLI probe confirmed that current limitation; the documented pre-creation workaround is covered by an additional acceptance test. No resolver behavior is changed.

<!-- gh-comment-id:6194286270 --> @hichipli commented on GitHub (Oct 15, 2026): Thanks for spelling out both cases. I checked this against Hermes upstream before changing the resolver and found that the current behavior appears to preserve Hermes CLI compatibility. At NousResearch/hermes-agent `73b9f2af`: - [`_hermes_root_of`](https://github.com/NousResearch/hermes-agent/blob/73b9f2af7b87a619f42396710c332be04353aef2/hermes_constants.py#L66-L74) selects the platform default root for a home physically beneath it. - [`_apply_profile_override`](https://github.com/NousResearch/hermes-agent/blob/73b9f2af7b87a619f42396710c332be04353aef2/hermes_cli/main.py#L612-L632) reads that root’s `active_profile`. - [`resolve_profile_env`](https://github.com/NousResearch/hermes-agent/blob/73b9f2af7b87a619f42396710c332be04353aef2/hermes_cli/profiles.py#L2443-L2460) resolves the selected name beneath the configured `HERMES_HOME` spelling. I executed those upstream root/profile resolver functions in isolated fixtures (extracted from the pinned source, without installing or launching Hermes), and compared them with actual REA CLI dry runs on macOS. Both refuse a missing nested `coder` profile, select `sub/profiles/coder` when it exists, and use `sub` itself when the outer selection is absent. The nested `active_profile=other` does not change those outcomes. The guide’s phrase “the root’s active_profile” is ambiguous for this layout. PR #4145 clarifies the documentation and adds CLI acceptance coverage for these cases, preserving the compatible resolver behavior. An explicit `HERMES_HOME` ending in `profiles/other` remains the direct way to select that profile. Happy to revisit the behavior if a supported Hermes version demonstrates different routing. PR #4145 is now ready for review. It clarifies the lookup without changing the resolver and adds all three nested-home acceptance controls. The Hermes CLI acceptance suite passes all 17 tests; `npm run check` and `npm run docs:check` also pass. The human owner has confirmed review of the final change and validation evidence. Review follow-up: the guide now qualifies the symlinked-default case when the nested override does not exist yet. An actual CLI probe confirmed that current limitation; the documented pre-creation workaround is covered by an additional acceptance test. No resolver behavior is changed.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

The current source changes the assessment of this report. At d87e73f, the resolver reads the native home's sticky selection for a nested override and now resolves the selected profile beneath that same native home. It no longer combines the outer selection with a nested profile destination as the original reproduction described.

The guide explicitly adopts that native-root rule. A current regression supplies conflicting native/nested selections and asserts the native profiles/coder destination for both resolution and skills. Configuration and skill destinations share this resolver. These are source/test observations; I have not executed current CLI dry runs or independently compared a supported Hermes release. The older compatibility results in the discussion describe a different implementation.

What remains is contract adjudication and verification, rather than a demonstrated fresh runtime defect. If the documented compatibility rule is accepted, rerun both original dry-run fixtures: with a live native coder selection, both should plan configuration and skills under the native profile. Also check that removing the native selection falls back to the configured override and an explicit profiles/other override bypasses sticky selection. Matching supported Hermes behavior would support closure as superseded. Requiring the nested root's own selection would instead change the current contract and needs an explicit compatibility decision.

<!-- gh-comment-id:6380888562 --> @coygeek commented on GitHub (Oct 24, 2026): The current source changes the assessment of this report. At `d87e73f`, the [resolver](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/HermesClientHome.ts#L140-L179) reads the native home's sticky selection for a nested override and now resolves the selected profile beneath that same native home. It no longer combines the outer selection with a nested profile destination as the original reproduction described. The [guide](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/docs/client-configuration.md#L411-L423) explicitly adopts that native-root rule. A [current regression](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/boundary/filesystem/skillSelectionProvenance.test.ts#L16-L32) supplies conflicting native/nested selections and asserts the native `profiles/coder` destination for both resolution and skills. [Configuration and skill destinations](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/SupportedClients.ts#L648-L655) share this resolver. These are source/test observations; I have not executed current CLI dry runs or independently compared a supported Hermes release. The older compatibility results in the discussion describe a different implementation. What remains is contract adjudication and verification, rather than a demonstrated fresh runtime defect. If the documented compatibility rule is accepted, rerun both original dry-run fixtures: with a live native `coder` selection, both should plan configuration and skills under the native profile. Also check that removing the native selection falls back to the configured override and an explicit `profiles/other` override bypasses sticky selection. Matching supported Hermes behavior would support closure as superseded. Requiring the nested root's own selection would instead change the current contract and needs an explicit compatibility decision.
Author
Owner

@hichipli commented on GitHub (Oct 24, 2026):

Thanks @coygeek — I reran the original layouts on main 9140deae6. REA now selects the native profiles/coder when it exists, and refuses when only the nested coder exists. No native selection falls back to the override; an explicit profile bypasses sticky selection.

There is still a compatibility difference with the guide's pinned Hermes 748c79a1: profile_root_for_env_home and resolve_profile_env use the configured override for the profile destination. I executed those original resolver functions in isolated fixtures, alongside actual REA CLI dry runs:

Live coder profile Hermes resolver REA preview
Native only missing profile native profile
Nested only nested profile needs_human
Both nested profile native profile

The no-selection and explicit-profile controls agree. This checks extracted resolver functions, not a full installed Hermes CLI.

I've updated #4145 to describe current REA routing, flag this difference and document the direct-profile workaround, without changing the resolver. Could a maintainer confirm whether native-root routing is intentional or REA should follow that Hermes version? I'd leave this issue open until that is settled.

<!-- gh-comment-id:6390702658 --> @hichipli commented on GitHub (Oct 24, 2026): Thanks @coygeek — I reran the original layouts on main `9140deae6`. REA now selects the native `profiles/coder` when it exists, and refuses when only the nested `coder` exists. No native selection falls back to the override; an explicit profile bypasses sticky selection. There is still a compatibility difference with the guide's pinned Hermes `748c79a1`: [`profile_root_for_env_home` and `resolve_profile_env`](https://github.com/NousResearch/hermes-agent/blob/748c79a1c6873124d84071cdabb4062a75913e1f/hermes_cli/profiles.py#L2431-L2461) use the configured override for the profile destination. I executed those original resolver functions in isolated fixtures, alongside actual REA CLI dry runs: | Live `coder` profile | Hermes resolver | REA preview | | --- | --- | --- | | Native only | missing profile | native profile | | Nested only | nested profile | `needs_human` | | Both | nested profile | native profile | The no-selection and explicit-profile controls agree. This checks extracted resolver functions, not a full installed Hermes CLI. I've updated #4145 to describe current REA routing, flag this difference and document the direct-profile workaround, without changing the resolver. Could a maintainer confirm whether native-root routing is intentional or REA should follow that Hermes version? I'd leave this issue open until that is settled.
Author
Owner

@morluto commented on GitHub (Oct 26, 2026):

Thanks for the nested-home reproductions. Main has since changed the resolver in d1fbebb57: it now reads the native sticky selection and resolves the selected profile from the same native root, rather than mixing the native selection with a nested profile root. That changes the originally reported wrong destination, but #4145 documents a remaining difference from the pinned Hermes resolver. I am keeping this open for the upstream-compatibility decision and verification; I did not run Hermes or setup in this triage.

<!-- gh-comment-id:6428612261 --> @morluto commented on GitHub (Oct 26, 2026): Thanks for the nested-home reproductions. Main has since changed the resolver in d1fbebb57: it now reads the native sticky selection and resolves the selected profile from the same native root, rather than mixing the native selection with a nested profile root. That changes the originally reported wrong destination, but #4145 documents a remaining difference from the pinned Hermes resolver. I am keeping this open for the upstream-compatibility decision and verification; I did not run Hermes or setup in this triage.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1548
No description provided.