feat(v1): configurable agentic-judge grading policy, uploads, and reward weights - #2109
Conversation
…ard weights
- prompt/prompt_file: policy-only override; the verdict contract and workspace
note are always appended, and the task statement is appended when the policy
doesn't place {prompt} itself
- [env.uploads]: transcript/trace paths (nullable) plus trace.info key -> path
uploads (e.g. a captured patch as a real file in the judge's box)
- the uploaded raw record strips rewards/metrics/task so the judge can't anchor
on the graded run's own scores or the row's ground truth
- JudgeTask replays the source task's setup in the judge's box, so the judge
sees the solver's true starting state (e.g. a repo reset to base commit)
- judge_weight / task_reward_weight for reward composition
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ce-centric naming - [env.task]: prompt (inline text or .md/.txt policy file, one field) + uploads - [env.score]: task_weight (default 0 = judge-only) + judge_weight (default 1) - raw record uploads as /tmp/trace.json; prompt wording says trace - docs/v1/env.md diff dropped Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The box is provisioned once from the solver's runtime policy via agents.solver.provision; solver and judge play in it back to back, so the judge inspects the work exactly as the agent left it. --env.shared-runtime false keeps the fresh-box mode: a new box mirroring the solver task's world, replayed to the starting state via the source task's setup. The container requirement follows whoever provisions the judge's box (solver when shared, judge otherwise), and a shared box skips the setup replay (it would destroy the solver's work). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- verdicts use the plugged rubric judge's criteria format (mirrored, not
imported): {name, text, weight=1, choices worst->best} from a .toml/.json
criteria file (--env.task.rubric), one built-in solved criterion by default;
the judge writes {"verdicts": [{name, reason, verdict}, ...]} to
/tmp/verdict.json and finalize validates strictly (name set must match, only
on-menu answers), records judge/<name> metrics on the solver trace, and
rewards the weighted mean
- uploads collapse to --env.task.trace (the raw record): a policy that needs a
trace.info field as a file (e.g. the captured patch) instructs the judge to
extract it — no rendered-transcript or per-key upload knobs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AgentConfig.replay points a seat at a saved run dir (or traces.jsonl): _ReplayEpisodeAgent revalidates the saved trace for each task and re-stamps it with the seat's standing instead of sampling, so the other seats iterate against fixed work — e.g. re-judging saved solver rollouts through the real agentic-judge env: --env.solver.replay <run>. A single-seat run matches any requested seat name. A replayed seat runs no model and leaves no runtime state, so envs that need the seat's box refuse it (agentic-judge requires --env.shared-runtime false and says so at construction). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… traces" This reverts commit 29c7333.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- shared_runtime knob removed: the judge always plays in the solver's box (container requirement on the solver); the fresh-box mode is gone with it - the uploaded record is complete — nothing stripped; the workspace note tells the judge recorded scores and reference material are context, never the standard Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… drop finalize docstring Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…onfig Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ge box A solver could leave /tmp/trace.json as a symlink onto any writable file; the upload would follow it and overwrite the target. Same treatment the verdict file already gets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…write-wins Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ca25ca9. Configure here.
ApprovabilityVerdict: Needs human review This PR adds new feature capabilities including configurable rubric criteria, custom prompt/hint files, weighted reward composition, and changes the architecture so the judge shares the solver's container. These new features and runtime behavior changes warrant human review. You can customize Macroscope's approvability policy. Learn more. |
The judge plays in the solver's box, but its config defaulted to subprocess — tripping the base env's host-execution warning and stamping a runtime the judge never uses onto its trace. Overwrite it with the solver's policy at construction so both stay truthful. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ord can be huge --env.task.hint (inline text or .md/.txt file) injects a Hints section between the policy and the verdict contract — task-family pointers into the trace or box (math: where the reference answer lives; SWE: diff the repo / read info.patch). The workspace note now also tells the judge to peek at the record selectively instead of dumping it whole. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Summary
Makes the built-in
agentic-judgeenv a complete out-of-the-box judge, configurable end-to-end from TOML/CLI so domain-specific setups (SWE, search, ...) are pure.md+.tomlartifacts:--env.solver.runtime.type docker|prime, checked at construction); the solver plays the task in it and the judge then inspects the work exactly as the agent left it. The judge pins only its model; its runtime policy is unused. (Borrowed boxes are never retried into, so per-agent rollout retries don't apply; episode retries still do.)[env.task](JudgeTaskConfig) — the judge's minted task:prompt: grading-policy override, one field — a value ending in.md/.txtis read from disk, anything else is inline policy text (a missing file fails at env construction). A policy replaces only the judging instructions: the verdict contract and workspace note are always appended, so it can never break verdict parsing.{prompt}interpolates the solver task's prompt; if absent, the task statement is appended.hint: optional hints injected as their own section (inline text or a.md/.txtfile) — task-family pointers into the trace or box, e.g. where the reference answer lives for math, or "diff the repo / readinfo.patch" for SWE.rubric: criteria the judge grades against — a.toml/.jsoncriteriafile in the plugged rubric judge's format ({name, text, weight=1, choices worst→best}; logic mirrored, not imported), defaulting to a single built-insolvedcriterion. The judge writes{"verdicts": [{"name", "reason", "verdict"}, ...]}to/tmp/verdict.json;finalizevalidates strictly (name set must match, only on-menu answers), recordsjudge/<name>metrics on the solver trace, and rewards the weighted mean./tmp/trace.json(not configurable) — messages, tool calls,trace.infoartifacts (e.g. a captured patch), and the task row with whatever scores/reference material it carries; nothing is stripped. Independence is prompted, not enforced: the workspace note tells the judge recorded scores and references are context, never the standard, and warns that the record can be very large — peek selectively, never dump it whole. A policy that needs a record field as a file instructs the judge to extract it.[env.score](ScoreConfig) — reward composition on the solver's trace:task_weight(default 0: judge-only reward) rescales the taskset's own rewards,judge_weight(default 1) scales the verdict.Verification
uv run eval @ configs/agentic_judge.toml(gsm8k, docker, glm-5.2), re-smoked after every design round (rubric verdicts, shared-only box, full-record upload): trace records show the judge on the borrowed solver box, verdicts land asjudge/solved+ thejudgereward, and the defaultscore.task_weight=0zeroes the task's own reward.--env.id agentic-judge(prime + docker, bash seats), and a 64-instance dogfood: the judge flipped 8/9 manually-identified narrow-test false negatives to ≥0.7 while keeping 7/8 pytest-passing controls at 1.0; on a re-run with rubric verdicts and judge-side patch extraction it flipped 17/23 pytest-failures and kept 36/40 passes, agreeing with the earlier judge on every audited case. Gap experiment, run twice on 256 fixed-seed instances x {glm-4.5-air, glm-5.2}: judge scoring widened the model gap from +0.206 to +0.290 (continuous 0-10 verdicts; 243 pairs) and, on the rubric-verdict re-run, from +0.210 to +0.380 (binarysolved; 224 pairs; delta +0.170, 95% bootstrap CI [+0.089, +0.246]). (Experiment builds predate the shared-box/full-record round.)ruff check,ruff format --check, repoty check(diagnostic count unchanged vs main), full non-e2etests/v1green;test_env_id_agentic_judgecovers the config surface (needsPRIME_API_KEY+ docker).