Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 77 additions & 65 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,18 @@ concurrency:
# about the #9465 fence against #9465's own body before you write it down.
# ─────────────────────────────────────────────────────────────────────────────

# ─────────────────────────────────────────────────────────────────────────────
# ⭐ GATE ORDER IS DELIBERATE, not topical (#13690, maintainer ruling
# 2026-09-03): the `Lint & Repo Gates` job below runs its gates sequentially
# and aborts at the first non-zero exit, so the cheap, frequently-red gates run
# FIRST — `Docs anchors resolve to real headings` and `ADR anchors + number
# uniqueness` sit immediately after the six setup steps because they produced
# 78% of this job's reds, and running them there shrinks the unmeasured tail on
# the majority of red runs at no cost to a green one (whose wall clock is the
# sum of its steps, and so is order-independent) — ⛔ so do not re-sort them
# back among their topical neighbours as tidying.
# ─────────────────────────────────────────────────────────────────────────────

jobs:

lint:
Expand Down Expand Up @@ -162,6 +174,71 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

# #7484 docs anchors: `lychee.toml` sets `include_fragments = "none"`, so
# the `Check Documentation Links` job resolves a link's FILE and never its
# `#anchor` — measured with the pinned lychee 0.24.2 under the CI argv, a
# link to a heading that does not exist is reported `[200] OK`. So a
# cross-file anchor was a two-sided invariant with no owner: rename the
# heading, leave the inbound link, ship green. This resolves every internal
# `#fragment` against the destination page's real heading ids, computed
# with `github-slugger` — the same package fumadocs-core renders the site
# with, so the gate and the renderer cannot disagree about a slug.
#
# It lives in this job rather than in check-links.yml for two reasons: it
# needs a workspace install (that job is deliberately a checkout plus two
# dependency-free `node` checks), and it is REQUIRED where that lane is
# advisory. The four anchors that were already dead were fixed in the same
# PR, so this ships with no baseline and no allowlist.
#
# ── FOLDED IN, deliberately: the `file:line` anchor half ──────────────
#
# `content/docs/permissions/system-context.mdx` calls itself "the
# authority" for every behaviour keyed off `ExecutionContext.isSystem`
# and says it is "built by census over the whole repo, not by recall".
# Nothing held it to either claim, and it rotted at a measured rate: 101
# of its 111 anchors pointed at a line that no longer held what the row
# named, 19 days after the census was written, with CI green throughout.
#
# ⭐ The load-bearing direction is CENSUS -> PAGE, not page -> code. At
# the commit that motivated the card, re-resolving EVERY anchor on the
# page would have passed while it was missing 32 sites and its headline
# was 29 too low: a gate that only checks what a page already says can
# never find what the page failed to say. Measured again here on the
# shipped gate -- with an unanchored read site injected, the resolution
# checks report 0 findings and the population check names the site.
#
# It is a second command in THIS step rather than a step of its own: this
# job has no paths filter, so the census runs on code-only PRs (the ones
# that ADD a read site), and folding costs no new check context, no new
# `check:*` manifest key, and no new required-status entry for one page.
# The `--self-test` invocation is what `check:self-test-wired` requires,
# and it is the only instrument on this gate's matching rules -- a clean
# tree cannot tell a working rule from a weakened one.
#
# Invoked as `node` rather than through a `pnpm check:*` alias: see the
# GATE INVOCATION IDIOM note at the top of this file.
- name: Docs anchors resolve to real headings
run: |
pnpm check:doc-anchors
node scripts/check-system-context-census.mjs --self-test
node scripts/check-system-context-census.mjs

# #3723 ADR anchors: code an accepted ADR governs must keep naming it.
# That incident reversed three accepted ADRs with a patch-level changeset,
# and the mechanism was simply that the edited file never mentioned them —
# so the author could not have known. Presence check only; the invariant
# travels in the failure message (scripts/adr-anchors/, one file per anchor since #6957).
#
# Since #5992 the same script also audits the premise the anchors rest on:
# an ADR number names exactly ONE decision. Three numbers were each claimed
# by two unrelated records (0010/0019/0057) — grandfathered on an explicit
# shrink-only allowlist in the script, so a FOURTH collision goes red here.
# The `--self-test` in the pnpm script provokes that red path on synthetic
# directories every run, because a collision is rare enough that nobody
# would otherwise see the gate fail before it mattered.
- name: ADR anchors + number uniqueness (governed code names its decision)
run: pnpm check:adr-anchors

# Enforces the no-restricted-imports guard against @objectstack/spec root
# namespace imports (the dormant rule was never run in CI). Syntactic
# only, so no build step needed.
Expand Down Expand Up @@ -1464,55 +1541,6 @@ jobs:
node scripts/check-docs-nav-label.mjs --self-test
node scripts/check-docs-nav-label.mjs

# #7484 docs anchors: `lychee.toml` sets `include_fragments = "none"`, so
# the `Check Documentation Links` job resolves a link's FILE and never its
# `#anchor` — measured with the pinned lychee 0.24.2 under the CI argv, a
# link to a heading that does not exist is reported `[200] OK`. So a
# cross-file anchor was a two-sided invariant with no owner: rename the
# heading, leave the inbound link, ship green. This resolves every internal
# `#fragment` against the destination page's real heading ids, computed
# with `github-slugger` — the same package fumadocs-core renders the site
# with, so the gate and the renderer cannot disagree about a slug.
#
# It lives in this job rather than in check-links.yml for two reasons: it
# needs a workspace install (that job is deliberately a checkout plus two
# dependency-free `node` checks), and it is REQUIRED where that lane is
# advisory. The four anchors that were already dead were fixed in the same
# PR, so this ships with no baseline and no allowlist.
#
# ── FOLDED IN, deliberately: the `file:line` anchor half ──────────────
#
# `content/docs/permissions/system-context.mdx` calls itself "the
# authority" for every behaviour keyed off `ExecutionContext.isSystem`
# and says it is "built by census over the whole repo, not by recall".
# Nothing held it to either claim, and it rotted at a measured rate: 101
# of its 111 anchors pointed at a line that no longer held what the row
# named, 19 days after the census was written, with CI green throughout.
#
# ⭐ The load-bearing direction is CENSUS -> PAGE, not page -> code. At
# the commit that motivated the card, re-resolving EVERY anchor on the
# page would have passed while it was missing 32 sites and its headline
# was 29 too low: a gate that only checks what a page already says can
# never find what the page failed to say. Measured again here on the
# shipped gate -- with an unanchored read site injected, the resolution
# checks report 0 findings and the population check names the site.
#
# It is a second command in THIS step rather than a step of its own: this
# job has no paths filter, so the census runs on code-only PRs (the ones
# that ADD a read site), and folding costs no new check context, no new
# `check:*` manifest key, and no new required-status entry for one page.
# The `--self-test` invocation is what `check:self-test-wired` requires,
# and it is the only instrument on this gate's matching rules -- a clean
# tree cannot tell a working rule from a weakened one.
#
# Invoked as `node` rather than through a `pnpm check:*` alias: see the
# GATE INVOCATION IDIOM note at the top of this file.
- name: Docs anchors resolve to real headings
run: |
pnpm check:doc-anchors
node scripts/check-system-context-census.mjs --self-test
node scripts/check-system-context-census.mjs

# #13548 tenant-audit census. `content/docs/permissions/tenant-audit-census.mdx`
# carries the population `SqlDriver.auditMissingTenant` acts on: every
# application-surface write call site against a tenancy-enabled object, and
Expand Down Expand Up @@ -2138,22 +2166,6 @@ jobs:
node scripts/check-section-landing-index.mjs --self-test
node scripts/check-section-landing-index.mjs

# #3723 ADR anchors: code an accepted ADR governs must keep naming it.
# That incident reversed three accepted ADRs with a patch-level changeset,
# and the mechanism was simply that the edited file never mentioned them —
# so the author could not have known. Presence check only; the invariant
# travels in the failure message (scripts/adr-anchors/, one file per anchor since #6957).
#
# Since #5992 the same script also audits the premise the anchors rest on:
# an ADR number names exactly ONE decision. Three numbers were each claimed
# by two unrelated records (0010/0019/0057) — grandfathered on an explicit
# shrink-only allowlist in the script, so a FOURTH collision goes red here.
# The `--self-test` in the pnpm script provokes that red path on synthetic
# directories every run, because a collision is rare enough that nobody
# would otherwise see the gate fail before it mattered.
- name: ADR anchors + number uniqueness (governed code names its decision)
run: pnpm check:adr-anchors

# #13556 ADR SYMBOL ANCHORS — the other direction of the same contract.
# `check:adr-anchors` above asks whether governed CODE still names its
# ADR; this asks whether an ADR's pointer INTO code still resolves.
Expand Down
Loading