Skip to content

Commit 6f1e195

Browse files
test(cli): authoring-door spawn family — share repeated spawns, declare budgets (#23181 slice 16) (#26493)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"} --> _Requested by **Leehom** · [project thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XLW4ZjyuLt1yEBySkebVp6y)_ Part of #23181 (slice 16). The card is a series and stays open. Clause-②: no Metadata-protocol: none Test-only diff: `skip-changeset`. Eight files under `packages/cli/test/`, which is not in `@objectstack/cli`'s `files[]` (`dist`, `README.md`, `CHANGELOG.md`); no `files[]` changes in any package. ## 维护者速读 - 改了什么:cli 里 8 个「真实调用 `os validate` / `os build` / `os lint`」的作者门测试文件。其中 2 个把重复的同一条 CLI 调用合并成 `beforeAll` 里跑一次;8 个都在第 2 行加了 `// test-budget:` 声明。 - 为什么改:这 8 个文件在 CI 上都超过 10 s。逐个数了调用:6 个文件的每次调用都是不同的(命令、参数、夹具)组合,耗时下限就是调用次数,按裁决 6181973363 B 声明预算;2 个对等性文件把同一组合跑了 2–3 次,合并后本地实测各快约 43%。 - 风险与回滚:无运行时风险,只动测试;每条断言原样保留、含义不变,未改超时与分层。回滚即 revert 本 PR。 - 先看:`packages/cli/test/lint-per-package-authoring-parity.test.ts` 的 `beforeAll`、`packages/cli/test/validate-per-package-authoring-parity.test.ts` 的 `beforeAll`(唯二有逻辑改动的地方),其余 6 个文件只有第 2 行。 - 两分钟验证:`cd packages/cli && pnpm exec vitest run --project integration test/lint-per-package-authoring-parity.test.ts test/validate-per-package-authoring-parity.test.ts`,预期 2 个文件 9 个用例全绿,单文件约 14–17 s。 - 需要你做的:无,席位落地。 ## What changes **Counted from the code** (spawns = CLI child processes per file run; triples = distinct command, flags and fixture): | file | CI max (s) | tests | spawns before | distinct triples | change | budget | |:--|--:|--:|--:|--:|:--|--:| | `test/stack-conversion-record-door.test.ts` | 141.61 | 22 | 24 | 24 | header | 180 | | `test/stack-provenance-door.test.ts` | 60.77 | 12 | 12 | 12 | header | 80 | | `test/picklist-reference-doors.test.ts` | 57.37 | 11 | 11 | 11 | header | 80 | | `test/lint-per-package-authoring-parity.test.ts` | 43.77 | 5 | 9 | 5 | shared `beforeAll` (9 → 5 spawns) + header | 60 | | `test/union-fold-command-parity.test.ts` | 33.65 | 9 | 9 | 9 | header | 50 | | `test/package-union-readers.test.ts` | 32.22 | 7 | 9 | 9 | header | 50 | | `test/validate-per-package-authoring-parity.test.ts` | 28.80 | 4 | 7 | 4 | shared `beforeAll` (7 → 4 spawns) + header | 40 | | `test/validate-view-container-name.test.ts` | 20.34 | 5 | 4 | 4 | header | 30 | No file is under 10 s in any reading, so every file gets a header. **The two hoists.** In `lint-per-package-authoring-parity` the cases spawned `os build --json` on the flip fixture three times and `os lint --json` on it three times. In `validate-per-package-authoring-parity` the cases spawned `os build --json` on the multi-package fixture three times and `os validate --json` on it twice. Each file now runs each distinct triple once, in sequence, in its existing `beforeAll`, and each case reads the shared run of its triple. Every assertion is unchanged, in the same case, on the output of the same command, flags and fixture. The hook's timeout is the old per-case allowance (180 s) times the spawns it runs, so no per-spawn allowance moved. The spawns stay sequential: no in-file concurrency. **The header.** One line at line 2 of each file, the ruled grammar `// test-budget: N — spawns the real producer once per single-cause fixture: REASON` (ruling record 6181973363, letter B; grammar `readTestBudget` in `scripts/measure-test-shard-timings.mjs`). N = slowest CI reading x 1.25, rounded up to the next 10 s. After the hoists every remaining spawn in all eight files is a distinct triple, so the spawn count is the floor. ## Why **The reading.** No shard-timings refresh has run on `main` since the oclif test manifest (PR #26256). The CI numbers are the vitest module lines (they include `beforeAll`) of the three scheduled full-battery `ci.yml` runs on `main` after it, all with the manifest setup on: | file | 40392443783 (`69aa630b9f`) | 40424746941 (`e843b5b320`) | 40447801748 (`26e7c75e9a`) | |:--|--:|--:|--:| | `stack-conversion-record-door` | **141.61** | 98.16 | 97.84 | | `stack-provenance-door` | **60.77** | 59.62 | 37.58 | | `picklist-reference-doors` | 43.88 | 56.55 | **57.37** | | `lint-per-package-authoring-parity` | **43.77** | 42.36 | 26.12 | | `union-fold-command-parity` | **33.65** | 32.07 | 20.57 | | `package-union-readers` | 22.68 | 30.03 | **32.22** | | `validate-per-package-authoring-parity` | **28.80** | 21.83 | 24.89 | | `validate-view-container-name` | **20.34** | 19.96 | 12.91 | The first two columns are slice 15's readings (report on #23181). The third run is newer, so it was read too (its Test Core shards via `get_job_logs`: 510 cli file lines; the run's own failure is in `Test UI (1/16)`, not Test Core). **The two hoisted files keep the pre-hoist budget.** Their budgets come from CI readings of the 9-spawn and 7-spawn shapes, the only CI readings that exist. Locally the hoist cuts them by about 44%. A shard-timings refresh re-sizes the budget from the new shape; it is not lowered by hand. ## Tests <details><summary>A/B of the two hoisted files (N=3, one lock hold)</summary> The BASE file (`775a0006bd`) copied next to the HEAD file as `test/zzbase-*.test.ts`, all four in one run, `cd packages/cli && pnpm exec vitest run --project integration --maxWorkers=1 --reporter=default`, three runs in one `os-verify-lock` hold on a shared 4-vCPU container, manifest setup on (queue tier). The copies were deleted before the gates ran. Each run: `Test Files 4 passed (4)`, `Tests 18 passed (18)`, exit 0. | file | BASE (s) | HEAD (s) | change | |:--|:--|:--|:--| | `lint-per-package-authoring-parity` | 32.21 / 30.80 / 30.29 | 17.36 / 17.08 / 17.25 | about -44% (9 → 5 spawns) | | `validate-per-package-authoring-parity` | 25.54 / 23.35 / 24.31 | 13.72 / 13.77 / 14.67 | about -43% (7 → 4 spawns) | </details> <details><summary>The family on this head</summary> Same command, `--maxWorkers=2`, the eight files: `Test Files 8 passed (8)`, `Tests 75 passed (75)`, exit 0, run wall 155.3 s. Per file (s): stack-conversion-record-door 102.2, stack-provenance-door 41.6, picklist-reference-doors 38.5, union-fold-command-parity 23.8, package-union-readers 20.2, lint-per-package-authoring-parity 18.3, validate-view-container-name 14.1, validate-per-package-authoring-parity 13.9. </details> <details><summary>Header parse, before and after</summary> `readTestBudget` on each file at BASE `775a0006bd` returns `null` for all eight (a file over 10 s with no declared budget, the offender reading). On this head all eight return class `spawns the real producer once per single-cause fixture` at line 2, seconds 180 / 80 / 80 / 60 / 50 / 50 / 40 / 30. </details> <details><summary>Typecheck and lint</summary> `pnpm --filter @objectstack/cli typecheck`: VERDICT command-exit 0 (`check:test-typecheck: OK`). `pnpm exec eslint --no-inline-config` on the eight files: exit 0. </details> ## Acceptance notes - Pin: the next shard-timings refresh lists none of these eight files in its over-budget table. Each appears under the budget it declares, and comes back only if a run reads it over its own budget. - Nothing else repeats a triple in this family. `package-union-readers` already ran its nine distinct spawns in shared `beforeAll` hooks, two at a time; that pre-existing shape is untouched. - Next unheadered family (out of scope here, per the order): `test/verify-*.test.ts`. ## Verification Local gates at `35212c7e`: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 48 commands. Each ran with its exit code captured by redirect before any pipe: 47 exit 0. `check-plugin-teardown-shape.mjs --self-test` first exited 3 on the shallow clone and exited 0 after `git fetch origin 621a487`. `pnpm check:dual-build-cjs-loads` exited 3 (PREREQUISITE NOT MET: it needs every package's `dist/`, and only the cli dependency closure was built); it is NOT MEASURED locally and runs in CI. `--ran` reconciliation: `48 derived famil(ies) accounted for — 47 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)`, exit 0. The three artifact-roster families whose roster sits under `packages/` were run too: `pnpm check:authz-resolver`, `pnpm check:error-code-casing` and `pnpm check:filter-alias-parity`, each exit 0. The repo-wide `pnpm lint` and the `test:repo` project are left to CI (Lint & Repo Gates, Test Core). ## 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. - [x] 3. No formatter ran; `pnpm lint` passes. (eslint on the eight files locally; the repo-wide run is CI's) - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (publishes nothing: `skip-changeset`) - [x] 6. `packages/spec` touched: its generated artifacts are regenerated and committed. (not touched) - [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. - [x] The *Before Submitting* list in CONTRIBUTING.md is done. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FEkQ5pUpKjymejZTAe9SWn --- _Generated by [Claude Code](https://claude.ai/code/session_01FEkQ5pUpKjymejZTAe9SWn)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 27f38ad commit 6f1e195

8 files changed

Lines changed: 77 additions & 38 deletions

‎packages/cli/test/lint-per-package-authoring-parity.test.ts‎

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 60 — spawns the real producer once per single-cause fixture: five CLI spawns in one beforeAll, each a distinct triple (`os build`, `os lint`, `os lint --strict` over the flip fixture; `os build`, `os lint` over the single-package control); slowest CI reading 43.77 s (run 40392443783), taken with nine spawns before the repeats were shared of the three scheduled CI runs after the oclif test manifest (PR #26256: 40392443783, 40424746941, 40447801748), x 1.25, rounded up
23

34
/**
45
* #18778, the BEHAVIOURAL half — `os lint` and `os build` report the same
@@ -211,6 +212,17 @@ export default defineStack({
211212

212213
const dirs = { flip: '', single: '' };
213214

215+
/** A cold `tsx` spawn of the CLI source entry, the per-case allowance this file always gave one. */
216+
const SPAWN_TIMEOUT_MS = 180_000;
217+
218+
/**
219+
* One run per distinct (command, flags, fixture) triple. The cases below used
220+
* to spawn `os build --json` on the flip fixture three times and `os lint
221+
* --json` on it three times; every case now reads the one run of its triple,
222+
* spawned in sequence in the `beforeAll` below.
223+
*/
224+
const runs = {} as Record<'buildFlip' | 'lintFlip' | 'lintStrictFlip' | 'buildSingle' | 'lintSingle', Run>;
225+
214226
function plant(config: string): string {
215227
const dir = mkdtempSync(join(tmpdir(), 'os-lintpp-'));
216228
mkdirSync(join(dir, 'src'), { recursive: true });
@@ -225,31 +237,36 @@ function plant(config: string): string {
225237
}
226238

227239
describe('#18778 — `os lint` and `os build` report the same per-package finding set', () => {
228-
beforeAll(() => {
240+
beforeAll(async () => {
229241
dirs.flip = plant(CONFIG_FLIP);
230242
dirs.single = plant(CONFIG_SINGLE);
231-
});
243+
runs.buildFlip = await runCli(['build', '--json'], dirs.flip);
244+
runs.lintFlip = await runCli(['lint', '--json'], dirs.flip);
245+
runs.lintStrictFlip = await runCli(['lint', '--json', '--strict'], dirs.flip);
246+
runs.buildSingle = await runCli(['build', '--json'], dirs.single);
247+
runs.lintSingle = await runCli(['lint', '--json'], dirs.single);
248+
}, 5 * SPAWN_TIMEOUT_MS);
232249

233250
afterAll(() => {
234251
for (const dir of Object.values(dirs)) if (dir) rmSync(dir, { recursive: true, force: true });
235252
});
236253

237-
it('the fixture reaches the pass at all, and the UNION sees nothing — `os build` raises exactly the survivor', async () => {
254+
it('the fixture reaches the pass at all, and the UNION sees nothing — `os build` raises exactly the survivor', () => {
238255
// Asserted BEFORE any claim about parity: a fixture that never reaches the
239256
// per-package pass makes every comparison below vacuous, and a fixture whose
240257
// union run raises the same finding makes the comparison an echo count.
241-
const build = await runCli(['build', '--json'], dirs.flip);
258+
const build = runs.buildFlip;
242259
expect(build.code, `os build --json failed:\n${build.stdout}${build.stderr}`).toBe(0);
243260
const warnings = payloadOf(build, 'os build --json').warnings as unknown[];
244261
expect(buildPerPackage(warnings).length).toBeGreaterThan(0);
245262
// Nothing else: every warning on this fixture is a per-package survivor.
246263
expect(warnings.length).toBe(buildPerPackage(warnings).length);
247-
}, 180_000);
264+
});
248265

249-
it('`os lint` reports every per-package finding `os build` does', async () => {
266+
it('`os lint` reports every per-package finding `os build` does', () => {
250267
// The pin. On `origin/main` 7572329069 this read `build: 1, lint: 0`.
251-
const build = await runCli(['build', '--json'], dirs.flip);
252-
const lint = await runCli(['lint', '--json'], dirs.flip);
268+
const build = runs.buildFlip;
269+
const lint = runs.lintFlip;
253270
expect(build.code, `os build --json failed:\n${build.stdout}${build.stderr}`).toBe(0);
254271
expect(lint.code, `os lint --json failed:\n${lint.stdout}${lint.stderr}`).toBe(0);
255272

@@ -260,25 +277,25 @@ describe('#18778 — `os lint` and `os build` report the same per-package findin
260277
missing,
261278
'these per-package findings ride `os build` and `os lint` cannot see them — the #18778 false-clean set',
262279
).toEqual([]);
263-
}, 180_000);
280+
});
264281

265-
it('…and nothing per-package rides `os lint` that `os build` does not report either', async () => {
282+
it('…and nothing per-package rides `os lint` that `os build` does not report either', () => {
266283
// Parity is an equality. A lint that over-reports is its own defect — an
267284
// author fixing a finding the command that SHIPS never raises.
268-
const build = await runCli(['build', '--json'], dirs.flip);
269-
const lint = await runCli(['lint', '--json'], dirs.flip);
285+
const build = runs.buildFlip;
286+
const lint = runs.lintFlip;
270287
const inBuild = new Set(buildPerPackage(payloadOf(build, 'os build --json').warnings as unknown[]));
271288
expect(lintPerPackage(payloadOf(lint, 'os lint --json').issues as unknown[]).filter((w) => !inBuild.has(w))).toEqual([]);
272-
}, 180_000);
289+
});
273290

274-
it('⚠️ THE NARROWING — `os lint --strict` now EXITS 1 on a project it exited 0 for', async () => {
291+
it('⚠️ THE NARROWING — `os lint --strict` now EXITS 1 on a project it exited 0 for', () => {
275292
// The Clause-② evidence, pinned so it can neither regress silently nor widen
276293
// silently. `--strict` is documented as "treat warnings as errors", and a
277294
// per-package advisory is a warning this door could not see before, so the
278295
// verdict moves with it. ⛔ The DEFAULT face is deliberately NOT asserted to
279296
// move: no `error`-severity per-package-only finding was exhibited on this
280297
// fixture, and a pin asserting one would be asserting something unmeasured.
281-
const strict = await runCli(['lint', '--json', '--strict'], dirs.flip);
298+
const strict = runs.lintStrictFlip;
282299
const payload = payloadOf(strict, 'os lint --json --strict');
283300
expect(payload.strict).toBe(true);
284301
expect(payload.failing).toBe(1);
@@ -287,21 +304,21 @@ describe('#18778 — `os lint` and `os build` report the same per-package findin
287304

288305
// …and the same project without `--strict` still exits 0, so what moved is
289306
// the strict verdict and not the default one.
290-
const plain = await runCli(['lint', '--json'], dirs.flip);
307+
const plain = runs.lintFlip;
291308
expect(plain.code).toBe(0);
292309
expect(payloadOf(plain, 'os lint --json').passed).toBe(true);
293-
}, 180_000);
310+
});
294311

295-
it('CONTROL — a single-package project raises NO per-package finding on either door', async () => {
312+
it('CONTROL — a single-package project raises NO per-package finding on either door', () => {
296313
// "Present" must be distinguishable from "always present": the prefix this
297314
// file matches on is not something either command emits unconditionally, and
298315
// `os lint --strict` still exits 1 there for the ORDINARY warning, which is
299316
// how the case above is read as a change and not as a constant.
300-
const build = await runCli(['build', '--json'], dirs.single);
301-
const lint = await runCli(['lint', '--json'], dirs.single);
317+
const build = runs.buildSingle;
318+
const lint = runs.lintSingle;
302319
expect(build.code, `os build --json failed:\n${build.stdout}${build.stderr}`).toBe(0);
303320
expect(lint.code, `os lint --json failed:\n${lint.stdout}${lint.stderr}`).toBe(0);
304321
expect(buildPerPackage(payloadOf(build, 'os build --json').warnings as unknown[])).toEqual([]);
305322
expect(lintPerPackage(payloadOf(lint, 'os lint --json').issues as unknown[])).toEqual([]);
306-
}, 180_000);
323+
});
307324
});

‎packages/cli/test/package-union-readers.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 50 — spawns the real producer once per single-cause fixture: nine CLI spawns across three beforeAll hooks, each a distinct triple (`os build`, `os validate`, `os lint --strict`, `os i18n check`, `os i18n extract` over five fixtures); slowest CI reading 32.22 s (run 40447801748) of the three scheduled CI runs after the oclif test manifest (PR #26256: 40392443783, 40424746941, 40447801748), x 1.25, rounded up
23

34
/**
45
* #22238 / #22189 — the CLI readers that judged only a multi-package

‎packages/cli/test/picklist-reference-doors.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 80 — spawns the real producer once per single-cause fixture: 11 CLI spawns over four fixtures, each a distinct command, face or fixture (`os validate` json and text, `os build`, `os lint`); slowest CI reading 57.37 s (run 40447801748) of the three scheduled CI runs after the oclif test manifest (PR #26256: 40392443783, 40424746941, 40447801748), x 1.25, rounded up
23

34
/**
45
* The `picklist` kind through the three authoring doors, over the real CLI.

‎packages/cli/test/stack-conversion-record-door.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 180 — spawns the real producer once per single-cause fixture: 24 CLI spawns, one per case leg, each a distinct (command, flags, fixture) triple across `os validate` / `os build` / `os lint` over eight fixtures; slowest CI reading 141.61 s (run 40392443783) of the three scheduled CI runs after the oclif test manifest (PR #26256: 40392443783, 40424746941, 40447801748), x 1.25, rounded up
23

34
/**
45
* The ADR-0087 conversions a stack PRODUCER applied reach `os validate` and

‎packages/cli/test/stack-provenance-door.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 80 — spawns the real producer once per single-cause fixture: 12 CLI spawns, `os validate` and `os build` once each over six fixtures, every one a distinct triple; slowest CI reading 60.77 s (run 40392443783) of the three scheduled CI runs after the oclif test manifest (PR #26256: 40392443783, 40424746941, 40447801748), x 1.25, rounded up
23

34
/**
45
* `os validate` and `os build` accept one authoring shape (#20367 ruling B).

‎packages/cli/test/union-fold-command-parity.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 50 — spawns the real producer once per single-cause fixture: nine CLI spawns, `os validate` / `os lint` / `os build` once each over three fixtures, every one a distinct triple; slowest CI reading 33.65 s (run 40392443783) of the three scheduled CI runs after the oclif test manifest (PR #26256: 40392443783, 40424746941, 40447801748), x 1.25, rounded up
23

34
/**
45
* #17069 — the three authoring commands judge the SAME STACK, on the ADR-0130

‎packages/cli/test/validate-per-package-authoring-parity.test.ts‎

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 40 — spawns the real producer once per single-cause fixture: four CLI spawns in one beforeAll, each a distinct triple (`os build`, `os validate` over the multi-package fixture and over the single-package control); slowest CI reading 28.80 s (run 40392443783), taken with seven spawns before the repeats were shared of the three scheduled CI runs after the oclif test manifest (PR #26256: 40392443783, 40424746941, 40447801748), x 1.25, rounded up
23

34
/**
45
* #18677, the BEHAVIOURAL half — `os validate` and `os build` report the same
@@ -230,6 +231,17 @@ export default defineStack({
230231

231232
const dirs = { multi: '', single: '' };
232233

234+
/** A cold `tsx` spawn of the CLI source entry, the per-case allowance this file always gave one. */
235+
const SPAWN_TIMEOUT_MS = 180_000;
236+
237+
/**
238+
* One run per distinct (command, flags, fixture) triple. The cases below used
239+
* to spawn `os build --json` on the multi-package fixture three times and `os
240+
* validate --json` on it twice; every case now reads the one run of its
241+
* triple, spawned in sequence in the `beforeAll` below.
242+
*/
243+
const runs = {} as Record<'buildMulti' | 'validateMulti' | 'buildSingle' | 'validateSingle', Run>;
244+
233245
function plant(config: string): string {
234246
const dir = mkdtempSync(join(tmpdir(), 'os-ppparity-'));
235247
mkdirSync(join(dir, 'src'), { recursive: true });
@@ -244,20 +256,24 @@ function plant(config: string): string {
244256
}
245257

246258
describe('#18677 — `os validate` and `os build` report the same per-package advisory set', () => {
247-
beforeAll(() => {
259+
beforeAll(async () => {
248260
dirs.multi = plant(CONFIG_MULTI);
249261
dirs.single = plant(CONFIG_SINGLE);
250-
});
262+
runs.buildMulti = await runCli(['build', '--json'], dirs.multi);
263+
runs.validateMulti = await runCli(['validate', '--json'], dirs.multi);
264+
runs.buildSingle = await runCli(['build', '--json'], dirs.single);
265+
runs.validateSingle = await runCli(['validate', '--json'], dirs.single);
266+
}, 4 * SPAWN_TIMEOUT_MS);
251267

252268
afterAll(() => {
253269
for (const dir of Object.values(dirs)) if (dir) rmSync(dir, { recursive: true, force: true });
254270
});
255271

256-
it('the multi-package fixture reaches the pass at all — and its survivor is NOT an echo', async () => {
272+
it('the multi-package fixture reaches the pass at all — and its survivor is NOT an echo', () => {
257273
// Asserted BEFORE any claim about parity: a fixture that never reaches the
258274
// per-package pass makes every comparison below vacuous, which is the exact
259275
// way this defect stayed invisible.
260-
const build = await runCli(['build', '--json'], dirs.multi);
276+
const build = runs.buildMulti;
261277
expect(build.code, `os build --json failed:\n${build.stdout}${build.stderr}`).toBe(0);
262278
const warnings = payloadOf(build, 'os build --json').warnings as unknown[];
263279
expect(perPackageWarnings(warnings).length).toBeGreaterThan(0);
@@ -276,12 +292,12 @@ describe('#18677 — `os validate` and `os build` report the same per-package ad
276292
PER_PACKAGE_WHERE.test(named(industryWarnings[0])),
277293
'the `industry` finding is being raised by the UNION run — the survivor would be an echo',
278294
).toBe(true);
279-
}, 180_000);
295+
});
280296

281-
it('`os validate` reports every per-package advisory `os build` does', async () => {
297+
it('`os validate` reports every per-package advisory `os build` does', () => {
282298
// The pin. On `origin/main` 09e16a574 this read `build: 1, validate: 0`.
283-
const build = await runCli(['build', '--json'], dirs.multi);
284-
const validate = await runCli(['validate', '--json'], dirs.multi);
299+
const build = runs.buildMulti;
300+
const validate = runs.validateMulti;
285301
expect(build.code, `os build --json failed:\n${build.stdout}${build.stderr}`).toBe(0);
286302
expect(validate.code, `os validate --json failed:\n${validate.stdout}${validate.stderr}`).toBe(0);
287303

@@ -294,29 +310,29 @@ describe('#18677 — `os validate` and `os build` report the same per-package ad
294310
missingFromValidate,
295311
'these per-package findings ride `os build` and `os validate` cannot see them — the #18677 false-clean set',
296312
).toEqual([]);
297-
}, 180_000);
313+
});
298314

299-
it('…and nothing per-package rides `os validate` that `os build` does not report either', async () => {
315+
it('…and nothing per-package rides `os validate` that `os build` does not report either', () => {
300316
// The reverse end. Parity is an equality, and a validate that over-reports
301317
// is its own defect — an author fixing a finding the command that SHIPS
302318
// never raises.
303-
const build = await runCli(['build', '--json'], dirs.multi);
304-
const validate = await runCli(['validate', '--json'], dirs.multi);
319+
const build = runs.buildMulti;
320+
const validate = runs.validateMulti;
305321
const bw = payloadOf(build, 'os build --json').warnings as unknown[];
306322
const vw = payloadOf(validate, 'os validate --json').warnings as unknown[];
307323

308324
const inBuild = new Set(perPackageWarnings(bw));
309325
expect(perPackageWarnings(vw).filter((w) => !inBuild.has(w))).toEqual([]);
310-
}, 180_000);
326+
});
311327

312-
it('CONTROL — a single-package project raises NO per-package finding on either door', async () => {
328+
it('CONTROL — a single-package project raises NO per-package finding on either door', () => {
313329
// "Present" must be distinguishable from "always present": the prefix this
314330
// file matches on is not something either command emits unconditionally.
315-
const build = await runCli(['build', '--json'], dirs.single);
316-
const validate = await runCli(['validate', '--json'], dirs.single);
331+
const build = runs.buildSingle;
332+
const validate = runs.validateSingle;
317333
expect(build.code, `os build --json failed:\n${build.stdout}${build.stderr}`).toBe(0);
318334
expect(validate.code, `os validate --json failed:\n${validate.stdout}${validate.stderr}`).toBe(0);
319335
expect(perPackageWarnings(payloadOf(build, 'os build --json').warnings as unknown[])).toEqual([]);
320336
expect(perPackageWarnings(payloadOf(validate, 'os validate --json').warnings as unknown[])).toEqual([]);
321-
}, 180_000);
337+
});
322338
});

‎packages/cli/test/validate-view-container-name.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 30 — spawns the real producer once per single-cause fixture: four CLI spawns, `os validate` json and text over three fixtures, every one a distinct triple; slowest CI reading 20.34 s (run 40392443783) of the three scheduled CI runs after the oclif test manifest (PR #26256: 40392443783, 40424746941, 40447801748), x 1.25, rounded up
23

34
/**
45
* [#20331] `os validate` refuses a `views:` container whose own `name`

0 commit comments

Comments
 (0)