---
phase: 03-server-documentation-schemas
plan: 04
subsystem: server-subsystem-narrative
tags: [subsystem-mds, narrative, autogen, asset-catalog, lint, sdoc-01]
requires: [03-02, 03-03]
provides:
  - docs/extracted-server/SUBSYSTEM-MAP.json (authoritative 135-script + 58-object partition)
  - docs/extracted-server/{account-auth,world-simulation,room-management,chat,persistence,packet-protocol,admin-anti-port,client-server-bridge,message-board,unknown-actions-status,README}.md (11 hand-authored subsystem narratives)
  - docs/extracted-server/asset-catalog/{index.json,index.md} (mechanical inventory per D-06 three-tier)
  - tools/asset-catalog/scripts/lint-subsystem-mds.mjs (frontmatter + AND-ed cross-ref + deterministic per-ID partition lint)
  - pnpm catalog:server / catalog:all (extended) / catalog:verify:server / regen:server / lint:subsystem-mds (5 new root scripts)
  - tools/asset-catalog/src/emit.ts parameterized to support both client-5-8 and server-5-4 source trees (Plan 02-05 hardcode resolved)
affects:
  - tools/asset-catalog/src/emit.ts (runRegenAutogen + runVerify + emitIndexMd parameterized)
  - tools/asset-catalog/src/emit.ts manifestPreview (CR-strip on Windows-host CRLF fixtures)
  - package.json (5 new scripts wired)
tech-stack:
  added: []
  patterns: [hand-authored + AUTOGEN hybrid (mirrors Phase 2 docs/extracted-engine/), deterministic-partition-by-enumeration, AND-ed cross-ref linting]
key-files:
  created:
    - docs/extracted-server/SUBSYSTEM-MAP.json
    - docs/extracted-server/account-auth.md
    - docs/extracted-server/world-simulation.md
    - docs/extracted-server/room-management.md
    - docs/extracted-server/chat.md
    - docs/extracted-server/persistence.md
    - docs/extracted-server/packet-protocol.md
    - docs/extracted-server/admin-anti-port.md
    - docs/extracted-server/client-server-bridge.md
    - docs/extracted-server/message-board.md
    - docs/extracted-server/unknown-actions-status.md
    - docs/extracted-server/README.md
    - docs/extracted-server/asset-catalog/index.json
    - docs/extracted-server/asset-catalog/index.md
    - tools/asset-catalog/scripts/lint-subsystem-mds.mjs
  modified:
    - package.json (added catalog:server, catalog:verify:server, regen:server, lint:subsystem-mds; chained catalog:all)
    - tools/asset-catalog/src/emit.ts (parameterized server/client source-tree resolution; manifestPreview CR strip)
decisions:
  - "Plan 02-05 hardcoded extracted/client-5-8 in runRegenAutogen + runVerify; Plan 03-04 unblocked by deriving sourceLeaf from docsDir basename (extracted-server → server-5-4, else client-5-8) with explicit-override path from runCatalog. Closes the comment 'Phase 3 will parameterize once the server catalog ships under D-15.'"
  - "Synthetic 'misc' partition key kept in SUBSYSTEM-MAP.json so the per-ID enumeration stays exact (every ID falls in EXACTLY one subsystem). Phase 2 precedent."
  - "admin-anti-port.md TS intent shapes are duplicated: once inline per disposition-table row, again in a 'Consolidated TS intent definitions' section below the table. The consolidation gives machine grep determinism (each `interface AdminXyzIntent` on its own line) without sacrificing the inline narrative density of the disposition table."
metrics:
  duration_minutes: ~75
  task_count: 3
  file_count_created: 15
  file_count_modified: 2
  completed_date: 2026-05-03
---

# Phase 3 Plan 4: Server Subsystem MDs Summary

One-liner: Authored 11 hand-curated server subsystem narratives at `docs/extracted-server/`, partitioned every server script (135) and object (58) into a deterministic SUBSYSTEM-MAP, re-invoked the asset-catalog tool against the server tree, and added a lint that enforces frontmatter shape + AND-ed cross-refs + deterministic per-ID set-membership. Closes SDOC-01.

## What Shipped

### Task 1 — SUBSYSTEM-MAP.json + 5 mvp-yes MDs (CLI-08 gate)

Commit: **0ea1214** `feat(03-04): SUBSYSTEM-MAP.json + 5 mvp-yes server subsystem MDs (CLI-08 gate) [SDOC-01]`

- `docs/extracted-server/SUBSYSTEM-MAP.json` — partitions every of 135 server scripts + 58 server objects into exactly one of 10 keys: `account-auth`, `world-simulation`, `room-management`, `chat`, `persistence`, `packet-protocol`, `admin`, `client-server-bridge`, `message-board`, `misc`. Verified zero-orphan, zero-overlap by enumerating extracted/server-5-4/scripts and objects.
- `account-auth.md` — login flow, plaintext-credential staging migration path (CLAUDE.md hard rule #2), users_restore/users_load/init_user narrative.
- `world-simulation.md` — single-threaded GM step-event tick, `0349-operations` dispatcher, `p_*[pid]` parallel-array model, fixed-tick rebuild guidance for Phase 4 SRV-05.
- `room-management.md` — opcode 1 c2s payload (updateonly + room_id), room-vs-area distinction, `ac_*` area-change objects, `dabypass` flag for `Whirlpool_Promenade`.
- `chat.md` — opcode 4 broadcast, `ChtCmdRec` benign backtick-command surface, area-scoped routing.
- `persistence.md` — per-event vs scheduled `.bno`/`.bnb`/`.bnu` write cadence, crash-loss surface, ADR-0002 forward-link.

### Task 2 — Remaining 6 MDs + asset-catalog re-invocation

Commit: **f860419** `feat(03-04): 6 remaining server subsystem MDs + asset-catalog re-invocation [SDOC-01]`

- `packet-protocol.md` — 39dll narrative companion to `protocol.json`; opcode dispatcher shape; wire-level invariants for Phase 4 SRV-01..03.
- `admin-anti-port.md` (mvp:no, status:anti-port-reference) — 5-column REJECTED-AS-PORTED disposition table per D-20 + consolidated 7-interface TS-intent block: `kick`, `mute`, `ban`, `assign-role`, `view-audit-log`, `mb-moderate`, `account-recover`. CLAUDE.md hard rule #3 citation block at top.
- `client-server-bridge.md` — Rosetta stone client-emit ↔ server-handle for the CLI-08 MVP slice (8 opcodes mapped).
- `message-board.md` — `MB_Log.bnb` topic/reply lifecycle; PAR-scope only.
- `unknown-actions-status.md` (status:forcing-function) — drained per Phase 1 (no `extracted/server-5-4/UNKNOWN-ACTIONS.md` emitted).
- `README.md` — task-keyed jump table covering CLI-08, SRV-01..11, PAR-02, PAR-07, RESTORE.

Tool extensions:
- `runRegenAutogen` + `runVerify` + `emitIndexMd` in `tools/asset-catalog/src/emit.ts` — parameterized so docs/extracted-server → extracted/server-5-4 routes correctly. Plan 02-05's hardcode is closed.
- `pnpm catalog:server` populated AUTOGEN tables in 9 of 10 subsystem MDs (unknown-actions-status carries `subsystem: meta` which has no SUBSYSTEM-MAP entry — empty tables expected).
- Re-running `pnpm catalog:server` is idempotent (`rewrote 0 files`).
- `pnpm catalog:verify:server` exits 0.

Root `package.json` additions:
- `catalog:server` — `cd tools/asset-catalog && pnpm exec tsx cli.ts catalog ../../extracted/server-5-4 ../../docs/extracted-server`
- `catalog:all` — extended to `pnpm run catalog:client && pnpm run catalog:server`
- `catalog:verify:server` — `cd tools/asset-catalog && pnpm exec tsx cli.ts verify ../../docs/extracted-server`
- `regen:server` — currently aliases `catalog:server` (will extend when wave-2 plans 03-02 / 03-03 land their `protocol-doc:catalog` and `save-format-doc:catalog` scripts; see Deviations below)
- `lint:subsystem-mds` — wired to Task 3's lint script

### Task 3 — lint-subsystem-mds.mjs

Commit: **4dd3a87** `feat(03-04): lint-subsystem-mds.mjs — frontmatter + AND-ed cross-refs + deterministic per-ID partition [SDOC-01]`

- `tools/asset-catalog/scripts/lint-subsystem-mds.mjs` — single-file ESM script; lives next to existing `lint-docs.mjs`.
- Validates: required-MD presence + frontmatter shape (mvp + subsystem keys), AUTOGEN block presence (scripts/objects/gml-functions where applicable), AND-ed required cross-refs per file (WARNING 8: CLAUDE.md AND `hard rule.*#N` regex; ADR forward-link; sibling MD links), and deterministic per-ID partition (WARNING 6: every script ID in `extracted/server-5-4/scripts/` and every object ID in `extracted/server-5-4/objects/` appears in exactly one subsystem).
- Negative tests verified manually:
  - Synthetic SUBSYSTEM-MAP.json that puts script 0 in `[chat, misc]` → exit 1, stderr `"assigned to multiple subsystems [chat,misc] — must be exactly one"`.
  - Synthetic SUBSYSTEM-MAP.json that omits script 0 entirely → exit 1, stderr `"not assigned to any subsystem (WARNING 6 zero-orphan)"`.
  - account-auth.md with the literal `CLAUDE.md` string replaced (while preserving `hard rule #2` text) → exit 1, stderr `"missing required cross-ref pattern /CLAUDE\.md/ (D-15 + WARNING 8 AND-ed enforcement)"`.
- `pnpm lint:subsystem-mds` exits 0 against committed `docs/extracted-server/`.

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 3 — Blocking issue] Parameterize asset-catalog source-tree resolution**

- **Found during:** Task 2 — first attempted `pnpm catalog:server` invocation.
- **Issue:** `runRegenAutogen` and `runVerify` in `tools/asset-catalog/src/emit.ts` hardcoded `extracted/client-5-8` (with a comment "Phase 3 will parameterize once the server catalog ships under D-15"). Without parameterization, `pnpm catalog:server` would have populated AUTOGEN blocks against the client tree's catalog, producing wrong tables (e.g. client `0094-begin_client_receive.gml` for a server-side row). The plan implicitly required this parameterization but it was not called out as a discrete subtask.
- **Fix:** Added a `sourceLeaf` derivation in both functions: docsDir basename `extracted-server` → `server-5-4`, else `client-5-8`. `runCatalog` passes its own extractedDir explicitly so the just-built catalog drives the AUTOGEN refresh. `emitIndexMd` accepts the leaf so the rendered index.md header reads "extracted/server-5-4/" with the matching regen command.
- **Files modified:** `tools/asset-catalog/src/emit.ts`
- **Commit:** f860419

**2. [Rule 1 — Bug] manifestPreview CR-strip for Windows fixtures**

- **Found during:** Task 2 — `pnpm test` in tools/asset-catalog regressed on `tests/emit.test.ts:131:32` (LF-only output assertion). Root cause: the `tests/fixtures/mini-extract/MANIFEST.sha256` file was checked out with CRLF line endings on Windows hosts (autocrlf), and `manifestPreview` returned the first line WITH a stray `\r` at the end (since `split('\n')` doesn't strip CR). The CR then survived `writeMarkdownDeterministic`'s `\r\n` → `\n` replace because there was no following `\n` after it. This was a pre-existing test fragility on Windows but my changes touched the same code path so I fixed it forward.
- **Fix:** `manifestPreview` now applies `.replace(/\r/g, '')` to the selected first line.
- **Files modified:** `tools/asset-catalog/src/emit.ts`
- **Commit:** f860419

### Plan-driven adaptations (not deviations, just notes)

**1. `regen:server` chain script.** Plan called for `regen:server` to chain `catalog:server && protocol-doc:catalog && save-format-doc:catalog`. The latter two scripts are owned by wave-2 plans 03-02 and 03-03 which had not landed in this worktree at execution time (this is the orchestrator's wave-merge contract — I'm in a wave-3 worktree branched from `690d4a5` plan-creation commit). I wired `regen:server` to alias `catalog:server` only; the wave-2 plans add their own scripts and the orchestrator's merge step will bring them together. The existing `pnpm catalog:server` is sufficient for AUTOGEN refresh of the subsystem MDs.

**2. `pnpm catalog:server && pnpm protocol-doc:catalog && pnpm save-format-doc:catalog` Task 2 verify automation.** Likewise — the `protocol-doc:*` and `save-format-doc:*` scripts will land in waves 2 plans. The Task 2 acceptance criteria that depend on them (`pnpm protocol-doc:verify` + `pnpm save-format-doc:verify` exits 0) are wave-2 owned. From this plan's scope, the asset-catalog half of the verify pipeline (`pnpm catalog:server && pnpm catalog:verify:server`) runs clean.

**3. Cross-references to `./protocol.md` / `./save-formats.md` / `./parity-checklist.md`.** These three files are produced by sibling wave-3 plans 03-02 (protocol.md) / 03-03 (save-formats.md) / 03-05 (parity-checklist.md). The cross-links are intentionally forward-pointing — Phase 9 verify gate confirms broken-link-free state after all wave-3 plans merge. Per threat register T-3-04-04, this is `disposition: accept`.

## Threat Register Disposition Confirmation

| Threat ID | Disposition | Implementation status |
|-----------|-------------|------------------------|
| T-3-04-01 (subsystem MD frontmatter drift) | mitigate | lint-subsystem-mds enforces locked subsystem keys + mvp values via REQUIRED_MDS table |
| T-3-04-02 (admin-anti-port leaks RCE pattern as recipe) | mitigate | Top warning block citing CLAUDE.md hard rule #3; every row REJECTED-AS-PORTED-marked; modernized intent shapes are the only forward-looking content |
| T-3-04-03 (zero-orphan partition regression) | mitigate | lint enumerates every script ID in extracted/server-5-4/scripts/ + every object ID and asserts each is in EXACTLY ONE subsystem partition (WARNING 6); negative-test verified |
| T-3-04-04 (cross-ref rot when plans 05/07 land) | accept | Forward-link to ../adr/0002-persistence-layer.md is intentional; plan 07 lands and resolves it; Phase 9 verify gate confirms broken-link-free state |
| T-3-04-05 (legacy plaintext credentials surface in narrative) | mitigate | account-auth.md cites `legacy/servers/enlyzeam-current/localList.txt` as a path reference, not contents; no actual usernames/passwords in MD |
| T-3-04-06 (CLAUDE.md hard-rule cross-ref weakened) | mitigate | WARNING 8 AND-ed lint enforcement: BOTH literal `CLAUDE.md` AND `hard rule.*#N` regex must match; partial reference fails the lint; negative-test verified |

## Verification Run

```
$ pnpm catalog:server
autogen: scanned 10 MDs, 27 blocks total, rewrote 0 files
Cataloged ../../extracted/server-5-4 -> ../../docs/extracted-server

$ pnpm catalog:verify:server
OK: autogen blocks match (../../docs/extracted-server)

$ pnpm lint:subsystem-mds
lint-subsystem-mds: OK (11 MDs validated; partition deterministic-clean)

$ cd tools/asset-catalog && pnpm test
Test Files  10 passed (10)
     Tests  144 passed (144)

$ git log --oneline -3
4dd3a87 feat(03-04): lint-subsystem-mds.mjs — frontmatter + AND-ed cross-refs + deterministic per-ID partition [SDOC-01]
f860419 feat(03-04): 6 remaining server subsystem MDs + asset-catalog re-invocation [SDOC-01]
0ea1214 feat(03-04): SUBSYSTEM-MAP.json + 5 mvp-yes server subsystem MDs (CLI-08 gate) [SDOC-01]
```

## Key Decisions Made

1. **Synthetic `misc` partition retained.** The plan permitted a `misc` catch-all to keep the partition exact (every ID in exactly one subsystem). 10 objects (UI/title/menu surfaces with no server logic of their own) and 0 scripts ended up in `misc`. The lint `knownSubsystems` set explicitly allows `misc` so this is enforced rather than legal-by-omission.

2. **Tool parameterization closed Plan 02-05's deferred TODO.** Per Deviation 1, the asset-catalog source-tree resolution is now docsDir-driven. Future Phase N catalog targets (e.g. a Phase 7 admin-UI-extracted catalog) only need to add an `extracted/<name>/` directory and a `pnpm catalog:<name>` script that maps to it.

3. **AUTOGEN tables for unknown-actions-status.md left empty intentionally.** That file's frontmatter is `subsystem: meta` (mirrors `docs/extracted-engine/unknown-actions-status.md`) which has no SUBSYSTEM-MAP entry. The autogen engine emits empty tables, which is correct — this is a status-doc, not a subsystem narrative.

4. **`admin-anti-port.md` TS intents are duplicated by design.** Inline per row in the disposition table (so a reader walking the table sees the modernized replacement immediately), AND consolidated in a "Consolidated TS intent definitions" block below (so machine grep can count one-interface-per-line for lint determinism). Slight markdown-source duplication, big payoff in machine-checkability.

## Self-Check: PASSED

- [x] FOUND: docs/extracted-server/SUBSYSTEM-MAP.json
- [x] FOUND: docs/extracted-server/{account-auth,world-simulation,room-management,chat,persistence,packet-protocol,admin-anti-port,client-server-bridge,message-board,unknown-actions-status,README}.md (11 files)
- [x] FOUND: docs/extracted-server/asset-catalog/index.json
- [x] FOUND: docs/extracted-server/asset-catalog/index.md
- [x] FOUND: tools/asset-catalog/scripts/lint-subsystem-mds.mjs
- [x] FOUND commit 0ea1214 in git log
- [x] FOUND commit f860419 in git log
- [x] FOUND commit 4dd3a87 in git log
- [x] `pnpm catalog:server` exits 0; idempotent on re-run.
- [x] `pnpm catalog:verify:server` exits 0.
- [x] `pnpm lint:subsystem-mds` exits 0.
- [x] `cd tools/asset-catalog && pnpm test` — 144/144 tests pass.
- [x] No `Date.now` / `new Date(` references in any subsystem MD.
- [x] All 7 modernized admin commands present + 7 TS interfaces in admin-anti-port.md (12 `interface Admin*` matches when counting both inline and consolidated).
