feat(modules): installed_modules and the module state machine (phase 2, PR 1) #128
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/modules-state"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 2, PR 1 of the module system — §2.7's first item, the
installed_modulestable and the §2.4 state machine.The table and the machine only. No loader, no routes, no boot wiring, nothing imports the new model yet.
npm run routes:manifest -- --checkreports 229 routes, up to date — Phase 2's exit criterion holds trivially here, and it should keep holding through PR 9.What's here
server/db/schema.sqlinstalled_modules— one row per installed module, keyed by itsmodule.jsonidserver/src/model/modules/modules.db.jsserver/src/model/modules/modules.model.jsserver/test/modules.model.test.jsThe two decisions this PR settles
Both approved by the org lead before implementation; §2.4 left them open.
1. One
statecolumn, not a policy flag beside a runtime one.installed → enabled → started, withdisabledandstartup_failedrecoverable — stored exactly as §2.4 words it, so the doc and the column say the same thing.2. A
startup_failedmodule is retried on every restart. Each boot resets every non-disabled row toenabledand clears its recorded failure; the load that follows writes this boot's outcome. An operator who fixes the cause — a truncated file, a missing dependency, a database that was not up yet — gets the module back by restarting, with no admin-panel visit. §2.5 already makes restart the install step, so "retry" in the panel is just "restart".Three consequences fall out, and each has a test:
disabledis the only state a boot leaves alone. It is the operator's decision; everything else is an outcome and is recomputed.failure_stage/failure_reason.What the row is not
It never decides which routes exist. The loader scans the filesystem at require time, before the database is reachable (
MODULE_API.md§4.1), so the URL surface is a property of the volume — which is what letsroutes.manifest.jsonbe generated against a dead database, and why a disabled module stays mounted and guarded (§4.5) rather than being unmounted.An illegal transition throws rather than writing a row that misrepresents what happened — except on the boot path, where a disabled row and an unrecognised failure stage are softened instead, because one module's failure must never become everybody's (§4.4).
Verification
modules.db(thepages.model.test.jspattern — no database).ensureSchema()applies the table through its own comment-strip +;splitter, and install → enable → start → fail → boot-reset → purge all behave as the model tests claim.Docs: RunicGateway/docs#125.
🤖 Generated with Claude Code