Repository navigation
Commit 7f1de2e
test(cli): pin the three-probe restart acceptance for an API-created package (#20069)
Part of #17676 — ruling A′ item 5, the acceptance. #17676 remains open:
at this head probe 2 disagrees after a restart, and its cause is in
`packages/runtime` (the `domain:cli` lane), outside this card's file
surface.
Clause-②: no
## What this adds
-
**`packages/cli/test/package-restart-acceptance.integration.test.ts`**.
This is a declared cross-lane, test-only edit: one new test file under
`packages/cli`, and no source change in `packages/cli` or
`packages/rest`.
- It boots `os serve --dev` twice on ONE SQLite file. The host app
declares neither `marketplace` nor `package-registry` (the
`examples/app-crm` shape), so this is the stock composition.
- On the first boot it runs the card's repro chain: `POST
/api/v1/packages` with the body Studio's create dialog sends, then `PUT
/api/v1/meta/object/leave_request?mode=draft&package=com.example.leave`,
then `POST /api/v1/packages/com.example.leave/publish-drafts`. It reads
the three probes, stops, boots again, and reads them again.
- Harness: it reuses the shared spawn helpers in
`test/helpers/serve-process.ts` (`CLI`, `TSX`, `childEnv`,
`probeThroughChild`, the port errors). It also follows the
long-lived-boot pattern of
`serve-package-registry-always-on.e2e.test.ts`. That file's `bootServe`
/ `stop` are file-local, so they are repeated here rather than lifted:
lifting them would edit a `domain:cli` file outside this card's surface.
## The three probes, measured
| probe | route | before restart | after restart |
|:--|:--|:--|:--|
| 1 · Studio writable list | `GET /api/v1/packages`, rows whose `scope`
is not `system`/`cloud` and whose `writable` is `true` |
`com.example.leave` listed | listed |
| 2 · data | `GET /api/v1/data/leave_request` | **200** | **404**
`OBJECT_NOT_FOUND` — `Object 'leave_request' is not registered` |
| 3 · published | `GET /api/v1/meta/object/leave_request/published` |
200 | 200 |
**Where probe 1's route comes from.** objectui at the pinned
`.objectui-sha` `62597c58`:
- `packages/app-shell/src/views/studio-design/BuilderLanding.tsx`
renders the heading `engine.studio.landing.mineHeading` ("My packages
(writable)", upper-cased by CSS) and the empty state
`engine.studio.landing.noneWritable` ("No writable packages yet — create
one to start.").
- It lists `fetchPackages()` filtered to `writable`.
- `studio-design/packages-io.ts` `fetchPackages()` is ONE read,
`fetch('/api/v1/packages')`, with no query parameters. `parsePackages`
drops `scope: system | cloud` rows and reads the row's top-level
`writable`.
- On the server, `GET /packages` is
`packages/runtime/src/domains/packages.ts`: `registry.getAllPackages()`,
then `withWritableVerdict`.
- The pin's `studioWritableIds` is that reader. It leaves out the
client's fallback for servers that send no `writable`.
## Why probe 2 disagrees: root cause, measured
1. For a config app, the stock `serve` composition is
`createStandaloneStack` (`packages/runtime/src/standalone-stack.ts`). It
builds `new ObjectQLPlugin({ environmentId, runPlatformMigrations })`
with `environmentId` defaulting to `'env_local'` and no
`hydrateMetadataFromDb`.
2. `ObjectQLPlugin.start()` hydrates `sys_metadata` only when
`environmentId === undefined || hydrateMetadataFromDb`. Otherwise it
logs `Project kernel — skipping sys_metadata hydration (metadata sourced
from artifact)`. A debug-level reboot on the same database file prints
exactly that line.
3. The published row sits in `sys_metadata` with `state = active`,
`organization_id` NULL and `package_id = com.example.leave`. That is
exactly what `loadMetaFromDb` selects, but the row is never read. So the
object is never re-registered and the data route answers 404, while the
published read answers 200 straight off the stored row. Reading `GET
/api/v1/meta/object/leave_request` and the object list on the live
second boot does not change the 404.
4. **Diagnostic, never committed.** I added only `hydrateMetadataFromDb:
true` to that constructor call and rebuilt `@objectstack/runtime`. On
the same database file, probe 2 then answers 200 after the restart.
Through this pin (leg B below), the other four assertions pass and the
`it.fails` goes red. So all three probes agree.
5. **Not fixed here.** `packages/runtime` is the `domain:cli` lane,
outside this card's file surface. The skip line's own words ("metadata
sourced from artifact") are false on this composition, where
runtime-authored metadata lives in `sys_metadata`. That is part of the
same defect, not a second one.
## How the agreement is pinned
- Probe 2 after the restart is an **`it.fails`**, following the
precedent in `packages/cli/test/commands.test.ts`: known-broken, promote
to a plain assertion once fixed.
- `it.fails` is green on ANY failure, so four harness-health assertions
stand beside it: the chain lands (201 / 200 / `outcome: 'published'`),
the three probes agree before the restart, probe 1 holds after it, and
probe 3 holds after it.
- It goes red on the change that makes the composition re-register
runtime-authored objects. That change promotes it to `it`, and it is the
one that meets #17676's acceptance.
## Tier
- The file name carries no nightly tier (`scripts/nightly-tiers.mjs`).
It spawns the CLI, so `vitest-tiers.ts` places it in the `integration`
project, and it runs in the per-PR and merge-queue Test Core.
- Measured with `vitest list --filesOnly`: `--project integration` lists
it 1 time and `--project unit` 0 times. Under `OS_TEST_TIERS=nightly` it
is listed 0 times; the control,
`serve-package-registry-always-on.e2e.test.ts`, is listed 1 time.
- Cost: 22–23 s held under the verify lock per run (two boots).
- Why not nightly: the change that fixes probe 2 lives in a dependency
of this suite. Test Core therefore runs this file on that pull request,
so the `it.fails` flips there rather than on a nightly after the change
lands.
## Before / after (every run at `8af659d715`, every mutation through
`scripts/ablation-replace.mjs`, each restore proven by blob equality
with HEAD and an empty `git diff HEAD`)
| run | mutation | result |
|:--|:--|:--|
| head | none | 4 passed, 1 expected fail |
| reason | `it.fails(` → `it(` in this file | 1 failed: `GET
/data/leave_request after a restart: {"error":"Object 'leave_request' is
not registered","code":"OBJECT_NOT_FOUND","object":"leave_request"}
(code OBJECT_NOT_FOUND): expected 404 to be 200`; 4 passed |
| ablation A | the `'package-registry'` key of
`Serve.CAPABILITY_PROVIDERS` renamed, so the key is absent.
`bin/run-dev.js` runs `serve.ts` from `src` through tsx, so there is no
dist hop | 1 failed (probe 1 after the restart), 3 passed, 1 expected
fail. The failure transcript's plugin row carries 0
`PackageServicePlugin` |
| leg B | `hydrateMetadataFromDb: true` in `createStandaloneStack`,
through `dist`: `@objectstack/runtime` rebuilt,
`ablation-dist-preflight` finds the marker in `index.js` and `index.cjs`
| 4 passed; the `it.fails` is red with `Expect test to fail` |
For the restore, `@objectstack/runtime` was rebuilt from pristine source
and `ablation-dist-preflight --absent` exited 0 (the marker is absent
from all 6 built files). It was checked again after the full package
build below.
## Tests and gates (at `8af659d715`)
- **The pin:** `pnpm --filter @objectstack/cli exec vitest run --project
integration --maxWorkers=2
test/package-restart-acceptance.integration.test.ts` gives 1 file: 4
passed, 1 expected fail.
- **Unit project:** `pnpm --filter @objectstack/cli exec vitest run
--project unit --maxWorkers=2` gives 224 files and 3158 tests passed.
That includes the tier partition pin
`test/vitest-tiers-partition.test.ts`.
- **Typecheck:** `pnpm --filter @objectstack/cli typecheck` exits 0. The
`check:test-typecheck` ledger is unchanged (3 files / 28 errors / 6
signatures). The new file is in the `tsconfig.test.json` program
(`--listFiles`: 1).
- **Rest of the integration project:** declared to CI. A new standalone
test file shares no state with the other 51 files.
- **ESLint, narrowed:** `eslint --no-inline-config --format json` on the
new file reports 1 file, 0 errors, 0 warnings. The population is
eslint's own `--print-config` for that file. Invariance: the config
enables no type-aware linting (`eslint.config.mjs` says so, and the
resolved `parserOptions` carry no `project`), so adding one file cannot
change any other file's verdict.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` from the real diff gives 48 commands,
and all 48 finish at exit 0. Two were NOT MEASURED on the first pass:
`check:dual-build-cjs-loads` (exit 3, PREREQUISITE NOT MET, 12 packages
without `dist`) and `check:type-check-debt` (exit 124, my 280 s
per-command timeout). Both exit 0 after a full package build (turbo,
72/72). `--ran` reports 48 derived, 48 run, 0 NOT-MEASURED, 0 UNRUN.
- **Citations:** `node scripts/check-issue-citations.mjs --base
2274894` exits 0 but reads 0 files. `packages/**/*.test.ts` is a
declared deferred surface of that gate, so its green says nothing about
this diff. The only number this file cites is #17676.
## Changeset
None; `skip-changeset` applies. `@objectstack/cli` ships `files:
["dist", "README.md", "CHANGELOG.md"]` and builds from `src`. After
`pnpm --filter @objectstack/cli build`, a grep of those paths for
`studioWritableIds` / `package-restart-acceptance` finds 0 files. The
control, `PackageServicePlugin`, is found in 1 `dist` file.
## Acceptance notes
- **H1 holds at this head.** The stock boot's banner lists
`PackageServicePlugin` once, and the install logs no in-memory-only
line.
- **Protocol warning on every restart.** Each restart warns `package
'com.example.leave' declares no engines.protocol range; rehydrating
without a compatibility check (ADR-0087)` for a package created with
Studio's create-dialog body, which sends no `engines`. This is an
observation and is not filed.
- **Harness duplication.** `bootServe` / `stop` now exist in two
`packages/cli/test` files (see above). Lifting them into
`test/helpers/serve-process.ts` is the `domain:cli` seat's call.
- **`origin/main` moved.** It moved 5 commits since this branch's base
`2274894cc`. The only overlap with the packages this pin exercises is
`packages/objectql/src/validation/rule-validator.ts` (validation-rule
traversal), which is not the hydration seam. I did not merge `main` in;
the merge queue rebuilds on the current `main`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Bvd69VPa6puiNzzPUroDBx)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent bf37b99 commit 7f1de2e
1 file changed
Lines changed: 396 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
0 commit comments