fix(tests): isolate the platform-default Hermes root

The shared fixture isolates HERMES_HOME, but profile-root resolution also
resolves the native default. This trips the real-home guard even for tests
that use a temporary custom home. Base 75a646e5b3 has the same failures.

Isolate the native default in the shared fixture. Capture its parent before
test fixtures run so explicit home overrides keep their own layout. Leave
HOME, Path.home(), production resolver behavior, and the I/O guard intact.

On the original base, this fixture fixes all 24 PM authority failures and
92 update failures/setup errors. The same four unrelated /proc DB-holder
probe failures remain on both base and current code. Current targeted
profile, path, PM, and guard checks pass: 214 passed, 4 skipped.
This commit is contained in:
ethernet
2026-09-11 18:15:30 -04:00
parent c91d2be042
commit ea8adb5e22

View File

@@ -5,11 +5,10 @@ Hermetic-test invariants enforced here (see AGENTS.md for rationale):
1. **No credential env vars.** All provider/credential-shaped env vars
(ending in _API_KEY, _TOKEN, _SECRET, _PASSWORD, _CREDENTIALS, etc.)
are unset before every test. Local developer keys cannot leak in.
2. **Isolated HERMES_HOME.** HERMES_HOME points to a per-test tempdir so
code reading ``~/.hermes/*`` via ``get_hermes_home()`` can't see the
real one. (We do NOT also redirect HOME — that broke subprocesses in
CI. Code using ``Path.home() / ".hermes"`` instead of the canonical
``get_hermes_home()`` is a bug to fix at the callsite.)
2. **Isolated Hermes homes.** HERMES_HOME and the platform-default root
resolve inside a per-test tempdir. Profile/root resolution can inspect
both without probing production state. HOME and Path.home() stay intact
for subprocesses and non-Hermes paths. Explicit test overrides still win.
3. **Deterministic runtime.** TZ=UTC, LANG=C.UTF-8, PYTHONHASHSEED=0.
4. **No HERMES_SESSION_* inheritance** — the agent's current gateway
session must not leak into tests.
@@ -60,6 +59,11 @@ if str(PROJECT_ROOT) not in sys.path:
_PRE_SANDBOX_KANBAN_OVERRIDE = os.environ.get("HERMES_KANBAN_HOME", "").strip()
_PRE_SANDBOX_HERMES_HOME = os.environ.get("HERMES_HOME", "")
# Capture before any test fixture can override Path.home()/LOCALAPPDATA.
from hermes_constants import _get_platform_default_hermes_home
_NATIVE_HERMES_PARENT = _get_platform_default_hermes_home().parent
def _hermes_home_points_at_production(value: str) -> bool:
"""True when a pre-set HERMES_HOME resolves to the real production root.
@@ -489,16 +493,24 @@ def _hermetic_environment(tmp_path, monkeypatch):
# custom host resolution override/delete this explicitly.
monkeypatch.setenv("HERMES_HONCHO_HOST", "hermes")
# 3. Redirect HERMES_HOME to a per-test tempdir. Code that reads
# ``~/.hermes/*`` via ``get_hermes_home()`` now gets the tempdir.
#
# NOTE: We do NOT also redirect HOME. Doing so broke CI because
# some tests (and their transitive deps) spawn subprocesses that
# inherit HOME and expect it to be stable. If a test genuinely
# needs HOME isolated, it should set it explicitly in its own
# fixture. Any code in the codebase reading ``~/.hermes/*`` via
# ``Path.home() / ".hermes"`` instead of ``get_hermes_home()``
# is a bug to fix at the callsite.
# 3. Isolate both inputs to profile/root resolution. HERMES_HOME alone
# is insufficient: get_default_hermes_root() resolves the native root
# too, to distinguish standard profiles from custom deployments.
# Patch only the Hermes default, not HOME/Path.home(). Subprocesses need
# a stable HOME. Hardcoded real-home I/O must still trip the guard.
import hermes_constants
platform_default = hermes_constants._get_platform_default_hermes_home
def isolated_platform_default() -> Path:
root = platform_default()
# Explicit Path.home()/LOCALAPPDATA overrides in individual tests
# still select their own layout. Suffix changes retain their name.
return tmp_path / root.name if root.parent == _NATIVE_HERMES_PARENT else root
monkeypatch.setattr(
hermes_constants, "_get_platform_default_hermes_home", isolated_platform_default
)
fake_hermes_home = tmp_path / "hermes_test"
fake_hermes_home.mkdir()
(fake_hermes_home / "sessions").mkdir()