Skip to content

sidecall: read a verbose Claude Code reply; a failed proposer call skips its tick - #164

Merged
Saksham Uboweja (SakshamUboweja) merged 1 commit into
microsoft:mainfrom
SakshamUboweja:fix/verbose-sidecall-reply
Nov 8, 2026
Merged

Saksham Uboweja (SakshamUboweja) merged 1 commit into
microsoft:mainfrom
SakshamUboweja:fix/verbose-sidecall-reply

Conversation

@SakshamUboweja

Copy link
Copy Markdown
Collaborator

Fixes #131. With "verbose": true in Claude Code's user settings, claude -p --output-format json prints the whole event list (system, assistant, ..., result) instead of one result object. Every Claude Code side call then failed, and because the micro-skills proposer called its runner without a guard, the failure aborted the whole fold.

This builds on the approach in #134 by Dhruv Arora (@dhruv1220) and the follow-up commit pushed there: read the result event rather than pass --settings '{"verbose":false}', and let a failed proposer call skip its tick. Thanks to Raymond Manaloto (@sortakool) for the detailed report in #131.

What changed

  • sidecall.claude_json(stdout) returns the result object from either shape: the plain object, or the last type == "result" event in a list, and {} for anything else. It is shared by claude_call and by the te toolprune measure probe (toolcheck._prompt_tokens), which had the same data.get on a list and would also have crashed with verbose on.
  • claude_call treats an is_error result as no reply. Before this change, an error result such as "Invalid API key · Please run /login" has a non-empty result, so it came back as the proposer's or judge's text. It also skipped _no_reply, so the signed-out pause never triggered for Claude Code. It now goes through _no_reply like an empty reply.
  • Distiller._maybe_propose catches a runner exception and skips the tick instead of the fold, as the loop guard's judge already does. The failed call still counts as the tick (last_propose = now), so a CLI that keeps failing is tried every PROPOSE_EVERY sessions, not at every fold.
  • runner_for wraps each host's call so that one that raises leaves one line in errors.log before it propagates (sidecall claude: no reply (timed out after 180s), ... (exit 2), ... failed (RuntimeError: ...)). This covers the proposer and the judge on every host. It never logs the command or a traceback: most hosts take the prompt on argv, a TimeoutExpired prints its whole command, and te doctor shows this log's tail, which people paste into issues. It does not touch plugin/fold.py, so it does not conflict with microskills: add te microskills forget / unforget #160 (checked with git merge-tree).

Tests

tests/test_claude_verbose_reply.py:

  • claude_json on the object, the verbose list, a list with no result, several results, non-dict JSON, invalid and empty output.
  • claude_call with a verbose reply returns the text and its usage. With no result event it comes back empty and is noted.
  • An is_error result, plain or verbose, is no reply and pauses a signed-out host.
  • The measure probe reads a verbose reply.
  • A failed proposer call skips the tick, charges nothing, and the next tick proposes as usual.
  • A runner that raises (timeout, non-zero exit, other) leaves exactly one line in errors.log, without the prompt.

python -m pytest tests experiments/tests: everything passes except experiments/tests/test_fleet_budget.py::test_an_unwritable_cgroup_warns_and_never_fails_the_launch, which fails the same way on main in this environment (cgroup setup, unrelated).

…ips its tick

With "verbose": true in the person's Claude Code settings, `claude -p
--output-format json` prints the whole event list instead of one result
object, so every Claude Code side call failed, and the micro-skills
proposer's unguarded call aborted the fold (microsoft#131).

- sidecall.claude_json: the result object from either shape, {} for
  none; claude_call and the toolprune measure probe both read it.
- claude_call: an is_error result is no reply, not the proposer's text,
  and a signed-out one pauses the host's side calls as intended.
- Distiller._maybe_propose: a runner that raises skips the tick instead
  of the fold, and counts as the tick, so a failing CLI is not retried
  at every fold.
- runner_for: a side call that raises leaves one line in errors.log
  before it propagates, for the judge and the proposer alike. Never the
  command: most hosts take the prompt on argv, and `te doctor` prints
  the log's tail.

Fixes microsoft#131. Builds on the approach in microsoft#134 by @dhruv1220 and the
follow-up commit there.
@SakshamUboweja
Saksham Uboweja (SakshamUboweja) marked this pull request as ready for review November 8, 2026 00:56
@SakshamUboweja
Saksham Uboweja (SakshamUboweja) merged commit cb42207 into microsoft:main Nov 8, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude Code side calls fail when the user settings have verbose: true, and a failed proposer call aborts the fold

1 participant