feat(site): phase 17 - per-game module pages, /runicnpc/, and checkTerms over the built site
All checks were successful
PR checks / checks (pull_request) Successful in 2m37s

PLAN.md §16 phase 17, built on the org lead's answers of 2026-10-22,
recorded as D75-D79 (count of record 79).

- D75: core marketing pages describe what a game module adds in core's
  words. "Game intelligence" became "What a game module adds"; each
  game's own list moved to src/data/games/ and renders only on the new
  /modules/uo/ and /modules/rust/. /modules/ is a card per game.
- The page-side capability check is per game (assertGameCoverage), the
  item phase 13 moved here.
- D76: the homepage keeps its game screenshot, labelled. screens.mjs
  entries may carry `game` (D67); Screenshot.astro and the app grid
  print the game's name and mark the figure data-game.
- D77: /app/ describes the Rust screens (released the same day).
- D78: /runicnpc/, the standalone page, is in the header and footer;
  the narrow header was re-measured so five links keep two rows.
- D79: checkTerms.mjs --built reads every built page's visible text
  after the build; game routes and data-game elements allow their
  game's words. Proven against ten plants. Still the twelfth check.
- Re-read fixes: /architecture/'s "two machines is also the maximum",
  Discord "guild" on /integrations/, and collection.mjs against what
  Steam sign-in and the Rust module keep (PLAY_DATA_SAFETY.md
  regenerated; the Play answers are unchanged).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-22 20:10:51 -05:00
parent ec65b2d5aa
commit 56473d4d51
32 changed files with 1924 additions and 451 deletions

117
src/data/games/uo.mjs Normal file
View File

@@ -0,0 +1,117 @@
/**
* games/uo.mjs — what the Ultima Online module adds, in Ultima Online's own words.
*
* PLAN.md §16, phase 17. Until this phase these items were the "Game intelligence" group of
* `capabilities.mjs`, which the homepage and `/features/` render — core pages, which D63
* says may name the games but not use their vocabulary. They moved here, to a file that
* belongs to one game, and `/modules/uo/` renders them. The core pages now describe what a
* game module adds in core's words and link here (D75).
*
* `caps` is the contract with `platform.json`'s `games.uo.module.capabilities`, which
* `checkFacts.mjs` re-reads from the module's own `module.json`. `assertGameCoverage()` in
* `index.mjs` fails the build when the two drift apart, in either direction — so the day the
* module gains a capability, the JSON goes red, and the day the JSON moves, this page does.
*
* Slugs are NOT one-per-item: `shard` is the source of four separate user-facing features,
* and the marketplace draws on `market` and `cliloc` together (item names arrive as cliloc
* ids and are resolved against the shard's own string table). Coverage, not a bijection.
*
* `demoPath` is a stable public route on the module's own pages, deep-linked into a demo
* when one is mounted (D25). Paths are read from `module-uo`'s `client/src/entry.jsx`.
*/
/** @typedef {import('../capabilities.mjs').Capability} Capability */
/** @type {Capability[]} */
export const features = [
{
label: 'Live shard status',
caps: ['shard'],
demoPath: '/uo/shard',
detail:
'Whether the shard is up, who is on it, and how long ago the site last heard from it. ' +
'When the game is down this page is the thing that says so — the site does not go ' +
'down with it.',
},
{
label: 'Economy and activity',
caps: ['shard'],
demoPath: '/uo/shard/activity',
detail:
'A live feed of what is happening in the world, and the economy underneath it. Every ' +
'event passes an allowlist before it can reach a public page; staff read a second ' +
'stream carrying the rest.',
},
{
label: 'Character sheets',
caps: ['shard'],
detail:
'Skills, stats and equipment, drawn from the live world rather than from a form ' +
'somebody filled in. Reachable by the account the character is linked to, and by ' +
'staff — not by the public.',
},
{
label: 'Points and loyalty boards',
caps: ['shard'],
demoPath: '/uo/leaderboards',
detail:
'The leaderboards the shard already keeps, published without anyone exporting a ' +
'spreadsheet on a Sunday.',
},
{
label: 'Player-vendor marketplace',
caps: ['market', 'cliloc'],
demoPath: '/uo/market',
detail:
'Every player vendor on the shard and what is on it, searchable without logging in to ' +
'the game. Item names arrive from the world as cliloc ids and are resolved against the ' +
"shard's own string table, so they read as names rather than numbers — beside the " +
"item's own picture, in the colour it was dyed.",
},
{
label: 'Houses and IDOC decay',
caps: ['houses'],
demoPath: '/uo/houses',
detail:
'Who owns what and where it stands, including which houses are decaying — published ' +
'while it is still information rather than after it has become a rumour.',
},
{
label: 'Spawn atlas',
caps: ['atlas'],
demoPath: '/uo/atlas',
detail:
"A bestiary and spawn map built by reading your shard's own spawn tables, so it " +
"describes your server rather than someone else's idea of the game. Regions, " +
'landmarks and champion altars come with it, and each creature is shown as the ' +
'artwork your own client draws it with.',
},
{
label: 'Champion boards',
caps: ['champs'],
demoPath: '/uo/champs',
detail: 'Which altars are running, how far along they are, and what turned up.',
},
{
label: 'Guilds',
caps: ['guilds'],
demoPath: '/uo/guilds',
detail:
'Guild rosters and standings, kept in step with the shard. Each guild is also a Team ' +
'on the site, with the forum, the notifications and the voice channel that core ' +
'attaches to one.',
},
{
label: 'City governors',
caps: ['governors'],
demoPath: '/uo/governors',
detail: 'Who holds which city, and what they did with it.',
},
];
/**
* The screenshots `/modules/uo/` shows beside the list, by id from `screens.mjs`. Every one
* is tagged `game: 'uo'` there, so it prints the game's name beside it (D67) wherever it is
* shown. They lived on `/features/` until phase 17.
*/
export const screens = ['shard-status', 'marketplace', 'spawn-atlas', 'guilds', 'houses'];