Repository navigation
Commit 882fcdb
test(cli): speed up the os migrate test family and budget its spawn floors (#28610)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XXJFU9vDsux28Wt28Go65Se)_
Part of #23181 (slice 24, the `os migrate` family). The card stays open:
slices 25 to 28 take other files from slice 23's offender table.
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:`packages/cli` 里 8 个 `os migrate` 测试文件。`apply.remote-posture`
的 stderr 桩改为回调写入回调,每次命令运行不再白等 2 s;两个 `os migrate meta` 文件改用本进程的 node 跑
`bin/run-dev.js`,不再经 tsx 二进制;6 个地板是真实 spawn/boot 的文件各加一行第 2 行 `//
test-budget:` 头。
- **为什么改**:卡 #23181 的测试提速职责:超过 10 s 的测试文件要么提速,要么按裁决 6181973363 B 声明预算。
- **风险与回滚**:只改测试文件,不发布任何东西(cli 的 `files[]` 只有
dist、README、CHANGELOG);没有删、没有放宽任何断言,没有改超时、层级或项目;回滚即 revert。
-
**先看**:`packages/cli/src/commands/migrate/apply.remote-posture.test.ts`(stderr
桩,约 141 行);`packages/cli/test/migrate-meta-default-range.test.ts`(spawn
入口);其余只加了头。
- **两分钟验证**:`pnpm --filter @objectstack/cli exec vitest run
src/commands/migrate/apply.remote-posture.test.ts` → 12/12 通过,本地约 12
s(改前约 34 s)。
- **需要你做的**:无,席位落地。
## What changes
The eight files are slice 23's offender rows for the `os migrate`
family. Readings are the default reporter's per-file module line in the
Test Core shard logs of the five scheduled `ci.yml` runs `41199450735`,
`41217827347`, `41237094739`, `41255489006`, `41271604267` (table:
`pm-orders/server/evidence/23181-s23/offenders-after-s23.md` in the
project files).
**Cause, per file.** I counted spawns, boots and distinct (command,
flags, fixture) triples in each file, then timed it locally.
| file | CI max (s) | tests | spawns / boots | distinct triples | cause
| fix |
|:--|--:|--:|:--|--:|:--|:--|
| `migrate/plan.boot-parity.integration` | 60.6 | 3 | 6 source-CLI
spawns (a serving provision-and-exit boot plus `migrate plan --json`,
per app shape) | 6 | one spawn per distinct triple, about 5.5 s each
locally | header 80 |
| `test/migrate-meta-default-range` | 46.3 | 8 (1 skipped) | 4 `migrate
meta` spawns, already run once each and shared | 4 | each spawn went
through the `tsx` binary (about 7 s locally) | spawn on
`process.execPath`; header 60 |
| `migrate/plan.nullable-tenant-columns.integration` | 35.5 | 1 | 1
serving boot, 3 `migrate plan` spawns | 4 | one spawn per distinct
triple (the store changes between plans) | header 50 |
| `migrate/resume.recorded-by.integration` | 34.6 | 9 | 4 crash children
(each boots the data stack), 2 `os serve` boots, 7 in-process `migrate
resume` boots, all in one `beforeAll` | 13 | each crash is a distinct
chunk plan, each serve and resume runs on its own copy | header 50 |
| `migrate/apply.remote-posture` | 25.7 | 12 | 16 in-process command
runs over a stubbed stack, no database | 16 | **a real wait:** the
stderr spy never called its write callback, so `exitOneShotCommand`
waited out its 2 s drain timeout on every run | spy calls back; header
40 |
| `utils/schema-migrate.one-shot-family.integration` | 17.7 | 129 | 1
served boot, then one in-process command boot per case, each on its own
copy | 129 | distinct (command, mode, state) per case | header 30 |
| `migrate/plan.deferred-reads.integration` | 16.2 | 2 | 2 `migrate
plan` spawns (human, `--json`) | 2 | one spawn per distinct triple |
header 30 |
| `test/migrate-meta-engine-guidance` | 12.0 | 3 | 1 `migrate meta`
spawn, shared by every case | 1 | the one spawn went through the `tsx`
binary | spawn on `process.execPath`; no header (about 4 s locally) |
- **`apply.remote-posture`.** Both commands end in `exitOneShotCommand`
(`src/utils/one-shot-exit.ts`), which writes an empty chunk to stdout
and stderr and waits for each callback, up to 2 s. The file's stdout spy
already called back; its stderr spy returned `true` and dropped the
callback, so every run waited 2 s (most one-run cases read 2.02 to 2.05
s locally, each two-run case about 4.05 s). The stderr spy now calls
back the same way the stdout spy does, and stderr output is still
discarded. What is left is the beforeAll's import of the apply and plan
graph and the first oclif config load (the first case reads about 4.3
s), so the file takes a `whole-package import` header from its slowest
CI reading on the old shape.
- **The two `migrate meta` files** now spawn `bin/run-dev.js` with
`process.execPath`, as the rest of the family has since slice 12
(`plan.boot-parity`, `object-scope`, `unmapped-columns`). The entry,
`src/` and the pinned `NODE_ENV=development` are the same; the `tsx`
wrapper only added a second process and its own transform. The files
stay in the `integration` tier by behaviour (`vitest-tiers.ts` still
sees `child_process` plus `run-dev.js`). `default-range` keeps 4
distinct spawns, so it still takes a header from its CI reading on the
tsx shape.
- **Headers** (ruling 6181973363 letter B): N = the slowest of the five
CI readings × 1.25, rounded up to 10 s. Class `spawns the real producer
once per single-cause fixture` for the spawn and boot files,
`whole-package import` for `apply.remote-posture`. All six parse with
`readTestBudget` (`scripts/measure-test-shard-timings.mjs`) at line 2.
Not changed: no assertion, case name, timeout, tier or project. No
source file, and no shared test helper (`childEnv` from
`test/helpers/serve-process.ts` is only called, as before).
## Tests
- **A/B** (one box, one lock hold, base copied beside head as
`zzbase.*`, interleaved and alternating order, N=3, `--maxWorkers=1`,
default reporter file line; copies deleted before the gate run):
| file | base (s) | head (s) | change |
|:--|:--|:--|--:|
| `apply.remote-posture` | 34.44, 34.63, 34.45 | 12.23, 11.82, 11.49 |
−66% (mean 34.51 → 11.85) |
| `migrate-meta-default-range` | 28.15, 28.28, 27.69 | 16.45, 16.50,
17.38 | −40% (mean 28.04 → 16.78) |
| `migrate-meta-engine-guidance` | 6.81, 7.35, 7.23 | 3.98, 4.41, 4.08 |
−42% (mean 7.13 → 4.16) |
Every run exit 0, same test counts on both sides (12, 8 with 1 skipped,
3).
- **Header-only files**, run once on base (header lines are comments):
`plan.boot-parity` 3/3 (34.3 s), `plan.nullable-tenant-columns` 1/1
(23.2 s), `resume.recorded-by` 9/9 (27.2 s),
`schema-migrate.one-shot-family` 129/129 (11.8 s), `plan.deferred-reads`
2/2 (10.3 s).
- **All eight on head**, after merging `origin/main` (slice 23 landed as
`617dcf43`): head `90445cc3`, one `vitest run --maxWorkers=1
--reporter=default` over the eight files, Test Files 8/8, Tests 166
passed and 1 skipped (the registry-selected `skipIf` case, skipped on
base too). File lines: boot-parity 37.1 s, resume 24.4 s, nullable 24.5
s, default-range 18.3 s, one-shot-family 14.5 s, deferred-reads 12.7 s,
engine-guidance 4.8 s, remote-posture 8.6 s (the gate sweep ran beside
it on the same cores).
- `pnpm --filter @objectstack/cli typecheck` (tsc plus
`check:test-typecheck`) exit 0 on `e6ea56ec`, with
`NODE_OPTIONS=--max-old-space-size=4096`. The merge brought no change to
`packages/cli`'s test layer.
- No ablation: no tested behaviour changed (test-only).
## Acceptance notes
- **`skip-changeset`.** Test-only. `@objectstack/cli`'s `files[]` is
`dist`, `README.md`, `CHANGELOG.md`, and its build emits no test file
into `dist/`, so no `files[]` content changes.
- **`migrate-meta-engine-guidance` takes no header.** After the entry
change it reads about 4 s locally, and its slowest CI reading on the tsx
shape was 12.0 s (base locally about 7 s), so it should land under 10 s.
If the next scheduled reading still puts it over, its floor is one real
spawn and it takes a header then.
- **`apply.remote-posture` should now read well under its header.** The
header uses the old-shape CI reading (the rule is CI readings, never
local ones); a shard-timings refresh re-sizes it.
## Verification
All gate readings on head `90445cc3` (the merge of `origin/main` into
this branch's two commits), after `pnpm exec turbo run build
--concurrency=2` (118/118 tasks).
- **Derived gate families:** `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` → 52 families (the same 52
as before the merge), each run with its exit code captured before any
pipe: 52 exit 0. `--ran` reconciliation: `✓ dispatch-gates --ran: 52
derived famil(ies) accounted for — 52 run, 0 NOT-MEASURED`. On the
pre-merge head `e6ea56ec` three first reads were container prerequisites
and were re-read after meeting them: `check-plugin-teardown-shape
--self-test` exit 3 (shallow clone) → `git fetch --depth=1` of its
control commit → exit 0; `check:dual-build-cjs-loads` and
`check:i18n-coverage` exit 3 (no full build) → full build → both exit 0.
- **Hand gates:** `pnpm exec eslint --no-inline-config` over the eight
files exit 0 (`@objectstack/cli` has no `lint` script; the repo-wide
`pnpm lint` is CI's); `node scripts/check-commit-card-trailers.mjs
--range origin/main..HEAD` exit 0 (3 commits).
- **CI** families (Test Core with the cli integration tier, lint farm,
type-check lanes) are read off this PR's check-runs by the seat.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01FEkQ5pgVXsp3aShmRuEpgc
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent f3d84b3 commit 882fcdb
8 files changed
Lines changed: 31 additions & 5 deletions
File tree
- packages/cli
- src
- commands/migrate
- utils
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
138 | 139 | | |
139 | 140 | | |
140 | 141 | | |
141 | | - | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
142 | 150 | | |
143 | 151 | | |
144 | 152 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
83 | 84 | | |
84 | 85 | | |
85 | 86 | | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
86 | 94 | | |
87 | | - | |
88 | 95 | | |
89 | 96 | | |
90 | 97 | | |
| |||
144 | 151 | | |
145 | 152 | | |
146 | 153 | | |
147 | | - | |
| 154 | + | |
148 | 155 | | |
149 | 156 | | |
150 | 157 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
69 | 76 | | |
70 | | - | |
71 | 77 | | |
72 | 78 | | |
73 | 79 | | |
| |||
381 | 387 | | |
382 | 388 | | |
383 | 389 | | |
384 | | - | |
| 390 | + | |
385 | 391 | | |
386 | 392 | | |
387 | 393 | | |
| |||
0 commit comments