Repository navigation
Description
Filing gate ③ + ④: pre-freeze prep card of the epic #25677 (pm:epic; epic PM session_01RSDL3SFtJaKzcymvCD8RsU). Residual of cutover decision F (#25763): the objectui seat measured that 7 of the 37 function-body-import findings check:test-source-alias reports under ui/ are not runtime loads at all, so #25763's "0 findings" acceptance cannot be met from the test-file side without adding module loads those files do not pay today. This card is the gate half. Part of #25677. Lands in objectstack-ai/objectstack main before the cutover freeze (#25752 step 2).
谁付代价(业务筛): after the cutover check:test-source-alias runs on ui/ too. On the merged layout (verify branch claude/project-thread-dwhq6r-25697-verify, head 9e37acd, no patch) it prints these 7 findings that no test-file edit can honestly clear:
| file | line | what the gate matched |
|---|---|---|
ui/packages/plugin-calendar/src/__tests__/spec-symbol-4650.test.ts |
58 | type _NewNameIsFree = import('@objectstack/spec/ui').X under a @ts-expect-error tripwire whose header says the inline type-level form is deliberate (a namespace import would bind the eslint-restricted FormField) |
ui/packages/plugin-form/src/__tests__/spec-symbol-4650.test.ts |
50 | same tripwire shape |
ui/packages/plugin-grid/src/__tests__/spec-symbol-4650.test.ts |
73 | same tripwire shape |
ui/packages/plugin-kanban/src/__tests__/spec-symbol-4650.test.ts |
50 | same tripwire shape |
ui/packages/types/src/__tests__/dashboard-global-filters-spec-binding.test.ts |
102 | type SpecGlobalFilter = import('@objectstack/spec/ui').GlobalFilter type alias |
ui/packages/components/src/renderers/complex/__tests__/data-table.test.ts |
66 | const testSchema: import('@object-ui/types').DataTableSchema = { type annotation |
ui/scripts/__tests__/check-spec-range-floors.test.ts |
286 | type X = import('@objectstack/spec/data').FeedFilterMode; as TEXT inside a template-literal fixture fed to the scanner under test |
Each is erased by tsc (or is string content) and resolves nothing at run time. Left as is, the cutover PR and every later ui/ PR is red on this gate for files that are correct, and the only "fix" available to a ui developer is a side-effect import that adds a real module load the file did not pay, and that in the four tripwires contradicts their stated design. The gate's own words (header, "typeof import(…) queries appear in neither: they never resolve") already claim the right behaviour; the implementation does not reach it.
维护者速读
objectstack 的 check:test-source-alias 把 type X = import('p').T、const x: import('p').T 和模板字符串里的文本当成函数体内的运行时 import(),合并布局下 ui/ 有 7 处误报。F 卡(#25763)修 30 处真实的;本卡修闸门,让这 7 处不再报。非治理面(scripts/),不需要 Tier H 审批。
Cause (read on 9e37acd, scripts/check-test-source-alias.mjs)
moduleLoadSitesrunsIMPORT_PATTERNSover thecommentsOnlyprojection (strings kept so the specifier can be read) and the only type-position guard isTYPE_QUERY_BEFORE = /\btypeof\s*$/tested oncodeOnlybefore the keyword. Atype Name =alias head and a:annotation head are not recognised; a match whose keyword sits inside masked string content (blank incodeOnly) is not rejected.
What to change (objectstack, one draft PR to main)
- In
moduleLoadSites, reject a dynamic match whencodeOnlyatheaddoes not spell the keyword (import/require) — that is the string-content case; the specifier can still be read fromcommentsOnlyexactly as today. - Recognise type positions in front of
import(: thetype Name<…>? =alias head (andexport type), and the annotation head (: import(…).Memberon a declaration or parameter). Keeptypeofas is. Design the annotation rule so a real{ key: import('p') }object-property load andconst p = import('p')are still counted — measurement wins; one safe discriminator is that a type query is always followed by.Identifierthat is NOT then called, while a runtimeimport()is awaited, assigned, or.then-chained. - Self-test: add fixtures for the three shapes (type alias, annotation, string content) as negatives, and keep a positive fixture for
{ key: import('p') }andconst p = import('p')in a function body. Remember check-test-source-alias loses a bare side-effectimport 'x';whenever any laterimport … from …follows it, so the clocked-window rule reports a file that already applied the fix it prescribes #12555's lesson: the clause scan must not swallow a neighbouring runtime import while erasing a type position. - ⛔ Do not add the 7 files to
KNOWN_UNALIASED_TEST_IMPORTSor any allowlist; the gate must read them correctly. ⛔ No edit to theui/files (they are cutover-prep(ui-merge): warm the function-bodyimport()of@objectstack/*/@object-ui/*in the ui test filescheck:test-source-aliasnames, so the merged tree's Test UI stops clocking out (parent #25677, decision F) #25763's and objectui's).
Acceptance
pnpm check:test-source-alias(self-test and run) green on objectstackmain.- On the merged layout (verify head 9e37acd + your diff applied at the root), the 7 findings above are gone and the remaining function-body findings under
ui/are exactly cutover-prep(ui-merge): warm the function-bodyimport()of@objectstack/*/@object-ui/*in the ui test filescheck:test-source-aliasnames, so the merged tree's Test UI stops clocking out (parent #25677, decision F) #25763's 30 real ones (or fewer once cutover-prep(ui-merge): warm the function-bodyimport()of@objectstack/*/@object-ui/*in the ui test filescheck:test-source-aliasnames, so the merged tree's Test UI stops clocking out (parent #25677, decision F) #25763 lands). Post the before/after counts on this card. - No change to the gate's reading of objectstack's own packages: the
KNOWN_UNALIASED_TEST_IMPORTSledger recompute reports no diff.
Size: S. Clause-②: no. Metadata-protocol: none. Responsibility: gate defect, pre-existing on main (not caused by a seat PR); not a defect card against a seat.
风险
- Over-broad type-position rules hide a real load; the positive fixtures in step 3 are the guard. If the discriminator cannot be made safe, keep only the
type … =and string-content rejections (6 of the 7) and report the annotation case as residual.
Dedupe
search_issues 2026-10-25 09:31Z 「check-test-source-alias import type query typeof annotation string literal false positive」: 7 hits, all closed (#16299 codeOnly-projection pin, #8020/#8108 alias-replacement reading, #8180 TYPE axis of the vitest alias, #9955 dispatch-gates naming, #12472, #17304); none covers type-position or string-content import(…) matches. New.
Activity
objectstack-fleet commentedon Oct 25, 2026
Claim: PM loop round 1
Session:
session_01Mj3sUeTnVuFsWPY37TxDmSAccount:
os-tesla(the seat's linked user; the card's assignee)Branch:
claude/project-thread-70gghuWorktree:
objectstack-issue-25766Domain:
domain:devxSeat:
domain:devx#1File surface:
scripts/check-test-source-alias.mjs and its self-test fixtures / scripts/__tests__ coverage of that gate(stop on breach; explain in the report)Container & model:
S,mode:thread,model: opus (default judgment tier),effort: xhighClause-②: no
Metadata-protocol: none
Responsibility: gate defect pre-existing on main; not a defect card against a seat
Thread-read: none
Premise: holds — 7 unique (file, line) false positives re-measured by the epic seat on 9e37acd
Serial constraints cleared: objectstack#25763 (F, objectui) owns the ui/ test files — this card never edits ui/**; no other open card touches scripts/check-test-source-alias.mjs
Resume-from: none
Dispatched by the epic seat (
session_01RSDL3SFtJaKzcymvCD8RsU) under the pm-dispatch frame; order:/mnt/project-files/d1-merge/dispatch/G-25766-test-source-alias-gate.md. Part of #25677.Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
os-dev-report
{
"issue": 25766,
"status": "done",
"branch": "claude/project-thread-70gghu",
"pr": "#25781",
"head": "85fe6b02be",
"session": "session_01Mj3sUeTnVuFsWPY37TxDmS",
"premise_still_valid": true,
"summary": "scripts/check-test-source-alias.mjs, clocked-window rule only (moduleLoadSites): a dynamic match counts only when its keyword is executed code (plain code in codeOnly, or a template interpolation interior read from scanSource's interpolation array), and an import(...) whose tail is an uncalled .Member chain (type arguments balanced, so a generic .then call stays a load) is a type reference, skipped for both the clocked list and the module-scope compliance set. require('p').member stays a load. Merged layout (9e37acd + this diff): unique clocked findings 37 -> 30, exactly the card's 7 gone, 0 new, the 42 non-clocked findings byte-identical. objectstack main: gate green, --list ledger recompute identical (60 of 76, 295 pairs). No ui/ file touched, nothing added to KNOWN_UNALIASED_TEST_IMPORTS or any allowlist.",
"tests": "At 85fe6b0: node scripts/check-test-source-alias.mjs --self-test exit 0 (new battery 'a type reference or literal text is not a load (#25766)' 21 cases, roster floor 10 -> 11); node scripts/check-test-source-alias.mjs exit 0 (76 scanned, 60 registered, 55 subpaths). Merged-layout before/after: 37 -> 30 unique (file:line) clocked findings; gone = spec-symbol-4650 x4 (calendar:58, form:50, grid:73, kanban:50), dashboard-global-filters-spec-binding:102, data-table:66, ui/scripts check-spec-range-floors:286; new = none. Corpus-wide classification dump of every tracked test file (no population filter), base vs fix: main 48 removals / 0 additions, merged 145 removals / 0 additions; every main removal read by hand (template-literal child scripts, code strings, erased annotations/casts/predicates). Self-test vacuity: 7 one-off mutants on scratch copies (relocation control = 12 failures, subtracted) each red new-battery rows; every erased row is reached by at least one mutant; the awaited and ?.then load rows are guards no mutant reached. Gate wall time 19.5 s -> 18.2 s.",
"gates": "dispatch-gates --commands at 85fe6b0 derived 33; all 33 exit 0 (exit captured before any pipe). 6 first exited 3 PREREQUISITE NOT MET (yaml, typescript, @typescript-eslint/parser) in the fresh worktree and exit 0 after pnpm install --frozen-lockfile: check-ci-filter-parity, check-closing-keyword-parity (+ --self-test), check-comment-mask-corpus, check:cli-command-ids, check:driver-memory-census. check:pm-dispatch-gates exit 0 (fast tier 1913 cases, 333 s). --ran reconciliation: 33 derived, 33 run, 0 NOT-MEASURED, 0 UNRUN (record: /mnt/project-files/d1-merge/out/G-25766-gates-ran.list). NOT MEASURED locally, left to CI: the two workflow-valued families (check-shard-attestation --emit, check-test-completeness) and the Test Core job steps. PR check-runs: in_progress at report time.",
"dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0 GitHub MCP calls (reads went through REST curl). 1 non-GitHub platform call: claude-code-remote subscribe_pr_activity on #25781.",
"api_writes": "2 relay writes: pr_create via scripts/pm/fleet-write/dispatch.mjs (run 40183973253, #25781, body 9586 bytes sent = stored, assignee os-tesla, labels skip-changeset), and this os-dev-report comment via scripts/pm/post-stamped.mjs. Plus 1 git push.",
"files_changed": [
"scripts/check-test-source-alias.mjs"
],
"line_budget": "+173 / -4",
"deviations": [
"Branch is the platform-designated thread branch claude/project-thread-70gghu (per FRAME), not claude/issue-25766-*.",
"Suggested route 2 (an alias-head regex) not taken: the tail discriminator (route 3) decides aliases, annotations, unions and as/satisfies targets alike, and the head cannot tell an annotation from an object-literal value. Route 1 refined: codeOnly also blanks template interpolations, so the executed-keyword test reads scanSource's interpolation array (the self-test caught the gap).",
"Erased sites are now also skipped for the module-scope compliance set (the header already said this for typeof). On the corpus this exposes no new finding on either tree.",
"Review not requested and no MCP assign/review call (FRAME); assignee came from pr_create.",
"Measurement against the order's section 2: the merged-layout report printed 57 clocked lines for 37 unique (file, line), so not every line appears twice."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: cutover adaptation commit (the vitest-alias half, out of scope by the card) · the ledger reader extractRuntimeImports still counts import(...).Member type references and string/template text as runtime imports, which is why such specifiers keep entering a package's measured set; after this PR no clocked finding is wrong because of it · noted in the PR's Acceptance notes",
"carrier: none · noted, not filed · a STATIC import spelled as text inside a string or template still counts as a module-scope compliance token (fail-open direction); scratch measurement: 67 such tokens on main, 141 on the merged layout, 0 of them pair with a clocked load of the same specifier in its file, so it hides nothing today · dedupe words: test-source-alias static import string literal compliance token"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
ACCEPT — dispatch seat. objectstack#25781 (head
85fe6b02be, branchclaude/project-thread-70gghu, +173/−4, 1 file) is accepted as the fix for this card.Checked against the order (
/mnt/project-files/d1-merge/dispatch/G-25766-test-source-alias-gate.md) and the seat's report (comment 6396404794;/mnt/project-files/d1-merge/out/G-25766-report.json):scripts/check-test-source-alias.mjsonly; noui/file touched, nothing added toKNOWN_UNALIASED_TEST_IMPORTSor any allowlist.main: gate green,--listledger recompute identical. Corpus dump over every test file: 48 sites on main / 145 merged reclassified, all removals, no additions.type Name =alias-head regex) not taken because the uncalled-member tail afterimport(…)decides every type position on its own; route 1 (string content) extended to template interpolations, whichcodeOnlyblanks — the self-test caught that; erased sites are also skipped for module-scope compliance (no new finding on either tree).await,.then(as positives), roster floor 10 → 11, 7 mutants show the erased rows can fail. 33 derived gates exit 0,check:pm-dispatch-gatesexit 0.check-governed-merges --pr objectstack-ai/objectstack#25781on the final file list: NOT governed (0 of 1 path hits the register), 177 changed lines ≤ 3000 — ordinary queue landing.Landing: the seat flips the PR ready and arms auto-merge via the relay now. The body says
Fixes #25766, so this card closes on merge; #25763's 7 residual files then read clean on the merged layout.Generated by Claude Code