Phase 16c of the events plan: `runicgateway.com`'s half of the workstream, now
that `main` carries the engine, the module, the app and the bundle.
The checks were already red and named their own answers:
* `checkFacts` — nine values had moved. Protocol 5 → 7 in all three declaration
sites, `moduleApi` 1.9.0 → 1.10.0, the bundle to 2026.09.10 with sidecar
v2.2.0 and overlay v1.2.0, `link` v2.2.0, `Module-uo` v1.2.2.
* `checkReference` — twenty-seven `Bridge.cfg` keys the site listed nowhere: the
events switch and its sweep, the ten caps, the oracle NPC, the two lease keys
and the seven participation keys. They are five new groups rather than an
appendix to an existing one, because `EventsEnabled` is a second consent
switch and belongs beside its own ceilings.
Two pages, matching the treatment Teams has:
* **Scheduled events** (Administration) — where it is and who sees it, authoring
and immutable versions, the switchboard that arrives off, caps as a condition
on an `UPDATE` rather than a role check, the dry run, the run console, what an
event owns versus what it borrows, generated cleanup, the shard's own switches,
and what a player sees.
* **Events architecture** — the two sentences it turns on, what is a table and
what deliberately is not, budgets in SQL, the ledger's two rules, at-most-once
on a wire that can lose an answer, the three layers, and the four omissions.
And the rest of the surface:
* `/privacy` gains **`deploy-events`** — the participation ledger is personal
data and no row named it. Scores and ranks against a module-opaque member key,
linked to an account where one is linked; the diagnostic log swept after 90
days on terminal runs only; the run, its steps and its participants not swept
at all, because they are the record of what was done to a shared world.
`deploy-game-data`'s citation moves from `link/v4.md` to `v7.md`.
* **Protocol versions** — the most recent bump touched *five* repositories, and
the `website` row is the interesting one: core is normally out of a protocol
bump's reach and this one reached it, because what changed was not a game noun
but the shape of a thing core owns the ledger for. The store-migration
paragraph now says four bumps' worth rather than two.
* Two capability entries, so `/`, `/features/` and `/modules/` stop omitting the
subsystem — an Administration item, and an **Event calendar** under Community
with `/site/events` as its deep link. Deliberately *not* `needsModule`: a bare
core can author and run an event, and only the world verbs need a module.
* **`reference/event-catalog` is retitled "Shard event catalog"** and says what
it is not. Two things in the docs were called an event catalog; the route is
unchanged, so nothing outside this repository breaks.
* `canonicalDocs` gains `website/EVENTS.md` and moves `link/v4.md` → `v7.md`.
No screenshots. Capturing the events surfaces means standing the whole rig back
up — game server, sidecar, core, module, a published event with a live run — for
two or three images that no check requires, and the engagement workstream's own
site leg added none either.
`npm run verify` green end to end, including `checkReference` against the
protocol spec that only reached `docs` `main` in RunicGateway/docs#232 — the
seventh cutover step, which 16b had left on `edge`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
Phase 13 landed while this branch waited: link/servuo-plugins/website/Module-uo
are all on main, the bundle was republished at 2026.09.01, and Module-uo cut
v1.1.0. These are the four values PLAN.md 12 said could not be written until the
republish existed, plus the two releases that moved with it.
Every one is the value checkFacts.mjs itself reports as the authority's answer:
bundle.tag 2026.08.19 -> 2026.09.01
bundle.sidecar v2.0.0 -> v2.1.0
bundle.overlay v1.0.0 -> v1.1.0
releases.link v2.0.0 -> v2.1.0
releases.Module-uo v1.0.2 -> v1.1.0
verifiedOn 2026-08-19 -> 2026-09-01
No page hardcodes any of them -- every quote is an interpolation of
platform.bundle.* or platform.releases.*, so there is nothing else to re-read.
Co-Authored-By: Claude <noreply@anthropic.com>
Engagement Phase 12b. **This branch is deliberately red and must not be merged until the
Phase 13 cutover has landed link and servuo-plugins on `main` and CI has republished the
bundle.** checkFacts.mjs fetches every value from the source repo's `main`, so it fails
today exactly as designed:
FAIL protocol (sidecar) platform.json 5 link main 4
FAIL protocol (overlay) platform.json 5 servuo-plugins main 4
FAIL moduleApi platform.json 1.9.0 website main 1.6.0
FAIL protocol (bundle) platform.json 5 installer bundles:current.json 4
Every other check is green on this branch: checkLinks 2503, checkReference 22, checkA11y,
checkCsp, checkSidebar, 42 tests.
moduleApi is **1.9.0**, not the 1.7.0 the plan named -- Phase 11 moved it twice after that
sentence was written (1.8.0 for the `admin` ceiling, 1.9.0 for the module seed API).
## Also: the one page whose whole subject is the protocol number was the one page that
## hardcoded it
platform.json's own header says no version number is ever hardcoded in prose, and
`architecture/protocol-versions.mdx` had `4` written out three times -- the headline
sentence and both declaration-site rows. It now imports platform.json like every other
page that quotes a fact, so it moves with the JSON and cannot say 4 while the JSON says 5.
Note the interpolation is OUTSIDE the code spans: MDX does not evaluate an expression
inside backticks, so `PROTOCOL_VERSION: u32 = {platform.protocol}` would have rendered the
braces literally. Verified in the built HTML -- "currently 5", and no `platform.protocol`
survives anywhere in the output.
The same page's "what a bump obliges" section said version 4 was the first bump to need a
store migration, which read as though every bump does. v5 needed none (it only widens
frames the store already keeps whole -- docs link/v5.md), so the sentence now says which
did and which did not, and v5.md joins v4.md under canonical documents.
## The fill-in step, at merge time
Three values are not knowable today because the artefact does not exist yet. After the
bundle republishes, run:
GITEA_TOKEN=<token> node scripts/checkFacts.mjs
and copy what it prints in the "says" column into src/data/platform.json:
* bundle.tag, bundle.sidecar, bundle.overlay -- from installer bundles:current.json
* releases.Module-uo -- if the cutover cuts a new module release
* verifiedOn -- the date you ran it
Then re-read `getting-started/connect-a-game-server` and `administration/the-shard-connection`,
which quote the bundle, before merging. The check is green when all 19 agree.
AI-assisted: written with Claude Code.
Co-Authored-By: Claude <noreply@anthropic.com>
D4 asked for screenshots of the review stack rather than placeholders. Seventeen
of them: eleven of the site in a browser, six of the app on a phone, all from one
demo deployment wired to a running ServUO shard over a real sidecar, captured on
one day (D42).
The deployment is branded "Runic Gateway Demo" rather than a real community (D43),
and the captures sit beside the claims they support — the homepage, /features/, and
five of the administration pages phase 7 could describe but not show (D44).
The rig is committed rather than remembered (D45):
scripts/seedDemo.mjs content, by driving the site's own API — never SQL,
because a row the product could not have produced is
a screenshot of a product that does not exist
src/data/screens.mjs every capture: route, viewport, scroll, alt, caption
scripts/captureScreens.mjs npm run screens:capture
scripts/checkScreens.mjs the ninth check script, in CI
Shard-side dressing is servuo-plugins' scaffolding (D46), never deployed.
The rig found five things nothing else had. One is fixed upstream — a fresh
module-uo install pinned wire protocol 3 against a sidecar speaking 4, released as
v1.0.2, which this repo's own facts check then caught in platform.json. Four are
raised as product observations and worked around in the rig: a renamed guild
member never reaches the site, a guild deleted while the shard is down is a ghost
row forever, "Houses in danger" cannot show a house that was already collapsing,
and the app's news list prints raw ISO timestamps.
Players online reads 0. Logging a character in needs a UO client driven by hand,
and that is where this stopped — PLAN.md §10 says exactly why, and how to retake
the two frames that would change.
Co-Authored-By: Claude <noreply@anthropic.com>
Recovering installer's orphan tag published v0.1.1, which moved the platform
under this branch and turned checkFacts red -- the check working exactly as it
should, since a version this site quotes had changed.
FAIL release installer
platform.json says : v0.1.0
installer releases/latest says : v0.1.1
The 500 that orphaned the tag was a race with the tag push one second earlier,
not a structural failure: re-running the workflow took the built-in orphan-tag
recovery path and published all four assets unchanged.
So the stale-path Aside stops saying "v0.1.0 is still the current download",
which is no longer true, and says the durable thing instead -- v0.1.0 prints
the old path, v0.1.1 prints the real one -- which stays correct however many
releases follow. platform.json and the PLAN.md version table move to v0.1.1,
and the phase 7 findings record the pipeline defect as a fourth finding.
Co-Authored-By: Claude <noreply@anthropic.com>
Builds `/app/` and `/beta/`, the SQLite signup store, the rate limiting and the
export CLI of PLAN.md §8, and adds this repository's first test suite.
Four decisions of record, D26–D29 (§8, "How phase 5 built the app and the beta"):
- D26 — the screenshot slot ships empty, reserved for phase 9. §10 promised
`/app/` "the 14 existing screenshots"; they are a July trusted-device smoke
test against an unseeded dev instance, captured before the theming work, and
five of the fourteen are two-factor prompts. Shipping them would break D4.
Phase 9 already builds the rig, so it gains an emulator pass.
- D27 — the public demo is the tester target. `ConnectScreen.kt` gates the whole
app on a validated deployment address, so a tester needs somewhere to point it.
The beta therefore waits on the demo VM, and the page says so.
- D28 — `/beta` handles its own POST; there is no `/api/beta-signup`. An endpoint
cannot report a validation error without JavaScript. §6's diagram is amended.
- D29 — the APK and the beta get equal billing, and the APK link is off:
`androidApk.serviceable` is false because the published v0.5.0 build does not
work. The panel stays and states that plainly rather than being removed.
Three mechanisms the plan did not anticipate:
- `liveBrand()` — a server-rendered page never passes through the boot rewrite,
so `/beta` reads the mounted brand.json itself. Pasting the Play opt-in URL in
takes effect on the next request rather than the next restart.
- `checkLinks.mjs` derives on-demand routes from `prerender = false` in the
source. A PLANNED_ROUTES entry would have been wrong: its reverse check fires
when a route has been built, and an on-demand route never produces a file, so
the entry could never rot out.
- `npm test` — the five existing checks all read built output, and none of this
logic appears there. A honeypot can stop working and leave the build identical.
Also: `checkFacts.mjs` gains the APK assets and `minSdk`, and learns that RFC 2606
reserved domains are not contact addresses; the D13 rule is otherwise unchanged.
Verified end to end against the built server: every outcome renders with no
JavaScript, cross-origin POSTs are refused, a mounted opt-in URL appears without
a restart, and the export CLI round-trips.
Co-Authored-By: Claude <noreply@anthropic.com>
`data/` without a leading slash matches a directory of that name at any
depth, so it silently swallowed src/data/platform.json -- the single file
every page and checkFacts.mjs reads.
The working tree still had it, so `npm run verify` passed locally with all
14 facts green. CI cloned fresh and `astro check` failed on the missing
module. Anchored both patterns to the repository root.
Verified the way it should have been the first time: exported HEAD to a
clean directory, npm ci, and ran the checks there rather than in the tree
that was hiding the problem.
Co-Authored-By: Claude <noreply@anthropic.com>