Skip to content

test(agent): cover the confirm gate and approve-all scoping in the Run loop#132

Merged
xunholy merged 1 commit into
mainfrom
test/agent-loop-gates
Jun 26, 2026
Merged

test(agent): cover the confirm gate and approve-all scoping in the Run loop#132
xunholy merged 1 commit into
mainfrom
test/agent-loop-gates

Conversation

@xunholy

@xunholy xunholy commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

Builds on the scriptable-LLM seam (#131) to cover the two highest-value agent-loop behaviours still lacking end-to-end coverage — both safety properties exercised through the real gate chain in Run, not in isolation.

  • ConfirmDenyInLoop — a tool at/above the confirm threshold prompts; a deny blocks execution; the refusal is fed back as a tool_result and the model re-plans to completion. The loop-level analogue of the unit confirm tests.
  • ApproveAllScoping — the crown-jewel "approve all" property. Approve-all auto-approves subsequent same-or-lower-risk tools in the same turn (the convenience), but must not silently escalate — a tool above the approved tier re-prompts. The batch is [Low, Low, Medium] with approve-all on the first Low (ceiling = Low): the second Low is auto-approved (no prompt), the Medium exceeds the ceiling and re-prompts.

Design note: the approve-all test uses a Low offline tool (tool_search) for the approved legs so the assertions are purely about gating order with zero hardware execution (the escalating Medium tool is denied before it runs) — no panics, no noisy recovery logs, no audit wiring needed.

Verification

  • task ci green (lint 0 / vet / build / test:full 0 fail / govulncheck clean); task eval 15/15.

Test-only, no production change — no release on its own (rides the next feature release, with #131).

…n loop

Builds on the scriptable-LLM seam (#131) to add the two highest-value
agent-loop behaviours that still lacked end-to-end coverage — both safety
properties exercised through the real gate chain in Run, not in isolation:

- ConfirmDenyInLoop: a tool at/above the confirm threshold prompts; a deny
  blocks execution; the refusal is fed back as a tool_result and the model
  re-plans to completion. The loop-level analogue of the unit confirm
  tests.

- ApproveAllScoping: the crown-jewel "approve all" property. Approve-all
  auto-approves subsequent same-or-lower-risk tools in the same turn (the
  convenience), but must NOT silently escalate — a tool above the approved
  tier re-prompts. The batch is [Low, Low, Medium] with approve-all on the
  first Low (ceiling = Low): the second Low is auto-approved (no prompt),
  the Medium exceeds the ceiling and re-prompts. Using a Low offline tool
  (tool_search) for the approved legs keeps the assertions purely about
  gating order with no hardware execution — the escalating Medium tool is
  denied before it runs.

Test-only; no production change (the seam already exists). Verified:
task ci green (lint 0 / vet / build / test:full 0 fail / govulncheck
clean); task eval 15/15. internal/agent/run_loop_gates_test.go.
@xunholy xunholy merged commit 627c7d1 into main Jun 26, 2026
5 of 6 checks passed
xunholy added a commit that referenced this pull request Jun 26, 2026
…loop (#136)

Completes the agent loop's end-to-end safety/recovery coverage. The
scriptable-LLM seam (#131) and reflectorFn seam already existed; the
operator-steering and failure-recovery behaviours were tested at the unit
level (confirm_test, reflexion_test) but never driven through the real
multi-turn Run loop. Three integration tests close that gap:

- ReviseSkipsToolAndInjectsRevision: a gated tool answered with
  DecisionRevise must NOT execute; the synthesised tool_result marks it
  skipped; and the operator's revision is injected into the conversation as
  a fresh user turn ("please re-plan") so the model re-plans. Asserted
  against the marshalled history.
- ReflexionFiresOnToolError: a failing tool (cbor_decode on bad hex)
  triggers the reflector and its diagnosis is appended, delimited, to the
  tool result the model sees.
- ReflexionCapPerTurn: four failing tools in one assistant message trigger
  at most maxReflectionsPerTurn reflections, proving the per-turn counter
  is shared across every tool in the turn (not one cap per tool).

With the dispatch / tool-call-cap / read-only / confirm-deny /
approve-all-scoping tests from #131-#132, the loop's safety and recovery
behaviours are now covered end-to-end.

Test-only; no production change. Verified: task ci green (lint 0 / vet /
build / test:full 0 fail / govulncheck clean); task eval 15/15.
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.

1 participant