The Harness Owns Approval, the Sandbox Owns Execution
An agent harness is the control plane that routes model calls, dispatches tools, and decides what needs approval. A sandbox is the execution plane that enforces filesystem, command, and network limits at the OS level. Skip that split in code and approval logic gets tangled with isolation logic, so changing one permission forces you to touch the execution environment too. SemaClaw, a personal-agent harness design, implements the split as a two-tier policy: internal tools (memory retrieval, workspace management) are pre-authorized, while external tools (payments, outbound messages, external APIs) require a fresh user confirmation on every call.
Approval Fatigue: Where the Safeguard Quietly Fails
Designing for human review on every call does not mean that review holds up over time. A pattern repeatedly observed in production is that once confirmation dialogs repeat, users stop reading them and default to clicking allow — at that point, an approval prompt is still on screen, but real human oversight has collapsed toward zero. Persisting "always allow" rules to disk so they survive a restart reduces the fatigue, but at a cost: the rule keeps applying long after the context that justified it has disappeared.
Schema Gating: A Defense That Removes the Tool Entirely
A stronger defense than a runtime permission check is removing the tool from the schema in the first place. A capability absent from the agent's callable tool list has nothing to bypass, so catching a declaration-behavior mismatch before deployment — a tool declared as a calculator that actually imports network access — is more robust than adding one more confirmation dialog.
From Design to Operations: A Harness Permission and Sandbox Gating Checklist
Fix target numbers before writing any code. A reasonable first goal is cutting human confirmation requests per session by at least 80% versus the pre-automation baseline (one reference case reports an 84% reduction), with a 100% match rate between a tool schema's declared permissions and what it actually uses. Set a maximum lifetime for "always allow" rules — 30 days is a reasonable default — at this same stage.
Failures repeat across four patterns. First, a tool schema declared broader than what is needed, so a calculator tool ends up with network access — a declaration-behavior mismatch. Second, confirmation dialogs firing double digits per session until users stop reading and just click allow — approval fatigue. Third, an "always allow" rule surviving a shift in project scope, so permissions granted under an old context keep applying to new work — missed expiration. Fourth, bundling internal and external tools into the same approval tier, so read-only internal calls demand approval as often as the genuinely risky external ones, which only deepens fatigue where it matters most.
Design recovery to reduce the volume of approval requests itself. At tool-declaration time, have a policy engine cross-check declared capabilities against actual imports and calls, blocking mismatches before deployment. When approval fatigue crosses a threshold — more than 10 confirmations in a session, say — automatically shift that session to a stricter sandbox isolation level, buying back safety margin without requiring a human click. Never let an "always allow" rule persist without an expiration, and enforce a renewal cadence.
Build the operations checklist on a default-deny schema. Lock in a two-tier approval level in code — internal tools pre-authorized, external tools confirmed on every call — and document a selection table matching sandbox isolation level (none, gVisor, microVM, WASM) to task risk. Pre-deployment scenario tests must include a declaration-behavior mismatch case, such as a calculator tool quietly picking up network access. Audit logs need tool name, request parameters, the approving party (human or automated policy), isolation level, and time-to-approval as fields, so an incident can be reconstructed after the fact.
Run continuous improvement weekly: pull the approval log, auto-expire any "always allow" rule untouched for more than 30 days, and shrink the schema itself in the next release for any tool with a recurring declaration-behavior mismatch. If the approval-prompt reduction rate stalls, automation may just be relocating the total volume of human intervention rather than cutting it — so track confirmations per session alongside how often sessions escalate to a stricter isolation level.
Takeaways at a Glance
Approval fatigue is solved by reviewing less, not reviewing more. Narrow the tool schema to default-deny, split internal and external tools into a two-tier approval flow, and document your isolation-level selection criteria and "always allow" expiration policy alongside the audit log — that makes for a sturdier harness than one more confirmation dialog ever could.