feat(tools): agent-callable verify/critique tool (#4196)#4199
Conversation
Add `verify`, a tool the agent can CHOOSE to call to run an adversarial self-review of its own recent work before claiming a non-trivial change done. It spends extra test-time compute on the model's own judgment: the agent states a claim plus optional scope (recent git diff / staged diff / named files / the requirement), and an independent critic runs at elevated reasoning (default Max, independent of the session tier) tasked to REFUTE it, returning structured findings (issue, severity, evidence, suggested fix) plus a verdict and an unresolved-risk flag. Design: - Critic mechanism: a single-shot adversarial model call (mirrors the `review` tool) with reasoning_effort set explicitly, so elevated reasoning — the test-time-compute lever — never inherits a low tier. - Bounded / no runaway (hard requirement): the critic request carries `tools: None`, so the critic literally cannot invoke `verify` (or any tool) — recursion is impossible by construction rather than by a denylist. A task-local re-entry guard adds defense in depth. - Fail-safe parsing: unstructured or "upheld-but-has-serious-findings" critic output is treated as unresolved risk, guarding the green-CI-but-wrong trap. - Disjoint from subagent/mod.rs (parallel #4193 change) — calls no sub-agent internals; evidence is gathered deterministically by the tool. - Opt-out: `Feature::Verify` (`[features] verify_tool`, default on) gates registration in the agent runtime surface. Tests: tool contract (name/schema/registered/model-visible), a critic-finds-a-planted-bug test via MockLlmClient (also asserting the outgoing request is elevated + toolless), the recursion re-entry guard, fail-safe verdict normalization, and the feature-gated registration.
|
Claude finished @Hmbown's task in 3m 30s —— View job Review:
|
) Address two correctness gaps from review of PR #4199 (design-agnostic; the direct-call-vs-sub-agent decision is unchanged, still pending maintainer review). (1) normalize() only forced unresolved_risk on critical/high findings, so verdict=upheld + a MEDIUM finding + critic-set unresolved_risk=false reported "no unresolved risk" — the exact green-but-wrong case this tool exists to catch. Now any finding at MEDIUM severity or above forces unresolved_risk=true and forbids an `upheld` verdict (downgraded to `uncertain`; critical/high still downgrade to `refuted`). `low` nits remain exempt so they don't block a claim. (2) gather_git_diff() built `git diff {base}...HEAD`, which captures branch commits but OMITS the uncommitted working-tree changes the agent usually wants to verify before claiming done. Replaced with gather_diff_evidence(): for the working-tree scope with a base it now emits BOTH the committed-since-base diff (`git diff base...HEAD`) AND the uncommitted diff (`git diff HEAD`, with a plain-worktree fallback on an unborn HEAD), as two labelled evidence blocks. Tests: - upheld_with_medium_finding_flags_unresolved_risk: medium finding => unresolved_risk true and verdict uncertain (not upheld). - upheld_with_only_low_finding_stays_upheld: low-only nit stays upheld, no unresolved risk (documents the carve-out). - diff_scope_with_base_includes_uncommitted_worktree_changes: real dirty repo + base ref => evidence contains both the committed-since-base and the uncommitted marker, in two distinct labelled blocks.
|
Claude finished @Hmbown's task in 59s —— View job Re-review after push (8666e4c)
New commit 8666e4c ("verify — flag medium findings + include worktree diff") addresses two correctness gaps and is a clean improvement:
This also fixes my prior finding #1 (staged+base invalid git invocation): the staged branch now runs Still open from the original review (not touched by this commit, still worth addressing before merge):
Not blocking, unchanged from before: Net: the pushed fix is correct and well-tested for what it targets. Recommend addressing #2/#3 (small, low-risk) before merge, but neither blocks on correctness grounds. |
Approved TTC design: one shared CriticEngine behind three triggers — the agent-invoked verify tool (Hmbown#4196, MVP Hmbown#4199), advisor watcher (Hmbown#3982), and post-turn verification gates (Hmbown#4013) — plus clamp->floor sub-agent reasoning (Hmbown#4137). Synthesized from three independent reviews (verify-tool contributor, GLM 5.2, internal analysis). Design only, no code; v0.8.69, behind stopship. Drafted with agent assistance.
Resolve review base refs to commits, compute their merge base with HEAD, and compare that tree to the index when staged review is requested. This includes committed and staged branch work without admitting unstaged edits or option-shaped refs. Cover the combined staged-plus-base path with a real temporary Git repository fixture. Found while auditing the deferred Hmbown#4199 verify-tool PR. Signed-off-by: Hunter B <hmbown@gmail.com>
Fixes #4196
What
Adds
verify— a tool the agent can choose to call to run an adversarial self-review of its own recent work before claiming a non-trivial change done. It spends extra test-time compute on the model's own judgment (elevated reasoning + an independent critic), on demand, not as an always-on watcher.The agent states a claim plus optional scope (recent git diff / staged diff / named
files/ the originalrequirement/ afocusrisk). An independent critic then runs at elevated reasoning (defaultMax, independent of the session tier), tasked to REFUTE the claim, and returns structured findings.Design (for maintainer review before merge)
verify(distinct fromreview= code review of a target, andrun_verifiers= external test/build gates).claim(required); optionalrequirement,scope(diff|staged|none),base,files[],focus.reviewtool) withreasoning_effortset explicitly on the outgoing request, so the test-time-compute lever never inherits a Low session tier. The tool gathers evidence deterministically (git diff by scope + named file contents) and hands it to the critic — it does not touchsubagent/mod.rs(parallel fix(fleet): interactive TUI in-process spawn ignores profile provider pin (#4093 TUI follow-up) #4193), keeping the change additive and disjoint.{ verdict: refuted|upheld|uncertain, summary, findings: [{severity, issue, evidence, suggested_fix}], unresolved_risk }+ usage metadata.Bounded / no runaway (hard requirement)
tools: None, so the critic literally cannot invokeverify(or any tool) — recursion is impossible by construction, not by a denylist that could be forgotten.verifyrefuse if entered while a critique is already in progress.Opt-out
Feature::Verify([features] verify_tool, default on) gates registration in the agent runtime surface; cost is only incurred when the agent chooses to call it.Tests
MockLlmClient(also asserts the outgoing request is elevated + toolless, and threads the claim/requirement/evidence).Verification gate
cargo fmt --all --check— cleancargo clippy --workspace --all-features --locked -D warnings …— cleancargo test --workspace --locked— 6004 passed; only the 2 known pre-existing unrelated failures (git_repo_root_reports_attempted_paths_when_no_repo_found,skill_hotbar_action_activates_skill_through_dollar_alias), confirmed failing identically on pristineorigin/main.cargo build --release -p codewhale-tui— successNote
Do not merge yet — this is a new product capability; the DESIGN should be reviewed with the maintainer first. A follow-up could swap the critic mechanism to a spawned sub-agent (autonomous exploration) behind the same tool contract once #4193 settles; the recursion guard would then also need to exclude
verifyfrom that sub-agent's tool allowlist.