Repository navigation
feat(verifiers): read coding-agent CLI trajectories in the judge action log - #223
kaihackney-scale wants to merge 5 commits into
Conversation
…on log
The direct judge's action log (compaction_type=screenshot, trajectory_text=action_log)
only parsed OTel spans. Trajectories from coding-agent CLIs (stream-json rows) and
OpenCode (part events) produced an empty transcript, so a direct judge on those runs
graded without seeing a single tool call.
- transcript_from_raw detects the format: OTel spans (unchanged), stream-json rows
(tool_use paired with tool_result by id; is_error recorded as {"error": ...}),
OpenCode tool/text parts (re-emitted parts merged; random mcp_<hex>_ prefix dropped).
JSON-in-text tool results are decoded in the two new parsers.
- action_log takes max_args_chars / max_result_chars; TrajectoryFilter gains
action_log_max_args_chars / action_log_max_result_chars (defaults unchanged at
110 / 160) and rubrics_verifier passes them through.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…read transcript_from_raw returned an empty transcript for JSON in no format it reads, so a direct judge with trajectory_text=action_log graded an empty log without any sign that it could not see the run. Transcript now records its source format (otel / stream-json / opencode, empty, malformed), and the verifier raises when the action log is asked to read unrecognised JSON. Text that is not JSON at all stays a failed run, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…esults
- An OpenCode tool in status "error" with its reason in `output` (no
`error` field) rendered as {"error": null}. Fall back to `output`.
- A stream-json result of "[]" or "{}" decoded to an empty collection and
was then dropped as no result, so the judge could not tell a search
with no matches from a call with no recorded result. Only an empty
text result now counts as none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
🤖 Automated Claude routine — Env Pod PR Context Review. This is a bot, not Edgar Arakelyan; not a human review. Context from outside the diff, on the OpenCode prefix strip.
A related naming note on the stream-json path: I haven't checked a real OpenCode export to confirm which spelling it carries, so this is a question about intended coverage rather than a claim the parsing is wrong. Generated by Claude Code |
|
🤖 Automated Claude routine — Env Pod PR Context Review. This is a bot, not Edgar Arakelyan; not a human review. Context from elsewhere in this repo rather than from the diff: the explorer already parses five CLI trajectory formats, and two of them land awkwardly on the new dispatch.
Gemini slips past the new guard rather than being caught by it. Since Codex goes the other way. If Gemini and Codex runs aren't in scope for the direct judge, only the first looks worth a line: a format that reports a Generated by Claude Code |
…rver alias
- A Gemini CLI trajectory also closes on a {"type": "result"} row, so it was
read as Claude stream-json, parsed to nothing and reported a source, which
let an empty action log past the unrecognised-format guard. Stream-json is
now recognised by rows only Claude Code writes (a system row, or a turn
whose message is an object); Gemini and Codex trajectories raise instead.
- Claude Code names MCP tools mcp__<server>__<tool>, and the server alias is
often random per deploy (env7948), so one tool was named differently in
every run. The action log now uses the bare tool name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude Code names an MCP tool mcp__<server>__<tool> and OpenCode <server>_<tool>. Both now render as <server>_<tool>, with a random per-deploy server alias (env + 4 digits, from random_mcp_server_name, or the older mcp_<hex>) dropped, since it differs in every run. A declared server name (hg) is kept: OpenCode's names carry no separator that would tell a server prefix from the tool name, so it cannot be removed safely. On real Atlas runs, mcp__hg__x (Claude Code) and hg_x (OpenCode) now match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Replying to the two context-review comments above (Oct 17 and Oct 18). Both are fixed as of a8ebd3e. Gemini read as an empty stream-json run (255e19f): stream-json is now recognised only by rows Claude Code writes, a Tool-name spelling (a8ebd3e): I checked real OpenCode exports before changing this. They carry neither the hex alias nor
On 13 real trajectories (10 Claude Code, 3 OpenCode), no random alias is left and the same tool from a declared server has the same name in both. A tool served under a random alias still renders bare while one under a declared name keeps it; that follows the deploy, not the harness. The criterion regex knobs match with an unanchored |
Why
The direct (non-agent) judge's action log (
compaction_type: screenshot,trajectory_text: action_log) only parsed OTel spans. Trajectories from coding-agent CLIs (stream-json rows:system/assistant/user/result) and from OpenCode (partevents) produced an empty transcript. A direct judge on those runs therefore graded without seeing a single tool call. The fixed per-call cuts (110 chars of arguments, 160 of result) are also too small for MCP tool traffic.What
transcript_from_rawdetects the format:_transcript_from_otel.tool_useblocks are paired with theirtool_resultby id;is_errorresults become{"error": ...}; assistanttextblocks become messages; the closingresultrow is skipped (the judge already gets the final response).toolparts (input, then output or error) andtextparts; a part re-emitted as its state advances keeps its first position and its latest state.systemrow, or a turn whosemessageis an object). Gemini CLI also closes on{"type": "result"}but must not be read as an empty Claude run; Gemini and Codex trajectories are unrecognised (see the guard below).<server>_<tool>, with a random per-deploy server alias (env+ 4 digits fromrandom_mcp_server_name, or the oldermcp_<hex>) dropped. Claude Code'smcp__hg__slack_postand OpenCode'shg_slack_postboth render ashg_slack_post; a declared server name is kept because OpenCode's names give no way to tell it from the tool name.{"result": "{\"id\": ...}"}otherwise renders as mostly escape characters.action_logtakesmax_args_chars/max_result_chars.TrajectoryFiltergainsaction_log_max_args_chars/action_log_max_result_chars(validated likeaction_log_max_lines, serialized into_dict/from_dict). Defaults stay 110 / 160, so existing tasks render exactly as before.rubrics_verifierpasses them through.rendered_actionat the default cut, as before.Transcriptrecords the format it was read from (source). Whentrajectory_text=action_logand the trajectory is JSON in no format the action log reads, the verifier raises instead of handing the judge an empty log. Text that is not JSON at all stays a failed run, as before.Testing
tst/unit/task_step/verifiers/judge_utils/test_frame_selection_cli_formats.pycovers stream-json pairing (errors, images, orphan results), OpenCode parts (status updates, error, prefix), nested-JSON decoding, the new cuts, and that unrecognised lists still give an empty transcript. Its 4 behaviour tests fail onmainand pass here.test_rubrics_verifier_per_criterion.py: the new filter fields round-trip and are validated, and a stream-json trajectory reaches the direct judge's prompt as an action log at the configured cuts.[]) still reaches the judge. With the guard disabled, its test fails.outputkeeps the reason, and a[]stream-json result stays apart from no result. Each test fails with its fix reverted.tst/unit/task_step/verifiers/: 362 passed locally. The full suite is left to CI: the local environment lacks some newer dependencies.Why it matters
On a sample of real MCP-agent runs, a direct judge reading this action log agreed with the agent judge on about 92% of criteria, at a fraction of the latency.
No task's configuration changes in this PR.
🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no new actionable issues remain.Summary
Adds Claude Code and OpenCode tool calls to the judge’s action log, configurable text limits, and a guard against unsupported JSON formats.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Raw trajectory JSON] --> B{Recognized format?} B -->|OTel| C[Read spans] B -->|Claude Code| D[Pair calls and results] B -->|OpenCode| E[Read latest tool parts] C --> F[Transcript] D --> F E --> F F --> G[Apply configured text limits] G --> H[Judge action log] B -->|Unsupported nonempty JSON| I[Raise before judging]Reviews (3) · Last reviewed commit: "fix(verifiers): spell MCP tool names one..." · Reviewed by Greptile