From 84e688ec005164529a94403a374494e941b2ef3c Mon Sep 17 00:00:00 2001 From: sprooty Date: Wed, 5 Aug 2026 01:50:06 +0000 Subject: [PATCH] fix: the CLI's agent default could not write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `session_executor.DEFAULT_AGENT_COMMAND` carries `--permission-mode acceptEdits`. The CLI's `--agent` default was a second, separately written string that did not. So `run --session-host` without the flag produced an agent that was refused every `Edit`. What that looks like from outside is the problem. The agent finishes cleanly, the harness reports `no_changes`, and the item fails as "the agent made no changes" — indistinguishable from a model that considered the task and declined. The agent's own account was in a session scrollback nothing reads: "every write to the working tree is being denied by the permission layer … The files themselves are writable, so this is the harness permission mode, not the filesystem. I'm not going to route around a declined permission via shell redirection." It then wrote out the complete change it would have made. Correct behaviour throughout, recorded as producing nothing, and it cost a full agent run — minutes of a real CLI agent rather than one API call. The CLI now takes the executor's command rather than declaring its own. The test asserts that relationship rather than the string, because the defect was a second default existing at all: a literal that happens to match today drifts again tomorrow, which is exactly what happened to the review rubric (#167). Part of #165. The remaining half of that issue — that a permission refusal still reports as `no_changes` — is untouched here and is the part that matters when the cause is a deployment rather than a flag. Co-Authored-By: Claude Opus 5 (1M context) --- src/agent_harness/__main__.py | 13 +++++++++++-- tests/test_cli_roles.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/agent_harness/__main__.py b/src/agent_harness/__main__.py index 87cee0c..e1a737e 100644 --- a/src/agent_harness/__main__.py +++ b/src/agent_harness/__main__.py @@ -888,6 +888,7 @@ def main(argv: list[str] | None = None) -> int: # starts for `--help` on a machine with no queue and no credentials, and # only the default value is needed to print it. from .executor import DEFAULT_CONTEXT_BUDGET + from .session_executor import DEFAULT_AGENT_COMMAND parser = argparse.ArgumentParser(prog="agent-harness", description=__doc__) parser.add_argument( @@ -1123,9 +1124,17 @@ def main(argv: list[str] | None = None) -> int: ) p_run.add_argument( "--agent", - default="claude -p {prompt_file}", + # The executor's default, not a second one. They disagreed: the + # executor's carried `--permission-mode acceptEdits` and the CLI's did + # not, so `run --session-host` without this flag produced an agent that + # could not write. It reported no changes and read as a model that had + # considered the task and declined — measured, and it cost a run. + default=" ".join(DEFAULT_AGENT_COMMAND), metavar="CMD", - help="CLI agent to run per item. `{prompt_file}` is substituted.", + help="CLI agent to run per item. `{prompt_file}` is substituted. " + "Defaults to the agent command the session executor declares, which " + "grants edit permission — an agent that cannot write reports no " + "changes, which is indistinguishable from one that chose to make none.", ) p_run.add_argument("--limit", type=int, help="stop after N items") p_run.add_argument( diff --git a/tests/test_cli_roles.py b/tests/test_cli_roles.py index af3c28a..e7c987c 100644 --- a/tests/test_cli_roles.py +++ b/tests/test_cli_roles.py @@ -287,3 +287,33 @@ def capture(self: Any, method: str, url: str, **kwargs: Any) -> Any: assert "timeout" not in sent["json"] and "role" not in sent["json"] assert sent["headers"]["x-api-key"] == "shared-key" assert "Authorization" not in sent["headers"] + + +def test_the_cli_agent_default_is_the_executor_s() -> None: + """They disagreed, and the CLI's was the one that could not write. + + `session_executor.DEFAULT_AGENT_COMMAND` carries `--permission-mode + acceptEdits`; the CLI's `--agent` default did not. So `run --session-host` + without the flag produced an agent that was refused every Edit, reported + no changes, and read as a model that had considered the task and declined. + + Measured: it cost a full agent run, and the agent's own account of the + refusal sat in a scrollback nothing reads. + + Asserted against the source rather than a parsed value, because the defect + was *a second default existing at all* — a literal here that happens to + match today would drift again tomorrow. + """ + import inspect + + from agent_harness import __main__ + from agent_harness.session_executor import DEFAULT_AGENT_COMMAND + + source = inspect.getsource(__main__) + assert 'default=" ".join(DEFAULT_AGENT_COMMAND)' in source, ( + "the CLI must take the executor's agent command, not declare its own" + ) + assert '"--permission-mode"' in inspect.getsource( + __import__("agent_harness.session_executor", fromlist=["x"]) + ), "and that command must still grant edit permission" + assert "--permission-mode" in " ".join(DEFAULT_AGENT_COMMAND)