Repository navigation
Delegated claude-cli agent nodes fail with an empty error message #96
Description
Activity
Fixed on
mainin #99. All three parts of this report, each with a regression test.1. The delegated call failing where a direct one succeeds. The root cause was not in the adapter's hardened invocation — it was that the stdlib registry could not delegate at all.
default_harnessbuilds aHarnesswith an explicitLocalExecutor, andHarness.__init__acceptedworkspace=only to construct a defaultSandboxedExecutor— so passing an executor silently discarded the workspace.AgentNode._run_delegatedthen asked the executor for a workspace, found none, and refused before spawning anything. Every agent phase of the one registry-plus-backend combination whose documented behaviour is delegation failed the same way.Harnessnow records its workspace whichever executor is in play,default_harnessnames it, and_run_delegatedasks the harness before the executor. Tests:test_a_harness_keeps_the_workspace_it_was_given,test_the_stdlib_harness_names_a_workspace_so_claude_code_can_be_delegated_to.2. The empty error message.
AgentResult.outputis empty by contract for everytermination_reasonexceptTARGET_MET— the mid-work text lives inpartial_output— andstdlib._agent_factoryformattedoutputfor the curtailed branch. So[error]was not a lost exception message; it was the right message being read out of the wrong field. It readspartial_output, thennote, then"(no output)". Test:test_a_curtailed_phase_reports_what_it_managed_to_say.That is also why the underlying failure in (1) was invisible: the real string —
"the delegated executor needs a workspace directory, and this harness's executor (LocalExecutor) does not expose one"— was being written and then dropped on the floor.3. The goal check counting an error as progress.
goal_metwaslen(notes) >= 1. It now ignores notes that a phase wrote to say it did not finish, recognised by a marker built from theStopReasonenum rather than a hardcoded string, so a new stop reason cannot silently start reading as success. A run whose only notes are curtailed no longer stopsgoal_met. Tests:test_a_run_whose_only_note_is_an_error_has_not_met_its_goal,test_a_real_report_still_meets_the_goal,test_curtailed_is_read_off_the_stop_reasons_not_guessed.Verified end to end afterwards:
grapharc plan "…" --model claude-cli --registry grapharc.stdlib:build_registry --gonow runs its delegated phases, and the recording of one is indocs/demo/.
Found while benchmarking (
bench/). On theclaude-cli/*backend, stdlib agent nodes (investigate,summarize) fail in ~2s and fold an empty exception message into state:findings=['[error] ']. A bareclaude -pworks on the same machine and same prompt, so the defect is somewhere in the adapter's hardened invocation (isolated settings, disallowed tools, stdin prompt), not in the CLI itself. Reproduced with allCLAUDE*env vars stripped, so it is not (only) nested-session detection.Two problems, the second worse than the first:
goal_metwith nothing but['[error] ']innotes. A run whose only output is a swallowed error should never read as success.Repro:
🤖 Generated with Claude Code