From a89a1ccea2baf30450f0088dd25167d2d39b7e09 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 01:58:15 +0000 Subject: [PATCH 1/2] test(runtime,metadata-protocol,objectql): pay the lint root's first load in a setup file, outside the clocked case A served view read and an object publish load @objectstack/lint lazily; under vitest that first load is a cold transform paid inside whichever case makes the file's first such read. A traced full run of each suite found it inside a case in 9 runtime, 39 metadata-protocol and 18 objectql test files. Each package now imports the root in a setup file, which vitest runs before collection and no timeout clocks. objectql gains the anchored ^@objectstack/lint$ source alias, since it does not depend on lint itself. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_017ihL2gLghEqeDKTbDTC7Zd --- .../test/lint-root-preload.setup.ts | 35 ++++++++++++++++++ packages/metadata-protocol/vitest.config.ts | 7 +++- .../objectql/test/lint-root-preload.setup.ts | 36 +++++++++++++++++++ packages/objectql/vitest.config.ts | 20 +++++++++++ .../runtime/test/lint-root-preload.setup.ts | 34 ++++++++++++++++++ packages/runtime/vitest.config.ts | 12 ++++++- 6 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 packages/metadata-protocol/test/lint-root-preload.setup.ts create mode 100644 packages/objectql/test/lint-root-preload.setup.ts create mode 100644 packages/runtime/test/lint-root-preload.setup.ts diff --git a/packages/metadata-protocol/test/lint-root-preload.setup.ts b/packages/metadata-protocol/test/lint-root-preload.setup.ts new file mode 100644 index 00000000000..5633df5b2e9 --- /dev/null +++ b/packages/metadata-protocol/test/lint-root-preload.setup.ts @@ -0,0 +1,35 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. +// +// Pay the first load of the `@objectstack/lint` package ROOT before a test +// file is collected, never inside a clocked case. Wired as `setupFiles` in +// `../vitest.config.ts`. +// +// ## Why +// +// The protocol loads the lint root lazily, on first use, in two places: a +// served `view` read decorates its `_diagnostics` with the form-layout +// warnings (`loadFormLayoutLint`, src/metadata-diagnostics.ts), and an object +// publish runs its build probes (`runBuildProbes`, src/build-probes.ts). The +// lazy shape is product design: the kernel boot path never reaches the root. +// Under vitest the first of those loads is a cold transform of the lint +// module graph, paid inside whichever case makes the file's first such read +// or publish. Here the root resolves to `lint/dist/index.js` (no alias; the +// package's row in `check:test-source-alias` lists it), and one run of the +// whole suite traced that load inside a case in 39 test files. In those +// cases it measured 714 ms p50 idle and 975 ms p50 (1241 ms max) with four +// copies on four cores, against siblings in the tens of milliseconds and the +// 5000 ms `testTimeout`. +// +// ## What it does +// +// It imports the root once, at module top. vitest runs a setup file before it +// collects the test file, and neither `testTimeout` nor `hookTimeout` clocks +// that. The setup file shares the test file's module graph, so the +// protocol's later `import()` gets this instance back. +// +// A setup file rather than a per-file import, because 39 files need it and a +// new test that first reaches a view read or an object publish needs it too, +// without knowing it. The price is that every test file in the package +// evaluates the lint root once during setup, including the files that never +// reach it. +import '@objectstack/lint'; diff --git a/packages/metadata-protocol/vitest.config.ts b/packages/metadata-protocol/vitest.config.ts index 87a915b5e69..0e9d87c873c 100644 --- a/packages/metadata-protocol/vitest.config.ts +++ b/packages/metadata-protocol/vitest.config.ts @@ -1,6 +1,6 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. -// This config exists for exactly two settings; everything else stays on +// This config exists for exactly three settings; everything else stays on // vitest's defaults, deliberately — a key added here re-specifies behaviour // for every test file in the package (packages/cli/vitest.config.ts's header // records the incident that taught that). @@ -16,6 +16,11 @@ export default defineConfig({ // Mechanism + measured costs: examples/app-showcase/vitest.config.ts. // Enforced repo-wide by scripts/check-console-intercept-disarm.mjs. disableConsoleIntercept: true, + // [#29627] The lint package root's first load is paid here, before a test + // file is collected, never inside the case that makes the file's first + // served view read or object publish. The module's header carries the + // measurement and the price. + setupFiles: ['./test/lint-root-preload.setup.ts'], }, resolve: { alias: [ diff --git a/packages/objectql/test/lint-root-preload.setup.ts b/packages/objectql/test/lint-root-preload.setup.ts new file mode 100644 index 00000000000..2df5b18954d --- /dev/null +++ b/packages/objectql/test/lint-root-preload.setup.ts @@ -0,0 +1,36 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. +// +// Pay the first load of the `@objectstack/lint` package ROOT before a test +// file is collected, never inside a clocked case. Wired as `setupFiles` in +// both projects of `../vitest.config.ts`. +// +// ## Why +// +// `@objectstack/metadata-protocol` loads the lint root lazily, on first use, +// in two places: a served `view` read decorates its `_diagnostics` with the +// form-layout warnings (`loadFormLayoutLint`, metadata-diagnostics.ts), and an +// object publish runs its build probes (`runBuildProbes`, build-probes.ts). +// The lazy shape is product design: the kernel boot path never reaches the +// root. Under vitest the first of those loads is a cold transform of the lint +// module graph, paid inside whichever case makes the file's first such read +// or publish. One run of the whole suite traced that load inside a case in +// 18 test files of this package. In those cases it measured 796 ms p50 idle +// and 1169 ms p50 (2282 ms max) with four copies on four cores, against +// siblings in the tens of milliseconds and the 5000 ms `testTimeout`. +// +// ## What it does +// +// It imports the root once, at module top. vitest runs a setup file before it +// collects the test file, and neither `testTimeout` nor `hookTimeout` clocks +// that. The setup file shares the test file's module graph, so the +// protocol's later `import()` gets this instance back. This package does not +// depend on `@objectstack/lint`, so the specifier resolves through the +// anchored alias in `../vitest.config.ts`, which sends the protocol's own +// `import()` to the same source file. +// +// A setup file rather than a per-file import, because 18 files need it and a +// new test that first reaches a view read or an object publish needs it too, +// without knowing it. The price is that every test file in the package +// evaluates the lint root once during setup, including the files that never +// reach it. +import '@objectstack/lint'; diff --git a/packages/objectql/vitest.config.ts b/packages/objectql/vitest.config.ts index 5f83f66abf7..8fb737ef0b3 100644 --- a/packages/objectql/vitest.config.ts +++ b/packages/objectql/vitest.config.ts @@ -104,7 +104,25 @@ runProjectCliOverridePreflight({ parse: parseCLI, }); +// [#29627] The lint package root's first load is paid in this setup file, +// before a test file is collected, never inside the case that makes the +// file's first served view read or object publish. Named in EACH project +// below, like the per-project settings beside it. The module's header carries +// the measurement and the price. +const LINT_ROOT_PRELOAD = './test/lint-root-preload.setup.ts'; + export default defineConfig({ + resolve: { + alias: [ + // [#29627] The package root the preload above imports. This package does + // not depend on `@objectstack/lint`; metadata-protocol does, and loads + // the root lazily from its `dist/`. Anchored, so lint's published + // subpaths are not swallowed by the file replacement, and pointed at + // source, so the preload and the protocol's own `import()` resolve to + // the same module, the lint rule in the checkout, never `dist/`. + { find: /^@objectstack\/lint$/, replacement: path.resolve(__dirname, '../lint/src/index.ts') }, + ], + }, test: { // Each project re-declares the root block's test options: a ROOT-level // value is inert for a project run (measured on vitest 4.1.10, see @@ -124,6 +142,7 @@ export default defineConfig({ // block. A ROOT-level value is inert for a project run, so it is // declared here as well (scripts/check-console-intercept-disarm.mjs). disableConsoleIntercept: true, + setupFiles: [LINT_ROOT_PRELOAD], }, }, { @@ -139,6 +158,7 @@ export default defineConfig({ // block. A ROOT-level value is inert for a project run, so it is // declared here as well (scripts/check-console-intercept-disarm.mjs). disableConsoleIntercept: true, + setupFiles: [LINT_ROOT_PRELOAD], }, }, ], diff --git a/packages/runtime/test/lint-root-preload.setup.ts b/packages/runtime/test/lint-root-preload.setup.ts new file mode 100644 index 00000000000..4ad621d52d8 --- /dev/null +++ b/packages/runtime/test/lint-root-preload.setup.ts @@ -0,0 +1,34 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. +// +// Pay the first load of the `@objectstack/lint` package ROOT before a test +// file is collected, never inside a clocked case. Wired as `setupFiles` in +// both projects of `../vitest.config.ts`. +// +// ## Why +// +// `@objectstack/metadata-protocol` loads the lint root lazily, on first use, +// in two places: a served `view` read decorates its `_diagnostics` with the +// form-layout warnings (`loadFormLayoutLint`, metadata-diagnostics.ts), and an +// object publish runs its build probes (`runBuildProbes`, build-probes.ts). +// The lazy shape is product design: the kernel boot path never reaches the +// root. Under vitest the first of those loads is a cold transform of the lint +// module graph, paid inside whichever case makes the file's first such read +// or publish. One run of the whole suite traced that load inside a case in 9 +// test files of this package. Here the root resolves through the anchored +// `^@objectstack/lint$` alias to lint's source, which costs less than the +// built bundle the two sibling packages load, but it still lands in the case. +// +// ## What it does +// +// It imports the root once, at module top. vitest runs a setup file before it +// collects the test file, and neither `testTimeout` nor `hookTimeout` clocks +// that. The setup file shares the test file's module graph, so the +// protocol's later `import()` gets this instance back. +// +// A setup file rather than a per-file import, because 9 files need it and a +// new test that first reaches a view read or an object publish needs it too, +// without knowing it. `meta-write-org-scope.test.ts` keeps its own module-top +// import, which now finds the module already loaded. The price is that every +// test file in the package evaluates the lint root once during setup, +// including the files that never reach it. +import '@objectstack/lint'; diff --git a/packages/runtime/vitest.config.ts b/packages/runtime/vitest.config.ts index 8e78e65c9d2..5c48eb0f11c 100644 --- a/packages/runtime/vitest.config.ts +++ b/packages/runtime/vitest.config.ts @@ -98,6 +98,13 @@ runProjectCliOverridePreflight({ parse: parseCLI, }); +// [#29627] The lint package root's first load is paid in this setup file, +// before a test file is collected, never inside the case that makes the +// file's first served view read or object publish. Named in EACH project +// below, like the per-project settings beside it. The module's header carries +// the measurement and the price. +const LINT_ROOT_PRELOAD = './test/lint-root-preload.setup.ts'; + export default defineConfig({ resolve: { // ARRAY form, not the object form: only the array form accepts a RegExp @@ -154,7 +161,8 @@ export default defineConfig({ { find: /^@objectstack\/lint\/runtime$/, replacement: path.resolve(__dirname, '../lint/src/runtime.ts') }, // [#29603] The package root: a served view read imports it lazily for the // form-layout warnings (metadata-protocol's `loadFormLayoutLint`), and - // `meta-write-org-scope.test.ts` pays that load at module top. Both + // `meta-write-org-scope.test.ts` pays that load at module top, as every + // test file does through `LINT_ROOT_PRELOAD` (#29627). All of them // resolve here, so the read judges the lint rule in the checkout, never `dist/`. { find: /^@objectstack\/lint$/, replacement: path.resolve(__dirname, '../lint/src/index.ts') }, // The MCP bridge (`domains/mcp.ts`) reads "this field declares options" @@ -272,6 +280,7 @@ export default defineConfig({ // block. A ROOT-level value is inert for a project run, so it is // declared here as well (scripts/check-console-intercept-disarm.mjs). disableConsoleIntercept: true, + setupFiles: [LINT_ROOT_PRELOAD], }, }, { @@ -290,6 +299,7 @@ export default defineConfig({ // block. A ROOT-level value is inert for a project run, so it is // declared here as well (scripts/check-console-intercept-disarm.mjs). disableConsoleIntercept: true, + setupFiles: [LINT_ROOT_PRELOAD], }, }, ], From 4bab1b183f15ccb456e55f1c29a55cf001a329fd Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 02:24:19 +0000 Subject: [PATCH 2/2] test(runtime,metadata-protocol,objectql): import a lint-root preload testkit at module top instead of a setup file A setup file made every test file pay the lint root's load, measured at about +200 s of worker time in metadata-protocol and runtime and +330 s in objectql. A testkit per package, imported at module top by only the files whose traced run showed the first load inside a case, moves exactly that load into collection. objectql declares @objectstack/lint as a devDependency so the import type-checks, and keeps the anchored source alias that check:test-source-alias requires for it. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_017ihL2gLghEqeDKTbDTC7Zd --- .../src/lint-root-preload.testkit.ts | 29 +++++++++++++++ .../protocol-publish-drafts-closure.test.ts | 2 ++ ...tocol-publish-drafts-endpoint-gate.test.ts | 2 ++ ...l-publish-drafts-object-field-refs.test.ts | 2 ++ .../protocol-publish-drafts-org-scope.test.ts | 2 ++ ...tocol-publish-drafts-package-scope.test.ts | 2 ++ .../src/protocol.app-package-id-key.test.ts | 2 ++ .../protocol.draft-publish-verdict.test.ts | 2 ++ .../protocol.extension-field-suffix.test.ts | 2 ++ ...protocol.flow-by-name-shipped-name.test.ts | 2 ++ .../protocol.list-slot-prefer-local.test.ts | 2 ++ .../src/protocol.lock-door-read-agree.test.ts | 2 ++ ...otocol.lock-one-resolution.shard-1.test.ts | 2 ++ ...otocol.lock-one-resolution.shard-2.test.ts | 2 ++ ...otocol.lock-one-resolution.shard-3.test.ts | 2 ++ ...otocol.lock-one-resolution.shard-4.test.ts | 2 ++ .../src/protocol.lock-one-resolution.test.ts | 2 ++ .../src/protocol.lock-org-axis-agree.test.ts | 2 ++ .../protocol.named-base-overlay-write.test.ts | 2 ++ ...ocol.object-cascade-delete-objects.test.ts | 2 ++ .../protocol.object-disabled-actions.test.ts | 2 ++ ...ct-registry-write-through-spelling.test.ts | 2 ++ .../protocol.org-scoped-write-refused.test.ts | 2 ++ ...rotocol.package-publish-audit-rows.test.ts | 2 ++ .../protocol.packageless-save-address.test.ts | 2 ++ ...ublish-commit-capture-read-failure.test.ts | 2 ++ ...ublish-side-effects-canonical-type.test.ts | 2 ++ ...ocol.publish-stored-type-canonical.test.ts | 2 ++ .../protocol.reads-environment-only.test.ts | 2 ++ ...l.record-package-commit-durability.test.ts | 2 ++ .../src/protocol.save-union-issues.test.ts | 2 ++ .../src/protocol.scoped-list-fallback.test.ts | 2 ++ .../protocol.seed-external-id-refusal.test.ts | 2 ++ .../src/protocol.served-content-hash.test.ts | 2 ++ .../src/protocol.stored-conversions.test.ts | 2 ++ ...otocol.summary-rollup-publish-gate.test.ts | 2 ++ ...rotocol.unknown-datasource-refusal.test.ts | 2 ++ ...tocol.unknown-grant-object-refusal.test.ts | 2 ++ ...ol.view-container-reset-expansions.test.ts | 2 ++ .../view-container-runtime-expansion.test.ts | 2 ++ .../test/lint-root-preload.setup.ts | 35 ------------------ packages/metadata-protocol/vitest.config.ts | 7 +--- packages/objectql/package.json | 1 + .../__fixtures__/lint-root-preload.testkit.ts | 31 ++++++++++++++++ packages/objectql/src/build-probes.test.ts | 2 ++ .../environment-overlay-five-types.test.ts | 2 ++ .../src/package-disable-enforcement.test.ts | 2 ++ .../src/protocol-commit-history.test.ts | 2 ++ ...custom-override-bare-slot-envelope.test.ts | 2 ++ ...col-custom-override-effective-read.test.ts | 2 ++ ...ol-duplicate-package-names-in-step.test.ts | 2 ++ ...rotocol-lock-artifact-package-axis.test.ts | 2 ++ packages/objectql/src/protocol-meta.test.ts | 2 ++ .../src/protocol-packaged-view-base.test.ts | 2 ++ ...ocol-packageless-overlay-migration.test.ts | 2 ++ ...packageless-read-dependent-package.test.ts | 2 ++ .../protocol-publish-package-drafts.test.ts | 2 ++ .../src/protocol-publish-rollback.test.ts | 2 ++ .../src/protocol-revert-org-scope.test.ts | 2 ++ .../publish-package-drafts-backfill.test.ts | 2 ++ ...ackage-drafts-response-conformance.test.ts | 2 ++ ...package-drafts-seed-receipt-counts.test.ts | 2 ++ .../objectql/test/lint-root-preload.setup.ts | 36 ------------------- packages/objectql/vitest.config.ts | 22 ++++-------- .../domains/meta-app-package-id-key.test.ts | 2 ++ .../src/domains/meta-package-all-fold.test.ts | 2 ++ .../meta-published-runtime-publish.test.ts | 2 ++ .../meta-save-preconditions-parity.test.ts | 2 ++ ...ase-unchanged-per-type.integration.test.ts | 2 ++ ...ish-drafts-change-note.integration.test.ts | 2 ++ .../runtime/src/lint-root-preload.testkit.ts | 30 ++++++++++++++++ ...list-commits-org-scope.integration.test.ts | 2 ++ ...-runtime-owner-refusal.integration.test.ts | 2 ++ ...-revert-stored-members.integration.test.ts | 2 ++ .../runtime/test/lint-root-preload.setup.ts | 34 ------------------ packages/runtime/vitest.config.ts | 13 ++----- pnpm-lock.yaml | 3 ++ 77 files changed, 236 insertions(+), 137 deletions(-) create mode 100644 packages/metadata-protocol/src/lint-root-preload.testkit.ts delete mode 100644 packages/metadata-protocol/test/lint-root-preload.setup.ts create mode 100644 packages/objectql/src/__fixtures__/lint-root-preload.testkit.ts delete mode 100644 packages/objectql/test/lint-root-preload.setup.ts create mode 100644 packages/runtime/src/lint-root-preload.testkit.ts delete mode 100644 packages/runtime/test/lint-root-preload.setup.ts diff --git a/packages/metadata-protocol/src/lint-root-preload.testkit.ts b/packages/metadata-protocol/src/lint-root-preload.testkit.ts new file mode 100644 index 00000000000..90cac27f348 --- /dev/null +++ b/packages/metadata-protocol/src/lint-root-preload.testkit.ts @@ -0,0 +1,29 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. +// +// Import this at module top in a test file whose cases make a served `view` +// read or an object publish, so the file pays the first load of the +// `@objectstack/lint` package ROOT during collection, never inside a clocked +// case. Test-only: no product module imports it. +// +// The protocol loads the lint root lazily, on first use, in two places: a +// served `view` read decorates its `_diagnostics` with the form-layout +// warnings (`loadFormLayoutLint`, metadata-diagnostics.ts), and an object +// publish runs its build probes (`runBuildProbes`, build-probes.ts). The lazy +// shape is product design: the kernel boot path never reaches the root. +// Under vitest the first such load in a file is a cold transform of the lint +// module graph, paid inside whichever case gets there first. A module-top +// import is paid during collection, which neither `testTimeout` nor +// `hookTimeout` clocks, and the protocol's later `import()` gets the same +// module instance back (AGENTS.md § Build & Test, the clocked-window rule). +// +// Here the root resolves to `lint/dist/index.js`: this package's row in +// `check:test-source-alias` lists `@objectstack/lint` as still read from +// `dist/`. +// +// Who imports it: every test file in which a traced run of the whole suite +// saw that first load inside a case. A new file whose cases reach a view read +// or an object publish imports it too. It is not a `setupFiles` entry, +// because then every test file in the package pays the load, including the +// many that never reach it: measured, that added about 200 s of worker time +// and about a third to the suite's wall time. +import '@objectstack/lint'; diff --git a/packages/metadata-protocol/src/protocol-publish-drafts-closure.test.ts b/packages/metadata-protocol/src/protocol-publish-drafts-closure.test.ts index c61c3d5f67a..94073d5c6e9 100644 --- a/packages/metadata-protocol/src/protocol-publish-drafts-closure.test.ts +++ b/packages/metadata-protocol/src/protocol-publish-drafts-closure.test.ts @@ -56,6 +56,8 @@ * `saveMetaItem` / `publishPackageDrafts` run. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; // The producer's OWN write-verb dispatch decisions, so the fake engine below // cannot accept a call ObjectQL refuses. From `@objectstack/metadata-core`, diff --git a/packages/metadata-protocol/src/protocol-publish-drafts-endpoint-gate.test.ts b/packages/metadata-protocol/src/protocol-publish-drafts-endpoint-gate.test.ts index de4dc032600..b1ac29aac9f 100644 --- a/packages/metadata-protocol/src/protocol-publish-drafts-endpoint-gate.test.ts +++ b/packages/metadata-protocol/src/protocol-publish-drafts-endpoint-gate.test.ts @@ -1,5 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update), so the fake engine below cannot accept a call ObjectQL diff --git a/packages/metadata-protocol/src/protocol-publish-drafts-object-field-refs.test.ts b/packages/metadata-protocol/src/protocol-publish-drafts-object-field-refs.test.ts index 3ef5f170ff5..ee636c293d9 100644 --- a/packages/metadata-protocol/src/protocol-publish-drafts-object-field-refs.test.ts +++ b/packages/metadata-protocol/src/protocol-publish-drafts-object-field-refs.test.ts @@ -41,6 +41,8 @@ * `publishPackageDrafts`; nothing on the gate path is stubbed. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts b/packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts index 440fd0c5a1a..cefa03aef96 100644 --- a/packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts +++ b/packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts @@ -1,5 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update), so the fake engine below cannot accept a call ObjectQL diff --git a/packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts b/packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts index 1527a361717..9d919b49e42 100644 --- a/packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts +++ b/packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts @@ -1,5 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update), so the fake engine below cannot accept a call ObjectQL diff --git a/packages/metadata-protocol/src/protocol.app-package-id-key.test.ts b/packages/metadata-protocol/src/protocol.app-package-id-key.test.ts index 33fa7fda09a..b79eeecfaab 100644 --- a/packages/metadata-protocol/src/protocol.app-package-id-key.test.ts +++ b/packages/metadata-protocol/src/protocol.app-package-id-key.test.ts @@ -29,6 +29,8 @@ * `findOne` over `sys_metadata` rows, and a registry answering a * package-scoped lookup with that package's items only. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { assertEngineFindOnePredicate, isCodeArtifactBody } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.draft-publish-verdict.test.ts b/packages/metadata-protocol/src/protocol.draft-publish-verdict.test.ts index ffb8f23db0e..0b00f4eba27 100644 --- a/packages/metadata-protocol/src/protocol.draft-publish-verdict.test.ts +++ b/packages/metadata-protocol/src/protocol.draft-publish-verdict.test.ts @@ -28,6 +28,8 @@ * shape). Nothing on the save, read or publish path is stubbed. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.extension-field-suffix.test.ts b/packages/metadata-protocol/src/protocol.extension-field-suffix.test.ts index 32eaeb7cb95..09842c97500 100644 --- a/packages/metadata-protocol/src/protocol.extension-field-suffix.test.ts +++ b/packages/metadata-protocol/src/protocol.extension-field-suffix.test.ts @@ -30,6 +30,8 @@ * as a row stored before the rule (`residueField()`). */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, describe, expect, it, vi } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, imported from // `@objectstack/metadata-core` and NOT from `@objectstack/objectql`: objectql diff --git a/packages/metadata-protocol/src/protocol.flow-by-name-shipped-name.test.ts b/packages/metadata-protocol/src/protocol.flow-by-name-shipped-name.test.ts index eb9ce79badb..fb0a64555b3 100644 --- a/packages/metadata-protocol/src/protocol.flow-by-name-shipped-name.test.ts +++ b/packages/metadata-protocol/src/protocol.flow-by-name-shipped-name.test.ts @@ -27,6 +27,8 @@ * caller naming an organization reads as the environment does, and a type in * the overlay regime keeps its overlay. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it, vi } from 'vitest'; import { assertEngineFindOnePredicate, isCodeArtifactBody } from '@objectstack/metadata-core'; import type { FlowRegistration } from '@objectstack/spec/api'; diff --git a/packages/metadata-protocol/src/protocol.list-slot-prefer-local.test.ts b/packages/metadata-protocol/src/protocol.list-slot-prefer-local.test.ts index e336e77f3d3..2c2c5bd6eb9 100644 --- a/packages/metadata-protocol/src/protocol.list-slot-prefer-local.test.ts +++ b/packages/metadata-protocol/src/protocol.list-slot-prefer-local.test.ts @@ -39,6 +39,8 @@ * `protocol.scoped-list-fallback.test.ts` (#21817): its package-less rows * reach the merge as stand-ins, so its slot takes the same order. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; import { assertEngineFindOnePredicate, isCodeArtifactBody } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.lock-door-read-agree.test.ts b/packages/metadata-protocol/src/protocol.lock-door-read-agree.test.ts index 8e23c0111ac..6bb47b4b8d8 100644 --- a/packages/metadata-protocol/src/protocol.lock-door-read-agree.test.ts +++ b/packages/metadata-protocol/src/protocol.lock-door-read-agree.test.ts @@ -32,6 +32,8 @@ * * `@objectstack/objectql` cannot be imported here: it depends on this package. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { assertEngineFindOnePredicate, isCodeArtifactBody } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-1.test.ts b/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-1.test.ts index d1f016617f7..6dc8ed62157 100644 --- a/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-1.test.ts +++ b/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-1.test.ts @@ -6,6 +6,8 @@ * its call and nothing else; the shard it is comes from its own name, and * `protocol.lock-one-resolution.kit.test.ts` pins that the four files exist. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { registerRowShard } from './protocol.lock-one-resolution.kit.test.js'; registerRowShard(); diff --git a/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-2.test.ts b/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-2.test.ts index 25d8b82e100..f0a9693559d 100644 --- a/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-2.test.ts +++ b/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-2.test.ts @@ -6,6 +6,8 @@ * its call and nothing else; the shard it is comes from its own name, and * `protocol.lock-one-resolution.kit.test.ts` pins that the four files exist. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { registerRowShard } from './protocol.lock-one-resolution.kit.test.js'; registerRowShard(); diff --git a/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-3.test.ts b/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-3.test.ts index 9889ed09404..ac9e3e0b8ab 100644 --- a/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-3.test.ts +++ b/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-3.test.ts @@ -6,6 +6,8 @@ * its call and nothing else; the shard it is comes from its own name, and * `protocol.lock-one-resolution.kit.test.ts` pins that the four files exist. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { registerRowShard } from './protocol.lock-one-resolution.kit.test.js'; registerRowShard(); diff --git a/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-4.test.ts b/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-4.test.ts index c49c1c4af9b..c11a56031cb 100644 --- a/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-4.test.ts +++ b/packages/metadata-protocol/src/protocol.lock-one-resolution.shard-4.test.ts @@ -6,6 +6,8 @@ * its call and nothing else; the shard it is comes from its own name, and * `protocol.lock-one-resolution.kit.test.ts` pins that the four files exist. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { registerRowShard } from './protocol.lock-one-resolution.kit.test.js'; registerRowShard(); diff --git a/packages/metadata-protocol/src/protocol.lock-one-resolution.test.ts b/packages/metadata-protocol/src/protocol.lock-one-resolution.test.ts index 6b391766c53..f52848ed983 100644 --- a/packages/metadata-protocol/src/protocol.lock-one-resolution.test.ts +++ b/packages/metadata-protocol/src/protocol.lock-one-resolution.test.ts @@ -130,6 +130,8 @@ * rule. Nothing the table enumerates is dropped: the 16 360 tests that ran as * one file run under the same titles, and the kit adds two tests of its own. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { MetadataLockSchema, diff --git a/packages/metadata-protocol/src/protocol.lock-org-axis-agree.test.ts b/packages/metadata-protocol/src/protocol.lock-org-axis-agree.test.ts index e613e72fb70..11874816f6b 100644 --- a/packages/metadata-protocol/src/protocol.lock-org-axis-agree.test.ts +++ b/packages/metadata-protocol/src/protocol.lock-org-axis-agree.test.ts @@ -44,6 +44,8 @@ * * `@objectstack/objectql` cannot be imported here: it depends on this package. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { MetadataLockSchema } from '@objectstack/spec/kernel'; import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; diff --git a/packages/metadata-protocol/src/protocol.named-base-overlay-write.test.ts b/packages/metadata-protocol/src/protocol.named-base-overlay-write.test.ts index 76f04303ec6..aa17477e781 100644 --- a/packages/metadata-protocol/src/protocol.named-base-overlay-write.test.ts +++ b/packages/metadata-protocol/src/protocol.named-base-overlay-write.test.ts @@ -35,6 +35,8 @@ * Refusals are asserted as the envelope an HTTP boundary answers with * (`resolveThrownHttpError`). */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineFindOnePredicate, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; import { resolveThrownHttpError } from '@objectstack/types'; diff --git a/packages/metadata-protocol/src/protocol.object-cascade-delete-objects.test.ts b/packages/metadata-protocol/src/protocol.object-cascade-delete-objects.test.ts index e1d0be08cfd..9ee8d9855d1 100644 --- a/packages/metadata-protocol/src/protocol.object-cascade-delete-objects.test.ts +++ b/packages/metadata-protocol/src/protocol.object-cascade-delete-objects.test.ts @@ -21,6 +21,8 @@ * engine, with the real protocol, in `@objectstack/objectql`'s * `engine-cascade-delete-objects.test.ts`). */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it, vi } from 'vitest'; import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.object-disabled-actions.test.ts b/packages/metadata-protocol/src/protocol.object-disabled-actions.test.ts index baf8d2a5eaf..4f2cdb2bf7b 100644 --- a/packages/metadata-protocol/src/protocol.object-disabled-actions.test.ts +++ b/packages/metadata-protocol/src/protocol.object-disabled-actions.test.ts @@ -23,6 +23,8 @@ * (pinned on the real engine, with the real protocol, in * `@objectstack/objectql`'s `protocol-meta-disabled-actions.test.ts`). */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it, vi } from 'vitest'; import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts b/packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts index d3a8c4cd9a0..89b81b58ca8 100644 --- a/packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts +++ b/packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts @@ -147,6 +147,8 @@ // 6. Comment stripper neutered to return '' -> RED in §4 // (non-vacuity: the expected count is ONE, not zero) +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it, vi } from 'vitest'; import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; diff --git a/packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts b/packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts index e70b4eae95c..13725f26d72 100644 --- a/packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts +++ b/packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts @@ -39,6 +39,8 @@ * `saveMetaItem` / `publishMetaItem` / `publishPackageDrafts`, so a harness * that mocks them cannot see it. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update). Imported from `@objectstack/metadata-core`, never from diff --git a/packages/metadata-protocol/src/protocol.package-publish-audit-rows.test.ts b/packages/metadata-protocol/src/protocol.package-publish-audit-rows.test.ts index aec2d407b77..26163de1fed 100644 --- a/packages/metadata-protocol/src/protocol.package-publish-audit-rows.test.ts +++ b/packages/metadata-protocol/src/protocol.package-publish-audit-rows.test.ts @@ -93,6 +93,8 @@ // ADR-0067 D2 in the same breath — nothing promoted, no commit row, no // `allowed` row. See that block's header. +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it, vi } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, imported from // `@objectstack/metadata-core` and NOT from `@objectstack/objectql`: objectql diff --git a/packages/metadata-protocol/src/protocol.packageless-save-address.test.ts b/packages/metadata-protocol/src/protocol.packageless-save-address.test.ts index b61f6ab0b17..7cc10476160 100644 --- a/packages/metadata-protocol/src/protocol.packageless-save-address.test.ts +++ b/packages/metadata-protocol/src/protocol.packageless-save-address.test.ts @@ -63,6 +63,8 @@ * read doors run against it, and its write verbs route through the producer's * own dispatch predicates. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineFindOnePredicate, assertEngineUpdateDispatch, isCodeArtifactBody } from '@objectstack/metadata-core'; import { applyProtection } from '@objectstack/spec/shared'; diff --git a/packages/metadata-protocol/src/protocol.publish-commit-capture-read-failure.test.ts b/packages/metadata-protocol/src/protocol.publish-commit-capture-read-failure.test.ts index 913951e254e..339572451f7 100644 --- a/packages/metadata-protocol/src/protocol.publish-commit-capture-read-failure.test.ts +++ b/packages/metadata-protocol/src/protocol.publish-commit-capture-read-failure.test.ts @@ -54,6 +54,8 @@ * the capture loop is reached the way a real publish reaches it. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, it, expect } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, so the fake engine // cannot accept a call ObjectQL refuses. From `@objectstack/metadata-core`, not diff --git a/packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts b/packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts index c40afc901c4..ec7b859c478 100644 --- a/packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts +++ b/packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts @@ -55,6 +55,8 @@ // this helper calling `syncObjectSchema` with the folded // spelling, rather than passing for want of an assertion) +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, describe, expect, it, vi } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, imported from // `@objectstack/metadata-core` and NOT from `@objectstack/objectql`: objectql diff --git a/packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts b/packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts index a0c56ee71a1..f91cca3ee2a 100644 --- a/packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts +++ b/packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts @@ -75,6 +75,8 @@ // a blanket rule here would break ordinary publishing, and a refusal pin with // no control cannot show the rule is selective. +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, imported from // `@objectstack/metadata-core` and NOT from `@objectstack/objectql`: objectql diff --git a/packages/metadata-protocol/src/protocol.reads-environment-only.test.ts b/packages/metadata-protocol/src/protocol.reads-environment-only.test.ts index 43d80e3dfd9..f7c2dfcceb2 100644 --- a/packages/metadata-protocol/src/protocol.reads-environment-only.test.ts +++ b/packages/metadata-protocol/src/protocol.reads-environment-only.test.ts @@ -24,6 +24,8 @@ * Reverse verification (one-shot, recorded in the PR): removing the sealed arm * from `declinesStoredRow` turns §2 red. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { assertEngineDeleteDispatch, diff --git a/packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts b/packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts index d802c538693..82e93df948c 100644 --- a/packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts +++ b/packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts @@ -47,6 +47,8 @@ * logged" can never pass on a harness that never published at all. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, it, expect, vi, afterEach } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, so the fake engine // cannot accept a call ObjectQL refuses. From `@objectstack/metadata-core`, not diff --git a/packages/metadata-protocol/src/protocol.save-union-issues.test.ts b/packages/metadata-protocol/src/protocol.save-union-issues.test.ts index c9d20663cf6..ab44495dcdb 100644 --- a/packages/metadata-protocol/src/protocol.save-union-issues.test.ts +++ b/packages/metadata-protocol/src/protocol.save-union-issues.test.ts @@ -22,6 +22,8 @@ * `protocol.save-flow-canonicalization.test.ts` — a fix INSIDE `saveMetaItem` * cannot use a harness that mocks `saveMetaItem`. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update), so the fake engine below cannot accept a call ObjectQL diff --git a/packages/metadata-protocol/src/protocol.scoped-list-fallback.test.ts b/packages/metadata-protocol/src/protocol.scoped-list-fallback.test.ts index 6de1306593d..a32faf2b69f 100644 --- a/packages/metadata-protocol/src/protocol.scoped-list-fallback.test.ts +++ b/packages/metadata-protocol/src/protocol.scoped-list-fallback.test.ts @@ -45,6 +45,8 @@ * 7. The lock: where the served row moves to the package-less row, the * scoped slot's lock family still equals `getMetaItem`'s envelope. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; import { assertEngineFindOnePredicate, isCodeArtifactBody } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.seed-external-id-refusal.test.ts b/packages/metadata-protocol/src/protocol.seed-external-id-refusal.test.ts index 88a8e94c8a8..d0b6149fed0 100644 --- a/packages/metadata-protocol/src/protocol.seed-external-id-refusal.test.ts +++ b/packages/metadata-protocol/src/protocol.seed-external-id-refusal.test.ts @@ -35,6 +35,8 @@ * `protocol.unknown-datasource-refusal.test.ts`, kept local, with a registry * that answers registered objects and a log of every data-table insert. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.served-content-hash.test.ts b/packages/metadata-protocol/src/protocol.served-content-hash.test.ts index 4c693143fe0..5f945b0aeae 100644 --- a/packages/metadata-protocol/src/protocol.served-content-hash.test.ts +++ b/packages/metadata-protocol/src/protocol.served-content-hash.test.ts @@ -37,6 +37,8 @@ * it, the write stamps it, and a row WITH a `checksum` is judged as before. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { createHmac } from 'node:crypto'; import { describe, expect, it } from 'vitest'; import { diff --git a/packages/metadata-protocol/src/protocol.stored-conversions.test.ts b/packages/metadata-protocol/src/protocol.stored-conversions.test.ts index fc6ab0f7898..e5bd9dd4dd7 100644 --- a/packages/metadata-protocol/src/protocol.stored-conversions.test.ts +++ b/packages/metadata-protocol/src/protocol.stored-conversions.test.ts @@ -15,6 +15,8 @@ * bypassing `saveMetaItem`'s schema gate, exactly like a real row written * years ago under an older protocol. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update), so the fake engine below cannot accept a call ObjectQL diff --git a/packages/metadata-protocol/src/protocol.summary-rollup-publish-gate.test.ts b/packages/metadata-protocol/src/protocol.summary-rollup-publish-gate.test.ts index 47d0884dc88..8cc3a5330c0 100644 --- a/packages/metadata-protocol/src/protocol.summary-rollup-publish-gate.test.ts +++ b/packages/metadata-protocol/src/protocol.summary-rollup-publish-gate.test.ts @@ -30,6 +30,8 @@ * path is stubbed. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.unknown-datasource-refusal.test.ts b/packages/metadata-protocol/src/protocol.unknown-datasource-refusal.test.ts index 064d75d6670..838473bd606 100644 --- a/packages/metadata-protocol/src/protocol.unknown-datasource-refusal.test.ts +++ b/packages/metadata-protocol/src/protocol.unknown-datasource-refusal.test.ts @@ -32,6 +32,8 @@ * engine's `getDriverByName` over a mutable driver set and a registry that * answers code-layer datasources. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.unknown-grant-object-refusal.test.ts b/packages/metadata-protocol/src/protocol.unknown-grant-object-refusal.test.ts index 602a848b0d9..31bd46448db 100644 --- a/packages/metadata-protocol/src/protocol.unknown-grant-object-refusal.test.ts +++ b/packages/metadata-protocol/src/protocol.unknown-grant-object-refusal.test.ts @@ -33,6 +33,8 @@ * `protocol.unknown-datasource-refusal.test.ts`, kept local, with a registry * that answers `getObject` over a mutable set of registered objects. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; diff --git a/packages/metadata-protocol/src/protocol.view-container-reset-expansions.test.ts b/packages/metadata-protocol/src/protocol.view-container-reset-expansions.test.ts index 281e6153d16..654423204f4 100644 --- a/packages/metadata-protocol/src/protocol.view-container-reset-expansions.test.ts +++ b/packages/metadata-protocol/src/protocol.view-container-reset-expansions.test.ts @@ -73,6 +73,8 @@ * rows, then `loadMetaFromDb`), and that a true orphan is adopted with the * registry following its row at once, its record moved to the new binding. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; import { assertEngineDeleteDispatch, diff --git a/packages/metadata-protocol/src/view-container-runtime-expansion.test.ts b/packages/metadata-protocol/src/view-container-runtime-expansion.test.ts index 62a54119218..2af582d2205 100644 --- a/packages/metadata-protocol/src/view-container-runtime-expansion.test.ts +++ b/packages/metadata-protocol/src/view-container-runtime-expansion.test.ts @@ -27,6 +27,8 @@ * at one leaves the other empty. `hydrateOverlayIntoRegistry` is the single * choke point all three runtime hydration callers share. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, isCodeArtifactBody } from '@objectstack/metadata-core'; import { expandViewContainer, isAggregatedViewContainer, ViewSchema } from '@objectstack/spec/ui'; diff --git a/packages/metadata-protocol/test/lint-root-preload.setup.ts b/packages/metadata-protocol/test/lint-root-preload.setup.ts deleted file mode 100644 index 5633df5b2e9..00000000000 --- a/packages/metadata-protocol/test/lint-root-preload.setup.ts +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. -// -// Pay the first load of the `@objectstack/lint` package ROOT before a test -// file is collected, never inside a clocked case. Wired as `setupFiles` in -// `../vitest.config.ts`. -// -// ## Why -// -// The protocol loads the lint root lazily, on first use, in two places: a -// served `view` read decorates its `_diagnostics` with the form-layout -// warnings (`loadFormLayoutLint`, src/metadata-diagnostics.ts), and an object -// publish runs its build probes (`runBuildProbes`, src/build-probes.ts). The -// lazy shape is product design: the kernel boot path never reaches the root. -// Under vitest the first of those loads is a cold transform of the lint -// module graph, paid inside whichever case makes the file's first such read -// or publish. Here the root resolves to `lint/dist/index.js` (no alias; the -// package's row in `check:test-source-alias` lists it), and one run of the -// whole suite traced that load inside a case in 39 test files. In those -// cases it measured 714 ms p50 idle and 975 ms p50 (1241 ms max) with four -// copies on four cores, against siblings in the tens of milliseconds and the -// 5000 ms `testTimeout`. -// -// ## What it does -// -// It imports the root once, at module top. vitest runs a setup file before it -// collects the test file, and neither `testTimeout` nor `hookTimeout` clocks -// that. The setup file shares the test file's module graph, so the -// protocol's later `import()` gets this instance back. -// -// A setup file rather than a per-file import, because 39 files need it and a -// new test that first reaches a view read or an object publish needs it too, -// without knowing it. The price is that every test file in the package -// evaluates the lint root once during setup, including the files that never -// reach it. -import '@objectstack/lint'; diff --git a/packages/metadata-protocol/vitest.config.ts b/packages/metadata-protocol/vitest.config.ts index 0e9d87c873c..87a915b5e69 100644 --- a/packages/metadata-protocol/vitest.config.ts +++ b/packages/metadata-protocol/vitest.config.ts @@ -1,6 +1,6 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. -// This config exists for exactly three settings; everything else stays on +// This config exists for exactly two settings; everything else stays on // vitest's defaults, deliberately — a key added here re-specifies behaviour // for every test file in the package (packages/cli/vitest.config.ts's header // records the incident that taught that). @@ -16,11 +16,6 @@ export default defineConfig({ // Mechanism + measured costs: examples/app-showcase/vitest.config.ts. // Enforced repo-wide by scripts/check-console-intercept-disarm.mjs. disableConsoleIntercept: true, - // [#29627] The lint package root's first load is paid here, before a test - // file is collected, never inside the case that makes the file's first - // served view read or object publish. The module's header carries the - // measurement and the price. - setupFiles: ['./test/lint-root-preload.setup.ts'], }, resolve: { alias: [ diff --git a/packages/objectql/package.json b/packages/objectql/package.json index 7dd6ed43184..89b2a089584 100644 --- a/packages/objectql/package.json +++ b/packages/objectql/package.json @@ -50,6 +50,7 @@ "zod": "^4.6.6" }, "devDependencies": { + "@objectstack/lint": "workspace:*", "tsx": "^4.23.15", "typescript": "^6.0.3", "vitest": "^4.1.11" diff --git a/packages/objectql/src/__fixtures__/lint-root-preload.testkit.ts b/packages/objectql/src/__fixtures__/lint-root-preload.testkit.ts new file mode 100644 index 00000000000..5be5023e1ca --- /dev/null +++ b/packages/objectql/src/__fixtures__/lint-root-preload.testkit.ts @@ -0,0 +1,31 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. +// +// Import this at module top in a test file whose cases make a served `view` +// read or an object publish, so the file pays the first load of the +// `@objectstack/lint` package ROOT during collection, never inside a clocked +// case. Test-only: no product module imports it. +// +// `@objectstack/metadata-protocol` loads the lint root lazily, on first use, +// in two places: a served `view` read decorates its `_diagnostics` with the +// form-layout warnings (`loadFormLayoutLint`, metadata-diagnostics.ts), and an +// object publish runs its build probes (`runBuildProbes`, build-probes.ts). +// The lazy shape is product design: the kernel boot path never reaches the +// root. Under vitest the first such load in a file is a cold transform of the +// lint module graph, paid inside whichever case gets there first. A +// module-top import is paid during collection, which neither `testTimeout` +// nor `hookTimeout` clocks, and the protocol's later `import()` gets the same +// module instance back (AGENTS.md § Build & Test, the clocked-window rule). +// +// Here the root resolves through the anchored `^@objectstack/lint$` alias in +// `../../vitest.config.ts` to lint's source, and so does the protocol's own +// `import()` from its `dist/`. `@objectstack/lint` is a devDependency of this +// package for this import alone. The file sits under `__fixtures__` because +// it is test code, not a module of the package. +// +// Who imports it: every test file in which a traced run of the whole suite +// saw that first load inside a case. A new file whose cases reach a view read +// or an object publish imports it too. It is not a `setupFiles` entry, +// because then every test file in the package pays the load, including the +// many that never reach it: measured, that added about 330 s of worker time +// and about a third to the suite's wall time. +import '@objectstack/lint'; diff --git a/packages/objectql/src/build-probes.test.ts b/packages/objectql/src/build-probes.test.ts index 059290cd96a..ff6bd9e7707 100644 --- a/packages/objectql/src/build-probes.test.ts +++ b/packages/objectql/src/build-probes.test.ts @@ -1,5 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, it, expect, vi } from 'vitest'; import { runBuildProbes, type ProbeEngine } from '@objectstack/metadata-protocol'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; diff --git a/packages/objectql/src/environment-overlay-five-types.test.ts b/packages/objectql/src/environment-overlay-five-types.test.ts index cc42e9e511c..95f1e942a2c 100644 --- a/packages/objectql/src/environment-overlay-five-types.test.ts +++ b/packages/objectql/src/environment-overlay-five-types.test.ts @@ -93,6 +93,8 @@ * per-organization path — and it hands this same request to `saveMetaItem`. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import type { ServiceObject } from '@objectstack/spec/data'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; diff --git a/packages/objectql/src/package-disable-enforcement.test.ts b/packages/objectql/src/package-disable-enforcement.test.ts index 83f9c86ce19..f1198ae52f0 100644 --- a/packages/objectql/src/package-disable-enforcement.test.ts +++ b/packages/objectql/src/package-disable-enforcement.test.ts @@ -1,5 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, it, expect } from 'vitest'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { buildEffectiveObjectPermissions, registryObjectPackageDisabled } from '@objectstack/core'; diff --git a/packages/objectql/src/protocol-commit-history.test.ts b/packages/objectql/src/protocol-commit-history.test.ts index 41bca2fea8e..f3797902df4 100644 --- a/packages/objectql/src/protocol-commit-history.test.ts +++ b/packages/objectql/src/protocol-commit-history.test.ts @@ -1,5 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, it, expect, vi } from 'vitest'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { SchemaRegistry } from './registry.js'; diff --git a/packages/objectql/src/protocol-custom-override-bare-slot-envelope.test.ts b/packages/objectql/src/protocol-custom-override-bare-slot-envelope.test.ts index 48986a84724..c05ad657726 100644 --- a/packages/objectql/src/protocol-custom-override-bare-slot-envelope.test.ts +++ b/packages/objectql/src/protocol-custom-override-bare-slot-envelope.test.ts @@ -36,6 +36,8 @@ * Test-path resolution: the protocol is `@objectstack/metadata-protocol`'s * `dist/`, so a change there is read here after that package is rebuilt. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import type { ServiceObject } from '@objectstack/spec/data'; import { ObjectStackProtocolImplementation, resetEnvWritableMetadataTypes } from '@objectstack/metadata-protocol'; diff --git a/packages/objectql/src/protocol-custom-override-effective-read.test.ts b/packages/objectql/src/protocol-custom-override-effective-read.test.ts index 88180e0429d..0f2815cd811 100644 --- a/packages/objectql/src/protocol-custom-override-effective-read.test.ts +++ b/packages/objectql/src/protocol-custom-override-effective-read.test.ts @@ -34,6 +34,8 @@ * Test-path resolution: the protocol is `@objectstack/metadata-protocol`'s * `dist/`, so a change there is read here after that package is rebuilt. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import type { ServiceObject } from '@objectstack/spec/data'; import { diff --git a/packages/objectql/src/protocol-duplicate-package-names-in-step.test.ts b/packages/objectql/src/protocol-duplicate-package-names-in-step.test.ts index 9b4cd034afb..ffeff54bb2a 100644 --- a/packages/objectql/src/protocol-duplicate-package-names-in-step.test.ts +++ b/packages/objectql/src/protocol-duplicate-package-names-in-step.test.ts @@ -49,6 +49,8 @@ * protocol's own `getMetaItem` — never a payload. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { randomUUID } from 'node:crypto'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { ObjectKernel } from '@objectstack/core'; diff --git a/packages/objectql/src/protocol-lock-artifact-package-axis.test.ts b/packages/objectql/src/protocol-lock-artifact-package-axis.test.ts index ed32bc7912e..d856a44bfe6 100644 --- a/packages/objectql/src/protocol-lock-artifact-package-axis.test.ts +++ b/packages/objectql/src/protocol-lock-artifact-package-axis.test.ts @@ -24,6 +24,8 @@ * `'no-overlay'`: the door refused the save under one order and the delete * under the other, and refuses both under both orders now. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, expect, it } from 'vitest'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; diff --git a/packages/objectql/src/protocol-meta.test.ts b/packages/objectql/src/protocol-meta.test.ts index 311dd51baf0..a3e93c2a744 100644 --- a/packages/objectql/src/protocol-meta.test.ts +++ b/packages/objectql/src/protocol-meta.test.ts @@ -1,5 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { ObjectStackProtocolImplementation, resetEnvWritableMetadataTypes } from '@objectstack/metadata-protocol'; import { SchemaRegistry } from './registry.js'; diff --git a/packages/objectql/src/protocol-packaged-view-base.test.ts b/packages/objectql/src/protocol-packaged-view-base.test.ts index 98d24eb7fc9..49204473ebf 100644 --- a/packages/objectql/src/protocol-packaged-view-base.test.ts +++ b/packages/objectql/src/protocol-packaged-view-base.test.ts @@ -26,6 +26,8 @@ * name, which is the identity the base is looked up by. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, it, expect } from 'vitest'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { translateView, type TranslationBundle } from '@objectstack/spec/system'; diff --git a/packages/objectql/src/protocol-packageless-overlay-migration.test.ts b/packages/objectql/src/protocol-packageless-overlay-migration.test.ts index d9e7775860c..457a94a5bdb 100644 --- a/packages/objectql/src/protocol-packageless-overlay-migration.test.ts +++ b/packages/objectql/src/protocol-packageless-overlay-migration.test.ts @@ -34,6 +34,8 @@ * Test-path resolution: the protocol is `@objectstack/metadata-protocol`'s * `dist/`, so a change there is read here after that package is rebuilt. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import type { ServiceObject } from '@objectstack/spec/data'; import { diff --git a/packages/objectql/src/protocol-packageless-read-dependent-package.test.ts b/packages/objectql/src/protocol-packageless-read-dependent-package.test.ts index 3f41fb1bcd7..381b2e5037a 100644 --- a/packages/objectql/src/protocol-packageless-read-dependent-package.test.ts +++ b/packages/objectql/src/protocol-packageless-read-dependent-package.test.ts @@ -39,6 +39,8 @@ * Test-path resolution: the protocol is `@objectstack/metadata-protocol`'s * `dist/`, so a change there is read here after that package is rebuilt. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import type { ServiceObject } from '@objectstack/spec/data'; import { ObjectStackProtocolImplementation, resetEnvWritableMetadataTypes } from '@objectstack/metadata-protocol'; diff --git a/packages/objectql/src/protocol-publish-package-drafts.test.ts b/packages/objectql/src/protocol-publish-package-drafts.test.ts index 0646a4ed615..fe2a8b7608f 100644 --- a/packages/objectql/src/protocol-publish-package-drafts.test.ts +++ b/packages/objectql/src/protocol-publish-package-drafts.test.ts @@ -1,5 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, it, expect, vi } from 'vitest'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { assertEngineUpdateDispatch } from './engine-update-dispatch.js'; diff --git a/packages/objectql/src/protocol-publish-rollback.test.ts b/packages/objectql/src/protocol-publish-rollback.test.ts index eac76f3eeca..9ee428df858 100644 --- a/packages/objectql/src/protocol-publish-rollback.test.ts +++ b/packages/objectql/src/protocol-publish-rollback.test.ts @@ -1,5 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, expect, it, vi } from 'vitest'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { assertEngineDeleteDispatch } from './engine-delete-dispatch.js'; diff --git a/packages/objectql/src/protocol-revert-org-scope.test.ts b/packages/objectql/src/protocol-revert-org-scope.test.ts index d68c41437ff..3d2fb419545 100644 --- a/packages/objectql/src/protocol-revert-org-scope.test.ts +++ b/packages/objectql/src/protocol-revert-org-scope.test.ts @@ -47,6 +47,8 @@ * writer/reader key agreement it pins is the env-wide one. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, it, expect, beforeEach } from 'vitest'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import type { ServiceObject } from '@objectstack/spec/data'; diff --git a/packages/objectql/src/publish-package-drafts-backfill.test.ts b/packages/objectql/src/publish-package-drafts-backfill.test.ts index 5437d9d7138..5759ad311ed 100644 --- a/packages/objectql/src/publish-package-drafts-backfill.test.ts +++ b/packages/objectql/src/publish-package-drafts-backfill.test.ts @@ -16,6 +16,8 @@ * authored hooks use (`registerMiddleware`, `registerHook`), so what they pin * is that the write reaches them carrying the caller's context. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, it, expect } from 'vitest'; import type { ServiceObject } from '@objectstack/spec/data'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; diff --git a/packages/objectql/src/publish-package-drafts-response-conformance.test.ts b/packages/objectql/src/publish-package-drafts-response-conformance.test.ts index 690f0426fdd..607b4168a55 100644 --- a/packages/objectql/src/publish-package-drafts-response-conformance.test.ts +++ b/packages/objectql/src/publish-package-drafts-response-conformance.test.ts @@ -28,6 +28,8 @@ * upgrade only when a consumer needs a field) — so the cases here assert it is * carried through unstripped, never its inner shape. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, it, expect } from 'vitest'; import type { ServiceObject } from '@objectstack/spec/data'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; diff --git a/packages/objectql/src/publish-package-drafts-seed-receipt-counts.test.ts b/packages/objectql/src/publish-package-drafts-seed-receipt-counts.test.ts index 43ed0ac43b1..b95b482cd02 100644 --- a/packages/objectql/src/publish-package-drafts-seed-receipt-counts.test.ts +++ b/packages/objectql/src/publish-package-drafts-seed-receipt-counts.test.ts @@ -24,6 +24,8 @@ * `publish-package-drafts-response-conformance.test.ts` — copied, not shared, * so each file can fail on its own; this package carries no sqlite driver). */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './__fixtures__/lint-root-preload.testkit.js'; import { describe, it, expect } from 'vitest'; import type { ServiceObject } from '@objectstack/spec/data'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; diff --git a/packages/objectql/test/lint-root-preload.setup.ts b/packages/objectql/test/lint-root-preload.setup.ts deleted file mode 100644 index 2df5b18954d..00000000000 --- a/packages/objectql/test/lint-root-preload.setup.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. -// -// Pay the first load of the `@objectstack/lint` package ROOT before a test -// file is collected, never inside a clocked case. Wired as `setupFiles` in -// both projects of `../vitest.config.ts`. -// -// ## Why -// -// `@objectstack/metadata-protocol` loads the lint root lazily, on first use, -// in two places: a served `view` read decorates its `_diagnostics` with the -// form-layout warnings (`loadFormLayoutLint`, metadata-diagnostics.ts), and an -// object publish runs its build probes (`runBuildProbes`, build-probes.ts). -// The lazy shape is product design: the kernel boot path never reaches the -// root. Under vitest the first of those loads is a cold transform of the lint -// module graph, paid inside whichever case makes the file's first such read -// or publish. One run of the whole suite traced that load inside a case in -// 18 test files of this package. In those cases it measured 796 ms p50 idle -// and 1169 ms p50 (2282 ms max) with four copies on four cores, against -// siblings in the tens of milliseconds and the 5000 ms `testTimeout`. -// -// ## What it does -// -// It imports the root once, at module top. vitest runs a setup file before it -// collects the test file, and neither `testTimeout` nor `hookTimeout` clocks -// that. The setup file shares the test file's module graph, so the -// protocol's later `import()` gets this instance back. This package does not -// depend on `@objectstack/lint`, so the specifier resolves through the -// anchored alias in `../vitest.config.ts`, which sends the protocol's own -// `import()` to the same source file. -// -// A setup file rather than a per-file import, because 18 files need it and a -// new test that first reaches a view read or an object publish needs it too, -// without knowing it. The price is that every test file in the package -// evaluates the lint root once during setup, including the files that never -// reach it. -import '@objectstack/lint'; diff --git a/packages/objectql/vitest.config.ts b/packages/objectql/vitest.config.ts index 8fb737ef0b3..222fef71084 100644 --- a/packages/objectql/vitest.config.ts +++ b/packages/objectql/vitest.config.ts @@ -104,22 +104,16 @@ runProjectCliOverridePreflight({ parse: parseCLI, }); -// [#29627] The lint package root's first load is paid in this setup file, -// before a test file is collected, never inside the case that makes the -// file's first served view read or object publish. Named in EACH project -// below, like the per-project settings beside it. The module's header carries -// the measurement and the price. -const LINT_ROOT_PRELOAD = './test/lint-root-preload.setup.ts'; - export default defineConfig({ resolve: { alias: [ - // [#29627] The package root the preload above imports. This package does - // not depend on `@objectstack/lint`; metadata-protocol does, and loads - // the root lazily from its `dist/`. Anchored, so lint's published - // subpaths are not swallowed by the file replacement, and pointed at - // source, so the preload and the protocol's own `import()` resolve to - // the same module, the lint rule in the checkout, never `dist/`. + // [#29627] The lint package ROOT, which `src/__fixtures__/lint-root-preload.testkit.ts` + // imports at module top so a test file pays its first load during + // collection. metadata-protocol loads the same root lazily from its + // `dist/` on a served view read or an object publish; through this + // entry both resolve to one module, the lint rule in the checkout, + // never `dist/`. Anchored, so lint's published subpaths are not + // swallowed by the file replacement. { find: /^@objectstack\/lint$/, replacement: path.resolve(__dirname, '../lint/src/index.ts') }, ], }, @@ -142,7 +136,6 @@ export default defineConfig({ // block. A ROOT-level value is inert for a project run, so it is // declared here as well (scripts/check-console-intercept-disarm.mjs). disableConsoleIntercept: true, - setupFiles: [LINT_ROOT_PRELOAD], }, }, { @@ -158,7 +151,6 @@ export default defineConfig({ // block. A ROOT-level value is inert for a project run, so it is // declared here as well (scripts/check-console-intercept-disarm.mjs). disableConsoleIntercept: true, - setupFiles: [LINT_ROOT_PRELOAD], }, }, ], diff --git a/packages/runtime/src/domains/meta-app-package-id-key.test.ts b/packages/runtime/src/domains/meta-app-package-id-key.test.ts index 1e014390d73..6275bbcb89f 100644 --- a/packages/runtime/src/domains/meta-app-package-id-key.test.ts +++ b/packages/runtime/src/domains/meta-app-package-id-key.test.ts @@ -41,6 +41,8 @@ * theirs: the last rows pin them by name, as before the arm. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import '../lint-root-preload.testkit.js'; import { describe, it, expect, afterEach } from 'vitest'; import { ObjectQL } from '@objectstack/objectql'; import { SqlDriver } from '@objectstack/driver-sql'; diff --git a/packages/runtime/src/domains/meta-package-all-fold.test.ts b/packages/runtime/src/domains/meta-package-all-fold.test.ts index e4af885bea5..85c5f18d56a 100644 --- a/packages/runtime/src/domains/meta-package-all-fold.test.ts +++ b/packages/runtime/src/domains/meta-package-all-fold.test.ts @@ -43,6 +43,8 @@ * a real package id still scopes. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import '../lint-root-preload.testkit.js'; import { describe, it, expect, afterEach } from 'vitest'; import { ObjectQL } from '@objectstack/objectql'; import { SqlDriver } from '@objectstack/driver-sql'; diff --git a/packages/runtime/src/domains/meta-published-runtime-publish.test.ts b/packages/runtime/src/domains/meta-published-runtime-publish.test.ts index 4be494de971..727ec456c79 100644 --- a/packages/runtime/src/domains/meta-published-runtime-publish.test.ts +++ b/packages/runtime/src/domains/meta-published-runtime-publish.test.ts @@ -26,6 +26,8 @@ * so what they measure is the wiring, not a stub's opinion of it. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import '../lint-root-preload.testkit.js'; import { describe, it, expect } from 'vitest'; // The producer's OWN write-verb dispatch decisions, so the fake engine below // cannot accept a call ObjectQL itself would refuse — a double looser than the diff --git a/packages/runtime/src/domains/meta-save-preconditions-parity.test.ts b/packages/runtime/src/domains/meta-save-preconditions-parity.test.ts index 9f424b40800..1be13c9e1eb 100644 --- a/packages/runtime/src/domains/meta-save-preconditions-parity.test.ts +++ b/packages/runtime/src/domains/meta-save-preconditions-parity.test.ts @@ -47,6 +47,8 @@ * the store, never the envelope's bytes. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import '../lint-root-preload.testkit.js'; import { describe, it, expect, vi, afterEach } from 'vitest'; import { ObjectQL } from '@objectstack/objectql'; import { SqlDriver } from '@objectstack/driver-sql'; diff --git a/packages/runtime/src/domains/packages-export-base-unchanged-per-type.integration.test.ts b/packages/runtime/src/domains/packages-export-base-unchanged-per-type.integration.test.ts index 056fa476f87..6bde275945e 100644 --- a/packages/runtime/src/domains/packages-export-base-unchanged-per-type.integration.test.ts +++ b/packages/runtime/src/domains/packages-export-base-unchanged-per-type.integration.test.ts @@ -20,6 +20,8 @@ * the base export's answer is the export's own and not an unpublished * override. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import '../lint-root-preload.testkit.js'; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; diff --git a/packages/runtime/src/domains/packages-publish-drafts-change-note.integration.test.ts b/packages/runtime/src/domains/packages-publish-drafts-change-note.integration.test.ts index caeaf293dea..132ce04096b 100644 --- a/packages/runtime/src/domains/packages-publish-drafts-change-note.integration.test.ts +++ b/packages/runtime/src/domains/packages-publish-drafts-change-note.integration.test.ts @@ -17,6 +17,8 @@ * back are what landed in SQLite, not what a double was told. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import '../lint-root-preload.testkit.js'; import { afterEach, describe, expect, it } from 'vitest'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; diff --git a/packages/runtime/src/lint-root-preload.testkit.ts b/packages/runtime/src/lint-root-preload.testkit.ts new file mode 100644 index 00000000000..3d971474622 --- /dev/null +++ b/packages/runtime/src/lint-root-preload.testkit.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. +// +// Import this at module top in a test file whose cases make a served `view` +// read or an object publish, so the file pays the first load of the +// `@objectstack/lint` package ROOT during collection, never inside a clocked +// case. Test-only: no product module imports it. +// +// `@objectstack/metadata-protocol` loads the lint root lazily, on first use, +// in two places: a served `view` read decorates its `_diagnostics` with the +// form-layout warnings (`loadFormLayoutLint`, metadata-diagnostics.ts), and an +// object publish runs its build probes (`runBuildProbes`, build-probes.ts). +// The lazy shape is product design: the kernel boot path never reaches the +// root. Under vitest the first such load in a file is a cold transform of the +// lint module graph, paid inside whichever case gets there first. A +// module-top import is paid during collection, which neither `testTimeout` +// nor `hookTimeout` clocks, and the protocol's later `import()` gets the same +// module instance back (AGENTS.md § Build & Test, the clocked-window rule). +// +// Here the root resolves through the anchored `^@objectstack/lint$` alias in +// `../vitest.config.ts` to lint's source, and so does the protocol's own +// `import()` from its `dist/`. That source graph loads faster than the built +// bundle the two sibling packages read, but it still lands in the case. +// +// Who imports it: every test file in which a traced run of the whole suite +// saw that first load inside a case. A new file whose cases reach a view read +// or an object publish imports it too. It is not a `setupFiles` entry, +// because then every test file in the package pays the load, including the +// many that never reach it: measured, that added about 200 s of worker time +// and about an eighth to the suite's wall time. +import '@objectstack/lint'; diff --git a/packages/runtime/src/package-list-commits-org-scope.integration.test.ts b/packages/runtime/src/package-list-commits-org-scope.integration.test.ts index 74235bc983b..9d63b1aa251 100644 --- a/packages/runtime/src/package-list-commits-org-scope.integration.test.ts +++ b/packages/runtime/src/package-list-commits-org-scope.integration.test.ts @@ -5,6 +5,8 @@ // the rows stored env-wide, hiding whole stretches of a package's ADR-0067 // timeline from any session that had an active organization. +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, it, expect, afterEach } from 'vitest'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; diff --git a/packages/runtime/src/package-promote-runtime-owner-refusal.integration.test.ts b/packages/runtime/src/package-promote-runtime-owner-refusal.integration.test.ts index 781ef8d1f9f..d2528cfc8a4 100644 --- a/packages/runtime/src/package-promote-runtime-owner-refusal.integration.test.ts +++ b/packages/runtime/src/package-promote-runtime-owner-refusal.integration.test.ts @@ -57,6 +57,8 @@ * rebuild it before reading a result here after touching `protocol.ts`. */ +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, it, expect, beforeAll, afterAll, afterEach, beforeEach, vi } from 'vitest'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; diff --git a/packages/runtime/src/package-revert-stored-members.integration.test.ts b/packages/runtime/src/package-revert-stored-members.integration.test.ts index 069959aa627..eece7a0da82 100644 --- a/packages/runtime/src/package-revert-stored-members.integration.test.ts +++ b/packages/runtime/src/package-revert-stored-members.integration.test.ts @@ -4,6 +4,8 @@ // Studio-authored package answered 404 "No metadata items found" while the // package held a published item and a pending draft. +// Pays the lint root's first load at collection, never inside a clocked case. +import './lint-root-preload.testkit.js'; import { describe, it, expect, afterEach } from 'vitest'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; diff --git a/packages/runtime/test/lint-root-preload.setup.ts b/packages/runtime/test/lint-root-preload.setup.ts deleted file mode 100644 index 4ad621d52d8..00000000000 --- a/packages/runtime/test/lint-root-preload.setup.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. -// -// Pay the first load of the `@objectstack/lint` package ROOT before a test -// file is collected, never inside a clocked case. Wired as `setupFiles` in -// both projects of `../vitest.config.ts`. -// -// ## Why -// -// `@objectstack/metadata-protocol` loads the lint root lazily, on first use, -// in two places: a served `view` read decorates its `_diagnostics` with the -// form-layout warnings (`loadFormLayoutLint`, metadata-diagnostics.ts), and an -// object publish runs its build probes (`runBuildProbes`, build-probes.ts). -// The lazy shape is product design: the kernel boot path never reaches the -// root. Under vitest the first of those loads is a cold transform of the lint -// module graph, paid inside whichever case makes the file's first such read -// or publish. One run of the whole suite traced that load inside a case in 9 -// test files of this package. Here the root resolves through the anchored -// `^@objectstack/lint$` alias to lint's source, which costs less than the -// built bundle the two sibling packages load, but it still lands in the case. -// -// ## What it does -// -// It imports the root once, at module top. vitest runs a setup file before it -// collects the test file, and neither `testTimeout` nor `hookTimeout` clocks -// that. The setup file shares the test file's module graph, so the -// protocol's later `import()` gets this instance back. -// -// A setup file rather than a per-file import, because 9 files need it and a -// new test that first reaches a view read or an object publish needs it too, -// without knowing it. `meta-write-org-scope.test.ts` keeps its own module-top -// import, which now finds the module already loaded. The price is that every -// test file in the package evaluates the lint root once during setup, -// including the files that never reach it. -import '@objectstack/lint'; diff --git a/packages/runtime/vitest.config.ts b/packages/runtime/vitest.config.ts index 5c48eb0f11c..0ee264cb0d3 100644 --- a/packages/runtime/vitest.config.ts +++ b/packages/runtime/vitest.config.ts @@ -98,13 +98,6 @@ runProjectCliOverridePreflight({ parse: parseCLI, }); -// [#29627] The lint package root's first load is paid in this setup file, -// before a test file is collected, never inside the case that makes the -// file's first served view read or object publish. Named in EACH project -// below, like the per-project settings beside it. The module's header carries -// the measurement and the price. -const LINT_ROOT_PRELOAD = './test/lint-root-preload.setup.ts'; - export default defineConfig({ resolve: { // ARRAY form, not the object form: only the array form accepts a RegExp @@ -161,8 +154,8 @@ export default defineConfig({ { find: /^@objectstack\/lint\/runtime$/, replacement: path.resolve(__dirname, '../lint/src/runtime.ts') }, // [#29603] The package root: a served view read imports it lazily for the // form-layout warnings (metadata-protocol's `loadFormLayoutLint`), and - // `meta-write-org-scope.test.ts` pays that load at module top, as every - // test file does through `LINT_ROOT_PRELOAD` (#29627). All of them + // `meta-write-org-scope.test.ts` pays that load at module top, as the files + // importing `src/lint-root-preload.testkit.ts` do (#29627). All of them // resolve here, so the read judges the lint rule in the checkout, never `dist/`. { find: /^@objectstack\/lint$/, replacement: path.resolve(__dirname, '../lint/src/index.ts') }, // The MCP bridge (`domains/mcp.ts`) reads "this field declares options" @@ -280,7 +273,6 @@ export default defineConfig({ // block. A ROOT-level value is inert for a project run, so it is // declared here as well (scripts/check-console-intercept-disarm.mjs). disableConsoleIntercept: true, - setupFiles: [LINT_ROOT_PRELOAD], }, }, { @@ -299,7 +291,6 @@ export default defineConfig({ // block. A ROOT-level value is inert for a project run, so it is // declared here as well (scripts/check-console-intercept-disarm.mjs). disableConsoleIntercept: true, - setupFiles: [LINT_ROOT_PRELOAD], }, }, ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e51617f1d1d..646a817f63d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1573,6 +1573,9 @@ importers: specifier: ^4.6.6 version: 4.6.7 devDependencies: + '@objectstack/lint': + specifier: workspace:* + version: link:../lint tsx: specifier: ^4.23.15 version: 4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6)