| name | fix-broken-links |
| description | Triage and fix broken-link reports from the Glean developer site's nightly link checker. Use when investigating a `Broken Links Detected` issue, a failing `Link Checker` workflow run, or any scenario where you need to act on a `link-check-fixme.md` / `link-check-results.json` artifact. Covers source-content fixes, adding exclusions, and verification. |
Fix broken links
The Glean developer site (gleanwork/glean-developer-site) runs a nightly link checker. When it fails, the workflow produces three artifacts. This skill describes the contract for an agent to consume them and ship a fix.
Where to start
- Identify the failing run. It's either:
- The most recent open
Broken Links Detected issue (auto-created on schedule failures), or
- A failing
Link Checker workflow run linked from the issue.
- Download the run's artifacts. The bundle is named
link-check-results-deep (or -quick) and contains:
link-check-results.json — raw lychee output (structured, ground truth)
link-check-summary.md — human-readable, grouped by source page
link-check-fixme.md — start here — a self-contained prompt with the failures, triage rubric, and links to project files
- Use
link-check-fixme.md as your working list. It groups failures by source page and tags each with a triage category.
Project context the skill encodes
- Workflow:
.github/workflows/link-checker.yml. Runs on a daily cron and on workflow_dispatch.
- Driver script:
scripts/check-links.sh. Fetches the production sitemap, invokes lychee, then calls scripts/render-link-check-results.mjs to produce the artifacts.
- Renderer:
scripts/render-link-check-results.mjs. Reads the lychee JSON, emits the markdown summaries.
- Exclusion blocks: in
scripts/check-links.sh, look for the comment markers:
- Permanent exclusions (auth-required hosts, example domains, internal sites): the existing
lychee_cmd+=(...) blocks. Each entry has an inline # REASON comment.
- Temporary exclusions: the
# ── TEMPORARY EXCLUSIONS ── block. Format: --exclude "^https://..." # REASON: [why] (added YYYY-MM-DD).
- Local verification:
pnpm links:check:local builds the site, serves it on port 8888, and runs the checker end to end. Do not run pnpm start — the dev server doesn't generate sitemap.xml, which the checker needs.
Triage rubric (matches link-check-fixme.md)
| Category | Status codes | Action |
|---|
broken | 404, 410 | Find the source .mdx (grep -rn "<URL>" docs/). If a replacement exists, update the link. If not, remove the link or the surrounding context. |
rate-limited | 429 | Usually transient. If the host appears repeatedly across runs, add a permanent exclusion for that host. |
auth-required | 401, 403 | The host requires login. Add a permanent exclusion in scripts/check-links.sh. |
server-error | 5xx | Upstream is broken. Re-run once. If persistent, exclude the host or open an issue with the upstream. |
network / other | timeouts, DNS, connection failures | Re-run. If persistent, exclude. |
Procedure
- Read
link-check-fixme.md — it groups failures by source page (the URL of the page that contained the broken link). Each source maps to a likely .mdx path under docs/.
- For each failure, classify by category and decide:
- Source-content fix: open the
.mdx (use the path hint in fixme.md or grep), update the URL or remove the link. Multiple failures on the same source page typically share an .mdx — batch them.
- Exclusion: edit
scripts/check-links.sh. Use the temporary block for time-bounded reasons ("page launches next month"); use the permanent block for hosts that systematically reject the checker (auth, anti-bot, etc.). Always include a # REASON comment.
- No-op: if the failure is a transient (single 5xx, single timeout) and the rubric says re-run, note it in the PR description and skip.
- Verify locally:
pnpm links:check:local
This must pass before opening the PR. The script builds the site, serves it on :8888, and runs the checker against the local build.
- Open a PR with the diff. In the PR body:
- Link to the failing run.
- List which failures were fixed in source vs. excluded vs. left alone.
- For exclusions, justify each one (transient vs. systematic).
Common gotchas
- Lychee runs in deep mode against production by default — it crawls each sitemap URL to find broken links inside the rendered HTML. Anchor links generated by JavaScript (
#request-foo) inside API docs are intentionally excluded; don't "fix" those by adding fragments to the source MDX.
- The OpenAPI reference pages (
docs/api/client-api/**, docs/api/indexing-api/**) are auto-generated from upstream specs. If a broken link is inside an .api.mdx file, the fix typically belongs upstream in gleanwork/open-api, not in this repo. Note this in the PR and either exclude or wait for the upstream fix.
- Many "failures" are 429 from CodePen, GitHub, or npm.com when the run hits them rapidly. There's already a
--max-concurrency 8 and exclusions for the worst offenders — only add new ones if you see the same host failing across multiple consecutive runs.
Output
A PR with:
- Source-content edits in
docs/**/*.mdx (if any).
- Updates to
scripts/check-links.sh (if any new exclusions).
- A PR body that links the failing run, summarises each failure's disposition, and notes anything left as a known issue.