fix(oneshot): honor --skills / --ignore-rules / --ignore-user-config in -z mode - #59402
Conversation
…in -z mode The three parameters were accepted by argparse but silently dropped by run_oneshot(), contradicting the module docstring promise that 'Rules / memory / AGENTS.md / preloaded skills = same as a normal chat turn'. - run_oneshot() signature: add skills / ignore_rules / ignore_user_config - env vars HERMES_IGNORE_RULES / HERMES_IGNORE_USER_CONFIG set early - skip_context_files / skip_memory forwarded to AIAgent - SKILL body injected via ephemeral_system_prompt (matches chat mode) - main.py two dispatch sites forward the new params Verified: model can quote SKILL body verbatim when --skills is passed, which was previously impossible in -z mode. Carry patch — upstream PR pending.
Related: #26633 / #31548 (oneshot drops |
Thanks for the triage @alt-glitch — helpful map. To align expectations: our downstream ( One quick clarification on the overlap with #51797 to help triage:
We heavily lean toward keeping this scoped to the 3 flags (Option 1). The discovery-gating in #51797 is a separable architectural stage. Letting #51797 land independently keeps both reviews narrow, and I am happy to rebase on top of it immediately. Cc @hzhaoy for awareness on the potential rebase coordination. If the maintainers strongly prefer folding the safe-mode axis here instead, let me know. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for tracing the oneshot path. The core premise is verified on current main: hermes_cli/_parser.py:200-232 accepts these flags, while the two run_oneshot() dispatches (hermes_cli/main.py:12675-12681, :14832-14838) still omit them and hermes_cli/oneshot.py:393-417 does not pass the normal CLI skip flags.
Problems
- The new test asserts
mock_agent.chat.calledattests/cli/test_oneshot_preloaded_skills.py:105, but the implementation callsagent.run_conversation()at PRhermes_cli/oneshot.py:450; the test will fail. Its scalar_run_agentstubs at test lines 57 and 68 also no longer match the(response, result)contract. - The warning at PR
hermes_cli/oneshot.py:441is emitted inside the stderr-to-devnull redirect established at line 226, so unknown skills remain silent in actual-zruns. - Current main added
--usage-filesupport in7dfd5077ceef4d5a6f7953c050bad1a75e86e215; the PR is conflicting and salvage must preserve that newer argument and reporting behavior.
Suggested changes
- Rebase the implementation mechanically onto the current signature and preserve
usage_file. - Update tests for
run_conversation()and tuple returns, and assert the public forwarding plus the two AIAgent skip kwargs. - Surface all-missing skill failures or route the warning outside the redirect.
Automated hermes-sweeper review.
|
|
||
| monkeypatch.delenv("HERMES_IGNORE_RULES", raising=False) | ||
|
|
||
| with patch("hermes_cli.oneshot._run_agent", return_value="mock response"): |
There was a problem hiding this comment.
_run_agent() returns (final_response, result) (also declared by this PR at hermes_cli/oneshot.py:300), but this scalar stub makes run_oneshot() hit its unpacking-error path. Return a tuple and assert _run_agent received the expected flag-derived behavior.
| _run_agent("test prompt", skills="libero-decompose") | ||
|
|
||
| assert "SKILL_BODY libero-decompose" in mock_agent.ephemeral_system_prompt | ||
| assert mock_agent.chat.called |
There was a problem hiding this comment.
_run_agent() calls agent.run_conversation() at hermes_cli/oneshot.py:450, not agent.chat(). Set mock_agent.run_conversation.return_value to a result dict and assert that method instead; this assertion otherwise fails.
| task_id=getattr(agent, "session_id", None), | ||
| ) | ||
| if missing_skills: | ||
| sys.stderr.write( |
There was a problem hiding this comment.
This write occurs under redirect_stderr(devnull) from run_oneshot() line 226, so the advertised warning is never visible in a real -z invocation. Surface it through the saved stderr outside the redirect, or fail when every requested skill is missing as normal CLI does.
Summary
hermes -zaccepts--skills,--ignore-rules, and--ignore-user-configon the CLI but silently drops all three beforerun_oneshot()— despite the module docstring inhermes_cli/oneshot.py:8explicitly promising:This PR connects the three flags to the actual agent construction so
-zmode behavior matches the docstring and mirrors whathermes chatalready does with-s/--ignore-rules/--ignore-user-config.Motivation
Downstream evaluation harness (libero) hit this bug hard:
hermes -z "..." --skills libero-decompose --ignore-user-configsystem_prompt_lencame out identical (31311) with all user MEMORY, USER PROFILE, and AGENTS.md content — the flags had no effectrun_oneshot(prompt, model, provider, toolsets)signature never consumed skills/ignore-* args;main.pydispatch sites (lines 12458, 13891) didn't forward them either-zmode, which was previously impossibleChanges
hermes_cli/oneshot.py(+78 lines)run_oneshot()signature withskills: Optional[str],ignore_rules: bool,ignore_user_config: boolHERMES_IGNORE_USER_CONFIG/HERMES_IGNORE_RULESenv vars early (mirrorsmain.py:cmd_chatlines 2328-2343)skip_context_files/skip_memorytoAIAgentwhen--ignore-rulesis setagent.ephemeral_system_prompt(mirrorscli.py:15190-15202); useephemeral_system_prompt— not_cached_system_prompt— because the cached prompt is rebuilt during the conversation loop_parse_skills_list()normalizes"a,b,c"/ list / Nonehermes_cli/main.py(+6 lines)run_oneshot()dispatch sites (lines 12458, 13891) now forward the three paramstests/cli/test_oneshot_preloaded_skills.py(+154 lines, new file)_parse_skills_list(5), env var setting (3), skill injection (2)tests/cli/test_cli_preloaded_skills.py— no regression (3/3 chat mode tests still green)Verification
Design notes
Why
ephemeral_system_promptand not_cached_system_prompt?_cached_system_promptis rebuilt by_restore_or_build_system_prompt()inside the conversation loop, which would overwrite any direct mutation.ephemeral_system_promptis injected at API-call time (seeagent/conversation_loop.py:490and:815) after the cached prompt is resolved — the same mechanism chat mode uses (viaHermesCLI.system_prompt→ephemeral_system_promptincli_agent_setup_mixin.py:359).Why not just set env vars in
main.py?Env vars for
--ignore-rulesalone are insufficient —AIAgentalso needsskip_context_files=Trueandskip_memory=Truepassed to its constructor (seeagent/agent_init.py:1166-1170). Setting only env vars was tested and did not fully suppress MEMORY injection.Behavior for unknown skills: prints stderr warning, does not abort (mirrors chat mode's tolerance in
cli.py:15195-15197, though chat currently raises — this PR intentionally chooses tolerance for-zbecause scripted callers can't respond to interactive errors).Backward compatibility
run_oneshot()(default:None/False); existing callers unaffectedtest_cli_preloaded_skills.py)Downstream impact
libero— a local-first AI time-management coach — depends onhermes -zfor its evaluation harness. This bug caused all their smoke tests to run with pollutedsystem_prompt(contaminated with user MEMORY that included decompose-specific preferences), producing a completely misleading 15% drift rate. After this fix + closing pollution, drift dropped to 5%. Full write-up: [link to your smoke report if you want to share]