Builds on @steezkelly's Bug A fix (#25857, top-level default_permissions
via _insert_managed_block_at_top_level) by addressing the other two
config-corruption bugs described in #26250:
Bug B (duplicate [plugins.X] tables)
- Codex itself writes [plugins."<name>@<marketplace>"] tables to
config.toml when the user runs `codex plugins enable` directly,
before hermes-agent's managed block exists. On the next migrate run,
_query_codex_plugins() re-discovers the same plugins via plugin/list
and render_codex_toml_section() re-emits them inside the managed
block. Codex's strict TOML parser then rejects the duplicate table
header on startup.
- Add _strip_unmanaged_plugin_tables() that drops [plugins.*] tables
from the user-content portion of the file. Only run it when
plugin/list succeeded — if the RPC failed we can't re-emit and
must preserve the user's tables. plugin/list is the source of
truth when it answers.
Bug C (HERMES_HOME pytest-tempdir leak into ~/.codex/config.toml)
- _build_hermes_tools_mcp_entry() read HERMES_HOME directly from
os.environ, so a sibling pytest's monkeypatch.setenv("HERMES_HOME",
tmp_path) silently burned a transient pytest tempdir into the
user's real ~/.codex/config.toml. After pytest reaped the tempdir,
every codex-routed hermes-tools tool call failed silently.
- Derive HERMES_HOME from get_hermes_home() (the canonical resolver
that goes through the profile-aware path) and refuse to emit
obvious test-tempdir paths via _looks_like_test_tempdir() as
belt-and-suspenders for any other callsite that forgets to patch
migrate().
- test_enable_succeeds_when_codex_present in test_codex_runtime_switch.py
invoked the real migrate() (no mock), writing to Path.home() / .codex
using whatever HERMES_HOME the running pytest session had set. Add
the same migrate patch the other apply() tests already use, so the
suite stops touching the user's real ~/.codex/config.toml.
E2E verification (replicating the issue's repro):
- Pre-state config.toml with user [mcp_servers.omx_team_run] +
codex-installed [plugins."tasks@openai-curated"],
HERMES_HOME="/private/var/folders/.../pytest-of-.../..."
- On origin/main: tomllib refuses to load the result with
"Cannot declare ('plugins', 'tasks@openai-curated') twice" AND
the pytest-tempdir HERMES_HOME is burned in.
- On this branch: file parses cleanly, default_permissions is
top-level, exactly one [plugins."tasks@openai-curated"] table
inside the managed block, no HERMES_HOME in the MCP env.
7 new regression tests covering all three bugs + the test-leak guard.
`bash scripts/run_tests.sh tests/hermes_cli/test_codex_runtime_*.py` —
95 passed, 0 failed.
Closes#26250