Skip to content

fix(pm): dispatch-gates names every CI-run gate — whole-tree census gates join the declared bucket, residue measured - #15344

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-15312-census-gates-derivation
Sep 4, 2026
Merged

fix(pm): dispatch-gates names every CI-run gate — whole-tree census gates join the declared bucket, residue measured#15344
baozhoutao merged 1 commit into
mainfrom
claude/issue-15312-census-gates-derivation

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #15312

The defect, and the shape of the fix

check:driver-memory-census counts a vi.mock of the frozen @objectstack/driver-memory package as a module binding wherever in the tree it is written, and names no path literal anywhere in its source. scripts/pm/dispatch-gates.mjs therefore scored it undetermined for every card in the repo, so no --commands harvest could contain it. The seat on PR #15282 derived its family, ran 57 of them with 53 green, and CI's Lint & Repo Gates failed on the one gate the derivation could not offer.

The card's ⚠️ is binding and is honoured: nothing here widens what a seat is told to run by hand. Every change below is computed from the tree at runtime.

1. MEASUREMENT — which gates does CI run that no derivation can name

Measured mechanically at cd1f8ee96, not recalled. Column A is every gate script CI runs, taken from check-self-test-wired.mjs's own collectInvocations (an independent parser, deliberately not dispatch-gates' discovery, so a discovery gap surfaces as residue too). Column B is the union of what dispatch-gates can ever name: the matched derivation over the whole tracked tree as one maximal card, plus the convention block, plus the whole-tree bucket, plus the artifact-roster block.

The probe card for Column B is the whole tracked tree minus each family's own file closure. That subtraction is load-bearing: every family matches the card that edits the gate itself through the identity key, and counting that as "the derivation can name it" scores the entire population green on a card nobody files. Without it the residue measures 14; with it, 44.

CI-run gate scripts (independent parser):        186
dispatch-gates families discovered:              252
gate scripts some derivation can name:           169  (170 after this PR)
RESIDUE:                                          44  (43 after this PR)

check:driver-memory-census is in the pre-fix residue — the card's positive control, confirmed rather than assumed.

A. CI runs it, but it is not a check:* family — 13

gate workflow step why no derivation names it
scripts/assert-console-spec-injection.mjs ci.yml a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/build-console.sh ci.yml, cut-rc.yml (pnpm objectui:build), cut-rc.yml (pnpm release), release.yml, release.yml (pnpm release), showcase-smoke.yml a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/collect-release-notes.sh lint.yml a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/console-spec-probes.mjs ci.yml a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/downstream-smoke.sh cut-rc.yml, release.yml a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/gen-sdui-manifest.sh cut-rc.yml (pnpm sdui:manifest) a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/pm/os-regen-merge.sh lint.yml a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/pm/os-verify-lock.sh lint.yml a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/publish-smoke.sh pack-smoke-optin.yml, publish-smoke.yml a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/release-publish.sh cut-rc.yml (pnpm release), release.yml (pnpm release) a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/release-spec-changes.sh cut-rc.yml, release.yml a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/sync-protocol-version.mjs cut-rc.yml (pnpm version), release.yml (pnpm version) a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction
scripts/vitest-worker-cap.mjs ci.yml, rerun-safety-nightly.yml a build / release / runner step, never invoked through a check: script or a node scripts/check-*.mjs step — outside the derivation's population by construction

B. declared no-path-population (examined; the derivation prints the reason) — 3

gate workflow step why no derivation names it
check:objectui-bump lint.yml (pnpm check:objectui-bump) every path this file writes or reads
check:pm-clause2-carriers lint.yml (pnpm check:pm-clause2-carriers) this gate reads no file in the tree at all; its whole input is the GitHub API (PRs, their labels, and the claim comments on their cards), so no card's
check:release-body cut-rc.yml, lint.yml (pnpm check:release-body), release.yml check:release-body runs this renderer's --self-test against fixtures only; the workspace and CHANGELOG reads belong to the release run, which no pull

C. carries a repo-root walk — the ledgered class — 4

gate workflow step why no derivation names it
scripts/check-console-intercept-disarm.mjs --self-test · scripts/check-console-intercept-disarm.mjs lint.yml a walk CALLED on the repo-root binding as its whole argument; ROOT_WALK_RESIDUE_LEDGER row
check:org-identifier lint.yml (pnpm check:org-identifier) a git ls-files enumeration of the tracked corpus; ROOT_WALK_RESIDUE_LEDGER row
scripts/check-skill-frame-freshness.mjs --self-test lint.yml a walk whose DEFAULT PARAMETER is the repo-root binding; ROOT_WALK_RESIDUE_LEDGER row
scripts/symbol-anchors.mjs --self-test lint.yml a git ls-files enumeration of the tracked corpus; ROOT_WALK_RESIDUE_LEDGER row

D. --self-test-only family of a shared library — 6

gate workflow step why no derivation names it
scripts/import-prerequisite.mjs --self-test lint.yml CI runs only its self-test; its population is fixtures, and its honest declaration is the no-path marker
scripts/invoked-as.mjs --self-test half-state-patrol.yml, lint.yml CI runs only its self-test; its population is fixtures, and its honest declaration is the no-path marker
scripts/js-comment-mask.mjs --self-test lint.yml CI runs only its self-test; its population is fixtures, and its honest declaration is the no-path marker
scripts/partition-test-shards.mjs --self-test ci.yml, lint.yml CI runs only its self-test; its population is fixtures, and its honest declaration is the no-path marker
scripts/pm/git-history.mjs --self-test lint.yml CI runs only its self-test; its population is fixtures, and its honest declaration is the no-path marker
scripts/ts-parse.mjs --self-test lint.yml CI runs only its self-test; its population is fixtures, and its honest declaration is the no-path marker

E. Column-A parser artifact — 1

gate workflow step why no derivation names it
scripts/check-reference-carrier-shape.mjs lint.yml check-self-test-wired's INVOCATION_RE is anchored on scripts/, so it keys lint.yml's node packages/lint/scripts/check-reference-carrier-shape.mjs to a root path that does not exist. dispatch-gates does not discover the real invocation either — its DIRECT_CHECK_INVOCATION is anchored the same way.

F. names no path literal and carries no repo-root walk — the FOLLOW-UP class — 16

gate workflow step why no derivation names it
scripts/check-declaration-mirrors.mjs --self-test · scripts/check-declaration-mirrors.mjs lint.yml directory walk present (readdirSync), seeded from runtime literal(s) this scan could not read — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:init-service-contract lint.yml (pnpm check:init-service-contract) directory walk present (readdirSync), seeded from runtime literal(s) packages — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:live-db-isolation lint.yml (pnpm check:live-db-isolation) directory walk present (readdirSync), seeded from runtime literal(s) packages, apps, examples — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:meta-type-normalized lint.yml (pnpm check:meta-type-normalized) directory walk present (readdirSync), seeded from runtime literal(s) packages — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:optional-error-sink lint.yml (pnpm check:optional-error-sink) directory walk present (readdirSync), seeded from runtime literal(s) packages — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:resume-authority-declared lint.yml (pnpm check:resume-authority-declared) directory walk present (readdirSync), seeded from runtime literal(s) packages, examples — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:runner-env-posture lint.yml (pnpm check:runner-env-posture) directory walk present (readdirSync), seeded from runtime literal(s) packages, apps, examples — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
scripts/check-sdui-manifest.mjs --self-test · scripts/check-sdui-manifest.mjs lint.yml no directory walk in its source; its population is unread and needs a per-gate reading before any declaration can be written
check:settings-bind-window lint.yml (pnpm check:settings-bind-window) directory walk present (readdirSync), seeded from runtime literal(s) packages — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:startup-registry-verdict lint.yml (pnpm check:startup-registry-verdict) directory walk present (readdirSync), seeded from runtime literal(s) packages — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:tenant-chokepoint lint.yml (pnpm check:tenant-chokepoint) no directory walk in its source; its population is unread and needs a per-gate reading before any declaration can be written
check:verify-stand-in lint.yml (pnpm check:verify-stand-in) directory walk present (readdirSync), seeded from runtime literal(s) packages, examples — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:wildcard-fallthrough lint.yml (pnpm check:wildcard-fallthrough) directory walk present (readdirSync), seeded from runtime literal(s) packages — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:publish-smoke-pin lint.yml (pnpm check:publish-smoke-pin) no directory walk in its source; its population is unread and needs a per-gate reading before any declaration can be written
check:stall-guard ci.yml, coverage-nightly.yml, lint.yml (pnpm check:stall-guard), rerun-safety-nightly.yml directory walk present (readdirSync), seeded from runtime literal(s) this scan could not read — remedy is the ordinary ROOT_DIR_WATCH_HINTS subtree declaration
check:docs-image-tag-sync cut-rc.yml (pnpm version), lint.yml (pnpm check:docs-image-tag-sync), release.yml (pnpm check:docs-image-tag-sync), release.yml (pnpm version) no directory walk in its source; its population is unread and needs a per-gate reading before any declaration can be written

TOTAL RESIDUE: 43

2. THE FIX for the class the card names

scripts/check-driver-memory-census.mjs joins the existing "declared WHOLE-TREE and named on every card" bucket, in the shape the bucket already uses — the dispatch-gates: whole-tree-population marker, with a required reason, backed by the published liveness predicate reading its git ls-files enumeration.

The declaration is truthful and was read rather than assumed: candidateFiles() enumerates every tracked *.ts, *.tsx, *.mts, *.cts and every package.json in the repository, with a repo-root walk as the non-git fallback. No subtree narrows it. This is the same shape as two families already in the bucket (check:watch-hint-literal and check-comment-mask-corpus are type-filtered whole-tree walks, not literally every byte).

The always-runs bucket goes 6 to 7, and its liveness row prints beside it:

- pnpm check:driver-memory-census   [lint.yml]   declared whole-tree population — `candidateFiles` is a
  `git ls-files` enumeration of every tracked *.ts/*.tsx/*.mts/*.cts and every package.json in the
  repository (repo-root walk as the non-git fallback), so no card's file surface can narrow this gate ...
      ↳ liveness: its own source carries a `git ls-files` enumeration of the tracked corpus

Residue gates that are NOT whole-tree are not widened into the bucket. Four carry a recognised repo-root walk and are still not whole-tree; each gets a justified ROOT_WALK_RESIDUE_LEDGER row saying what it reads instead (group C above). The 16 in group F need a per-gate reading the card did not make, and are filed as a follow-up rather than guessed at.

3. THE REGRESSION GUARD, computed

ROOT_WALK_RESIDUE_LEDGER plus a live --self-test case, following the COMPOUND_ANCHOR_LEDGER convention in the same file (a literal table, a maintenance docblock, and a live case that reds on both an unlisted member and a stale row).

The case asserts: every family whose own source carries a recognised repo-root walk must declare whole-tree, declare path-less, be derivable by path, or hold a justified exclusion row. Two controls make it an instrument rather than a sweep that quietly finds nothing — the population must be non-empty, and the card's own specimen must be seen to have left it through the bucket and not through the ledger.

RED on the pre-fix residue, proven by ablation (mutation confirmed on disk, restore proven by matching blob hash and an empty git diff HEAD):

MUTATION ON DISK: marker lines before=1 after=0
 scripts/check-driver-memory-census.mjs | 1 -
✗ dispatch-gates self-test: 2 of 1392 case(s) failed.
✗ every gate CI runs whose own source sweeps the repo root is DECLARED whole-tree, declared
  path-less, derivable by path, or a justified row in ROOT_WALK_RESIDUE_LEDGER
  — unlisted: check:driver-memory-census
✓ control: the residue population is non-empty (6 member(s)), so the case above is measuring something
✗ control: the card's own specimen has left this population through the BUCKET, not through the ledger
RESTORE: head_blob=93092caa1a6ce9374400d8a188ef50ee010710db now_blob=93092caa1a6ce9374400d8a188ef50ee010710db
RESTORE PROVEN: git diff HEAD is empty for the target

GREEN after: ✓ dispatch-gates self-test: 1392 cases pass. No existing case is weakened or deleted — the diff is additive only (0 deletions in dispatch-gates.mjs), and the ablated run registered the same 1392 cases with 2 failing.

The card's scenario, reproduced as a clean A/B

A test file under packages/** carrying vi.mock('@objectstack/driver-memory', …), derived on both trees with the identical single-path card so the gate's own identity key cannot contaminate either side:

A: base tree (origin/main)      36 commands · check:driver-memory-census hits = 0
B: this branch, same card       37 commands · check:driver-memory-census hits = 1
set difference: 20a21
> pnpm check:driver-memory-census

Exactly one row moves. On the base tree the same file as a scratch commit under --changed --commands derives 36 commands with the census gate absent.

Verification

Union run at c1c5f96bb, the final commit.

  • node scripts/pm/dispatch-gates.mjs --self-test✓ dispatch-gates self-test: 1392 cases pass. (exit 0, foreground under scripts/pm/os-verify-lock.sh)
  • Derived family, --changed --commands --repo objectstack-ai/objectstack — 23 commands, every one run, all exit 0. ✓ dispatch-gates --ran: 23 derived famil(ies) accounted for — 23 run, 0 NOT-MEASURED.
  • ✓ check:declared-population-live — 201 of 252 famil(ies) declare a path population, and every one of them reaches this tree's 7442 tracked file(s).
  • check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states "#6664 census: 2 ruled consumers".
  • ✓ check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner would parse as a workflow command. plus its --self-test (23 cases, every battery at or above its pinned floor)
  • check:nul-bytes, check:watch-hint-literal, check:ratchet-remedy-authority, check-self-test-wired.mjs (+ --self-test) — exit 0
  • Whole-repo pnpm lint (eslint . --no-inline-config) — exit 0, no findings. Not narrowed.

skip-changeset: scripts/** only, nothing published from any released package.

Follow-ups filed

The residue beyond the card's class is filed rather than swallowed, so the boundary is a card and not an omission. Neither is addressed here; both stay open.

🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

…he repo root

`check:driver-memory-census` counts a `vi.mock` of a frozen driver package as a
module binding wherever in the tree it is written, and names no path literal in
its source — so the dispatch derivation scored it `undetermined` for every card
and no `--commands` harvest could contain it. A seat derived its family, ran 57
of them with 53 green, and CI's lint job then failed on the one gate the
derivation could not offer.

The gate's truthful population is the whole repository, so it now says so with
the `whole-tree-population` marker the bucket already uses, vouched by the
liveness predicate that reads its `git ls-files` enumeration.

Fixing one gate is not the fix. `ROOT_WALK_RESIDUE_LEDGER` and its live
`--self-test` case make the class computed: every family whose own source
carries a recognised repo-root walk must declare whole-tree, declare path-less,
be derivable by path, or hold a justified exclusion row. The next unnamed census
gate reds in `check:pm-dispatch-gates` instead of in CI a cycle later.

The wider residue was measured rather than guessed: of the 186 gate scripts CI
runs, 44 are named by no derivation. The remainder are subtree walkers whose
remedy is the ordinary ROOT_DIR_WATCH_HINTS declaration, one per-gate judgement
each; that class is filed separately rather than swallowed by a forty-row table
nobody would revisit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 4, 2026
@github-actions github-actions Bot added the size/m label Sep 4, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 4, 2026 12:47
@baozhoutao
baozhoutao enabled auto-merge September 4, 2026 12:47
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit 27875fc Sep 4, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-15312-census-gates-derivation branch September 4, 2026 13:21
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

Development

Successfully merging this pull request may close these issues.

finding(pm): dispatch-gates does not derive check:driver-memory-census for a change that trips it — the local family went green and CI found the red

2 participants