Repository navigation
Commit 27d7c2a
fix(cli): a --no-server boot takes the datasource admin routes' own degrade line, not an ordering fault (#24132)
Fixes #24022
Clause-②: no
## What was wrong
On a boot that composes no HTTP server, the boot diagnostics printed:
```
WARN [datasource-admin] route registration failed: [Kernel] Service 'http.server' not found (while plugin 'com.objectstack.cli.datasource-admin-routes' was initializing — a composition/ordering fault; registration order is not a contract.)
```
Nothing was at fault. `--no-server` composes no server on purpose. The
plugin read the server with `ctx.getService?.('http.server') ??
ctx.getService?.('http-server')`. The kernel's `getService` throws on a
name nothing registered (`packages/core/src/kernel.ts`, the `[Kernel]
Service '...' not found` throw, with the init-order suffix from
`describeInitOrderFault` in `plugin-order.ts`). So the `??` never ran.
The throw landed in the registration `catch`, and the plugin's own
degrade line was unreachable.
## The fix
`packages/cli/src/commands/serve.ts` only, in two plugins:
`com.objectstack.cli.datasource-admin-routes`, and its sibling
`com.objectstack.cli.unknown-hostname-guard`:
- The plugin moves out of `run()` into an exported factory,
`createDatasourceAdminRoutesPlugin(registerRoutes)`, next to
`createUnknownHostnameGuardPlugin`. That makes its `init` reachable from
a test against a real kernel. `run()` now calls
`kernel.use(createDatasourceAdminRoutesPlugin(registerDatasourceAdminRoutes))`.
Name, version and `optionalDependencies` are unchanged.
- `init` reads the server through the existing `readComposedHttpServer`
(reused, not changed): `http.server` only, inside a `try`. No server:
the plugin's own line, `[datasource-admin] http.server unavailable; REST
routes not installed`. A server composed and registration throws: `route
registration failed` with the error, as before.
- The old fallback to the deprecated `http-server` alias was never
reached: the canonical read threw first. Every in-repo producer of
`http-server` (the Hono server plugin and the http-conformance node
plugin) also registers `http.server`, the same instance. So every boot
that mounted these routes before still mounts them.
- The unknown-hostname guard (`createUnknownHostnameGuardPlugin`,
composed when `OS_ROOT_DOMAIN` is set) had the same trap, so its `init`
now reads the server through `readComposedHttpServer` too. It is a
one-line hunk. The read stays inside the existing `try`, and
`readComposedHttpServer` never throws. With no server, the boot now
takes `[unknown-hostname-guard] http.server unavailable; guard not
installed`. On a composed server whose install throws, it still warns
`install failed` with the error. The claim's file surface was widened
for this in comment `6228552643`.
- `scripts/slot-lookup-baseline.json`: `serve.ts` 9 to 6, because both
untyped two-lookup chains are gone (`check:slot-lookup` holds at 101
sites in 25 files).
## Pins
- `packages/cli/src/commands/serve-datasource-admin-routes.test.ts`
(unit tier, new). The plugin runs inside a real `ObjectKernel`, so the
throw it has to survive is the kernel's own:
1. premise: the kernel throws for an unregistered `http.server`;
2. no server: no `route registration failed`, no `ordering fault`,
exactly one `[datasource-admin]` line;
3. only `http.server` is asked for, never the alias;
4. CONTROL: a composed server gets the real routes, all under
`/api/v1/datasources`, and no `[datasource-admin]` line;
5. CONTROL: a composed server whose registration throws still warns
`route registration failed`, naming the error;
6. source pin, comments masked: `run()` mounts through the factory, and
no second inline copy exists.
- `packages/cli/src/commands/serve-no-server-banner.integration.test.ts`
(integration tier), against the built CLI:
- shapes 1 and 2 (production `--no-server --preset minimal`, and `--dev
--no-server`) carry exactly one `[datasource-admin]` line, and it does
not report a failed registration or an ordering fault;
- shape 3 (a default boot) is the CONTROL: `/api/v1/datasources` is not
404, while an unmounted sibling path is 404, and no `[datasource-admin]`
line prints;
- shape 4 (`--no-server` with a config-mounted `HonoServerPlugin`) has
the same control. It backs the changeset's claim that the routes still
mount on that shape. This round adds it.
- `packages/cli/src/commands/serve-unknown-hostname-guard.test.ts` (unit
tier, the guard's existing file):
1. a real `ObjectKernel` boot with no server: no `install failed`, no
`ordering fault`, and exactly one `[unknown-hostname-guard]` line, the
degrade line;
2. CONTROL: a real kernel boot with a composed server, ordered ahead by
`optionalDependencies`, logs `installed`, prints no
`[unknown-hostname-guard]` warning, and refuses an unmapped host with
404 `ENVIRONMENT_NOT_FOUND`;
3. CONTROL: a usable server whose raw app refuses the middleware still
warns `install failed`, naming the error;
4. the existing throwing service-resolution case now expects the degrade
line instead of `install failed`. The kernel throws on an unregistered
name, so a throwing read IS the no-server boot.
## Verification, all at head `6c33626a8`
- Unit tier: `pnpm --filter @objectstack/cli exec vitest run --project
unit --maxWorkers=2` reads 309 files, 4591 tests, all passed, exit 0.
- Integration, the touched file: `pnpm --filter @objectstack/cli exec
vitest run --project integration --maxWorkers=2
src/commands/serve-no-server-banner.integration.test.ts` reads 4 of 4
passed, exit 0. The other integration-tier files are declared to CI.
- `pnpm --filter @objectstack/cli typecheck` exits 0,
`check:test-typecheck` included. `tsc --noEmit --listFiles` compiles all
three touched test files.
- `pnpm exec eslint --no-inline-config` on the four changed `.ts` files
exits 0 with no output. The `.md` and `.json` files are outside eslint's
configured file set.
- Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derives 75 commands on `6c33626a8`, and all
75 exit 0. `--ran` reads `75 derived, 75 run, 0 NOT-MEASURED, 0 UNRUN`.
- The derivation warns that the tree is 3 commits behind `origin/main`
`c1c0a9094`, where 3 family files changed: `check-route-envelope.mjs`,
`engine-double-contract.pinned.json` and one `scripts/adr-anchors` file.
Those families ran the branch's copies. CI's merge-ref run is the
reading on main's copies.
- Also run, outside the derived set: `check:startup-registry-verdict`
(exit 0, 44 seams, none recording a verdict the boot can contradict).
- At the door, against the built CLI, with
`OS_ROOT_DOMAIN=example.test`:
- `os serve objectstack.config.ts --no-server --preset minimal` prints
`WARN [unknown-hostname-guard] http.server unavailable; guard not
installed` and `WARN [datasource-admin] http.server unavailable; REST
routes not installed`, and no ordering fault;
- a default boot, and a `--no-server` boot whose config mounts its own
`HonoServerPlugin`, each log `INFO [unknown-hostname-guard] installed`,
with no guard warning.
### Ablation, round 1 (the datasource admin routes, at `a12bf2f9b`)
The fix was committed first. The mutation, done with
`scripts/ablation-replace.mjs`, puts the pre-fix read back: the throwing
`getService` chain, inside the `try`. The anchor count went from 1 to 0,
and the blob went from `a48fc3da93e4` to `95b1a98c5f93`.
- Mutated leg: the CLI was rebuilt, and `ablation-dist-preflight
@objectstack/cli 'readComposedHttpServer(ctx)' --absent` passed.
- The unit file reads 1 failed, 5 passed. The failure is `takes its own
degrade line`.
- The integration file reads 2 failed, 2 passed. Shapes 1 and 2 failed,
each showing the card's exact `route registration failed ... a
composition/ordering fault` line. The two controls stayed green, as they
should.
- Restore leg: blob back to the HEAD blob, `git diff HEAD` empty, `git
status --porcelain` empty. The CLI was rebuilt, and the preflight found
the marker present again. Unit 6 of 6 and integration 4 of 4 passed.
- Unit case 3 (only `http.server` asked) stays green under the mutation,
because the old chain also never reached the alias once the canonical
read threw. It guards against an alias read coming back. It does not
discriminate this card's defect.
### Ablation, round 2 (the guard)
The fix was committed first (`6c33626a8`).
`scripts/ablation-replace.mjs` put the guard's pre-fix line back. The
anchor count went from 1 to 0, and the blob went from `a080f01a90c5` to
`a48fc3da93e4`, which is byte-identical to the previous head's
`serve.ts`. The test imports its subject from source (`./serve.js`), so
no rebuild sits in the path.
- Mutated leg: the guard file reads 2 failed, 36 passed. Both failures
are the cases that read a throwing service as no server: the real-kernel
no-server boot, which received the exact `[unknown-hostname-guard]
install failed ... a composition/ordering fault` line, and the
throwing-resolution case. Both controls stayed green.
- Restore leg: blob back to the HEAD blob, `git diff HEAD` empty, `git
status --porcelain` empty. 38 of 38 passed.
## Handover
This resumes the released claim's branch at `4b0a631575`. Every commit
was re-read hunk by hunk:
- kept `7177b4e8c` (the fix, and the new unit file, which landed in this
commit, not in `2e52e010a`), `2e52e010a` (the integration cases) and
`16be28cdd` (the baseline row, re-measured after this round's merge of
`origin/main` `50ccfa669`);
- corrected `acd285a2b`: the changeset's alias sentence now says the
fallback never ran, instead of reading like a narrowing;
- added the shape 4 control.
All verification above was re-run in this round.
## Acceptance notes
- The new unit file imports `serve.ts`, and on this shared box that
import costs about 18.8 s (tests 14 ms). A sibling that imports the same
module (`serve-unknown-hostname-guard.test.ts`) measured the same in one
run, 36.6 s of import across the two files. No budget line is declared,
since none of the three budget classes fits. This is a reading, not a
gate.
- `origin/main` moved to `c1c0a9094` after this branch's merge. The 3
commits touch `packages/rest`, `plugin-email`, `qa/dogfood`, `runtime`
tests and `spec/liveness`, none of them under `packages/cli`. `git
merge-tree` is clean, so they were not merged.
- `packages/runtime/README.md` shows a server plugin registering only
the deprecated `http-server` alias. Docs drift, noted here, not filed.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01BVm1pAXVXQ3PjC42j1kPFf)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent c1c0a90 commit 27d7c2a
6 files changed
Lines changed: 404 additions & 42 deletions
File tree
- .changeset
- packages/cli/src/commands
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
42 | 53 | | |
43 | 54 | | |
44 | 55 | | |
| |||
73 | 84 | | |
74 | 85 | | |
75 | 86 | | |
| 87 | + | |
| 88 | + | |
76 | 89 | | |
77 | 90 | | |
78 | 91 | | |
| |||
247 | 260 | | |
248 | 261 | | |
249 | 262 | | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
250 | 275 | | |
251 | 276 | | |
252 | 277 | | |
| |||
277 | 302 | | |
278 | 303 | | |
279 | 304 | | |
280 | | - | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
281 | 308 | | |
282 | 309 | | |
283 | 310 | | |
284 | 311 | | |
285 | 312 | | |
286 | 313 | | |
287 | 314 | | |
288 | | - | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
289 | 318 | | |
290 | 319 | | |
291 | 320 | | |
292 | 321 | | |
293 | 322 | | |
294 | 323 | | |
295 | 324 | | |
296 | | - | |
| 325 | + | |
297 | 326 | | |
298 | 327 | | |
299 | 328 | | |
300 | 329 | | |
301 | 330 | | |
302 | 331 | | |
303 | 332 | | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
304 | 338 | | |
305 | 339 | | |
306 | 340 | | |
| |||
311 | 345 | | |
312 | 346 | | |
313 | 347 | | |
314 | | - | |
| 348 | + | |
315 | 349 | | |
316 | 350 | | |
317 | 351 | | |
| |||
321 | 355 | | |
322 | 356 | | |
323 | 357 | | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
324 | 363 | | |
325 | 364 | | |
326 | 365 | | |
| |||
0 commit comments