# Phase 3: Server Documentation & Schemas - Context

**Gathered:** 2026-05-03
**Status:** Ready for planning
**Mode:** Interactive on canonical-snapshot ADR (SDOC-06); Claude's discretion on SDOC-01..05 with explicit "modernize and streamline the server infrastructure" steer from user.

<domain>
## Phase Boundary

Read `extracted/server-5-4/` (135 scripts, 58 objects, plus sprites/backgrounds/rooms/datafiles/timelines/settings.json) and `legacy/servers/{enlyzeam-current,enlyzeam-archive,local-current/BNO_Server}/` world-state to produce six locked deliverables that close out SDOC-01..06:

1. `docs/extracted-server/` — narrative documentation of every server feature, organized by subsystem (SDOC-01).
2. `docs/extracted-server/protocol.{md,json}` + `tools/protocol-doc/output/protocol.ts` — reversed 39dll opcode table, every C→S and S→C message, derived from GML `write*`/`read*` call order in extracted Master 5-4 (SDOC-02).
3. `docs/extracted-server/save-formats.{md,json}` + TS types — `.bno`/`.bnb`/`.bnu` field-by-field schemas, derived from GML `file_text_*` call sequence (SDOC-03). **Wiki errata:** `decomp/wiki/16-bno-bnb-notes.md` claims `file_bin_*`; ground truth in extracted Master 5-4 is `file_text_*` (line-based). Update wiki as part of phase scope.
4. `docs/adr/0002-persistence-layer.md` — SQLite + Litestream vs Postgres ADR locked, with draft Drizzle schema for accounts / characters / inventory / message_board / audit_log (SDOC-04).
5. `docs/extracted-server/parity-checklist.{md,json}` — closed parity-feature checklist locked at phase end, every feature marked `in-phase-6` | `in-phase-7` | `deferred-stage-8` | `rejected-with-reason`. Feeds Phase 7 (SDOC-05).
6. `docs/adr/0003-canonical-snapshot.md` — canonical-snapshot ADR resolving the three drift'd server snapshots (SDOC-06).

In scope:
- Subsystem-MD narrative + auto-generated indices (per Phase 2 D-09/D-11 thin-wrapper pattern).
- Server-side `tools/asset-catalog` invocation against `extracted/server-5-4/` → `docs/extracted-server/asset-catalog/`.
- Server admin anti-port reference + modernized Phase 7 admin command surface specification (parallel Phase 2 D-02).
- Wiki/16 errata commit (file_text_* not file_bin_*).
- Drizzle schema draft (DDL + migration 0001 baseline) feeding Phase 4 SRV-01..03 directly.

Out of scope (belongs in later phases):
- New TypeScript outside `tools/` (CLAUDE.md hard rule #6 — no `apps/server`, no `packages/protocol`, no `packages/game-logic` until Phase 4).
- Client engine documentation (Phase 2, complete).
- Asset format conversion BMP→PNG, MIDI→OGG, font→WOFF2 (Phase 6/7 `tools/asset-pipeline`).
- Implementation of the modernized admin web UI (Phase 7 PAR-07; Phase 3 only specifies command surface).
- pnpm workspace conversion (Phase 4).
- Older Master `.gmd`/`.gb1` extraction (legacy/servers/enlyzeam-archive/Master 4.gmd, 5-1..5-3.exe). Master 5-4 is source-of-truth per CLAUDE.md.

</domain>

<decisions>
## Implementation Decisions

### SDOC-06 — Canonical-snapshot ADR (interactive)

- **D-01 (purpose):** Canonical snapshot is the **production seed for migration**. Returning BNO players see their last character / inventory / username on first login against the new server. The seed is consumed by Phase 4 SRV-10 (legacy account import script) and Phase 7 PAR-05 (`.bnu` character migration). Not historical preservation only; not GML-drift-reference only.
- **D-02 (resolution):** **Pick one snapshot whole — `legacy/servers/enlyzeam-current/`.** No per-record merge across the three. Rationale: largest world-state dataset by load-bearing dimensions:
  - 973 `.bnu` user files (vs enlyzeam-archive 967, local-current 211)
  - 597-line `localList.txt` (vs enlyzeam-archive 597, local-current 90)
  - 39549-byte `MB_Log.bnb` with **newer mtime** than enlyzeam-archive's identical-size copy
  - enlyzeam-archive's larger total dir size (46M vs 34M) is from older Master `.exe` + `.gb1` backups + dev cruft, not world-state.
  - local-current dropped in v1: 73% smaller world-state; clearly truncated/older run despite later activity.
- **D-03 (older-GML extraction):** **Skip.** Master 5-4 is source-of-truth per CLAUDE.md hard rule. Do NOT extend Phase 1 extractor to Master 4.gmd / 5-1.exe / 5-2.exe / 5-3.exe. SDOC-02 protocol table is grounded in Master 5-4 alone. If a specific opcode trace fails or a script reference looks broken during execution, on-demand extract the relevant older artifact and document the deviation; otherwise no Phase-3 extraction work on legacy snapshots.
- **D-04 (PII handling for the seed):** Phase 3 documents the **legacy-credential staging schema** consumed by Phase 4 SRV-10/SRV-11:
  ```
  legacy_credentials_staging (read-once-then-purged)
    username           TEXT PRIMARY KEY
    legacy_hash        BLOB
    algorithm          TEXT  -- 'plaintext' | 'bcrypt-weak' | ...
    force_reset        INTEGER  -- 1 if algorithm in {'plaintext','bcrypt-weak'}
    legacy_source      TEXT  -- 'enlyzeam-current/localList.txt:LINE'
    imported_at        INTEGER
  ```
  Plaintext from `localList.txt` lands ONLY in this staging table, NEVER in the production `accounts` table. Phase 4 SRV-09/10/11 owns: argon2id rehash on first successful legacy login (drop legacy_hash row), force-reset flow when `force_reset=1`. Phase 5 deploy procedure includes the staging-table read-once-then-purge step in `RESTORE.md`. Plaintext password rows leave the seed snapshot in `legacy/servers/enlyzeam-current/` — never copied verbatim into the prod database.

### SDOC-02 — 39dll wire protocol (Claude's discretion, modernize/streamline bias)

- **D-05 (output layering):** Three artifacts, all generated from a single canonical JSON to prevent drift (Phase 1 D-15 / Phase 2 D-12 pattern):
  1. `docs/extracted-server/protocol.json` — canonical machine-readable opcode table.
  2. `tools/protocol-doc/output/protocol.ts` — TypeScript types + binary codec stubs. **Not yet a workspace package** (pnpm workspaces land Phase 4 per Phase 1 D-17). Output lives in `tools/protocol-doc/output/` so Phase 4 `packages/protocol` can `cp -r` or `import` it directly without re-deriving.
  3. `docs/extracted-server/protocol.md` — human-readable rendered table with AUTOGEN blocks per Phase 2 D-11.
- **D-06 (per-opcode metadata):** Each opcode row carries:
  - `opcode_byte` (hex)
  - `direction` (`c2s` | `s2c`)
  - `mvp` (boolean — `true` if needed for CLI-08 movement+chat slice)
  - `name` (semantic name derived from script context, not the original numeric ID)
  - `fields[]` — ordered list, each with `{name, gml_type, ts_type, byte_size}`
  - `gml_origin[]` — `{script: 'NNNN-name.gml', line: N, snippet: '...'}` for every `write*`/`read*` site
  - `sample_bytes` — at least one hex test vector per opcode (synthetic if no capture available)
- **D-07 (methodology):** Per PITFALLS A4 GML-grep procedure:
  1. `grep -rn "sendmessage\|receivemessage" extracted/server-5-4/{scripts,objects}/`
  2. For each call site, trace backward to enumerate the `clearbuffer; writebyte; writedouble; writestring; ...` sequence.
  3. Cross-reference `legacy/open-source-release/BN Online Message ID's Table.xls` (XLS → CSV via `xlsx` lib in tools/protocol-doc) for legacy opcode-name hints, but the call sequence in extracted Master 5-4 is canonical truth on conflict.
  4. Wireshark / packet captures used as **post-hoc validation only**, never as primary source.

### SDOC-03 — Save-format schemas (Claude's discretion, modernize/streamline bias)

- **D-08 (wiki errata):** First task of phase: update `decomp/wiki/16-bno-bnb-notes.md` to record the file_text_* finding. Verified in `extracted/server-5-4/scripts/0365-mb_backup.gml` (`file_text_open_write("MB_Log.bnb")` + `file_text_write_string` + `file_text_writeln`) and `0367-users_restore.gml` (`file_text_open_read("User_DBUpdated.bnu")` + `file_text_read_string` + `file_text_readln`). Same correction applies to PITFALLS A5 wording. Commit the errata before generating schemas so downstream agents read the corrected wiki.
- **D-09 (output layering):** Same three-artifact pattern as protocol (D-05):
  1. `docs/extracted-server/save-formats.json` — canonical machine-readable schema.
  2. `tools/save-format-doc/output/save-formats.ts` — TypeScript types + parsers. Phase 4/7 migration code consumes these, never reads bytes blind (PITFALLS A5).
  3. `docs/extracted-server/save-formats.md` — rendered tables, AUTOGEN.
- **D-10 (per-format metadata):** Each format row carries:
  - `extension` (`.bno` | `.bnb` | `.bnu`)
  - `filename_pattern` (e.g., `MSettings.bno`, `MB_Log.bnb`, `UserData/HXB/<username>.bnu`)
  - `load_script` + `save_script` GML citations (e.g., `0365-mb_backup.gml:1`, `0367-users_restore.gml:1`)
  - `mvp` (true for accounts + character position; false for message-board archive)
  - `grammar` — ordered list of fields, each with `{name, type ('string'|'real'|'sectioned'), repetition, terminator, semantics}`. Sectioned formats (e.g., `mb_backup` `@TOPIC`/`@REPLY` markers) explicitly modeled with section grammar, not flat field list.
  - `sample_records` — at least one decoded example per format from enlyzeam-current.

### SDOC-04 — Persistence ADR (Claude's discretion, modernize/streamline bias)

- **D-11 (lock SQLite + Litestream):** ADR records SQLite (better-sqlite3) + Litestream-to-Tigris with rationale tied to documented data model: <50 CCU target, no relational join requirement surfaces in extracted server (`.bnu` is per-user denormalized state already, message_board is append-mostly log). Postgres rejected with reason; OPS-03 (v2) preserves migration option if Phase 7+ surfaces a relational requirement.
- **D-12 (Drizzle ORM, schema-first):** Drizzle for type-safe DDL + migrations. Lock at end of Phase 3 with reason: shared TS types into Phase 4 `packages/protocol` boundary; raw SQL for hot paths permitted via `db.run(sql\`...\`)` escape hatch.
- **D-13 (draft schema in-phase):** Phase 3 produces `docs/extracted-server/schema.sql` + Drizzle table definitions for:
  - `accounts` (id, username, argon2id_hash, created_at, last_login_at, force_reset, role)
  - `legacy_credentials_staging` (per D-04)
  - `characters` (id, account_id, room_id, x, y, sprite_id, created_at, last_saved_at) — derived from `.bnu/HXB` schema
  - `inventory_items` (account_id, slot, item_id, qty) — derived from `.bnu/Inv` schema
  - `message_board_topics` + `message_board_replies` — derived from `MB_Log.bnb` grammar (D-10)
  - `audit_log` (id, actor_account_id, action, target_account_id, payload_json, created_at) — empty table seeded for Phase 7 PAR-07
  - `sessions` (id, account_id, room_id, connected_at, heartbeat_at) — for Phase 4 SRV-06 reconnection grace
  Phase 4 SRV-01..03 consumes this schema verbatim — no redesign loop.
- **D-14 (no original-table-shape preservation):** Original `.bnu` text format is read-once-then-discarded by SRV-10 import. New schema is normalized SQL designed for the new server, not a faithful port of file-text-line layout. "Modernize and streamline" steer drives this.

### SDOC-01 — Subsystem narrative & SDOC-05 — Parity checklist (Claude's discretion, modernize/streamline bias)

- **D-15 (subsystem MD list, locked):** `docs/extracted-server/`:
  - `account-auth.md` — credential storage, login/logout, account creation
  - `world-simulation.md` — tick loop (none in original — single-threaded GameMaker), entity update order
  - `room-management.md` — room transitions, instance lifecycle, persistence flags
  - `chat.md` — message routing, command parsing (ChtCmdRec — `0008-ChtCmdRec.gml`), whispers, channels
  - `persistence.md` — `.bno`/`.bnb`/`.bnu` write cadence, save triggers, crash-loss surface
  - `packet-protocol.md` — narrative companion to `protocol.{json,ts,md}` (D-05); points at autogen
  - `admin-anti-port.md` — Ctrl+O Codes.txt, ,ServerCommands.txt, Account Updater.exe, Server Saver.exe behaviors. Marked REJECTED-AS-PORTED with reason; Phase 7 modernized replacements named per command.
  - `client-server-bridge.md` — links Phase 2 `client-networking.md` to `packet-protocol.md`
  - `message-board.md` — MB_Log.bnb format + topic/reply lifecycle
  - `unknown-actions-status.md` — same forcing function as Phase 2 D-08, server-side DnD scope
  - `README.md` — task-keyed jump table per Phase 2 specifics
  - Plus autogen `asset-catalog/` subtree.
- **D-16 (narrative depth):** Functional clusters with citation + key-script call-out (parallel Phase 2 D-10). Group by behavior ("account login — see `0367-users_restore.gml`, `0359-server_receive.gml`"). Quote 5-30 line snippets inline. Do NOT reverse every line of every script — diminishing returns. mvp: yes/no front-matter per feature for Phase 6 grep (parallel Phase 2 D-04).
- **D-17 (server asset catalog):** Re-invoke `tools/asset-catalog` against `extracted/server-5-4/` → `docs/extracted-server/asset-catalog/index.{json,md}`. Server has fewer renderable assets (no sprite-rendering; server is logic) but the script roster + object roster autogen is load-bearing for SDOC-01 narrative cross-refs (parallel Phase 2 D-11). Repo-level script `pnpm catalog:server` added; existing `pnpm catalog:all` extended to run client + server.
- **D-18 (parity checklist granularity):** **Per-feature** rows, NOT per-opcode and NOT per-script. Each row in `parity-checklist.json`:
  ```
  {
    "feature": "whisper",
    "originating_gml": ["0008-ChtCmdRec.gml", "0359-server_receive.gml"],
    "originating_opcodes": [12, 47],   // refs into protocol.json
    "originating_save_fields": [],
    "mvp": false,
    "disposition": "in-phase-7",       // in-phase-6 | in-phase-7 | deferred-stage-8 | rejected-with-reason
    "reason": "Full chat surface per PAR-04; out of CLI-08 MVP scope.",
    "modernized_replacement": null     // e.g., "kick" -> "POST /admin/kick {account_id, reason}" for admin features
  }
  ```
  Locked at phase end. Phase 7 PAR-08 closes against this list; nothing not on the list is in v1 scope.
- **D-19 (modernize-bias for parity dispositions):** Aggressive `rejected-with-reason` for any feature whose original implementation is incompatible with server-authoritative discipline (PITFALLS B1) OR depends on the Ctrl+E clipboard-RCE admin model (CLAUDE.md hard rule #3). Each rejection records a one-line reason. Examples expected: client-trusted positions, client-trusted scores, raw-clipboard admin commands. Modernized replacements named in `modernized_replacement` field where applicable.
- **D-20 (modernized admin command surface):** `docs/extracted-server/admin-anti-port.md` per command lists:
  - Original keybind / mechanism (e.g., `Ctrl+O`, `,ServerCommands.txt`)
  - Original behavior (1-line)
  - REJECTED-AS-PORTED marker + reason
  - Modernized replacement command surface (intent name + payload TS shape) — e.g., `kick`, `mute`, `ban`, `assign-role`, `view-audit-log`, `mb-moderate`, `account-recover`. Concrete TS intent shape per command, not just placeholder name. Phase 7 PAR-07 implements; Phase 4 SRV-12 documents in apps/server as anti-port stub.

### Tooling

- **D-21 (tool layout):** Two new tools under `tools/`:
  - `tools/protocol-doc/` — TS Node CLI, reads `extracted/server-5-4/`, emits `protocol.{json,ts,md}` deterministically (Phase 1 D-15 / Phase 2 D-16 conventions).
  - `tools/save-format-doc/` — TS Node CLI, reads `extracted/server-5-4/scripts/`, emits `save-formats.{json,ts,md}`.
  Standalone Node CLIs, not yet workspace packages (parallel Phase 1 D-17 / Phase 2 D-15).
- **D-22 (lints):** `lint-protocol.mjs` enforces: every opcode has at least one `gml_origin` citation; every `mvp:true` opcode appears in CLI-08 message-list (movement, chat, login, room-join, room-leave, heartbeat). `lint-save-formats.mjs` enforces: every format has `load_script` + `save_script` citations. `lint-parity.mjs` enforces: every parity-checklist row has non-empty `originating_gml` AND a non-null `disposition`; aggregate count of rows in each disposition logged.

### Claude's Discretion

User selected canonical-snapshot ADR (SDOC-06) for interactive discussion and delegated SDOC-01..05 with steer "modernize and streamline the server infrastructure." Decisions D-05..D-22 reflect best judgment grounded in:
- Phase 1 D-15 (deterministic output), Phase 1 D-17 (tools/ layout, no workspaces yet)
- Phase 2 D-04 (mvp tagging), D-09 (subsystem MDs), D-10 (functional-cluster depth), D-11 (autogen indices), D-15 (asset-catalog tooling)
- PROJECT.md Stack constraints, STACK.md (SQLite+Litestream+Better-Auth+argon2id locked), SUMMARY.md (Phase 3 deliverables)
- PITFALLS A4/A5 (GML-call-order = schema), B1 (server-authoritative discipline)
- CLAUDE.md hard rules #2 (no plaintext password port), #3 (no Ctrl+E admin port), #4 (extract before parse), #6 (extract→document→rewrite)

Override any decision in planning if downstream research or codebase reading surfaces a conflict.

### Folded Todos

None — `gsd-sdk query todo.match-phase 3` returned 0 matches.

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### Project planning
- `.planning/PROJECT.md` — vision, constraints (SoT = `BN Online Master 5-4.gmd`), key decisions, anti-decisions
- `.planning/REQUIREMENTS.md` §"Server Documentation (Stage 3)" — SDOC-01..06 acceptance text
- `.planning/ROADMAP.md` §"Phase 3: Server Documentation & Schemas" — goal + 5 success criteria, "research-flagged" call-out
- `.planning/research/SUMMARY.md` §"Phase 3: Stage 3 — Server Analysis" — research-flagged items (canonical-snapshot merge, schema methodology, modernized admin command set)
- `.planning/research/STACK.md` — Colyseus 0.17.10, Better-Auth, argon2id, better-sqlite3, Drizzle, Litestream-to-Tigris, version pins
- `.planning/research/ARCHITECTURE.md` — three-pipeline topology; where `tools/protocol-doc` and `tools/save-format-doc` fit
- `.planning/research/FEATURES.md` §"MVP-Critical (Stage 6 gate)" — feeds D-06 / D-10 mvp:true tags and parity-checklist mvp field
- `.planning/research/PITFALLS.md` §A4 (39dll wire = GML call order), §A5 (`.bno`/`.bnb`/`.bnu` = GML call order — file_text_* per D-08 errata, NOT file_bin_* as wiki/16 currently states), §B1 (server-authoritative), §C5 (`.bnu` migration), §C7 (parity scope explosion → closed checklist)
- `.planning/codebase/CONCERNS.md` §"Plaintext player credentials checked into the archive (CRITICAL)", §"Server runs arbitrary clipboard code as superuser", §"Three drift'd server snapshots with no merge plan", §"Bnu/bno/bnb formats undocumented except as 'read the GML'"
- `.planning/phases/01-extraction/01-CONTEXT.md` — D-09 (DnD descriptor canonical), D-15 (determinism), D-17 (tools/ standalone, no workspaces)
- `.planning/phases/02-client-engine-documentation/02-CONTEXT.md` — D-02 (admin-anti-port pattern), D-04 (mvp tagging), D-09 (subsystem MD list), D-10 (functional-cluster depth), D-11 (autogen indices), D-15/D-16 (catalog tooling + determinism)
- `docs/adr/0001-client-engine.md` — first ADR in repo (template for D-11/D-03 ADRs Phase 3 produces); Phaser 3.90.0 locked

### Phase 1 outputs (consumed as input)
- `extracted/server-5-4/` — primary input: 135 scripts, 58 objects, sprites/backgrounds/rooms/datafiles/timelines/settings.json
- `extracted/server-5-4/MANIFEST.sha256` — reproducibility manifest
- `extracted/server-5-4/UNKNOWN-ACTIONS.md` — Phase 3 server-side parallel of Phase 2 D-08 forcing function
- `extracted/server-5-4/scripts/0014-dllinit.gml`, `0015-dllfree.gml`, `0023-sendmessage.gml`, `0024-receivemessage.gml`, `0359-server_receive.gml`, `0360-init_user.gml`, `0008-ChtCmdRec.gml` — load-bearing for SDOC-02 protocol RE
- `extracted/server-5-4/scripts/0365-mb_backup.gml`, `0366-mb_restore.gml`, `0367-users_restore.gml`, `0368-uarea_restore.gml`, `0369-uarea_backup.gml`, `0371-unews_backup.gml`, `0372-unews_restore.gml`, `0376-uinv_backup.gml`, `0377-uinv_restore.gml`, `0379-users_restore_old.gml` — load-bearing for SDOC-03 save schemas (`file_text_*` not `file_bin_*` per D-08)
- `tools/extract-gmd/src/types.ts` — canonical extraction shapes consumed by `tools/protocol-doc` and `tools/save-format-doc`
- `tools/asset-catalog/` — re-invoked against server tree (D-17)

### Reverse-engineering wiki (decomp/wiki/)
- `decomp/wiki/00-overview.md` — entry point
- `decomp/wiki/05-gml-vm.md` — GML script semantics
- `decomp/wiki/06-gml-syntax-5x.md` — 5.x syntax details
- `decomp/wiki/07-gml-core-functions.md` — `external_define`, `file_text_*`, `file_bin_*`, `clearbuffer`/`writebyte`/`readbyte` etc.
- `decomp/wiki/08-39dll-networking.md` — 39dll workflow, "packet structure = read/write call order"
- `decomp/wiki/16-bno-bnb-notes.md` — **wiki errata target per D-08**: currently asserts `file_bin_*`; change to `file_text_*` based on extracted server findings
- `decomp/wiki/quick-reference.md`, `glossary.md`

### Anti-port reference (legacy artifacts)
- `legacy/open-source-release/,ServerCommands.txt` — server admin keybind list (D-20 input)
- `legacy/servers/enlyzeam-current/Ctrl+O Codes.txt` — operator code reference, embeds GML referencing live `server` object (D-20 input)
- `legacy/open-source-release/BN Online Message ID's Table.xls` — opcode-name reference (D-07 cross-check, NOT canonical truth)

### Canonical snapshot (D-02 lock)
- `legacy/servers/enlyzeam-current/` — **canonical world-state seed for SRV-10 / PAR-05 migration**:
  - `localList.txt` (597 lines, ~298 accounts) → `legacy_credentials_staging` per D-04
  - `UserData/HXB/*.bnu`, `UserData/Inv/*.bnu`, `UserData/MB_News/*.bnu` (973 files) → `characters` + `inventory_items` per D-13
  - `MB_Log.bnb` (39549 B) → `message_board_topics` + `message_board_replies` per D-13
  - `Settings.bno`, `MSettings.bno` → admin-config baseline (plaintext admin creds per CONCERNS — argon2id rehash via D-04 staging path)

### Sources of truth — read-only
- `legacy/open-source-release/BN Online Master 5-4.gmd` — server source `.gmd` (already extracted to `extracted/server-5-4/`)

### External implementation references
- LateralGM source (`github.com/IsmAvatar/LateralGM`) — Action_ID resolution if any unmapped server-side DnD surfaces (parallel Phase 2 D-08)
- Colyseus 0.17 docs (`docs.colyseus.io`) — schema model informs D-05 (`packages/protocol` shape Phase 4 will adopt)
- Drizzle ORM docs (`orm.drizzle.team`) — D-12/D-13 schema authoring
- Litestream docs (`litestream.io`) — informs D-11 ADR rationale
- Better-Auth docs (`better-auth.com`) — informs D-04 staging-table integration with Phase 4 SRV-09/10/11

</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets

- **`tools/extract-gmd/`** (Phase 1) — `tools/protocol-doc` and `tools/save-format-doc` reuse the same project layout, build/test/typecheck conventions, deterministic-output discipline, and exported types from `tools/extract-gmd/src/types.ts` (`Script`, `GmObject`, `Event`, etc.).
- **`tools/asset-catalog/`** (Phase 2) — re-invoked against `extracted/server-5-4/` per D-17. Existing aggregator/derived/cross-ref tier logic generalizes; only the input directory changes. Repo `pnpm catalog:server` script added; `pnpm catalog:all` extended.
- **`extracted/server-5-4/**`** — load-bearing input. 135 scripts + 58 objects + room/settings metadata.
- **`extracted/server-5-4/UNKNOWN-ACTIONS.md`** — Phase 3 forcing function parallel to Phase 2 D-08; resolve before SDOC-01 narrative is considered complete.
- **`docs/extracted-engine/`** (Phase 2 output) — `client-networking.md` is the symmetric client-side reference for `packet-protocol.md`; cross-link rather than duplicate.
- **`docs/adr/0001-client-engine.md`** — template for ADR format (Status / Context / Decision / Consequences). Phase 3 produces 0002 (persistence) and 0003 (canonical-snapshot).
- **`decomp/wiki/`** (17 docs) — narrative input for every server subsystem MD; cross-link the wiki, do not duplicate (Phase 1 D-19 thin-wrapper pattern).

### Established Patterns

- TS-everywhere (PROJECT.md). Tools live at `tools/<tool-name>/`. Standalone Node CLIs, not workspace packages until Phase 4.
- Headless, CI-runnable, no JVM/Windows on the daily-dev critical path (Phase 1 D-01).
- Reproducibility-first: deterministic JSON (sorted keys, 2-space, LF, no timestamps), source-order enumeration (Phase 1 D-15).
- Thin-wrapper authoring: docs link the wiki + autogen blocks rather than duplicating (Phase 1 D-19, Phase 2 D-11).
- Lint-as-forcing-function: every doc/schema artifact has a `lint-*.mjs` that exits non-zero on drift (Phase 2 lint-docs / lint-matrix / lint-adr; D-22 adds three more).
- Mvp tagging: `mvp: yes|no` front-matter per feature (Phase 2 D-04), grep-able for Phase 6 CLI-08 cherry-pick.
- Subsystem-MD organization: one MD per subsystem with autogen cross-ref tables (Phase 2 D-09).
- Anti-port reference: catalog the original surface + REJECTED-AS-PORTED marker + Phase-7 modernized replacement name (Phase 2 D-02 — generalized server-side via D-15/D-20).

### Integration Points

- **Inputs:** `extracted/server-5-4/` (Phase 1), `legacy/servers/enlyzeam-current/` (canonical seed per D-02), `legacy/open-source-release/{,ServerCommands.txt,BN Online Message ID's Table.xls}`, `decomp/wiki/`, `.planning/research/{STACK,FEATURES,PITFALLS}.md`, `docs/extracted-engine/client-networking.md`.
- **Outputs:**
  - `docs/extracted-server/*.md` (subsystem narrative per D-15)
  - `docs/extracted-server/protocol.{json,md}` + `tools/protocol-doc/output/protocol.ts` (per D-05)
  - `docs/extracted-server/save-formats.{json,md}` + `tools/save-format-doc/output/save-formats.ts` (per D-09)
  - `docs/extracted-server/parity-checklist.{json,md}` (per D-18)
  - `docs/extracted-server/asset-catalog/index.{json,md}` (per D-17)
  - `docs/extracted-server/admin-anti-port.md` (per D-20)
  - `docs/extracted-server/unknown-actions-status.md` (per D-15)
  - `docs/extracted-server/schema.sql` + Drizzle table definitions (per D-13)
  - `docs/adr/0002-persistence-layer.md` (per D-11)
  - `docs/adr/0003-canonical-snapshot.md` (per D-01..D-04)
  - `tools/protocol-doc/`, `tools/save-format-doc/` (per D-21)
  - Wiki errata commit on `decomp/wiki/16-bno-bnb-notes.md` (per D-08)
- **Downstream consumers:**
  - Phase 4 server rebuild — reads `protocol.json` for `packages/protocol` codec; reads `schema.sql` + Drizzle defs for SRV-01..03; reads `save-formats.json` + `legacy_credentials_staging` schema for SRV-10/11; reads `parity-checklist.json` `mvp:true` rows for MVP scope; reads `admin-anti-port.md` for SRV-12 stubbing.
  - Phase 5 deploy — reads `RESTORE.md` updates that include legacy_credentials_staging read-once-then-purge step (D-04).
  - Phase 6 client — reads `protocol.json` `mvp:true` opcodes for CLI-02/03/04/05; reads modernized intent shapes for chat input.
  - Phase 7 full parity — reads `parity-checklist.json` `in-phase-7` and `deferred-stage-8` rows as the closed scope (PAR-08); reads `admin-anti-port.md` modernized command shapes for PAR-07.

</code_context>

<specifics>
## Specific Ideas

- **Wiki errata first.** Phase 3 first task: update `decomp/wiki/16-bno-bnb-notes.md` (and PITFALLS A5 wording) to `file_text_*` based on extracted server scripts. Commit before generating SDOC-03 schemas so downstream agents read the corrected wiki. Verified ground: `0365-mb_backup.gml` opens `MB_Log.bnb` via `file_text_open_write` and writes section markers `@TOPIC`/`@REPLY`; `0367-users_restore.gml` reads `User_DBUpdated.bnu` via `file_text_open_read` + `file_text_read_string`. The 39dll `file_bin_*` paradigm from wiki/16 does not match what shipped.
- **Section-marker grammar.** `.bnb` save format uses ASCII section markers (`@TOPIC`, `@REPLY`) not byte counts. SDOC-03 grammar must model `sectioned` field type, not flat field list.
- **Single canonical JSON, multiple rendered surfaces.** Per D-05 / D-09 — one source of truth (`protocol.json`, `save-formats.json`) generates TS types + prose MD. Identical determinism discipline to Phase 2 MATRIX-rows.json → MATRIX.md autogen.
- **Phase 4 boundary clarity.** `tools/protocol-doc/output/protocol.ts` is **emitted artifact**, not a workspace package. Phase 4 plan creates `packages/protocol/` and copies/imports from this output. No premature workspace conversion.
- **Audit log seeded empty.** Phase 3 schema includes `audit_log` table even though no Phase 3..6 code writes to it — Phase 7 PAR-07 admin actions write here. Avoids a Phase 7 schema migration.
- **Modernized admin command surface is concrete.** Per D-20, every rejected admin command has a TS intent shape spec'd, not a TODO. Phase 7 PAR-07 ships UI on a defined contract.
- **Plaintext password rows never copied.** D-04 staging table is a Phase 4 read-once-then-purge artifact. The plaintext rows from `localList.txt` stay in `legacy/servers/enlyzeam-current/` (which itself remains private per CLAUDE.md hard rule #8). Production database never holds plaintext.

</specifics>

<deferred>
## Deferred Ideas

- **Per-record merge across the three snapshots.** Rejected per D-02 — enlyzeam-current is canonical whole. If post-launch a returning player reports their account exists in enlyzeam-archive but not enlyzeam-current, handle as a one-off recovery against the staging table; no general merge tool built.
- **Older Master `.gmd`/`.gb1` extraction** (Master 4.gmd, 5-1.exe, 5-2 DEBUG.exe, 5-3.exe in `legacy/servers/enlyzeam-archive/`). Rejected per D-03 / CLAUDE.md SoT rule. Re-evaluate only if a specific protocol/script trace fails during execution.
- **Local-current snapshot** (`legacy/servers/local-current/BNO_Server/`). Dropped as canonical seed per D-02 (211 .bnu vs 973). Documented in ADR 0003 with the reason; no code consumes it.
- **Apps/server scaffold** — Phase 4 owns. No `apps/server/` directory in Phase 3.
- **Packages/protocol scaffold** — Phase 4 owns. `tools/protocol-doc/output/protocol.ts` is the artifact Phase 4 imports from; the workspace package is created in Phase 4.
- **Packages/game-logic scaffold** — Phase 4 owns. Movement/collision/room-model rules will be derived from extracted-server scripts during Phase 4 planning, not pre-extracted as a separate Phase 3 artifact.
- **Modernized admin web UI implementation** — Phase 7 PAR-07. Phase 3 specifies command surface only (D-20).
- **Vintage soundfont selection / audio pipeline** — Phase 7 AST-02. Out of Phase 3 scope.
- **Drizzle migration runner wiring** — Phase 4 owns. Phase 3 produces baseline DDL + `0001_baseline.sql` Drizzle migration but does not run it.
- **Wireshark capture of original server traffic.** Per PITFALLS A4 — captures are post-hoc validation only, never primary source. Phase 3 derives the protocol from GML; capture validation is optional and may be deferred to Phase 6 dogfood.
- **Older-snapshot legacy-credential import.** enlyzeam-archive has its own 597-line localList.txt; rejected per D-02 (canonical = enlyzeam-current). Account ownership disputes from archive-only users handled as one-off post-launch recovery.

### Reviewed Todos (not folded)

None — todo cross-reference returned no matches for Phase 3 scope.

</deferred>

---

*Phase: 03-server-documentation-schemas*
*Context gathered: 2026-05-03*
