docs(website): settle boot/shutdown dispatch and what a boot writes down
Records phase 2 PR 5 of the module system: the lifecycle hooks a module registers, how they are dispatched, and what a boot does to installed_modules. MODULE_API.md 2.5 gains the reconcile's four steps in order, the rules that fall out of them (the operator's `disabled` wins over any outcome; a bookkeeping failure is not a boot failure; a module with no onBoot still reaches `started`; a module whose onBoot threw gets no onShutdown), and why onBoot has no timeout while onShutdown has a five-second budget -- shutdown races the process being killed and boot does not. 4.4 gains the failure_stage table: every failure is recorded against the 4.3 step that produced it, so the admin panel can say where a module broke and not only what the message was. MODULE_SYSTEM.md 2.4 records the new rule for a row whose directory is gone, and 2.7 the PR 5 progress entry with its four decisions. BACKEND_DESIGN.md's installed_modules section gains the write path now that one exists. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -403,6 +403,13 @@ A re-install or an upgrade refreshes `name`/`version`/provenance and deliberatel
|
||||
alone: upgrading an enabled module must not silently switch it off, and re-installing a disabled one
|
||||
must not silently switch it on.
|
||||
|
||||
**A row whose directory is gone is marked `startup_failed`** (stage `require`, reason "module
|
||||
directory not present on the volume"), settled with PR 5. The boot reset above has just moved it to
|
||||
`enabled`, and a row claiming to be enabled for a module that is not on the volume is the one state
|
||||
that is simply untrue — it would be read that way by the admin panel and by
|
||||
`GET /api/v1/public/modules` alike. This catches only a directory deleted by hand: an uninstall
|
||||
leaves the row `disabled`, which the reset never touches.
|
||||
|
||||
### 2.5 Install, uninstall, purge
|
||||
|
||||
Modules live on a **mounted volume**, not in the image — the same treatment `uploads` already gets in
|
||||
@@ -484,7 +491,7 @@ too (API §7.2).
|
||||
Exit criterion: `routes.manifest.json` diff is zero lines and every existing test passes. If Phase 2
|
||||
changes one URL, it is wrong.
|
||||
|
||||
**Progress: PRs 1-4 done.**
|
||||
**Progress: PRs 1-5 done.**
|
||||
|
||||
- **PR 1** — `installed_modules` and the state machine, with the stored shape and the boot rules
|
||||
settled in §2.4 above.
|
||||
@@ -533,6 +540,27 @@ client can see: **884 tests pass** and `routes.manifest.json` is unchanged at 22
|
||||
lines of OpenAPI that do move are the retry endpoint's summary and its `leg`, which is no longer a
|
||||
fixed enum because the leg set is whatever has been registered.
|
||||
|
||||
- **PR 5** — boot/shutdown dispatch and the `installed_modules` reconcile, `src/modules/lifecycle.js`.
|
||||
`api.onBoot`/`api.onShutdown` stop throwing, `server.js` gains one call on each side, and the
|
||||
§2.4 machine finally runs against real outcomes — which is what makes §4.5's `disabled` 404 leg
|
||||
reachable for the first time. Four decisions landed with it, all recorded in
|
||||
[`MODULE_API.md`](MODULE_API.md) §2.5 and §4.4: **the loader classifies its failures** by §4.3 step,
|
||||
so `failure_stage` says where a module broke instead of being a column nothing filled; **a row whose
|
||||
directory is gone is marked failed** rather than left claiming `enabled` (§2.4 above); **core's eight
|
||||
UO boot call sites stay in `server.js`** until Phase 3, because unlike a registered announce leg a
|
||||
boot call site already has somewhere to live and moving it now would be extraction done early in a
|
||||
phase whose exit criterion is that nothing changes; and **`onBoot` gets no timeout** — shutdown races
|
||||
a SIGKILL and boot does not, and a slow `onBoot` delaying the listener is the contract's promise to
|
||||
a module that must warm up before it serves.
|
||||
|
||||
The dispatch lives outside the loader for the reason the schema replay does: the loader is required
|
||||
by `app.js` against a dead pool, and this half is database-first. They meet at one function,
|
||||
`loader.setState()`, so the in-memory record the dispatch guard reads and the row the admin panel
|
||||
reads cannot drift apart.
|
||||
|
||||
Still nothing on the volume: **900 tests pass**, `routes.manifest.json` is unchanged at 229 routes
|
||||
and the OpenAPI spec regenerates byte-identical.
|
||||
|
||||
**Phase 3 — Extract `module-uo`.** Moves out of `website/`: the 8 model directories and their 25
|
||||
tables; the nine UO `utils/` files plus `newsGump.js`; the 13 router/controller files;
|
||||
`scripts/importSpawnAtlas.js` and `db/spawnAtlas.art.json`; `usersShard.controller.js` **minus
|
||||
|
||||
Reference in New Issue
Block a user