diff --git a/android/PLAN.md b/android/PLAN.md index 3247618..483996e 100644 --- a/android/PLAN.md +++ b/android/PLAN.md @@ -110,6 +110,19 @@ spec-aligned (as recorded for M1); 17 new JVM unit tests cover the account + pla > Deferred rather than coordinated into the slice (org lead, 2026-08-11): the website work lands on > `edge` and reaches `main` as one cutover, so there is room. **It must land before that cutover** — > after it, a shipped app quietly loses a feature. +> +> **The gate was released and the cutover went first (org lead, 2026-08-12.)** The module-system +> cutover merged as [website#150](https://gitea.whitlocktech.com/RunicGateway/website/pulls/150) +> with this unfixed, deliberately: the app needs a broader pass than this one field, so holding a +> finished workstream for it bought delay and nothing else. +> +> **So this is no longer owed-but-harmless — it is live.** Against `main`, `/public/settings` no +> longer carries `gameAccountSignup`, `PublicDto.kt:80` reads its `false` default, and +> `CharactersViewModel.kt:72` hides game-account creation from every user of the shipped app. Nothing +> crashes and nothing logs; the button is simply not there. The fix is unchanged — one DTO field, one +> ViewModel line and two tests, reading the flag from `/public/shard/features`, which the app already +> calls — and it is now a **bug fix against a live regression** rather than a scheduling item, to be +> picked up with whatever else that broader app pass covers. ✅ **M5 — design pass** (2026-07-20, `RunicGateway/Android-app#10`): the shard-website theme applied across every screen, restyling the working M1–M4 UI with **no architecture, data-flow, endpoint, or DTO diff --git a/website/MODULE_SYSTEM.md b/website/MODULE_SYSTEM.md index 7460a49..45bbfc8 100644 --- a/website/MODULE_SYSTEM.md +++ b/website/MODULE_SYSTEM.md @@ -1,9 +1,17 @@ # The Module System — design of record -**Status:** approved design, **in implementation** — Phase 2's core scaffolding is landing on the -website `edge` branch, PRs 1–7 of 9 done (§2.7 tracks what each settled). Every decision in Part 3 -has been settled with the org lead; Part 1 records what was verified against the working trees on -2026-08-10, including the places the original draft was wrong. +**Status: SHIPPED.** All six phases are built and the `edge` → `main` cutover merged on +**2026-08-12** ([website#150](https://gitea.whitlocktech.com/RunicGateway/website/pulls/150)), so +core on `main` is game-agnostic: **166 public + 2 internal** routes with no game in them, and the 72 +UO routes arriving from [`RunicGateway/Module-uo`](https://gitea.whitlocktech.com/RunicGateway/Module-uo). +The contract is at `MODULE_API_VERSION` **1.5.0**. §2.7 tracks what each phase settled, §2.9 records +the cutover itself. + +This document stays in the present tense about the *design* and the past tense about the *build*. +Where it says "will", read it as the plan speaking; the "as landed" notes under each phase are what +actually happened, and several of them record the plan being wrong. Every decision in Part 3 was +settled with the org lead; Part 1 records what was verified against the working trees on 2026-08-10, +including the places the original draft was wrong. **The normative contract is [`MODULE_API.md`](MODULE_API.md)** (Phase 1). This document decides what the module system *is*; that one decides exactly what a module may call. Where the two differ, that @@ -1103,8 +1111,13 @@ either repo reached the branch. Both regression tests were shown to fail before **A known break, accepted:** the shipped Android app reads `gameAccountSignup` off `/public/settings` (`PublicDto.kt:80`). The field defaults to `false`, so nothing crashes — the app silently stops offering game-account creation until it reads the module's `/public/shard/features` -instead. Out of scope for this slice, recorded in the Android plan, and it lands well before this -workstream's cutover reaches `main`. +instead. Out of scope for this slice and recorded in the Android plan. + +*(Amended at the cutover, 2026-08-12: this said the fix "lands well before this workstream's cutover +reaches `main`", and it did not. The org lead released that gate rather than holding the cutover — +the app needs a broader pass than this one field, so waiting bought nothing but delay. The break is +therefore **live** from the cutover, not hypothetical, and the OWED note in `../android/PLAN.md` +carries it.)* ##### What the browser found this time @@ -1218,9 +1231,11 @@ Slice 1 moved the `UPDATE` to module-uo and left the `INSERT` behind. The two fi together — core's schema is replayed **in full** before any module fragment (§2.6) — so the marker existed before the guard ever read it and the one-shot could never fire. An install carrying a protocol-2 row would have stayed pinned at 2 against a v3 sidecar: every REST call 409, the WS closed -on `ws.hello`, which is precisely what the migration exists to prevent. Latent rather than live, and -only because `edge` has not cut over: it bites an install that first boots a post-slice-1 build while -already holding a `uo_link_config` row. +on `ws.hello`, which is precisely what the migration exists to prevent. Latent rather than live when +it was found, and only because `edge` had not cut over yet: it bites an install that first boots a +post-slice-1 build while already holding a `uo_link_config` row — which is exactly what every +existing instance did at the 2026-08-12 cutover, so the fix landing in this slice rather than after +it is the reason there was nothing to notice. Verified against a real MariaDB, all three states: a fresh database writes both rows and has nothing to migrate; a database with `protocol = 2` and no marker is moved to 3 and marked; and a database @@ -1404,7 +1419,7 @@ all being debugged at once. Its shape, the six decisions it turned on, the per-s acceptance table with its results are in §2.7.2 below — all four criteria met, each against the real published `module-uo` release rather than a fixture. -**Phase 5 — The Integration Kit. STARTED 2026-08-12.** `RunicGateway/Integration-kit`, the +**Phase 5 — The Integration Kit. COMPLETE 2026-08-12**, in four slices. `RunicGateway/Integration-kit`, the instruction book for building a module for a game that is not UO — the website module, the sidecar and why it is mandatory, and the game-side plugin that feeds it, on top of a `template/` module that really builds. Scaffolded when Phase 2 lands, written against Phase 3's extraction, started once @@ -1921,6 +1936,46 @@ The fix is one line — `branches: [main, edge]` — and it must land on `websit first module PR, not alongside it. `build-images.yml` is untouched: it triggers on push to `main`, so images are published and production rolls at the cutover and at no point before it, which is correct. +#### The cutover, as landed — 2026-08-12 + +[website#150](https://gitea.whitlocktech.com/RunicGateway/website/pulls/150), 52 commits, `main` a +strict ancestor of `edge` so there was nothing to resolve. The branch was deleted afterwards, as the +protocol v3 and installer cutovers did before it: `edge` is cut fresh the next time it is needed, and +a branch left alive is one that collects work by habit. + +Four decisions settled it, and the shape of them is worth keeping because none was a code question: + +1. **The Android parity gate was released, not met.** `../android/PLAN.md` said the + `gameAccountSignup` fix "must land before that cutover"; it had not, and the org lead's call was + that the app needs a broader pass than that one field, so holding the cutover bought delay and + nothing else. The break is live and recorded in both plans rather than quietly dropped — that is + the whole difference between an accepted break and a forgotten one. +2. **The UO module was declared before the merge, not installed after it.** UOMysticmoon's tenant + template gained `MODULES=uo@0.3.0=…` ([website#149](https://gitea.whitlocktech.com/RunicGateway/website/pulls/149)), + so the compose host resolves its own set at boot (§2.7.2 decision 4). The admin-panel path would + have worked equally well and left the site game-less between the image roll and someone clicking + install — for an instance with a shard to be down for, that gap is the entire objection. +3. **The docs status pass rode with the cutover**, this section included, so no document claimed a + branch that no longer exists. +4. **`Integration-kit`'s pinned core ref moved off `edge`** (§2.11.1), which is the one thing outside + this repo that the cutover was always going to invalidate. + +**What the merge does to a live deployment**, checked against the diff rather than reasoned about, +because this is the only merge in the workstream that rolls production: + +- **No `DROP TABLE` anywhere.** Core stops *creating* the 27 `shard_*` tables and never drops them; + the module's fragment is `CREATE TABLE IF NOT EXISTS`, so an existing instance keeps its shard data + and its encrypted `uo_link_config` row. The extraction is invisible to the data. +- **One destructive statement in 228 changed files** — phase 2 PR 4's `announce_jobs` column drop, + guarded on `information_schema`, idempotent, and verified against the live `uomm-db` when it was + written. +- **No newly-required environment variable**, so nothing fails to boot for want of configuration. +- A declared module **starts on that same first boot**: a fresh `installed_modules` row lands + `installed`, and `lifecycle.boot()` skips only a row that is explicitly `disabled`. No operator + click stands between the deploy and a working shard. + +The one thing a cutover PR cannot prove is the deploy, which the org lead ran. + ### 2.10 Process obligations Every server-side PR runs `npm run swagger`, `npm run routes:manifest` (the diff is reviewed, not @@ -2008,6 +2063,13 @@ holds the whole website workstream on `edge` until one cutover, and `main` has n the things the cutover has to revisit — recorded here because a pinned ref that quietly points at an abandoned branch is exactly the failure this kit is supposed to be immune to. +*(Resolved at the cutover, 2026-08-12. The constraint expired exactly as written: `edge` merged to +`main` and was deleted, so `ci/core-ref.json` moved to `main` at the merge commit. The **sha did not +change** and neither did `MODULE_API_VERSION` — the pinned commit is an ancestor of `main`, so the +`coreApi` equality assertion stays armed at 1.5.0 and no chapter needed re-reading. Only the branch +label was ever wrong, which is the cheap kind of stale and precisely why the pin names a branch +alongside the sha instead of trusting the sha to explain itself.)* + ##### Six decisions, settled by the org lead 1. **The kit ships a buildable `template/`, not prose alone.** A minimal module that really compiles @@ -2160,7 +2222,7 @@ check in this repo can see. | 8 | SPA pages namespaced: `/uo/*`, `/admin/uo/*`, `/player/uo/*` | §2.8 | | 9 | Clean break — no redirects, no nav-override migration; site is not public yet | §2.8 | | 10 | Client half loads as a prebuilt ESM chunk with React shared via a core global | §2.6 | -| 11 | Website work lands on `edge` and reaches `main` as one cutover at the end | §2.9 | +| 11 | Website work lands on `edge` and reaches `main` as one cutover at the end — **done 2026-08-12**, website#150, `edge` deleted | §2.9 | | 12 | The module repo is `RunicGateway/Module-uo`; the module id is `uo` | §2.3 | | 13 | `RunicGateway/Integration-kit` is the module-builder's instruction book — module + sidecar + game plugin, teaching only, never re-specifying a contract | §2.11 | | 14 | Phase 3 extracts **server-first, then client**, sliced by feature; `module-uo` merges before `website` in each pair | §2.7.1 |