Repository navigation
Commit 9a4182a
fix(spec,runtime,cli)!: the in-memory engine is no longer a boot store — every boot door refuses it and names SQLite instead (#21598)
Fixes #21492
Fixes #21572
Clause-②: yes (narrowing)
## What this lands
One landing, as triage ruled (`5969348039`, (ii)): #21572's spec row and
#21492's runtime and CLI halves. The in-memory (mingo) engine is retired
as a **boot store**, at its declaration, on both hosts. It refuses every
tenant-scoped read by design, so a boot on it signed the seeded admin in
and then answered `503` to every data read. Every boot door now refuses
it with one sentence that names the replacement: `os dev --fresh` for a
throwaway database, or the URL `:memory:` for SQLite's own in-memory
database.
- **spec** (`packages/spec/src/data/driver/config-registry.zod.ts`, the
row from `d480ef0d70` plus its stale doc comments): `memory`, `mingo`
and `in-memory` move from the selection face to `contractOnlyAliases`,
beside `inmemory`. `DATABASE_DRIVER_SELECTION_ALIASES`,
`DATABASE_DRIVER_SELECTION_IDS`, the `--database-driver` allowlist and
`--help` drop them by derivation. `resolveDriverId` is unchanged, so a
stored `datasource.driver: memory` still parses. No artifact moves
(`check:generated`: all 15 up to date).
- **runtime**:
- `resolveProjectDatabaseUrl`: the `memory-driver` rung becomes a
refusal that stands **ahead of every rung**. `ProjectDatabaseUrlSource`
loses `'memory-driver'`.
- `resolveStandaloneDatabase`: the URL detection recognises `memory://`
and `mingo://`, and `refuseRetiredMemoryEngine` refuses them. It names
the source: the typed URL, `OS_DATABASE_URL`, or the project's declared
default datasource.
- `unsupportedDriverMessage` gives a spelling of the engine the
retirement sentence, not the typo list.
- `StandaloneDatabaseDriverSchema` now enumerates the selection face.
The dispatch loses its `memory` arm, and
`ResolvedStandaloneDatabase.driver` can no longer name it.
- The sentence lives in one place, `retiredMemoryEngineMessage`, with
the predicate `namesRetiredMemoryEngine`. Both are exported for the CLI.
- **cli**:
- `inferDriverTypeFromUrl` refuses `memory://` and `mingo://` with
`UnsupportedDriverError` (`recognized: false`).
- `resolveStorageDefinition` refuses the spellings with the same
sentence. The `memory` definition arm is gone.
- The `--database` help of `dev` and `start` offers `:memory:` instead
of `memory://`.
- The `storage-driver.ts` #3276 note and its module header are
rewritten: the header now says which boots reach that file (legacy
only). The parity pin's header, the `database-driver-flag.ts` doc and
serve's comments are rewritten too.
- **docs**: `deployment/cli.mdx`,
`deployment/environment-variables.mdx`, `data-modeling/drivers.mdx`,
`plugins/index.mdx`.
- **QA** (`docs/qa/platform-checklist/areas/cli.json`, revisions bumped
with history):
- `cli.dev-boot-contract`: the memory tier becomes a REFUSED variant,
with a negative and the `:memory:` variant added.
- `cli.migrate-duplicates-inventory`: the `no_sql_seam` negative cites
the seam-double pin.
- devx's rider `5967974683`: `cli.build-own-contract`'s six live strings
name `this.exit(2)`. Its two dated history entries keep their wording,
because they record what was true when written.
- **ledger**: `scripts/driver-memory-census.ledger.json`'s CLI row `why`
no longer claims that a boot spelling routes to the driver.
- **one changeset** with the narrowing kit: `fix(...)!`, BREAKING,
`minor` for the three packages, and the ADR-0087 marker `not-required
(no-migration-prescription)`.
The `driver-memory` package is unchanged. A declared non-default
`driver: 'memory'` datasource and a directly constructed
`InMemoryDriver` still build: per the ruling, neither is a boot
spelling.
## A1: reach, measured on both trees
**Before**, on `origin/main` `901e7cf13a` with `examples/app-crm`
(closure build exit 0, 60/60 tasks):
| invocation | result |
|---|---|
| `os dev --fresh --database-driver memory` | sign-in 200; `GET
/api/v1/data/crm_lead` **503** `SERVICE_UNAVAILABLE`; the banner shows
`Database: file:/tmp/objectstack-dev-…/data/objectstack.db` beside
`Driver: _InMemoryDriver → (in-memory)` (the banner finding) |
| `os migrate plan --database-url memory://qa` | **exit 0**, `datasource
'default': connected (driver=memory …)` |
| `os dev --fresh` (control) | sign-in 200; crm_lead **200**;
`SqlDriver(better-sqlite3)` |
**After**, on this branch (`133871a251`; the refusal code has not
changed since):
| door | result |
|---|---|
| `os dev --fresh --database-driver memory` | exit 2 at flag parse
(oclif: `Expected --database-driver=memory to be one of: sqlite,
sqlite-wasm, postgres, mysql, mongodb, turso`) |
| `os start --database-driver memory` | exit 2, same |
| `OS_DATABASE_DRIVER=memory os dev --fresh` (and `mingo`) | exit 1
before any `Database:` row, naming `--fresh` and `:memory:` |
| `OS_DATABASE_DRIVER=in-memory os start` | exit 1, same sentence |
| `os dev --fresh --database memory://x` | the parent prints `Database:
memory://x`; the serve child refuses, exit 1 |
| `os migrate plan --database-url memory://qa` / `mingo://qa` | exit 1,
`[StandaloneStack] The database URL "…" selects the in-memory (mingo)
engine …` |
| `os migrate duplicates --database-url memory://qa` | exit 1,
`{"error":"boot_failed", …}` with the sentence |
| `OS_MODE=legacy OS_DATABASE_DRIVER=memory os serve --dev` | exit 1
(legacy CLI host) |
| `OS_MODE=legacy OS_DATABASE_URL=mingo://x os serve --dev` | exit 1
(legacy CLI host, inference) |
| `OS_ARTIFACT_PATH=… OS_DATABASE_URL=memory:// os serve --dev`
(artifact path, empty cwd) | exit 1 |
| `os dev --fresh` (control) | crm_lead **200**,
`SqlDriver(better-sqlite3)` |
| `os dev --database :memory:` (the replacement) | crm_lead **200**,
`Database: :memory:`, `SqlDriver(better-sqlite3) → :memory:` |
## A2: which doors reach which resolver (symbol walk)
- **Runtime host** (`resolveStandaloneDatabase`, which calls
`resolveProjectDatabaseUrl`), reached by:
- `os serve` over a config (`createStandaloneStack`) and over an
artifact (`createDefaultHostConfig`, then `createStandaloneStack`);
these are the serve children of `os dev` and `os start`;
- every `bootSchemaStack` user: `os migrate` `apply`, `plan`, `meta`,
`duplicates`, `account-issuer`, `audit-metadata-bodies`,
`files-to-references`, `multi-value-columns`, `recorded-by`, `resume`,
`summary-nulls` and `value-shapes`, plus `os meta resync`, `os secret
orphans` / `rewrap` and `os storage orphans`;
- `probeMigrationTarget`, the pre-boot probe of seven migrate
subcommands;
- every embedder.
- **The shared resolver alone** (`resolveProjectDatabaseUrl`), reached
by the `os dev` and `os start` parents (`resolveDevDatabase`,
`resolveStartDatabase`) and by `os db clean`.
- **CLI host** (`resolveDriverType` with `resolveStorageDefinition`),
reached by `os serve`'s legacy path only: `OS_MODE=off|none|legacy`,
`bootMode: 'off'`, or a host config (`shouldBootWithLibrary`).
- One refusal per host, at the resolver. No command carries a copy.
## A3: refusal shape and code
The refusals reuse each host's existing shape for an unrecognised driver
or scheme: the runtime's plain `Error`, prefixed `[StandaloneStack]`
inside the standalone stack, and the CLI's `UnsupportedDriverError`. No
ledgered error code is involved, and the error-code ledger is not
edited. The CLI flag door is oclif's own allowlist refusal, derived from
the spec (see Acceptance notes).
## A4: parity pin
`driver-vocabulary-parity.test.ts` is the pin, with no exemption:
- The accept rows still iterate the selection face, so the three
spellings leave them by derivation.
- A new row asserts which spellings the table withdrew: contract face
yes, selection face no, derived from `DRIVER_ID_ALIASES`.
- New refuse rows: both hosts refuse `memory` / `mingo` / `in-memory`,
in dev and in prod, and both name `--fresh` and `:memory:`.
- New URL-door rows: both hosts refuse `memory://`, `memory://named`,
`mingo://` and `MINGO://upper` with no driver named.
- A control: both hosts accept `:memory:` as `sqlite`.
- The `mingo://` cross-host divergence (the CLI accepted it, the runtime
called it an unknown scheme) is closed: both hosts now recognise and
refuse it.
## A5: the census ledger row
`scripts/driver-memory-census.ledger.json`, gated by
`scripts/check-driver-memory-census.mjs`. The file is under `scripts/`:
it is not `packages/spec` and not a governed surface. The CLI manifest
row's `why` is rewritten, its axis is unchanged, and
`check:driver-memory-census` is green.
## A6: reverse verification
Both legs were run through `scripts/ablation-replace.mjs` on the
committed tree (`b0b835ba75`), in WRAP mode, with a trap that restores
from `HEAD` by absolute path.
- **Runtime leg.** Three sites were planted, each behind an
`OS_ABLATION_RUNTIME_*` env marker: the rung refusal,
`refuseRetiredMemoryEngine`, and the `unsupportedDriverMessage` branch.
Each anchor went 1 to 0, and each blob changed.
- Runtime rebuilt (exit 0); `ablation-dist-preflight` found all three
markers in `dist/` (exit 0 each).
- Runtime pins: **14 red** of 124, in 4 files.
- CLI: `dev-default-db` 2 red; the parity pin 7 red, each failing on its
**runtime-half** lines (`:231`, `:254`).
- `storage-driver.test.ts` (the CLI host): **green**.
- Restore: blob equals `HEAD`, `git diff HEAD` empty. Runtime rebuilt;
`preflight --absent` exit 0 for all three markers.
- **CLI leg.** Two sites were planted in `storage-driver.ts`, the
inference refusal and the spelling refusal; the anchors went 1 to 0. The
tests import that file from source, so no build was involved.
- `storage-driver.test.ts`: **9 red**. The parity pin: 7 red, each
failing on its **CLI-half** lines (`:225`, `:248`).
- The other host stays **green**: runtime pins 124/124, and
`dev-default-db` (runtime-backed).
- Restore: blob equals `HEAD`, 0 markers left, `git diff HEAD` empty.
## Tests and gates (at `b0b835ba75`)
- **Full suites:**
- `@objectstack/runtime` (all projects): 319 files, 5210 passed and 19
skipped.
- `@objectstack/cli` `--project unit`: 254 files, 3742 passed.
- `@objectstack/spec`: 657 files, 18834 passed and 1 todo.
- **CLI integration**, the 19 integration-tier files that boot `os
migrate` / `bootSchemaStack` or spawn `serve` with a store flag or URL:
19 files, 210 passed and 1 skipped. `duplicates.null-seam.test.ts` now
boots nothing, so the tier predicate files it under `unit`.
- **Typecheck:** runtime, cli and spec typecheck all exit 0,
test-typecheck debt held.
- **Lint:** the full `pnpm lint` exits 0.
- **Spec artifacts:** `@objectstack/spec check:generated` exits 0.
- **Derived gates:** `dispatch-gates --commands` (no paths) derived 121
for the 31 changed paths, and all 121 exit 0. `--ran` reports 121 run
and 0 NOT-MEASURED. Three of them (`check:skill-examples`,
`check:dual-build-cjs-loads`, `check:i18n-coverage`) first exited 3 for
a missing build. They were re-run after a full `turbo run build
--filter=!@objectstack/docs` (exit 0) and exit 0.
## Acceptance notes
- **The flag door names no replacement.** `--database-driver memory` is
refused by oclif's derived allowlist, in oclif's wording. It lists the
legal kinds, `sqlite` among them, but does not name `--fresh` or
`:memory:`. Making it do so would mean dropping the derived `options:`
or special-casing a value at the consumer. Both are forbidden by
`database-driver-flag.ts` and by the ruling. Every other door names the
replacement.
- **The URL door refuses after the parent's banner.** `os dev --database
memory://x` prints the parent's `Database: memory://x` row, and the
serve child then refuses, naming `OS_DATABASE_URL` (the channel it
received the URL on). This is the same shape every unsupported scheme
already has. The driver spelling is refused before the banner. The
banner finding (a `file:` row beside an in-memory child) cannot occur
any more.
- **The CLI's `@objectstack/driver-memory` dependency** now serves no
CLI-owned path. The ledger row says so. It is kept, because removing a
published dependency is a packaging decision the ruling did not take.
Carrier: none.
- `duplicates.ts`'s header still quotes the original `memory://qa`
measurement. That is a dated record of the defect, not an instruction.
---
_Generated by [Claude
Code](https://claude.ai/code/session_016GiHYRmLSNWTfbX9gVQkpz)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent b610eab commit 9a4182a
31 files changed
Lines changed: 1011 additions & 365 deletions
File tree
- .changeset
- content/docs
- data-modeling
- deployment
- plugins
- docs/qa/platform-checklist/areas
- packages
- cli/src
- commands
- migrate
- utils
- runtime/src
- spec/src/data/driver
- scripts
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
82 | | - | |
| 82 | + | |
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| |||
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
104 | | - | |
| 104 | + | |
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| |||
798 | 798 | | |
799 | 799 | | |
800 | 800 | | |
801 | | - | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
802 | 814 | | |
803 | 815 | | |
804 | 816 | | |
805 | 817 | | |
806 | 818 | | |
807 | | - | |
| 819 | + | |
808 | 820 | | |
809 | 821 | | |
810 | 822 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
72 | | - | |
| 72 | + | |
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
| |||
182 | 182 | | |
183 | 183 | | |
184 | 184 | | |
185 | | - | |
186 | | - | |
| 185 | + | |
| 186 | + | |
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
| |||
472 | 472 | | |
473 | 473 | | |
474 | 474 | | |
475 | | - | |
476 | | - | |
| 475 | + | |
| 476 | + | |
477 | 477 | | |
478 | 478 | | |
479 | 479 | | |
| |||
502 | 502 | | |
503 | 503 | | |
504 | 504 | | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
505 | 519 | | |
506 | 520 | | |
507 | 521 | | |
508 | | - | |
| 522 | + | |
509 | 523 | | |
510 | 524 | | |
511 | 525 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
49 | | - | |
| 49 | + | |
50 | 50 | | |
51 | | - | |
| 51 | + | |
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
222 | 222 | | |
223 | 223 | | |
224 | 224 | | |
225 | | - | |
226 | | - | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
227 | 228 | | |
228 | 229 | | |
229 | 230 | | |
| |||
0 commit comments