Docs half of website PR 3 (phase 2). MODULE_SYSTEM.md 2.7 gains the PR 3 entry;
MODULE_API.md 2.6 gains the decisions the section did not previously answer.
The one that shapes the code: a fragment is VALIDATED at load time and EXECUTED
later, split on whether a database is needed to know the answer. Everything 2.6
states about the SQL is knowable by reading the file, so breaking one of those
rules costs a module its mount entirely rather than mounting and 503ing with its
tables half created. What is left for the replay is the class of failure only
the server can report, and that is post-mount and answers 503.
Also recorded: the rules are enforced as a leading-verb allowlist (CREATE,
ALTER, INSERT, UPDATE -- the four core's own schema.sql uses) rather than the
DROP denylist 2.6 words them as, because the file is replayed on every boot and
a denylist only ever bans what somebody thought of; the replay sits outside
ensureSchema's wait-for-the-database retry loop, so one module's bad SQL cannot
cost the boot two minutes; partial application is accepted, since MariaDB
self-commits DDL and no transaction could undo it; and `npm run seed` is the one
sanctioned caller that replays nothing, because it never requires app.js and so
has no scan to read.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
Part 7 of MODULE_API.md: the three exit criteria and their results, the two
contract changes the spike forced (ctx.express/ctx.validator and
window.__rg.jsxRuntime), the empirical confirmation of the OpenAPI split, the
loader's tested failure guarantees, and the three artifacts in the branch that
are consequences of stopping at six routes rather than intended shape.
Phase 1 is complete: docs/website/MODULE_API.md is written and the spike met
every exit criterion on website branch spike/module-atlas (cut from edge, never
merged).
Co-Authored-By: Claude <noreply@anthropic.com>
Both approved by the org lead 2026-08-10. §6.1 moves from open question to
decision A with the per-side obligations spelled out in a new §6.1a: modules
ship a swagger-fragment.json with fully-qualified paths and namespaced schema
keys, core merges started modules' fragments into /api/docs.json at request
time and always wins a key collision, and swagger-output.json stays exactly
what core's own routes generate.
Co-Authored-By: Claude <noreply@anthropic.com>
The normative contract between core and an installed module: the `ctx` handed
to a module's entry point, the `register*` calls, the client-side registry and
shared-dependency global, the schema-fragment rules, and the loader's
validation and failure obligations. Every member is derived from what the UO
code actually imports today, re-read against the working tree.
Part 6 records four places the survey contradicted MODULE_SYSTEM.md:
• OpenAPI generation is STATIC analysis (swagger-autogen parses app.js as
text), unlike routeManifest.js which walks the live Express stack. A
filesystem-scanning loader is invisible to it, so module routes would be
silently absent from swagger-output.json. Three options, one recommended;
needs a decision before Phase 2.
• The client contract is much larger than §2.1 implies — the atlas pages
import five core modules beyond React, so the plan needs a curated UI kit
and a request primitive on window.__rg.
• shardVisibility is module-owned and the atlas depends on it, so the spike
carries it; two copies coexist during the spike by design.
• Two counts corrected: 27 UO tables (not 25), 6 atlas routes (not 5).
MODULE_SYSTEM.md gains a pointer to the contract and the corrected table count.
Co-Authored-By: Claude <noreply@anthropic.com>