Test Agent Approval Against Changed Tool Arguments
Suneet Malhotra
Oct 29, 2026
An approval should authorize the action the person reviewed. If an agent changes the recipient, attachment, or destination afterward, the earlier approval should no longer permit execution. For QA, the useful test is to pause the workflow after approval, change one meaningful field, and verify that the write never reaches its destination.
This extends The Plan Is Not the Permission with a reproducible boundary test. Everything below is a proposed synthetic email workflow. The addresses, files, and outcomes are test fixtures, not production observations or employer data.
Where the approval sits
Imagine an assistant that prepares a release summary and offers to email it. The path is: draft the message, show a preview, record approval, queue execution, and call the mail adapter. A revision can enter between any two steps. A worker might reload the latest draft when it starts, even though the person reviewed an earlier version.
MCP can carry the tool invocation. Its 2025-06-18 tools specification describes calls containing a tool name and arguments, and recommends human oversight. It does not mandate a particular interaction design. A confirmation dialog alone therefore tells QA little about which arguments the executor will receive.
The established security principle is narrower: bind authorization to the significant transaction details and check it at execution. OWASP's transaction authorization guidance recommends server-side enforcement, invalidating authorization when transaction data changes, and a final gate before execution. The following fixture applies that principle to an agent integration.
Build an action you can compare
Create proposal P17: send release-summary.txt to reviewer@example.test from a synthetic project account. Store the exact recipient list, subject, message body, attachment bytes or immutable version, account, and tool destination. Give the proposal a revision identifier. Render the preview from that stored revision.
For this exercise, approval A17 refers only to P17 revision 1. It is not a general permission to send whatever the assistant later considers appropriate. The executor receives the proposal reference and resolves the approved snapshot through a trusted store.
A digest can help compare snapshots, but a hash is not permission. If a caller can replace both the payload and the expected digest, matching them establishes nothing. The approval record must be protected separately from editable draft state. Define canonical serialization if you hash structured fields, and include attachments by content or immutable version rather than filename alone.
Pause at the dangerous boundary
Use a deterministic barrier immediately before the executor performs its final authorization check. A barrier is a test-controlled pause that the test can release explicitly. It avoids relying on a lucky sleep interval to create the race.
Start each scenario from a fresh fixture. Approve revision 1, wait until the worker reaches the barrier, apply one mutation, and release the worker. Use an instrumented fake mail adapter with a destination ledger. Assert both the executor result and the recorded side effects.
| Scenario | Mutation after approval | Required result in this fixture |
|---|---|---|
| Baseline | None | One delivery of revision 1 |
| Recipient change | Replace reviewer with outsider@example.test | Denied; zero deliveries |
| Attachment change | Replace bytes but keep filename | Denied; zero deliveries |
| Tool destination change | Switch the bound mail account | Denied; zero deliveries |
| New approval | Approve the changed revision explicitly | One delivery of that revision |
These are expected test outcomes, not measured results. The baseline matters: a system that refuses every request can pass all denial cases while being unusable. The new-approval case also proves that the workflow can recover without silently reusing A17.
Check what the person actually saw
Add an assertion before clicking approve: the preview recipient, attachment version, and account must match the stored proposal. Then compare the destination ledger with the same snapshot after execution. That produces a three-way check across preview, approval record, and actual tool arguments.
Do not reduce this to checking an approved boolean in a trace. In the synthetic fixture, capture proposal ID, revision, approval ID, actor, decision reason, adapter invocation count, and delivered content identity. Keep full message content inside the controlled fixture; production evidence needs its own retention and access rules.
Run a negative control in a local test version: deliberately remove the revision comparison while keeping the rest of the executor unchanged. The recipient-change scenario must now fail its zero-delivery assertion. Restore the comparison and require it to pass. This demonstrates that the suite observes a meaningful authorization defect rather than an unrelated validation failure.
A final check still leaves a race
Now add a second barrier after the final check but before the adapter reads its input. Mutate the original draft again. For the immutable-snapshot design proposed here, execution must still use the approved snapshot. If the adapter reloads mutable draft fields, the system has reopened the gap it just checked.
Choose and document a contract: execute the frozen approved action, or reject when its required version is no longer current. Either can be intentional. Executing changed content under the old approval is the failure this exercise targets. If the destination cannot enforce a resource version, document that limitation instead of claiming an atomic guarantee across services.
Duplicate delivery and uncertain outcomes need additional tests. Reusing the same approval after a timeout must not become a new action by accident. The related cancellation exercise explains why a stopped client does not establish whether a destination committed a write.
For the first regression test, keep the scope small: one fake adapter, one approval, one changed recipient, and an assertion on destination state. Which field in your agent workflow can still change between the preview and the write?
Share this post
You Might Also Like
Test AI Stream Failure Before Saving the Answer
A stream can deliver plausible text and still fail. Interrupt a synthetic response, then check the UI, saved record, and completion metric before trusting success.
AI & AutomationSwap Answer Order Before Trusting an AI Judge
An AI judge should pick the same answer after its position changes. Build a swap test, verify winner mapping, and separate order sensitivity from ordinary variation.
Career & Best PracticesThe Cheapest Test Is the One You Run Before You Build
The most expensive engineering mistakes in my systems were not hard bugs. They were reasonable implementations of questions nobody had defined.
Quantitative TradingA Signal Is Not a Decision
A signal says that conditions are interesting. It does not say that the portfolio should trade. The missing layer is the decision record between detection and execution.
Latest Blog Posts
Test AI Stream Failure Before Saving the Answer
A stream can deliver plausible text and still fail. Interrupt a synthetic response, then check the UI, saved record, and completion metric before trusting success.
Swap Answer Order Before Trusting an AI Judge
An AI judge should pick the same answer after its position changes. Build a swap test, verify winner mapping, and separate order sensitivity from ordinary variation.
Test Trace Sampling Before Trusting AI Failure Rates
Keeping every error trace helps debugging but distorts failure percentages. Test sampling policies with a synthetic workload and verify the denominator behind each dashboard metric.
Related Tools & Demos
The QA Field Manual to Language Models
A free 24-chapter book. Start at βwhat is AI, really?β and finish with a small language model you built yourself β one that reads a failing Playwright test and proposes a fix you can run. Read it in your browser, or download the PDF or the Mac app.
View Source Code βMulti-Model LLM Harness
One interface to call any AI model β capability routing, fallback chains, budgets, circuit breakers, and a quality feedback loop. A practical architecture pattern write-up.
Automated Trading System
Multi-engine trading platform with real-time risk management, regime-based strategy selection, and automated order execution.
View Source Code β
Stay in the Loop
Get weekly insights on AI-driven QA, engineering leadership, and automation strategies.
No spam, ever. Unsubscribe anytime.