hermes - ✅(Solved) Fix [Bug]: atomic writes to HERMES_HOME files replace symlinked targets (config.yaml/SOUL.md) [1 pull requests, 2 comments, 3 participants]

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…
GitHub stats
NousResearch/hermes-agent#16743Fetched 2026-04-28 06:51:07
View on GitHub
Comments
2
Participants
3
Timeline
7
Reactions
0
Author
Timeline (top)
labeled ×4commented ×2cross-referenced ×1

Root Cause

Managed/shared deployments often separate runtime home from package canon using symlinks. Replacing symlinks breaks that contract and causes config/persona drift unless operators add periodic relink scripts.

Fix Action

Fixed

PR fix notes

PR #16777: fix: preserve symlinks during atomic file writes (#16743)

Description (problem / solution / changelog)

Summary

os.replace(tmp, path) atomically replaces whatever exists at path. When path is a symlink, the symlink itself is replaced with a regular file — silently detaching the user's git-tracked source-of-truth.

This PR resolves symlinks via os.path.realpath() before os.replace(), so the real file is overwritten in-place while the symlink survives.

Fix Details

Root cause: atomic_json_write(), atomic_yaml_write(), and 17 other os.replace() call sites use the symlink path directly, causing os.replace to replace the symlink itself.

Fix: Added os.path.islink() + os.path.realpath() resolution before every os.replace() call.

Files changed (7):

  • utils.pyatomic_json_write() and atomic_yaml_write() (covers save_config, save_config_value)
  • hermes_cli/config.py — env sanitizer, save_env_value(), remove_env_value()
  • tools/skill_manager_tool.py_atomic_write_text() (SOUL.md, skill files)
  • tools/memory_tool.py — memory file writes
  • tools/skills_sync.py — manifest writes
  • cron/jobs.py — job state + output file writes
  • agent/shell_hooks.py — hook file writes

Safety

  • os.path.islink() returns False for non-existent paths → first-time creates are unaffected
  • os.path.realpath() on non-symlinks returns the path unchanged → idempotent
  • Temp file is created in the same directory via mkstemp(dir=path.parent) → same filesystem as real_path → atomic replace still works
  • No cross-filesystem edge case introduced

Testing

python3 -c "import ast; ast.parse(open('utils.py').read())"  # syntax OK for all 7 files

Manual test:

mkdir /tmp/test-symlink
echo "original" > /tmp/test-symlink/real.yaml
ln -s /tmp/test-symlink/real.yaml /tmp/test-symlink/link.yaml
# After fix: link.yaml remains a symlink, real.yaml has new content

Fixes #16743

Changed files

  • agent/shell_hooks.py (modified, +3/-1)
  • cron/jobs.py (modified, +6/-2)
  • hermes_cli/config.py (modified, +9/-3)
  • tools/memory_tool.py (modified, +4/-1)
  • tools/skill_manager_tool.py (modified, +3/-1)
  • tools/skills_sync.py (modified, +3/-1)
  • utils.py (modified, +10/-4)

Code Example

ln -s /path/to/tracked/config.yaml ~/.hermes/config.yaml
RAW_BUFFERClick to expand / collapse

Bug Description

When HERMES_HOME contains symlinks (for example config.yaml symlinked to a git-tracked profile package), Hermes writes to that path via atomic replace and the symlink is replaced by a regular file.

This causes managed package setups to drift: runtime keeps working, but git-tracked source-of-truth is silently detached.

Steps to Reproduce

  1. Create ~/.hermes/config.yaml as a symlink to another file:
ln -s /path/to/tracked/config.yaml ~/.hermes/config.yaml
  1. Trigger any Hermes path that writes config (e.g. gateway /sethome, or settings write path).
  2. Inspect ~/.hermes/config.yaml.

Expected Behavior

If the target path is a symlink, writes should preserve link semantics (or provide an explicit supported mode for managed symlink deployments).

Actual Behavior

Symlink is replaced with a regular file at ~/.hermes/config.yaml.

Affected Component

Config + gateway write paths using atomic replace helpers.

Related

  • #6447 (/sethome persists into yaml path)
  • #10581 (home-channel prompt/source mismatch)
  • #14181 (atomic writes and metadata/permissions drift in managed installs)

Why this matters

Managed/shared deployments often separate runtime home from package canon using symlinks. Replacing symlinks breaks that contract and causes config/persona drift unless operators add periodic relink scripts.

extent analysis

TL;DR

Preserve symlink semantics when writing to HERMES_HOME by modifying the atomic replace helpers to handle symlinks correctly.

Guidance

  • Identify the atomic replace helpers used in the affected components (Config and gateway write paths) and modify them to check if the target path is a symlink before writing.
  • Consider adding an explicit supported mode for managed symlink deployments to handle such scenarios.
  • Inspect the related issues (#6447, #10581, #14181) to ensure the solution does not introduce similar problems.
  • Test the modified atomic replace helpers with symlinks to verify the fix.

Example

# Example of how to check if a path is a symlink in bash
if [ -L "~/.hermes/config.yaml" ]; then
  # Handle symlink case
fi

Notes

The solution may require changes to the underlying file system operations, and care should be taken to ensure that the fix does not introduce security vulnerabilities or break existing functionality.

Recommendation

Apply workaround: Modify the atomic replace helpers to handle symlinks correctly, as this is a specific fix for the identified issue and does not require upgrading to a potentially non-existent fixed version.

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…

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 - ✅(Solved) Fix [Bug]: atomic writes to HERMES_HOME files replace symlinked targets (config.yaml/SOUL.md) [1 pull requests, 2 comments, 3 participants]