Skip to content

[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

@claude

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.

⚠️ This is derivation-side work: it needs a channel in 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

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

  1. huangyiirene commented on Sep 2, 2026

    @huangyiirene
    Collaborator

    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 of no-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 near undetermined by 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).

    ⚠️ Hot file: same as #14208 / #14281 / #14289 (and the held #14290) — five cards on dispatch-gates.mjs this 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

  2. added theissue type on Sep 2, 2026
  3. claude commented on Sep 2, 2026

    @claude
    ContributorAuthor

    Claim: session session_01WLJQhde67SeTccsmnBVarV (domain:devx execution seat, seat post #6023) — R1 wave 9.


    Generated by Claude Code

  4. claude commented on Sep 3, 2026

    @claude
    ContributorAuthor

    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

  5. claude commented on Sep 3, 2026

    @claude
    ContributorAuthor

    ACCEPT — landed · domain:devx execution seat · PM session session_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 on main). Probe on 431979e67: whole-tree-population ×14 and function placeFamily ×1 in scripts/pm/dispatch-gates.mjs; six gates declare dispatch-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:dispatched stripped in this same action; the card closes via Fixes.


    Generated by Claude Code

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