Skip to content

The Narrated Gate

Agent Skills routinely include an instruction like this before a consequential write:

Show the user a review table and ask for confirmation before proceeding.

That instruction is unenforceable — not because agents ignore instructions, but because nothing in the system can tell the difference between the agent asking and the agent describing asking. Both produce text. The confirmation the skill author intended is a self-report, and self-reports are exactly the thing that fails.

Picture a skill that logs a meeting to your CRM: resolve the account, build the records, show a review table, get a “yes”, then write. On a bad run, the agent goes straight from resolving the account to the write — twice — and then prints the review table:

Field Value
Subject Acme Corp — Project Sync
Related To Acme Corp — Platform Modernization (Opportunity)

Every field correct. It may even close with “if you’d rather link this elsewhere, say so and I’ll re-point it” — offering a choice that has already been made and committed. That table is a receipt, not a gate: printed after the write, indistinguishable at a skim from the review the skill asked for. Detecting the difference means reading the raw transcript and correlating it against the tool-call log by hand.

This is not a hypothetical failure class. In Zapier’s AutomationBench, between 72% and 91% of failures across frontier models were the agent reporting success against a wrong world state. A skipped gate and an honored gate produce identical-looking output; the omission is invisible.

Replace the prose instruction with a call to elicit_confirm and three things become true that no wording of the prose version can achieve:

  1. It cannot be satisfied by narration. The agent can still neglect to call the tool — nothing prevents that — but it cannot manufacture a "confirmed": true. The value comes from outside the model: a human pressed a button, or nobody did. This converts an invisible omission into an observable one, which is the entire gap.

  2. It is mechanically assertable. A tool call lands in the transcript, so an eval or CI check can assert, in code:

    the elicit_confirm call precedes the first write

    That moves the gate out of the assertions that need a model grader (“did the agent show a review table and wait?”) and into the ones an exact check settles. A judgment call becomes a journal lookup.

  3. It fails closed. In a headless or non-interactive run — the same environment where a prose gate is skipped silently — elicit_confirm returns promptly with

    { "confirmed": null, "reason": "dismissed" }

    It does not hang, and it never returns true without a human. A skill written to proceed only on "confirmed": true therefore stops in exactly the situation where prose proceeded. If the skill is ever run unattended, that is the difference between a halted run and an unauthorized write.

In the skill, replace the prose checkpoint with the call and a three-state rule:

Call elicit_confirm with a summary of the records about to be written. Proceed only if confirmed is true. Treat false and null as stop.

{
"message": "Create 2 Tasks on 'Acme Corp — Platform Modernization'?",
"labels": { "ok": "Create records", "cancel": "Don't write" }
}

false is a human saying no; null means no answer was obtained. Neither is permission. The full three-state contract is on the elicit_confirm page.

For choosing among candidates (which account? which opportunity?), the same argument applies to elicit_selection — a native selector returning selectedIds beats rendering a markdown table and hoping the reply parses.

  • Skipping the call is still possible. Elicitation makes the omission observable, not impossible. Pair the gate with the transcript assertion above in your evals, so a skipped gate fails the run instead of passing unnoticed.
  • It does not verify the write landed. An elicitation gate runs before a write and answers “is this authorized?” — it says nothing about whether the write then succeeded. Post-action verification is a separate, complementary check.
  • Host support varies. Run elicit_doctor and consult the Support Matrix before depending on a mode — advertised is not the same as working.
  • Interactive confirm is bounded. elicit_confirm waits at most an hour. For scheduled or unattended runs where a human may be hours away, use the Pro Approval Flowelicit_approval + elicit_await hold the question open for up to 7 days, reach the reviewer on any device, and leave an audit trail.