Skip to content

Two Hermes skills-dir tests fail on native Windows: they assume ~/.hermes, but the resolver documents %LOCALAPPDATA%\hermes #312

Description

@PerryLink

Summary

cargo test --workspace fails on native Windows on main, in two tests in skill_install::harness. Both assert the POSIX ~/.hermes layout, but the resolver deliberately mirrors Hermes's own convention, which uses %LOCALAPPDATA%\hermes on native Windows.

test skill_install::harness::tests::detects_hermes_from_home_layout ... FAILED
test skill_install::harness::tests::skills_dirs_match_harness_spec ... FAILED

test result: FAILED. 341 passed; 2 failed; 1 ignored; 0 measured; 0 filtered out

Because the lib test target fails, cargo stops there and the integration tests never run.

Failure 1 — skills_dirs_match_harness_spec (harness.rs:485)

let home = Path::new("/home/user");          // line 456
...
assert_eq!(
    HarnessId::Hermes.skills_dir_for_home(home),
    PathBuf::from("/home/user/.hermes/skills")
);
left:  "C:\\Users\\zzhdz\\AppData\\Local\\hermes\\skills"
right: "/home/user/.hermes/skills"

Every other assertion in this test passes on Windows. They all reduce to home.join(".<name>").join("skills"), and Path equality compares components, so /home/user\.agents\skills and /home/user/.agents/skills compare equal despite the separator. Hermes is the one entry that does not derive from home on Windows.

Failure 2 — detects_hermes_from_home_layout (harness.rs:564)

let home = tmp.path();
std::fs::create_dir_all(home.join(".hermes")).unwrap();
let report = HarnessId::Hermes.report_for_home(home);
assert!(report.detected);
assert_eq!(report.skills_dir, home.join(".hermes").join("skills"));
left:  "C:\\Users\\zzhdz\\AppData\\Local\\Temp\\.tmpl9hHN5\\.hermes\\skills"   (expected)
right: "C:\\Users\\zzhdz\\AppData\\Local\\hermes\\skills"                       (actual)

Creating .hermes under the temp home has no effect, because on native Windows the resolver does not consult the passed home for this harness at all.

Root cause

hermes_home_for_user_home documents the behaviour it implements, at harness.rs:321-322:

/// Mirrors [`get_hermes_home()`](https://github.com/NousResearch/hermes-agent/blob/main/hermes_constants.py):
/// `HERMES_HOME` when set, else `%LOCALAPPDATA%\hermes` on native Windows, else `~/.hermes`.
fn hermes_home_for_user_home(home: &Path) -> PathBuf {

The branch is selected by the presence of the LOCALAPPDATA environment variable (harness.rs:333), not by cfg!(windows). On Linux LOCALAPPDATA is unset, the lookup falls through to ~/.hermes, and both tests pass. On native Windows LOCALAPPDATA is always set, so the %LOCALAPPDATA%\hermes branch is taken and neither assertion can hold.

So the implementation looks intentional and correct: it mirrors the upstream Hermes constant, and the separate hermes_home_signal_label helper exists specifically to render %LOCALAPPDATA%\hermes as a label, which suggests the Windows branch is a considered part of the design rather than an oversight. The two failing tests do not account for it.

I checked this is not an environment artefact on my machine: HERMES_HOME is unset at Process, User and Machine scope, and %LOCALAPPDATA%\hermes does not exist. The path in the failure is computed by the resolver, not inherited.

Why CI does not catch it

In .github/workflows/ci.yml:

  • cargo test --workspace --locked runs only in the rust job, on ubuntu-latest.
  • The Windows jobs run cargo test with filtered test paths — daemon::ipc::windows::tests, daemon::audit::tests, daemon::lockfile::tests, cli::update::tests, plus specific integration test targets. None of them selects skill_install::harness.

So the Windows CI surface never executes these two tests, and the Linux job cannot see the Windows branch.

Suggested direction

Make the two assertions aware of the documented Windows branch, for example by expecting the %LOCALAPPDATA% path when that variable is set, or by clearing LOCALAPPDATA for the duration of the test the same way hermes_skills_dir_honors_hermes_home_env already handles HERMES_HOME. Both tests already take lock_harness_env(), so the serialisation needed for the second approach is in place.

I am happy to open a PR if you would prefer that to fixing it yourself — I did not want to guess at which of the two repairs matches your intent, since the present assertions may also be encoding an expectation you would rather keep.

Environment

  • Windows 11 (Microsoft Windows 11 家庭版 中文版, build 26200), LOCALAPPDATA set
  • cargo test --workspace --locked
  • rustc 1.98.1 (48a229cea 2026-09-01), x86_64-pc-windows-msvc, stable toolchain per rust-toolchain.toml
  • Fails identically with and without an unrelated local change, i.e. on a clean main (c1e5052)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions