docs(website): record slice 3, and the boot-order defect it created
MODULE_SYSTEM.md §2.7.2 gains the slice 3 write-up: why the id and version are written out in MODULES (the offline no-op has to be a file read, not a fetch), the four decisions, and the table of what the smoke proved against the real v0.3.0 release on a fresh database. Two sections are corrected rather than extended. §2.5's "declarative module set" promise did not anticipate that the two install surfaces need a rule about who wins — the declaration owns the volume, the row owns whether a module runs — and decision 4's "resolution runs before the server starts" is true of the SCAN, not of the process: it runs inside start(), which is what buys it the database. MODULE_API.md §2.6 no longer says the fragments are replayed by ensureSchema(). They are, for every caller except the server, which scans the volume later than it ensures the schema and so replays them itself. That was a live defect for an afternoon: it announced itself only as the "no module scan in this process" skip line, which is correct output for `npm run seed` and means the opposite in a booting server, and on a database that already had the tables the module started perfectly. Nothing in the contract moves, so MODULE_API_VERSION is unchanged. BACKEND_DESIGN.md: the boot sequence in the tree, and MODULES beside the module_source_hosts setting it installs through. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -296,8 +296,10 @@ naming a UO file, and the last thing binding core to the module.
|
||||
### 2.5 Lifecycle
|
||||
|
||||
```
|
||||
(core schema + seed) → MODULES resolved onto the volume ← §2.7.2 decision 4
|
||||
↓
|
||||
require(module) → register(ctx, api) → [routes mounted, app.js require returns]
|
||||
↓ (server.js, after ensureSchema + seed)
|
||||
↓ (server.js: schema fragments, then)
|
||||
onBoot(ctx) → started
|
||||
↓ (SIGINT/SIGTERM)
|
||||
onShutdown()
|
||||
@@ -365,8 +367,8 @@ the dispatch guard reads and the row the admin panel reads are moved together an
|
||||
|
||||
### 2.6 Schema fragments
|
||||
|
||||
`schema` is an idempotent `.sql` file replayed by the same `ensureSchema()` that replays core's,
|
||||
immediately after it, statement by statement, split the same way. It is subject to the same rules
|
||||
`schema` is an idempotent `.sql` file replayed immediately after core's own `schema.sql`, statement
|
||||
by statement, split the same way. It is subject to the same rules
|
||||
core's file already follows: `CREATE TABLE IF NOT EXISTS`, `ALTER TABLE … ADD COLUMN IF NOT EXISTS`,
|
||||
no `--` inside a string literal, no `DROP`.
|
||||
|
||||
@@ -404,6 +406,18 @@ break seeding outright. The replay asks `isLoaded()` and logs the skip. That is
|
||||
use of that predicate: everywhere else, reading the module list before `load()` still throws, because
|
||||
a booting server quietly getting no module tables is precisely what §7.6 exists to prevent.
|
||||
|
||||
**And the server replays them itself, because it now scans the volume LATER than it ensures the
|
||||
schema.** `ensureSchema()` carries the replay for every ordinary caller, but `server.js` passes
|
||||
`replayModules: false` and calls `replayFragments()` of its own accord after requiring `app.js`. The
|
||||
reason is `MODULES` (`MODULE_SYSTEM.md` §2.7.2 decision 4): resolving a declared module set has to
|
||||
happen before the scan, it needs the host-allowlist setting to do it, and that setting does not exist
|
||||
until the schema and the seed have run — so core's schema now precedes the scan, and a replay wired
|
||||
to core's schema would run when there was nothing yet to replay. Nothing about the contract moves:
|
||||
fragments still run after core's tables exist and before any `onBoot`. **This was a live defect for
|
||||
the length of one afternoon**, and the shape of it is worth keeping: it announced itself only as the
|
||||
skip line above, appearing in a *booting server's* log where it means the opposite of what it means
|
||||
in `npm run seed`, and on a database whose tables already existed the module started perfectly.
|
||||
|
||||
**Table names are namespaced and collision-checked.** New tables must be prefixed `<id>_`. The
|
||||
loader extracts every `CREATE TABLE IF NOT EXISTS <name>` from the fragment and rejects the module
|
||||
if a name collides with a core table or with another module's — a wrong `DROP`-free fragment can
|
||||
|
||||
Reference in New Issue
Block a user