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 2172bf90cad..db3dd3284b6 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, hashSpec } 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/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 852f435a184..b0a3b0428a3 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 @@ -70,6 +70,8 @@ * `environment-overlay-email-locale-restart.test.ts`'s: the hydration * `ObjectQLPlugin.start()` runs, then the pair. */ +// 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/vitest.config.ts b/packages/objectql/vitest.config.ts index 5f83f66abf7..222fef71084 100644 --- a/packages/objectql/vitest.config.ts +++ b/packages/objectql/vitest.config.ts @@ -105,6 +105,18 @@ runProjectCliOverridePreflight({ }); export default defineConfig({ + resolve: { + alias: [ + // [#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') }, + ], + }, 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 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/vitest.config.ts b/packages/runtime/vitest.config.ts index 8e78e65c9d2..0ee264cb0d3 100644 --- a/packages/runtime/vitest.config.ts +++ b/packages/runtime/vitest.config.ts @@ -154,7 +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. Both + // `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" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 703a93cb753..d4092ee479a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1570,6 +1570,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)