Commit 2dc5889
fix(pm): the clause-② path limb and SUSPECT_TIER_GLOBS except spec test files (#19948)
Fixes #19936
Clause-②: no
The clause-② enqueue gate's path limb read every file under
`packages/spec/src`, while the contract-review rule owes an at-tier
review for non-test files there only. For a test-only spec PR from an
off-tier seat, the two rules gave opposite answers, so the PR could
never enqueue. The ruling on the card (comment 5805897677, letter A)
settles it toward the review rule: the path limb and
`SUSPECT_TIER_GLOBS` except test files, using the repo's own test-file
predicate. `contract-review.md` is unchanged.
## What changed
### `scripts/pm/dispatch-gates.mjs`
- **`SUSPECT_TIER_GLOBS`**: the `packages/spec/src/**` entry gains
`except: isTestPath` and an `exceptWhy` reason. The glob and its `why`
are byte-identical, so `check-widening-tells.mjs`, which builds its
contract surface from `g.glob` and `g.why`, sees no change.
- **`deriveTier`**: a suspect glob's `except` is applied per path,
before the suspicion is recorded. A mixed diff keeps the suspicion on
its contract files. `MANDATORY_TIER_GLOBS` has no exception and its
semantics are unchanged.
- **The predicate is imported, not respelled**: `isTestPath` from
`scripts/check-undeclared-dep-imports.mjs`. The docblock above the table
says why this one was chosen (see the table below).
- **Docs**: a new docblock section on `SUSPECT_TIER_GLOBS`, one sentence
in the clause-② header paragraph, and one on `deriveTier`.
- **Self-test**: 20 new cases, listed under Verification. One existing
census row moved from `dispatch-gates.mjs:708` to `:713`, because the
import shifted that file's own `inherited-population` declaration by
five lines. That census names each declaration by file and line on
purpose.
### `.claude/skills/pm-dispatch/references/landing-operations.md` line
10
The line is rewritten in place and no line is added.
```diff
-- 路径肢 = diff 触及契约面 `packages/spec/src/**`,含 error-code-ledger 与 `*.zod.ts` 契约 schema。
+- 路径肢 = diff 触及契约面 `packages/spec/src/**` 非测试,含 error-code-ledger、`*.zod.ts` 契约 schema。
```
The line goes from 111 to 119 bytes.
`非测试` is the exact word `contract-review.md` line 9 uses. To stay under
the 120-byte line cap, 与 became 、, with no change in meaning. The file
is still 101 lines, at its ceiling of 101.
## Which test-file predicate, and why (measured)
The ruling says to take "the repo's own test-file predicate, not a new
spelling". The repo has several. Each was run over five shape probes and
over the 1541 tracked files under `packages/spec/src` on this branch:
| predicate | shape probes excepted | tracked files excepted |
`*.zod.ts` excepted |
|---|---|---|---|
| `check-undeclared-dep-imports.mjs` `isTestPath` (exported) ⭐ | 5 of 5
| 499 | 0 |
| `check-position-name-fold-loaders.mjs` `isTestFile` (exported) | 5 of
5 | 499 | 0 |
| `check-examples-live-imports.mjs` (not exported) | 5 of 5 | 499 | 0 |
| `check-dispatcher-error-vocabulary.mjs` `isTestFile` (not exported;
also bare `fixtures/` anywhere) | 5 of 5 | 499 | 0 |
| `check-runner-env-posture.mjs` `isTestFile` (also `.bench.`, `.pin.`,
bare `fixtures/`) | 5 of 5 | 500 | 0 |
| `check-adr-0087-registration.mjs` `TEST_FILE_RE` (filename only) | 2
of 5 | 500 | 0 |
| this file's own `isTestFilePath` (filename only, deliberately) | 2 of
5 | 499 | 0 |
| `tenant-audit-census.mjs` / `isystem-census.mjs` `isTestPath` (treats
`qa/` as a test dir) | 4 of 5 (misses `test/`) | 501 | **1**
(`qa/testing.zod.ts`) |
The five shape probes cover the ruling's four shapes: `stack.test.ts`,
`type-alias-convention.pin.test.ts`, a helper under `__tests__/`, a
fixture under `__tests__/`, and a fixture under `test/`.
`isTestPath` was chosen because its gate asks the ruling's exact
question: which files under a package's `src/` are published source, and
which are its tests. It is exported and it covers all four shapes. Its
only test directories are `test/`, `tests/`, `__tests__/` and
`__fixtures__/`, none of which is a contract domain under
`packages/spec/src`. A predicate that also treats `qa/` as a test
directory would drop a real contract:
`packages/spec/src/qa/testing.zod.ts` is a real contract schema, and it
stays a SUSPECT. That case is pinned.
## The lit case (ruling item 3)
`node scripts/pm/dispatch-gates.mjs --tier --repo
objectstack-ai/objectstack
packages/spec/src/type-alias-convention.pin.test.ts` (the one file PR
#19932 changes):
- base `43460b95`: prints the SUSPECT block, and its path row reads `-
packages/spec/src/type-alias-convention.pin.test.ts ⇢
'packages/spec/src/**' — the contract surface (error-code ledger,
*.zod.ts contract schemas) — the normal landing zone of a clause-②
card`.
- head `ef4d57c0`: no line contains `SUSPECT`. It prints `Model tier —
no path-derived mandate: the surface hits none of the 3 declared glob(s)
…` and the standing clause-② note (`Clause ② is NOT reachable from
paths: … This line is a FLOOR, never a clearance.`).
Same command on `packages/spec/src/ui/view.zod.ts` at head `ef4d57c0`:
the SUSPECT block still prints, and its path row reads `-
packages/spec/src/ui/view.zod.ts ⇢ 'packages/spec/src/**' — the contract
surface …`. The block's header line names the contract-review tier's
model id, so it is not quoted here.
## Verification (at HEAD `ef4d57c0`)
The gates came from `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`, run with no paths (36 commands).
Five more came from the dispatch-time list. Each exit code was captured
before any pipe. The `--ran` result: `✓ dispatch-gates --ran: 36 derived
famil(ies) accounted for — 36 run, 0 NOT-MEASURED`.
| gate | exit | verdict line |
|---|---|---|
| `pnpm check:pm-dispatch-gates` | 0 | `✓ dispatch-gates self-test: 1925
cases pass.` (831 s, run detached as its header prescribes) |
| `pnpm check:pm-skill-ratchet` | 0 | `landing-operations.md is 101
lines (ceiling 101; headroom 0)` |
| `pnpm check:pm-skill-id-lint` | 0 | `34 file(s) clean` |
| `node scripts/check-scripts-symbol-anchors.mjs` | 0 | `3668 anchors
across 281 scripts resolve` |
| `node scripts/check-self-test-wired.mjs` | 0 | `every one of the 229
script(s) CI runs that ship a --self-test has that self-test run by CI`
|
| `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 |
`279 cases pass` |
| `node scripts/check-skills-token-ratchet.mjs` | 0 | `54 authored
bundle file(s) within their ceilings` |
| `pnpm check:pm-widening-tells` (not derived; it imports this table) |
0 | `525 cases pass` |
| `pnpm --filter @objectstack/lint run check:doc-formula-expressions` |
0 | The first run exited 3 (PREREQUISITE NOT MET: `formula` and `lint`
were not built). It exited 0 after `turbo run build` of those two
packages under the verify lock. |
| `pnpm check:nul-bytes` | 0 | `scanned 9353 text file(s) … no raw ASCII
control bytes` |
| the other 30 derived or listed commands | 0 | green |
The 20 new self-test cases:
- The lit case raises no suspicion. Its rendering has no SUSPECT line
and still carries the clause-② note.
- A `*.zod.ts` path is still a suspect.
- Five shape cases are each excepted: a plain test, a pin test, a
`__tests__/` helper, a fixture under `__tests__/`, and a fixture under
`test/`.
- A mixed diff keeps its contract file.
- `qa/testing.zod.ts` stays a suspect while `qa/testing.test.ts` is
excepted.
- A directory surface that contains the contract surface is still a
suspect.
- A test-named file under a mandated root keeps its mandate.
- The exception is the imported function, and its home is a gate script,
so a card editing it derives this gate by gate-script identity.
- Every excepting entry carries a reason.
- Live: none of the 203 tracked `*.zod.ts` files is excepted, and the
exception really drops tracked files.
- End to end on the CLI: `--tier` on the lit case prints no SUSPECT
line, and `--tier` on `view.zod.ts` does.
**Ablation**, from committed state:
- Mutation: `deriveTier`'s guard `if (hintCovers(g.glob, p) && !(typeof
g.except === 'function' && g.except(p))) {` became `if
(hintCovers(g.glob, p)) {`. It was applied through
`scripts/ablation-replace.mjs` in WRAP mode, and the file hashes show it
landed: anchor 1 → 0, replacement 0 → 1, blob `caf66526bc7d` →
`f2f8a50e2b5e`.
- Predicted before the run: 11 red. Observed: `✗ dispatch-gates
self-test: 11 of 1925 case(s) failed.`, the predicted 11 exactly. They
are the two lit-case cases, the five shape cases, the mixed diff, the
`qa/testing.test.ts` case, the live non-vacuity case, and the CLI lit
case.
- The zod, `qa/testing.zod.ts`, ancestor, mandate, provenance and
live-zod cases stayed green, as they should with no exception applied.
- Restore: blob after restore `caf66526bc7d` equals HEAD, `git diff
HEAD` is empty, and `git status --porcelain` is empty.
**eslint, narrowed** to the one changed `.mjs` file:
- Population: `eslint --print-config` shows 2 rules
(`no-restricted-imports`,
`comment-swallow/no-code-inside-block-comment`), both per-file, and no
`parserOptions.project`.
- Count: `--format json` reports 1 file, 0 errors, 0 warnings.
- Invariance: no type-aware linting is enabled, so this diff cannot move
any untouched file's verdict. The `.md` file is not linted.
NOT MEASURED: `pnpm check:published-readme-exports`, exit 3. It needs a
full `pnpm build` of the workspace, which was not run. It is an
artifact-roster family printed for every card. This diff touches no
package, no package README and no type surface.
## Acceptance notes
- `check-widening-tells.mjs`, the mechanical half of the declaration
limb, imports `SUSPECT_TIER_GLOBS` and `hintCovers` from this file. It
already excludes tests with its own filename-only predicate
(`isContractSourceFile`). A `__tests__/` helper under
`packages/spec/src` would still be read there for tells. There are none
on the tree today. That is outside this card's surface and was not
changed.
- A derivation gap that predates this PR: `dispatch-gates.mjs --commands
scripts/pm/dispatch-gates.mjs` does not name `pnpm
check:pm-widening-tells`, although that gate imports from this file. The
same holds for its other two imports (`scripts/regen-artifacts.mjs`,
`scripts/pm/check-half-states.mjs`). The documented remedy is a module
constant in the importing gate, the `SURFACE_MODULE` shape in
`check-dispatch-gates.mjs`. The gate was run here anyway. Carrier: none.
- `packages/spec/src/benchmark.bench.ts` stays a SUSPECT. Neither the
chosen predicate nor the ruling's four shapes name bench files.
- The ruling's `__tests__/**` shape would also except a `*.zod.ts`
placed under a test directory. The package's `files[]` entry for `src`
zod files would still ship such a file. None exists today, and the new
live pin reds the day one does.
## 维护者速读(草稿)
**改了什么**:入队闸门的「路径肢」(`landing-operations.md` 第 10 行)和
`dispatch-gates.mjs` 的嫌疑表 `SUSPECT_TIER_GLOBS`,现在都把
`packages/spec/src/**`
下的测试文件排除在外。排除用的是仓里现成的测试文件判据(`check-undeclared-dep-imports.mjs` 的
`isTestPath`),直接 import,没有另写一份。`--tier` 对只改 spec 测试的 diff 不再打印 SUSPECT
行;改 `*.zod.ts` 的照旧打印。`contract-review.md` 未动。
**为什么改**:按裁决 A(评论 5805897677,维护者「同意」)。两条规则原先对「只改 spec 测试文件的
PR」互相矛盾:一条要求先有达档复核记录,另一条又说不许为它另起复核。结果出自不达档席位的这类 PR 永远进不了队列,眼下卡住的是 PR
#19932。测试文件不随包发布,改它不改变任何对外契约。
**风险与代价(含回滚)**:这道门放宽了,但只放宽到「测试文件」。另有两处自测直接对着真实文件树兜底:仓里 203 个 `*.zod.ts`
一个都不许被排除;`qa/testing.zod.ts` 这种名字像测试、实为契约的文件必须照旧打嫌疑行。消融实测:去掉排除,恰好预期的 11
条自测变红。强制档表(MANDATORY)没有任何排除。回滚:revert 本 PR 即可,无数据迁移。
**席位意见**:
**你要做的**:无需操作。本 PR 属 Tier S(`.claude/**` + `scripts/pm/**`),由 skills
席位完成达档复核后入队;PR #19932 在本 PR 落地后入队。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01A22sUB3mUWs6M36VgfijBq)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 9a0c0b5 commit 2dc5889
2 files changed
Lines changed: 108 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
464 | 464 | | |
465 | 465 | | |
466 | 466 | | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
467 | 472 | | |
468 | 473 | | |
469 | 474 | | |
| |||
12347 | 12352 | | |
12348 | 12353 | | |
12349 | 12354 | | |
12350 | | - | |
12351 | | - | |
12352 | | - | |
| 12355 | + | |
| 12356 | + | |
| 12357 | + | |
| 12358 | + | |
12353 | 12359 | | |
12354 | 12360 | | |
12355 | 12361 | | |
| |||
12476 | 12482 | | |
12477 | 12483 | | |
12478 | 12484 | | |
| 12485 | + | |
| 12486 | + | |
| 12487 | + | |
| 12488 | + | |
| 12489 | + | |
| 12490 | + | |
| 12491 | + | |
| 12492 | + | |
| 12493 | + | |
| 12494 | + | |
| 12495 | + | |
| 12496 | + | |
| 12497 | + | |
| 12498 | + | |
| 12499 | + | |
| 12500 | + | |
| 12501 | + | |
| 12502 | + | |
| 12503 | + | |
| 12504 | + | |
| 12505 | + | |
| 12506 | + | |
| 12507 | + | |
| 12508 | + | |
| 12509 | + | |
| 12510 | + | |
| 12511 | + | |
| 12512 | + | |
| 12513 | + | |
| 12514 | + | |
| 12515 | + | |
| 12516 | + | |
| 12517 | + | |
| 12518 | + | |
| 12519 | + | |
12479 | 12520 | | |
12480 | 12521 | | |
12481 | 12522 | | |
12482 | 12523 | | |
12483 | 12524 | | |
| 12525 | + | |
| 12526 | + | |
12484 | 12527 | | |
12485 | 12528 | | |
12486 | 12529 | | |
| |||
12548 | 12591 | | |
12549 | 12592 | | |
12550 | 12593 | | |
| 12594 | + | |
| 12595 | + | |
| 12596 | + | |
| 12597 | + | |
12551 | 12598 | | |
12552 | 12599 | | |
12553 | 12600 | | |
| |||
12557 | 12604 | | |
12558 | 12605 | | |
12559 | 12606 | | |
12560 | | - | |
| 12607 | + | |
| 12608 | + | |
| 12609 | + | |
12561 | 12610 | | |
12562 | 12611 | | |
12563 | 12612 | | |
| |||
22895 | 22944 | | |
22896 | 22945 | | |
22897 | 22946 | | |
22898 | | - | |
| 22947 | + | |
22899 | 22948 | | |
22900 | 22949 | | |
22901 | 22950 | | |
| |||
25570 | 25619 | | |
25571 | 25620 | | |
25572 | 25621 | | |
| 25622 | + | |
| 25623 | + | |
| 25624 | + | |
| 25625 | + | |
| 25626 | + | |
| 25627 | + | |
| 25628 | + | |
| 25629 | + | |
| 25630 | + | |
| 25631 | + | |
| 25632 | + | |
| 25633 | + | |
| 25634 | + | |
| 25635 | + | |
| 25636 | + | |
| 25637 | + | |
| 25638 | + | |
| 25639 | + | |
| 25640 | + | |
| 25641 | + | |
| 25642 | + | |
| 25643 | + | |
| 25644 | + | |
| 25645 | + | |
| 25646 | + | |
| 25647 | + | |
| 25648 | + | |
| 25649 | + | |
| 25650 | + | |
| 25651 | + | |
| 25652 | + | |
| 25653 | + | |
| 25654 | + | |
| 25655 | + | |
| 25656 | + | |
| 25657 | + | |
| 25658 | + | |
| 25659 | + | |
| 25660 | + | |
| 25661 | + | |
| 25662 | + | |
| 25663 | + | |
| 25664 | + | |
| 25665 | + | |
| 25666 | + | |
25573 | 25667 | | |
25574 | 25668 | | |
25575 | 25669 | | |
| |||
26507 | 26601 | | |
26508 | 26602 | | |
26509 | 26603 | | |
| 26604 | + | |
| 26605 | + | |
| 26606 | + | |
| 26607 | + | |
| 26608 | + | |
| 26609 | + | |
| 26610 | + | |
| 26611 | + | |
26510 | 26612 | | |
26511 | 26613 | | |
26512 | 26614 | | |
| |||
0 commit comments