Uh oh!
There was an error while loading. Please reload this page.
Declare the populations four under-matching gates really read, and refuse a dead declaration - #14188
Merged
Merged
Conversation
…ead, and refuse a dead declaration Fixes#13519
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
…nt its worklist row
baozhoutao
marked this pull request as ready for review
September 1, 2026 11:08
Uh oh!
There was an error while loading. Please reload this page.
baozhoutao
deleted the
claude/issue-13519-gate-population-underdeclare
branch
September 1, 2026 11:33
This was referenced Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#13519
Four of the seven gates in the census declared a population that under-matched
what they actually read. Each is repaired where the read lives — a literal
population declaration in the gate's own module body, held against the very
constant that gate reads from — and the class is closed by giving a verdict to
a fact the derivation already computes and prints as prose.
⛔
scripts/pm/dispatch-gates.mjsis not touched. No gate name was added toany derivation, table or roster.
⭐ The lead gate: matched population 0 → 29, with the command
check:stall-guard-headroomresolves toscripts/measure-stall-guard-headroom.mjs(not
check-stall-guard-*.mjs— re-derived frompackage.json, as the cardwarns). Its only path-shaped literal was
DEFAULT_REPO— the repo slugobjectstack-ai/objectstack— which names no tracked file.45b9051248)["objectstack-ai/objectstack"]["objectstack-ai/objectstack", ".github/workflows/**"]Why it declared nothing is the interesting half, and it is not a forgotten
string. The population is declared — in
check-stall-guard-budget.mjs, whichthis tool imports
WORKFLOW_DIRandscanfrom. The import follow deliberatelyrefuses a target that is itself a discovered gate file, so nothing was
inherited. That refusal is right; what was missing is this end of it. A gate that
reads a population through a sibling gate has to declare that population
itself. The declaration is held against the imported
WORKFLOW_DIRin the gate'sown
--self-test, so a moved read reds there rather than going quiet in a brief.Per-gate verdict — 4 fit the pattern, 3 need another mechanism
check:stall-guard-headroomscripts/measure-stall-guard-headroom.mjs.github/workflows/**check:skill-docspackages/spec/scripts/build-skill-docs.tsskills/*/SKILL.md+skills/README.mdcheck:service-providersscripts/check-service-providers.mjsPARENT/*/package.jsonpatternscheck:turbo-task-graphscripts/check-turbo-task-graph.mjscheck:nul-bytesscripts/check-nul-bytes.mjscheck-comment-mask-corpusscripts/check-comment-mask-corpus.mjscheck:refd-timer-probescripts/check-refd-timer-probe.mjsEach was re-confirmed by reading the gate's own header on this tree, independent
of the mechanical classifier. None was already fixed.
Why the last three are refused rather than forced
All three read a repo-wide population and their headers say so:
check-nul-bytesscansgit ls-filesplus untracked-not-ignored;check-refd-timer-probe'sreadTreedoes the same;check-comment-mask-corpuswalks
REPO_ROOTfor every authored JS/TS file. A truthful declaration for anyof them is "every file", and the derivation's own header prices that direction:
"22 leads is the same as none." A gate on every card is a gate on no card.
They do not need a declaration; they need a repo-wide / always-runs channel
in the derivation, which is the derivation side and out of this card's fence.
Filed unassigned as #14189, with the per-gate evidence.
What
check:skill-docscost to decide — the tree had already refused the easy answerThe first draft declared
skills/**, which is what the idiom's usual escapelooks like.
scripts/pm/bare-root-worklist.mjsreddened on it, by name, and itwas right to: it carries the measured triage for this exact root and records this
gate's population as 12 of the 50 files tracked under it (24%), so a subtree
hint names the gate for 38 files it never opens — the REFUSE-WIDE trade this
repo prices as the costlier error.
What that recorded refusal turned on is worth reading closely, because it is a
claim about one spelling: "no single spelling of this idiom reaches both" —
the 11 skill entrypoints, and the root README the generator WRITES, which sits
outside every skill directory. Two literals reach both and reach nothing else:
12 of 12, precise and complete. So the declaration stands and the row's
verdict is re-pointed
SPELLABLE-UNDECLARED→DECLARED-NARROWER, which is thevalue defined for a gate that took the escape at a strictly narrower population
and whose bare root stays uncovered. That is one of the two resolutions the
worklist's own self-test names for this contradiction.
⛔ Its neighbour row,
check-skills-token-ratcheton the same root, is notre-pointed: that gate walks the root recursively at 49 of 50, where the precise
spelling buys one file of discrimination, and its deferral is untouched.
What
check:turbo-task-graphcost to decideIts declaration block used to say the member manifests "stay undeclared on
purpose (the enumerator owns them and declares none)".
workspace-enumerator.mjsdoes declare none — but its header states why, and the reason points the other
way: "each gate keeps declaring its OWN population in its OWN module body […]
What is consolidated here is the PARSE, never the DECLARATION." The +41725
(gate, file) pairs it measured and refused are the price of the subtree
claim, ~5400 files. The claim made here is the manifests only — 79 files, every
one of which
readWorkspaceScriptsreally opens — and--self-testholds itagainst the enumerator's live answer in both directions, so a twelfth glob in
pnpm-workspace.yamlreds in this gate.Why this class cannot recur under another gate name
Adding four declarations protects four gates. It does not stop the next gate
shipping with a declaration that reaches nothing — and that is the shape the lead
had: the declaration and its absence print identically. A family whose
declared literals all reach zero tracked files scores an ordinary silence, byte
for byte the output of a gate that declared nothing, and no one reading the brief
can tell them apart.
The derivation already computes this fact. It prints it under
--residue:scripts/check-declared-population-live.mjsgives that standing fact a verdict.No new instrument, no allowlist, no ratchet, and no gate name in it.
The rule is per-family, not per-literal: a gate may legitimately spell a slug,
a sentinel path or an example inside a message beside a live declaration, and
only a whole declaration that reaches nothing is refused.
Proven able to red, by ablation (mutation and restore both confirmed on
disk —
git hash-objectagainst theHEADblob, before and after):The stronger rule was implemented and refused
"A gate that enumerates a directory must carry a watch-hint declaration" is the
rule that suggests itself first. Measured at
45b9051248: of 193 discovered gatefiles, 114 enumerate a directory and 86 of those carry no declaration under any
spelling of the idiom. A gate shipping 86 findings is an allowlist with a
verdict attached — and an allowlist of gate names is the repair this lane has
already ruled against three times. Most of the 86 are not defects either: they
declare their population as ordinary path literals, invisible to a scan for the
idiom. Refused, and recorded here rather than in a follow-up, because the refusal
is part of the argument for the rule that shipped.
The second dead declaration the new gate found
The sweep found exactly two families in the fleet. The other is
check:pm-clause2-carriers, whose only path-shaped literal was the`owner/name`placeholder inside a refusal message. That gate reads nofile in the tree at all — its whole input is the GitHub API — so it takes the
gate's own remedy (b): the example is respelled
`owner`/`name`so itis no longer read as a declaration, and the gate now carries a
dispatch-gates: no-path-populationmarker with its reason. The seven-memberrepair set is untouched; this is the guard's own remedy applied to the one other
carrier the guard names, and without it the guard could not ship green.
Changeset
skip-changeset: nothing here is published from any package. The diff is gatescripts, one workflow step, one root
package.jsonscript entry, andpackages/spec/scripts/build-skill-docs.ts—packages/spec'sfilesfieldpublishes
dist,json-schema,liveness,prompts,llms.txt,README.md,src/**/*.zod.ts,api-surfaceandspec-changes.json, and notscripts/.Gates run — at
04b7f769, the final commitThe family was derived from the actual diff with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands(59 commands, re-derived after the last commit and unchanged), plus
node scripts/check-ratchet-remedy-authority.mjs. All 60 pass, except fourthat report PREREQUISITE NOT MET because this container's workspace is not built
—
check-dev-prereqs(exit 1, "the workspace is not built"),check-test-completeness(exit 3, no saved turbo log — its own text says torecord this as NOT MEASURED),
check:dual-build-cjs-loads(exit 3, nodist/)and
check:type-check-debt(exit 3, closure not built). Each exit code wascaptured before any pipe. None is a finding.
bare-root-worklist --self-testwas a genuine red on the first draft and isgreen here — see the
check:skill-docssection above for what it caught.Also run, beyond the derived family:
pnpm lintover the whole repo (exit 0, nonarrowing needed),
pnpm --filter @objectstack/spec exec tsc --noEmit(exit 0)and — because
--listFilesshows the edited.tsis not in that program —tsc -p tsconfig.scripts.json --noEmit --listFiles, which does include it(1 match) and is clean.
Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Generated by Claude Code