Skip to content

Commit e449ee1

Browse files
ci(docs): gate that apps/docs/redirects.mjs destinations resolve (#9014) (#9036)
`apps/docs/redirects.mjs` is a 92-entry table of permanent (308) redirects and nothing in the repo read it: `dispatch-gates.mjs apps/docs/redirects.mjs` named no check family over 99 discovered ones, and `git grep -l 'redirects.mjs' -- scripts .github` had 0 hits. #8948 found three destinations aiming at pages that do not exist -- live URLs answering a 308 into a 404. Adds `scripts/check-docs-redirects.mjs` (dependency-free, pure filesystem), the `check:docs-redirects` root script, and a step in lint.yml's required ESLint job. Three assertions: destinations resolve the way Fumadocs routes them, wildcard destinations resolve to a real directory, and no destination is itself matched by a source (chains), following Next's own matching semantics. Every limb carries a positive control in `--self-test` over a temp fixture that runs the real loadTable + checkTable path, paired with a clean-table control so an over-eager checker fails as loudly as a blind one. The live table has been 100% green since 62b2655, so a passing run over real data cannot distinguish a working resolver from one that resolves everything. Claude-Session: https://claude.ai/code/session_011RB4waLuNbdruCo6X9oobm Co-authored-by: Claude <noreply@anthropic.com>
1 parent c0525b2 commit e449ee1

3 files changed

Lines changed: 733 additions & 0 deletions

File tree

.github/workflows/lint.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,46 @@ jobs:
271271
- name: Docs anchors resolve to real headings
272272
run: pnpm check:doc-anchors
273273

274+
# #9014 docs redirects: `apps/docs/redirects.mjs` is a 92-entry table of
275+
# PERMANENT (308) redirects, and before this step nothing in the repo read
276+
# it — `dispatch-gates.mjs apps/docs/redirects.mjs` named no family over 99
277+
# discovered ones, and `git grep -l 'redirects.mjs' -- scripts .github` had
278+
# 0 hits. #8948 found three destinations aiming at pages that do not exist:
279+
# live URLs answering a 308 into a 404, which browsers cache and crawlers
280+
# treat as final. The gate resolves every destination the way Fumadocs
281+
# routes it, requires a wildcard destination's target directory to exist,
282+
# and rejects chains (a destination itself matched by a source), following
283+
# Next's own matching semantics.
284+
#
285+
# ── Lane, decided against the two workflow files rather than from memory ──
286+
#
287+
# NOT `check-links.yml`, the topical neighbour: that lane is advisory by
288+
# maintainer ruling 2026-08-07 and — verified in the file, since this is the
289+
# load-bearing half of the argument — its `on:` block carries only
290+
# `workflow_dispatch` and `pull_request`. No `merge_group`, and its own
291+
# header forbids promoting it without adding one in the same change. A gate
292+
# landed there would not block, which is the whole point of filing #9014.
293+
#
294+
# This job rather than the same workflow's `TypeScript Type Check` job,
295+
# which the card recommended: BOTH are required contexts on lint.yml (see
296+
# REQUIRED_CONTEXTS in scripts/check-required-contexts.mjs) and lint.yml
297+
# carries `merge_group`, so the required/advisory half of the card's
298+
# reasoning holds either way — but the ledger records THIS job as the one
299+
# carrying "the whole check:* gate family", and every other docs guard
300+
# (`check:doc-anchors` above, `check:docs-audit-scope`, `check:role-word`,
301+
# `check:quick-reference-counts`) sits in it. The typecheck job additionally
302+
# runs a full workspace build this dependency-free filesystem check has no
303+
# use for.
304+
#
305+
# Runs its own --self-test first (via the pnpm script). That is not
306+
# ceremony here: the live table has been 100% green since 62b2655d8, so a
307+
# passing run over real data cannot distinguish a working resolver from one
308+
# that resolves everything, and the chain and wildcard limbs have never
309+
# fired against real data at all. The self-test is where all three limbs are
310+
# observed failing.
311+
- name: Docs redirect destinations resolve, and no chains
312+
run: pnpm check:docs-redirects
313+
274314
# #4851: the docs-accuracy-audit workflow carries its default scope inline
275315
# (a workflow script runs in a vm with no filesystem, so it cannot enumerate
276316
# content/docs/ itself). Hand-kept, that list rotted in BOTH directions —

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"check:doc-authoring": "node scripts/check-doc-authoring.mjs --self-test && node scripts/check-doc-authoring.mjs",
3939
"check:doc-anchors": "node scripts/check-doc-anchors.mjs --self-test && node scripts/check-doc-anchors.mjs",
4040
"check:docs-audit-scope": "node scripts/docs-audit/affected-docs.mjs --self-test && node scripts/docs-audit/check-audit-scope.mjs --self-test && node scripts/docs-audit/check-audit-scope.mjs",
41+
"check:docs-redirects": "node scripts/check-docs-redirects.mjs --self-test && node scripts/check-docs-redirects.mjs",
4142
"check:role-word": "node scripts/check-role-word.mjs --self-test && node scripts/check-role-word.mjs",
4243
"check:quick-reference-counts": "node scripts/check-quick-reference-counts.mjs --self-test && node scripts/check-quick-reference-counts.mjs",
4344
"check:skill-frame-sync": "node scripts/check-skill-frame-sync.mjs --self-test && node scripts/check-skill-frame-sync.mjs",

0 commit comments

Comments
 (0)