hermes-agent/gateway
Teknium 19fc9c103e fix(tests): fail hard when pytest resolves the production state.db (live-DB isolation guard)
Forensics on a live developer machine found pytest fixture rows inside the
REAL ~/.hermes/state.db — sessions with chat_id 'chat-1', '123', 'wx-chat',
and gateway_routing rows whose scope was literally under /tmp/pytest-of-*/.
A pytest-spawned process also opened the live DB and flipped its journal
mode (journal_mode=DELETE fallback on SQLite 3.50.4) under the WAL-mode
gateway writer, destroying committed transcripts ("Persisted transcript
lagged live cached history ... possible FTS write corruption", 15+
occurrences). The existing live-system guard covers kill primitives but not
the SessionDB/SessionStore write paths.

Root cause (leak vector): the session-level HERMES_HOME sandbox in
tests/conftest.py only created a tempdir when HERMES_HOME was UNSET. On a
machine where the shell (e.g. gateway-launched, or an exported
HERMES_HOME=~/.hermes) hands pytest the production home, the sandbox was
skipped entirely — every argless SessionDB()/SessionStore() and every
collection-time DEFAULT_DB_PATH froze onto the real state.db.

Fixes (fail the class, one owner):

* hermes_state._ensure_test_isolation(): single choke point wired into
  SessionDB.__init__ (every construction, incl. read_only). Under pytest
  (PYTEST_CURRENT_TEST / PYTEST_VERSION — inherited by subprocess
  children), a db path resolving to <real-root>/state.db or
  <real-root>/profiles/<name>/state.db raises RuntimeError('live-system
  guard: ...') before any connection, mkdir, or journal-mode pragma.
* tests/conftest.py: session sandbox now also tempdir-redirects a pre-set
  HERMES_HOME that points at the production root (the actual escape
  vector); kanban deny-list capture updated to match. New autouse
  _state_db_write_guard fixture honors the existing
  @pytest.mark.live_system_guard_bypass marker as the escape hatch and
  feeds custom (non-~/.hermes) production roots into the guard deny-list.
* gateway/session.py: SessionStore.__init__ no longer swallows the guard's
  RuntimeError into the JSONL fallback — guard trips are loud.
* tests/hermes_state/test_live_db_isolation_guard.py: behavioral
  regression tests — production paths (direct, profile, read-only,
  unnormalized, default-resolution) raise; tmp HERMES_HOME works; bypass
  marker works; SessionStore re-raises guard errors but still degrades on
  ordinary failures; subprocess child without HERMES_HOME is refused while
  a hermetic child succeeds.

No new HERMES_* env vars; no hardcoded ~/.hermes (platform root comes from
hermes_constants._get_platform_default_hermes_home()).
2026-08-06 07:49:50 -07:00
..
assets
builtin_hooks
platforms refactor(gateway): route session event stream through _sse_frame (ensure_ascii=False) 2026-08-04 13:07:44 +05:30
relay fix(gateway): bound go_dormant ws.close with teardown timeout 2026-08-04 11:17:11 +05:30
__init__.py
authz_mixin.py fix(secrets): route authz gate and pairing allowlist reads through the profile secret scope 2026-08-02 10:02:33 -07:00
cgroup_cleanup.py
channel_directory.py feat(gateway): simplex channel enumeration + show configured platforms in hermes send --list 2026-08-02 15:08:45 -07:00
code_skew.py
config.py chore: remove unused imports and dead locals (ruff F401/F841 sweep) 2026-07-29 11:53:39 -07:00
cwd_placeholder.py
dead_targets.py
delivery.py
delivery_ledger.py
display_config.py
drain_control.py
hooks.py
kanban_watchers.py fix: use lazy logging with %s formatting in logger calls 2026-08-02 23:17:40 +05:30
lifecycle_ledger.py fix(gateway): treat pid=None lifecycle sentinel as unknown ownership in mark_exited 2026-07-28 22:41:09 -07:00
memory_monitor.py
message_timestamps.py
mirror.py
pairing.py fix(secrets): route authz gate and pairing allowlist reads through the profile secret scope 2026-08-02 10:02:33 -07:00
platform_registry.py fix(teams): suppress SDK import-time dotenv instead of clearing environ 2026-08-01 15:34:19 -07:00
profile_routing.py
readiness.py
response_filters.py
restart.py fix(gateway): defer in-band restart until active turns finish (#77184) 2026-08-03 09:57:58 +05:30
restart_loop_guard.py
rich_sent_store.py
run.py refactor(gateway): extract the hygiene recovery gate and forward the failure reason 2026-08-06 15:04:53 +05:30
runtime_footer.py feat(gateway): add opt-in 'latency' runtime footer field 2026-08-03 17:16:57 +05:30
scale_to_zero.py
session.py fix(tests): fail hard when pytest resolves the production state.db (live-DB isolation guard) 2026-08-06 07:49:50 -07:00
session_context.py fix(cron): scope cron approval context per session 2026-08-03 00:25:20 +05:30
session_stall.py feat(gateway): session activity watchdog, stall notify, compress timeout (#72424) 2026-08-02 16:16:36 -07:00
session_state.py refactor(gateway): extract the hygiene recovery gate and forward the failure reason 2026-08-06 15:04:53 +05:30
shutdown_flush.py fix: consolidate agent-history preservation into shutdown_flush module 2026-07-29 16:45:35 +05:30
shutdown_forensics.py
shutdown_watchdog.py fix(gateway): detect and report unclean shutdowns via lifecycle ledger (NS-608) 2026-07-28 22:41:09 -07:00
slash_access.py
slash_commands.py feat: /refine — run the memory/skill self-improvement review on demand 2026-08-05 22:40:51 -07:00
status.py fix(windows): native Windows correctness for CLI, gateway status, banner, and WSL browser paths 2026-07-29 23:16:18 -07:00
status_phrases.py
sticker_cache.py
stream_consumer.py fix(gateway): don't claim deleted head chunks as delivered in the empty-fallback recovery 2026-08-06 03:13:32 +05:30
stream_dispatch.py
stream_events.py
streaming_tts_consumer.py feat(gateway): streaming TTS adapter contract and consumer (#60671) 2026-07-28 22:31:40 -07:00
systemd_notify.py
turn_context.py fix(gateway): reap only the background processes an abandoned turn created 2026-08-02 14:23:55 +05:30
turn_lease.py
wake.py
whatsapp_identity.py