Skip to content

Commit a9b959a

Browse files
test(runtime): pay the view read's lint load at module top in meta-write-org-scope (#29622)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9V2uHgH5HekdBdXA8rYk895)_ Fixes #29603 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:只改测试。`meta-write-org-scope.test.ts` 在文件顶部预先加载 `@objectstack/lint`,runtime 的 vitest 配置把这个包根指到源码。 - 为什么改:那个 ⭐ D6 用例里的视图读取会第一次懒加载 lint 包,vitest 下冷编译要 1.2 秒(最慢 1.7 秒),全算进用例的 5 秒超时里,分片负载一高就超时变红。现在这笔开销在收集阶段付掉,用例本身降到约 20 毫秒。 - 风险与回滚:不动产品代码,不加超时,不重试,断言一条没改。回滚即还原本 PR。 - 先看:`packages/runtime/src/meta-write-org-scope.test.ts`(顶部那条 import 和注释),`packages/runtime/vitest.config.ts`(新的 `@objectstack/lint` 别名)。 - 两分钟验证:`pnpm --filter @objectstack/runtime exec vitest run --project local src/meta-write-org-scope.test.ts --reporter=verbose`,预期 20 个用例全绿,⭐ D6 用例几十毫秒(main 上约 1.2 秒)。 - 需要你做的:无,席位落地。 ## What changes - `packages/runtime/src/meta-write-org-scope.test.ts`: a module-top `import '@objectstack/lint'`, with a comment naming the cost it moves. No case, assertion or timeout changes. - `packages/runtime/vitest.config.ts`: one anchored alias, `^@objectstack/lint$` → `../lint/src/index.ts`, beside the existing `/runtime` entry. `check:test-source-alias` requires it for the new import (its remedy text, verbatim shape), and it is the same entry metadata-protocol's lazy import resolves through, so the view read now judges the lint rule in the checkout rather than `dist/`. ## Why The failure on `1a95c746` (run 41514567522, Test Core 3/8, job 125515971369) was a **timeout**, not an assertion: `Error: Test timed out in 5000ms.` at `meta-write-org-scope.test.ts:419:5`; the file ran 5978 ms that run. Where the time goes, measured by timing the case's steps in a scratch copy (deleted): `makeStack` 0 ms, the `PUT /view/…` 19 ms, the **first** `GET /view/…` 1500 ms, a second identical GET 2 ms. The first GET is the first view read in the file. A served view read decorates `_diagnostics` with the lint's form-layout warnings (`withFormLayoutWarnings`, called from `getMetaItem`'s `serveFormLayout`), and that helper imports the lint package root lazily on first use: `loadFormLayoutLint`, `packages/metadata-protocol/src/metadata-diagnostics.ts:481`. Under vitest that first import is a cold transform of the lint module graph, inside the clocked case. The sibling plural-spelling case does the same PUT but no GET, which is why it runs in 3 ms. The lazy import is deliberate product design (the kernel boot path reaches `@objectstack/lint` only through `/runtime`), so the product code stays as it is. The fix is the repo's clocked-window rule (AGENTS.md § Build & Test; `scripts/check-test-source-alias.mjs`): pay the first load at module top, during collection, which no timeout clocks. The protocol's later `import()` gets the same module instance back (measured: the first GET drops from 1500 ms to 7 ms). `check:test-source-alias` cannot see this load on its own, because it happens in another package's code. ## Tests Duration of the ⭐ case and the file (sum of its 20 case durations), `--maxWorkers=1`, one vitest process per run, under `os-verify-lock.sh` (shared-box seconds): | | ⭐ case p50 / max | file total p50 / max | slowest sibling (max) | |---|---|---|---| | before, `2da42e74`, 20 runs | 1202 / 1740 ms | 1433 / 1939 ms | 130 ms | | before, 4 concurrent copies on 4 cores | 1463 / 1654 ms | 1695 / 1862 ms | 62 ms | | after, `867f4777`, 20 runs | 20 / 32 ms | 218 / 268 ms | 72 ms | | after, 4 concurrent copies on 4 cores | 20 / 30 ms | 283 / 329 ms | 95 ms | The case now runs at sibling speed. The load moved into collection (the run's `import` phase), which `testTimeout` does not clock. Forced test edits: none. No assertion, timeout or case moved; the case still proves the overlay-enabled `view` lands env-wide and the read serves it. <details><summary>Runtime suite on the final head</summary> `pnpm --filter @objectstack/runtime test` (vitest project `local`) on `867f4777`, under the lock: `Test Files 528 passed (528)`, `Tests 8506 passed | 19 skipped (8525)`, VERDICT command-exit 0. The same suite on the first head `da6bf162` (preload only): 528/528, 8506 passed. </details> ## Verification Head `867f4777` (branch merged with `origin/main` `81cd9eb0e` before the PR). - `node scripts/pm/dispatch-gates.mjs --commands` on `867f4777`: 50 commands (2 paths, 19 changed lines). All 50 ran on this head, each exit 0. `--ran` reconciliation: `50 derived, 50 run, 0 NOT-MEASURED, 0 UNRUN`. - On the first head, `check:test-source-alias` was red (`NEW unaliased artifact import(s) … @objectstack/lint`); the alias in `vitest.config.ts` is its remedy, green on this head (`120 packages with tests scanned`). - `pnpm --filter @objectstack/runtime typecheck`: exit 0 (`check:test-typecheck: OK`, debt ledger held). - Lint: root eslint over `packages/runtime` with `--no-inline-config` (the package has no lint script): exit 0. - No ablation: the diff changes no tested behaviour. The before/after table above is the reverse reading: without the preload the case pays the 1.2 s load, with it 20 ms. - Dogfood: n/a (no `!`, no `(narrowing)` arm). - CI check-runs: not read yet; the seat reads them at ACCEPT. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (not touched) - [x] 2. Every name I added uses the casing for its kind. (no new names) - [x] 3. No formatter ran; `pnpm lint` passes. (eslint on `packages/runtime`, exit 0) - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset. (test-only: `skip-changeset`) - [x] 6. `packages/spec` touched: n/a. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English (the 维护者速读 section is Chinese by repo rule). --- _Generated by [Claude Code](https://claude.ai/code/session_01ADHkmRczGnhouFCUs9DfQ2)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent fa07650 commit a9b959a

2 files changed

Lines changed: 19 additions & 0 deletions

File tree

‎packages/runtime/src/meta-write-org-scope.test.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,20 @@ import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protoco
8282
// quantified over the CONTRACT rather than over a hand-copied specimen list
8383
// (Prime Directive #8): a future spelling limb arrives inside the quantifier.
8484
import { META_URL_TO_SINGULAR, canonicalMetaUrlType } from '@objectstack/spec/shared';
85+
// [#29603] Pay the first load of `@objectstack/lint` at MODULE LOAD, never
86+
// inside a clocked case. A served VIEW read adds the lint's form-layout
87+
// warnings to `_diagnostics` (`withFormLayoutWarnings`), and the protocol
88+
// imports that package root lazily, on the first view read of the process
89+
// (`loadFormLayoutLint`, packages/metadata-protocol/src/metadata-diagnostics.ts).
90+
// Under vitest that first import is a cold transform of the lint package's
91+
// whole module graph: measured 1.2 s p50 / 1.7 s max idle, all of it inside the
92+
// GET of the ⭐ D6 case below, the first view read in this file. A shard under
93+
// load stretched it past the 5000 ms `testTimeout` once. The protocol's later
94+
// `import()` gets this same module instance back, so with it paid here the
95+
// case times the write and the read, not the compiler (the clocked-window rule,
96+
// `scripts/check-test-source-alias.mjs`; that gate cannot see this load,
97+
// because it happens in another package's code). The read still runs the rule.
98+
import '@objectstack/lint';
8599
import { HttpDispatcher } from './http-dispatcher.js';
86100
import type { HttpDispatcherResult } from './http-dispatcher.js';
87101

‎packages/runtime/vitest.config.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ export default defineConfig({
152152
// dependency closure with `resolvePackageClosure`, the one walk the save
153153
// door's publish gate uses; its tests judge the source, never `dist/`.
154154
{ find: /^@objectstack\/lint\/runtime$/, replacement: path.resolve(__dirname, '../lint/src/runtime.ts') },
155+
// [#29603] The package root: a served view read imports it lazily for the
156+
// form-layout warnings (metadata-protocol's `loadFormLayoutLint`), and
157+
// `meta-write-org-scope.test.ts` pays that load at module top. Both
158+
// resolve here, so the read judges the lint rule in the checkout, never `dist/`.
159+
{ find: /^@objectstack\/lint$/, replacement: path.resolve(__dirname, '../lint/src/index.ts') },
155160
// The MCP bridge (`domains/mcp.ts`) reads "this field declares options"
156161
// through `@objectstack/formula`'s `declaresOptions`; its tests judge the
157162
// predicate in the checkout, never `dist/`.

0 commit comments

Comments
 (0)