Skip to content

fix(scripts): dispatch-gates derives the name-fold loader gate for every file under the roots it walks - #24263

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-24037-dispatch-gates-scan-roots
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-24037-dispatch-gates-scan-roots

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24037
Clause-②: no

The dispatch derivation now names node scripts/check-position-name-fold-loaders.mjs (and its --self-test) for a change to any file under the roots that gate walks. The fix is in the gate's own source: its roots become the ROOT_DIR_WATCH_HINTS literal the derivation already reads, and the walk is derived from that literal. scripts/pm/dispatch-gates.mjs and scripts/pm/dispatch-gates.data.mjs are unchanged: no new gate, row, ratchet, --tier mandate or constant (ruling 6243472660, term 3).

What changed

  • scripts/check-position-name-fold-loaders.mjs. The walked roots are now one exported literal, ROOT_DIR_WATCH_HINTS = ['packages/**', 'examples/**', 'apps/**', 'scripts/**', '.github/**']. SCAN_ROOTS is derived from it, so the walk and the declaration are one list and cannot drift. The one root not carried over is tools: the tree has no tools/ directory (0 tracked files). The scan count is unchanged at 10263 files before and after. A declared root the tree does not have would print as a dead lead on every card the gate matches.
  • scripts/pm/bare-root-worklist.mjs. The four REFUSE-WIDE verdicts on this gate's SCAN_ROOTS rows (packages, examples, apps, scripts; measured 2026-08-31) are withdrawn. Once SCAN_ROOTS is derived, the bare-word literals they judged no longer exist, so the self-test's STALE remedy applies: "Delete the entry; do not re-point it at another row". A comment in their place names the ruling and the measured consumer. The declaration battery registers two cases per recorded row, so its floor follows the row count: 128 to 126 (134 to 126 on a run). One docblock sentence that cited the withdrawn 99% row is annotated. This file is outside the claim's declared file surface. The edit is forced: with the gate change alone, this self-test exits 1 with 5 failures (the STALE case plus four attribution cases).
  • scripts/pm/dispatch-gates.self-test.mjs. One fast-tier block, against the live tree, for both invocations of the gate. It reads one probe per declared subtree root off the family's own extracted hints, plus the exact path PR fix(service-package,cloud-connection): convert stored package bodies forward at the sys_packages and marketplace doors #24028 edited (scripts/cross-package-test-inputs.mjs), and each must be MATCHED. In-place ablation: with the subtree hints stripped, the PR fix(service-package,cloud-connection): convert stored package bodies forward at the sys_packages and marketplace doors #24028 path and a brand-new package file both read silent. A floor case requires at least five subtree roots, so the loop cannot run zero times.

The mechanism, measured (the card marked it "likely, not confirmed")

  • Half right. The extractor (extractWatchHints) keys on no constant name and no export. It reads every quoted, path-shaped literal in the module body. The gate's roots were invisible because they were bare single-segment words, which looksPathy refuses: extractWatchHints("const X = 'packages';") yields []. '.github' is dotted and was already read. So SCAN_ROOTS being private was not the cause; the spelling was. check:watch-hint-literal constrains the declaration to a literal array of admissible literals, and the new one passes (--list shows it).
  • The miss was recorded, not accidental. bare-root-worklist.mjs held four REFUSE-WIDE verdicts on exactly these rows: the declaration would be true, and was refused for the precision of the matched column. Three things decide it here:
  • Precision cost. The gate is now MATCHED for every tracked file under the five roots: 10343 of 12129 tracked files (85%; packages 9545, examples 265, apps 51, scripts 437, .github 45), in both invocations. One run takes 2.4 s wall.

Derivation, before and after

probe f5ef054a this branch
--commands scripts/cross-package-test-inputs.mjs (the PR #24028 miss) 28 commands, no name-fold gate 30 commands; the diff is exactly the two name-fold commands
--commands on the four card paths (dispatch-gates.mjs, .data.mjs, .self-test.mjs, the gate) 33 commands n/a (the derivation files are not in this diff)
--commands with no paths (this diff, 3 files) — 32 commands, listed under Verification

Census (acceptance 2)

Population, by reading. Every gate's population constant, read by node scripts/pm/bare-root-worklist.mjs, the derivation-coupled instrument for this exact species (a population constant holding a bare top-level word the extractor reads nothing for). Its --wide sweep is the unrestricted contrast, and its CENSUS map holds the fs-trace census of computed roots. Each root's placement below is the derivation's own placeFamily verdict for a brand-new file under that root, read on this branch.

Queries over scripts/*.mjs and scripts/pm/*.mjs on this branch (files hit; controls first):

  • control isEntrypoint: 213 · control readFileSync: 255
  • readdirSync|readdir\(: 146 · \bwalk[A-Za-z]*\(: 99 · 'ls-files': 32 · SCAN_ROOTS?\b: 25 · \bROOTS\b: 22
  • ^(export )?const [A-Z_]*_WATCH_HINTS = (existing declarations under scripts/): 60 at f5ef054a, 61 here. check:watch-hint-literal --list prints 75 declarations repo-wide.
  • Bare-root worklist (restricted): 82 rows, 47 gate source files at f5ef054a; 78 rows, 46 files here. Wide contrast sweep: 140 pairs, 84 files at f5ef054a; 136 pairs, 83 files here. The diff in both sweeps is exactly the four name-fold rows.

Answer: yes, other gates keep roots the same undeclared way. One is converted in this PR; every other is already read or is listed below with its reason. No row was added to dispatch-gates.data.mjs.

how the roots are held today gates (source file · root) action
bare words, private; now the declared literal check-position-name-fold-loaders · packages, examples, apps, scripts (plus .github, already read) converted in this PR
bare word beside a declared literal at the same root (REACHABLE: a brand-new file under the root derives the gate) check-doc-formula-expressions (packages/lint) · content, docs, skills; check-agent-test-spelling · scripts, skills; check-cli-command-ids · scripts; check-corpus-claim-drift · skills; check-doc-authoring · packages, content, docs, skills; check-doc-route-spelling · skills; check-examples-live-imports · examples; check-org-identifier · apps, examples, packages; check-role-word · skills; check-scripts-symbol-anchors · scripts; check-skill-compatibility-version · skills; check-skill-frame-sync · skills; check-skill-identifier-liveness · skills already read
declared literal, narrower than the root (DECLARED-NARROWER; the gate reads only that subtree) build-skill-docs, build-skill-references (packages/spec) · skills; check-declaration-mirrors · scripts; check-driver-conformance DRIVERS_DIR · packages; check-dual-build-cjs-loads · packages; check-i18n-coverage · examples; check-logger-receiver-detach · apps, examples, packages; check-objectql-double-limit · packages; check-ratchet-remedy-authority · scripts; check-where-matcher-conformance · packages; pm/check-skill-id-lint · skills already read
bare word; a precise spelling exists and is deferred by a recorded verdict for want of a consumer (SPELLABLE-UNDECLARED) check-adr-0087-registration · apps, examples, packages; check-skill-compatibility-version PACKAGE_ROOTS · apps, examples, packages; check-i18n-bundles, check-i18n-coverage, check-i18n-stale-fill · packages; check-skills-token-ratchet · skills listed. The verdict is recorded; declaring re-decides it and needs its own measured consumer
bare word; the root IS the population and the declaration is refused for precision (REFUSE-WIDE) check-dispatcher-error-vocabulary, check-error-code-casing, check-error-status-conformance, check-runtime-services-index, check-single-authz-resolver, check-skill-identifier-liveness IMPL_ROOTS, measure-durability-swallow-family · packages; check-engine-double-contract · examples, packages listed. Recorded verdict, no measured consumer on any of these rows (see open question). check-error-status-conformance is derived by its content kind instead
bare word, REFUSE-WIDE, and the gate is on the wide-population channel (maintainer ruling 2026-09-05) check-optional-error-sink-contract · packages; check-resume-authority-declared · examples, packages; check-verify-stand-in-erasure · examples, packages listed. Declared wide by ruling; a subtree hint would contradict the marker
bare word; the population is a file-kind filter no glob spells (REFUSE-UNSPELLABLE) check-driver-conformance CASE_SETS_DIR, check-examples-live-imports PACKAGES_ROOT · packages; check-meta-type-normalized · packages (wide channel); check-runner-env-posture · apps, examples, packages (wide channel) listed. Its roots cannot be read that way
bare word, judged by nobody (UNJUDGED; the map's owner judges these) check-doc-security-posture (packages/lint) · skills; check-live-db-isolation · apps, examples, packages (wide channel); check-vendor-version-stamps · apps, examples, packages, scripts; check-whole-set-label-write · scripts (MATCHED through other hints). Measured "not a population": check-console-injection ×2, check-doc-frontmatter · docs, check-doc-security-posture · docs listed. The worklist forbids the pass that sees a row from judging it
computed root, no constant (fs-trace CENSUS, 13 families) wide channel: check:route-envelope, check:live-db-isolation, check:startup-registry-verdict, check:wildcard-fallthrough, check:init-service-contract, check:settings-bind-window; always-runs: check:driver-memory-census; brand-new package file silent: check:parse-guard, check:cli-command-ids, check:type-check-coverage, check:type-check-debt, check:error-code-provenance; MATCHED: check:org-identifier listed. A join(ROOT, 'packages') or loop literal is not a literal the extractor can read, so these cannot be read that way
computed root outside both recognisers (the --wide sweep's 34 extra files, each read) check-cross-package-test-inputs (a loop over packages/apps/examples; its population is test files, and a new test file under any of the three derives it through the test-file convention trigger, probed with examples/app-crm/src/zz-probe.test.ts); check:skill-examples, check:yaml-examples (a dir: property, path.resolve(REPO_ROOT, 'skills')); check:empty-state (a resolved packages root; undetermined); check:object-def-param-keys (git ls-files packages); check:service-providers (MANIFEST_PARENTS); check:docs-audit-scope (walkSrc(join(root, 'packages'))); check:console-sha (a dist path); check:tenant-chokepoint (join-built file paths); measure-test-shard-timings --self-test (a loop literal); check:durability-log-level (derived by its content kind and its declared scan surface) listed. Computed spellings the extractor cannot read. Of the 34 extra files: 8 are the CENSUS families in the row above; check-self-test-wired walks scripts and declares scripts/**/*.mjs, .mts and .sh (already read); these 11 are listed; the other 14 hold the word as a data key, a baseline path built by join, a self-test fixture or path parsing, not a walked root

The three earlier instances the card names, read today (--commands on real files):

  • check:error-status-conformance: derived for packages/metadata/src/loaders/ambiguous-metadata-stem.ts through its HTTP-status content kind. Its SCAN_ROOT packages row stays REFUSE-WIDE.
  • check:durability-log-level: derived for packages/metadata/src/loaders/database-loader.ts through its durability content kind and its declared scan surface.
  • check:i18n-coverage: derived for examples/app-showcase/src/data/analytics/showcase.cube.ts through its declared examples hints. Its packages half stays SPELLABLE-UNDECLARED and is reached through the extract-config convention trigger.

Verification

All readings below are on this branch's final head, cf4e110f (git rev-parse --short HEAD), with no commit after them.

  • Derived local subset. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack with no paths (the change set came from git: 3 files vs merge base f5ef054a7) derived 32 commands. Each was run with its exit code captured before any pipe, and all 32 exited 0. pnpm check:declared-population-live was also run beyond the union, because this diff adds a declaration; it exited 0 (268 of 330 families declare a population, all live).
  • Reconciliation. --ran printed ✓ dispatch-gates --ran: 32 derived famil(ies) accounted for — 32 run, 0 NOT-MEASURED (a DERIVED zero — all 32 recorded an exit code and none of them is 3), with exit 0.
  • Verdict lines for the families this diff moves:
    • node scripts/check-position-name-fold-loaders.mjs → 10263 file(s) scanned under packages, examples, apps, scripts, .github; 8 reference(s): 6 test, 2 declared instrument, 0 loader (exit 0). The same 10263 at f5ef054a with tools still in the list.
    • … --self-test → 39 cases pass (exit 0).
    • node scripts/pm/bare-root-worklist.mjs --self-test → OK self-test: 78 live row(s), 56 unreachable as spelled, 43 recorded verdict(s) — none stale, none missing, none contradicted (exit 0). With the gate change alone it exits 1 with 5 failures.
    • pnpm check:watch-hint-literal → 75 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 50 … (exit 0).
    • pnpm check:pm-dispatch-gates (the fast tier, as CI runs it on a PR) → ✓ dispatch-gates self-test (fast tier): 1902 cases pass (exit 0, 384 s).
  • Full battery, both tiers (beyond what a PR runs; it is what main's push and hourly runs execute): node scripts/pm/dispatch-gates.mjs --self-test run detached → fast 1902 case(s) in 394.0s · slow 260 case(s) in 1170.8s, ✓ dispatch-gates self-test: 2162 cases pass., exit 0, 1566 s wall.
  • Other local gates, all exit 0: check-ci-filter-parity, check-closing-keyword-parity (both), check-comment-mask-corpus, check-declaration-mirrors (both), check-scripts-symbol-anchors (both), check-self-test-wired (both), check-self-test-workflow-commands (both), check-whole-set-label-write (both), check:agent-test-spelling, check:bash32-floor, check:cli-command-ids, check:cross-package-test-inputs, check:driver-memory-census, check:entry-guard, check:gitlink-declared, check:nul-bytes (12120 text files, no control bytes), check:parse-guard, check:pnpm-filter-targets, check:pnpm-flag-spellings, check:ratchet-remedy-authority, check:refd-timer-probe.
  • Not run locally, CI's to report: the full check:* farm in Lint & Repo Gates and the other required contexts. No package is touched, so there is no build closure, package test or typecheck to owe.

Acceptance notes


Generated by Claude Code

…OT_DIR_WATCH_HINTS, and the walk reads them

The walk read a private list of bare single-segment words, which the
dispatch-gates hint extractor admits as nothing, so a card editing any file
under those roots was never told about this gate. The roots are now one
literal subtree declaration, and SCAN_ROOTS is derived from it. The dead
tools root (no tracked file) is dropped: the scanned count is unchanged.

Claude-Session: https://claude.ai/code/session_01QBcLjVCmcWbZEk6qhYMib2
Co-authored-by: Claude <noreply@anthropic.com>
…WIDE rows its gate's declaration made stale

check-position-name-fold-loaders now declares its roots and derives SCAN_ROOTS
from that literal, so the bare-word literals these verdicts judged no longer
exist and the self-test's STALE remedy applies. The declaration battery's
floor follows the row count, two cases per row, 134 to 126 on a run.

Claude-Session: https://claude.ai/code/session_01QBcLjVCmcWbZEk6qhYMib2
Co-authored-by: Claude <noreply@anthropic.com>
…ame-fold gate walks derives it

Live-tree cases for both invocations: one probe per declared subtree root,
read off the family's own extracted hints, plus the exact path PR #24028
edited; stripping the subtree hints leaves the measured miss NOT MATCHED.

Claude-Session: https://claude.ai/code/session_01QBcLjVCmcWbZEk6qhYMib2
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 18, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

Seat domain:skills#1 · session_01QBcLjVCmcWbZEk6qhYMib2 (sunhaolin) · read 2026-10-18T05:50Z · head cf4e110f3d89c8e885ee7d341cf888027be05c6f · verdict: ACCEPT (not governed — scripts/** only; the seat's ACCEPT is the review of record and the seat lands it through the queue)

Implemented-by: claude/issue-24037-dispatch-gates-scan-roots
Reviewed-by: session_01QBcLjVCmcWbZEk6qhYMib2

  1. Shape. Draft PR against main; body line 1 Fixes #24037, line 2 Clause-②: no (copied from claim 6245578220). The only closing keyword in the body is that line; PR fix(service-package,cloud-connection): convert stored package bodies forward at the sys_packages and marketplace doors #24028 and card 24171 appear beside no closing verb. File list: scripts/check-position-name-fold-loaders.mjs (+21/−1), scripts/pm/bare-root-worklist.mjs (+16/−27), scripts/pm/dispatch-gates.self-test.mjs (+46/−0). mergeable true at this read. check-governed-merges.mjs --pr 24263: no governed path, +83/−28 under the 3000-line threshold.
  2. Premise and acceptance, read by the seat. The miss reproduces and closes: --commands scripts/cross-package-test-inputs.mjs derives 28 commands at f5ef054a and 30 on this head, the diff being the two name-fold commands (acceptance 1). The gate now declares ROOT_DIR_WATCH_HINTS = ['packages/**', 'examples/**', 'apps/**', 'scripts/**', '.github/**'] and derives SCAN_ROOTS from it — one list, two readers; dispatch-gates.mjs and .data.mjs are untouched because the extractor already reads path-shaped literals (the seat's assumption (a) was half right: the extractor keys on no name and no export; bare single-segment words were the invisible part). The dropped tools root: git ls-tree origin/main -- tools lists 0 files, and the scan count is 10263 before and after. Acceptance 2 (the census) is a table in the PR body built from bare-root-worklist.mjs, its --wide sweep and the fs-trace CENSUS, with queries and control counts; the three earlier instances the card names are each derived today through their own hints or content kinds. Acceptance 3 held: no new gate, row, ratchet, mandate or constant.
  3. The one re-decision, confirmed by the seat. Four REFUSE-WIDE rows for this gate's bare SCAN_ROOTS words are withdrawn from scripts/pm/bare-root-worklist.mjs. The file's own coupling pin demands it: 「STALE: a verdict that outlives its row … when a gate takes a declaration … the verdict must come out with it … Delete the entry; do not re-point it at another row」 (its self-test exits 1 with the gate change alone). The batch Release version 0.1.x for ObjectStack Protocol packages #43 ruling 「this file's rows are NOT deleted」 (the file's L1879–L1882) is the resolution for gates that carry the wide-population marker — the refused families moved onto that channel keep their rows because the marker says the same fact in the gate's own source. This gate took no marker; it declared real subtree roots because ruling 6243472660 term 3 orders exactly that conversion for it. The withdrawn rows leave a dated WITHDRAWN note naming the ruling and the measured consumer (PR fix(service-package,cloud-connection): convert stored package bodies forward at the sys_packages and marketplace doors #24028), and the measured reasons stay readable at f5ef054a. The precision cost (the gate is MATCHED for 85% of tracked files, 2.4 s a run) is the trade the ruling took on a measured miss. The declaration-battery floor moves 128 → 126 for the two cases each withdrawn row registered, a fewer-rows reading, not a weakened pin.
  4. The self-test block. One fast-tier, live-tree block: the family is discovered; at least five subtree roots are read off the family's own extracted hints (a floor, so an emptied hint set cannot pass by running zero cases); one probe per root plus the exact path PR fix(service-package,cloud-connection): convert stored package bodies forward at the sys_packages and marketplace doors #24028 edited must be MATCHED; and an in-place ablation strips the subtree hints and the two probes must leave MATCHED (silent/undetermined). Discriminating in both directions.
  5. Gates — check-runs pinned to head cf4e110f. 43 completed: 32 success (the Lint · and Type Check · lanes, Test Core 1–6/6, Governed Surface Queue Guard, Check Changeset, the four claim checks, Dogfood Regression Gate), 11 skipped, 0 failed. Required contexts: Lint & Repo Gates, TypeScript Type Check, Governed Surface Queue Guard, Check Changeset success; Test Core success (all six shards); Build Core skipped by the selector (no package touched). check-expected-skips.mjs --pr 24263 OK, exit 0 (every skip in the roster). The dev's local subset: 32 of 32 derived families exit 0 (--ran reconciled, check:pm-dispatch-gates fast tier 1902 cases in 384 s), the full battery both tiers 2162 cases pass (1566 s, detached, tail --pid), bare-root-worklist --self-test 78 live rows, none stale/missing/contradicted, check:watch-hint-literal 75 declarations.
  6. Changeset. None owed (scripts/** is in no package's files[]); skip-changeset declared in the pr_create stroke and present, Check Changeset success. The seat concurs.
  7. Report hygiene. mcp_calls 0; api_writes 2 relay strokes, no PATCH/PUT/DELETE; four git push (the probe and three WIP commits). Deviations D1–D9 read: the file-surface breach on bare-root-worklist.mjs is forced by its STALE pin and declared in the body — the claim's file surface is revised by this record and the card pointer (same round); the tools drop (verified above); the precision cost (judged in 3); no main merge (the queue rebuilds on the current main; a local merge-tree against fb6fb24e is clean per the dev); trailers and footer per AGENTS.md; two stray pid files at the filesystem root (/dg-full.pid, /dg-fast1.pid) that neither the dev nor the seat may remove — the safety check blocks a removal at /; left for the maintainer, named in the round report.
  8. Open question answered by the seat (no escalation: instrument policy already ruled). Convert the remaining REFUSE-WIDE rows now? A — per row, on a measured consumer only, as this PR did: ruling 6243472660 term 3 names this gate and lists the rest; the worklist's 2026-08-26 criterion (a recorded consumer) stands; option B is a broad widening with no measured pull that floods package cards with leads and would re-open batch Release version 0.1.x for ObjectStack Protocol packages #43's wide-population ruling; option C is what term 3 already rejected for this gate. No card for B; a future measured miss files its own card.
  9. Out of scope. The census listing in the PR body is the family-closing record (acceptance 2); nothing filed separately. Carried on the seat post: the pid files, and the census table as the reference for any later per-row conversion.
  10. Landing (not governed). Every check green or an expected skip ⇒ relay pr_ready then automerge_enable; the probe-table landing comment follows when the queue ref is gone and origin/main carries the commit. subscribe_pr_activity is live on this PR since 2026-10-18T05:43Z. Serial queue: card 24073 (term 2, the same three dispatch-gates* files) dispatches after this PR merges, as the lane's one dev under the maintainer's serial posture.

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants