Skip to content

fix(kanban): expand ~ in external_dirs against real user home, not $HOME - #25

Merged
sahilm-ti merged 1 commit into
mainfrom
fix/kanban-tilde-real-home
May 26, 2026
Merged

sahilm-ti merged 1 commit into
mainfrom
fix/kanban-tilde-real-home

Conversation

@sahilm-ti

@sahilm-ti sahilm-ti commented May 26, 2026 •

Copy link
Copy Markdown
Owner

Problem

_resolve_skill_under_home() in hermes_cli/kanban_db.py expanded ~
paths in a profile's skills.external_dirs config using
os.path.expanduser(), which reads the calling process's HOME env var.

When the kanban dispatcher runs inside a profile-sandboxed orchestrator
(where HOME is rewritten to ~/.hermes/profiles/<orch>/home/),
cross-profile skill validation mis-expands the path and returns False
even when the skill is present.

Real-world impact: t_da084aa4 (Synapse investigation, bt-optimizer)
crashed 3 times with Error: Unknown skill(s): kanban-worker. Blocked the
investigation for ~3.5 hours. Workaround was changing all 3 BrainTrust
profile configs' external_dirs to absolute paths.

Fix

  • Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir —
    unaffected by HOME env var.
  • Replace os.path.expanduser(entry) in _resolve_skill_under_home()'s
    external_dirs loop with a manual tilde substitution using _real_user_home().
  • Fix the None-hermes_home fallback (was Path.home() which also reads HOME).

Tests

  • Added TestResolveSkillUnderHomeCrossProfile with 2 tests:
    • test_resolves_tilde_in_external_dirs_despite_fake_home: verifies
      _real_user_home() returns the real home when HOME is faked, and
      that absolute external_dirs entries still work.
    • test_resolves_tilde_entry_with_fake_home: writes the config entry as
      ~/<unique-subdir>, overrides HOME to a fake path, asserts the
      resolver correctly finds the skill via the real home.
  • All 196 test_kanban_db.py tests pass.

Follow-up

Profile configs in ~/.hermes/profiles/*/config.yaml that were converted to
absolute paths as a workaround can be reverted to ~/.hermes/skills once
this lands.

Summary by CodeRabbit

  • Bug Fixes
    • External skill directories are now correctly resolved even in sandboxed or environments where HOME is overridden.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 26, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@sahilm-ti, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 34 minutes and 30 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ef4a1b6-9bf2-491b-85ba-a09a20648a25

📥 Commits

Reviewing files that changed from the base of the PR and between 5fad4ec050102c0566c5b897092d7d99a2cb38db and 0155fff.

📒 Files selected for processing (2)
  • hermes_cli/kanban_db.py
  • tests/hermes_cli/test_kanban_db.py
📝 Walkthrough

Walkthrough

This PR fixes skill directory resolution to use the real OS user home directory instead of the HOME environment variable. A new _real_user_home() helper resolves the actual user home via OS password database, with fallback to os.path.expanduser(). The skill resolver is updated to use this real home when validating and expanding tilde-prefixed external skill directories, with comprehensive cross-profile regression tests.

Changes

Skill home resolution against real OS user

Layer / File(s) Summary
Real OS home resolution function
hermes_cli/kanban_db.py
Added pwd module import and _real_user_home() function that queries the OS password database (pwd.getpwuid(os.getuid()).pw_dir) for the real user home, with fallback to os.path.expanduser("~") when the database is unavailable.
Skill path resolver using real home
hermes_cli/kanban_db.py
Updated _resolve_skill_under_home() to anchor skill base paths and expand tilde-prefixed entries in skills.external_dirs against the real OS home instead of environment-dependent Path.home() behavior.
Cross-profile home resolution tests
tests/hermes_cli/test_kanban_db.py
Added TestResolveSkillUnderHomeCrossProfile test class with regression tests validating that _real_user_home() returns the actual OS home even when $HOME is sandboxed, and that skill directory tilde expansion uses the real home.

Possibly related PRs

  • sahilm-ti/hermes-agent#13: The liveness check _kanban_worker_skill_available() relies on _resolve_skill_under_home(), which this PR modifies to use real OS home resolution.
  • sahilm-ti/hermes-agent#7: Both PRs modify skill-path resolution in hermes_cli/kanban_db.py; this PR changes home directory resolution while the retrieved PR uses the resolver for dispatcher-side pre-flight checks.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A rabbit's home is where the OS knows it to be,
Not lost in a $HOME that's false, you see!
Real paths from the pwd database we trace,
So skills find their burrow in the right place! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main fix: expanding tilde in external_dirs against the real user home instead of the $HOME environment variable, which directly addresses the core bug being resolved.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/kanban-tilde-real-home

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 26, 2026 •

Copy link
Copy Markdown

🔎 Lint report: fix/kanban-tilde-real-home vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9378 on HEAD, 9378 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4957 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@sahilm-ti

Copy link
Copy Markdown
Owner Author

auto-review: changes requested.

  • C1-ci / Windows footguns (blocking): hermes_cli/kanban_db.py:3517 — bare os.getuid() triggers the Windows footgun checker.
    evidence: CI run https://github.com/sahilm-ti/hermes-agent/actions/runs/26451695838/job/77874030678

    hermes_cli/kanban_db.py:3517: [bare os.getuid / os.geteuid / os.getgid]
        return pwd.getpwuid(os.getuid()).pw_dir
        — os.getuid / os.geteuid / os.getgid do not exist on Windows and raise
          AttributeError at import time if referenced.
        Fix: Use getpass.getuser() for the username, or gate with hasattr(os, 'getuid').
    

    The runtime except (AttributeError, KeyError) already handles the Windows case correctly, but the static checker flags bare os.getuid regardless.

    Two valid fixes:

    1. Add # windows-footgun: ok on the same line (i.e., return pwd.getpwuid(os.getuid()).pw_dir # windows-footgun: ok) — appropriate since AttributeError is already caught.
    2. Restructure: if hasattr(os, 'getuid'): return pwd.getpwuid(os.getuid()).pw_dir before the fallback.

All other rules pass: U1-U4 clean, C2 (no new type: ignore / cast), C4 (tests added), S2/S3 clean.

@sahilm-ti
sahilm-ti force-pushed the fix/kanban-tilde-real-home branch from 5fad4ec to 8818948 Compare May 26, 2026 13:48
@sahilm-ti

Copy link
Copy Markdown
Owner Author

auto-review: changes requested.

Blocking findings

  • C1-ci: Windows footguns (blocking) — hermes_cli/kanban_db.py:76 — import pwd is an unconditional top-level import. The Windows footgun static checker is a static-analysis pass (see CI: https://github.com/sahilm-ti/hermes-agent/actions/runs/26452095735/job/77875520700); it flags the bare module-level import pwd before any runtime gate is evaluated. The hasattr(os, 'getuid') guard added to _real_user_home() is correct for runtime, but it does not satisfy the static checker because the import itself is still unconditional.

    Fix: move import pwd inside the try block of _real_user_home() so it is only attempted at runtime on non-Windows:

    def _real_user_home() -> str:
        try:
            import pwd  # not available on Windows; caught below
            return pwd.getpwuid(os.getuid()).pw_dir
        except (ImportError, AttributeError, KeyError):
            return os.path.expanduser("~")

    Remove the top-level import pwd at line 76 entirely. With the import local to the function, the static checker no longer sees it as a module-level Windows footgun.

All other rules passed

U1-U4, U5 (UNSTABLE = non-required only, MERGEABLE), C2 (no new type: ignore / cast()), C3 (ruff enforcement: SUCCESS), C4 (tests touched), U3 (no secrets), S2 (no system leaks).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@hermes_cli/kanban_db.py`:
- Line 3542: Replace the manual fallback that builds "~/.hermes" when
hermes_home is falsy with a call to get_hermes_home() from hermes_constants so
the code becomes profile-aware: instead of using (_P(_real_user_home()) /
".hermes") as the else branch, call get_hermes_home() and wrap its result with
_P as needed; update the expression that sets base (currently using hermes_home)
to use _P(hermes_home) if hermes_home else _P(get_hermes_home()), referencing
hermes_home and get_hermes_home() to locate the change.
- Line 76: Move the platform-specific pwd usage behind a guarded import and
broaden exception handling: remove the top-level "import pwd" and instead import
pwd inside _real_user_home(), catching (AttributeError, KeyError, OSError)
around pwd.getpwuid(os.getuid()) so Windows or lookup failures don't raise at
module import; and in _resolve_skill_under_home() replace the hermes_home is
None fallback that uses (_real_user_home() / ".hermes") with a call to
hermes_constants.get_hermes_home() (or accept and use the str(get_hermes_home())
value passed from _validate_task_skills(..., _worker_home) when claimed.assignee
is falsy) to ensure Hermes-home is resolved via hermes_constants consistently
and profile-safe.

In `@tests/hermes_cli/test_kanban_db.py`:
- Around line 3765-3802: The test imports the Unix-only pwd module directly and
will fail on Windows; update the
test_resolves_tilde_in_external_dirs_despite_fake_home method (or the test
class) to guard for platforms by calling pytest.importorskip("pwd") at the start
of the test (or add a class-level pytest.mark.skipif using sys.platform), then
proceed to use pwd only when present; ensure references to kb._real_user_home()
and kb._resolve_skill_under_home("kanban-worker", ...) remain unchanged so the
behavior is validated only on supported platforms.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 35d10079-9180-48cb-ab9e-9f8743b38e81

📥 Commits

Reviewing files that changed from the base of the PR and between 38fd1d8 and 5fad4ec050102c0566c5b897092d7d99a2cb38db.

📒 Files selected for processing (2)
  • hermes_cli/kanban_db.py
  • tests/hermes_cli/test_kanban_db.py

Comment thread hermes_cli/kanban_db.py Outdated
Comment thread hermes_cli/kanban_db.py
@@ -3523,7 +3539,7 @@ def _resolve_skill_under_home(skill_name: str, hermes_home: Optional[str]) -> bo
if not skill_name:
return True

base = _P(hermes_home) if hermes_home else (_P.home() / ".hermes")
base = _P(hermes_home) if hermes_home else (_P(_real_user_home()) / ".hermes")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Use get_hermes_home() for Hermes-home fallback.

When hermes_home is None, constructing ~/.hermes directly bypasses the project’s profile-aware Hermes-home resolver.

💡 Suggested fix
-    base = _P(hermes_home) if hermes_home else (_P(_real_user_home()) / ".hermes")
+    if hermes_home:
+        base = _P(hermes_home)
+    else:
+        from hermes_constants import get_hermes_home
+        base = _P(str(get_hermes_home()))

As per coding guidelines, "Use get_hermes_home() from hermes_constants for all code paths that reference the Hermes home directory to ensure profile-aware behavior."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
base = _P(hermes_home) if hermes_home else (_P(_real_user_home()) / ".hermes")
if hermes_home:
base = _P(hermes_home)
else:
from hermes_constants import get_hermes_home
base = _P(str(get_hermes_home()))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hermes_cli/kanban_db.py` at line 3542, Replace the manual fallback that
builds "~/.hermes" when hermes_home is falsy with a call to get_hermes_home()
from hermes_constants so the code becomes profile-aware: instead of using
(_P(_real_user_home()) / ".hermes") as the else branch, call get_hermes_home()
and wrap its result with _P as needed; update the expression that sets base
(currently using hermes_home) to use _P(hermes_home) if hermes_home else
_P(get_hermes_home()), referencing hermes_home and get_hermes_home() to locate
the change.

Comment on lines +3765 to +3802
def test_resolves_tilde_in_external_dirs_despite_fake_home(self, tmp_path, monkeypatch):
"""The core bug: config has ``~/.hermes/skills`` (tilde-prefixed) and the
caller's HOME is set to a sandbox path. The resolver must use the real
user home from pwd, not $HOME."""
import pwd as _pwd

real_home = _pwd.getpwuid(os.getuid()).pw_dir

# Build the profile home under the real user home (mirrors real usage).
profile_home = tmp_path / "profiles" / "bt-optimizer"
profile_home.mkdir(parents=True)
(profile_home / "skills").mkdir()

# Shared skills dir with the skill.
shared_skills = tmp_path / "shared-skills"
self._write_skill(shared_skills, "kanban-worker")

# Config uses tilde — exactly the pattern the bug broke.
# We can't use the real ~/.hermes/skills path because that would
# resolve correctly even with the old code (real dir exists).
# Instead, write the config with the absolute path of shared_skills
# prefixed by the fake real_home replacement. The simplest
# end-to-end test is to use the shared_skills absolute path
# directly in external_dirs (already tested in the other class)
# and separately test that the tilde-expansion helper itself
# returns the real home regardless of $HOME.
fake_home = str(tmp_path / "fake_home")
monkeypatch.setenv("HOME", fake_home)

# _real_user_home must ignore $HOME and return the actual home.
assert kb._real_user_home() == real_home

# And the resolver must find the skill when external_dirs uses an
# absolute path (sanity check that fake HOME doesn't break absolute paths).
(profile_home / "config.yaml").write_text(
f"skills:\n external_dirs:\n - {shared_skills}\n", encoding="utf-8",
)
assert kb._resolve_skill_under_home("kanban-worker", str(profile_home)) is True

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add platform compatibility guard for Unix-only pwd module.

Both tests in this class import pwd (lines 3769, 3807), which is Unix-only and will cause test failures on Windows. The production code has a fallback when pwd is unavailable, but these tests explicitly call pwd.getpwuid().

✅ Add pytest skip marker

Add at the class level:

+@pytest.mark.skipif(not hasattr(__import__('pwd', fromlist=['']), 'getpwuid'), reason="Requires pwd module (Unix only)")
 class TestResolveSkillUnderHomeCrossProfile:
     """Regression for the HOME-sandbox mis-expansion bug (2026-05-26).

Or use pytest.importorskip("pwd") at the start of each test method.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/hermes_cli/test_kanban_db.py` around lines 3765 - 3802, The test
imports the Unix-only pwd module directly and will fail on Windows; update the
test_resolves_tilde_in_external_dirs_despite_fake_home method (or the test
class) to guard for platforms by calling pytest.importorskip("pwd") at the start
of the test (or add a class-level pytest.mark.skipif using sys.platform), then
proceed to use pwd only when present; ensure references to kb._real_user_home()
and kb._resolve_skill_under_home("kanban-worker", ...) remain unchanged so the
behavior is validated only on supported platforms.

@sahilm-ti

Copy link
Copy Markdown
Owner Author

auto-review: changes requested.

  • C1-ci: Windows footguns (blocking): import pwd is STILL a top-level unconditional import in the diff.
    evidence: hermes_cli/kanban_db.py:76 — +import pwd (module-level import added in this PR's diff)
    The function body calls pwd.getpwuid(os.getuid()) using the module-level import — there is no lazy import inside the function.
    Run 231's comment claimed to have moved import pwd inside _real_user_home() but the actual diff on the branch contradicts that claim. CI confirms: Windows footguns check FAIL on run https://github.com/sahilm-ti/hermes-agent/actions/runs/26452095735/job/77875520700

    Required fix: Remove the top-level +import pwd (line 76 of diff) and change _real_user_home() to:

    def _real_user_home() -> str:
        try:
            import pwd  # lazy — not available on Windows; ImportError caught below
            return pwd.getpwuid(os.getuid()).pw_dir
        except (ImportError, AttributeError, KeyError):
            return os.path.expanduser("~")

    Note: with lazy import, the hasattr(os, "getuid") guard and explicit raise AttributeError are no longer needed — ImportError on the import catches the Windows path, and getuid() on macOS/Linux is always available once pwd is imported successfully.

All other rules passed: U1-U5, C2, C3 (ruff: SUCCESS), C4, S2, S3.

@sahilm-ti
sahilm-ti force-pushed the fix/kanban-tilde-real-home branch from 8818948 to 9bfbe76 Compare May 26, 2026 14:00
@sahilm-ti

Copy link
Copy Markdown
Owner Author

auto-review (run 235): changes requested.

  • C1-ci: Windows footguns (blocking) — hermes_cli/kanban_db.py:3517 — os.getuid() is still flagged by the static checker. CI run: https://github.com/sahilm-ti/hermes-agent/actions/runs/26452859191/job/77878323471

    The lazy import pwd (run 234's fix) is correct and present in the diff. But the checker is purely static and doesn't credit the AttributeError in the except tuple — it sees the bare os.getuid() call and flags it regardless.

    The checker itself says the fix is one of:

    1. Add # windows-footgun: ok on the same line as os.getuid() — justified because AttributeError IS caught in the except block.
    2. Gate with hasattr(os, 'getuid') before calling.

    Exact line to change:

    # Option 1 (simplest — one character change):
    return pwd.getpwuid(os.getuid()).pw_dir  # windows-footgun: ok
    
    # Option 2 (no suppression comment):
    if hasattr(os, 'getuid'):
        return pwd.getpwuid(os.getuid()).pw_dir
    raise AttributeError('no getuid on this platform')

All other rules passed: U1, U2, U3, U4, U5 (UNSTABLE/MERGEABLE), C2, C3 (ruff+ty SUCCESS), C4, S2, S3.

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.
@sahilm-ti
sahilm-ti force-pushed the fix/kanban-tilde-real-home branch from 9bfbe76 to 0155fff Compare May 26, 2026 14:05
@sahilm-ti

Copy link
Copy Markdown
Owner Author

auto-review: approved, awaiting human merge + kanban_approve.

All rules pass (run 237):

  • U1-U5: PASS
  • C1: all required/blocking checks green; test (5) flake is non-required (mergeStateStatus=UNSTABLE, unrelated WebSocket timing race in test_web_server.py)
  • C2-C4: PASS
  • Skill rules: N/A

Fix is correct: lazy import pwd inside _real_user_home(), # windows-footgun: ok on os.getuid(), and tilde expansion uses real user home in both the external_dirs loop and the hermes_home=None fallback.

@sahilm-ti
sahilm-ti merged commit e013d44 into main May 26, 2026
21 of 22 checks passed
@sahilm-ti
sahilm-ti deleted the fix/kanban-tilde-real-home branch May 26, 2026 14:17
sahilm-ti added a commit that referenced this pull request May 28, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 28, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 28, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 29, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jun 3, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jun 5, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jun 15, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jun 17, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jun 22, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 3, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 9, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 10, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 11, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 13, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 15, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 17, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 21, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 23, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 28, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Aug 24, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Sep 2, 2026
…OME (#25)

_resolve_skill_under_home() used os.path.expanduser() which expands ~
against the calling process's HOME env var.  When the kanban dispatcher
runs inside a profile-sandboxed orchestrator (HOME overridden to
~/.hermes/profiles/<orch>/home/), validating another profile's
skills.external_dirs config mis-expands the path and returns False even
when the skill is present.

Fix:
- Add _real_user_home() helper using pwd.getpwuid(os.getuid()).pw_dir
  (unaffected by HOME env var)
- Use _real_user_home() when expanding ~ in external_dirs entries
- Use _real_user_home() in the None-hermes_home fallback (was Path.home()
  which also reads HOME)

Root cause of t_da084aa4 crashing 3x with 'Unknown skill(s): kanban-worker'.
The bt-optimizer / braintrusteng profiles had their external_dirs set to
~/.hermes/skills; the dispatcher running from the orchestrator's sandboxed
HOME failed to find kanban-worker and aborted worker spawn.

Tests: add TestResolveSkillUnderHomeCrossProfile (2 tests) — simulate
cross-profile call with fake HOME, assert both _real_user_home() and
_resolve_skill_under_home() return correct results.  196/196 kanban_db
tests pass.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants