Website work lands on an `edge` branch and reaches `main` as a single cutover
at the end -- the same shape used for protocol v3 and the Android theming
workstream. A core that has grown a module loader but not yet lost its UO
code is a coherent state; a core mid-extraction is not.
Adds a Phase 0, because the strategy is blocked on one line of CI config:
pr-checks.yml is `on: pull_request: branches: [main]`, so every PR into edge
would run no checks at all -- the same trap that let all nine Android M12
phase PRs merge with zero CI. It matters more here, since Phase 2's exit
criterion IS a CI result (zero-line routes.manifest.json diff, passing
tests). The fix must land on website main before the first module PR.
Also records the real module repo: RunicGateway/Module-uo (capital M, as
Android-app), currently empty -- no branches, no initial commit -- and
disambiguates the repo name from the module id `uo`.
Co-Authored-By: Claude <noreply@anthropic.com>
Turns Runic Gateway from a UO-specific platform into a game-agnostic one:
game-specific routes, tables, screens and nav leave the core website and
become an installable module. Operators install the base site, install the
module for their game, and restart -- they never build anything.
Verified against the working trees rather than written from the draft. The
draft's load-bearing assumptions that did not survive:
* one flat module route prefix cannot coexist with URL preservation, since
UO routes span three access tiers -- modules own a named slot per tier
* there is no server-side nav list, and navOverrides.js refuses to have one,
so nav registration is client-side
* the nav feature-gate mechanism is itself the shard visibility system
* there is no migration system to model a module runner on -- schema.sql is
replayed idempotently every boot, so modules ship fragments
* boot/shutdown holds eight UO call sites with no hook to receive them
* notificationStreams, pushDispatch and announceWorker are entangled, not moves
* six UO routes are nested under the core users resource
* the Discord bot has no UO logic at all
* the installer never contacts the website and its Bundle is hardcoded to
two components, so delivery is website-side
* routeManifest and swagger walk app.js with no DB, so modules mount
synchronously from the filesystem
* production is a prebuilt pull-only image and CSP forbids inline script,
which together decide how the client half loads
Ten decisions are recorded as settled in Part 3.
Co-Authored-By: Claude <noreply@anthropic.com>