The verification gap nobody talks about

Blind and low-vision users already use AI agents to operate the web — filling forms, managing lists, navigating sites with dozens of menus. The part that still doesn't work is trust. A sighted user glances at the screen and knows instantly whether an action succeeded. A screen reader user has no equivalent glance. Today, verifying what an agent did means re-navigating the whole page by hand, which erases most of the time an agent was supposed to save.

A study of 16 screen-reader-using programmers found that verifying AI output, not producing it, was consistently the most time-consuming part of their workflow. That's the problem Prove It is built to solve.

What it does

Prove It is a household task manager where every action an AI agent takes — adding a task, marking it done, changing its priority, deleting it — produces a structured, inspectable receipt: what changed, exactly where it lives in the page, and what to say if it failed. Every receipt includes a deep link. Clicking it moves real keyboard and screen-reader focus straight to the element that changed, so the person hears their own assistive technology confirm the action in its own words — not a paraphrase from the agent, and not a guess from re-navigating the page.

Failures get the same treatment. If the agent tries something invalid — an unknown task, a bulk delete it isn't allowed to perform — that produces an error receipt too. Proving what an agent didn't do, and why, is as important as proving what it did.

How I built it

The whole app runs on one function, performTool(name, args), which is the single source of truth for all seven tools (list_tasks, add_task, set_task_status, update_task_priority, delete_task, focus_task, get_last_receipt). That one function is wired to two transports:

  1. Native WebMCP. On load, the page feature-detects document.modelContext / navigator.modelContext and calls registerTool() for each tool with a strict JSON Schema. Any WebMCP-aware agent — Chrome's built-in agent with the WebMCP flag on, or ChatGPT's in-app browser — can discover and call these tools directly, no code changes required.
  2. In-page demo agent (fallback). If WebMCP isn't available in the browser, the same seven tool schemas are sent to the Claude API as tools, so anyone can test the full loop — natural language in, tool call, receipt, verify — in an ordinary browser with no flags.

Every write tool returns { tool, changeType, timestamp, target: {selector, description}, before, after, errors }. changeType is created, updated, deleted, verified, or error. Receipts render as a literal torn-paper "receipt tape" in the UI — a visual metaphor for the whole concept: a printed proof of exactly what happened.

The negative test

There is no delete_all_tasks or bulk-export tool, on purpose. The agent is instructed to delete tasks one at a time and to ask before any bulk action — so "prove it" also covers proving the agent can't do something irreversible, because the capability was never exposed as a tool in the first place, not because a prompt told it to behave.

Challenges I ran into

The trickiest part wasn't the receipts — it was making the deep link mechanism actually work for a screen reader, not just look like it does. Early on, clicking a receipt just scrolled to an element and added a CSS glow, which does nothing for someone who can't see the glow. The fix was making sure every deep-linked element is a real, focusable DOM node (tabindex="-1" where it isn't natively focusable) with an aria-label that reflects its current state, and that the deep link calls .focus() on that exact node. The visual highlight is a bonus for sighted collaborators, not the verification mechanism itself.

The other challenge was keeping one tool contract honest across two very different transports. Rather than writing tool logic twice, performTool() is called by both the native registerTool().execute() handlers and the in-page agent loop, so a receipt means the same thing regardless of which agent produced it.

What I learned

Verifiability isn't a feature you bolt onto an agent's output after the fact — it has to be a structural property of the tool contract itself: a real DOM target, a real before/after diff, a real focus event. WebMCP's tool registration model turns out to be a genuinely good fit for this, because the receipt an agent returns and the page's actual state can be forced to be the same object, not two things that merely claim to agree.

What's next

Extending the receipt/deep-link pattern beyond a single demo page — a small library other WebMCP sites could adopt so "verifiable receipts" becomes a convention across the ecosystem, not a one-off feature of this app.

Built With

Share this project:

Updates

Submission history