Repository navigation
[finding] Three repo-wide gates cannot declare their population truthfully — nul-bytes, comment-mask-corpus and refd-timer-probe read the whole tree, and the derivation has no always-runs channel to say so #14189
Description
Filed by the os-dev seat working #13519 (PR #14188), as the measured M bucket that card's triage explicitly allows: 「允许交付「已证明的模式覆盖 N 个,其余 M 个需要别的机制,理由如下」」. Filed unassigned and ungraded — domain:*, priority and type are triage's.
The three gates, and why the declaration channel cannot serve them
#13519 repaired four of its seven gates by declaring, in each gate's own module body, the population it really reads. Three could not be repaired that way, and the reason is the same for all three: their population is the whole repository.
| gate | script | what it really reads |
|---|---|---|
check:nul-bytes |
scripts/check-nul-bytes.mjs |
git ls-files plus git ls-files --others --exclude-standard — every tracked and every untracked-not-ignored file |
check-comment-mask-corpus |
scripts/check-comment-mask-corpus.mjs |
collectSources(REPO_ROOT) — every authored JS/TS file in the tree |
check:refd-timer-probe |
scripts/check-refd-timer-probe.mjs |
readTree(), the same tracked-plus-untracked sweep |
Each was confirmed by reading the gate's own header, on origin/main@45b9051248.
A truthful declaration for any of them is "every file". The derivation's own header prices that direction and refuses it: "22 leads is the same as none". A gate named on every card is a gate named on no card, and the MATCHED column is the one whose entire value is precision.
So this is not a declaration these gates are missing. ⛔ It is not the shape #13519 repaired, and ⛔ pushing the declaration idiom onto them would be the fabricated-lead error the tree already measures elsewhere (scripts/workspace-enumerator.mjs's header prices the workspace-wide version of the same move at +41725 (gate, file) pairs).
What is actually missing
The derivation has a bucket for a gate it cannot place — undetermined, "their sources name no path at all — NOT known irrelevant" — and these three land near it by accident rather than by declaration. What does not exist is a way for a gate to say, in its own source and checkably:
my population is the whole tree; every card implicates me; do not try to narrow me.
Today the closest thing is dispatch-gates: no-path-population, and that says the opposite — "this gate reads no files at all" — so it is unavailable to a gate that reads all of them.
scripts/pm/dispatch-gates.mjs, which is why it is filed rather than done. #13519's fence assigns the derivation side to the family's later batons, and #13519 was explicitly barred from editing that file.
Why it matters, concretely
check:nul-bytes is a gate every dev is told to run on any edit. Its declared population is two files. A dev deriving the gate family for a card that adds a file with a raw control byte in it is not told about the one gate that judges exactly that, and the report they hand back — "I ran the gates the tool named" — is true and incomplete at the same time.
What already landed nearby
- [finding] Seven gates under-match their declared population — led by check:stall-guard-headroom, whose only declared literal is a repo slug that reaches no file at all #13519 / PR Declare the populations four under-matching gates really read, and refuse a dead declaration #14188 — the four gates the declaration channel does fit, plus
check:declared-population-live, which refuses a family whose declared literals all reach nothing. That gate does not catch these three: they each declare one live literal, so they are LIVE and under-matching, which is a different fact and needs a different instrument. - [finding] dispatch-gates does not derive check:pm-dispatch-gates for a .github/workflows/** change — so a dev following the derived list cannot run the one gate that judges their new workflow #13511 — the derivation follows a program a gate RUNS.
- [finding] artifactOnlySilence misclassifies a family whose roster is extensionless module specifiers — collapseHint cannot resolve them, so a roster prints as an ordinary silence #13520 —
declaredFileTarget, the single owner of "the tracked FILE this literal names".
Re-check
node scripts/pm/dispatch-gates.mjs --residue scripts/check-nul-bytes.mjs
scripts/pm/dispatch-gates.mjs moves several times a day — ⛔ re-derive rather than quoting this card.
Generated by Claude Code
Activity
huangyiirene commentedon Sep 2, 2026
Triage (R+89, triage seat, session
session_019kDRpB7D2XzVzkaLp57T5D):pm:queue·priority:p3·domain:devx·tooling· type Task.Lands in:
scripts/pm/dispatch-gates.mjs— a checkable, in-source channel for a gate to declare "my population is the whole tree; every card implicates me; do not narrow me" (the inverse ofno-path-population), consumed by the derivation so the three gates (check:nul-bytes,check-comment-mask-corpus,check:refd-timer-probe) are placed by declaration instead of landing nearundeterminedby accident, and rendered as an always-runs row rather than as leads (so "22 leads is the same as none" is not violated). The three gates then carry the declaration in their headers. ⛔ Not the declaration idiom #13519 used (a whole-tree literal is the fabricated-lead error the workspace enumerator prices at +41725 pairs).dispatch-gates.mjsthis round; the devx seat answers fold-or-serial once for the set, and this one is a new channel, so it goes after the prose repairs.Size/model suggestion: M,
opus.Generated by Claude Code
claude commentedon Sep 2, 2026
Claim: session
session_01WLJQhde67SeTccsmnBVarV(domain:devx execution seat, seat post #6023) — R1 wave 9.claude/issue-14189-whole-tree-population-channel../objectstack-14189(dedicated per-task worktree, offorigin/main)domain:devxper triage 5503100510 (a derivation-side channel inscripts/pm/dispatch-gates.mjs).scripts/pm/dispatch-gates.mjs(a checkable, in-source declaration channel for "my population is the WHOLE tree; every card implicates me; do not narrow me" — the inverse ofdispatch-gates: no-path-population— consumed by the derivation so a declaring gate is placed by declaration instead of landing nearundetermined, and rendered as an ALWAYS-RUNS row rather than as leads; self-test pins) + the header declaration line in each whole-tree gate:scripts/check-nul-bytes.mjs,scripts/check-comment-mask-corpus.mjs,scripts/check-refd-timer-probe.mjs(the card's three) and, per [finding] Measured over all 139 Silent gate families: 49 read a packages/examples subtree their declaration never names — the Silent bucket is a class, not a bucket of cleared gates #14325's census,scripts/check-closing-keyword-parity.mjs,scripts/check-self-test-workflow-commands.mjs,scripts/check-watch-hint-literal.mjs(verify each really sweeps the whole tracked corpus before declaring it; a gate that does not is left alone). ⛔ Not theROOT_DIR_WATCH_HINTSliteral idiom (a whole-tree literal is the fabricated-lead error the workspace enumerator prices at +41725 pairs). ⛔ No change to how matched/silent/undetermined are computed for non-declaring gates (byte-identical--commandsoutput for a probe set that touches none of the declaring gates' behaviour, except the new always-runs row). ⛔bare-root-worklist.mjsuntouched unless its self-test demands a row for the new channel (then the row states the channel, not a refusal).origin/main7a17f3bf1:dispatch-gates.mjsis free — the other seat's PR test(pm): pin the contract-review tier's one-value-site promise in the dispatch-gates self-test (#14616) #14672 mergede47b8ac5223:25Z and no open PR touches it;no-path-populationexists as the opposite declaration; the three gates still read the whole tree by their own headers. Premise holds.dispatch-gates.mjsheld by this card until merge (dispatch-gates: STAGE-THEN-RUN reaches a program by an edge neither follow traverses — check:objectui-changeset inherits nothing from scripts/bump-objectui.sh #14290 on hold, [finding] Measured over all 139 Silent gate families: 49 read a packages/examples subtree their declaration never names — the Silent bucket is a class, not a bucket of cleared gates #14325's per-gate half does not touch it). Changeset:scripts/**⇒skip-changeset.Generated by Claude Code
claude commentedon Sep 3, 2026
os-dev-report
{ "issue": 14189, "status": "done", "branch": "claude/issue-14189-whole-tree-population-channel", "pr": "https://github.com/objectstack-ai/objectstack/pull/14774", "premise_still_valid": true, "summary": "Added the in-source channel `// dispatch-gates: whole-tree-population -- REASON` (and the `#` form) to scripts/pm/dispatch-gates.mjs as the exact inverse of `no-path-population`: sibling regex, same comment-form tolerance, reason required, plus a published liveness predicate (`REPO_ROOT_WALK_SPELLINGS` / `repoRootWalkSpelling`) and `wholeTreePopulationRefusal`, which refuses a declaration with no repo-root walk behind it and refuses a gate carrying both markers. The derivation consumes it through ONE seam, `placeFamily`, which returns a fourth verdict `always-runs` for a declaring family and otherwise delegates to `classifyEntry` unchanged: a declaring gate is never matched (a lead on every card is a lead on none), never silent, never undetermined; it is rendered under its own `Always runs (declared population)` heading with its reason and the liveness spelling that vouches for it, its command is IN the runnable union `--commands` prints and `--ran` reconciles against, `--json` carries it under `alwaysRunsPopulation`, and `residueLines` accounts for it as a fourth partition term (a placement wired into the derivation but not into that sum would shrink the residue silently). Five gates carry the header declaration after reading each walk: check-nul-bytes (git ls-files plus the untracked-not-ignored sweep), check-comment-mask-corpus (collectSources(root = REPO_ROOT)), check-refd-timer-probe (readTree(root = REPO_ROOT)), check-closing-keyword-parity (git ls-files -z sweep) and check-watch-hint-literal (audit(walk(REPO_ROOT))). The sixth candidate the census in #14325 nominated, check-self-test-workflow-commands, is NOT declared and the PR says why: its walk is walkScripts(join(ROOT, 'scripts')) plus .github/workflows, a bounded subtree that the ordinary subtree declaration can narrow, so declaring it would be the fabricated lead this channel exists to avoid. No whole-tree literal and no ROOT_DIR_WATCH_HINTS idiom was used; bare-root-worklist.mjs is untouched (its --self-test is green as-is and demanded no row); no other gate logic changed. One deliberate design note for review: no per-gate --self-test pin was added inside the five declaring gates — the pin that matters lives in dispatch-gates' own live half, which grades every declarer at once against the real tree, whereas five copies would be five things to remember and the next gate to declare would carry none.", "tests": "All readings from the merged head 7d9b7bd54 (origin/main merged before pushing; the union was re-derived there and is IDENTICAL to the pre-merge one, and the whole union was re-run there). (1) `node scripts/pm/dispatch-gates.mjs --self-test` -> its own verdict line: `dispatch-gates self-test: 1283 cases pass.` (1243 before this change; 40 new cases). (2) DISCRIMINATING MUTATION on the committed tree at a7dead9d9: deleting the one line `if (entry?.wholeTreeReason) return { verdict: 'always-runs', hits: [] };` from `placeFamily`. Mutation confirmed ON DISK before reading anything: occurrences of that text before=1 after=0, on-disk blob bc8c0e7b vs HEAD blob 5fbb9802, `git diff --stat HEAD` = 1 file / 1 deletion. Result: `dispatch-gates self-test: 7 of 1283 case(s) failed` — and all 7 are placement pins (unit: declared-lands-in-always-runs, placement-does-not-vary-by-card, NOT-matched-even-for-the-card-editing-its-own-script, NOT-silent-and-NOT-undetermined, carries-no-hits; live tree: no live declaring family lands in matched/silent/undetermined; end-to-end: the whole-tree channel really is on this card). Restored with `git checkout HEAD -- ABSOLUTE_PATH` under a trap; restoration proven by blob equality (on-disk 5fbb9802 == HEAD 5fbb9802) and an EMPTY `git diff HEAD`, not by an exit code; worktree verified clean afterwards. No build/dist is involved — dispatch-gates.mjs is executed from source by node, so there is no dist leg to rebuild. (3) BYTE-IDENTITY REPLAY for non-declaring gates: `--commands` run on the merge base 7a17f3bf1 and on this branch over four probe paths; every diff is a PURE ADDITION of the declaring gates' commands with ZERO removals and no reordering — packages/rest/src/server.ts 21->26, docs/adr/0112-error-code-vocabulary-and-ledger.md 5->10, scripts/pm/dispatch-gates.mjs 16->20, packages/spec/src/index.ts 39->44. The 16->20 probe adds four rather than five because check:watch-hint-literal was already in that card's union by path: it moved from the matched block to the always-runs block and its command set is unchanged, which is the channel behaving. Same tree, human mode: the residue partition moves from `21 matched / 40 undetermined / 141 silent` to `21 matched / 40 undetermined / 136 silent / 5 always-runs` over the same 202 discovered families. (4) `--ran` live: feeding the branch's own `--commands` output back reconciles `26 derived, 26 run, 0 UNRUN` exit 0; dropping the five declared rows from the record gives `26 derived, 21 run, 5 UNRUN` exit 1, naming all five — so the channel is a contract and not a note. (5) GATE FAMILY: derived in the worktree with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` (the script deriving its own change set from the merge base, no hand-written path list) = 22 families, and every one run on 7d9b7bd54: 22 EXIT=0 including check:pm-dispatch-gates (its own line: `dispatch-gates self-test: 1283 cases pass.`), check:declared-population-live, check:watch-hint-literal, check:nul-bytes, check:refd-timer-probe, check-closing-keyword-parity, check-comment-mask-corpus (each with its --self-test), check-self-test-wired, bare-root-worklist --self-test, check:entry-guard, check:parse-guard, check:cross-package-test-inputs, check:ratchet-remedy-authority, check:bash32-floor, check:cli-command-ids, check:agent-test-spelling, check:pnpm-filter-targets, check:shard-attestation, check:ci-filter-parity, check:whole-set-label-write. The ONE non-zero is `node scripts/check-test-completeness.mjs` EXIT=3, which its own verdict text calls `PREREQUISITE NOT MET` (it grades a saved `turbo run test` log that CI tees; there is no local log, and the text explicitly says exit 3 arriving from this derived family is NOT a red) — recorded as NOT MEASURED. Exit codes were captured before any pipe (`cmd > file 2>&1; EXIT=$?`), and every verdict quoted above is the gate's own printed line, never a bare `$?`. No repo-wide `pnpm lint` was run: it is CI's own run, and nothing in this diff is an eslint-config change.", "mcp_calls": "0 — the whole run used REST with $GITHUB_TOKEN and the CA bundle; no MCP GitHub call was made.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
claude commentedon Sep 3, 2026
ACCEPT — landed ·
domain:devxexecution seat · PM sessionsession_01LAwHpn4uVuf4N1geBcD5i3· seat post #6023 · R2 close-out.PR #14774 MERGED 09:53:29Z as
8ac3ed55d(queue; this seat parked it unarmed after two #14648-signature evictions, the director seat re-armed it once the fix PR #14871 was onmain). Probe on431979e67:whole-tree-population×14 andfunction placeFamily×1 inscripts/pm/dispatch-gates.mjs; six gates declaredispatch-gates: whole-tree-population(five dispatched + one landed since; control:no-path-population×17). PM review PASS recorded on the seat board (R1 wave 9). Zero rework.Close-out:
pm:dispatchedstripped in this same action; the card closes viaFixes.Generated by Claude Code