Skip to content

Commit 8b9a10e

Browse files
committed
ci(lint): run the two most frequently red gates first (#13690)
`Lint & Repo Gates` runs 142 steps sequentially and aborts at the first non-zero exit, so the unmeasured tail of a red run is (142 - failing position). The #13690 measurement found ZERO gate-to-gate dependency edges in this job — 134/134 gate steps exit 0 and write no files — so the order is a free variable and this is a permutation, not a refactor. Per the maintainer ruling of 2026-09-03 (option C, the minimal permutation), the two gates that produced 78% of current-shape reds move to immediately after the six setup steps, in that order: Docs anchors resolve to real headings 51 -> 7 ADR anchors + number uniqueness (governed code ...) 73 -> 8 Everything between them shifts up to fill the gap; steps after position 73 are untouched. The workflow header gains one sentence recording the ordering intent so a later author does not undo it as tidying. Nothing else moves: no slow-gate reshuffle, no job split, no `continue-on-error`, no change to the required contexts, and no gate over gate order. Correctness proof — the multiset of the job's 142 steps (`name`, `run` body and every other key) is identical before and after; only positions change. At the raw-text level the diff is 65 insertions / 65 deletions whose line multisets are equal. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
1 parent e37456e commit 8b9a10e

1 file changed

Lines changed: 77 additions & 65 deletions

File tree

‎.github/workflows/lint.yml‎

Lines changed: 77 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,18 @@ concurrency:
106106
# about the #9465 fence against #9465's own body before you write it down.
107107
# ─────────────────────────────────────────────────────────────────────────────
108108

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

111123
lint:
@@ -162,6 +174,71 @@ jobs:
162174
- name: Install dependencies
163175
run: pnpm install --frozen-lockfile
164176

177+
# #7484 docs anchors: `lychee.toml` sets `include_fragments = "none"`, so
178+
# the `Check Documentation Links` job resolves a link's FILE and never its
179+
# `#anchor` — measured with the pinned lychee 0.24.2 under the CI argv, a
180+
# link to a heading that does not exist is reported `[200] OK`. So a
181+
# cross-file anchor was a two-sided invariant with no owner: rename the
182+
# heading, leave the inbound link, ship green. This resolves every internal
183+
# `#fragment` against the destination page's real heading ids, computed
184+
# with `github-slugger` — the same package fumadocs-core renders the site
185+
# with, so the gate and the renderer cannot disagree about a slug.
186+
#
187+
# It lives in this job rather than in check-links.yml for two reasons: it
188+
# needs a workspace install (that job is deliberately a checkout plus two
189+
# dependency-free `node` checks), and it is REQUIRED where that lane is
190+
# advisory. The four anchors that were already dead were fixed in the same
191+
# PR, so this ships with no baseline and no allowlist.
192+
#
193+
# ── FOLDED IN, deliberately: the `file:line` anchor half ──────────────
194+
#
195+
# `content/docs/permissions/system-context.mdx` calls itself "the
196+
# authority" for every behaviour keyed off `ExecutionContext.isSystem`
197+
# and says it is "built by census over the whole repo, not by recall".
198+
# Nothing held it to either claim, and it rotted at a measured rate: 101
199+
# of its 111 anchors pointed at a line that no longer held what the row
200+
# named, 19 days after the census was written, with CI green throughout.
201+
#
202+
# ⭐ The load-bearing direction is CENSUS -> PAGE, not page -> code. At
203+
# the commit that motivated the card, re-resolving EVERY anchor on the
204+
# page would have passed while it was missing 32 sites and its headline
205+
# was 29 too low: a gate that only checks what a page already says can
206+
# never find what the page failed to say. Measured again here on the
207+
# shipped gate -- with an unanchored read site injected, the resolution
208+
# checks report 0 findings and the population check names the site.
209+
#
210+
# It is a second command in THIS step rather than a step of its own: this
211+
# job has no paths filter, so the census runs on code-only PRs (the ones
212+
# that ADD a read site), and folding costs no new check context, no new
213+
# `check:*` manifest key, and no new required-status entry for one page.
214+
# The `--self-test` invocation is what `check:self-test-wired` requires,
215+
# and it is the only instrument on this gate's matching rules -- a clean
216+
# tree cannot tell a working rule from a weakened one.
217+
#
218+
# Invoked as `node` rather than through a `pnpm check:*` alias: see the
219+
# GATE INVOCATION IDIOM note at the top of this file.
220+
- name: Docs anchors resolve to real headings
221+
run: |
222+
pnpm check:doc-anchors
223+
node scripts/check-system-context-census.mjs --self-test
224+
node scripts/check-system-context-census.mjs
225+
226+
# #3723 ADR anchors: code an accepted ADR governs must keep naming it.
227+
# That incident reversed three accepted ADRs with a patch-level changeset,
228+
# and the mechanism was simply that the edited file never mentioned them —
229+
# so the author could not have known. Presence check only; the invariant
230+
# travels in the failure message (scripts/adr-anchors/, one file per anchor since #6957).
231+
#
232+
# Since #5992 the same script also audits the premise the anchors rest on:
233+
# an ADR number names exactly ONE decision. Three numbers were each claimed
234+
# by two unrelated records (0010/0019/0057) — grandfathered on an explicit
235+
# shrink-only allowlist in the script, so a FOURTH collision goes red here.
236+
# The `--self-test` in the pnpm script provokes that red path on synthetic
237+
# directories every run, because a collision is rare enough that nobody
238+
# would otherwise see the gate fail before it mattered.
239+
- name: ADR anchors + number uniqueness (governed code names its decision)
240+
run: pnpm check:adr-anchors
241+
165242
# Enforces the no-restricted-imports guard against @objectstack/spec root
166243
# namespace imports (the dormant rule was never run in CI). Syntactic
167244
# only, so no build step needed.
@@ -1464,55 +1541,6 @@ jobs:
14641541
node scripts/check-docs-nav-label.mjs --self-test
14651542
node scripts/check-docs-nav-label.mjs
14661543
1467-
# #7484 docs anchors: `lychee.toml` sets `include_fragments = "none"`, so
1468-
# the `Check Documentation Links` job resolves a link's FILE and never its
1469-
# `#anchor` — measured with the pinned lychee 0.24.2 under the CI argv, a
1470-
# link to a heading that does not exist is reported `[200] OK`. So a
1471-
# cross-file anchor was a two-sided invariant with no owner: rename the
1472-
# heading, leave the inbound link, ship green. This resolves every internal
1473-
# `#fragment` against the destination page's real heading ids, computed
1474-
# with `github-slugger` — the same package fumadocs-core renders the site
1475-
# with, so the gate and the renderer cannot disagree about a slug.
1476-
#
1477-
# It lives in this job rather than in check-links.yml for two reasons: it
1478-
# needs a workspace install (that job is deliberately a checkout plus two
1479-
# dependency-free `node` checks), and it is REQUIRED where that lane is
1480-
# advisory. The four anchors that were already dead were fixed in the same
1481-
# PR, so this ships with no baseline and no allowlist.
1482-
#
1483-
# ── FOLDED IN, deliberately: the `file:line` anchor half ──────────────
1484-
#
1485-
# `content/docs/permissions/system-context.mdx` calls itself "the
1486-
# authority" for every behaviour keyed off `ExecutionContext.isSystem`
1487-
# and says it is "built by census over the whole repo, not by recall".
1488-
# Nothing held it to either claim, and it rotted at a measured rate: 101
1489-
# of its 111 anchors pointed at a line that no longer held what the row
1490-
# named, 19 days after the census was written, with CI green throughout.
1491-
#
1492-
# ⭐ The load-bearing direction is CENSUS -> PAGE, not page -> code. At
1493-
# the commit that motivated the card, re-resolving EVERY anchor on the
1494-
# page would have passed while it was missing 32 sites and its headline
1495-
# was 29 too low: a gate that only checks what a page already says can
1496-
# never find what the page failed to say. Measured again here on the
1497-
# shipped gate -- with an unanchored read site injected, the resolution
1498-
# checks report 0 findings and the population check names the site.
1499-
#
1500-
# It is a second command in THIS step rather than a step of its own: this
1501-
# job has no paths filter, so the census runs on code-only PRs (the ones
1502-
# that ADD a read site), and folding costs no new check context, no new
1503-
# `check:*` manifest key, and no new required-status entry for one page.
1504-
# The `--self-test` invocation is what `check:self-test-wired` requires,
1505-
# and it is the only instrument on this gate's matching rules -- a clean
1506-
# tree cannot tell a working rule from a weakened one.
1507-
#
1508-
# Invoked as `node` rather than through a `pnpm check:*` alias: see the
1509-
# GATE INVOCATION IDIOM note at the top of this file.
1510-
- name: Docs anchors resolve to real headings
1511-
run: |
1512-
pnpm check:doc-anchors
1513-
node scripts/check-system-context-census.mjs --self-test
1514-
node scripts/check-system-context-census.mjs
1515-
15161544
# #13548 tenant-audit census. `content/docs/permissions/tenant-audit-census.mdx`
15171545
# carries the population `SqlDriver.auditMissingTenant` acts on: every
15181546
# application-surface write call site against a tenancy-enabled object, and
@@ -2138,22 +2166,6 @@ jobs:
21382166
node scripts/check-section-landing-index.mjs --self-test
21392167
node scripts/check-section-landing-index.mjs
21402168
2141-
# #3723 ADR anchors: code an accepted ADR governs must keep naming it.
2142-
# That incident reversed three accepted ADRs with a patch-level changeset,
2143-
# and the mechanism was simply that the edited file never mentioned them —
2144-
# so the author could not have known. Presence check only; the invariant
2145-
# travels in the failure message (scripts/adr-anchors/, one file per anchor since #6957).
2146-
#
2147-
# Since #5992 the same script also audits the premise the anchors rest on:
2148-
# an ADR number names exactly ONE decision. Three numbers were each claimed
2149-
# by two unrelated records (0010/0019/0057) — grandfathered on an explicit
2150-
# shrink-only allowlist in the script, so a FOURTH collision goes red here.
2151-
# The `--self-test` in the pnpm script provokes that red path on synthetic
2152-
# directories every run, because a collision is rare enough that nobody
2153-
# would otherwise see the gate fail before it mattered.
2154-
- name: ADR anchors + number uniqueness (governed code names its decision)
2155-
run: pnpm check:adr-anchors
2156-
21572169
# #13556 ADR SYMBOL ANCHORS — the other direction of the same contract.
21582170
# `check:adr-anchors` above asks whether governed CODE still names its
21592171
# ADR; this asks whether an ADR's pointer INTO code still resolves.

0 commit comments

Comments
 (0)