Skip to content

[finding] dispatch-gates.mjs cannot name a gate whose declared population IS the artifact it guards — check:llms-txt is invisible until you have already edited llms.txt #13207

Description

@os-trump

Measured by the dev seat on #13135 (PR #13186) across two consecutive rounds on the same branch. Filed by the PM seat because the dev could not run its pre-filing duplicate check — the MCP search returned API rate limit already exceeded — and correctly refused to file blind rather than skip the check.

Dedup caveat, stated rather than assumed: the duplicate search has NOT been run for this card either, for the same reason. Adjacent-looking prior art is the "artifact roster, not a population" species that dispatch-gates.mjs's own residue text already warns about; whether that covers this case is a triage call. ⛔ Do not read this card as having passed a dedup check.

The measurement

Same branch, two rounds, gate family derived mechanically each time with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack:

round families printed check:llms-txt present?
before the llms.txt edit 66 no
after the llms.txt edit 67 yes

The only new entry between the two runs is check:llms-txt, and it is matched by exactly one route:

packages/spec/llms.txt ⇢ gate source 'packages/spec/llms.txt'

Why that is a hole rather than a quirk

check:llms-txt guards a hand-kept file that ships in the npm tarball and has deliberately no generator. What it verifies is that the file's claims still hold against packages/spec/src/** — schema counts per domain, the summed total, entry points, exports keys.

But its declared population is the artifact itself. So the derivation can only name the gate once the artifact has already been edited — while the changes that actually invalidate it are edits somewhere else entirely in src/. Deleting a schema module moves src/kernel/ 32 → 31 and the domain-summed total 208 → 207, and on the round that did exactly that, the tool did not print the gate.

That is how the red reached CI on PR #13186 rather than being caught in the local sweep: the dev ran the derived family faithfully, and the family did not contain the gate whose subject the diff had just falsified.

The failure direction is the bad one. A gate that over-matches costs a wasted run; this one silently under-matches, and the tool's output gives no signal that a gate was omitted.

Dispositions offered, none judged here

  1. Give check:llms-txt a real population — the src/**/*.zod.ts tree whose counts it re-derives, plus contracts/, so any change to that tree names it. This is the mechanical fix; it closes the hole for every future diff without relying on anyone remembering.
  2. Make check:generated's "not run here" list a hard obligation in the dev contract. That list did name check:llms-txt — it enumerates eleven source audits check:generated declines to run. On the round that missed, only a subset of the eleven was run; on the fix round all eleven were run and all were green. This closes the human half.
  3. Both.

The dev's own recommendation is 3, and its reasoning is worth preserving: 1 is the real fix, because no amount of dev diligence recovers a family the derivation never prints; 2 is still owed, because the information was available in another gate's output and was not acted on.

Whether any other gate in the register has the same shape — declared population equal to its own guarded artifact — is not measured here and is the obvious follow-up question.

Refs: #13135 / PR #13186 (where it was measured) · the check:generated "not run here (11 source audits)" line (the second channel that did carry the signal).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions