Summary
simard merge-pr <PR> is unusable through its recipe-runner-backed merge judge: it always fails with
ERROR simard: command failed error=base type 'recipe-merge-judge' failed during invocation:
recipe-merge-judge: no verdict keyword (ready/not_ready/unclear) found in recipe output;
raw="Recipe: merge-readiness-judge (v1.0.0)\nSteps: 1\n\nRecipe 'merge-readiness-judge': SUCCESS (34.0s)\n [completed] judge-merge-readiness (34.0s)\n\n"
The recipe runs (agent executes for ~30-44s, step SUCCESS), but the verdict is never surfaced to the parser.
Root cause
src/stewardship/recipe_merge_judge.rs::judge() invokes:
Command::new("recipe-runner-rs").arg(recipe).arg("-c")...output()
i.e. without --output-format json. recipe-runner-rs's default --output-format text prints only the step-progress summary to stdout; the agent's actual output (the {"verdict": ...} JSON the merge-readiness-judge.yaml recipe is documented to emit on agent stdout) is not included. parse_merge_verdict_from_text(raw) therefore finds no ready/not_ready/unclear keyword and errors. This is the same class of bug fixed for the distill path in #2401 ("capture agent JSON via recipe-runner-rs --output-format json"), but the merge-judge call site was never updated.
Reproduced on deployed simard v0.19.1 and v0.22.0 (both fail identically) against a fully merge-ready, CI-green, MERGEABLE/CLEAN PR.
Impact
The gated merge path (simard merge-pr) cannot complete via the recipe judge — every merge is blocked at the infra level, regardless of PR readiness.
Workaround (used to land PR #2427 / #2426)
build_merge_judge() falls back to the direct LlmMergeJudge when RecipeMergeJudge::new() returns None, which happens when recipe-runner-rs is not on PATH. Running simard merge-pr with ~/.cargo/bin (where recipe-runner-rs lives) removed from PATH routes through the working LlmMergeJudge, which correctly returned a verdict and merged.
Proposed fix
In recipe_merge_judge.rs::judge(), pass --output-format json to recipe-runner-rs and extract the agent step's output from the JSON envelope before calling parse_merge_verdict_from_text (mirroring the #2401 distill fix), or have parse_merge_verdict_from_text read the agent output from the structured result rather than the human progress summary.
Summary
simard merge-pr <PR>is unusable through its recipe-runner-backed merge judge: it always fails withThe recipe runs (agent executes for ~30-44s, step
SUCCESS), but the verdict is never surfaced to the parser.Root cause
src/stewardship/recipe_merge_judge.rs::judge()invokes:i.e. without
--output-format json.recipe-runner-rs's default--output-format textprints only the step-progress summary to stdout; the agent's actual output (the{"verdict": ...}JSON themerge-readiness-judge.yamlrecipe is documented to emit on agent stdout) is not included.parse_merge_verdict_from_text(raw)therefore finds noready/not_ready/unclearkeyword and errors. This is the same class of bug fixed for the distill path in #2401 ("capture agent JSON via recipe-runner-rs--output-format json"), but the merge-judge call site was never updated.Reproduced on deployed simard v0.19.1 and v0.22.0 (both fail identically) against a fully merge-ready, CI-green,
MERGEABLE/CLEANPR.Impact
The gated merge path (
simard merge-pr) cannot complete via the recipe judge — every merge is blocked at the infra level, regardless of PR readiness.Workaround (used to land PR #2427 / #2426)
build_merge_judge()falls back to the directLlmMergeJudgewhenRecipeMergeJudge::new()returnsNone, which happens whenrecipe-runner-rsis not onPATH. Runningsimard merge-prwith~/.cargo/bin(whererecipe-runner-rslives) removed fromPATHroutes through the workingLlmMergeJudge, which correctly returned a verdict and merged.Proposed fix
In
recipe_merge_judge.rs::judge(), pass--output-format jsontorecipe-runner-rsand extract the agent step's output from the JSON envelope before callingparse_merge_verdict_from_text(mirroring the #2401 distill fix), or haveparse_merge_verdict_from_textread the agent output from the structured result rather than the human progress summary.