refactor(modules)!: de-UO core's copy, and enforce it (phase 3, slice 4) #140

Merged
whitlocktech merged 1 commits from feature/module-de-uo-core into edge 2026-08-12 03:02:40 +00:00
Member

Phase 3 slice 4 — the slice that makes acceptance criterion 1 a fact rather than a promise. Pairs with Module-uo#5 (merge that one first) and docs#139.

637 server (+17) and 157 client tests pass; manifest unchanged at 158 public + 2 internal; OpenAPI spec byte-identical.

1. The bindings were dead a whole slice before anyone noticed

client/src/api/client.js still carried 190 lines of UO namespaces — shard, atlas, both SSE URLs, admin.shard/shardOps/atlas/userShard, the uo-link and town-crier calls, player.shard — with zero core consumers since slice 3 deleted the views that called them. Nothing failed, nothing warned, the client build was happy: an API binding with no caller is inert. Only the check below would have found it.

Five assertions went with them, to Module-uo#5 — core's apiClient.test.js was still testing UO URLs, so deleting the bindings would have deleted the coverage. The encoding test that used governorHistory is re-pointed at a core route rather than dropped: what it guards is req's encoding, which is core's.

2. The copy

About, Screenshots, Website's cards, Wiki, SiteFooter, the default hero, brand.js's tagline and description, the seeded wiki categories, and two user-visible NavEditor strings that named a module's admin screen by its proper name ("Shard Visibility"). Status is the interesting one — it reports site mode, has never had anything to do with a game server, and was called "Shard Status" purely by habit.

Nothing live changes wording. An instance already has three places to say which game it is: BRAND_* (and .env.uomysticmoon.example sets both brand strings explicitly), the hero editor, and CMS pages. heroLayout.defaultLayout is both the rendered default and the editor's starting point, which is the argument for keeping the words in code and making them neutral rather than inventing a config surface for prose.

Wiki page slugs are deliberately untouched: seedDefault* only inserts a row that is absent, so renaming maps-atlas adds a ninth page to every install rather than renaming anything.

3. The check — scripts/checkModuleIdentifiers.js

npm run check:modules, and the first step of the server-tests job, before npm ci — it is plain Node with no dependency of its own, so a boundary break is the first thing a reviewer sees rather than something found under a pile of unrelated failures.

It reads code, not prose (MODULE_API.md §5.2): file names, import specifiers, route path literals, declared identifiers, property names. Core's English may still say "shard" where saying it is worth more than the word costs.

Four things it gets right only because getting them wrong was tried first:

  • Whole words, not substrings. A case-insensitive substring pass flags defaultImage — which contains "ultIma" — and did so four times in this repo on its first run. uo+link and town+crier match only as adjacent pairs; "link" alone is ordinary core vocabulary.
  • Comments and string bodies masked in one character walk, comments first. The checkImports.js lesson for the third time: a comment contains quotes, a string contains //. Masked rather than deleted, so offsets survive and line numbers stay honest.
  • The three §6.5 grandfathering allowlists are exempt by name, with reasonsLEGACY_TABLE_PREFIXES and LEGACY_STREAM_IDS/LEGACY_LEGS are maps keyed by module id, and grandfathering cannot be written down without naming who is grandfathered. An exemption that stops matching fails the build.
  • git ls-files, not a directory walk. An operator's gitignored server/db/data/spawnAtlas.art.json sits in the tree of anyone who has run the atlas import and would otherwise report a violation no commit could fix.

server/test/checkModuleIdentifiers.test.js — 17 tests. A boundary check is written when the boundary is already clean, so it never fires again and nothing distinguishes "still checking" from "quietly broken". Every "must not catch" case in it is a false positive an earlier version really produced.

Breaking change

Core no longer seeds game_account_signup or uo_link_protocol_3_migrated; module-uo's fragment does (Module-uo#5, which also repairs the one-shot migration core's ordering had silently disabled). An install running core without module-uo keeps whatever rows it has and gains no new ones — nothing in core reads either key.

Also removed: an orphan comment block in schema.sql describing the spawn-atlas tables slice 1 took away.

Verified

The pair run together — module built and copied into modules/uo, core booted against a real MariaDB:

  • Manifest with the module loaded: 230 public + 2 internal, the 70 UO routes reappearing exactly.
  • check:modules clean with the module installed (it skips modules/, as it must).
  • Browser (§7.7): the neutral default hero, /site/about, /site/status, /site cards, the footer, the module's nine nav rows and its /uo/shard page — zero console errors, zero CSP reports. The only console line is the module's own registered against core API 1.3.0.
  • Both rewritten NavEditor strings confirmed in the built bundle with correctly-compiled typographic apostrophes.

Deferred, deliberately

README.md's 48 UO mentions, including a whole ## Shard integration (uo-link) section and the architecture diagram. §5.2 does not cover prose, and core's README is a rewrite that belongs with slice 5's phase-closing documentation pass rather than half-done inside a code slice.


  • AI-assisted (Claude Code)
Phase 3 slice 4 — the slice that makes acceptance criterion 1 a fact rather than a promise. Pairs with **Module-uo#5** (merge that one first) and **docs#139**. 637 server (+17) and 157 client tests pass; manifest unchanged at 158 public + 2 internal; OpenAPI spec byte-identical. ## 1. The bindings were dead a whole slice before anyone noticed `client/src/api/client.js` still carried **190 lines** of UO namespaces — `shard`, `atlas`, both SSE URLs, `admin.shard`/`shardOps`/`atlas`/`userShard`, the uo-link and town-crier calls, `player.shard` — with **zero core consumers** since slice 3 deleted the views that called them. Nothing failed, nothing warned, the client build was happy: an API binding with no caller is inert. Only the check below would have found it. Five assertions went with them, to Module-uo#5 — core's `apiClient.test.js` was still testing UO URLs, so deleting the bindings would have deleted the coverage. The encoding test that used `governorHistory` is re-pointed at a core route rather than dropped: what it guards is `req`'s encoding, which is core's. ## 2. The copy `About`, `Screenshots`, `Website`'s cards, `Wiki`, `SiteFooter`, the default hero, `brand.js`'s tagline and description, the seeded wiki categories, and two **user-visible** NavEditor strings that named a module's admin screen by its proper name ("Shard Visibility"). `Status` is the interesting one — it reports **site mode**, has never had anything to do with a game server, and was called "Shard Status" purely by habit. **Nothing live changes wording.** An instance already has three places to say which game it is: `BRAND_*` (and `.env.uomysticmoon.example` sets both brand strings explicitly), the hero editor, and CMS pages. `heroLayout.defaultLayout` is both the rendered default *and* the editor's starting point, which is the argument for keeping the words in code and making them neutral rather than inventing a config surface for prose. Wiki page **slugs** are deliberately untouched: `seedDefault*` only inserts a row that is absent, so renaming `maps-atlas` adds a ninth page to every install rather than renaming anything. ## 3. The check — `scripts/checkModuleIdentifiers.js` `npm run check:modules`, and the **first** step of the server-tests job, before `npm ci` — it is plain Node with no dependency of its own, so a boundary break is the first thing a reviewer sees rather than something found under a pile of unrelated failures. It reads **code, not prose** (MODULE_API.md §5.2): file names, import specifiers, route path literals, declared identifiers, property names. Core's English may still say "shard" where saying it is worth more than the word costs. Four things it gets right only because getting them wrong was tried first: - **Whole words, not substrings.** A case-insensitive substring pass flags `defaultImage` — which contains "ultIma" — and did so four times in this repo on its first run. `uo`+`link` and `town`+`crier` match only as adjacent pairs; "link" alone is ordinary core vocabulary. - **Comments and string bodies masked in one character walk, comments first.** The `checkImports.js` lesson for the third time: a comment contains quotes, a string contains `//`. Masked rather than deleted, so offsets survive and line numbers stay honest. - **The three §6.5 grandfathering allowlists are exempt by name, with reasons** — `LEGACY_TABLE_PREFIXES` and `LEGACY_STREAM_IDS`/`LEGACY_LEGS` are maps *keyed by module id*, and grandfathering cannot be written down without naming who is grandfathered. An exemption that stops matching **fails the build**. - **`git ls-files`, not a directory walk.** An operator's gitignored `server/db/data/spawnAtlas.art.json` sits in the tree of anyone who has run the atlas import and would otherwise report a violation no commit could fix. `server/test/checkModuleIdentifiers.test.js` — 17 tests. A boundary check is written when the boundary is already clean, so it never fires again and nothing distinguishes "still checking" from "quietly broken". Every "must not catch" case in it is a false positive an earlier version really produced. ## Breaking change Core no longer seeds `game_account_signup` or `uo_link_protocol_3_migrated`; module-uo's fragment does (Module-uo#5, which also repairs the one-shot migration core's ordering had silently disabled). An install running core without module-uo keeps whatever rows it has and gains no new ones — nothing in core reads either key. Also removed: an orphan comment block in `schema.sql` describing the spawn-atlas tables slice 1 took away. ## Verified The pair run together — module built and copied into `modules/uo`, core booted against a real MariaDB: - Manifest with the module loaded: **230 public + 2 internal**, the 70 UO routes reappearing exactly. - `check:modules` clean with the module installed (it skips `modules/`, as it must). - Browser (§7.7): the neutral default hero, `/site/about`, `/site/status`, `/site` cards, the footer, the module's nine nav rows and its `/uo/shard` page — **zero console errors, zero CSP reports**. The only console line is the module's own `registered against core API 1.3.0`. - Both rewritten NavEditor strings confirmed in the built bundle with correctly-compiled typographic apostrophes. ## Deferred, deliberately `README.md`'s 48 UO mentions, including a whole `## Shard integration (uo-link)` section and the architecture diagram. §5.2 does not cover prose, and core's README is a rewrite that belongs with slice 5's phase-closing documentation pass rather than half-done inside a code slice. --- - [x] AI-assisted (Claude Code)
wtclaude added 1 commit 2026-08-12 02:56:21 +00:00
refactor(modules)!: de-UO core's copy, and enforce it (phase 3, slice 4)
All checks were successful
PR Checks / bot-install (pull_request) Successful in 18s
PR Checks / client-build (pull_request) Successful in 27s
PR Checks / server-tests (pull_request) Successful in 31s
0c4eacfa4a
Phase 3's acceptance criterion 1, made real. Three things, one review:

**The dead bindings.** `client/src/api/client.js` still carried ~190 lines of UO
namespaces — `shard`, `atlas`, the two SSE URLs, `admin.shard/shardOps/atlas/
userShard`, the uo-link and town-crier calls, `player.shard` — with zero core
consumers since slice 3 deleted the views. module-uo vendors its own bindings.
The five assertions core's `apiClient.test.js` made about those URLs moved with
them (Module-uo#5); the encoding test that used `governorHistory` now uses a
core route.

**The copy.** Core is the platform, not one game's site, so its words are
game-neutral now: `About`, `Screenshots`, `Website`'s cards, `Status` (which was
never about a game server at all — it reports site mode), `Wiki`, `SiteFooter`,
the default hero, `brand.js`'s tagline and description, the seeded wiki
categories, and two user-visible NavEditor strings that named a module's admin
screen by its proper name. Which game an instance is for is the operator's to
say — BRAND_* vars, the hero editor, CMS pages — and every real instance already
does: `.env.uomysticmoon.example` sets both brand strings explicitly, so nothing
live changes wording. Wiki page SLUGS are untouched: `seedDefault*` only inserts
what is absent, so renaming one adds a duplicate page to every install.

Also gone: an orphan comment block in `schema.sql` describing the spawn-atlas
tables slice 1 took away, and the two settings rows core seeded for a module
(`game_account_signup`, `uo_link_protocol_3_migrated`). The second was a live
defect — see Module-uo#5, which takes ownership of both and repairs the
one-shot migration core's ordering had disabled.

**The check.** `scripts/checkModuleIdentifiers.js` + `npm run check:modules`,
first step of the server-tests job because it needs no dependencies. It reads
CODE, not prose — file names, import specifiers, route path literals, declared
identifiers and property names — per §5.2, so core's English may still say
"shard" where saying it is worth more than the word costs.

Two things it gets right only because getting them wrong was tried first: it
matches WHOLE WORDS (a substring pass flags `defaultImage`, which contains
"ultIma", four times in this repo), and it strips comments and string bodies in
one character walk (a comment contains quotes, a string contains `//`) — the
`checkImports.js` lesson. It has its own 17-test suite, because a boundary check
that silently stops checking is worse than none. The three §6.5 grandfathering
allowlists are exempt by name, and an exemption that stops matching fails the
build rather than lingering.

BREAKING CHANGE: core no longer seeds `game_account_signup` or
`uo_link_protocol_3_migrated`; module-uo's schema fragment does. An install
running core without module-uo keeps whatever rows it already has and gains no
new ones — nothing in core reads either key.

Deferred to slice 5, deliberately: README.md's 48 UO mentions, including a
`## Shard integration (uo-link)` section and the architecture diagram. That is
documentation, which §5.2 does not cover, and it belongs with the phase-closing
docs pass rather than half-done here.

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech merged commit 87230c879a into edge 2026-08-12 03:02:40 +00:00
whitlocktech deleted branch feature/module-de-uo-core 2026-08-12 03:02:40 +00:00
Sign in to join this conversation.
No description provided.