feat(screens): phase 9 — real screenshots, from a real shard #13

Merged
whitlocktech merged 1 commits from feat/phase-9-screens into main 2026-08-25 15:04:21 +00:00
Member

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.

Five decisions were taken before the rig was built; PLAN.md §10 "How phase 9 took the screenshots" records them in full.

D42 The full rig: ServUO + sidecar + website main + module-uo, with the demo database seeded on top for what a fresh shard cannot produce
D43 A neutral demo brand — "Runic Gateway Demo", not a real community
D44 The captures sit beside the claims they support: the homepage, /features/, and five of the administration pages phase 7 could describe but not show
D45 The rig is committed, not remembered — seed, data, capture, check
D46 The world is dressed in servuo-plugins' scaffolding (its PR #17)

What is in the pictures

The marketplace rows are player vendors the game actually holds. The atlas is parsed from the shard's own spawn files. The guild rosters and the alliance came over the bridge. "Candlewick House is now IDOC" happened while the page was open — and appears both in the browser's Houses in danger and in the app's live activity feed, the same minute.

And there is a player in the world. The org lead signed a character in by hand (see below), so the shard console reads 1 player online, in Britain, and the app's shard card agrees. Presence reaches the public page as counts and regions, not names — the visibility framework doing its job unprompted.

The pipeline

scripts/seedDemo.mjs        content, by driving the site's own API
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, wired into CI

The seed never writes SQL. Every row it creates could have been an INSERT, and every INSERT would be a second implementation of a rule the website owns — how a body is sanitized, which excerpt is derived, how a password is hashed. A seed that writes SQL produces a database the product could not have produced.

checkScreens.mjs proves every entry has a file, at the declared size, that nothing in public/screens/ is orphaned, and that every declared capture is rendered somewhere. The size half is the one that repays it: a re-capture taken at the wrong viewport looks fine on its own and only shows up as a page that reflows while it decodes.

The retake proved the point of D45: the world changed, the recipe did not — npm run screens:capture shard-status app-shard was the whole operation.

What the rig found

A screenshot rig is an integration test with a human in the loop. Six things nothing else had:

  1. A fresh module-uo install pinned protocol 3 against a sidecar speaking 4 — every shard read 409s until an admin edits the number by hand. Fixed upstream, released as v1.0.2; this repo's own facts check then caught platform.json still saying v1.0.1 (also fixed here).
  2. A renamed guild member never reaches the site — the roster is re-emitted only when the member set changes. The same mechanism froze the board's "online" column during the retake: a member signing in does not change the signature, so the column stayed at 0 while the shard had a player.
  3. A guild deleted while the shard is offline is a ghost row forever — nothing emits guild.remove after a reconnect clears the cache. The demo's board was showing two guilds the world had not had for a week.
  4. "Houses in danger" cannot show a house that was already collapsing — the column is written only from the transition feed, so a house already in IDOC at connect time is invisible.
  5. The app's news list prints raw ISO timestamps — that screen was dropped from the phone set rather than shipped.
  6. The app says "1 players online". shard_online_count and ShardEventText.kt both interpolate a count into a fixed plural. It is in the shipped phone capture; a plurals resource is the fix, in the app. Say the word and it is a small Android-app PR plus a one-command retake.

1 is fixed. The rest are raised, not patched, and the rig works around 2 and 4 honestly (guilds are built after the rename; the IDOC staging is two passes with a wait, so the site watches the collapse happen).

Two things that did not go to plan

  • The character had to be signed in by hand. The scaffolding does its half — a known password on a seeded account — but ClassicUO mangles a plaintext password from settings.json, synthetic clicks reach it while synthetic text does not, and the foreground route failed to take focus and typed into the browser window in use at this machine. It was not tried again; the org lead signed in instead.
  • The phone shots are API 35, not 36. The API 36 image on this machine had 200 MB free and refused the install, and wiping somebody's development device to take a screenshot is not a trade worth making.

Verification

All nine checks plus astro check, the unit tests and a production build, locally: tokens, brand, links (2,410), facts (19), quickstart (59), data safety, reference (22), sidebar, screens (17). Every figure was looked at in a browser on the built site — homepage, /features/ and the docs pages.


AI-assisted: written with Claude Code (Claude Opus 5). Commits carry the Co-Authored-By trailer.

🤖 Generated with Claude Code

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. Five decisions were taken before the rig was built; PLAN.md §10 *"How phase 9 took the screenshots"* records them in full. | | | |---|---| | **D42** | The full rig: ServUO + sidecar + `website` `main` + `module-uo`, with the demo database seeded on top for what a fresh shard cannot produce | | **D43** | A neutral demo brand — "Runic Gateway Demo", not a real community | | **D44** | The captures sit beside the claims they support: the homepage, `/features/`, and five of the administration pages phase 7 could describe but not show | | **D45** | The rig is committed, not remembered — seed, data, capture, check | | **D46** | The world is dressed in `servuo-plugins`' scaffolding (its [PR #17](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/17)) | ## What is in the pictures The marketplace rows are player vendors the game actually holds. The atlas is parsed from the shard's own spawn files. The guild rosters and the alliance came over the bridge. "Candlewick House is now IDOC" happened while the page was open — and appears both in the browser's *Houses in danger* and in the app's live activity feed, the same minute. **And there is a player in the world.** The org lead signed a character in by hand (see below), so the shard console reads **1 player online, in Britain**, and the app's shard card agrees. Presence reaches the public page as *counts and regions, not names* — the visibility framework doing its job unprompted. ## The pipeline ``` scripts/seedDemo.mjs content, by driving the site's own API 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, wired into CI ``` The seed **never writes SQL**. Every row it creates could have been an `INSERT`, and every `INSERT` would be a second implementation of a rule the website owns — how a body is sanitized, which excerpt is derived, how a password is hashed. A seed that writes SQL produces a database the product could not have produced. `checkScreens.mjs` proves every entry has a file, at the declared size, that nothing in `public/screens/` is orphaned, and that every declared capture is rendered somewhere. The size half is the one that repays it: a re-capture taken at the wrong viewport looks fine on its own and only shows up as a page that reflows while it decodes. The retake proved the point of D45: the world changed, the recipe did not — `npm run screens:capture shard-status app-shard` was the whole operation. ## What the rig found A screenshot rig is an integration test with a human in the loop. Six things nothing else had: 1. **A fresh `module-uo` install pinned protocol 3 against a sidecar speaking 4** — every shard read 409s until an admin edits the number by hand. Fixed upstream, released as **v1.0.2**; this repo's own facts check then caught `platform.json` still saying v1.0.1 (also fixed here). 2. **A renamed guild member never reaches the site** — the roster is re-emitted only when the member *set* changes. The same mechanism froze the board's **"online" column** during the retake: a member signing in does not change the signature, so the column stayed at 0 while the shard had a player. 3. **A guild deleted while the shard is offline is a ghost row forever** — nothing emits `guild.remove` after a reconnect clears the cache. The demo's board was showing two guilds the world had not had for a week. 4. **"Houses in danger" cannot show a house that was already collapsing** — the column is written only from the transition feed, so a house already in IDOC at connect time is invisible. 5. **The app's news list prints raw ISO timestamps** — that screen was dropped from the phone set rather than shipped. 6. **The app says "1 players online".** `shard_online_count` and `ShardEventText.kt` both interpolate a count into a fixed plural. It is in the shipped phone capture; a `plurals` resource is the fix, in the app. Say the word and it is a small `Android-app` PR plus a one-command retake. 1 is fixed. The rest are raised, not patched, and the rig works around 2 and 4 honestly (guilds are built *after* the rename; the IDOC staging is two passes with a wait, so the site watches the collapse happen). ## Two things that did not go to plan - **The character had to be signed in by hand.** The scaffolding does its half — a known password on a seeded account — but ClassicUO mangles a plaintext password from `settings.json`, synthetic clicks reach it while synthetic text does not, and the foreground route failed to take focus and typed into the browser window in use at this machine. It was not tried again; the org lead signed in instead. - **The phone shots are API 35, not 36.** The API 36 image on this machine had 200 MB free and refused the install, and wiping somebody's development device to take a screenshot is not a trade worth making. ## Verification All nine checks plus `astro check`, the unit tests and a production build, locally: tokens, brand, links (2,410), facts (19), quickstart (59), data safety, reference (22), sidebar, **screens (17)**. Every figure was looked at in a browser on the built site — homepage, `/features/` and the docs pages. --- AI-assisted: written with Claude Code (Claude Opus 5). Commits carry the `Co-Authored-By` trailer. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wtclaude added 1 commit 2026-08-25 14:56:40 +00:00
feat(screens): phase 9 — real screenshots, from a real shard
All checks were successful
PR checks / checks (pull_request) Successful in 1m25s
c29ec94f46
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>
whitlocktech merged commit ebb3f71786 into main 2026-08-25 15:04:21 +00:00
whitlocktech deleted branch feat/phase-9-screens 2026-08-25 15:04:22 +00:00
Sign in to join this conversation.
No description provided.