Skip to content

tooling(test-source-alias): a type reference or literal text is not a clocked load - #25781

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-70gghu
Oct 25, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-70gghu

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Fixes #25766
Part of #25677 (cutover prep G, the gate half of decision F; the test-file half is #25763).

Clause-②: no
Metadata-protocol: none

What this changes

One file, scripts/check-test-source-alias.mjs, the clocked-window rule only (moduleLoadSites). The ledger half of the gate (extractRuntimeImports, KNOWN_UNALIASED_TEST_IMPORTS, the vitest alias reading) is untouched.

Before this PR the rule counted every import('x') the import regex could see in the comment-masked text, and its only type-position guard was typeof in front of the keyword. Two kinds of sites were therefore read as clocked loads although they load nothing:

  1. Type references. type T = import('p').T and const s: import('p').Schema = ... are erased by tsc. The new isImportTypeReference decides this from what FOLLOWS the call: a type reference is always qualified (a bare import('p') in a type position is a compile error) and its member chain is never called. A runtime import() is awaited, assigned, passed, or chained into a call (.then(...), a generic .then call with a type argument, ?.then(...)). The head cannot decide it: { key: import('p') } is an object-literal load behind the same colon an annotation uses. Type arguments after the member are balanced (braces, semicolons and function-type arrows inside them included), so the token after the closing angle bracket tells a type argument list from a generic call. Only the import( form is read this way; require('p').member is the ordinary CJS spelling of a runtime load and stays a load. Anything the reader cannot parse reads as a load (the loud direction).
  2. Text. A dynamic form written inside a string or a template body (a scanner's own test feeding it input, a child-process script) is not code. A match now counts only when its keyword is executed code: plain code in the codeOnly projection, or a template interpolation interior, which codeOnly blanks too, read from scanSource's interpolation array. That scan runs only when a keyword is not plain code.

Both rules skip the site for the module-scope set as well, matching what the header already said for typeof: an erased site is neither a finding nor a compliance token. A module-top type T = import('p').T therefore no longer pays, on paper, for a real clocked await import('p') in the same file.

Measurements

Merged layout (verify branch claude/project-thread-dwhq6r-25697-verify at 9e37acd, this diff applied at the root with git apply, node scripts/check-test-source-alias.mjs):

before after
unique clocked findings (file:line) 37 30
the card's 7 false positives 7 0
new clocked findings 0
non-clocked findings (ledger, alias) 42 42, byte-identical lines

The 7 that are gone are exactly the card's table: spec-symbol-4650.test.ts in plugin-calendar :58, plugin-form :50, plugin-grid :73, plugin-kanban :50, dashboard-global-filters-spec-binding.test.ts:102, data-table.test.ts:66, ui/scripts/__tests__/check-spec-range-floors.test.ts:286. The 30 left are #25763's real ones. No ui/ file is edited and nothing is added to KNOWN_UNALIASED_TEST_IMPORTS or any allowlist.

objectstack main (merge base ab510f4):

  • pnpm check:test-source-alias: self-test OK, run OK, same green line as before (76 packages scanned, 60 registered, 55 published subpaths).
  • Ledger recompute: node scripts/check-test-source-alias.mjs --list before and after are identical (60 of 76 packages, 295 package-dependency pairs).
  • Corpus-wide reading, with no population filter: every tracked test file's sites, before vs after. main: 48 site entries reclassified, all of them removals, 0 additions. merged: 145 removals, 0 additions. Every main removal was read by hand. They are child-process scripts in template literals (SEED_CHILD, WORKER_SOURCE, PROBE, DRIVER_CJS, an os start stub), RuleTester-style code strings, and erased type positions (s is import('typescript').X predicates, as import('node:crypto').BinaryLike casts, { knex: import('knex').Knex } annotations). None of the files is a finding before or after, because none of these specifiers is in its package's measured set.
  • Gate wall time on this box: 19.5 s before, 18.2 s after.

Self-test

New battery a type reference or literal text is not a load (#25766), 21 cases. The roster floor goes from 10 to 11 batteries.

  • End to end: fixture (29) packages/clocked-type-reference holds an alias and an annotation (type arguments carrying a semicolon) inside a test body, plus a dynamic import as template text, each putting its specifier in the measured set. A module-top alias of @fx/core sits beside a real await import('@fx/core'). Only the real load may be reported, and it must be reported.
  • Shape table read straight off moduleLoadSites. 8 erased rows (alias, annotation, generic annotation, qualified chain with array and indexed access, parameter annotation, string, template body, require in a template body) and a module-top export type row. 9 load rows: unawaited assignment, object-literal value, awaited, .then(...), generic .then call, ?.then(...), a load inside a template interpolation, a load right after an erased alias on the same line (the check-test-source-alias loses a bare side-effect import 'x'; whenever any later import … from … follows it, so the clocked-window rule reports a file that already applied the fix it prescribes #12555 lesson: erasing one site must not swallow its neighbour), and require('p').member.

On scratch copies of the gate (repo root pinned, imports pointed at this tree; the unmutated copy shows 12 relocation-only failures, subtracted below), each mutation reds rows of the new battery:

mutation new-battery rows red
drop the executed-keyword test the 3 text rows, the end-to-end text and real-load legs
drop the type-reference test the 5 type rows, module-top alias, end-to-end legs, the same-line neighbour row
executed-keyword test without the interpolation array the interpolation load row
type reference ignores a following call the .then(...) and generic .then rows
a generic argument list reads as a load the generic annotation row, module-top generic alias, end-to-end legs
a head-based rule (colon or equals in front means type) the unawaited-assignment and object-literal rows
the type rule also applied to require the require('p').member row

Every erased row is reached by at least one mutation. The awaited and ?.then(...) load rows are not reached by these seven; they stand guard against a wider erasure rule.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 85fe6b0 (1 path, +173/-4). Exit codes were captured before any pipe. Every derived command exits 0:

node scripts/check-ci-filter-parity.mjs, check-closing-keyword-parity.mjs (+ --self-test), check-comment-mask-corpus.mjs, check-declaration-mirrors.mjs (+ --self-test), check-position-name-fold-loaders.mjs (+ --self-test), check-scripts-symbol-anchors.mjs (+ --self-test), check-self-test-wired.mjs (+ --self-test), check-self-test-workflow-commands.mjs (+ --self-test), check-whole-set-label-write.mjs (+ --self-test), scripts/pm/bare-root-worklist.mjs --self-test; pnpm check:agent-test-spelling, check:bash32-floor, check:cli-command-ids, check:cross-package-test-inputs, check:driver-memory-census, check:entry-guard, check:gitlink-declared, check:nul-bytes, check:parse-guard, check:pm-dispatch-gates, check:pnpm-filter-targets, check:pnpm-flag-spellings, check:ratchet-remedy-authority, check:refd-timer-probe, check:test-source-alias, check:watch-hint-literal.

Reconciled at 85fe6b0 with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list: 33 derived, 33 run, 0 NOT-MEASURED, 0 UNRUN.

The first run of six of them exited 3 (PREREQUISITE NOT MET: yaml, typescript, @typescript-eslint/parser missing in a fresh worktree). After pnpm install --frozen-lockfile all six exit 0. NOT MEASURED locally, left to CI: the two families that take a value from the workflow (check-shard-attestation.mjs --emit, check-test-completeness.mjs) and the Test Core job steps.

No changeset: the root package is private and scripts/** ships in no package's files[]. The PR carries skip-changeset.

Acceptance notes

  1. Out of scope by the card, noted: the ledger half (extractRuntimeImports) still reads type references and string or template text as runtime imports. That is why such specifiers keep entering a package's measured set (all 7 merged-layout sites were in it). It belongs to the vitest-alias half that the cutover adaptation commit owns. No finding is wrong because of it after this PR, since the clocked rule no longer reports those sites.
  2. Observed, not fixed (outside the card's step 1): a STATIC import spelled as text inside a string or template at module top still counts as a module-scope compliance token. That is the fail-open direction. Measured with a scratch copy that applies the same executed-keyword test to static matches: 67 such tokens on main and 141 on the merged layout, and none of them pairs with a clocked load of the same specifier in its file. Today it hides nothing. Carrier: none.

Generated by Claude Code

… clocked load

The clocked-window rule matched `import('x')` anywhere the import regex
could see it, with `typeof` as the only type-position guard. On the merged
ui/ layout that read seven erased or textual sites as clocked loads: an
`import('p').T` alias or annotation inside a test body, and a dynamic form
written as template text in a scanner's own test.

A dynamic match now counts only when its keyword is executed code (plain
code, or a template interpolation), and an `import(...)` whose tail is an
uncalled member chain is a type reference, neither a finding nor a
compliance token. `require(...).member` stays a load.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mj3sUeTnVuFsWPY37TxDmS
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Dispatch seat record. ACCEPTed on card #25766. check-governed-merges --pr objectstack-ai/objectstack#25781 on the final file list: NOT governed (0 of 1 path in the register; 7 surfaces govern objectstack), 177 changed lines (+173/−4) under the 3000-line human-merge threshold. CI on head 85fe6b02be: 51 check runs, 34 success, 17 skipped, 0 failed. Merged-layout reading: the 7 type-position / string-content false positives gone, 0 new, ledger recompute identical. Flipping ready and arming auto-merge (ordinary queue landing). No reviewer requested; this lands on the dispatch seat's verdict.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 25, 2026 10:29
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 25, 2026 10:29
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 25, 2026
Merged via the queue into main with commit 5612dd9 Oct 25, 2026
52 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-70gghu branch October 25, 2026 10:38
os-tesla pushed a commit that referenced this pull request Oct 25, 2026
…of non-ui/ members only (decision F, objectstack#25803)

Decision F (17f): adopt the function-body half for the objectui tests (warm module-scope
imports, card F) and scope the vitest-alias half of the gate to members outside ui/.
objectui's tests resolve their workspace deps through the built dist/ by design; at the
import 42 ui/ members read as unaliased with no registry entry.

inAliasHalf() keeps those members out of the measured registry population and out of the
'tests import ... no source alias' verdict. The scan still measures their specifiers, so the
clocked-window rule reads ui/ exactly as before; the ENOTDIR alias-table rules and config
readability are unchanged. A registry entry naming a ui/ member is refused as unneeded.
The OK line names how many scanned packages were not asked the alias half.

Self-test: fixture (1c) and four baseline assertions; dropping the scope reds three of
them. Before: exit 1 (42 alias-half + 57 clocked-window findings on the verify tree). After:
self-test 0; scan exit 1 on the 57 clocked-window findings only, the accepted class that
#25781 and card F clear.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CUXv64KLMfYv3ia2sWi6te
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants