Skip to content

test(cli): compile each scaffold sandbox in process, one spawn per distinct lint-eval run, budget the generate/i18n spawn files - #28592

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-oludil
Nov 1, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-oludil

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Part of #23181 (slice 27, the generate / scaffold / i18n family in packages/cli/test). The card stays open for later slices.

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:packages/cli/test 里 6 个超过 10 s 的测试文件。lint-eval-generator-refusal-separator 去掉两次与前面完全相同的 CLI 子进程(6 次变 4 次);scaffold-emission-typechecks 不再每个用例起一个 tsc 子进程,改为进程内每个脚手架一个独立的 TypeScript Program,共享已解析的 lib 与 spec 声明文件;另外 4 个文件每次子进程都是不同的工作,只加第 2 行 // test-budget: 头。
  • 为什么改:卡 test budget (domain:cli): 119 test file(s) over 10 s of wall in the hourly full battery (Part of #23151) #23181 的测试提速职责:超过 10 s 的文件要么提速,要么按裁决 6181973363 B 声明预算。
  • 风险与回滚:只改测试文件,不发布任何东西(cli 的 files[] 只有 dist、README、CHANGELOG);没有删改任何断言、用例名、超时或分层;回滚即 revert。
  • 先看:packages/cli/test/scaffold-emission-typechecks.test.ts 的 typecheckProject(进程内编译与声明文件缓存);packages/cli/test/lint-eval-generator-refusal-separator.test.ts 的 beforeAll;其余 4 个文件只有第 2 行。
  • 两分钟验证:pnpm --filter @objectstack/cli exec vitest run test/scaffold-emission-typechecks.test.ts → 13/13 通过,canary 仍以 TS2694 变红。
  • 需要你做的:无,席位落地。

What changes

Slice 23's offender table (the default reporter's per-file line in every Test Core shard log of the five scheduled ci.yml runs 41199450735, 41217827347, 41237094739, 41255489006, 41271604267) names these six files over 10 s with no header.

Cause, per file. I counted child processes against distinct (command, flags, fixture) triples:

file CI max (s) tests children distinct triples cause fix
lint-eval-generator-refusal-separator 35.7 5 6 source-CLI spawns 4 the same-door case re-spawned the two --json runs the cases above it already ran the 4 distinct runs move into one beforeAll; every case reads them. Header 50
scaffold-emission-typechecks 33.0 13 12 tsc children (canary, 3 os init templates, 8 os g generators) 12 sandboxes each child loads the compiler and parses and binds the same lib.*.d.ts and @objectstack/spec declarations again in process, one Program per sandbox from its own tsconfig.json, with one shared cache of the parsed declaration files outside the sandboxes. Header 50
generate-stack-reach 31.0 8 10 spawns, already in one beforeAll 10 distinct real work (five fixtures, each refusal read against its own byte snapshot) header 40
dependency-range-doors 27.6 5 5 spawns 5 distinct real work header 40
i18n-extract-check-json 22.2 5 8 spawns (1 setup extract + 7) 8 distinct real work header 30
i18n-extract-check-dry-run 19.3 5 7 spawns (1 setup extract + 6) 7 distinct real work header 30

lint-eval-generator-refusal-separator. The same-door case compared --json --generator "" with --json --generator MISSING_PATH, two runs with the same command, flags and fixture as the first and third cases. It now reads those runs. The pair it compares and every assertion are unchanged. The beforeAll timeout is the old per-case allowance times four.

scaffold-emission-typechecks. Each sandbox still compiles as its own Program, from its own tsconfig.json (the one renderScaffoldTsconfig writes), so no scaffold can lean on another's declarations. The verdict is tsc --noEmit's: getPreEmitDiagnostics (config, options, global, syntactic, semantic, declaration) plus the emit diagnostics, exit 0 exactly when none is an error, and the output is those diagnostics in --pretty false form. Only files outside the sandbox are cached, keyed by path and parse options. All sandboxes compile under the same SCAFFOLD_TSCONFIG_COMPILER_OPTIONS; they differ only in rootDir and include, which change no parse. This is the compiler's own reuse model: the language service shares source files across programs the same way. The compiler is the same typescript package typescript/bin/tsc resolved to. The docblock says all this in a new "One compiler, one Program per sandbox" section.

Headers (ruling 6181973363 letter B): N = the slowest of the five CI readings × 1.25, rounded up to 10 s. Classes: spawns the real producer once per single-cause fixture for the five spawn files, compiles the package type graph once for scaffold-emission-typechecks. The two fixed files keep their pre-fix CI reading. All six parse with readTestBudget (scripts/measure-test-shard-timings.mjs).

Not changed: no assertion, case name, per-case timeout, tier, project or test helper. No source file.

Tests

  • A/B (one box, one lock hold, N=3, --maxWorkers=1, base and head interleaved): the base file's bytes were written into the same path for each base leg and restored from HEAD after it, checked by blob hash, so both legs ran in the same tier and the same DECLARATION_READER_FILES slot (a zzbase.* copy beside it would not). Vitest's Duration line, with no per-file line in single-file runs:

    file base (s) head (s) change
    scaffold-emission-typechecks 26.81, 27.16, 25.54 15.31, 16.85, 16.52 −38.8% (mean 26.50 → 16.23); the tests phase 18.2 → 7.3 s
    lint-eval-generator-refusal-separator 32.29, 32.95, 33.75 22.76, 26.32, 25.12 −25.1% (mean 33.00 → 24.73)

    Every leg exit 0, 13/13 and 5/5 tests on both sides.

  • The in-process verdict matches tsc (one-time probe, not committed): a copy of the head file ran both the in-process compile and the old tsc child on every sandbox. Same verdict on all 12, the same error codes (canary TS2694 on both, none elsewhere). A second canary run after all the others, with the cache warm, still answers TS2694. tsc --noEmit exits 2 on an error under TypeScript 6.0.3, so the in-process verdict answers 2 too.

  • All six files on head, in one run: 6/6 files, 41/41 tests.

  • pnpm --filter @objectstack/cli typecheck (tsc + check:test-typecheck) exit 0.

  • No ablation: test-only, no tested behaviour changed.

Acceptance notes

  • A lever outside this order's fix list. These five spawn files start bin/run-dev.js through node_modules/.bin/tsx. The migrate files moved to process.execPath in slice 12. One os lint --eval --json --generator "" spawn here, without the queue-tier manifest: 9.5 to 11.8 s through tsx, 5.3 to 5.4 s through node, with byte-identical stdout. Not taken: the order rules the fix set (shared beforeAll, in-process read, header). Carrier: test budget (domain:cli): 119 test file(s) over 10 s of wall in the hourly full battery (Part of #23151) #23181.
  • The two i18n files share work across files. Both run the same setup extract and the same --check control over an empty --out. Sharing it would need a helper beyond these files or a file merge, so it was not done.
  • skip-changeset. Test-only. @objectstack/cli's files[] is dist, README.md, CHANGELOG.md, and test/ is not built into dist/, so no files[] content changes.

Verification

All readings on head cc5ee375 (git rev-parse --short HEAD).

  • Derived gate families: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack gives 48 families. Each ran with its exit code captured before any pipe. Two first reads were container prerequisites and were re-read after meeting them: check-plugin-teardown-shape.mjs --self-test exited 3 on the shallow clone, then 0 after git fetch --depth=1 of its positive-control commit; check:dual-build-cjs-loads exited 3 with no full build, then 0 after pnpm exec turbo run build --concurrency=2 (118/118 tasks). --ran reconciliation: ✓ dispatch-gates --ran: 48 derived famil(ies) accounted for — 48 run, 0 NOT-MEASURED.
  • Hand gates: pnpm exec eslint --no-inline-config over the six files exit 0 (@objectstack/cli has no lint script; the repo-wide pnpm lint runs in CI). node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD exit 0.
  • CI families (Test Core with both cli tiers, the lint farm, the type-check lanes) are read off this PR's check-runs by the seat.
  • Evidence (gate list with exit codes, A/B logs summary, the tsc equivalence probe output): pm-orders/server/evidence/23181-s27/ in the project files.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FEkQ5oeaQtmg5DUi2ojVqs

@objectstack-fleet objectstack-fleet Bot added skip-changeset PR has no user-facing published change; bypasses the changeset gate tests labels Nov 1, 2026
@github-actions github-actions Bot added the size/m label Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

What this run could not see

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC

席位核对 · ACCEPT · domain:server seat 1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-11-01T07:27Z

Head cc5ee375, report 6554826353 (on #23181), claim 6554858879 (slice 27). No contract review is owed: test-only, no packages/spec/** path, no governed path, no narrowing and no Clause-②: yes.

item reading
Shape Draft, base main, Part of #23181. Exactly slice 27's six files in packages/cli/test, labels tests + skip-changeset. Clause-②: no, Metadata-protocol: none.
Diff check (seat) lint-eval-generator-refusal-separator: the four distinct runs spawn once in beforeAll, and the same-door case reads the two machine-face runs instead of re-spawning them; every assertion is kept (6 spawns → 4). scaffold-emission-typechecks: each sandbox is still compiled as its own Program from its own tsconfig.json with tsc --noEmit -p diagnostics; only the read-only lib.*.d.ts and @objectstack/spec declarations are parsed once and shared. typescript is already a cli dependency, and the report shows the verdicts match the tsc child on all 12 sandboxes. Line-2 // test-budget: headers on all six files (50/50/40/40/30/30). No helper outside the six files is touched; no timeout is raised.
Evidence CI green on head cc5ee375 (35 success, 23 skipped, 0 failed). Governed check: NOT governed, 180 changed lines. Mergeable state clean. The report gives 48 of 48 derived gates exit 0, --ran reconciled, and A/B of −25 % (lint-eval) and −39 % (scaffold-emission).
Landing 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready and auto-merge through the relay, once every check on this head is green or an expected skip.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 1, 2026 07:28
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 1, 2026
Merged via the queue into main with commit ce001f9 Nov 1, 2026
59 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-oludil branch November 1, 2026 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant