# Roadmap: REBNO

## Milestones

- ✅ **v1.0 MVP** — Phases 1–6 (+ 06.1/06.2/06.5/06.6/06.7 sub-phases) — shipped 2026-05-17 at tag `v1.0` — [archive](milestones/v1.0-ROADMAP.md)
- 🚧 **v1.1 Map Groundwork** — Phases 7–12 — opened 2026-05-18

## Phases

<details>
<summary>✅ v1.0 MVP (Phases 1–6 + sub-phases) — SHIPPED 2026-05-17</summary>

- [x] Phase 1: Extraction (7/7 plans) — completed 2026-05-02
- [x] Phase 2: Client Engine Documentation (7/7 plans) — completed 2026-05-02
- [x] Phase 3: Server Documentation & Schemas (12/12 plans) — completed 2026-05-05
- [x] Phase 4: Server Rebuild (MVP) (14/14 plans) — completed 2026-05-07
- [x] Phase 5: Deploy (14/14 plans) — completed 2026-05-08
- [x] Phase 6: Client Rebuild — MVP Gate (8/9 plans + 06-09 SUPERSEDED by 06.7) — CLI-08 cleared via 06.7 at `1f6073e`
- [x] Phase 06.1: gap-closure D-39..D-46 (6/7 plans + 06.1-07 SUPERSEDED by 06.7)
- [x] Phase 06.2: gap-closure D-50..D-53 + reopens (11/11 plans) — completed 2026-05-13
- [⚠] Phase 06.3: cycle-4 gap-closure — verification SUPERSEDED by 06.7
- [⚠] Phase 06.4: cycle-5 gap-closure — entire phase SUPERSEDED by 06.7 (0/16 plans executed)
- [x] Phase 06.5: Static client asset split (5/5 plans) — completed 2026-05-16
- [x] Phase 06.6: UAT accounts + integer scaling + chat clamp + RMB menu (5/5 plans) — completed 2026-05-17
- [x] Phase 06.7: Client-trust network model + fall trigger (9/9 plans) — completed 2026-05-17, HUMAN-UAT round 2 PASS at `1f6073e`

</details>

### 🚧 v1.1 Map Groundwork (Phases 7–12) — opened 2026-05-18

- [⚠] **Phase 7: Workflow Smoke + Convention Locks** — Front-load HARD-gate decisions (gridSize ADR, BNCentral chunking, tilesetSourceHash, LDtk version pin, `pnpm preflight` substituting CI, D-63 audit script, trace:check placeholder sweep) — 8/8 executed; gap-closure 07-09..07-12 in progress
- [ ] **Phase 8: Map-Loader Package + Phaser LdtkLoader + TestBed_001** — `packages/map-loader` scaffold, Phaser `LdtkLoader` routing through `legacy-origin.ts`, hand-authored `TestBed_001.ldtk`, protocol `ldtkLayoutSchema` extension, `RoomRenderer` third-shape branch, v1.0 synthetic MVP room removed
- [ ] **Phase 9: Room-Converter ldtk-import + Runtime Metadata Layer** — `tools/room-converter ldtk-import` subcommand, all 6 content-category conversions (static/animated/metadata/interactive/entities), verbatim `__legacyCreationCode` preservation, deterministic round-trip, runtime metadata processors sharing typed schemas
- [ ] **Phase 10: Map-Deploy Pipeline** — Generalize `scripts/client-release.sh --kind {client,maps}`, `MAPS_DIR` env resolver, `/data/maps/current` atomic symlink swap, `/health/maps`, Vite HMR plugin on `.ldtk` files, `MAPS-DEPLOY.md` runbook
- [ ] **Phase 11: MinimapHUD** — RenderTexture-based overlay container in GameScene (NOT second camera per ADR 0008), event-driven player markers, M-key toggle + localStorage, optional multi-zone overworld view (P2 — gate on margin)
- [ ] **Phase 12: First Real Imported Zone + Milestone-Exit HUMAN-UAT** — Import `Online_Lobby` (NEVER BNCentral first), two-player HUMAN-UAT mirroring CLI-08 round on deployed staging, milestone-exit retrospective

## Phase Details

### Phase 7: Workflow Smoke + Convention Locks
**Goal**: Lock every HARD-gate convention and substitute-for-CI tooling BEFORE any `.ldtk` file is authored, so downstream phases can't ship undetectable drift.
**Depends on**: v1.0 MVP shipped (Phase 06.7 closed at `1f6073e`)
**Requirements**: MAP-02, MAP-15, HYG-01, HYG-02
**Success Criteria** (what must be TRUE):
  1. Four ADRs committed under `docs/adr/`: gridSize convention (recommended 4 for IntGrid/Tile, 1 for Entity layers), BNCentral GridVania chunk grid (recommended 4×4 or 2×2 of ≤2000×1600 px), `tilesetSourceHash` custom-field convention, LDtk version pin at `tools/ldtk-version.txt`.
  2. `pnpm preflight` script runs LDtk-version check, atlas-hash assertion, conversion-regression check, and `pnpm trace:check` — exits non-zero on any failure and is documented in `docs/deploy/LOCAL-DEPLOY.md` as MANDATORY before any `flyctl deploy`.
  3. `tooling/no-inline-origin.ts` D-63 audit script catches any inline origin math outside `apps/client/src/render/legacy-origin.ts` and is wired into `pnpm preflight`.
  4. `pnpm trace:check` reports zero `REQ-SRV-XX` / `REQ-X` placeholder findings across Phase 04 + 06.4 docs; manifest is clean before any new REQ-MAP-* / REQ-HYG-* tag lands.
  5. Synthetic 8000×6400 zone smoke-test confirms the chosen BNCentral GridVania chunking workflow is tolerable on the operator machine BEFORE any real zone is converted.
**Plans:** 12/12 plans complete
  - [x] 07-01-PLAN.md — ADR 0009 gridSize=4 unified
  - [x] 07-02-PLAN.md — ADR 0010 tilesetSourceHash sidecar JSON (Option B)
  - [x] 07-03-PLAN.md — ADR 0011 BNCentral 3×3 GridVania chunking
  - [x] 07-04-PLAN.md — ADR 0012 LDtk version pin + tools/ldtk-version.txt
  - [x] 07-05-PLAN.md — traceable-reqs.toml HYG-02 required_stages tighten
  - [x] 07-06-PLAN.md — lint scripts (no-req-placeholders, atlas-hash stub, conversion-regression stub) + HYG-01 tag
  - [x] 07-07-PLAN.md — package.json preflight wiring + LOCAL-DEPLOY.md MANDATORY section
  - [x] 07-08-PLAN.md — operator smoke test (3×3 GridVania UAT, autonomous=false)
  - [x] 07-09-PLAN.md — GAP: traceable-reqs.toml deactivation (D-1, 72 missing_stage findings)
  - [x] 07-10-PLAN.md — GAP: live Phase-7 doc tag conversion + docs/TRACEABILITY.md (D-2 live + D-4)
  - [x] 07-11-PLAN.md — GAP: archived milestone doc tag conversion (D-2 archived, ~137 findings)
  - [x] 07-12-PLAN.md — GAP: LOCAL-DEPLOY.md reconcile (D-3) + preflight exit-0 verification
**Research-flagged**: yes — addressed via 07-RESEARCH.md / 07-CONTEXT.md / 07-PATTERNS.md / 07-VALIDATION.md

### Phase 8: Map-Loader Package + Phaser LdtkLoader + TestBed_001
**Goal**: Prove the LDtk → Phaser end-to-end load path against a hand-authored test bed, and retire the v1.0 synthetic MVP room as the single source of room truth.
**Depends on**: Phase 7 (gridSize convention + tilesetSourceHash + LDtk version pin must be locked)
**Requirements**: MAP-01
**Success Criteria** (what must be TRUE):
  1. `packages/map-loader` (pure-data, no Phaser/fs/DOM, mirrors `@rebno/game-logic` discipline) parses `TestBed_001.ldtk` into a typed `ParsedLdtkWorld` and exports `deriveWalkable()` ready for Phase 06.8 wire-up.
  2. `apps/client/src/render/LdtkLoader.ts` routes every entity placement through `apps/client/src/render/legacy-origin.ts`; HARD-gate extended via `tooling/no-inline-origin.ts` to forbid `__pivot` reads outside the loader's discard zone; loader unit test asserts pivot discard with a synthetic `__pivot=[0.5,0.5]` LDtk JSON.
  3. Hand-authored `TestBed_001.ldtk` (20×20 BORDERED, 880×800 px per 44×40 extracted constant) renders correctly in the deployed staging client and a player can walk on it.
  4. `packages/protocol/src/intents.ts` `layoutSchema` union extended with `ldtkLayoutSchema`; `RoomRenderer` gains a third-shape branch (or unifies with `renderNew` if shapes converge); v1.0 synthetic MVP room code path is removed (single source of room truth = LDtk).
  5. Loader hard-asserts on LDtk multi-worlds (`worlds[]` presence) so a silent schema break can never load zero levels; QuickType-generated `LdtkJson.ts` is vendored at `packages/protocol/src/ldtk/` with a `pnpm regen:ldtk-types` script.
  6. HUMAN-UAT round on staging: a single operator browser loads `TestBed_001` end-to-end, walks the room, and sees no console errors.
**Plans**: TBD
**UI hint**: yes
**Research-flagged**: yes — run `/gsd:plan-phase 8 --research-phase` at plan time

### Phase 9: Room-Converter ldtk-import + Runtime Metadata Layer
**Goal**: Convert every legacy content category (static/animated/metadata/interactive/entities) into LDtk losslessly with verbatim `__legacyCreationCode` preservation, and ship the runtime-side typed-metadata processors that share schemas with the converter as a single source of truth.
**Depends on**: Phase 8 (loader + protocol schema must be in place so converter output round-trips through real Phaser render)
**Requirements**: MAP-03, MAP-04, MAP-05, MAP-06, MAP-07, MAP-08, MAP-09, MAP-16
**Success Criteria** (what must be TRUE):
  1. `tools/room-converter` gains an `ldtk-import` subcommand that emits Ed25519-signed `rooms/<id>/<rev>.{json,sig}` from `.ldtk` sources; existing `convert` + `verify` subcommands continue to work unchanged; standalone-CLI D-18 rule preserved by vendoring `LdtkJson.ts` into `tools/room-converter/src/ldtk/`.
  2. All five legacy content categories convert end-to-end against `extracted/client-5-8/rooms/*`: static floor tiles preserve 44×40 pitch + atlas tileset references; animated tiles preserve frame-timing metadata as typed LDtk custom fields; DYNAMIC neighbor-rule tiles use LDtk auto-tile rules; MOVER tiles preserve `dxspeed/dyspeed` from `creationCode`; HEXPORTER/HEXSLING/HEXBRIDGE/WARP/SPECTRAL_GATE all carry typed fields per type with WARP destinations as `F_EntityRef`.
  3. Per-instance metadata extracted from `creationCode` into typed LDtk custom fields for the initial v1.1 entity coverage (NPC dialogue lines, platform starting direction + travel distance, teleporter name, messageboard ID); every unmapped entity type preserves `creationCode` verbatim in `__legacyCreationCode: F_Text` and is flagged in the per-zone `conversion-report.json` sidecar.
  4. Lint rule forbids `eval()` of any `__legacyCreationCode` field (CLAUDE.md Hard Rule 3 — anti-RCE-as-a-feature posture); lint rule wired into `pnpm preflight`.
  5. Deterministic round-trip: re-running `ldtk-import` produces a byte-stable `.ldtk` (instances sorted by `instanceId`, LDtk UIDs minted from `hash(roomId, instanceId)`); `conversion-report.json` sidecar lists non-cell-aligned source instances with rounding deltas, unmapped entity types, per-objectId mapping decisions, tileset hash, and LDtk version stamp.
  6. Phaser-side runtime metadata processors per entity-type (NPC dialogue renderer, platform direction/distance handler, teleporter name lookup, messageboard ID lookup) consume typed schemas defined in `packages/map-loader` and shared with the converter — converter and runtime cannot disagree by construction.
**Plans**: TBD
**Research-flagged**: yes — run `/gsd:plan-phase 9 --research-phase` at plan time

### Phase 10: Map-Deploy Pipeline
**Goal**: Deliver the "edit → push → see live" workflow on top of the proven Phase 06.5 symlink-swap primitive, generalizing rather than forking the hardened client-release script.
**Depends on**: Phase 9 (converter must produce signed `.json` output before there's anything worth deploying)
**Requirements**: MAP-10, MAP-11
**Success Criteria** (what must be TRUE):
  1. `scripts/client-release.sh` is generalized with a `--kind {client,maps}` flag (NOT forked into a parallel script); the SHA-validation, atomic `mv -T` swap, and last-N=5 retention behaviors are reused verbatim.
  2. `apps/server/src/maps-dir.ts` mirrors `static-assets.ts` env-resolver semantics; `MAPS_DIR=/data/maps/current` is wired in `fly.staging.toml`; `RoomRegistry` boots on both `ROOMS_DIR` (bundled MVP rooms) and `MAPS_DIR` (LDtk-derived rooms) without crashing on dir absence.
  3. `/health/maps` endpoint returns the current `.ldtk`-derived JSON hash per zone so an operator can manually verify a deploy landed (substituting for missing CI smoke).
  4. Vite HMR plugin watches `.ldtk` files under `maps/` and triggers a Phaser scene reload in dev — uses `chokidar@4` (pinned to match Vite 8's transitive pin; chokidar 5 mismatch would create dual watcher instances).
  5. `docs/deploy/MAPS-DEPLOY.md` runbook documents the publish path (operator-local `flyctl` only — no HTTP webhook, no auto-deploy), the rollback procedure (`flyctl ssh` + symlink re-point), and the mid-session map-swap player-kick-to-lobby behavior.
**Plans**: TBD

### Phase 11: MinimapHUD
**Goal**: Ship an in-game minimap overlay that respects ADR 0008's Scale.NONE invariant and stays under <0.5 ms steady-state cost regardless of CCU or entity count.
**Depends on**: Phase 10 (minimap needs at least one deployed LDtk Level to exercise bake-on-swap)
**Requirements**: MAP-12, MAP-13
**Success Criteria** (what must be TRUE):
  1. `MinimapHUD` is an overlay container inside `GameScene` (NOT a separate Phaser Scene, NOT a second `cameras.add(...)` — would conflict with ADR 0008 Scale.NONE + manual setZoom invariant); main camera `ignore()`s the minimap container.
  2. RenderTexture-based geometry layer baked ONCE per `s2c.room_layout` change (zero per-frame cost for geometry); dev-mode perf assert verifies minimap update path consumes <0.5 ms steady-state.
  3. Local player marker + remote player markers update via Colyseus state add/remove/change event subscriptions (NEVER per-frame `room.state.entities.forEach()` — would re-trip per-tick-log-freeze trap).
  4. M-key toggle persists visibility state in `localStorage` and survives page reload; player marker uses the same Phaser-side coords as the main player sprite (no D-63 origin drift).
  5. (P2 — promote to P1 if Phase 10 lands with margin) Multi-zone overworld minimap reads LDtk multi-worlds `worlds[].levels[].worldX/worldY` adjacency — the headline LDtk differentiator over Tiled.
**Plans**: TBD
**UI hint**: yes

### Phase 12: First Real Imported Zone + Milestone-Exit HUMAN-UAT
**Goal**: Prove the entire v1.1 pipeline against real legacy content with a milestone-exit HUMAN-UAT mirroring CLI-08's two-player gate.
**Depends on**: Phase 11 (overworld minimap most valuable on real imported zone; full pipeline must be deployable before UAT)
**Requirements**: MAP-14
**Success Criteria** (what must be TRUE):
  1. `Online_Lobby` (smallest world room; `Prairie_Flats` acceptable backup) is converted via `ldtk-import`, deployed via the maps pipeline, and rendered by the LDtk loader on staging — NEVER BNCentral first (would trip chunking + gridSize + auto-tile + loader simultaneously, making failure non-diagnostic).
  2. Two real accounts (`dunsen_uat` + `rebbie_uat`) on two browsers walk the LDtk-loaded `Online_Lobby` and exchange chat messages without console errors, mirroring the CLI-08 round at `1f6073e`.
  3. `traceable-reqs.toml` contains all 18 v1.1 REQ-IDs (MAP-01..16 + HYG-01..02); `pnpm trace:check` passes with zero findings; `pnpm preflight` returns success.
  4. Milestone-exit retrospective documents accepted v1.2 carry-overs (BNCentral chunking import, full ~16-zone catalogue, HEXPORTER/WARP creationCode parsing expansion, fog-of-war design, CI restoration decision).
**Plans**: TBD

## Backlog

### Full Parity backlog (relegated from the original v1.0 "Phase 7 Full Parity" on 2026-05-17)

> Note: the **new** Phase 7 (above, opened 2026-05-18) is "Workflow Smoke + Convention Locks" under v1.1. The old Phase 7 below was the v1.0-era "Full Parity" envelope — recognized as multi-milestone scope and relegated to backlog. Phase number 7 was thus freed and reused by v1.1.

Original "Phase 7 Full Parity" scope preserved in [`todos/pending/2026-05-17-phase-7-full-parity-backlog.md`](todos/pending/2026-05-17-phase-7-full-parity-backlog.md). 11 v1 requirements deferred: AST-02..04 + PAR-01..08. Re-decompose into focused phases/milestones once map groundwork (v1.1) lands and admin-UI / chat-parity milestones can be sequenced.

### Carried out of v1.1 scope (revisit at next milestone)

- **Phase 06.8** — anti-cheat illegal-position fall trigger; `packages/map-loader` exports `deriveWalkable()` ready for 5-line server wire-up
- **SRV-08 kill -9 mid-tick verification** on Linux staging
- **Phase 5 HUMAN-UAT Test 5** — legacy `localList.txt` one-shot import
- **prod environment provisioning** (`rebno-prod` never created)
- **CI restoration** — operator-local `pnpm preflight` substitutes in v1.1; revisit when GH Actions storage available
- **Full typed metadata coverage for ALL legacy entity types** — v1.1 covers common known shapes per MAP-08; long-tail extension is v1.2+
- **Multi-zone overworld minimap promote to P1** — gated on Phase 11 margin

## Progress

| Milestone | Phases | Status | Date |
|---|---|---|---|
| v1.0 MVP | 13 (11 complete, 2 superseded) | ✅ Shipped | 2026-05-17 |
| v1.1 Map Groundwork | 6 (0 complete) | 🚧 Planning | 2026-05-18 |

| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 7 Workflow Smoke + Convention Locks | 12/12 | Complete   | 2026-06-08 |
| 8 Map-Loader + Phaser LdtkLoader + TestBed_001 | 0/? | Not started | - |
| 9 Room-Converter ldtk-import + Runtime Metadata | 0/? | Not started | - |
| 10 Map-Deploy Pipeline | 0/? | Not started | - |
| 11 MinimapHUD | 0/? | Not started | - |
| 12 First Real Imported Zone + Milestone-Exit HUMAN-UAT | 0/? | Not started | - |

## Hard Milestones

- **CLI-08** — MVP gate. Two players, one room, movement + chat, end-to-end over deployed Fly.io server. **CLEARED** at `1f6073e` (HUMAN-UAT round 2 PASS 2026-05-17).
- **MAP-14** — v1.1 gate. Two players walk + chat on an LDtk-loaded real imported zone (`Online_Lobby`) end-to-end over deployed Fly.io staging. Mirrors CLI-08 shape. **Pending** — Phase 12.

---
*Roadmap created: 2026-05-01*
*Last updated: 2026-06-07 — Phase 7 gap-closure plans 07-09..07-12 added (traceable-reqs debt: D-1 manifest deactivation, D-2 illustrative-tag conversion, D-3 LOCAL-DEPLOY reconcile, D-4 TRACEABILITY.md)*
