Repository navigation
Commit 7f2f081
fix(cli,runtime): a host-config os serve composes a MetadataPlugin, so its declared apis are served; a matcher-less composition warns once (#23078)
Fixes #22989
Clause-②: no
The A + B slice of #22989, as the maintainer ruled in `6125390666` (「A+B
(推荐)」). Slice 1, PR #23046 (`0cbfb7c32c`), made the config door register
a stack's `api` items into the metadata service. This slice makes a
host-config `os serve` boot serve them, and makes a composition that
still cannot serve them say so once.
The card's pin is met on this branch's final tree (`66e7f58fd5`): `os
serve objectstack.config.ts` of `examples/app-showcase`, production
posture, answers `GET /api/v1/apps/showcase/tasks` with no session `401
UNAUTHENTICATED`. It answered `404 ENDPOINT_NOT_FOUND`.
## What changes
**A. `packages/cli/src/commands/serve.ts`, the host-config
composition.** A host config (its `plugins[]` holds plugin instances)
never reaches `createStandaloneStack`, so outside `os dev`'s artifact
door its `metadata` slot was the kernel's in-memory core fallback, which
has no `matchEndpoint`. When the dev artifact door does not compose, the
config is a host config (`isHostConfig`), and `plugins[]` holds no
`MetadataPlugin` of its own, the boot now composes:
```ts
new MetadataPlugin({ watch: false, config: { bootstrap: 'lazy' }, rootDir: hostRoot })
```
- No `artifactSource`: the door reads none of the stack. The AppPlugin
wrap stays the single registrar of the four ADR-0057 collections, and
its `securityMetadataRegistrar` stays the default `'app-plugin'`. ⛔ It
is never `'artifact-door'` for this door.
- `bootstrap: 'lazy'` and `rootDir: hostRoot` are census answers 1 and 2
below.
- It is `kernel.use`d at the dev door's site, after the HTTP server
plugin, with no catch: a boot that cannot compose its `metadata`
occupant fails rather than falling back quietly.
- The dev door's decision and construction are unchanged. Its
missing-artifact warning and the comment above it are reworded, because
both said the dev SSE endpoint is not mounted on that boot. That boot
now composes this door, which mounts the endpoint under
`NODE_ENV=development` (census 10). The warning now names what is
actually lost, the artifact reload: `Dev artifact reload not enabled: no
compiled artifact at PATH`.
**B. `packages/runtime/src/app-plugin.ts`, inside
`registerDeclaredApiEndpoints` only.** When the stack declares at least
one `api` item and the `metadata` slot has no `matchEndpoint`, the
method logs one `warn` per app per boot, with structured fields `{
appId, count }`. The registration that follows is unchanged. The warning
reads, measured on an `OS_MODE=off` boot of a scratch stack with one
declared endpoint:
```text
WARN [AppPlugin] app 'com.example.fxb' declares 1 api endpoint(s), but this boot's 'metadata' service has no endpoint matcher (matchEndpoint), so every declared path answers 404 ENDPOINT_NOT_FOUND. Compose @objectstack/metadata's MetadataPlugin on this kernel to serve them. {"appId":"com.example.fxb","count":1}
```
The same boot on BASE printed nothing and answered the same 404. No
second matcher: the remedy is the composition's. The read is in
`start()`, after every `init()` has registered its services and the
kernel has injected its fallbacks.
## Census, measured before the composition edit
How each item was measured: `examples/app-showcase`, `os serve
objectstack.config.ts --no-ui` through the built entry
`packages/cli/bin/run.js`, cwd the app, `NODE_ENV` unset,
`OS_CLOUD_URL=off`, `OS_DATABASE_URL=file:` a fresh temp sqlite per
boot, `OS_AUTH_SECRET` set, `--log-level info` for the census boots.
BASE is a second worktree at `0cbfb7c32c`. AFTER is this branch at
`88abc71212`, before either merge of `main`. A first sign-up on a
production database is the bootstrap platform admin, and its session
reads `/meta`. Dev-posture boots sign in as the seeded dev admin
instead.
1. **The loader.** Measured with `MetadataPlugin` built from the BASE
tree, `rootDir` = the showcase directory, no artifact, `init` + `start`
on a capturing context. `eager` (the default) runs
`_loadFromFileSystem`, which logged `Metadata loading complete
{"totalItems":0,"registeredTypes":28}`. It loads nothing from the
showcase, so nothing double-registers there. `lazy` logged `lazy
bootstrap — skipping filesystem priming` and registered nothing either.
**Chosen: `lazy`**, the artifact door's shape: that door loads its
artifact and never runs the priming pass. `eager` with no artifact scans
`rootDir/TYPE/` and registers whatever it finds beside the wrap's copy,
without the stack's package stamp. The source pin shows the option is
load-bearing: a root that holds `object/probe_door.object.json` primes
`object/probe_door` under `eager` and nothing under `lazy`. After the
change, the showcase boot logs `lazy bootstrap` once and never logs
`Loading metadata from file system`.
2. **The repository.** Every mode but `artifact-only` attaches a
`FileSystemRepository`. After the change the boot logs
`FileSystemRepository attached
{"repoRoot":".../examples/app-showcase/.objectstack/metadata","watch":false}`.
Attaching writes nothing: no `.objectstack/metadata` exists anywhere in
the worktree after the boot, and none in a scripted run over an empty
root. **`rootDir` must be `hostRoot`**, the served app's root and the
value `createStandaloneStack` hands its own door as `projectRoot`.
Without it the plugin roots at `process.cwd()`. Measured from the
repository root (`os serve examples/app-showcase/objectstack.config.ts`,
cwd the worktree root): `repoRoot` is still the app's directory.
3. **Persistence.** The host-config boot already provisions the
`sys_metadata` storage objects through ObjectQL. The primary database
holds 108 tables before and after, an identical list that includes
`sys_metadata`, `sys_metadata_activation`, `sys_metadata_audit`,
`sys_metadata_commit` and `sys_metadata_history`, all with 0 rows. The
door's `Registered system metadata objects` (4 objects) adds no table
and no schema-sync step. Under the dev posture `sys_metadata_audit`
lives in the telemetry sibling, unchanged. **DatabaseLoader:** no
in-tree code calls `setDatabaseDriver` or `setDataEngine` (`git grep`
over every non-test `.ts` / `.mjs` outside `metadata-manager.ts`: 0
hits). No `DatabaseLoader configured` line appears on either door, so a
cold boot has no `sys_metadata` read-through. Reads go to the manager's
registry and its `FilesystemLoader` over `hostRoot/TYPE/`, the same pair
the artifact door's manager has.
4. **What `/meta` serves.** `GET /api/v1/meta/TYPE` with an admin
session, BASE vs AFTER: status, item count and a hash of the whole body
are equal for all 22 types read. No type's answer moves:
| type | BASE | AFTER | artifact door (`os serve --dev` over `dist/`) |
|---|---|---|---|
| app | 200 / 3 | same body | same body |
| view | 200 / 36 | same body | same body |
| page | 200 / 34 | same body | differs (pre-existing; equal before and
after) |
| dashboard | 200 / 4 | same body | differs (pre-existing; equal before
and after) |
| report, action, flow, webhook, hook, mapping, analytics_cube,
connector, email_template, doc, book | 200 / 4, 71, 30, 1, 5, 1, 1, 4,
25, 9, 1 | same bodies | same bodies |
| object, position, permission, capability, sharing_rule, api,
datasource | 200 / 108, 16, 17, 11, 2, 2, 2 | same bodies | same bodies
|
`/meta` already served these 15 types from the ObjectQL registry. The
metadata SERVICE lacking them on the config door (`6125234382`) is still
true after the change, and `/meta` does not read it alone.
`AppPlugin.registerUnclaimedTopLevel` moves nothing here: the showcase
carries no `packages[]`, and the residual rule logged no line. One
served face does move, as it should: `GET /api/v1/openapi.json` listed
no `/api/v1/apps/showcase/` path on BASE and lists both declared
endpoints (`/tasks`, `/inquiries/purge`) after. On BASE, `/meta/api` and
`/openapi.json` logged `no endpoint matcher is reachable` and omitted
both items. Neither line appears after. At the final tree `66e7f58fd5`,
after merging `main`, every body still equals BASE except
`email_template`. Its only differing keys are `category` and `isSystem`,
which `main` retired in #22990, not this change.
5. **The cluster bridge.** `Runtime` composes
`MetadataClusterBridgePlugin` on every boot
(`packages/runtime/src/runtime.ts`). Its lane 1 calls
`attachClusterPubSub` on the `metadata` service at `kernel:ready`. BASE
host-config boot: `WARN MetadataClusterBridgePlugin: metadata service
does not expose attachClusterPubSub(); cross-node cache invalidation
disabled`, once. After: 0, because the in-process memory driver now
takes the `debug` skip. A scripted run of the bridge over a fake cluster
service, BASE tree:
| occupant | driver | lane 1 | `metadata.changed` subscriptions |
published |
|---|---|---|---|---|
| core fallback | memory | warn | 0 | 0 |
| core fallback | redis | warn | 0 | 0 |
| MetadataManager | memory | debug skip | 0 | 0 |
| MetadataManager | redis | `bridged metadata.changed → cluster.pubsub`
| 1 | 1 per `register()`; 0 per `registerInMemory()` |
So with a remote driver this composition attaches lane 1, and that is
**the first `metadata.changed` bridge on this boot shape, not a second
broadcast.** The fallback had no publisher. Lane 2 (`metadata.mutated`,
the protocol's persistence fan-out) is a different channel with a
different owner, and it is unchanged. The boot's own `registerInMemory`
writes publish nothing.
6. **Boot cost.** 7 runs of each, interleaved BASE / AFTER, under the
shared verification lock: a shared box, so read the ratio. Command:
`node packages/cli/bin/run.js serve objectstack.config.ts --port P
--no-ui`, cwd `examples/app-showcase`, env as above, default log level.
Median time to the first HTTP answer: 8472 ms vs 8407 ms. Median to the
ready banner: 8473 ms vs 8458 ms. Median RSS 1.5 s after ready: 460980
kB vs 459340 kB. Ranges: listen 7538–9696 vs 7566–8691 ms. No measurable
cost.
7. **The dev routes.** Production, after: `GET
/api/v1/dev/metadata-events` answers 404, and the boot prints `dev
metadata-HMR endpoints NOT mounted ... NODE_ENV unset, treated as
production`. Pinned in process (below). Development posture, after, with
no compiled artifact (`os serve --dev` with no `dist/`, and the
supervisor-named missing artifact): 404 before, 200 `text/event-stream`
after. That is the one route that moves, and only under
`NODE_ENV=development`. It is the same route every other config's dev
boot mounts.
8. **The security registrar.** The wrap keeps `'app-plugin'`.
`Registered stack-declared security metadata
{"appId":"com.example.showcase","count":23}` is logged once before and
once after, and the door logs no registration of its own. The `/meta`
bodies of `position` (16), `permission` (17), `capability` (11) and
`sharing_rule` (2) are equal before and after (item 4). Pinned: the
source pin holds the one `'artifact-door'` spelling to the dev door's
expression, and the integration boot serves the fixture's position and
capability exactly once each.
9. **The order.** From the AFTER boot's kernel log: `Phase 1: Init
plugins` at 03.408. `Initializing Metadata Manager` at 03.553, then
`MetadataPlugin providing metadata service (primary mode)` at 03.556, so
the slot is filled during Phase 1. `Phase 2: Start plugins` at 03.736.
`Registered stack-declared api endpoints in metadata registry
{"count":2}` at 05.274, inside AppPlugin's `start()`. The slot holds the
`MetadataManager` when `registerDeclaredApiEndpoints` runs. That holds
by construction too: `init()` registers, `start()` reads, and the kernel
injects a core fallback only for a service that no `init()` provided.
10. **Which boots.** The new composition reaches:
- (a) a non-dev `os serve` of a host config;
- (b) `os start` beside a host config: its `serve` child boots non-dev
and loads the host module when the artifact is the config's own compiled
output, so it shares this code and changes too. HTTP NOT MEASURED for
`os start` itself;
- (c) `os serve --dev` of a host config with no compiled artifact
(measured 404 → 401);
- (d) the dev path with a supervisor-named missing artifact (measured
with `OS_INTERNAL_ARTIFACT_PATH` naming an absent
`dist/objectstack.json`: 404 → 401);
- (e) a host config under `--no-server` (composes; not measured over
HTTP, there is none).
It does not reach:
- the dev artifact door's boots (`os dev`, `os serve --dev` with `dist/`
on disk), which stay 401 before and after;
- every library boot (a bare config, the artifact fallback), which
already composes a `MetadataPlugin`;
- an `OS_MODE=off` (or `bootMode: 'off'`) bare config, which is not a
host config. That boot keeps the fallback and now prints B's warning
(measured: 1 warning, still 404).
## HTTP before and after
No session; `GET /api/v1/apps/showcase/tasks` (declared, `authRequired`)
and an undeclared path under the same namespace:
| boot | BASE declared | AFTER declared | undeclared (both) |
|---|---|---|---|
| `os serve objectstack.config.ts` (production, host config) | 404
`ENDPOINT_NOT_FOUND` | **401 `UNAUTHENTICATED`** | 404 |
| the same, from the repository root | — | 401 | 404 |
| `os serve --dev`, no `dist/` | 404 | **401** | 404 |
| `os serve --dev`, named artifact missing | 404 | **401** | 404 |
| `os serve --dev` over `dist/` (the artifact door) | 401 | 401 | 404 |
| `os dev --fresh --no-watch` | 401 | 401 | 404 |
| final tree `66e7f58fd5`, production host config | — | 401 | 404 |
`POST /api/v1/apps/showcase/inquiries/purge` (declared) moves 404 → 401
on the same three rows. With a session, the declared `GET` answers 200
with records after. The plain artifact-fallback boot of the showcase (no
config, `OS_ARTIFACT_PATH`) refuses at BASE: its connector providers are
code in `plugins[]`, which a JSON artifact cannot carry. The artifact
control for this stack is therefore the dev door.
## Tests
At the final tree `66e7f58fd5`, each run went through
`scripts/pm/os-verify-lock.sh` and printed `VERDICT command-exit 0`:
- `pnpm --filter @objectstack/runtime exec vitest run --project local
--maxWorkers=2`: `Test Files 360 passed (360)`, `Tests 5156 passed | 19
skipped (5175)`.
- `pnpm --filter @objectstack/runtime typecheck`: exit 0,
`check:test-typecheck: OK`. The new test is in `tsconfig.test.json`'s
program (`--listFiles`, 1 hit).
- `pnpm --filter @objectstack/cli exec vitest run --project unit
--maxWorkers=2`: `Test Files 284 passed (284)`, `Tests 4276 passed
(4276)`.
- `pnpm --filter @objectstack/cli typecheck`: exit 0,
`check:test-typecheck: OK`. The three changed `src/` test files are in
`tsconfig.json`'s program, and the `test/` one is in
`tsconfig.test.json`'s.
- `pnpm --filter @objectstack/cli exec vitest run --project integration
--maxWorkers=2 test/serve-host-config-metadata-door.integration.test.ts
test/artifact-flag-precedence.integration.test.ts`: `Test Files 2 passed
(2)`, `Tests 14 passed (14)`. The rest of the cli integration tier is
declared to CI.
- The pins named to stay green are green inside those runs:
`serve-host-config-security-registrar.pin.test.ts` and
`serve-host-config.test.ts` (cli unit),
`artifact-flag-precedence.integration.test.ts` (above), and PR #23046's
`app-plugin-config-door-api-items.test.ts` (runtime).
New and changed tests:
- `packages/runtime/src/app-plugin-api-matcher-absent-warning.test.ts`
(B; 3 cases on real kernel boots of `createStandaloneStack`'s plugins
over an absent artifact, plus the CLI's wrap).
- The in-memory-fallback composition logs exactly one warning, with meta
`{ appId, count: 1 }`.
- CONTROL: with a `MetadataManager`, no warning, and the declared path
matches.
- CONTROL: with no `apis`, no warning.
- The warning is identified by its named subjects (the app id, the
remedy plugin) and its fields, never by its prose.
-
`packages/cli/src/commands/serve-host-config-metadata-door.pin.test.ts`
(A; 6 unit cases).
- Structure: one named decision; its three-part gate; no
`artifactSource` or `artifactWatch`; `lazy`; `rootDir: hostRoot`; the
wrap's `'artifact-door'` never keyed on this door; `kernel.use`d after
the HTTP server plugin.
- Behavioural: under `NODE_ENV=production` the door mounts no dev
metadata-events route (CONTROL: under `development` it mounts `GET
/api/v1/dev/metadata-events`).
- Behavioural: `lazy` is load-bearing. The `eager` CONTROL primes
`object/probe_door` from the same root, `lazy` primes nothing, and no
`.objectstack/metadata` is written.
-
`packages/cli/test/serve-host-config-metadata-door.integration.test.ts`
(A; 4 integration-tier cases). A host-config fixture (a plugin instance,
one object, one `authRequired` api, one position, one capability),
booted with `os serve`.
- With no `dist/`, the declared path answers `401 UNAUTHENTICATED`, and
an undeclared path answers `404 ENDPOINT_NOT_FOUND`.
- The position and the capability are each served exactly once.
- CONTROL: with the compiled twin on disk, the artifact door answers the
same 401.
- `serve-host-config-security-registrar.pin.test.ts`: `new
MetadataPlugin(` count 1 → 2, with each construction site counted once
and the artifact source spelled once. The missing-artifact warning
literal follows the reword.
- `packages/cli/src/utils/boot-preparation-parity.test.ts`:
`isHostConfig` is declared in `SERVE_ONLY` with its reason. The one-shot
boot always composes the standalone stack's `MetadataPlugin`.
## Ablations
Both ablations ran through `node scripts/ablation-replace.mjs` in wrap
mode: the restore is trap-armed and the anchor and replacement are
counted on disk. Each restore was proven by blob == HEAD and an empty
`git diff HEAD`. Both moved in the expected direction: the positive pin
turned red.
- **A, the composition line.**
- Mutation: anchor `await kernel.use(hostConfigMetadataDoor);` went x1 →
x0, and the comment `// ablation-22989: the door is not composed` went
x0 → x1. Blob `0fa07d1065` → `f28bcffd47`.
- The integration file runs `serve.ts` from source through the tsx
entry, so no build sits in this loop.
- Result: `Tests 1 failed | 3 passed (4)`. The 401 pin failed with `-
"code": "UNAUTHENTICATED", "status": 401` / `+ "code":
"ENDPOINT_NOT_FOUND", "status": 404`. The undeclared-404, security-once
and artifact-door controls stayed green. Restored to `0fa07d1065`.
- A first run of the same mutation against an earlier draft of the file
booted the BUILT entry. That run rebuilt the cli, and `node
scripts/ablation-dist-preflight.mjs @objectstack/cli 'await
kernel.use(hostConfigMetadataDoor)' --absent` reported the marker absent
from all 612 built files. It turned the same single leg red (`expected {
status: 404, …(1) }`). Its restore leg rebuilt, and the preflight read
the marker back in `dist/commands/serve.js` with the tree clean.
- **B, the warning guard.**
- Mutation: anchor `if (typeof metadata?.matchEndpoint !== 'function')
{` went x1 → x0, with the guard made false. Blob `afe89beb7f` →
`00b2d3da1f`. The test imports `./app-plugin.js` from source.
- Result: `Tests 1 failed | 2 passed (3)`, `expected [] to have a length
of 1 but got +0`. Both controls stayed green. Restored to `afe89beb7f`.
- **`bootstrap: 'lazy'`** carries its ablation inside the source pin:
the `eager` CONTROL leg primes the item that `lazy` does not.
## Gates
Derivation:
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`, with no paths, at `66e7f58fd5`: 69
commands. The change set is 8 paths vs merge base `0a29bec6a`: committed
8, working tree 0, untracked 0.
- The explicit-path derivation over this diff's paths, at `e61879428f`,
printed the identical 69.
- Added to the 69: the 5 artifact-roster gates whose roster sits under
these paths, and `check-startup-registry-verdict` (B adds a `warn` that
reads a service in `start()`).
Results:
- 75 commands, each run with its exit code captured before any pipe, at
`66e7f58fd5`. All 75 exit 0.
- Reconciliation: `Run reconciliation — 69 derived, 69 run, 0
NOT-MEASURED, 0 UNRUN`; `a DERIVED zero — all 69 recorded an exit code
and none of them is 3`.
- `check:dual-build-cjs-loads` needed eight unrelated packages' `dist/`
first, rebuilt from the turbo cache. Its first-pass answer at
`e61879428f` was exit 3, PREREQUISITE NOT MET: a non-measurement, not a
red.
- The final derivation flagged a stale tree. `origin/main` gained two
commits after this branch's last merge:
`packages/plugins/organizations`, `packages/verify` and
`scripts/check-verify-stand-in-erasure.mjs`. They are disjoint from this
diff and are not merged.
```text
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-changeset-no-major.mjs --self-test :: exit 0
node scripts/check-ci-filter-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-keyed-text-bounds.mjs :: exit 0
node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0
node scripts/check-platform-object-tenancy-census.mjs :: exit 0
node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-registry-log-declared.mjs :: exit 0
node scripts/check-registry-log-declared.mjs --self-test :: exit 0
node scripts/check-rest-log-spy-declared.mjs :: exit 0
node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0
node scripts/check-system-context-census.mjs :: exit 0
node scripts/check-system-context-census.mjs --self-test :: exit 0
node scripts/check-undeclared-dep-imports.mjs :: exit 0
node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:cli-test-child-env :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:i18n :: exit 0
pnpm check:i18n-coverage :: exit 0
pnpm check:i18n-walk-parity :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:objectui-changeset :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:route-envelope :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:stack-collection-maps :: exit 0
pnpm check:swallow-census-controls :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
node scripts/check-changeset-fixed.mjs :: exit 0
pnpm check:authz-resolver :: exit 0
pnpm check:error-code-casing :: exit 0
pnpm check:filter-alias-parity :: exit 0
pnpm check:route-ledger-census :: exit 0
node scripts/check-startup-registry-verdict.mjs :: exit 0
```
## Lint, a proven narrowing
At `66e7f58fd5`, over the 7 changed TypeScript files; the repo-wide
`pnpm lint` is CI's.
1. **Population, from eslint's own config.** `eslint --no-inline-config
--print-config` resolves a rule set for every changed `.ts` file: 5
rules each, 6 for `app-plugin.ts`. It resolves none for the `.md`
changeset.
2. **Count, from `--format json`.** `files=7 errors=0 warnings=0`, exit
0.
3. **Invariance.** `eslint.config.mjs` never enables type-aware linting:
no `parserOptions.project` and no `projectService` in any block. Its
only file reads are two baseline JSONs at config load
(`scripts/slot-lookup-baseline.json`,
`scripts/query-options-erasure-baseline.json`), and this diff touches
neither. So no untouched file's verdict can move.
## Acceptance notes
- **Line 1 is `Fixes #22989`.** The card's pin is met on the final tree:
production `os serve` of `examples/app-showcase` answers 401 on the
declared path, where it answered 404.
- **The claimed file surface was exceeded twice, in `packages/cli` test
files only.**
- `packages/cli/src/utils/boot-preparation-parity.test.ts` gains one
`SERVE_ONLY` entry. `serve.ts` now calls `isHostConfig` from a
boot-preparation module, and that pin requires every such call to be
either a step or a declared serve-only read. Without the entry the cli
unit tier is red (`1 failed | 283 passed`, measured).
- The existing `serve-host-config-security-registrar.pin.test.ts` is
edited, as described under Tests.
- **The dev door's missing-artifact warning is reworded.** The door's
decision and construction are unchanged. Its warning and comment said
the dev SSE endpoint is not mounted on that boot, and that boot now
composes this door, which mounts it under development.
- **The integration boots run the source entry, in the development
posture.** `check:cli-test-child-env` pins the population of built-entry
spawners to a named set of seven files, in a script outside this claim.
The production-only facts are pinned in process (the dev route) and were
measured over HTTP on the showcase (census 3–8).
- **No dogfood pin.** The dogfood harness boots through
`@objectstack/verify`'s `bootStack`, which composes no `MetadataPlugin`,
so it cannot boot this composition. A showcase dogfood boot is exactly
where B's warning now speaks.
- **B returns early for a `packages[]` bundle.** It returns before
reading `apis`, so a multi-package stack's top-level residual reaches a
matcher-less slot without the warning. No in-repo stack declares `apis`
inside `packages[]`. Carrier: none.
- **`os serve --dev` with no compiled artifact now mounts `GET
/api/v1/dev/metadata-events`.** It is inert there: there is no artifact
to reload. Every other config's dev boot already mounts it.
- **Misleading prose on a matcher-less composition.** There, `GET
/api/v1/openapi.json` refuses a stored `api` item for its runtime
`_diagnostics` key (`unrecognized_keys`). It then logs that the item
"does not satisfy ApiEndpointSchema … fix the declaration rather than
the document". Measured on BASE's showcase boot, for both items. This
change takes the host config off that path. Its remaining reach (an
`OS_MODE=off` boot, lean kernels) is NOT MEASURED. Noted, not filed.
Carrier: none.
- **`os start` beside a host config shares this code and was not
separately measured over HTTP.**
---
_Generated by [Claude
Code](https://claude.ai/code/session_01VoSxBQujKLZKPwK2u5ehQ6)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 9b37edb commit 7f2f081
8 files changed
Lines changed: 753 additions & 27 deletions
File tree
- .changeset
- packages
- cli
- src
- commands
- utils
- test
- runtime/src
| 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 | + | |
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
124 | 130 | | |
125 | 131 | | |
126 | 132 | | |
| |||
145 | 151 | | |
146 | 152 | | |
147 | 153 | | |
148 | | - | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
149 | 157 | | |
150 | | - | |
| 158 | + | |
151 | 159 | | |
152 | 160 | | |
153 | 161 | | |
| |||
0 commit comments