fix(pm): the clause-② path limb and SUSPECT_TIER_GLOBS except spec test files - #19948
Merged
os-zhuang merged 3 commits intoSep 24, 2026
Merged
Conversation
…st files The enqueue gate's path limb read every file under packages/spec/src, while the contract-review rule owes an at-tier record for non-test files only, so a test-only spec PR from an off-tier seat could never enqueue. Per the ruling (letter A), SUSPECT_TIER_GLOBS' contract-surface entry now carries an `except` predicate — isTestPath, imported from check-undeclared-dep-imports.mjs, never respelled — that deriveTier applies per path before recording a suspicion; landing-operations.md's path limb reads 非测试 in place. Claude-Session: https://claude.ai/code/session_01A22sUB3mUWs6M36VgfijBq Co-authored-by: Claude <noreply@anthropic.com>
The lit case (a test-only spec diff) raises no suspicion and --tier prints no SUSPECT line; a *.zod.ts diff still does; each of the four ruled test shapes is excepted; a mixed diff keeps its contract file; qa/testing.zod.ts stays a suspect; the exception is the imported predicate and never drops a tracked *.zod.ts on this tree. Claude-Session: https://claude.ai/code/session_01A22sUB3mUWs6M36VgfijBq Co-authored-by: Claude <noreply@anthropic.com>
The isTestPath import above it shifted dispatch-gates.mjs's own inherited-population declaration from line 708 to 713; the census names each declaration by file:line on purpose, so the row moves with it. Claude-Session: https://claude.ai/code/session_01A22sUB3mUWs6M36VgfijBq Co-authored-by: Claude <noreply@anthropic.com>
Contributor
Author
Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
os-zhuang
marked this pull request as ready for review
September 24, 2026 03:42
os-zhuang
deleted the
claude/issue-19936-clause2-path-limb-excludes-tests
branch
September 24, 2026 04:04
This was referenced Sep 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 andSUSPECT_TIER_GLOBSexcept test files, using the repo's own test-file predicate.contract-review.mdis unchanged.What changed
scripts/pm/dispatch-gates.mjsSUSPECT_TIER_GLOBS: thepackages/spec/src/**entry gainsexcept: isTestPathand anexceptWhyreason. The glob and itswhyare byte-identical, socheck-widening-tells.mjs, which builds its contract surface fromg.globandg.why, sees no change.deriveTier: a suspect glob'sexceptis applied per path, before the suspicion is recorded. A mixed diff keeps the suspicion on its contract files.MANDATORY_TIER_GLOBShas no exception and its semantics are unchanged.isTestPathfromscripts/check-undeclared-dep-imports.mjs. The docblock above the table says why this one was chosen (see the table below).SUSPECT_TIER_GLOBS, one sentence in the clause-② header paragraph, and one onderiveTier.dispatch-gates.mjs:708to:713, because the import shifted that file's owninherited-populationdeclaration by five lines. That census names each declaration by file and line on purpose..claude/skills/pm-dispatch/references/landing-operations.mdline 10The line is rewritten in place and no line is added.
The line goes from 111 to 119 bytes.
非测试is the exact wordcontract-review.mdline 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/srcon this branch:*.zod.tsexceptedcheck-undeclared-dep-imports.mjsisTestPath(exported) ⭐check-position-name-fold-loaders.mjsisTestFile(exported)check-examples-live-imports.mjs(not exported)check-dispatcher-error-vocabulary.mjsisTestFile(not exported; also barefixtures/anywhere)check-runner-env-posture.mjsisTestFile(also.bench.,.pin., barefixtures/)check-adr-0087-registration.mjsTEST_FILE_RE(filename only)isTestFilePath(filename only, deliberately)tenant-audit-census.mjs/isystem-census.mjsisTestPath(treatsqa/as a test dir)test/)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 undertest/.isTestPathwas chosen because its gate asks the ruling's exact question: which files under a package'ssrc/are published source, and which are its tests. It is exported and it covers all four shapes. Its only test directories aretest/,tests/,__tests__/and__fixtures__/, none of which is a contract domain underpackages/spec/src. A predicate that also treatsqa/as a test directory would drop a real contract:packages/spec/src/qa/testing.zod.tsis 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):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.ef4d57c0: no line containsSUSPECT. It printsModel 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.tsat headef4d57c0: 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--ranresult:✓ dispatch-gates --ran: 36 derived famil(ies) accounted for — 36 run, 0 NOT-MEASURED.pnpm check:pm-dispatch-gates✓ dispatch-gates self-test: 1925 cases pass.(831 s, run detached as its header prescribes)pnpm check:pm-skill-ratchetlanding-operations.md is 101 lines (ceiling 101; headroom 0)pnpm check:pm-skill-id-lint34 file(s) cleannode scripts/check-scripts-symbol-anchors.mjs3668 anchors across 281 scripts resolvenode scripts/check-self-test-wired.mjsevery one of the 229 script(s) CI runs that ship a --self-test has that self-test run by CInode scripts/pm/check-governed-queue-guard.mjs --self-test279 cases passnode scripts/check-skills-token-ratchet.mjs54 authored bundle file(s) within their ceilingspnpm check:pm-widening-tells(not derived; it imports this table)525 cases passpnpm --filter @objectstack/lint run check:doc-formula-expressionsformulaandlintwere not built). It exited 0 afterturbo run buildof those two packages under the verify lock.pnpm check:nul-bytesscanned 9353 text file(s) … no raw ASCII control bytesThe 20 new self-test cases:
*.zod.tspath is still a suspect.__tests__/helper, a fixture under__tests__/, and a fixture undertest/.qa/testing.zod.tsstays a suspect whileqa/testing.test.tsis excepted.*.zod.tsfiles is excepted, and the exception really drops tracked files.--tieron the lit case prints no SUSPECT line, and--tieronview.zod.tsdoes.Ablation, from committed state:
deriveTier's guardif (hintCovers(g.glob, p) && !(typeof g.except === 'function' && g.except(p))) {becameif (hintCovers(g.glob, p)) {. It was applied throughscripts/ablation-replace.mjsin WRAP mode, and the file hashes show it landed: anchor 1 → 0, replacement 0 → 1, blobcaf66526bc7d→f2f8a50e2b5e.✗ 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, theqa/testing.test.tscase, the live non-vacuity case, and the CLI lit case.qa/testing.zod.ts, ancestor, mandate, provenance and live-zod cases stayed green, as they should with no exception applied.caf66526bc7dequals HEAD,git diff HEADis empty, andgit status --porcelainis empty.eslint, narrowed to the one changed
.mjsfile:eslint --print-configshows 2 rules (no-restricted-imports,comment-swallow/no-code-inside-block-comment), both per-file, and noparserOptions.project.--format jsonreports 1 file, 0 errors, 0 warnings..mdfile is not linted.NOT MEASURED:
pnpm check:published-readme-exports, exit 3. It needs a fullpnpm buildof 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, importsSUSPECT_TIER_GLOBSandhintCoversfrom this file. It already excludes tests with its own filename-only predicate (isContractSourceFile). A__tests__/helper underpackages/spec/srcwould still be read there for tells. There are none on the tree today. That is outside this card's surface and was not changed.dispatch-gates.mjs --commands scripts/pm/dispatch-gates.mjsdoes not namepnpm 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, theSURFACE_MODULEshape incheck-dispatch-gates.mjs. The gate was run here anyway. Carrier: none.packages/spec/src/benchmark.bench.tsstays a SUSPECT. Neither the chosen predicate nor the ruling's four shapes name bench files.__tests__/**shape would also except a*.zod.tsplaced under a test directory. The package'sfiles[]entry forsrczod 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