docs(site): the Asset Bridge — protocol 8, a screen for client files, and pictures in the screenshots (Phase 9c)
All checks were successful
PR checks / checks (pull_request) Successful in 1m41s

The last leg of the Asset Bridge (docs/link/v8.md §16, phase 9c). The cutover put
protocol 8 on `main` in every declaration site, which turned this repo red by
construction: `checkFacts.mjs` reads the protocol from `link`'s `main`.

Two checks were failing, and only one of them was expected.

**checkFacts (10)** — protocol 7 → 8 in all three reads (sidecar, overlay, bundle),
the bundle triple to 2026.09.15 / sidecar v2.3.0 / overlay v1.3.0, and the releases
to link v2.3.0, installer v0.2.0, Module-uo v1.3.0. `moduleApi` and the capability
list did not move: core's whole share of eight phases was a deletion.

**checkReference (9)** — nobody had planned for this one. `Bridge.cfg` grew nine keys
across phases 5, 6 and 7 and the reference page had gone on not mentioning them,
which is the failure mode that check exists for. They are listed as two new groups,
`Client assets` and `Spawn files`, because they are two separate consents.

## The prose the bridge changed

- **New page, Administration → Client files.** `Admin → Client files` is a screen an
  operator has to press a button on and the site had no page for it. It carries the
  rule nothing else states: **nothing here happens on a restart**, so a patched client
  keeps serving the old artwork until somebody says so.
- **Protocol versions** said the most recent bump touched five repositories and named
  `website`. Protocol 8 touched four and did not reach core — its absence is the
  interesting half, and the page now says why. The store-migration paragraph gains 8,
  which changed no line of the sidecar's store despite moving megabytes.
- **The bridge** said "two ways in". There are three: bulk reads go over the
  request/reply path in pages, one in flight at a time, and the reason they must not
  ride the event stream is structural rather than about speed.
- **Requirements** gains `libgdiplus` for Linux shard hosts, and the note that no game
  client has to be installed for any of this — a ServUO shard cannot boot without one.
- **Troubleshooting** gains the failure mode that did not exist before: a bestiary with
  no pictures, or items reading as numbers.
- **Maintenance** gains the button to press after patching a client, and **Verify the
  whole stack** the `doctor` check that landed with it.
- The spawn atlas and the marketplace both carry artwork now, so both capability
  descriptions say so, and `canonicalDocs` points at `link/v8.md`.

## Screenshots, retaken against the real thing

The atlas and marketplace shots predate the asset plane, so both were retaken on the
demo deployment against a real shard and a real sidecar, plus the new admin screen:
1,095 portraits imported in 2.1 s, 67,496 names in 1.4 s, 313 item pictures in 0.8 s.
Nothing here is a mock-up. The atlas shot moved 36px down its page because portraits
made the rows taller.

## One defect found on the way

`seedDemo.mjs` pinned the demo deployment at **protocol 4** with a comment explaining
a `module-uo` debt that has since been paid. Its default now comes from
`platform.json`, so the rig cannot drift two protocols behind the platform again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
This commit is contained in:
2026-09-15 05:45:38 -05:00
parent 73f664c38e
commit 4e1ce2316a
18 changed files with 267 additions and 43 deletions

View File

@@ -14,7 +14,7 @@
* UOLINK_BASE http://127.0.0.1:8080 sidecar REST, written to Admin → Shard
* UOLINK_WS ws://127.0.0.1:8080/ws sidecar WebSocket
* UOLINK_TOKEN (unset) sidecar auth token; skipped when absent
* UOLINK_PROTOCOL 4 wire protocol to pin — see the note below
* UOLINK_PROTOCOL (platform.json) wire protocol to pin — see the note below
*
* ---------------------------------------------------------------------------------------
* WHY THE SEED DRIVES THE API AND NEVER THE DATABASE
@@ -45,6 +45,8 @@
import { readFileSync } from 'node:fs';
import platform from '../src/data/platform.json' with { type: 'json' };
const BASE = (process.env.RG_BASE || 'http://localhost:3000').replace(/\/+$/, '');
const API = `${BASE}/api/v1`;
const ADMIN_USER = process.env.RG_ADMIN_USER || 'demoadmin';
@@ -53,16 +55,21 @@ const DEMO_PASS = process.env.RG_DEMO_PASS || 'DemoReview!2026';
const UOLINK_BASE = process.env.UOLINK_BASE || 'http://127.0.0.1:8080';
const UOLINK_WS = process.env.UOLINK_WS || 'ws://127.0.0.1:8080/ws';
const UOLINK_TOKEN = process.env.UOLINK_TOKEN || '';
// The pinned wire protocol has to be STATED, not left to the module's default.
// The pinned wire protocol, read from `platform.json` rather than written down here.
//
// `module-uo`'s schema fragment still carries `protocol INT NOT NULL DEFAULT 3`, from the
// protocol-3 cutover; the sidecar on `link` `main` speaks 4. The module handles protocol 4's
// frames — `guild.roster` and `guild.leave` ingest landed with the Teams cutover — but a
// FRESH install pins 3, and the sidecar answers a 3 with `409 protocol version mismatch` on
// every REST call. So a new deployment reads nothing from its shard until somebody edits the
// number in Admin → Shard. Raised with the org lead rather than patched from here: the fix
// belongs in `module-uo`, not in this repo's screenshot rig (PLAN.md §13 phase 9).
const UOLINK_PROTOCOL = Number(process.env.UOLINK_PROTOCOL || 4);
// It was a literal `4` until the Asset Bridge cutover, with a note explaining that
// `module-uo` pinned 3 on a fresh install while the sidecar spoke 4, so a new deployment
// read nothing from its shard until somebody edited the number in Admin → Shard. That debt
// has since been paid: the module's schema fragment defaults the column to the protocol its
// build speaks and carries a one-shot migration per bump, so both a fresh install and an
// upgraded one land on the right number by themselves.
//
// What remains is the rig's own reason to state it: this seed points a demo deployment at a
// sidecar, and if it pins the wrong number every REST call comes back `409`. Reading it from
// `platform.json` means the number is the one `checkFacts.mjs` verified against `link`'s
// `main` — so the rig cannot quietly drift two protocols behind the platform again, which is
// exactly what the literal did.
const UOLINK_PROTOCOL = Number(process.env.UOLINK_PROTOCOL || platform.protocol);
const DRY = process.argv.includes('--dry-run');