docs(website): the server half does not slice, and slice 1's record #136

Merged
whitlocktech merged 2 commits from docs/module-extract-server into main 2026-08-11 21:53:22 +00:00
Member

The docs half of Phase 3 slice 1, split off from docs#135 (which landed the plan and slice 0's record only).

What's here

docs(website): the server half does not slice, and why — found before writing any of slice 1. §2.7.1 had split the server extraction five ways by feature; it does not divide, because two contract rules compose. A mount prefix is claimed whole (/admin/shard is one 386-line router carrying 25 routes across atlas, clilocs, shard-ops, visibility, market and account links) and a model cannot be shared across the boundary, so it moves with its last consumer. Take the closure and every prefix is in it.

Giving the admin routes their own prefixes would divide it, and is rejected: it changes API URLs, which §1.2 promises not to do — and not hypothetically, since the shipped Android app calls POST /api/v1/admin/shard/kick, /ban, /unban, /broadcast and the three /pages routes.

The slice table is now six slices, not ten. The client half is unaffected and still slices cleanly.

docs(website): record slice 1, and MODULE_API 1.1.0 — the whole server half is out: 40 files, ~9,674 lines, 27 of 68 tables. The acceptance criterion held exactly — core's manifest goes 228 → 158 public routes, the 70 that left reappear byte-identical once the module loads, and routes.guards.json is identical across all 228.

The contract grew to 1.1.0: ctx.activity.log, ctx.users.getById, ctx.site.baseUrl, ctx.middleware.rateLimit + accountChangeLimiter, and a fourth registry, registerPostHook. Each is documented with why it could not be vendored — an admin action a module performs belongs in core's one audit log, a second rate-limit store is a limit enforced by two counters, and core's CMS was calling a UO file directly.

§2.7.1 also records the core.js port mechanism and its consequence (require order is load-bearing), the vendoring line (pure leaf helpers may be copied, security controls may not), the two core defects the extraction exposed, the one deliberate behaviour change, and the one test that looked like it should move and should not.

Corresponding code

Already merged: Module-uo#3 and website#137.

AI disclosure

Written with Claude Code.

Co-Authored-By: Claude noreply@anthropic.com

The docs half of Phase 3 slice 1, split off from docs#135 (which landed the plan and slice 0's record only). ## What's here **`docs(website): the server half does not slice, and why`** — found before writing any of slice 1. §2.7.1 had split the server extraction five ways by feature; it does not divide, because two contract rules compose. A mount prefix is claimed whole (`/admin/shard` is one 386-line router carrying 25 routes across atlas, clilocs, shard-ops, visibility, market and account links) and a model cannot be shared across the boundary, so it moves with its last consumer. Take the closure and every prefix is in it. Giving the admin routes their own prefixes would divide it, and is rejected: it changes API URLs, which §1.2 promises not to do — and not hypothetically, since the shipped Android app calls `POST /api/v1/admin/shard/kick`, `/ban`, `/unban`, `/broadcast` and the three `/pages` routes. The slice table is now **six** slices, not ten. The client half is unaffected and still slices cleanly. **`docs(website): record slice 1, and MODULE_API 1.1.0`** — the whole server half is out: 40 files, ~9,674 lines, 27 of 68 tables. The acceptance criterion held exactly — core's manifest goes 228 → 158 public routes, the 70 that left reappear byte-identical once the module loads, and `routes.guards.json` is identical across all 228. The contract grew to **1.1.0**: `ctx.activity.log`, `ctx.users.getById`, `ctx.site.baseUrl`, `ctx.middleware.rateLimit` + `accountChangeLimiter`, and a fourth registry, `registerPostHook`. Each is documented with *why it could not be vendored* — an admin action a module performs belongs in core's one audit log, a second rate-limit store is a limit enforced by two counters, and core's CMS was calling a UO file directly. §2.7.1 also records the `core.js` port mechanism and its consequence (require order is load-bearing), the vendoring line (pure leaf helpers may be copied, security controls may not), the two core defects the extraction exposed, the one deliberate behaviour change, and the one test that looked like it should move and should not. ## Corresponding code Already merged: `Module-uo#3` and `website#137`. ## AI disclosure Written with Claude Code. Co-Authored-By: Claude <noreply@anthropic.com>
wtclaude added 2 commits 2026-08-11 21:09:00 +00:00
Found before writing any of it. §2.7.1 split the server extraction five ways by
feature; it does not divide, because two contract rules compose.

A mount prefix is claimed whole -- ownedByCore probes the live tier router and
registerRoutes validates single-segment prefixes -- and /admin/shard is one
386-line router carrying 25 routes across atlas, clilocs, shard-ops, visibility,
market and account links. Meanwhile a model cannot be shared across the boundary
(§5.1), so it moves with the last route that consumes it. Take the closure and
every prefix is in it: /public/atlas holds shardAtlas with /admin/shard, which
holds shardState/shardEvents/shardMarket with /public/shard, which holds
uoLinkConfig with /admin/uo-link, and shardClilocs/shardLinks with /player/shard.

Landing any one of the old slices alone would either strand core importing
modules/uo/ -- what acceptance criterion 2 forbids -- or delete routes core is
still serving.

Giving the admin routes their own prefixes would divide it and is rejected: it
changes API URLs, which §1.2 promises not to do, and not hypothetically. The
shipped Android app calls POST /api/v1/admin/shard/kick, /ban, /unban,
/broadcast and the three /pages routes.

So the table is now six slices: 0 the skeleton (done), 1 the whole server half
as ONE PR per repo structured as five commits along the old slice lines, 2-4 the
client halves, 5 close the phase. The client half is unaffected and still slices
cleanly -- the registry takes routes per area, with no prefix atomicity and no
shared models, the same asymmetry that let the two halves be separated at all.

Co-Authored-By: Claude <noreply@anthropic.com>
The whole server half is out: 40 files, ~9,674 lines, 27 of 68 tables. The
acceptance criterion held exactly -- core's manifest goes 228 to 158 public
routes and the 70 that left reappear byte-identical once the module loads, with
routes.guards identical across all 228.

The contract grew to 1.1.0: ctx.activity.log, ctx.users.getById,
ctx.site.baseUrl, ctx.middleware.rateLimit + accountChangeLimiter, and a fourth
registry, registerPostHook. Each is documented with why it could not be
vendored, because that reasoning is the useful part -- an admin action a module
performs belongs in core's ONE audit log, a second rate-limit store is a limit
enforced by two counters, and core's CMS was calling a UO file directly.

§2.7.1 gains the slice record: the core.js port mechanism and its consequence
(require order is load-bearing), the vendoring line (pure leaf helpers may be
copied, security controls may not), the two core defects the extraction exposed,
the one deliberate behaviour change, and the one test that looked like it should
move and should not.

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech merged commit f47e2d3c6e into main 2026-08-11 21:53:22 +00:00
whitlocktech deleted branch docs/module-extract-server 2026-08-11 21:53:23 +00:00
Sign in to join this conversation.
No description provided.