docs(website): settle Phase 3's shape, slices and merge order

Phase 2 is closed, so Phase 3 needs a plan before any of it is extracted.
Four decisions, and one finding that set the first of them.

The finding: because API URLs are preserved (§1.2), a feature's server and
client halves are independent. Core's client keeps calling
/api/v1/public/shard/status after the module serves it, and a module page
calls the same URL while core still does. Nothing forces vertical slices, so
the extraction is server-first then client, sliced by feature, ten slices.

Merge order within a slice is module-uo first, then website. The loader's
ownedByCore probe stops a module LOADING while core owns its prefix, but the
module's own CI never loads it into core, so its PR merges fine beforehand --
and edge then serves the feature from core right up to the moment core drops
it, with no window where the branch is missing it outright.

Criterion 1's grep reads code, not prose: filenames, import specifiers, route
path literals and declared identifiers. Core's marketing copy legitimately
says "shard" in a dozen places and a literal word grep would have failed CI on
each while proving nothing about the boundary. That copy is rewritten in its
own slice instead, which is real work with a real review rather than an
exemption hidden in a pattern.

module-uo's CI clones core at a pinned ref to freeze its route manifest --
nothing else proves the URLs it claims are the URLs it serves -- and the
module-rust dry run lands in docs/modules/ where §2.10 already aggregates
module documentation.

Also records the measured surface (72 server files, 51 client, 32 test files),
which supersedes the Phase 0 estimate, and the one kit gap: lib/format.js is
vendored by the module rather than becoming an eighth §3.4 member.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 01:10:13 -05:00
committed by Claude
parent 12e4eaad10
commit 749233d378
2 changed files with 87 additions and 1 deletions

View File

@@ -817,6 +817,25 @@ specifiers in the client bundle are the four declared externals. A hit fails the
Phase 3's acceptance criterion 1: no `shard`, `uoLink`, `cliloc`, `atlas` or `towncrier` outside
`modules/`, as a CI grep test rather than a review promise.
**Settled 2026-08-11: the grep reads code, not prose.** It covers four things, and each of them is a
thing a module owns:
1. **File and directory names** under `server/src/`, `server/scripts/`, `server/db/` and `client/src/`.
2. **Import and require specifiers** — the path in `require('…')` / `from '…'`.
3. **Route path literals** — the string arguments to `.get`/`.post`/`.put`/`.patch`/`.delete`/`.use`.
4. **Declared identifiers** — function, const, class and property names.
It does **not** read comments or string content generally, and that is not a loophole. Core's
marketing copy legitimately says "shard" — `About.jsx`, `Screenshots.jsx`, `SiteFooter.jsx`,
`heroLayout.js` — and a literal word grep would turn each of those into a CI failure while proving
nothing about the boundary. Worse, it would forbid a core comment from ever using the word as an
example, which is the sort of rule people work around rather than obey. The boundary this test exists
to defend is *structural*: core must not name a module's files, import them, route to them, or
declare their symbols. It can talk about them in English.
Core's UO-flavoured default copy is dealt with directly instead, as `MODULE_SYSTEM.md` §2.7.1's
slice 8 — a rewrite with its own review, not an exemption.
### 5.3 Zero-line route manifest diff (CI, both repos)
`npm run routes:manifest -- --check` in core; the module generates and freezes its own manifest in
@@ -824,6 +843,16 @@ its own repo, using the same script pointed at a core+module app. Phase 2 must p
diff in core's; Phase 3 moves the UO entries out of core's and into module-uo's, which is the one
diff the whole workstream is allowed.
**Settled 2026-08-11: `module-uo`'s CI checks core out at a pinned ref.** The module's workflow
clones `RunicGateway/website` at a ref recorded in the module repo, drops itself in as `modules/uo`,
and runs core's own `routeManifest.js`. Nothing else proves the URLs a module claims are the URLs it
actually serves — a manifest frozen by hand goes stale silently, and the failure it would have caught
is a route that moved.
Pinning the ref rather than tracking `edge` is what keeps this from being a source of unexplained red
Xes: core moves for reasons that have nothing to do with the module, and a bump is then a deliberate
commit that says which core the module was last proved against.
---
## Part 6 — Amendments to MODULE_SYSTEM.md