hermes - 💡(How to fix) Fix [Bug]: hermes profile install --force deletes nested distribution-owned files under skills/<category>/hermes-agent/

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Hermes profile distribution currently applies USER_OWNED_EXCLUDE by basename at every directory depth during recursive copy. This correctly protects root-level runtime/user-owned paths, but it also silently drops distribution-owned nested paths that happen to share a protected basename.

The reproduced DEC-009 case is a profile source containing a distribution-owned nested skill directory named:

skills/autonomous-ai-agents/hermes-agent/

Because hermes-agent is in USER_OWNED_EXCLUDE, the nested skill directory is ignored during shutil.copytree(...), even though only the root-level hermes-agent/ runtime path should be excluded.

This reproduces against current upstream HEAD:

729a778af0b3f984b4934361cad3050f6afb79ba

Root Cause

Because hermes-agent is in USER_OWNED_EXCLUDE, the nested skill directory is ignored during shutil.copytree(...), even though only the root-level hermes-agent/ runtime path should be excluded.

Fix Action

Fix / Workaround

FFT local fix / workaround

FFT did not mutate upstream during the evidence-gathering phase. The documented local mitigation path is to avoid the collision shape until the substrate behavior is fixed or explicitly accepted as a permanent local dependency.

The DEC-009 retirement draft records the workaround/mitigation criteria as:

Code Example

skills/autonomous-ai-agents/hermes-agent/

---

729a778af0b3f984b4934361cad3050f6afb79ba

---

729a778af0b3f984b4934361cad3050f6afb79ba

---

hermes-agent/
   skills/autonomous-ai-agents/hermes-agent/SKILL.md

---

hermes-agent/
   skills/autonomous-ai-agents/hermes-agent/SKILL.md

---

hermes-agent/                 # absent from target

---

skills/autonomous-ai-agents/hermes-agent/SKILL.md   # present in target

---

{
  "target": "/var/folders/2g/y0rn21395y71tj0ys_f8rx0c0000h0/T/dec009_home_zolxe0pg/.hermes/profiles/dec009-repro-target",
  "root_protected_marker_exists": false,
  "nested_same_basename_skill_exists": false,
  "defect_reproduced": true,
  "expected_correct_behavior": "root marker absent and nested skill present",
  "actual_behavior": "root_exists=False; nested_exists=False"
}

---

for entry in staged.iterdir():
    name = entry.name

    if name in USER_OWNED_EXCLUDE:
        continue

---

shutil.copytree(
    entry,
    dest,
    ignore=lambda d, names: [n for n in names if n in USER_OWNED_EXCLUDE],
)

---

/Users/rfais370/.hermes/profiles/xo/local/option-d-phase-0-evidence.md
/Users/rfais370/.hermes/profiles/xo/local/dec-009-retirement-criteria-draft.md

---

f3d37e414038d29f7eed3d0ef997e58567c32d3fc29969eda109d75a1c832e96

---

Disposable workdir: /Users/rfais370/.hermes/profiles/xo/local/disposable-option-d-repro-upstream-head-20260523T032357Z
Disposable clone: /Users/rfais370/.hermes/profiles/xo/local/disposable-option-d-repro-upstream-head-20260523T032357Z/hermes-agent-upstream-head
Expected upstream HEAD: 729a778af0b3f984b4934361cad3050f6afb79ba
Actual checked-out HEAD: 729a778af0b3f984b4934361cad3050f6afb79ba
Reproduction exit code: 0

---

profiles/csm/skills/autonomous-ai-agents/hermes-agent
  profiles/xo/skills/autonomous-ai-agents/hermes-agent

---

profiles/csm/skills/autonomous-ai-agents/hermes-agent

---

mission-ctrl, claude-mcp, S-1 through S-9

---

hermes-agent/      # root staged path does not copy into target

---

skills/autonomous-ai-agents/hermes-agent/SKILL.md
RAW_BUFFERClick to expand / collapse

Root-level user-owned exclusion drops distribution-owned nested same-basename paths during profile distribution

Summary

Hermes profile distribution currently applies USER_OWNED_EXCLUDE by basename at every directory depth during recursive copy. This correctly protects root-level runtime/user-owned paths, but it also silently drops distribution-owned nested paths that happen to share a protected basename.

The reproduced DEC-009 case is a profile source containing a distribution-owned nested skill directory named:

skills/autonomous-ai-agents/hermes-agent/

Because hermes-agent is in USER_OWNED_EXCLUDE, the nested skill directory is ignored during shutil.copytree(...), even though only the root-level hermes-agent/ runtime path should be excluded.

This reproduces against current upstream HEAD:

729a778af0b3f984b4934361cad3050f6afb79ba

Steps to reproduce

  1. Use upstream NousResearch/hermes-agent at commit:

    729a778af0b3f984b4934361cad3050f6afb79ba
  2. Create or stage a profile distribution payload containing both:

    hermes-agent/
    skills/autonomous-ai-agents/hermes-agent/SKILL.md

    The root hermes-agent/ represents a user/runtime-owned path that should not be copied into the target profile. The nested skills/autonomous-ai-agents/hermes-agent/ path represents distribution-owned content and should be copied.

  3. Run the profile distribution copy path that invokes _copy_dist_payload(...) in hermes_cli/profile_distribution.py.

  4. Inspect the target profile for:

    hermes-agent/
    skills/autonomous-ai-agents/hermes-agent/SKILL.md

Expected behavior

  • Root-level protected/user-owned path is excluded:

    hermes-agent/                 # absent from target
  • Distribution-owned nested same-basename path is preserved:

    skills/autonomous-ai-agents/hermes-agent/SKILL.md   # present in target

In short: user-owned exclusions should be root-scoped where the protected path is root-owned, not applied as basename exclusions at all descendant depths.

Actual behavior

Both the root protected path and the nested distribution-owned same-basename path are absent from the target profile.

Reproduction output from the evidence package:

{
  "target": "/var/folders/2g/y0rn21395y71tj0ys_f8rx0c0000h0/T/dec009_home_zolxe0pg/.hermes/profiles/dec009-repro-target",
  "root_protected_marker_exists": false,
  "nested_same_basename_skill_exists": false,
  "defect_reproduced": true,
  "expected_correct_behavior": "root marker absent and nested skill present",
  "actual_behavior": "root_exists=False; nested_exists=False"
}

Relevant code references

Evidence points to hermes_cli/profile_distribution.py, _copy_dist_payload(...), upstream lines 530-565 at commit 729a778af0b3f984b4934361cad3050f6afb79ba.

The top-level skip is correct for root entries:

for entry in staged.iterdir():
    name = entry.name

    if name in USER_OWNED_EXCLUDE:
        continue

The recursive copy ignore lambda appears to cause the defect because it filters by basename at every depth:

shutil.copytree(
    entry,
    dest,
    ignore=lambda d, names: [n for n in names if n in USER_OWNED_EXCLUDE],
)

Evidence interpretation:

  • USER_OWNED_EXCLUDE includes protected names such as auth.json, .env, memories, sessions, local, and hermes-agent.
  • Current tests assert that those names are present in USER_OWNED_EXCLUDE.
  • No upstream test was found in the evidence package that verifies preservation of nested distribution-owned paths with the same basename as a root-level protected path.

Evidence package references

Local evidence artifacts used to draft this issue:

/Users/rfais370/.hermes/profiles/xo/local/option-d-phase-0-evidence.md
/Users/rfais370/.hermes/profiles/xo/local/dec-009-retirement-criteria-draft.md

Evidence package SHA-256 noted in the retirement draft:

f3d37e414038d29f7eed3d0ef997e58567c32d3fc29969eda109d75a1c832e96

Disposable reproduction context from the evidence:

Disposable workdir: /Users/rfais370/.hermes/profiles/xo/local/disposable-option-d-repro-upstream-head-20260523T032357Z
Disposable clone: /Users/rfais370/.hermes/profiles/xo/local/disposable-option-d-repro-upstream-head-20260523T032357Z/hermes-agent-upstream-head
Expected upstream HEAD: 729a778af0b3f984b4934361cad3050f6afb79ba
Actual checked-out HEAD: 729a778af0b3f984b4934361cad3050f6afb79ba
Reproduction exit code: 0

Local impact observed by FFT

FFT profile distribution sources contained nested distribution-owned paths named hermes-agent under profile skill trees. The evidence package confirmed:

  • Collision profile count: 2 profiles — csm, xo

  • Collision inventory: 56 total paths when counting collision roots and descendants

  • Collision shape:

    profiles/csm/skills/autonomous-ai-agents/hermes-agent
    profiles/xo/skills/autonomous-ai-agents/hermes-agent

The affected nested content is distribution-owned profile skill material, not root runtime state.

FFT local fix / workaround

FFT did not mutate upstream during the evidence-gathering phase. The documented local mitigation path is to avoid the collision shape until the substrate behavior is fixed or explicitly accepted as a permanent local dependency.

The DEC-009 retirement draft records the workaround/mitigation criteria as:

  • Restructure CSM source so it no longer contains:

    profiles/csm/skills/autonomous-ai-agents/hermes-agent

    unless the substrate is already fixed and regression-tested.

  • Remove, restructure, or regression-test the XO collision before retiring DEC-009 controls. XO carries 55 of the 56 collision paths and is the primary regression target.

  • Keep eleven held migrations paused until a separate retirement/release gate is authorized:

    mission-ctrl, claude-mcp, S-1 through S-9

Suggested fix direction

Scope root-owned exclusions to the root of the staged profile payload, while preserving distribution-owned nested directories/files whose basenames match entries in USER_OWNED_EXCLUDE.

Minimum regression coverage should include:

  1. Root protected path remains excluded:

    hermes-agent/      # root staged path does not copy into target
  2. Nested same-basename distribution path is preserved:

    skills/autonomous-ai-agents/hermes-agent/SKILL.md
  3. Existing user config remains preserved unless force-config behavior is explicitly invoked.

  4. Existing runtime/user-owned target paths remain untouched, including auth.json, .env, sessions, memories, local caches, and root hermes-agent.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

  • Root-level protected/user-owned path is excluded:

    hermes-agent/                 # absent from target
  • Distribution-owned nested same-basename path is preserved:

    skills/autonomous-ai-agents/hermes-agent/SKILL.md   # present in target

In short: user-owned exclusions should be root-scoped where the protected path is root-owned, not applied as basename exclusions at all descendant depths.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Bug]: hermes profile install --force deletes nested distribution-owned files under skills/<category>/hermes-agent/