# module-uo — the Phase 1 spike **This branch is evidence, not implementation.** `spike/module-atlas` is cut from `edge` and is never merged. Phase 2 rebuilds the loader properly, with the `installed_modules` table, the full state machine and the admin panel behind it; Phase 3 does the real extraction. What it demonstrates, and the results, are written up in [`docs/website/MODULE_API.md`](../../../docs/website/MODULE_API.md) Part 7. In one line: the six public spawn-atlas routes now live in a module, at byte-identical URLs, with the client half loading as a prebuilt ESM chunk under `script-src 'self'`. ## Reproducing it ```bash # 1. build the module's client chunk (its CI would do this and ship the result) cd modules/uo/client && npm install && npm run build # → dist/entry.js # 2. build core's client cd ../../../client && npm install && npm run build # 3. run the server against the local MariaDB cd ../server && npm start ``` Then: - `/uo/atlas` and `/uo/atlas/lizardman` render from the module's chunk. - `GET /api/v1/public/atlas/*` answers exactly as before — `npm run routes:manifest -- --check` reports the surface unchanged. - `npm test` in `server/` (core, 729) and `node --test` in `modules/uo/server/` (module, 81). `dist/entry.js` is committed here **only** because this branch is the evidence for a design decision and a reviewer should be able to inspect the built artifact without a toolchain. A real module publishes it from CI into its release bundle and never commits it. ## What in here is not design Three things are consequences of stopping at six routes, spelled out in MODULE_API.md §7.5: 1. **Core reaches into this module twice** — `server/src/router/v1/admin/shardAtlas.controller.js` and `server/test/atlasController.test.js`. The five admin atlas routes sit inside the `/shard` admin prefix core still owns, so they cannot move until the whole prefix does. 2. **`server/utils/visibility.js` is a copy** of core's `utils/shardVisibility.js`, which core still needs for the shard routes not yet extracted. Two caches over one table, briefly. 3. **There is no `swagger-fragment.json`** — it needs core's merge helper on the other side, which is Phase 2. Also note the table names here are `shard_*`, not `uo_*`. That is deliberate and grandfathered by an allowlist in the loader: renaming twenty-seven live tables is a data migration this workstream does not do. Every module written after this one carries its id as a table prefix.