Skip to content

test: load package barrels at module top so the collect phase pays for them - #21480

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21457-barrel-import-collect-phase
Oct 2, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21457-barrel-import-collect-phase

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #21457

Clause-②: no

What this changes

Five test files, test-only. Every case that loaded a package barrel with a dynamic import inside its body now gets that barrel from a module-top load, so the load is charged to vitest's collect phase instead of the case's 5000ms window (AGENTS.md: 「Clocked windows measure behaviour, never loading」). The shape and the reasoning are the core precedent's, packages/core/src/service-resolution-discriminator.contract.test.ts; no helper was added. No timeout, retry, skip, vitest.config.ts, source or gate change.

site (line numbers at 7e7e64b1) change
packages/objectql/src/system-write-organization.test.ts:506 import * as barrel from './index.js' at module top; the case is now synchronous and still asserts, by identity, that both names come from the barrel
packages/core/src/security/platform-admin.test.ts:279 import * as securityEntry from './index.js' at module top; the case still probes presence and absence on the security entry
packages/lint/src/validate-predicate-path-refs.test.ts:376 import * as barrel from './index.js' at module top
packages/plugins/plugin-sharing/src/translations/serving-seam.test.ts:86 and :100 one static import plus one module-top re-evaluation under a mock, see below
packages/lint/src/lazy-deps.test.ts:190 the triage's named exception: keeps its in-case import, and one comment line now says why

The one site where the hoist was not mechanical: serving-seam.test.ts

Hoisting changed what this file observes, so it was not a plain move. The :86 case evaluated the barrel against a MOVED source: a beforeEach ran vi.resetModules(), then the case ran vi.doMock('./en.objects.generated.js', ...) and imported the barrel. A static import cannot be mocked per case. The resolution keeps each assertion's meaning:

  • :100 ("substitutes nothing while the source has not moved") reads a static import { SharingTranslations } from './index.js', which is the barrel as committed.
  • :86 ("SERVES the current source when the source moves under the recorded leaf") reads servedAfterSourceMoved. That value is produced at module top by the same four steps the beforeEach and the case used to perform: vi.resetModules(), vi.doMock(...), await import('./index.js'), vi.doUnmock(...). A top-level await in a test module runs during collection; the repo already uses that shape in packages/cli/test/score-lint-crash.test.ts and packages/mcp/src/__tests__/plugin-execution-context.test.ts.
  • The beforeEach is removed. It existed only to isolate the two in-case imports, and no case imports anything now.

So one await import('./index.js') remains in that file, at column 0 at module top and not inside a case. The triage pin is about loads inside a case.

Ablation (one-time, on the committed tree, scripts/ablation-replace.mjs in WRAP mode with its own restore proof): deleting the module-top vi.resetModules(); turns exactly the :86 case red, AssertionError: expected 'Token' to be 'Share token', with 1 failed and 4 passed. Without the reset, the import returns the copy the static import cached. The restore was proven: blob 9d8e14d9fe22 equals HEAD's, and git diff HEAD is empty.

Measured

Every reading was taken on this container's shared box. Other agents' work ran alongside, and the verify lock excluded only other locked runs, so the absolute numbers are shared-box seconds. The signal is the per-case drop.

Per-case duration, run alone (one file plus -t, vitest JSON reporter; the number in brackets is the same case in a whole-file run):

case before (7e7e64b1) after (8e8b3bb6)
objectql barrel case 1669 ms (1556) 2.4 ms (0.1)
core security-entry case 1695 ms (1837) 2.1 ms (0.1)
lint predicate barrel case 57 ms (68) 5.2 ms (0.4)
sharing :86 107 ms (134) 1.5 ms (0.3)
sharing :100 114 ms (57) 1.4 ms (0.2)
lint lazy-deps in-process case (the exception, unchanged) 8372 ms 8296 ms

In the package's whole suite. CI's Test Core runs each of these four packages whole on its shard; only @objectstack/cli takes file-level slices. So the whole-suite run is their shard. The readings are on 2d2204d764; for the before leg, the edited file(s) were restored tree-only from 7e7e64b1.

case before after suite, both legs
objectql (--project local) 614 ms 0.1 ms 366 files, 7383 tests passed
core (both projects) 313 ms 0.3 ms 79 files, 2204 tests passed
lint predicate 34 ms 0.2 ms 119 files, 5619 tests passed
lint lazy-deps (the exception) 5476 ms 5544 ms same run
sharing :86 / :100 99 / 56 ms 0.2 / 0.2 ms 38 files, 954 tests passed

The load did not disappear. It moved into the collect phase, which vitest 4's summary calls "import". For example, objectql's single-case run went from import 7.84s and tests 1.67s to import 10.02s and tests 4ms.

Nobody had measured the other five sites before this. At these readings, only the objectql and core cases carried a load large enough to matter (1.5 to 1.8 s alone). The lazy-deps case is not under the default window at all: it already runs under COLD_LOAD_TIMEOUT_MS = 30_000, and it measures 5.5 to 8.4 s.

Verification

  • pnpm test per package on 2d2204d764: core 76 files / 2156 tests, lint 119 / 5619, plugin-sharing 38 / 954, all passed. objectql vitest run --project local, which is its test script: 366 / 7383 passed.
  • pnpm typecheck for all four packages: exit 0, and check:test-typecheck OK in each. All five files are in their package's tsconfig.test.json program (tsc --listFiles), and none of them carries an error.
  • Gates: dispatch-gates --commands derived 63 commands from this tree, the same list as the PM's derivation. All 63 were run and all exit 0. check:i18n and check:dual-build-cjs-loads first answered PREREQUISITE NOT MET (exit 3); after a turbo cache replay of their build closure, both re-ran green. The --ran reconciliation: 63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN.
  • ESLint, narrowed with the narrowing proven: eslint --no-inline-config --format json over the five files on 2d2204d764 linted 5 files (none reported as ignored), with 0 errors and 0 warnings. eslint.config.mjs enables no type-aware linting (no parserOptions.project, no projectService), so this diff cannot change a verdict on any file it does not touch. The repo-wide pnpm lint is CI's.
  • Pin grep, git grep -n "await import('\./index" -- 'packages/**/*.test.ts' ':!packages/spec', gives 3 hits: the core precedent's own comment, lazy-deps.test.ts:191 (the named exception) and serving-seam.test.ts:84 (at module top). The spec side, as the control, gives 45 hits.
  • check:test-source-alias does not flag this shape. Per the triage ruling, that is recorded here and the gate is not widened. It is green on this tree.

Acceptance notes

  • Same family, other spellings. These sites are outside this card's ruled pattern and outside its claimed files. Each is an in-case barrel load, measured alone on 2d2204d764:

    • packages/services/service-analytics/src/__tests__/dataset-compare-kind-refusal.test.ts:220, ../index.js: 568 ms;
    • packages/plugins/plugin-email/src/send-template.test.ts:336 and :342, ./transports/index.js: 37 ms and 1 ms;
    • packages/metadata/src/loaders/overlay-index-single-producer.test.ts:150, ../migrations/index.js: 24 ms.

    None is near the window. They are noted here and not filed.

  • Base: the branch was cut at 7e7e64b1 and merged with origin/main at 49524f6906.


Generated by Claude Code

claude added 2 commits October 2, 2026 21:35
…r them

Five test cases loaded a package barrel with a dynamic import inside the
case, which charges the whole index graph's load to vitest's per-case
5000ms budget; one of them was measured timing out on a loaded box. Each
barrel is now loaded at module top, as the core precedent
service-resolution-discriminator.contract.test.ts already does, so the load
is charged to the collect phase and the cases assert behaviour only.

- objectql system-write-organization: static namespace import of the barrel.
- core security/platform-admin: static namespace import of the security entry.
- lint validate-predicate-path-refs: static namespace import of the barrel.
- plugin-sharing translations/serving-seam: the committed barrel is a static
  import; the moved-source barrel is evaluated at module top (resetModules,
  doMock, import, doUnmock), which replaces the per-case beforeEach.
- lint lazy-deps keeps its in-case import (the load is its subject), now
  with a one-line note saying so.

No timeout, retry, skip, config or source change.

Claude-Session: https://claude.ai/code/session_01DDZNkDVwPQnevTFcYE47H3
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the size/s label Oct 2, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 2, 2026
@github-actions github-actions Bot added the tests label Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json aa4632235ba571ef800b95e6bc18d00a30aa1d57 → packageMentionDocs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants