Check for Drift in Documentation

The drift check finds documentation pages that have gone out of date. When a pull request merges into main and touches a documentation page under content/docs/**/*.mdx or content/nav.config.ts, a GitHub webhook checks whether the change makes any other documentation page stale - a broken cross-reference, terminology that no longer matches, or a described behavior that now conflicts - and writes a suggested fix for a person to review. It never edits, commits, or opens a pull request itself.

Warning

Don't turn on the drift check for a private repository. The drift check sends your documentation content to the Anthropic API and stores suggested fixes, including full page content, at public URLs that anyone can read without signing in.

What it checks for

  • Cross-references that break, such as a link to a heading anchor that was renamed

  • Terminology or option names that changed in one page but are still used elsewhere

  • Described behavior or examples that now conflict with the change

  • A nav.config.ts restructure that a page's own content no longer reflects

Note

The drift check only proposes edits to pages that weren't already changed in the triggering pull request.

How it works

  1. A GitHub webhook (registered on the beacon repository, subscribed to Pull request events) calls a Vercel serverless function at /api/github-webhook whenever a pull request merges.
  2. The function verifies the request's signature, then checks whether the merge actually touched a watched path. Anything else is a no-op.
  3. It reads the diff and every other current documentation page using a read-only GitHub token - it has no permission to write anything back to the repository.
  4. It asks Claude whether any other page is now stale, using a forced structured response so the output is always well-formed.
  5. If a fix is warranted, it writes a JSON suggestion (a summary plus full proposed file contents) to Vercel Blob storage and logs the URL. If nothing needs to change, it does nothing further.

Where to find a suggestion

There's no notification - checking is pull-based. After merging a change that touches a watched path:

  • The in-app dashboard at /dashboard - lists every suggestion with its summary, touched files, and status, newest first. This is the easiest way to review suggestions day to day.
  • Vercel Runtime Logs (Observability tab) - look for a line like [doc-update] PR #<N>: suggestion written to <url>, plus a one-line summary.
  • The Blob store's file browser (Storage tab → the connected store → Browser) - every suggestion lands at a predictable path, doc-suggestions/pr-<PR number>.json, so you can look one up by PR number directly.

A suggestion is a plain JSON file - a public URL you can open directly, containing the proposed full file contents for you to copy into a normal branch/commit/PR if you agree with it.

Reviewing a suggestion

Every suggestion has a status: pending (the default), accepted, or dismissed. On the dashboard, a pending suggestion shows Accept and Dismiss buttons; clicking either calls PATCH /api/doc-suggestion-status with the PR number and new status, which updates the status field on the same blob and re-renders it as a badge. Marking a status doesn't apply the suggested file changes anywhere - it's just a record that someone reviewed it, so you still copy the proposed content into a normal branch/commit/PR yourself if you agree with it.

The dashboard itself has no login, so this endpoint is protected by a separate shared secret instead: the first time you click Accept or Dismiss in a browser tab, it prompts for the secret and reuses it for the rest of that tab's session. A wrong or missing secret returns 401 and clears the cached value so the next click re-prompts.

Configuration

The watched repository and folder are read from environment variables:

VariableDefault
GITHUB_REPO_OWNERnone - required
GITHUB_REPO_NAMEnone - required
WATCHED_DOCS_FOLDERcontent/docs
WATCHED_NAV_CONFIG_PATHcontent/nav.config.ts
DOC_UPDATE_AUTOMATION_ENABLEDfalse

GITHUB_REPO_OWNER and GITHUB_REPO_NAME must always be set explicitly - they have no default. WATCHED_DOCS_FOLDER and WATCHED_NAV_CONFIG_PATH are optional and can be overridden if the target repo's folder layout ever differs from Beacon's own.

The drift check is disabled by default. Set DOC_UPDATE_AUTOMATION_ENABLED to the literal string true to turn it on. When disabled, the webhook handler responds immediately without verifying the signature, reading the diff, or calling Claude.

Four secrets are required as Vercel environment variables:

VariablePurpose
ANTHROPIC_API_KEYCalls the Claude API to judge staleness and draft a fix.
GITHUB_WEBHOOK_SECRETVerifies incoming webhook requests actually came from GitHub.
GITHUB_READ_TOKENA fine-grained GitHub PAT scoped to Contents: Read-only on this repo alone - no write access of any kind.
DASHBOARD_API_SECRETA shared secret guarding PATCH /api/doc-suggestion-status, since the dashboard itself has no login. Any value works; generate one with openssl rand -hex 32.

A connected Vercel Blob store provides the remaining credential automatically (via short-lived OIDC tokens), with no manual token to configure.

Why it never writes to GitHub

An earlier design had the drift check open its own pull request, which needed a GitHub token with write access to contents, pull requests, and issues. The current design needs none of that: the read-only token can't push a commit, open a PR, or touch anything else in the repository even if it were compromised. The tradeoff is that suggestions are pull-based rather than showing up as a ready-to-merge PR - see Where to find a suggestion above.