Skip to content

fix(cron): resolve dynamic paths at runtime for profile isolation (#4707)#4729

Open
devorun wants to merge 2 commits into
NousResearch:mainfrom
devorun:patch-30
Open

fix(cron): resolve dynamic paths at runtime for profile isolation (#4707)#4729
devorun wants to merge 2 commits into
NousResearch:mainfrom
devorun:patch-30

Conversation

@devorun

@devorun devorun commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Description

This PR fixes a critical profile isolation issue where background cron jobs incorrectly resolved to the default ~/.hermes environment instead of the active profile's scoped HERMES_HOME.

Fixes #4707

Root Cause

The cron/scheduler.py and cron/jobs.py modules were defining absolute path constants (_hermes_home, CRON_DIR, JOBS_FILE, _LOCK_FILE, etc.) at the module level. Because Python evaluates and caches these at import time, they were permanently frozen to the default ~/.hermes path before the profile-scoped gateway could dynamically apply its HERMES_HOME environment override.

Changes

  • cron/jobs.py: Removed statically evaluated global path constants. Introduced the _get_cron_paths() helper to dynamically resolve cron_dir, jobs_file, and output_dir via lazy evaluation.
  • cron/scheduler.py: Removed module-level lock paths. Updated the tick() and run_job() functions to compute the active HERMES_HOME and lock directory dynamically at runtime.
  • This ensures all directory creations, job executions, and file locking mechanisms strictly adhere to the profile-scoped HERMES_HOME dictated by the active gateway environment.

Related Issue

Fixes #

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

How to Test

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform:

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

For New Skills

  • This skill is broadly useful to most users (if bundled) — see Contributing Guide
  • SKILL.md follows the standard format (frontmatter, trigger conditions, steps, pitfalls)
  • No external dependencies that aren't already available (prefer stdlib, curl, existing Hermes tools)
  • I've tested the skill end-to-end: hermes --toolsets skills -q "Use the X skill to do Y"

Screenshots / Logs

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cron Cron scheduler and job management labels May 1, 2026
@kowenhaoai

Copy link
Copy Markdown

Dropping a friendly note from a duplicate PR (#19375, now closed): great work here @devorun — our approaches converged on the same _get_cron_paths() pattern, but yours goes further by also patching cron/scheduler.py, which mine missed.

One small, optional suggestion if you ever revisit this: my version kept the old module-level names (JOBS_FILE, HERMES_DIR, CRON_DIR, OUTPUT_DIR) alive via a module-level __getattr__ shim, to avoid breaking any external code that imports those directly (there are a few such callers in third-party skills I've seen in the wild):

def __getattr__(name):
    if name == 'JOBS_FILE':  return _get_cron_paths()[1]
    if name == 'CRON_DIR':   return _get_cron_paths()[0]
    if name == 'OUTPUT_DIR': return _get_cron_paths()[2]
    if name == 'HERMES_DIR': return get_hermes_home().resolve()
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

Totally optional — feel free to ignore if you'd rather treat the removal as a clean break. Either way, hope this lands soon!

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

Labels

comp/cron Cron scheduler and job management P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: cron under profile-scoped launchd gateway falls back to default ~/.hermes instead of profile HERMES_HOME

3 participants