Skip to content

check:dev-prereqs --self-test runs nowhere in CI — the second and only other member of #8110's split-gate family #8170

Description

@hotlong

Filed by the domain:devx dev working #8110 (PR pending). Out of scope there: that card's file surface is the one lint.yml step for check:skill-frame-freshness, and its dispatch ruled explicitly that other family members are reported, not fixed, in that PR.

The finding

#8110 asked whether other gates share its split shape. The criterion it fixed is narrow and deliberately not "any check:* script containing &&":

a gate whose scan half cannot run in CI, and whose CI-safe --self-test half is therefore also absent from CI.

Surveyed mechanically (method below). Exactly one other gate qualifies: check:dev-prereqs.

"check:dev-prereqs": "node scripts/check-dev-prereqs.mjs --self-test && node scripts/check-dev-prereqs.mjs"

Both halves measured on a clean worktree at 7cfe7ad, before any build:

half reading
--self-test exit 0 in 0.12s, 16 cases, on a checkout with no node_modules and nothing built — "every verdict reachable, exclusions and freshness coverage pinned"
the scan exit 1 — "The workspace is not built ... 67 of 67 workspace packages declare an entry point under dist/ that is not on disk"

That is the same asymmetry as #8110, for the same reason. The scan asks "is this workspace BUILT and is the one staleness-lying artifact CURRENT", which is a precondition for pnpm dev on a human's long-lived tree. In a CI job that has not built, it is a hard false red about a precondition CI does not have; in a job that just built, it is a tautological green. So it correctly stays out — and takes the self-test half with it.

The self-test half has neither property. It is hermetic (temp fixtures, no network, no workspace state — proven by the 0.12s green above on an unbuilt tree with no dependencies installed) and it is the thing that would notice check-dev-prereqs.mjs losing a verdict path.

Why it is worth a card rather than a note

Identical failure mode to the one #8110 measured: the gate keeps sitting in the gate list reading as coverage. check:dev-prereqs's exposure is arguably worse than the skill-frame one, because its scan half does run on every pnpm dev (the dev chain invokes scripts/check-dev-prereqs.mjs directly with node, without the self-test — that is documented in the script header as a deliberate hot-path saving). So the scan is exercised constantly by humans while the self-test that keeps it honest is exercised by nobody unless someone types pnpm check:dev-prereqs by hand.

Suggested shape

One lint.yml step, node scripts/check-dev-prereqs.mjs --self-test, in the same job and with the same "self-test half only, never the scan" naming as the #8110 step, so the deliberate omission reads as a decision. Precedent for the shape already exists twice in that file: the changeset-family self-tests (#6509, "the SELF-TEST halves only — the real scans stay in pr-automation.yml") and #8110's step.

How the survey was run (so the negative half is reviewable)

  • All 62 root check:* scripts enumerated from package.json, cross-referenced against every file in .github/workflows/ with YAML comment lines stripped first — that step matters: matching raw text puts check:platform-checklist in the "wired" column because lint.yml mentions it only in a comment explaining why it is not wired.
  • Result: 3 of 62 are referenced by no workflow — check:skill-frame-freshness (check:skill-frame-freshness --self-test runs nowhere in CI, so a broken fixture is invisible until a human runs the gate #8110), check:dev-prereqs (this card), check:platform-checklist.
  • Widened once beyond check:*: every scripts/**/*.mjs mentioning --self-test (63 files), same cross-reference. It surfaced no further candidates — the extras it named are a shared library (scripts/adr-anchors.mjs, imported by gates that do run), PM-loop tooling with no gate role (scripts/pm/check-half-states.mjs, scripts/pm/dispatch-gates.mjs), and scripts/setup-git-hooks.mjs, which runs in CI via the prepare lifecycle.

Considered and rejected: check:platform-checklist

It fails the criterion's first clause. Its scan is a static JSON-structure check over docs/qa/platform-checklist/ that would run in CI perfectly well; it is out by an explicit maintainer policy decision recorded in lint.yml ("the checklist is a QA ledger, not a code gate; keeping it out of the per-PR path means an unrelated PR is never blocked by checklist drift"). Its self-test half is therefore excluded on purpose, not as collateral damage — a different fact, and not this family's.

Also rejected: check:skill-frame-sync

Runs in CI whole, at lint.yml:1082, and is --self-test && scan — the counter-example that shows && is not the family marker.


Generated by Claude Code

Related: #8110

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions