fix(scripts): a bare-directory include is tsc's implicit glob, and --list annotates provenance too - #18708
fix(scripts): a bare-directory include is tsc's implicit glob, and --list annotates provenance too#18708os-litant wants to merge 2 commits into
include is tsc's implicit glob, and --list annotates provenance too#18708Conversation
…`--list` says `(via …)` too `check-type-source-resolution` mapped every `include` entry straight through `globToRegExp`, which anchors `^…$` and has no bare-directory branch. A tsconfig shipping `"include": ["src"]` therefore compiled to a pattern matching only a FILE named `src`, and that program's file set read as EMPTY — 22 of this repo's tsconfigs ship that spelling, which is legal TypeScript. An empty program is silent, not loud: it contributes no imports, so the package's exposure is attributed to whichever OTHER program did have files, and the `(via …)` provenance annotation then names a sibling config as the only route to a dep the build config reaches too. That annotation is the doc-block's own test for telling the re-baseline limb from the plain refusal, so a false one steers an author onto the limb the doc-block forbids. `expandImplicitGlob` reproduces tsc's own rule from `commandLineParser.ts`: `isImplicitGlob` tests the spec's LAST path component with `!/[.*?]/` and, when it holds, appends the recursive wildcard. The dot half is deliberate — tsc does not expand `src/index.ts`, and neither does this. Includes only; excludes keep reaching their directory case through `isExcluded`'s own bare-prefix branch. Second half, same subject: the doc-block says the annotation appears "in `--list` and in the failure text", and `--list` emitted zero of them. `withProvenance` moves to module scope and both surfaces call it, with the annotation riding in a trailing line comment so each row stays paste-able over the registry. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
`reported()` is a substring test, so `packages/bare-include` was satisfied by `packages/bare-include-twin`'s own failure row — the assertion that carries the finding could pass while the bare-directory package went silent. Measured: the first ablation leg (`expandImplicitGlob` reduced to identity) left that assertion GREEN and failed only on the twin-parity and scope cases. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
🔴 本 PR 的
|
|
指路,⛔ 不是关闭动作。 卡 #18373 已由维护者裁决:批 #153 第 3 项 · 字母 E —— 退役
⭐ 但本 PR 携带的测量不随之作废,裁决第 2 条也正是这么说的:裸目录 读数时刻 2026-09-18T06:31Z Generated by Claude Code |
⛔ 按裁决关闭 —— 批 #153 item 3 · letter E,本门禁整体退役
裁决
本席即该 claimant,据此关闭。⛔ 非因修复失败,⛔ 非因测量被推翻。 卡 #18373 已同笔改道 本 PR 留下的东西,以及它为什么支持「退役」而不是「re-baseline」修复本身是成立的并经消融证实:裸目录 ⭐ 而本 PR 最后一轮测出的东西,恰恰是裁决据以选 E 的那一条 —— 门禁自己印的
原因是结构性的:那 14 个失败程序全部是 两条绕路也已量过并关闭:给兄弟程序加 ⇒ 与裁决的评估同向且独立得出:登记册条目做不掉,所以它不是一份会缩小的棘轮。 ⛔ 本席的错,记在明处本席在裁决落下两小时后,仍按「修红」派了一轮到这条分支上 —— 因为本席没有在再次派发前重读卡与全部评论,理由是「这是已知 PR 的 CI 修复轮」。本席自己的检查清单第 7 条正是「每次派发前读卡和所有评论」。那一轮因此建立在一个已死的前提上;它产出的测量仍然有用(见上),但它本不该被派出去。
Generated by Claude Code |
Fixes #18373
Clause-②: no
Authored by Claude Code in session
session_01LvwGppdonww4zGLWZo5rho(https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho).What was wrong
scripts/check-type-source-resolution.mjsmapped every tsconfigincludeentry straight throughglobToRegExp, which anchors its pattern at both ends and carries no bare-directory branch. Atsconfig shipping
"include": ["src"]therefore compiled to a pattern that can only match a FILEnamed
src— neversrc/engine.ts— and that program's file set read as empty. 22 of thisrepo's 138 parseable tsconfigs ship that spelling, and it is legal TypeScript: tsc's
isImplicitGlobincommandLineParser.tstests the spec's LAST path component against a characterclass of dot, star and question mark, and when none of them is present it appends the recursive
wildcard before building the pattern.
An empty program is silent, not loud. It contributes no imports, so the package's exposure is
attributed to whichever OTHER program did have files — and the
(via ...)provenance annotationthen names a sibling config as the only route to a dep the build config reaches too. That
annotation is
KNOWN_DIST_RESOLVED_TYPE_IMPORTS's own test for telling the re-baseline limb fromthe plain refusal — "if you cannot point at the program, you do not have this case" — so a false one
steers an author onto the limb that doc-block forbids. It already did, once: the #17396 delivery
round was blocked by this gate on PR #18198 and the
(via tsconfig.test.json)it was given wasfalse.
Second symptom, same subject. The doc-block says the annotation appears "in
--listand in thefailure text".
--listemitted zero of them, becauseprintListprintedpkg.distResolvedrawwhile
withProvenancelived insidecheck().What changed
expandImplicitGlobreproduces tsc's own rule and runs on everyincludeentry before itbecomes a pattern. The dot half of the character class is deliberate and pinned: tsc does not
expand
src/index.ts, and neither does this. Includes ONLY — excludes keep reaching theirdirectory case through
isExcluded's existing bare-prefix branch, which already is tsc's excludebehaviour.
withProvenancemoves to module scope and both reporting surfaces call the oneimplementation;
printList's body becomeslistLines, a pure function the self-test can read.--listcarries the annotation in a trailing line comment so each row stays paste-able over theregistry, whose values are bare package names.
SELF_TEST_BATTERIESwith the rosterfloor raised 11 to 13. Four new fixtures: the bare-directory package, its explicit-wildcard twin,
a scope guard whose sibling directory imports a different artifact package, and a dotted-entry
guard.
Measured
All readings on this branch at
0f319b07ac, exit codes captured from a redirect and never througha pipe.
f8eaf67045)0f319b07ac)--list: programs / packages--list: registry entries--list: package-dependency pairs(viaoccurrences in--list--self-testThe zero in that
(viarow is a real absence, not a dead instrument: atf8eaf67045the self-testpasses, and one of its pinned cases is that the FAILURE text emits
(via tsconfig.test.json)on thefixture tree. Same script, same run, same token — one surface emitted it and the other did not.
The program set did not move. 135 programs before and after: this widening is a FILE-set
widening inside programs that already existed, which is a shape the registry's re-baseline limb has
never had — its stated test is "whether the SET OF PROGRAMS changed".
Nothing went stale in either direction — 0 entries removed, 0 entries narrowed. Every one of the
72 newly-visible pairs is a type verdict that has always been a function of build state, in a tsc
program CI has been running on every PR; the instrument could not see it.
The 14 failure rows, in full:
cloud-connection(5),observability(1),service-analytics(5),service-cache(3),service-cluster-redis(2),service-datasource(8),
service-job(4),service-package(3),service-queue(4),service-settings(5).cli(+22),service-messaging(+6),service-i18n(+2),service-realtime(+2).Reverse verification
Two legs, one variable each, run from the committed state, each proving the mutation reached disk
before reading any result, each restoring with
git checkout HEAD -- pathand proving restorationby blob hash rather than by an exit code. Both restored to
faafc530e164e188b4a9315557cec205ee9ab4eewith
git diff HEADnaming zero paths.expandImplicitGlobreduced to the identity it replaced.--self-testexit 1, namingfour cases, the first being:
"include": ["src"]was read as a literal, the program came backEMPTY, and the package went silent instead of red.
listLines' trailing annotation blanked.--self-testexit 1, naming both paritycases:
--listemitted no annotation, and the two reporting surfaces disagreed about the samedep.
Leg A also found a defect in the first draft of these tests and it is fixed in the second commit:
reported()is a substring test, sopackages/bare-includewas being satisfied bypackages/bare-include-twin's own row — the assertion carrying the finding could pass while thebare-directory package went silent. The fixtures are renamed to non-colliding names, after which
Leg A fails that assertion too.
Local verification
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ranreconciles 29derived families, 29 run, 0 NOT-MEASURED, 0 UNRUN, with an exit code recorded for every one.
28 of the 29 are green; the single red is
check:type-source-resolutionitself, which is thischange.
check:pm-dispatch-gateswas measured at395c041609rather than re-run at0f319b07ac: its self-test reads its own fixtures and the derived change set is the same singlepath, so the later commit cannot move it. Every other family was re-run at
0f319b07ac.pnpm lintis NOT run whole here; the narrowing isnpx eslint scripts/check-type-source-resolution.mjs --format json— 1 file linted, 0 errors, 0warnings, the count read from the JSON output. The narrowing is sound because
eslint.config.mjsstates in place that this repo "never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file", so a one-file diffthat does not touch the config cannot move any untouched file's verdict.
turbo ls --affectedisnot a narrowing instrument here: a repo-root
scripts/path is a global input, so it returns abroad set.
git grep -l check-type-source-resolutionover*.test.ts,*.test.mts,*.spec.tsand*.test.mjsreturns nothing, with two positive controls that dohit. This script's suite is its own
--self-test, extended and run above.This PR is NOT landable as it stands
check:type-source-resolutionruns insideLint and Repo Gates, one of the seven requiredcontexts, so merging this reds every PR in the repo until the 14 rows are settled. Settling them is
not this card — and every route is fenced for the executing seat:
pathsremediation is the gate's own first remedy and is 14 packages of workthe gate's header says lands as its own card; PR build(rest): give the package a test-layer tsc program and ledger its 37 errors #12570 measured that route billing other
packages' source diagnostics into the consuming package's ledger (37 to 42 errors).
KNOWN_DIST_RESOLVED_TYPE_IMPORTSis the registry doc-block's ownlimb and is what the readings above are shaped for — the paste-able after-state is one
--listaway. But that registry is shrink-only, its re-baseline test names the program set(which did not move here), and widening a ratchet is a maintainer floor.
--listhalf alone propagates the false annotation to a second surface insteadof repairing it, so it is strictly worse than the state it replaces.
Reported to the dispatching seat as
needs_decisionfor that reason, with the measurement above.Acceptance notes
private: true, and across 70 publishedpackages, zero
files[]entry namesscripts,.or a parent path (positive control:packages/specships 10files[]entries, so the field really is populated). Nothing herepublishes, so
skip-changesetapplies.check-governed-merges --test scripts/check-type-source-resolution.mjs: 0 of 1 path hits theregister (positive control:
--test AGENTS.mdhits, 1 surface). Not a governed surface.packages/cli/tsconfig.build.jsonalso ships the bare-directory spelling, butno
typecheckscript names it, so it stays outside the program population and this change doesnot reach it. Carrier: the next author to onboard a
tsconfig.build.jsonto atypecheckscript.reported()helper is a substring test over failure strings,so any two fixture names in a prefix relationship can satisfy each other's assertion. After this
change one such pair remains,
packages/specandpackages/spec-tools, and it is harmless todaybecause
packages/specis never areported()subject. Carrier: the next author to add afixture whose name extends an existing one.
Generated by Claude Code