docs(website): settle the module loader's trigger and its collision check

Records what phase 2 PR 2 decided against the two things MODULE_API.md
left open for it.

7.6 is settled as an explicit modules.load(tierRouters) call in app.js
rather than a require-time scan: the loader needs the tier routers handed
to it for the 4.3 check, which a require-time side effect cannot receive,
and a require's position enforces an ordering constraint invisibly.

4.1 and 4.3 gain the mechanics that fall out -- where the call must sit
in app.js and why in both directions, that mounting is a second pass
after validation, and that core's prefix ownership is probed on the live
tier routers with express's layer.match() rather than declared in a table
that was already stale in the spike.

2.7 records PR 2 as done.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-10 14:36:22 -05:00
parent b5277d827c
commit 9355f2aec4
2 changed files with 53 additions and 8 deletions

View File

@@ -453,9 +453,23 @@ 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: PR 1 done** — `installed_modules` and the state machine, with the stored shape and the
boot rules settled in §2.4 above. No loader, no routes, no boot wiring yet, so it changes nothing an
operator or a client can see.
**Progress: PRs 1-2 done.**
- **PR 1** — `installed_modules` and the state machine, with the stored shape and the boot rules
settled in §2.4 above.
- **PR 2** — `server/src/modules/loader.js`: the filesystem scan, manifest validation, prefix and
table-name collision rejection, per-module try/catch and the tier mount, behind the §4.5 dispatch
guard. Two decisions landed with it, both recorded in [`MODULE_API.md`](MODULE_API.md): the load
trigger is **one explicit `modules.load(tierRouters)` call in `app.js`**, never a lazy scan
(API §7.6); and the "does core own this prefix" check **probes the live tier routers** rather than
a hardcoded table, so it cannot drift when core adds a capability router (API §4.3). The three
de-entanglement registries and the two lifecycle hooks throw `not available until phase 2 PR 4/5`
rather than no-op — an accepting stub would let a module believe it had registered something.
28 tests, all on the failure paths.
There is still no module on the volume, no schema replay and no boot wiring, so this changes nothing
an operator or a client can see: 842 tests pass and `routes.manifest.json` is unchanged at 229
routes.
**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;