Discovery: /sweep and /earshot
Bob tabbed through pages by ear (25 Tabs from the top, real NVDA) while explore subagents located suspects in parallel: seven sweeps that wrote proposals, on six pages of three apps, including one headless run from Bob Shell (three more runs hit the wrong page or mode, or could not write). They produced 27 proposals.
A human accepted 12 as 9 findings: three on /flights (F-04 identical booking buttons, F-05 no skip link, F-06 search field named by its placeholder), four on the Home page as F-07 (a button nested in a link, three places) and F-08 (unnamed GitHub link), F-10 on /destinations/mars (two links both named "Book"), F-11 on TodoMVC (unnamed checkboxes), and F-13 and F-14 on Uptime Kuma's dashboard (a silent Tab stop on the logo, and a second Tab stop inside every monitor link); six Uptime Kuma status filters (one bug) were deferred. The rest were rejected with reasons (same name for the same target, valid link-in-heading patterns, a cosmetic symbol, an adequate "GitHub" name) or were duplicates. On /destinations/mars and TodoMVC, the triage question was asked inside Bob by the one-command /earshot run.
Gate
Every UI commit replays the screen-reader hear-tests it can affect (16 in all) as a git pre-commit hook (.githooks/pre-commit → earshot_mcp/hear_tests.py); if any test fails the commit is blocked and nothing reaches HEAD.
I asked Bob to "tidy up" the focus-management code in Modal.tsx (removing tabIndex={-1}, returnFocusRef, and the setTimeout focus call), the gate caught it immediately:
$ git commit -m "refactor: tidy up Modal"
FAIL F-01: missing 'Sign In, dialog'
said: Moon, link, heading, level 3
FAIL F-02: missing 'Register, button'
said: Moon, link, heading, level 3
said: Select Seat Class, Earth to Moon, button
...
FAIL F-03: missing 'Close modal, button'
said: Moon, link, heading, level 3
...
PASS F-04
PASS F-05
PASS F-06
PASS F-07
PASS F-08
PASS N-02
BLOCKED by Earshot: this change breaks what a screen reader hears.
Verbatim terminal output (trimmed where marked with ...); run 2026-09-26 with all 9 tests; the full output is evidence/gate/terminal_demo_9tests.txt. Without the focus handling, focus never entered the dialog, so all three dialog tests heard the page behind it.
A second layer runs inside Bob: a lifecycle hook (.bob/settings.json, PreToolUse on execute_command) replays the tests a change can affect before any git commit Bob runs. When Bob was asked to skip the slow hook with git commit --no-verify, NVDA heard F-01 to F-03 fail and the hook blocked the command before git ran (evidence/gate/bob_hook_no_verify_blocked.txt).
Checking the first 6 announcements by hand with NVDA took a person 60 seconds at full speed, on every commit. The gate needs no attention: it replays only the affected tests (the 4 dialog tests, or Uptime Kuma's 3, each in about 2½ minutes); the first 13 together ran unattended in about 9 minutes.