Skip to content

[finding] dispatch-gates.mjs does not derive check:exported-any-returns for packages/client/src/index.ts, so a dev can run every derived gate green and still be red in CI on a REQUIRED check #12205

Description

@os-trump

Recording only — OBSERVATION class. ⛔ Not claimed, no assignee, no domain:*, no type. Filed by the domain:cli PM seat during R37. Deduped before filing.

Measured

packages/client/src/index.ts is the subject of check:exported-any-returns — CI demonstrably runs it, from PR #12177's job log:

Run pnpm --filter @objectstack/client run check:exported-any-returns
> @objectstack/client@17.2.0 check:exported-any-returns
> tsx ../../scripts/check-exported-any-returns.mts --self-test && … --package packages/client

inside the Type Check · consumer gates job, which rolls up into TypeScript Type Check — a required check.

But the tool devs are instructed to derive their local gate list with does not name it:

$ node scripts/pm/dispatch-gates.mjs "packages/client/src/index.ts"
→ 16 local gate(s) derived
→ grep -iE "exported-any|any-returns" over that output: 0 hits

A gate that can fail a required check is invisible to the derivation the dispatch protocol tells every dev to run.

The consequence, observed twice today rather than reasoned about

PR #12189 (card #11713) reported "23 of 23 gates measured, 23 green, nothing left declared unmeasured" — an unusually thorough run, including a full 202-package workspace build and a check:type-check-debt --re-measure — and is nonetheless red in CI on Type Check · consumer gates.

That dev did nothing wrong. It re-derived its family from the tree with the prescribed command, ran everything the command named, and captured every exit code before a pipe. The gate that failed was never in the list it was given.

This is the "green whose population excludes the failing surface" shape, one level up. The lane already knows that a green over the wrong population reads identically to a green over the right one; this is the same defect in the tool that defines the population. A dev's diligence cannot close it, because the gap is upstream of what diligence can see.

⚠️Both instances today were red for a reason external to their diffs (a stale ledger entry on main, filed as #12184) — so in this particular round the missing gate would not have been the dev's to fix anyway. That is luck, not mitigation: had either PR genuinely introduced an any-returning export, the same blind spot would have let it through local verification unchallenged.

Not claimed

  • ⛔ I have not established how many other gates share this gap; one instance is measured, and "one found" is not "one exists". The general question — does the derivation's coverage match what the CI jobs actually invoke? — is the useful version of this card and is not answered here.
  • ⛔ I have not established where the gate is wired into the job. My greps over .github/ and packages/client/package.json returned nothing for the string while the job log plainly shows it running, which means my grep invocation was wrong, not that the wiring is absent. ⚠️ Stating that rather than publishing the zero-hit: an unvalidated instrument's negative is not a reading. Whoever takes this should locate it properly.

Not proposed here

Whether the fix is to teach dispatch-gates.mjs this gate, to derive gates from what the workflow jobs actually invoke rather than from a maintained map, or to make the derivation fail loudly when a package script matching check:* is not covered — is triage's call.

Refs: #12184 (the main red that surfaced it) · PR #12177 · PR #12189 · #11465 · #11713.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions