{
 "note": "Single game roadmap. Every task: ≤ 1 agent session (S ≈ 30–60 min, M ≈ 1–2 h, L ≈ 2–4 h), starts and finishes with tools/task-log.mjs, has at least one automated test and one piece of evidence (screenshot/video/metrics JSON). Milestones M3+ list task groups; split them into S/M tasks with docs/process/TASK_TEMPLATE.md when the milestone starts. Engine: Unity 6 LTS client (ADR-024, accepted 2026-10-04). Since 2026-10-06 (ADR-029) each M0–M2 task lists what it `needs` (blender, rust, dotnet, unity, devices, owner); a task with `earlyStart` may begin once that task is done, even while its milestone's dependency is still open. The milestone gate itself is unchanged. Proposed GM arena tasks (owner decision 2026-10-08): GM-00 (M0), GM-01 (M2, after M2-01), GM-02 (M4), GM-04 (M6) and GM-03 (end of M9); M4, M6 and M9 list them as tasks next to their groups.",
 "tokenEstimate": {
  "method": "Measured on this wiki build (logs/tasks.json): total tokens per task including cache reads. Use as rough planning numbers; recalibrate after M0 with real game tasks.",
  "perTaskMillionTokens": {
   "S": [
    0.5,
    1.5
   ],
   "M": [
    1.5,
    4
   ],
   "L": [
    4,
    8
   ]
  }
 },
 "milestones": [
  {
   "id": "M0",
   "name": "Kickoff & toolchain",
   "phase": "pre-production",
   "dependsOn": [],
   "goal": "Every tool works on this machine, the monorepo builds, logging works, and the first ADRs are written.",
   "tasks": [
    {
     "id": "M0-01",
     "size": "S",
     "title": "git init, branch policy, CI script stubs (pnpm test, cargo test)",
     "test": "pnpm -r test passes",
     "evidence": "git log, logs/tasks.json",
     "needs": []
    },
    {
     "id": "M0-02",
     "size": "S",
     "title": "Blender headless smoke: build a cube, export GLB, render PNG",
     "test": "blender -b -P tools/blender/smoke.py exits 0; GLB validates",
     "evidence": "logs/test-output + render PNG",
     "needs": [
      "blender"
     ]
    },
    {
     "id": "M0-03",
     "size": "S",
     "title": "Install Rust; hello-world crate + golden-vector export (JSON) for the C# prediction subset",
     "test": "cargo test; vectors JSON schema test",
     "evidence": "build log + vectors.json",
     "needs": [
      "rust"
     ]
    },
    {
     "id": "M0-04",
     "size": "M",
     "title": "apps/client-unity: URP project (owner installed Unity Hub + pinned 6.x LTS), quality profiles Low/Mid/High/Ultra, FPS overlay; batchmode builds for macOS + Web + Android APK (ADR-032)",
     "test": "Unity -batchmode -runTests EditMode passes; Web build opened by Playwright reports the graphics API and 0 console errors",
     "evidence": "build logs + screenshot",
     "needs": [
      "unity"
     ],
     "guide": [
      "hardware-baseline"
     ]
    },
    {
     "id": "M0-04C",
     "size": "S",
     "title": "Client tier defaults, Ultra gate and frame targets from feel-quality.json (tierSelection, proposed): low-memory phones and low-end browsers start on Low, a native startup benchmark picks the default (the player’s saved choice wins), phone Ultra only through a test launch option until the M1 thermal/frame gate passes, per-tier frame-rate caps",
     "test": "Unity EditMode tests for every rule pass; Web, macOS and Android smokes show the data’s default tier and frame cap with 0 errors",
     "evidence": "logs/evidence/M0-04C/ (smoke JSON, a screenshot per platform, default tier and frame target table)",
     "needs": [
      "unity",
      "devices"
     ],
     "guide": [
      "hardware-baseline"
     ]
    },
    {
     "id": "M0-04D",
     "size": "S",
     "title": "Phone smokes for the M0-04C tier rules: Android APK smoke (default Mid by benchmark at 30 fps, High 60, Ultra only by the test launch option, overlay cycle Low/Mid/High) and the phone-Chrome Web smoke with the frame-cap and low-end checks",
     "test": "node tools/unity/android-smoke.mjs and web-smoke.mjs --cdp pass with 0 errors (platformNoise unchanged)",
     "evidence": "logs/evidence/M0-04C/ android-smoke.json, web-smoke-phone-chrome.json, android screenshots, tier-defaults.txt",
     "needs": [
      "unity",
      "devices"
     ],
     "guide": [
      "hardware-baseline"
     ]
    },
    {
     "id": "M0-05",
     "size": "M",
     "title": "Capability probe on the owner's Android phone, Chrome and the M3 (ADR-032); physical Low baseline (Galaxy A07 4G 4 GB, iPhone SE 2020, Intel N150 8 GB) stays pending until units exist",
     "test": "probe JSON schema test",
     "evidence": "logs/evidence/<task>/device-probes + reference-devices.json validation records",
     "needs": [
      "unity",
      "devices"
     ],
     "guide": [
      "hardware-baseline"
     ],
     "note": "ADR-032: owner results are labelled with their probe and do not validate the Low references."
    },
    {
     "id": "M0-06",
     "size": "S",
     "title": "ADR-003…005 + ADR-024 committed (server + protocol, monorepo, skill system, Unity client)",
     "test": "docs lint: every ADR has status/context/decision/consequences",
     "evidence": "docs/vision/DECISIONS.md (ADR-003, ADR-004, ADR-005, ADR-024); owner approval pending in logs/reviews.jsonl",
     "needs": []
    },
    {
     "id": "M0-07",
     "size": "S",
     "title": "Unity toolkit gate: Unity Test Framework + Performance Testing pinned and proven (no MCP: the unity-mcp smoke test was skipped by owner direction, 2026-10-08)",
     "test": "PlayMode performance tests pass in batchmode (sky step 0 GC allocations, Boot frame times recorded); unity-run.mjs prints median/p95",
     "evidence": "logs/evidence/M0-07 (perf results JSON + runner summary)",
     "needs": [
      "unity"
     ]
    },
    {
     "id": "M0-08",
     "size": "M",
     "title": "Tripo → Blender → Unity bake-off: 3 assets (monster, prop, weapon) from approved concept art, API credit cap set, Blender polish script, turntable renders for the owner",
     "test": "validator passes tris/bones/UV/texture budgets; credits used ≤ cap in logs/tripo-gen.jsonl",
     "evidence": "turntable clips + validator JSON",
     "needs": [
      "blender",
      "unity",
      "owner"
     ],
     "status": "replaced",
     "replacedBy": "MODEL-01",
     "note": "Replaced by MODEL-01 (ADR-033, 2026-10-08): no Tripo API and no credits; the owner makes models by hand in the Tripo website from the model request log, agents validate and polish them in Blender. Kept for the record."
    },
    {
     "id": "MODEL-01",
     "size": "M",
     "title": "Model request log (ADR-033): gen-model-requests.mjs → model-requests.json (monsters, elites, bosses, mounts, armour pieces, costumes, set pieces; budgets, Tripo settings, roadmap order, Dune Beetle first), reference views cut by tools/model-views.py, undersuit and armour-piece 2D requests",
     "test": "pnpm data:test (model-requests.test.mjs); python3 tools/model-views.py cuts the Dune Beetle views",
     "evidence": "logs/evidence/MODEL-01/ (Dune Beetle views contact sheet, monster views overview)",
     "needs": []
    },
    {
     "id": "MODEL-01B",
     "size": "M",
     "title": "Model inbox checker tools/model-inbox.mjs (pure-Node GLB parse: id, bounds, tris vs budget, TEXCOORD_0, embedded textures, no skin/animation → model-delivered.json; rules in model-requests.manual.json inbox) with synthetic GLB fixtures; view-cut fixes in tools/model-views.py (scale bar painted out, gap-first split: Ashroot Pangolin)",
     "test": "node --test packages/game-data/tests/model-inbox.test.mjs (valid, skinned, animated, no-UV, no-texture, over-budget, far over, wrong id, .v2 beats .v1, broken file, units); also in pnpm data:test",
     "evidence": "logs/evidence/MODEL-01B/ (inbox-check.txt, views-fixed.png, views-all-monsters.jpg)",
     "needs": []
    },
    {
     "id": "MODEL-01C",
     "size": "S",
     "title": "View-cut part ownership in tools/model-views.py (a connected part with ≥ 0.9 of its pixels in one view goes to that view whole; other views painted out: Ashroot Pangolin front sliver gone, clipped whiskers/tails back); art/3d/inbox/README.md, inbox GLBs git-ignored (sha256 provenance in model-delivered.json), model-inbox.mjs ignores the README",
     "test": "python3 tools/model-views.py cuts all 40 (contact sheet); pnpm data:test (model-inbox.test.mjs README fixture)",
     "evidence": "logs/evidence/MODEL-01C/ (views-fixed.png)",
     "needs": []
    },
    {
     "id": "MODEL-01D",
     "size": "S",
     "title": "Wiki page Model requests (follows Art/Audio generation): counts by status, owner steps rendered from TRIPO_PIPELINE Current mode, the agent step (model-inbox.mjs), kind/status filters and search, one card per request (id, kind, use, neededBy, budget, prompt + avoid with copy buttons, view previews or views missing → ART-… request via /art-requests/?q=, Tripo settings, saveAs, status and delivery note), nav entry in lib/routes.ts",
     "test": "wiki typecheck + build; e2e tests/model-requests.spec.ts: renders, card count = request count, kind and status filters, the views-missing link opens the art queue filtered to its ART id, no console errors",
     "evidence": "logs/evidence/MODEL-01D/ (page 1440×900 and 390×844 screenshots, first card desktop and mobile)",
     "needs": []
    },
    {
     "id": "MODEL-01E",
     "size": "S",
     "title": "Fix the Bellwhisker Jerboa side/front view split in tools/model-views.py (the cut falls on the thin tail: side.png is only the tuft); re-cut MDL-MON-bellwhisker_jerboa and its elite and refresh the wiki previews",
     "test": "python3 tools/model-views.py --only MDL-MON-bellwhisker_jerboa (side.png holds the whole body in profile); pnpm data:test",
     "evidence": "logs/evidence/MODEL-01E/ (jerboa views before/after)",
     "needs": []
    },
    {
     "id": "GM-00",
     "size": "S",
     "title": "GM arena roadmap (gm-room.json): GM-01 in M2 right after the Combat Lab, GM-02 in M4, GM-04 (the owner's local role CLI) in M6, GM-03 at the end of M9; the M2/M4/M6/M9 gates name their GM arena part and the wiki Roadmap shows a milestone's tasks next to its groups",
     "test": "pnpm data:test (gm-arena-roadmap.test.mjs: placement, proposed notes, GM rules, every gm-room.json area and command placed); node tools/ctx.mjs task GM-01 prints the card; the built wiki roadmap page lists GM-02 and GM-03",
     "evidence": "logs/evidence/GM-00/ (GM-01…GM-04 cards as .txt)",
     "needs": [],
     "proposed": true,
     "note": "Proposed: owner decision 2026-10-08 (GM arena run). GM-04 is its own M6 task because role access starts with the M6 accounts, three milestones before GM-03; GM-03 closes M9 instead of opening a new milestone (no new id, order or gate to keep in step)."
    },
    {
     "id": "M0-09",
     "size": "S",
     "title": "Engine-free C# gameplay core scaffold (ADR-029): a netstandard2.1 library with no UnityEngine references plus an xunit project, run by pnpm core:test; Unity will consume the same folder through an asmdef",
     "test": "dotnet test passes; a test fails if any file in the core references UnityEngine",
     "evidence": "test log",
     "needs": [
      "dotnet"
     ]
    },
    {
     "id": "M0-10",
     "size": "S",
     "title": "Unity runner (ADR-029): tools/unity-run.mjs wraps batchmode tests and builds, saves the full log and prints the result, the counts and the first five errors",
     "test": "runner unit tests on recorded logs (pass, compile error, failing test, licence error); one live EditMode run through it",
     "evidence": "runner output sample",
     "needs": [
      "unity"
     ]
    },
    {
     "id": "M0-11",
     "size": "M",
     "title": "One-simulation spike (ADR-029): build a small zoen-sim step function as a client plugin for macOS, Windows, Android and iOS and as a static library for the Web build with the pinned editor's Emscripten version; call it from Unity on Web, macOS and Android; Windows and iOS recorded not tested (ADR-032)",
     "test": "the golden vectors pass through the plugin on Web, macOS and Android; the Web build loads with 0 console errors; step cost and binary size recorded",
     "evidence": "spike report with pass/fail per target; ADR-029 updated with the result",
     "needs": [
      "rust",
      "unity"
     ]
    },
    {
     "id": "M0-12",
     "size": "S",
     "title": "Time-of-day and weather controller in the first scene (ADR-030): phases and weather states from time-weather.json, a server-clock interface, GM toggles /gm time, /gm cycle and /gm weather",
     "test": "EditMode: the state machine follows the data file; PlayMode: each toggle changes the state and logs it; the telegraph contrast check passes across the review matrix on Low",
     "evidence": "four-phase capture strip + state log",
     "needs": [
      "unity"
     ],
     "guide": [
      "time-weather"
     ]
    }
   ],
   "gate": [
    "Blender, Rust, Unity (batchmode build + EditMode/PlayMode tests), Playwright all verified by logged tests",
    "Token ledger shows M0 cost",
    "ADRs approved by owner",
    "Rust and .NET verified by logged smoke tests (tools/setup/toolchain-smoke.sh)",
    "One-simulation spike result recorded in ADR-029"
   ],
   "art": []
  },
  {
   "id": "M1",
   "name": "Feasibility spikes (render + network at scale)",
   "phase": "pre-production",
   "dependsOn": [
    "M0"
   ],
   "goal": "Prove the two hardest numbers before content: 150 animated characters on screen on mid mobile, and 1,000 bot players in one Rust zone at 20 Hz.",
   "tasks": [
    {
     "id": "M1-01",
     "size": "M",
     "title": "Crowd render test in Unity: 150 baked-animation crowd characters (GPU instancing) + 50 full skinned; GPU Resident Drawer on native",
     "test": "Performance Testing PlayMode run: p95 frame ≤ 16.7 ms on Windows/desktop, ≤ 33 ms on mid Android; Web build Mid p95 ≤ 33 ms in Chrome",
     "evidence": "perf.json per target",
     "needs": [
      "unity"
     ],
     "guide": [
      "hardware-baseline"
     ]
    },
    {
     "id": "M1-02",
     "size": "M",
     "title": "VFX stress: 300 pooled GPU particle emitters + 200 projectiles",
     "test": "p95 frame budget per tier; zero GC spikes > 4 ms",
     "evidence": "perf.json + video",
     "needs": [
      "unity"
     ],
     "guide": [
      "vfx-budgets",
      "hardware-baseline"
     ]
    },
    {
     "id": "M1-03",
     "size": "L",
     "title": "zoen-zone: fixed 20 Hz loop, spatial hash, AOI interest sets, delta snapshots",
     "test": "cargo test (AOI correctness, delta round-trip); criterion: tick ≤ 15 ms p99 with 1,000 bots + 4,000 mobs on 4 cores",
     "evidence": "bench report",
     "needs": [
      "rust"
     ],
     "earlyStart": "M0-03"
    },
    {
     "id": "M1-03B",
     "size": "M",
     "title": "zoen-zone part B: delta snapshots per client (baselines and acks, bit-packed position/yaw/anim/HP deltas, priority accumulators and the near/mid/far rates from NETCODE_1000.md), the first protocol schema entries in packages/protocol/zoen.schema.json (Rust generator), and a WebSocket endpoint the M1-04 bots connect to",
     "test": "cargo test (delta round-trip against full state, ack/baseline loss, snapshot within the 32 KB/s per-client cap); the M1-03 tick bench stays ≤ 15 ms p99 with snapshots",
     "evidence": "bench report with snapshot bytes per client",
     "needs": [
      "rust"
     ],
     "earlyStart": "M1-03"
    },
    {
     "id": "M1-03C",
     "size": "M",
     "title": "zoen-zone part C: WebSocket endpoint (zone binary, cargo run -p zoen-zone --bin zone -- --port <p>): minimal RFC 6455 on std::net (HTTP upgrade with SHA-1 + base64 accept key tested on the RFC vectors, binary frames, masking, ping/pong, close; no new crates until the owner approves one), network threads with bounded queues off the pure tick, hello/join, inputs into the tick, 20 Hz snapshots from the M1-03B replicas, acks, pong, graceful shutdown",
     "test": "cargo test: SHA-1/base64/accept-key RFC vectors, frame codec round-trip; integration test with 2-10 local Rust test clients that connect, move and receive consistent snapshots, then a graceful shutdown; re-run pnpm zone:snapbench on an idle machine for the M1-03B tick + snapshots p99 <= 15 ms criterion (unmeasurable at load average 180)",
     "evidence": "integration test log excerpt",
     "needs": [
      "rust"
     ],
     "earlyStart": "M1-03B"
    },
    {
     "id": "M1-03D",
     "size": "S",
     "title": "zoen-zone part D: socket load check for the zone binary (pnpm zone:loadcheck: about 200 Rust WebSocket clients on localhost for 10 s, random walk, ack every snapshot, ping each second) recording loop p50/p99/max, skipped frames and bytes per client per second, plus the M1-03B tick + snapshots p99 re-run on an idle machine",
     "test": "zone:loadcheck exits 0 with every client joined and cleanly closed, no stale/dropped inputs, frames skipped under 1 percent; pnpm zone:snapbench re-run",
     "evidence": "load-check JSON, server log excerpt",
     "needs": [
      "rust"
     ],
     "earlyStart": "M1-03C"
    },
    {
     "id": "M1-04",
     "size": "M",
     "title": "Bot client (Rust) simulating 1,000 players (walk, fight, chat) over WebSocket",
     "test": "1,000 bots connected 10 min, no disconnects, server tick p99 within budget, avg downstream ≤ 12 KB/s/client",
     "evidence": "metrics.json + Grafana-like PNG",
     "needs": [
      "rust"
     ],
     "earlyStart": "M1-03"
    },
    {
     "id": "M1-04B",
     "size": "M",
     "title": "M1-04 part B: split snapshot building and hand-off across 4 cores (M1-04 profile: build 11.7 ms + hand-off 2.6 ms of a 17.4 ms mean loop at 1,000 clients; tick-thread CPU 15.1 ms/tick, so code-bound), then the 10-minute 1,000-bot soak (stepped ramp 100/250/500/1,000) and the Grafana-like PNG chart",
     "test": "pnpm zone:bots: 1,000 bots 10 min hold, no disconnects, loop p99 within tick.targetP99Ms, avg downstream within loadTest.avgDownstreamKBps, memory flat within loadTest.memoryDriftPercent; no per-tick allocations (snapshot_no_alloc test still passes)",
     "evidence": "metrics.json + Grafana-like PNG, server log excerpt",
     "needs": [
      "rust"
     ],
     "earlyStart": "M1-04"
    },
    {
     "id": "M1-04C",
     "size": "M",
     "title": "M1-04 part C: the 10-minute 1,000-bot soak (stepped ramp 100/250/500/1,000 via --steps, memory-drift check, per-second RSS and RTT series) and the Grafana-like PNG chart (M1-04B: snapshot pool on 4 threads, 1,000 bots 80 s loop p99 12.1 ms)",
     "test": "pnpm zone:bots: 1,000 bots 10 min hold, no disconnects, loop p99 within tick.targetP99Ms, avg downstream within loadTest.avgDownstreamKBps, memory flat within loadTest.memoryDriftPercent; snapshot_no_alloc and snapshot_pool tests still pass",
     "evidence": "metrics.json + Grafana-like PNG, server log excerpt",
     "needs": [
      "rust"
     ],
     "earlyStart": "M1-04B"
    },
    {
     "id": "M1-04D",
     "size": "S",
     "title": "M1-04 part D: re-run the 10-minute 1,000-bot soak on a quiet Mac (M1-04C passed bots, bandwidth and memory but missed loop p99 at load average 200-380 on 8 cores: tick-thread CPU 4.5 ms + workers 7.9 ms per loop)",
     "test": "pnpm zone:bots -- --steps 100,250,500,1000 --step-s 30 --hold-s 600 at a load average near the core count: every check passes (loop p99 within tick.targetP99Ms, no dropped ticks, no disconnects, memory within loadTest.memoryDriftPercent)",
     "evidence": "metrics.json, soak-chart.png (tools/zone-soak-report.py), summary.txt with the load average, server log excerpt",
     "needs": [
      "rust"
     ],
     "earlyStart": "M1-04C"
    },
    {
     "id": "WIN-01",
     "size": "M",
     "title": "Windows test build (ADR-032): `unity-run build windows` (Mono x86_64 from this Mac), import rules for the Windows sim DLL, a zip + owner smoke kit (a .bat/.ps1 that runs the player per tier with -logFile and a node/PowerShell checker for the ZOEN lines; results pasted back as a file into logs/evidence), the Windows probe (M0-05 format) via the kit, and the PC build steps for the sim DLL",
     "test": "node tools/unity-run.mjs build windows passes (ZOEN_BUILD target and size in the build summary); EditMode tests pass with the Windows DLL import rules (x86_64 Windows player only); the kit checker passes on a saved sample log (pnpm data:test); the owner's returned results show every tier booting with 0 errors and a ZOEN_PROBE line that holds the probe contract",
     "evidence": "logs/evidence/WIN-01/ (build summary JSON, kit file list, checker output on the sample log, the owner's returned results file, the Windows probe JSON)",
     "needs": [
      "unity",
      "owner"
     ],
     "guide": [
      "test-platforms",
      "hardware-baseline"
     ],
     "proposed": true,
     "note": "Proposed: owner decision 2026-10-11 (ADR-032: test on Web, Android and Windows; macOS and iOS paused). The owner runs the kit on his Windows PC (agents can't reach it) and builds the sim DLL there with Rust and Visual Studio Build Tools; agents never download Microsoft's Windows SDK or CRT. Until the DLL exists the player runs without the native sim (the boot check reports it absent; not an error). The PC's probe fills reference-devices.json → ownerTestDevices owner_windows."
    },
    {
     "id": "M1-05",
     "size": "M",
     "title": "Unity client (native + Web) connects, predicts movement with the C# prediction subset, reconciles",
     "test": "PlayMode test with 200 ms injected latency: prediction error < 0.15 m p95; C# subset matches all Rust golden vectors",
     "evidence": "test log + clip",
     "needs": [
      "unity",
      "rust"
     ]
    },
    {
     "id": "M1-06",
     "size": "S",
     "title": "Security event log from day one: zone + gateway report rejected/suspicious actions (append-only, never public)",
     "test": "forged message from the bot client writes a proto_forged event; schema test of security.json",
     "evidence": "event sample JSON",
     "needs": [
      "rust"
     ],
     "earlyStart": "M1-03"
    }
   ],
   "gate": [
    "1,000-bot soak passes",
    "Mobile crowd budget met or a documented fallback (lower visible-player cap)",
    "ADR-007 network numbers recorded"
   ],
   "art": [],
   "public": {
    "slug": "foundations",
    "title": "Engine foundations",
    "summary": "The game client on Unity 6 and the authoritative servers, proven with crowds and real network conditions.",
    "status": "next"
   }
  },
  {
   "id": "M2",
   "name": "Combat Lab: one perfect skill",
   "phase": "pre-production",
   "dependsOn": [
    "M1"
   ],
   "goal": "Warblade · Horizon Cleave is AAA-complete end to end: input → server phases → animation → VFX → SFX → hit reaction on a dummy and a monster → damage numbers → camera.",
   "tasks": [
    {
     "id": "M2-09",
     "size": "M",
     "title": "Preparation gate: versioned Zoen mannequin, rest pose/axes/bind matrices/socket contract and Humanoid Avatar validation before skill clips",
     "test": "Rig validator and deformation poses pass; exports preserve contract; supported body/grip/armour review matrix recorded",
     "evidence": "rig contract hash + deformation sheet + review checklist",
     "needs": [
      "blender"
     ],
     "earlyStart": "M0-02",
     "guide": [
      "mannequin",
      "order-of-work"
     ]
    },
    {
     "id": "M2-01",
     "size": "M",
     "title": "Combat Lab scene: dummy, 10/25/50/100 dummy fields, spawnable monster, debug HUD; input/phase/cancel/hit/reconciliation timeline debugger; simulated 80 ms round trip by default with a 0 ms switch (NET-01, ADR-031)",
     "test": "scene loads; spawn commands work via console API",
     "evidence": "screenshot + replay timeline sample",
     "needs": [
      "unity"
     ],
     "guide": [
      "battle-suggestions",
      "area-brief"
     ]
    },
    {
     "id": "GM-01",
     "size": "L",
     "title": "GM arena core in the Combat Lab scene (gm-room.json): static_targets (/gm dummy with DPS meter and hit log), spawn_ring for the monsters that exist by then (/gm spawn), reset_station for what exists (/gm level, /gm reset cooldowns, /gm clear monsters), debug toggles (/gm god, /gm mana, /gm nocd, /gm hitboxes, /gm telegraphs, /gm ai freeze|resume), network lab (/gm lag) and Time/Weather buttons on the M0-12 controller (/gm time, /gm cycle, /gm weather)",
     "test": "one automated test per GM-01 command (server state after it, audit record written, refused without the dev GM flag); no toggle changes the global time scale or hides a telegraph; Time/Weather commands leave the server outcome identical (ADR-030)",
     "evidence": "arena panel screenshot + 10 s clip (spawn, dummy DPS meter, /gm lag with jitter and loss, dusk + sandstorm) + audit log excerpt (.txt)",
     "needs": [
      "unity",
      "rust"
     ],
     "guide": [
      "time-weather"
     ],
     "proposed": true,
     "note": "Proposed: owner decision 2026-10-08 (GM arena run). GM rules: every GM action is audited (who, what, where); GM-made items carry the gm flag and can never be traded, sold, listed or mailed; until M6 test builds use a dev-only local GM flag (never in release builds); from M6 access is by the gm or qa role, agents never grant roles, and the owner gives his own account the gm role with the local CLI (GM-04). GM tools never add a global time-scale and never hide telegraphs."
    },
    {
     "id": "M2-02",
     "size": "M",
     "title": "Server cast-phase state machine (windup/active/recovery, cancel windows, cast id, per-target hit ids); ADR-027 cancel in every own attack phase; cast-owned vs independent damage lifetime",
     "test": "golden timelines include cancel/contact ties, commit costs/refund, released entity persistence; no double hits/costs under duplication/loss/reordering",
     "evidence": "test log",
     "needs": [
      "rust"
     ],
     "earlyStart": "M0-03",
     "guide": [
      "cancel-contract",
      "movement-resources"
     ]
    },
    {
     "id": "M2-10",
     "size": "M",
     "title": "Movement-first input arbitration and cancellation self-tests C1–C18 across keyboard/mouse, gamepad and touch",
     "test": "Boundary-swept cancels in all phases; explicit actions beat held chain; WASD policies, costs, entity lifetimes, collision and network-fault replays pass; positive mana and exact reuse/travel boundaries, hard-control/root exceptions, combo memory expiry/refund and R3-only view toggle verified",
     "evidence": "control-results.json + input/cast/hit timeline + both-camera clips",
     "needs": [
      "unity",
      "dotnet"
     ],
     "guide": [
      "priority-ladder",
      "movement-resources",
      "cancel-contract",
      "wasd-policy",
      "control-tests",
      "camera-direction"
     ],
     "note": "ADR-032 (2026-10-11): keyboard/mouse on the Windows PC and in Chrome on the Mac, touch on the owner's phone; gamepad unverified until one is connected."
    },
    {
     "id": "M2-03",
     "size": "M",
     "title": "Horizon Cleave greybox on the validated mannequin; locomotion/steering contract and cancel/chain blends",
     "test": "contact ±1 frame; marked planted-foot slide <2 cm; grip drift <1 cm; continuous eight-view/both-camera review, cancel transitions and representative bodies pass",
     "evidence": "eight-view contact sheets + side strip + continuous motion and interruption clips + self-review checklist",
     "needs": [
      "blender",
      "unity"
     ],
     "guide": [
      "mannequin",
      "wasd-policy",
      "cancel-contract"
     ]
    },
    {
     "id": "M2-04",
     "size": "M",
     "title": "Horizon Cleave VFX: semantic roles, material impact, cancel cleanup, pooled Low/High profiles",
     "test": "tier/frame caps, overdraw/cost metrics and pool return pass; footprint and boss telegraph readable in both cameras, terrain and accessibility modes",
     "evidence": "Low/High side-by-side clip + budget JSON + readability/cancel checklist",
     "needs": [
      "unity"
     ],
     "guide": [
      "vfx-standard",
      "camera-direction",
      "time-weather"
     ]
    },
    {
     "id": "M2-05",
     "size": "S",
     "title": "SFX: 4-layer hit (swish, impact, material, sweetener) + voice limit",
     "test": "audio bus peak < −1 dBFS; voice cap respected",
     "evidence": "audio capture",
     "needs": [
      "unity"
     ]
    },
    {
     "id": "M2-06",
     "size": "M",
     "title": "Hit reactions: flinch_light/heavy, stagger, knockback on Dune Beetle placeholder (insectoid set)",
     "test": "reaction chosen = rule(impact, weight, poise) for 12 cases",
     "evidence": "video",
     "needs": [
      "unity"
     ]
    },
    {
     "id": "M2-07",
     "size": "S",
     "title": "Game-feel layer: local hitstop, trauma camera shake, hit flash, damage numbers, magnetism; strict and predicted-contact impact modes behind a switch, contact-to-confirm delay measured (ADR-031, NET-02)",
     "test": "toggles respected; server outcome identical with feel on/off and in both impact modes",
     "evidence": "A/B video",
     "needs": [
      "unity"
     ],
     "guide": [
      "settings"
     ]
    },
    {
     "id": "M2-08",
     "size": "S",
     "title": "Human review: owner watches the 10 s clips at 80 ms in both impact modes, picks one (ADR-031) and signs off (or lists fixes) in Chrome (Low–Mid) and on the Windows PC (High) (ADR-032, 2026-10-11)",
     "test": "review entry in logs/reviews.jsonl",
     "evidence": "clip + notes",
     "needs": [
      "owner"
     ],
     "guide": [
      "order-of-work",
      "multi-angle-review"
     ]
    },
    {
     "id": "M2-11",
     "size": "L",
     "title": "Skill factory (ADR-029): data-driven runtimes for the shape families, one gate runner that iterates skills.json and one command that produces the evidence (contact sheet, Low/High clips, budget JSON)",
     "test": "Horizon Cleave passes the gate through the factory with no skill-specific code; a second skill from another family is added as data only",
     "evidence": "gate report for both skills + evidence pack",
     "needs": [
      "unity",
      "dotnet"
     ],
     "guide": [
      "order-of-work",
      "multi-angle-review",
      "vfx-budgets"
     ]
    }
   ],
   "gate": [
    "G0/G1: mannequin contract and C1–C18 movement/control tests pass before skill expansion (docs/process/GAME_DEV_GUIDE.md)",
    "Skill passes the one-skill production gate (docs/production/SKILL_GATE.md)",
    "Owner sign-off on feel",
    "G2: multi-angle agent animation/VFX self-review complete; no failed or missing required view/transition accepted",
    "Skill gate row 14: every core detail of the AAA detail catalog that applies is shown by its proof (ADR-028)",
    "Skill factory proven with a second skill before M4 starts (ADR-029)",
    "GM arena core (GM-01): every GM-01 /gm command passes its automated test and is audited; no GM toggle adds a global time-scale or hides a telegraph"
   ],
   "art": [
    "ART-VFX-040"
   ],
   "public": {
    "slug": "combat",
    "title": "Combat that hits hard",
    "summary": "One skill made perfect end to end — readable wind-ups, heavy impacts and fair server-side hits — before scaling up.",
    "status": "planned"
   }
  },
  {
   "id": "M3",
   "name": "Character pipeline",
   "phase": "production",
   "dependsOn": [
    "M2"
   ],
   "goal": "Canonical human male + female from turnaround → mesh → rig → modular armour → locomotion + greatsword set, retargetable to all lineages.",
   "groups": [
    "Tripo pipeline automation: tools/tripo/generate.py (SDK, credit guard, logs) + tools/blender/polish_tripo.py (headless clean/retopo/UV/rig/LOD) — docs/production/TRIPO_PIPELINE.md",
    "Turnarounds (ART-CHR-050/051) → image-to-3D or sculpt → retopo (≤ 22k tris) → UV/texture 2K",
    "Rigify humanoid → Mixamo-compatible bone map → weight paint (≤ 4 influences) → corrective shapes",
    "Modular slots: helmet/body/gloves/boots + costume override; hide covered submeshes",
    "Locomotion set (idle/walk/run/sprint/turns/jump/land/hit/death) + greatsword grip layer",
    "LOD0–3 + VAT crowd version; glTF export validator",
    "Lineage proportion variants (orc/elf/vanara/auralin) with cosmetic corrections only"
   ],
   "gate": [
    "Turntable videos per body",
    "No foot sliding > 2 cm, no hand-weapon drift > 1 cm in clip tests",
    "Triangle/texture budgets met",
    "Versioned mannequin contract preserved; retarget/body-height/grip/armour extremes and continuous multi-angle review pass"
   ],
   "art": [
    "ART-CHR-050",
    "ART-CHR-051",
    "ART-CHR-052"
   ]
  },
  {
   "id": "M4",
   "name": "Vertical slice (3 builds)",
   "phase": "production",
   "dependsOn": [
    "M3"
   ],
   "goal": "Warblade, Windarcher and Spellslinger fully playable with 6 skills each, 6 movement gems, Atlas + Codex, Dewgrass/Orchard monsters with reaction sets, HUD v1 desktop + mobile.",
   "groups": [
    "18 skills through the skill gate, built with the skill factory (M2-11)",
    "Atlas UI + Codex UI + Smart Chain + keybinds remap",
    "6 mana-consuming movement gems with traversal validation; proposed Cairn Vault 1 s / Reed Rush 0.3 s reuse targets profiled; other gems individually tuned",
    "4 monsters (hare, fox, boar, beetle) modelled, rigged, reaction sets",
    "HUD v1 using ART-HUD-001…013",
    "Desktop + mobile input; gamepad basic",
    "Close/tactical camera toggle (V/R3/touch), optional close-view boss lock-on and off-screen threat cues",
    "Server-owned enemy engagement tokens by role/difficulty, active crowd positioning and boss punish windows",
    "Training/accessibility/telemetry foundation and optional cosmetic encounter KO/combo counter; perfect-dodge rewards, hit-confirm cancels, hazards and destruction deferred"
   ],
   "tasks": [
    {
     "id": "GM-02",
     "size": "L",
     "title": "GM arena for the vertical slice: preset_builds for the vertical-slice builds in builds.json (warblade, windarcher, pyromancer; /gm preset per presetRule: level 40, gear, gems, planBuild Atlas, Codex, hotbar), item_forge for slice items (/gm item, /gm affix, /gm socket, /gm refine), one-click Dewgrass/Orchard monster packs in spawn_ring, teleport inside the slice (/gm tp) and the Atlas, Codex and facet resets (/gm reset atlas|codex|facets)",
     "test": "each slice preset's Atlas allocation equals planBuild from the wiki Atlas planner (golden test); forged and preset items match the forge preview (affix tier and value in range) and carry the gm flag, and every trade, sell, list or mail path that exists by then refuses them; every slice pack and teleport target resolves; one automated test per GM-02 command",
     "evidence": "per-build preset screenshot (gear, Atlas, hotbar) + forge preview vs in-game popup pair + audit log excerpt (.txt)",
     "needs": [
      "unity",
      "rust"
     ],
     "proposed": true,
     "note": "Proposed: owner decision 2026-10-08 (GM arena run). GM rules: every GM action is audited (who, what, where); GM-made items carry the gm flag and can never be traded, sold, listed or mailed; until M6 test builds use a dev-only local GM flag (never in release builds); from M6 access is by the gm or qa role, agents never grant roles, and the owner gives his own account the gm role with the local CLI (GM-04). GM tools never add a global time-scale and never hide telegraphs."
    }
   ],
   "gate": [
    "Playtest: 30 min session at 30 fps on all physical Low references; 60 fps on the secondary High desktop; both cameras and rapid dash traversal profiled",
    "Owner review of each build's 10 s showcase",
    "G3 battle slice: chain + all movement gems + grunt/elite/representative placeholder boss + camp; all input devices, both cameras and tier/authority invariants pass",
    "GM arena slice (GM-02): /gm preset gives each vertical-slice build its presetRule character with the planBuild Atlas; gm-flagged items are refused by every trade, sell, list and mail path"
   ],
   "art": [
    "ART-HUD-001",
    "ART-HUD-002",
    "ART-HUD-004",
    "ART-KEY-warblade",
    "ART-KEY-windarcher",
    "ART-KEY-pyromancer"
   ],
   "public": {
    "slug": "first-slice",
    "title": "First playable slice",
    "summary": "Three weapon builds playable together in a first slice of the world.",
    "status": "planned"
   }
  },
  {
   "id": "M5",
   "name": "World greybox → art (Amber Basin 4×4 km)",
   "phase": "production",
   "dependsOn": [
    "M1"
   ],
   "goal": "Streamed terrain, roads, 12 zones, Amber Gate town, waypoints, mount, navigation grid shared with the server.",
   "groups": [
    "Heightmap + splat generator (deterministic) from world.json",
    "256 m chunk streaming + LOD + impostors",
    "Walkability grid (1 m) + HPA* used by server and client",
    "Amber Gate greybox → kit art (ART-ENV-054)",
    "Vegetation instancing + wind; sky/fog/lighting per zone mood",
    "Mount + waypoint travel + click-to-travel (map/minimap click → navmesh path, auto-mount over 40 m, waypoint offer; controls.json clickToTravel)",
    "Time-of-day keyframes and weather states per area (time-weather.json, ADR-030), night variants of the area beds, contrast and perf checks across the review matrix"
   ],
   "gate": [
    "Walk town → Jade Seal Ruins without falling/stuck (bot path test)",
    "Streaming hitch < 8 ms p99",
    "Memory within tier budget",
    "Area briefs; deterministic full inputs/overrides; movement-gem/escape routes and camera checks; Low/High telegraph readability and fixed story captures pass"
   ],
   "art": [
    "ART-MAP-030",
    "ART-ENV-054"
   ],
   "public": {
    "slug": "amber-basin",
    "title": "The Amber Basin",
    "summary": "The region takes shape from Amber Gate to the Jade Seal Ruins, with roads, mounts and waypoints.",
    "status": "planned"
   }
  },
  {
   "id": "M6",
   "name": "Online core at 1,000 per channel",
   "phase": "production",
   "dependsOn": [
    "M1",
    "M4"
   ],
   "goal": "Accounts, characters, persistence, channels (1,000 cap), parties, chat, reconnect; real clients + bots together.",
   "groups": [
    "Gateway (axum) + Postgres schema + migrations",
    "Admission + channel assignment + transfer",
    "Parties (4), chat, friends, inspect (opt-in)",
    "Inventory/stash/bank transactions",
    "Reconnect + crash recovery",
    "Load: 1,000 bots + 10 real clients for 60 min",
    "Accounts: one-tap Google sign-in (web One Tap, Android Credential Manager, iOS Google + Sign in with Apple, Windows system-browser PKCE); gateway token verification + Zoen sessions (docs/tech/ACCOUNTS_AUTH.md)",
    "Security system: rule engine over security.json (certain → auto permanent ban + rollback; strong → restriction; watch → log), ban screen with ban id + Contact us, appeal review tool, rule-quality limits; red-team and false-positive gates (docs/tech/SECURITY_ANTICHEAT.md)",
    "Version gate: /v1/status minimum version; Update required (native) and reload banner (Web)"
   ],
   "tasks": [
    {
     "id": "GM-04",
     "size": "M",
     "title": "GM role access: a local CLI the owner runs to give his own account the gm role (and qa to testers he picks), audited; /gm commands switch from the dev-only local GM flag to the gm/qa role check; agents build and test it on a throwaway local database and never grant a role",
     "test": "on a throwaway local database: grant and revoke write audit rows; /gm commands are refused without gm or qa and allowed with it; release builds contain no dev GM flag (build check)",
     "evidence": "CLI test log + audit rows excerpt (.txt)",
     "needs": [
      "rust"
     ],
     "proposed": true,
     "note": "Proposed: owner decision 2026-10-08 (GM arena run). Role access starts with the M6 accounts, so the CLI lands here, before GM-03 (M9). The owner runs it on his own account; agents never grant roles."
    }
   ],
   "gate": [
    "Soak 60 min, zero item duplication in fuzz tests",
    "Downstream ≤ 32 KB/s in crowded town",
    "Red team: every cheat attempt banned, nothing duplicated; false positives: zero bans for 1,000 legitimate bots",
    "GM role access (GM-04): /gm is refused without the gm or qa role, the dev-only GM flag is absent from release builds, and only the owner grants roles"
   ],
   "art": [],
   "public": {
    "slug": "online",
    "title": "A road shared with others",
    "summary": "Channels, parties and crowded towns, tested at scale.",
    "status": "planned"
   }
  },
  {
   "id": "M7",
   "name": "Quests + Auto-Quest + Auto-Hunt",
   "phase": "production",
   "dependsOn": [
    "M5",
    "M6"
   ],
   "goal": "Quest engine runs quests.json; Auto-Quest 1→10 hands-free; Auto-Hunt online; offline Auto-Hunt as a visible actor for 4 h.",
   "groups": [
    "Quest state machine + dialogue UI",
    "Auto-Quest planner (travel/talk/hunt/collect/interact/escort)",
    "Auto-Hunt presets (targets, radius, potions, buffs, loot filter, return rules)",
    "Offline actor lease + receipts",
    "Bot test: fresh character reaches L10 with Auto-Quest only"
   ],
   "gate": [
    "Auto-Quest L1→10 in ≤ 2 h effective (pace contract)",
    "No exploit: same command validation as manual"
   ],
   "art": [
    "ART-HUD-008",
    "ART-HUD-014"
   ],
   "public": {
    "slug": "story",
    "title": "The story road",
    "summary": "The main story with Auto-Quest, plus Auto-Hunt for busy days.",
    "status": "planned"
   }
  },
  {
   "id": "M8",
   "name": "Content alpha",
   "phase": "production",
   "dependsOn": [
    "M7"
   ],
   "goal": "All 9 pure builds, all 20 monsters + elites, chapters 1–3, Kiln Depths dungeon, items/crafting/refinement.",
   "groups": [
    "6 more pure builds (36 skills)",
    "16 more monsters + elites",
    "Chapters 1–3 (31 quests)",
    "Kiln Depths dungeon + ranking",
    "Crafting/orbs/refinement +15 per items doc",
    "Stash/stalls"
   ],
   "gate": [
    "Closed playtest L1→28",
    "Economy simulation report"
   ],
   "art": [
    "skill-icons P1",
    "weapon-icons",
    "npc-portraits"
   ]
  },
  {
   "id": "M9",
   "name": "Content beta",
   "phase": "production",
   "dependsOn": [
    "M8"
   ],
   "goal": "6 hybrid builds, chapters 4–5, Bellbound Regent, Kharuun, Lantern Convoy event.",
   "groups": [
    "6 hybrid builds (36 skills)",
    "Chapters 4–5 (16 quests)",
    "2 bosses with telegraphs + break pressure",
    "Lantern Convoy (16 players)",
    "Boss reward caps + receipts",
    "Named boss music start/end/reset/leash/leave/death/reconnect and warning ducking; ordinary outdoor travel remains environment-only"
   ],
   "tasks": [
    {
     "id": "GM-03",
     "size": "L",
     "title": "GM arena complete: boss_pit (/gm boss for the Bellbound Regent and Kharuun with start phase, enrage-timer toggle, fight reset and telegraph timing overlay), all 18 preset_builds (presetRule; Atlas by planBuild, the Atlas page's planner), item_forge over every base, affix, tier, socket, refinement +0…+15 and the fractured flag, /gm give stacks up to 999, /gm tp to every waypoint, area, boss spawn and quest step, and every gm-room.json command with its own automated test",
     "test": "a coverage check fails when any gm-room.json command has no automated test; 18/18 preset Atlas allocations equal planBuild; the forge round-trips every base and every affix tier's value bounds; every teleport target resolves; each boss start phase and the enrage toggle reach the expected server state with telegraphs visible on Low; trade, sell, listing and mail refuse gm items",
     "evidence": "command coverage report JSON + boss pit clip per start phase on Low + 18-build preset contact sheet + audit log excerpt (.txt)",
     "needs": [
      "unity",
      "rust"
     ],
     "proposed": true,
     "note": "Proposed: owner decision 2026-10-08 (GM arena run). Area features with no gm-room.json command yet (boss enrage timer and fight reset, dummy armour/resistance presets, show server positions, refill life and mana, remove buffs) get a proposed command there first, so each has its test. GM rules: every GM action is audited (who, what, where); GM-made items carry the gm flag and can never be traded, sold, listed or mailed; until M6 test builds use a dev-only local GM flag (never in release builds); from M6 access is by the gm or qa role, agents never grant roles, and the owner gives his own account the gm role with the local CLI (GM-04). GM tools never add a global time-scale and never hide telegraphs."
    }
   ],
   "gate": [
    "Full L1→40 run in ~40 effective hours (bots + humans)",
    "Boss fights readable on Low",
    "GM arena complete (GM-03): every gm-room.json command has a passing automated test; both bosses start at any phase in the boss pit with telegraphs readable on Low"
   ],
   "art": [
    "ART-KEY-*",
    "atlas-icons"
   ]
  },
  {
   "id": "M10",
   "name": "Art + audio + polish",
   "phase": "polish",
   "dependsOn": [
    "M9"
   ],
   "goal": "Final HUD art, VFX polish, music + SFX pass, accessibility, performance tuning on reference devices.",
   "groups": [
    "Replace every placeholder (art-requests empty for P0/P1)",
    "VFX polish per build palette",
    "Town + GM-room + named boss encounter themes (ADR-026), outdoor environment beds/emitters/accents + full SFX pass",
    "Accessibility (flash reduction, colour-blind, remap)",
    "Perf: Low/Mid/High/Ultra on 3 devices"
   ],
   "gate": [
    "Reviewer scorecard ≥ target on every build/zone",
    "Perf budgets met on all references"
   ],
   "art": [
    "all remaining"
   ]
  },
  {
   "id": "M11",
   "name": "Platform + closed alpha",
   "phase": "release",
   "dependsOn": [
    "M10"
   ],
   "goal": "PWA + Capacitor builds, hosting, monitoring, closed alpha.",
   "groups": [
    "Web build (Low–Mid) on the CDN: version check + reload prompt",
    "Store builds: Windows installer + launcher/patcher, Android AAB, iOS (store accounts are owner decisions)",
    "Hosting + monitoring + backups",
    "Closed alpha (50–200 players) + telemetry review",
    "Premium public site (docs/design/PUBLIC_SITE.md): visual directions → Home, Showcase, public Roadmap, Event times, Skill + Inventory playgrounds, Account, Redeem key; Lighthouse ≥ 90 mobile",
    "Auto-patching: Windows launcher/patcher (signed manifest, deltas, verify, rollback), Addressables remote content on all platforms, Android in-app updates, iOS update screen (docs/tech/PATCHING.md)",
    "Site + admin started early in the WEB lane (ADR-025, tasks WEB-*); this milestone adds store builds and the live account pages."
   ],
   "gate": [
    "Owner approval of any spend under the PHP 5,000 cap",
    "Alpha retention + crash metrics reviewed"
   ],
   "art": [
    "ART-UI-020",
    "ART-UI-021"
   ],
   "public": {
    "slug": "closed-alpha",
    "title": "Closed alpha",
    "summary": "Invite-only testing with one-time access keys. Alpha progress is wiped at the end.",
    "status": "planned"
   }
  }
 ],
 "publicIntro": [
  {
   "id": "pre-production",
   "title": "Pre-production",
   "summary": "The game is designed and written down: the Amber Basin, eighteen weapon builds, items, quests and the in-game interface.",
   "status": "done"
  }
 ],
 "publicOutro": [
  {
   "id": "open-beta",
   "title": "Open beta",
   "summary": "Open sign-ups and the full set of builds.",
   "status": "planned"
  },
  {
   "id": "launch",
   "title": "Launch",
   "summary": "The full release on Windows, Android, iOS and the browser.",
   "status": "planned"
  }
 ]
}
