Repository navigation
Commit 7338efe
fix(pm): dispatch-gates runs the diff-scoped issue-citation verdict locally, off the checker's own local-env declaration (#20314)
Fixes #20278
Clause-②: no
## What this changes
`dispatch-gates --commands` now lists the diff-scoped issue-citation
verdict, bare `node scripts/check-issue-citations.mjs`. That is the run
`Lint & Repo Gates` blocks on, and before this change the tool printed
it as `⊘ NOT MEASURED`, so a dev's local sweep could reconcile clean
while CI went red (measured on #20268's CI run). The census and every
other invocation of the same script keep exactly the classification they
had.
This implements option A of the seat ruling on the card (ruling comment
5858896081): two files, one declaration and one reader.
## Why the family was NOT MEASURED
The `&&` in `pnpm check:issue-citations && node
scripts/check-issue-citations.mjs` was already split into two families.
The cause is the **env carrier**: the lint.yml step passes
`GITHUB_TOKEN: ${{ github.token }}` and `OS_GATE_MERGE_GROUP_BASE_SHA:
${{ github.event.merge_group.base_sha }}` through `env:`.
`workflowEnvValues` treated both as values with none outside CI, so
`discoverFamilies` set `notRunnable` and `commandsFor` dropped the
family. The script needs neither on a pull-request run:
- The merge-group base renders empty on `pull_request`, and the checker
then uses `merge-base origin/main HEAD`.
- The token is used when present, and the diff mode reads the public
board with one probe per added number.
A rule about the variables alone cannot fix this. Dropping the job token
and the merge-group base generically was measured to also admit the same
script's `--census` (a report-only enumeration of the whole board) and
`check-required-contexts.mjs --verify-required-set` (exit 2 here). The
fact belongs to the program and to the mode, so the program declares it.
## The change (2 files)
1. **`scripts/check-issue-citations.mjs`**: one whole-line comment in
the existing `dispatch-gates:` marker idiom: `// dispatch-gates:
local-env GITHUB_TOKEN OS_GATE_MERGE_GROUP_BASE_SHA -- REASON`. The
reason states the file's own facts. It covers the bare invocation only;
the census needs the token and is not declared.
2. **`scripts/pm/dispatch-gates.mjs`**:
- `local-env` joins `PATH_LIST_MARKER_KEYS`, the LIST-then-reason
grammar. It gets the wholeness reading, the lookalike probe and the
census by construction. There is no new grammar copy, no table and no
gate.
- `declaredLocalEnv` reads the declaration. It refuses a cut reason and
any token that is not an env name.
- `localEnvRefusal` grades the declaration against the tree's workflows.
It throws, so the CLI answers `derivation failed` (exit 2), when either:
- a declared name is not passed by every step running the bare
invocation as an `env:` workflow value;
- no workflow runs the bare invocation.
- `localEnvAdmitted` spends the declaration on the bare key alone.
`--census`, `--self-test`, `check:*` keys and every other argv admit
nothing.
- `workflowEnvValues` gains one per-NAME limb: only the declared names
leave the list, so an undeclared name still keeps the family out.
- The matched row carries `localEnv`, in `--json` and in a same-line
note in the human block, so no rendering lists the command without
saying why.
- The docblock states what the reader does NOT do: guess; admit a name
the workflow does not pass; admit an argv no workflow runs; touch any
other invocation of the same script; repair an argv value.
- Self-test:
- Direction pins on the lint.yml step text: the bare command is in the
runnable union and not in the not-runnable set.
- The census invocation stays not-runnable on `GITHUB_TOKEN`.
- Controls: an undeclared script keeps the old reading, and a partial
declaration keeps the undeclared name.
- Refusals for a stale name, a name only some steps pass, and a missing
bare run.
- Live in-process pins on this tree for the `rls.zod.ts` specimen.
- The key roster and marker census moved from six to seven keys and from
seven to eight declarations, each row named.
- No absolute counts.
`package.json` is untouched. Renaming `check:issue-citations` was
optional in the triage notes and would not make the diff-scoped run
runnable: the derivation keys on what lint.yml runs, not on the alias
name.
## Evidence
**`--commands` for `packages/spec/src/security/rls.zod.ts`.** Before is
base `3f86dc52`, after is head `15ab041f`. Stdout gains exactly one
line; nothing else on stdout moves.
```text
before dispatch-gates --commands: 71 command(s) — 42 pnpm, 29 direct node (65 matched by path, ...)
before + 7 famil(ies) matched by path take a VALUE FROM THE WORKFLOW and are NOT above ...
before ⊘ NOT MEASURED — scripts/check-issue-citations.mjs --census
before ⊘ NOT MEASURED — scripts/check-issue-citations.mjs
after stdout line 12: node scripts/check-issue-citations.mjs
after dispatch-gates --commands: 72 command(s) — 42 pnpm, 30 direct node (66 matched by path, ...)
after + 6 famil(ies) matched by path take a VALUE FROM THE WORKFLOW and are NOT above ...
after ⊘ NOT MEASURED — scripts/check-issue-citations.mjs --census
```
The human block now renders the row as `- node
scripts/check-issue-citations.mjs [lint.yml] matched via
packages/spec/src/security/rls.zod.ts ⇢ gate source 'packages/**' · runs
here without GITHUB_TOKEN, OS_GATE_MERGE_GROUP_BASE_SHA: its script
declares local-env for this bare invocation, and CI passes them through
the step's env:`.
**The command it now lists answers the question here.** Measured in a
detached probe worktree at `3f86dc52` with an uncommitted edit, since
restored and removed:
- `#16608` re-added on `rls.zod.ts:426`: exit 2, `[allocated-but-absent]
packages/spec/src/security/rls.zod.ts:426 #16608`, the finding from
#20268's CI run.
- Control with a resolving number: exit 0.
- Control with `GITHUB_TOKEN`/`GH_TOKEN` unset: exit 0.
**Self-test, through the verify lock.**
- `pnpm check:pm-dispatch-gates` at `15ab041f`: `os-verify-lock: VERDICT
command-exit 0 · held the lock 975s (16m15s) · waited 0s · ⚠ SHARED-BOX
SECONDS`. Its log reads `✓ dispatch-gates self-test: 1976 cases pass.`
and `✓ check:pm-dispatch-gates --self-test: the exit contract holds in
all three directions.`
- An earlier `node scripts/pm/dispatch-gates.mjs --self-test` at
`23973549` also answered `VERDICT command-exit 0`, 1976 cases.
**Ablations.** Both legs used `scripts/ablation-replace.mjs`, which
checks the anchor hit and that the blob changed, then restores with blob
== HEAD and an empty `git diff HEAD`.
- Leg 1, declaration disabled (`// dispatch-gates: local-env
GITHUB_TOKEN` → `// ablated-marker: local-env GITHUB_TOKEN`, blob
`49bf630d` → `64ab1b63`): `--commands` for `rls.zod.ts` loses `node
scripts/check-issue-citations.mjs` from stdout (0 hits), and stderr
prints `⊘ NOT MEASURED — scripts/check-issue-citations.mjs` again.
Restored: blob == HEAD `49bf630d`.
- Leg 2, stale name planted (`... OS_GATE_MERGE_GROUP_BASE_SHA PR_BODY
--`): the CLI exits 2 with `derivation failed — dispatch-gates:
scripts/check-issue-citations.mjs:204 declares local-env PR_BODY, which
the step(s) running node scripts/check-issue-citations.mjs do not all
pass as a workflow value through env: ...`. Restored: blob == HEAD.
**Derived gate sweep for this diff.** Ran `node
scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` with no paths, 2 paths against merge base `3f86dc52`. It
derived 33 families, and each was run with its exit code captured before
any pipe. Reconciled at head `15ab041f`:
```text
Run reconciliation — 33 derived, 33 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 33 derived famil(ies) accounted for — 33 run, 0 NOT-MEASURED (a DERIVED zero — all 33 recorded an exit code and none of them is 3).
```
All 33 exit 0 at `15ab041f`, among them `node
scripts/check-issue-citations.mjs`, `pnpm check:issue-citations`, `pnpm
check:pm-dispatch-gates`, `pnpm check:watch-hint-literal`, `pnpm
check:nul-bytes` and `pnpm check:declared-population-live`. The first
sweep, at `23973549`, was red on one family: `pnpm
check:watch-hint-literal` exit 1, `every live declaration is a literal
-- scripts/pm/dispatch-gates.mjs:ROOT_DIR_WATCH_HINTS`. A self-test
fixture string used that rostered name. `15ab041f` renames the fixture
constant, and the whole sweep was then re-run on that head. `pnpm
check:pm-skill-id-lint` (named by the dispatch) also ran and exited 0:
`34 file(s) clean`.
**Lint, a narrowed run.** `eslint --no-inline-config --format json` on
the two changed files at `15ab041f` exited 0. The JSON reports 2 files,
0 errors and 0 warnings. The config `--print-config` reports for
`scripts/pm/dispatch-gates.mjs` carries no `parserOptions.project` and
no `projectService`, so linting is not type-aware and this diff cannot
move the verdict on any untouched file. Repo-wide `pnpm lint` is CI's.
**Changeset: `skip-changeset`.**
- Both files are in the repo-root package, which is `"private": true`
with no `files[]`.
- `git grep` for `declaredLocalEnv`, `localEnvRefusal` and the marker
over `packages/**` and `apps/**`: 0 hits.
- Positive control: `defineStack` hits `packages/spec/src/stack.zod.ts`,
which `@objectstack/spec`'s `files[]` ships (`src/**/*.zod.ts`).
**Line budget.**
- `scripts/pm/dispatch-gates.mjs`: 29507 → 29866 lines (1,746,809 →
1,768,400 bytes).
- `scripts/check-issue-citations.mjs`: 1213 → 1215 lines (65,982 →
66,904 bytes).
No gate added, no ceiling moved, no table added.
## Acceptance notes
- The census row, `node scripts/check-issue-citations.mjs --census`, is
still printed NOT MEASURED on every card in the checker's `packages/**`
population. Its only workflow, `half-state-patrol.yml`, is path-filtered
to the checker and patrol files, and the step is report-only. The ruling
records this as a note, and it is unchanged here.
- `check-required-contexts.mjs --verify-required-set` refuses here for a
proxy-route reason (exit 2, HTTP 401), while dispatch-gates names `env
GITHUB_TOKEN`. The ruling records this as a note, and it is unchanged
here.
- The census row's existing sibling note ("a DIFFERENT invocation of the
same script …") now names the bare diff run as that sibling. The wording
holds as written: a different argv asks another question.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01MjvgiFAmjHqsxy1XLiVYfH)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 4e0f72e commit 7338efe
2 files changed
Lines changed: 372 additions & 11 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
201 | 201 | | |
202 | 202 | | |
203 | 203 | | |
| 204 | + | |
| 205 | + | |
204 | 206 | | |
205 | 207 | | |
206 | 208 | | |
| |||
0 commit comments