Vigil

A field report for any public URL.

Vigil is a website health auditor. You give it a public address. It opens the site in a real headless browser, discovers controls, clicks or taps the safe ones, measures what actually changed, and writes PASS / FAIL / WARN / SKIP from those measurements.

It does not invent passes. If it cannot operate a control, that is SKIP, not FAIL. If a click lands and nothing measurable happens, that is FAIL.

Reusable on any public http / https site. One job per run. History keeps prior reports. Export Markdown, HTML, or JSON.


What you give it

InputRequiredWhat it does
URLYesSite to audit. google.com becomes https://google.com.
DepthYes (default Quick)How far to crawl.
ViewportYes (default Both)Desktop 1280×800, Mobile 390×844, or both.
Login / auth notesNoRecorded on the report. Does not log in for you. Auth-looking controls stay skipped.
Priority pagesNoExtra paths to visit (/pricing, /watch). On Quick they are visited after home. On Full they go first in the crawl queue.

Blocked: localhost, private IPs, .local / .internal hosts. Only public http/https.


How a scan runs

  1. Normalize the URL and refuse private targets.
  2. Launch a real browser (Chrome). If the browser cannot start, fall back to HTTP-only: fetch HTML, list links, no clicks, no play, no layout. The report says which engine ran.
  3. Open the URL. Record HTTP status. Record the final URL after redirects (google.comwww.google.com is the same site; a jump to a different registrable domain is flagged).
  4. Dismiss obvious overlays (cookie / “got it” / “accept”) when a matching button is visible.
  5. Discover visible nav, tabs, buttons, links, media, forms, headings, and page text.
  6. Interact in this order: tabs A–Z (including nested), then nav, then safe buttons, then media play, then search smoke, then link probes, then layout at the chosen widths.
  7. Full: crawl every same-origin page found (priority list first, then links discovered on each page), repeat discovery + interaction, until the queue is empty or the safety ceiling.
  8. Assemble the report: counts, failed-assertion cards, tab coverage tree, inventory, suggested fix order, exports.

Typical Quick run: ~30–40s, one page. Full: walks the whole same-origin site (safety ceiling 80 pages).

If a step stops making progress (e.g. play() on a live stream), a watchdog skips it, records the reason, and continues. After ~55s still frozen, remaining work is aborted and a partial report is written.


Depth and budgets

Hard caps so a huge public site cannot run forever. Full keeps discovering same-origin pages as it walks; each page gets its own nav/button budget.

LimitQuickFull
Pages1 (+ optional priority paths)80 (entire same-origin site, safety ceiling)
Nav clicks12 (home)16 per page
Button clicks12 (home)16 per page
Link probes40400
Media play attempts48 per page
Forms (search only auto-submitted)48 per page
Tabs activated80200
Nested tab depth58
Navigation timeout18s20s
Click timeout7s8s
Link timeout7s8s

Over-budget leftovers are INFO (“N links not probed”), not failures.


What it inspects

1. Document load

After each page load it reads visible text. It prefers main / [role=main], but falls back to `document.body` if main is a stub — some site builders put the nav in main.

AssertionPASSFAIL
HTTPstatus < 4004xx/5xx or no response
Contenttitle + enough body texttitle/body match an error regex, or visible text < 40 characters

2. Navigation

Finds links in nav, [role=navigation], and header. Clicks each (budget permitting).

  • PASS if the destination’s URL, title, headings, or text contain tokens from the label (e.g. “Equities” → /equities or the word “equities”).
  • FAIL if the result looks like an error page or is nearly blank.
  • WARN if the URL changed but the label cannot be confirmed, or the click did nothing measurable and the label is too generic (Home is a common WARN — “home” is ignored as a token).
  • SKIP if the item is covered / not visible, looks like login, or matches safe-mode (logout, checkout, delete, …).

3. Tabs (A–Z, including tabs inside tabs)

Finds [role=tablist] / [role=tab], sorts names A–Z, activates each, then walks child tablists inside the selected panel. Paths are recorded as Parent > Child.

A tab PASSes if after click:

  • aria-selected=true or data-state=active, or
  • URL changed, or
  • main/body text changed enough to count.
CoverageStatus
Tabs found and all activatedPASS
Some discovered, none activatedFAIL
PartialWARN
No role=tab on the pageINFO (not a site bug)

The report’s Tab coverage A–Z list is the tree of what was found vs activated.

4. Buttons

Visible button, [role=button], input[type=button|submit]. Disabled → INFO. Destructive / payment / auth labels → SKIP.

After a successful click it measures:

  • URL change
  • [role=dialog] / <dialog> count
  • [role=menu|listbox|grid]
  • iframe count
  • high z-index overlay count
  • aria-expanded
  • body/main text delta
  • popup window

PASS if any of those fire (this is how Google’s apps waffle passes: aria-expanded false→true + iframe + overlay).

FAIL only if the click landed and all of those stayed false.

Then it hits Escape / returns to the page so the next control is not blocked by a menu.

5. Links

Collects visible a[href] on the page (and inventory hrefs). Probes each URL:

  1. HEAD, follow redirects
  2. If HEAD is 404 / 403 / 405 / 501 → GET (some hosts 404 on HEAD and 200 on GET)
ResultStatus
2xxPASS
401 / 403SKIP (auth wall, not scored broken)
0 / timeoutFAIL
Other 4xx/5xx after GETFAIL

External links are labeled with host + path so support.google.com/websearch/… is not shown as a homepage 404.

javascript: hrefs are WARN.

6. Media

Finds video / audio and play-looking controls. Mutes, calls play(), waits ~700ms.

OutcomeStatus
currentTime > 0 and not pausedPASS
Autoplay policy / NotAllowedErrorSKIP (browser, not the site)
Player errorFAIL
Stayed pausedWARN
Loose “Play” button click, cannot prove decodeWARN

7. Forms / search

  • Search (input[type=search], search-named/placeholder fields): types a nonsense probe (zzzz-vigil-probe), presses Enter. PASS if URL or results text change, or an honest empty-state string appears. FAIL if nothing changes. SKIP if the field cannot be driven.
  • Other forms: SKIP (safe mode). Payment-like field names are called out in the note. Nothing is purchased or submitted.

8. Layout

Sets the viewport and compares documentElement.scrollWidth vs clientWidth.

OverflowStatus
≤ 8pxPASS
9–23pxWARN (usually 100vw / scrollbar slop)
≥ 24pxFAIL (user must side-scroll)

Desktop 1280×800 and/or mobile 390×844, depending on the viewport option.

9. Dead app on a 200 page

After load, Vigil records console pageerror, failed same-origin script/css/xhr, mixed http:// assets on HTTPS, and empty #app / #__next / #root shells.

10. Action destinations off-site

Follows book / order / schedule / donate / tickets / maps-looking links (max 6) in a fresh tab. FAIL if that URL is blank or 4xx/5xx. tel: / mailto: are checked for form only.

11. Contact / zip / newsletter forms

Safe probe values (vigil-probe@example.com, zip 10001). Never payment or password. PASS if thanks, validation, or URL change. FAIL if Submit does nothing.

12. Widgets, images, jumps, overlays, phone chrome

  • iframes (maps, chat, booking, video): must have src and non-zero size
  • visible <img>: naturalWidth > 0
  • #section links: target exists and scrolls into view
  • elementFromPoint on buttons: FAIL if another layer is on top
  • 390px: hamburger opens, tap targets, sticky header cover, zero-size iframes

Scoring

Read this first.

StatusMeaning
PASSThe assertion ran and the expected measurement happened.
FAILThe assertion ran and the expected measurement did not happen.
WARNSomething changed, but we could not confirm the label — or the issue is small (e.g. 10px overflow).
SKIPNot run: hidden, timeout, auth, captcha, safe-mode, autoplay block. Not a failure.
INFOPresent but not operable (disabled), or leftover budget.

The report opens on failed assertions (FAIL + WARN). Passing link checks are hidden until you open the full log. Skips live under Locator not run.

Copy list dumps the failures as a checklist.


Safe mode (always on)

Vigil will not click or submit anything whose label/href looks like:

delete, remove, destroy, cancel subscription, logout / sign out, purchase, buy now, checkout, pay now, add to cart, transfer funds, and similar.

Login / sign up / captcha / password controls are skipped. It does not type into real accounts. It will not scan a LAN or localhost.


What the report contains

  • Identity — host you entered, document title, final URL, job id, engine (browser vs HTTP fallback), depth, viewport, duration, pages visited
  • Counts — fail / warn / skip / pass
  • Tab coverage A–Z
  • Failed assertion cards — grouped (blank document, no post-click mutation, HTTP not 2xx, media, form, layout, unconfirmed labels). Each card: control, page, type, measured deltas, expected formula
  • Fix order — page load → nav/tabs → links → media → buttons → forms → layout
  • Locator not run — deduped skips
  • Full finding log — filterable table: Page / Control / Type / Expected / Actual / Status / Notes
  • Inventory — every major control and what it *appeared* to do from the DOM (href, submit, play, etc.) — observed, not assumed
  • Exports — Markdown, HTML, JSON

Start another URL from the bar on every report so jobs do not get mixed up.


What it does not do

  • Log in, solve CAPTCHAs, or use your cookies
  • Buy, delete, or change account data
  • Run as a real iPhone / Safari / WebKit device farm (Mobile = a 390×844 Chrome viewport)
  • Prove visual design, brand, SEO, performance, or accessibility beyond the checks above
  • Test authenticated dashboards without a session
  • Guarantee that a FAIL is the site’s fault — some FAILs have been our assertions (nav marked as main, HEAD-only 404s, menus that are not role=dialog). Those were fixed as we found them; new site patterns can still fool a check
  • Cover every control on a huge site (see budgets)
  • Watch streaming video quality — only “did play() advance currentTime?”

How to use it well

  1. Quick + Both on the homepage first.
  2. Treat FAIL as “we clicked/loaded and the measurement failed.” Open the card and read the deltas.
  3. Treat SKIP as untested.
  4. If many pages FAIL with the same tiny main length, suspect a builder shell (Vigil now falls back to body when main is a stub).
  5. Full when you want the whole same-origin site (priority paths first, then every internal page found).
  6. Re-run after a deploy. Compare job ids — each scan is a new report.

Product in one line

A bounded, safe, browser-driven pass over nav, tabs, buttons, links, media, search, layout, plus deeper work checks (dead shells, outbound actions, forms, widgets, images, jumps, phone chrome) — with a report that shows what was measured, not a vibe.