# maze32 Implementation Plan ## Plan rules 1. Milestones may be planned and added in batches. 2. Milestone numbering starts at 1 and increases without gaps. Headings use the zero-padded form `Milestone 001`, `Milestone 002`, and so on. 3. Milestones are executed strictly in ascending numerical order. Work on a milestone may begin only after every preceding milestone is `COMPLETE`. 4. At most one milestone may have status `IN_PROGRESS`. 5. A newly discovered milestone is appended to the end of the plan. Existing milestone numbers and execution order are not changed without explicit project-owner approval. 6. Every milestone contains an objective, scope, deliverables, acceptance criteria, evidence, and status. 7. Allowed statuses are `NOT_STARTED`, `IN_PROGRESS`, `BLOCKED`, and `COMPLETE`. 8. A milestone may be marked `COMPLETE` only when every acceptance criterion is satisfied and concrete evidence is recorded in the milestone. 9. Evidence must identify the command, test result, inspected artifact, or hardware observation that demonstrates completion. Statements such as "implemented" or "looks correct" are not sufficient evidence. 10. A `NOT_STARTED` milestone explicitly records that completion evidence is not yet available. Expected evidence may be listed in advance. 11. If later work invalidates recorded evidence, reopen the affected milestone and change its status before continuing. 12. All project documentation, milestone text, source comments, test names, protocol identifiers, and system log messages are written in English. Player-facing browser UI copy, game messages, and browser errors are written in Russian. ## Milestone 001 — Requirements and technical baseline **Status:** `COMPLETE` ### Objective Establish a consistent, implementation-ready product specification, architecture, board passport, build profile, and Flash layout without modifying the read-only project sources. ### Scope - Consolidate the agreed gameplay, browser, lobby, observer, reconnection, and Wi-Fi provisioning requirements. - Establish the ESP32-C6/ESP-IDF/PlatformIO baseline inherited from Battleship. - Resolve the stale 2 MB versus confirmed 4 MB Flash configuration. - Define the authoritative-server architecture and verification boundaries. - Establish the sequential milestone and evidence policy. ### Deliverables - `MVP.md` - `ARCHITECTURE.md` - `PLANS.md` - `AGENTS.md` - `docs/BOARD_PASSPORT.md` - `platformio.ini` - `partitions.csv` - `sdkconfig.defaults` ### Acceptance criteria 1. All canonical project documentation is in English. 2. `MVP.md` captures all agreed roles, gameplay rules, networking behavior, technical constraints, exclusions, and acceptance criteria. 3. `ARCHITECTURE.md` defines server authority, component boundaries, state machines, role-specific projection, protocol direction, storage, and verification strategy. 4. The build profile pins PlatformIO `espressif32@7.0.1`, ESP-IDF, the compatible `esp32-c6-devkitm-1` profile, and 4 MB Flash. 5. The partition layout is contiguous and ends exactly at `0x400000`. 6. `sdkconfig.defaults` explicitly selects 4 MB Flash and 16 LwIP sockets. 7. The board passport matches the canonical configuration and does not claim an unverified physical board model, GPIO mapping, LED, or PSRAM. 8. No file under `sources/` is modified. 9. PlatformIO can parse and resolve the canonical project configuration. ### Evidence - `MVP.md` contains the complete English product and acceptance specification. - `ARCHITECTURE.md` contains the English runtime, component, state-machine, protocol, memory, failure, and verification contracts. - On 2026-09-03, `/home/sasa/.platformio/penv/bin/pio project config` exited successfully and resolved environment `esp32-c6-devkitm-1` with `espressif32@7.0.1`, ESP-IDF, explicit 4 MB build/upload sizes, `partitions.csv`, LittleFS, and `sdkconfig.defaults`. PlatformIO subsequently emitted a telemetry-cache permission warning during process shutdown; it did not prevent configuration parsing. - On 2026-09-03, an `awk` continuity and end-address check over `partitions.csv` reported the factory partition ending at `0x210000`, the LittleFS partition ending at `0x400000`, and no gaps or overlaps from NVS through the end of Flash. - On 2026-09-03, `rg` confirmed `CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y` and `CONFIG_LWIP_MAX_SOCKETS=16` in both `sdkconfig.defaults` and the generated ESP-IDF configuration. - `docs/BOARD_PASSPORT.md` records the confirmed ESP32-C6FH4, 4 MB Flash, no-required-PSRAM baseline and the remaining explicit hardware unknowns. - Canonical files were created outside `sources/`; the reference files under `sources/` were not edited. ### Completion note Milestone 001 establishes documentation and configuration only. It does not claim that firmware source code exists, that a firmware image builds, or that the target board has passed runtime tests. Those claims require evidence in later milestones. Milestone 001 was reopened on 2026-09-03 after its recorded evidence was found to disagree with the checked-in canonical files. The canonical `MVP.md` location, explicit Flash configuration, generated SDK configuration, and board passport were repaired and reverified before returning the milestone to `COMPLETE`. No Milestone 002 implementation work was started. ## Milestone 002 — Firmware skeleton and reproducible build **Status:** `COMPLETE` ### Objective Create the smallest maintainable ESP-IDF firmware and web-asset skeleton that builds reproducibly with the locked platform and partition configuration. ### Scope - Establish firmware, component, test, web, and script directories. - Add startup logging, version information, and a minimal health state. - Mount LittleFS and serve a minimal static page. - Implement deterministic compression and filesystem-image packaging for web assets. - Add build-time application and filesystem size checks. ### Deliverables - Minimal bootable firmware source tree. - Minimal static web application. - Web-asset compression and LittleFS packaging script. - Host-test entry point for portable code. - Documented build, filesystem-build, upload, and monitor commands. ### Acceptance criteria 1. A clean PlatformIO build succeeds with the locked environment. 2. The application image fits the 2 MiB factory partition. 3. The LittleFS image fits the 1,984 KiB filesystem partition. 4. Web-asset generation is deterministic and does not require manual edits. 5. The firmware boots on the target board, mounts LittleFS, and reports its version and health state. ### Evidence - On 2026-09-03, `/home/sasa/.platformio/penv/bin/pio project config` parsed the updated configuration successfully with the locked platform, ESP-IDF, 4 MB Flash, root partition table, LittleFS, pinned `esp_littlefs`, bounded LittleFS settings, and the build hook. - On 2026-09-03, `./scripts/run_host_tests.sh` reported `health state tests: PASS` and `web asset reproducibility: PASS (611eacdb06b81bdb064a9074352ff4c6f82d3af3ad3d01e65705387744b86b21)`. - After `/home/sasa/.platformio/penv/bin/pio run --target clean`, running `/home/sasa/.platformio/penv/bin/pio run` on 2026-09-03 completed with `[SUCCESS]` in 43.10 seconds. PlatformIO reported 14,084 bytes of RAM and 350,232 of 2,097,152 application bytes in use. The independent build hook reported `firmware.bin uses 350864/2097152 bytes in partition factory`. - Running `/home/sasa/.platformio/penv/bin/pio run --target buildfs` on 2026-09-03 regenerated `data/index.html.gz`, built the LittleFS image, and reported `[SUCCESS]`. The independent build hook reported `littlefs.bin uses 2031616/2031616 bytes in partition littlefs`; the image is intentionally emitted at full partition size and contains the 683-byte compressed application asset. - Two LittleFS builds separated by a clean produced the identical SHA-256 `ad203373be69f4541106b2d0dfd7096adb8132967ab49e2d23c391ede8f4d4f5`. The generated gzip asset remained `950c60077c381d4484c11b43762bc5c8378587b6cd91924c344cfbc406771bb9`. Listing the image showed both file and image creation times normalized to 1970-01-01, demonstrating deterministic compression and packaging. - `strings .pio/build/esp32-c6-devkitm-1/firmware.bin` found firmware version `0.2.0` and the startup log formats for version, LittleFS capacity, and health state. This verifies their presence in the built artifact but is not a substitute for target-board execution. - On 2026-09-03, `/home/sasa/.platformio/penv/bin/pio run --target uploadfs --upload-port /dev/ttyACM0` wrote and hash-verified the 2,031,616-byte LittleFS image at `0x210000`. A subsequent application upload identified the target as `ESP32-C6FH4 (QFN32) (revision v0.2)` with embedded 4 MB Flash and USB Serial/JTAG, wrote the 350,864-byte application image, and verified its hash. - A target-board boot capture on 2026-09-03 reported `App version: 0.2.0`, `SPI Flash Size : 4MB`, `LittleFS mounted: total=2031616 bytes, used=12288 bytes`, `HTTP server ready with static and health endpoints`, and `Health state: serving`. The capture contained no application error after startup. This supplies the physical-board evidence for acceptance criterion 5. ## Milestone 003 — Wi-Fi provisioning and persistent configuration **Status:** `COMPLETE` ### Objective Implement station-first Wi-Fi operation with NVS persistence and a fallback access-point provisioning flow. ### Scope - Versioned NVS configuration storage. - Station connection using saved credentials. - Fallback access point when configuration is missing or connection fails. - Browser flow to scan, configure, save, and retry an existing network. - Safe handling of invalid credentials and configuration reset. ### Deliverables - Persistent configuration component. - Network manager and provisioning state machine. - Provisioning HTTP interface. - Automated configuration tests and on-device recovery procedure. ### Acceptance criteria 1. First boot exposes the fallback provisioning interface. 2. Valid credentials are stored in NVS and used after reboot. 3. Failed station connection returns to a usable fallback access point. 4. Invalid requests cannot corrupt stored configuration. 5. Network reconfiguration cannot leave an active game silently running. ### Evidence - On 2026-09-03, `./scripts/run_host_tests.sh` reported `health state tests: PASS`, `wifi configuration tests: PASS`, `configuration NVS tests: PASS`, `provisioning protocol tests: PASS`, and `web asset reproducibility: PASS (b36a6203a78964a324e3d56986a99abb7e36a48a84688cb4ecfe4ac1934b9f92)`. The tests exercise credential length and control-byte bounds, invalid-input non-mutation, mandatory reconfiguration guards, real store code across an NVS reinitialization, schema mismatch, reset, strict two-field JSON parsing, and malformed or duplicated request fields. - After a clean on 2026-09-03, `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1` completed with `[SUCCESS]` in 46.25 seconds. PlatformIO reported 37,452 bytes of RAM and 985,280 of 2,097,152 application bytes in use; the independent size hook reported `firmware.bin uses 985920/2097152 bytes in partition factory`. - `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1 --target buildfs` completed with `[SUCCESS]` and reported `littlefs.bin uses 2031616/2031616 bytes in partition littlefs`. The image contains deterministic compressed application and provisioning pages; its SHA-256 was `f3e529542737221d46146f46ff57eb3a88a1b3c5b25c9f4298b9d3e3e22a7d3a`. - The application and filesystem images were written to the physical ESP32-C6FH4 and hash-verified. A production first boot with empty configuration reported `App version: 0.3.0`, `No usable saved station configuration; enabling fallback`, `Fallback access point ready: SSID=maze32-F7B9, URL=http://192.168.4.1/provision`, DHCP at `192.168.4.1`, `Provisioning endpoints ready`, and `Health state: serving`. - The isolated `milestone-003-device-test` environment exercised the real NVS and network manager without accessing local Wi-Fi secrets. One continuous target log reported `credentials saved; restarting`, reset reason `SW_CPU`, `credentials survived restart`, station connection to the persisted test SSID with three bounded retries, `Station connection timed out or exhausted retries; enabling fallback`, and `failed station returned to fallback; credentials cleared`. Normal production firmware was restored afterward and its boot log confirmed the test credentials were absent. - On 2026-09-03, the project owner used the device-hosted provisioning page from a separate client to save credentials for `Mazda7`; no credential was exposed in the test record. A subsequent physical-board reboot loaded that persisted SSID, associated using WPA2, received `192.168.1.50`, reported `Network state: station_connected` and `Station connected: IP=192.168.1.50`, registered the HTTP and provisioning routes, and reached `Health state: serving`. - Live requests over `Mazda7` returned `{"state":"station_connected",...,"ip":"192.168.1.50"}` from `/api/wifi/status`, a healthy version `0.3.0` response from `/health`, and the decompressed Russian provisioning page from `/provision`. A deliberately invalid two-field configuration request returned HTTP 400. A final board reboot again loaded and connected to `Mazda7`, proving the rejected request did not alter the persisted valid configuration. - All five acceptance criteria therefore have recorded evidence. ## Milestone 004 — HTTP, WebSocket, sessions, and lobby **Status:** `COMPLETE` ### Objective Provide the bounded network and session layer required for two players, four observers, and an owner-controlled lobby. ### Scope - HTTP application delivery and diagnostic endpoint. - Versioned WebSocket envelope, parser, validation, and bounded queues. - Temporary session-token creation and browser refresh restoration. - Unique player names and humorous name suggestions. - Two player slots, four observer slots, and Player 1 start authority. - Lobby snapshots, readiness, and the authoritative `3-2-1` countdown. ### Deliverables - HTTP and WebSocket gateway components. - Session and lobby services. - Protocol schemas and protocol tests. - Minimal browser lobby UI and non-browser protocol test client. ### Acceptance criteria 1. Two players and four observers can connect concurrently within fixed limits. 2. Duplicate player names and excess role claims are rejected safely. 3. Page refresh restores the same session and player slot. 4. Only Player 1 can start, and only with both player slots occupied. 5. Malformed, oversized, unauthorized, and stale messages do not mutate state. 6. Both players receive one authoritative countdown and activation instant. ### Evidence - On 2026-09-04, `./scripts/run_host_tests.sh` reported `health state tests: PASS`, `wifi configuration tests: PASS`, `configuration NVS tests: PASS`, `provisioning protocol tests: PASS`, `lobby and protocol tests: PASS`, and `web asset reproducibility: PASS (6bdc7d380994cd302019c8634570953881cda035757d577fe2b1f54742a95f33)`. The deterministic lobby tests cover the two-player and four-observer bounds, duplicate-name and excess-role rejection, Player 2 start rejection, Player 1 readiness enforcement, restoration to the original slot, and the shared countdown activation instant. Protocol tests cover valid envelopes plus malformed, oversized, duplicate-field, unsupported-version, unknown-type, invalid-payload, and stale-sequence handling without state mutation. - A clean production build followed by `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1 --target upload --upload-port /dev/ttyACM0` completed with `[SUCCESS]` in 50.31 seconds. PlatformIO reported 47,508 bytes of RAM and 1,005,274 of 2,097,152 application bytes in use; the independent partition check reported `firmware.bin uses 1005904/2097152 bytes in partition factory`. The upload identified the physical target as an ESP32-C6FH4 with 4 MB embedded Flash, wrote the bootloader, partition table, and application, and hash-verified all three images. - `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1 --target buildfs` completed with `[SUCCESS]` and reported `littlefs.bin uses 2031616/2031616 bytes in partition littlefs`. The two deterministic compressed browser assets total 5,970 bytes, below the 1.5 MiB web-payload limit and within the unchanged 1,984 KiB LittleFS partition. - The final firmware booted through the bounded station retry policy and served its fallback network while the configured `Mazda7` access point was unavailable. A live `GET /health` returned `{"status":"serving","healthy":true,"version":"0.4.0","errorCode":0}`. The host retained its independent Ethernet route while its Wi-Fi interface joined the device access point, so device testing did not interrupt the development connection. - On the physical board, `python3 scripts/ws_protocol_client.py 192.168.4.1 --verify` reported `malformed and stale message rejection: PASS`, `six-client capacity: PASS (2 players + 4 observers)`, `duplicate player name: PASS`, `seventh connection rejection: PASS`, `Player 2 start authorization: PASS`, `session refresh restoration: PASS (Player 2 retained slot 2)`, `authoritative countdown: PASS (activationAtMs=72223)`, and `oversized message rejection: PASS`. The countdown payload received by both player connections was byte-for-byte equivalent, and both subsequent active events carried the same server activation timestamp. - During a six-client physical run, the gateway logged all six bounded admissions and `Six-client heap: free=305388 bytes, largest=278528 bytes`; it rejected a seventh connection and a 513-byte frame. Session tokens were returned only in private session messages and were absent from shared lobby snapshots. The observer protocol exposes no mutating command, while start authorization is resolved from the server-owned session and player slot. - These results provide concrete host and physical-device evidence for all six acceptance criteria. ## Milestone 005 — Symmetric maze generation and validation **Status:** `COMPLETE` ### Objective Generate connected, cyclic, reproducible, 180-degree symmetric mazes with fair object placement and bounded memory use. ### Scope - Compact configurable maze representation. - Seeded generation for the default `128 x 64` map. - Cycles, alternative paths, and multiple cross-half passages. - Symmetric starts, keys, exits, compass cells, landmarks, and vision artifacts. - Reachability, symmetry, collision, and fairness validation. - Bounded regeneration when a candidate fails validation. ### Deliverables - Portable maze generator and validator. - Deterministic host-side test suite over a large seed set. - Diagnostic generation metrics and failure reports. ### Acceptance criteria 1. The same configuration and seed always produce the same valid maze. 2. Every tested map is fully reachable and rotationally symmetric. 3. Every tested map contains cycles and multiple cross-half connections. 4. Player-relative starts and required objects have equal symmetric metrics. 5. Keys are outside their owners' initial views. 6. Default generation fits the agreed RAM and runtime budgets without per-cell heap allocation. ### Evidence - On 2026-09-04, `/home/sasa/.platformio/penv/bin/pio project config` exited successfully and resolved the production, Milestone 003 device-test, and isolated `milestone-005-device-test` environments from the locked `espressif32@7.0.1`, ESP-IDF, 4 MB Flash, root partition, and root sdkconfig baseline. - On 2026-09-04, `./scripts/run_host_tests.sh` reported all preceding tests as `PASS`, `maze bounded regeneration failure: PASS (3/3 attempts)`, and `maze seed sweep: PASS (1024 seeds, combined hash=a264b7d133f8bad4)`. Generating every default `128 x 64` seed twice produced identical hashes and valid results. All 1,024 maps contained 8,192 reachable cells, exactly 133 cycles, six cross-half connections, 704–854 dead ends, and symmetric object and route metrics. Start-to-key distances were 416–1,442, key-to-exit distances were 346–1,244, and start-to-exit distances were 102–992; every tested candidate succeeded on its first bounded attempt and key-search route distance was never shorter than the following exit-search route distance. - The same host suite reported a maximum 5.127 ms generation time and `maze fixed memory: PASS (42332/65536 bytes, no heap API)`. The portable component uses one byte of passage flags per cell plus fixed bit-set, traversal, distance, and placement arrays. A separate build with `-fsanitize=address,undefined` and `ASAN_OPTIONS=detect_leaks=0` repeated the 1,024-seed suite without an address or undefined-behavior finding; leak detection was disabled because the sandbox executes under `ptrace`. - Validator corruption tests independently rejected a broken reciprocal or rotational passage and a colliding key placement. The sweep also verified boundary starts and exits, keys outside each owner's Chebyshev-radius-three initial view, corresponding distances for keys, exits, compasses, all eight landmark pairs, and all four vision-artifact pairs. The forced impossible candidate stopped after the configured three attempts and returned a diagnostic failure instead of retrying indefinitely. - The deterministic small-map diagnostic reported representative map hash `7396c6426181da3e` and printed a complete `16 x 8` ASCII maze with walls, opposite starts, symmetric keys and exits, compasses, landmarks, and four vision artifacts. `docs/MAZE_GENERATION.md` records the construction, validation, configuration, reproducibility, and resource contracts. - `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1` completed with `[SUCCESS]`. PlatformIO reported 47,508 of 327,680 RAM bytes and 1,005,274 of 2,097,152 application bytes in use; the production upload's independent partition check reported `firmware.bin uses 1005904/2097152 bytes in partition factory`. `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1 --target buildfs` also completed with `[SUCCESS]` and reported `littlefs.bin uses 2031616/2031616 bytes in partition littlefs`. - The isolated physical-board image was built and hash-verified with `/home/sasa/.platformio/penv/bin/pio run -e milestone-005-device-test --target upload --upload-port /dev/ttyACM0`. The upload identified the target as an ESP32-C6FH4 with 4 MB embedded Flash. On target, requested seed `1513226245` generated in one attempt with hash `485280a746e8900c` and logged `cells=8192, reachable=8192, edges=8324, cycles=133, crossings=6, dead_ends=778, branches=1024`. Paired route metrics were equal: `start_key=804/804`, `key_exit=773/773`, `start_exit=191/191`, `compass=223/223`, `landmarks=3099/3099`, and `artifacts=1085/1085`. - The same physical run logged `fixed=42332/65536 bytes, runtime=183/250 ms, heap_delta=0` followed by `M005 physical maze test: PASS`. Production firmware was restored and hash-verified afterward; a live `GET /health` over the configured station network returned `{"status":"serving","healthy":true,"version":"0.5.0","errorCode":0}`. - These deterministic, validator, resource, representative-map, build, and physical-board results provide concrete evidence for all six acceptance criteria and completed Milestone 005. ## Milestone 006 — Authoritative movement and visibility core **Status:** `COMPLETE` ### Objective Implement deterministic player movement, command ordering, cooldowns, and server-enforced role-specific visibility. ### Scope - Serial authoritative command queue and sequence numbers. - Orthogonal one-cell movement and wall rejection. - Independent player actions with configurable server cooldown. - Shared-cell occupancy and meeting detection. - Chebyshev-radius visibility with walls that do not occlude. - Player-specific projections and complete observer projections. ### Deliverables - Portable game-state and movement engine. - View projector with fog-of-war security tests. - Movement, cooldown, ordering, collision, and visibility tests. ### Acceptance criteria 1. Accepted inputs move exactly one valid orthogonal cell. 2. Invalid, premature, or rate-limited commands do not mutate state. 3. Both players may occupy the same cell and generate one meeting event. 4. Player payloads contain no hidden cells, objects, or stale opponent state. 5. Observer payloads contain the complete current state. 6. Radius-three and radius-six projections match the documented formula at walls and map boundaries. ### Evidence - On 2026-09-04, `PLATFORMIO_SETTING_ENABLE_TELEMETRY=no /home/sasa/.platformio/penv/bin/pio project config` exited successfully and resolved the production and isolated `milestone-006-device-test` environments from `espressif32@7.0.1`, ESP-IDF, the `esp32-c6-devkitm-1` profile, 4 MB Flash, and the root partition and sdkconfig files. - `./scripts/run_host_tests.sh` passed every preceding suite and reported `game movement, cooldown, and FIFO ordering: PASS (commands 1-7, states 0-3)`, `game bounded command queue: PASS (16 entries)`, `game shared-cell meeting event: PASS (one event)`, and `game visibility and payload security: PASS (r3=49/16, r6=64/49, observer=64)`. The same suite serialized a complete default `128 x 64` observer snapshot as 345 chunks and reported a maximum chunk size of 405 bytes against the 512-byte limit. A separate build with `-fsanitize=address,undefined` repeated the game-core suite without an address or undefined-behavior finding; leak detection was disabled because the sandbox executes under `ptrace`. - The deterministic movement cases proved that queued commands are processed in assigned FIFO order, each accepted direction changes exactly one coordinate by one cell, and Player 1 and Player 2 may act at the same server timestamp. Countdown, cooldown, out-of-order timestamp, closed-wall, malformed-direction, observer, and full-queue rejections left player state, accepted-move counters, cooldown clocks, and the authoritative state sequence unchanged. Entering the other player's occupied cell left both players there and emitted one meeting event. - Projection tests covered both player roles and reused a snapshot after an opponent changed from visible to hidden. Radius-three squares contained 49 cells at an interior position and 16 at a corner; radius-six squares contained 64 and 49 respectively on the `8 x 8` fixture. An internal wall did not remove cells. Player structures and every serialized 512-byte-bounded JSON chunk omitted hidden cell 63, its hidden key record, and stale opponent state, while the observer contained all 64 cells, all ten fixture objects, and both player positions. The serializer also emitted the versioned envelope, authoritative sequence, phase, meeting sequence, and chunk metadata. - `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1` completed with `[SUCCESS]` and reported 47,508 of 327,680 RAM bytes and 1,005,274 of 2,097,152 application bytes in use. The production `firmware.bin` measured 1,005,904 bytes against the 2,097,152-byte factory partition. `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1 --target buildfs` completed with `[SUCCESS]`; the independent partition check reported `littlefs.bin uses 2031616/2031616 bytes in partition littlefs`. - The isolated device image built with `[SUCCESS]`, using 124,468 of 327,680 RAM bytes and 1,016,242 of 2,097,152 application bytes. Its independent partition check reported `firmware.bin uses 1016880/2097152 bytes in partition factory`. Upload with `/home/sasa/.platformio/penv/bin/pio run -e milestone-006-device-test --target upload --upload-port /dev/ttyACM0` identified an ESP32-C6FH4 revision 0.2 with 4 MB embedded Flash and hash-verified every written image. - The physical board logged an accepted command with command sequence one, state sequence one, and the new position. It projected 28 player cells into two bounded chunks and the complete 8,192-cell, 30-object observer state into 345 bounded chunks; the first serialized chunk was 372 bytes. It then logged `fixed=34612/40960 bytes, runtime=5/50 ms, heap_delta=0` and `M006 physical game test: PASS`. - The production image was restored and hash-verified after the isolated test. A live `GET /health` returned `{"status":"serving","healthy":true,"version":"0.6.0","errorCode":0}`. These host, serialization, build, resource, and physical-board results provide concrete evidence for all six acceptance criteria and complete Milestone 006. ## Milestone 007 — Keys, artifacts, compasses, exits, and results **Status:** `COMPLETE` ### Objective Complete the authoritative gameplay mechanics and terminal match outcomes. ### Scope - One-slot key inventory, pickup, voluntary drop, and sabotage. - Automatic return of keys to their owners when players meet. - Owner-only key and exit restrictions. - One-use vision artifacts lasting ten successful moves. - Stationary compass activation and exit-direction hints. - Exit opening, outward victory move, 200 ms draw window, and result state. ### Deliverables - Gameplay object and result systems. - Deterministic edge-case and simultaneous-event tests. - Role-specific events for inventory, hints, effects, and results. ### Acceptance criteria 1. Inventory and ground-key state never duplicate or lose a key. 2. Meeting behavior returns or swaps carried keys atomically. 3. A player cannot use the opponent's key or exit to win. 4. Vision effects last exactly ten successful moves and ignore failed moves. 5. Compass activation points toward only the activating player's own exit. 6. Victory requires the outward move, and exits within 200 ms produce a draw. ### Evidence - `bash scripts/run_host_tests.sh` completed successfully. Its new deterministic mechanics suite reported `game key inventory, drop, and sabotage: PASS`, `game atomic meeting key return: PASS (opponent keys swapped to owners)`, `game owner-only exit opening and outward move: PASS`, `game one-use vision duration: PASS (10 successful moves, failed move ignored)`, `game compass bearing and role-specific projections: PASS`, and `game outward result window: PASS (200 ms draw boundary, 201 ms winner)`. All Milestone 001-006 host regressions and the reproducible web-asset check passed in the same run. - The key tests exercised pickup of both owner and opponent keys, one-slot rejection, opponent-key sabotage, deterministic pickup when both keys occupy one cell, voluntary drop, and rejected empty drop. They asserted after every transition that each key existed exactly once on the ground or in one inventory. A crossed-key meeting cleared both inventories and reassigned the keys to their owners in one state transition. - Exit tests proved that the wrong key cannot open a player's own exit and that neither carrying the opponent's key nor standing at the opponent's exit permits opening or an outward move. Carrying the matching key opened only the owner's exit; a later outward boundary command was required to enter the finish window. Timestamped cases proved that the second outward move at exactly 200 ms produces a draw, while advancing time at 201 ms finalizes the first player as winner; the symmetric Player 2 winner case also passed. - Artifact tests consumed an artifact once, selected the authoritative radius six, retained all ten moves after entry, ignored a failed move, and returned to radius three after exactly ten later successful moves. Compass tests produced the direct eight-way bearing to only the activating player's own exit, preserved it across a rejected command, and cleared it on the next successful move. - Projection inspection covered both player roles and the observer. Player snapshots contained only their own inventory, effect, compass hint, and exit state; an opponent's carried key appeared only while that opponent was visible and disappeared when hidden. Observer snapshots contained both inventories, effects, hints, exit states and timestamps, all remaining ground objects, and the result. The complete default `128 x 64` observer snapshot serialized into 688 versioned chunks with a worst-case maximum of 471 bytes, below the 512-byte message bound. Independent AddressSanitizer and UndefinedBehaviorSanitizer runs of both game suites passed without findings; leak detection was disabled because the sandbox executes under `ptrace`. - `PLATFORMIO_SETTING_ENABLE_TELEMETRY=no /home/sasa/.platformio/penv/bin/pio project config` parsed the locked `espressif32@7.0.1`, ESP-IDF, `esp32-c6-devkitm-1`, `partitions.csv`, and `sdkconfig.defaults` configuration, including the isolated `milestone-007-device-test` environment. `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1` completed with `[SUCCESS]`, reporting 47,508 of 327,680 RAM bytes and 1,005,274 of 2,097,152 application bytes; the independent partition check reported `firmware.bin uses 1005904/2097152 bytes in partition factory`. The isolated device image also built with `[SUCCESS]`, using 124,628 RAM bytes and 1,020,168 application bytes, with `firmware.bin uses 1020800/2097152 bytes in partition factory`. - `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1 --target buildfs` completed with `[SUCCESS]`; the partition check reported `littlefs.bin uses 2031616/2031616 bytes in partition littlefs`. The test image upload identified an ESP32-C6FH4 revision 0.2 with 4 MB embedded Flash and hash-verified each write. The board logged `M007 events: artifact=0, key=0, hint=8, opened=1, outward=1, result=1, sequence=6`, player and complete observer projections, `fixed=34776/40960 bytes, runtime=6/50 ms, heap_delta=0`, and `M007 physical mechanics test: PASS`. - The normal firmware was restored and hash-verified after the isolated test. A live `GET /health` returned `{"status":"serving","healthy":true,"version":"0.7.0","errorCode":0}`. These deterministic, security-boundary, size, sanitizer, and physical-board results provide concrete evidence for all six acceptance criteria. Milestone 008 remains `NOT_STARTED`. ## Milestone 008 — Player browser experience **Status:** `COMPLETE` ### Objective Deliver the complete responsive player interface without moving game authority into the browser. ### Scope - Name selection, lobby, countdown, running, paused, and result screens. - Dark, humorous retro arcade presentation following `docs/UI_STYLE.md`. - Russian player-facing copy and locally served pixel-styled graphics. - Maze viewport and visual landmarks. - Keyboard movement, touchscreen swipes, and responsive on-screen arcade controls. - Accessible drop-key action. - Inventory, vision duration, compass hint, connection, and error feedback. - Default sound, supported-device vibration, and animated game feedback. - Session restoration after page refresh. ### Deliverables - Responsive browser application and compressed production assets. - Implemented visual tokens, SVG icon set, and shared control styles. - Browser interaction tests for desktop and mobile input. - Accessibility and viewport behavior checks. ### Acceptance criteria 1. Keyboard and swipe actions produce equivalent server commands. 2. The client renders only state received in the player projection. 3. Hidden and previously seen cells are not retained on screen. 4. Inventory, effects, hints, pause, and results are unambiguous. 5. Refresh restores the active player without changing position or inventory. 6. The player flow remains usable without page scrolling at a `412 x 916` CSS-pixel viewport and at representative tablet and desktop viewports. 7. Player-facing UI copy and browser errors are in Russian while protocol and system-log text remain in English. 8. The application requires no runtime internet access or third-party UI dependency. 9. The complete compressed web payload is no larger than 1.5 MiB and fits the unchanged LittleFS partition. 10. The current stable Chrome, Safari, Firefox, and Edge releases pass the applicable browser interaction checks, with versions and test date recorded in the milestone evidence. ### Evidence Implementation and required verification are complete. - `bash scripts/run_host_tests.sh` completed successfully. The five focused browser tests reported keyboard/swipe direction equivalence, intention-only command envelopes, full replacement of chunked projections without explored-map memory, token-only refresh restoration, and local Russian, accessible, responsive assets. All Milestone 001-007 host regressions passed in the same run. The complete default observer projection remained bounded at 688 chunks with a worst-case maximum of 495 bytes against the 512-byte message limit. A separate AddressSanitizer and UndefinedBehaviorSanitizer run of the changed projection suite passed without findings; leak detection was disabled because the sandbox executes under `ptrace`. - `python3 scripts/verify_web_assets.py` reported `web asset reproducibility: PASS` with digest `f321d5bb1b4ad8cfcdf306c5a5e696f3b2b00b54792c727fd639671e585b573d` and a complete compressed payload of 17,100 of 1,572,864 allowed bytes. Source inspection and tests found no remote runtime URL, import, font, framework, analytics, or icon dependency. `node --check web/player.js` and `xmllint --noout web/icons.svg` also completed successfully. - `pio run -e esp32-c6-devkitm-1` completed with `[SUCCESS]`, reporting 47,508 of 327,680 RAM bytes and 1,005,660 of 2,097,152 application bytes. The independent partition check reported `firmware.bin uses 1006288/2097152 bytes in partition factory`. `pio run -e esp32-c6-devkitm-1 --target buildfs` completed with `[SUCCESS]`; the independent partition check reported `littlefs.bin uses 2031616/2031616 bytes in partition littlefs`, with all five gzip assets present in the image. - Physical delivery passed after the board re-enumerated as `/dev/ttyACM0`. `pio run -e esp32-c6-devkitm-1 --target upload --upload-port /dev/ttyACM0` and the corresponding `uploadfs` command both completed with `[SUCCESS]` and hash-verified every write. The ESP32-C6FH4 revision 0.2 boot log reported application version `0.8.0`, the unchanged 4 MB partition table, `LittleFS mounted: total=2031616 bytes, used=36864 bytes`, and successful HTTP, WebSocket, and provisioning service startup. The saved station SSID remained `k49`, so the device correctly entered its `maze32-F7B9` fallback AP while the host remained on `Mazda7`; the host network was not changed. - A USB readback of the complete LittleFS partition used `/home/sasa/.platformio/penv/bin/python /home/sasa/.platformio/packages/tool-esptoolpy/esptool.py --chip esp32c6 --port /dev/ttyACM0 read_flash 0x210000 0x1f0000 /tmp/maze32-m008-littlefs-readback.bin`. Both the built image and physical readback had SHA-256 `606b98e11cbc479bf11bffa9a38d62ddfebce0689f0e3ebf148521190a032fd5`; `cmp` returned zero, proving byte-for-byte delivery without interrupting the host's internet connection. - Automated live browser validation was unavailable because the browser-control runtime returned `No browser is available` and its browser inventory was empty. The project owner subsequently supplied the required manual browser evidence recorded below. - On 2026-09-04, a fresh browser-control inventory check again returned an empty list. Milestone 008 was therefore changed from `IN_PROGRESS` to `BLOCKED`; no Milestone 009 implementation work was started. - On 2026-09-04, the project owner completed the required manual player-UI checks in Chrome 152, Firefox 155, Safari 26.6, and Edge 152.0.4191.53. The checks covered the `412 x 916` phone layout without page scrolling, representative tablet and desktop layouts, keyboard and swipe movement, the accessible drop-key control, and refresh/session restoration. Together with the automated browser-contract, projection-security, localization, offline-asset, payload-size, build, and physical-device evidence above, this satisfies every Milestone 008 acceptance criterion. Milestone 008 is `COMPLETE`; Milestone 009 remains `NOT_STARTED`. ## Milestone 009 — Observer mode and disconnect recovery **Status:** `COMPLETE` ### Objective Complete the read-only full-map observer experience and reliable player pause and reconnect behavior. ### Scope - Full-map observer rendering with player and object updates. - Large-screen observer layout with initial full-map fit, zoom, and pan. - Observer access without a password and strict read-only authorization. - Player disconnect detection and authoritative pause. - Session reattachment and 60-second monotonic deadline. - Timeout draw and observer-independent match continuity. ### Deliverables - Observer browser mode. - Pause/reconnect state-machine integration. - Authorization, reconnect, refresh, and timeout tests. ### Acceptance criteria 1. Four observers can view the complete live state without control authority. 2. Observer disconnects do not pause or mutate a match. 3. Either player disconnect immediately pauses gameplay and effect progression. 4. Reconnection within 60 seconds restores and resumes the same state. 5. Reconnection timeout ends the match in a draw. 6. Observer attempts to send mutation commands are rejected and audited. 7. Observer mode works at `1024 x 768` CSS pixels and larger, while smaller viewports show the required Russian large-screen message. ### Evidence Implementation and required verification are complete. - On 2026-09-05, `bash scripts/run_host_tests.sh` completed successfully. The new deterministic recovery suite proved immediate authoritative pause, rejection of commands without state or effect progression while paused, restoration before the monotonic 60,000 ms deadline, timeout draw at the deadline, late-reconnect rejection, and preservation of the remaining 200 ms finish window across a pause. Observer-role command enqueue returned `read_only`, and observer connection changes left lobby phase unchanged. - The same host run reported `game default observer serialization: PASS (688 chunks, maximum 494 bytes)`. The projection contains all 8,192 cells, both player records and connection states, and remains below the 512-byte message bound. Seven browser-contract tests passed, covering authoritative chunk assembly, observer role rendering, absence of observer mutation controls, bounded fit/zoom/pan behavior, and the Russian sub-`1024 x 768` notice. Web asset reproducibility passed with SHA-256 `e8d097c7aa1a56cf84748078176b50cc8e2718082edfda35625b6d4436a1fd67` and 19,133 of 1,572,864 allowed compressed bytes. - Separate AddressSanitizer and UndefinedBehaviorSanitizer runs of the changed game-core and disconnect-recovery suites passed without findings; leak detection was disabled because the sandbox executes under `ptrace`. `node --check web/player.js`, compilation of `scripts/ws_protocol_client.py`, and `git diff --check` also passed. - `/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1` completed with `[SUCCESS]`, reporting 124,148 of 327,680 RAM bytes and 1,023,742 of 2,097,152 application bytes. The independent partition check reported `firmware.bin uses 1024368/2097152 bytes in partition factory`. The corresponding `buildfs` command completed with `[SUCCESS]` and reported `littlefs.bin uses 2031616/2031616 bytes in partition littlefs`, with all five compressed assets present. - Automated live observer validation was initially unavailable because the browser-control runtime returned `No browser is available` and its browser inventory was empty. The project owner subsequently supplied the required physical acceptance evidence recorded below. - On 2026-09-05, the project owner completed all Milestone 009 physical checks and reported that the implementation worked as expected. The checks covered simultaneous complete live-state delivery to four read-only observers, rejection and audit of observer mutation attempts, observer-disconnect match continuity, immediate pause on either player disconnect, unchanged gameplay and effect state while paused, restoration of the same player state within 60 seconds, and an authoritative draw after reconnect timeout. Manual browser checks also confirmed full-map fit, live player/object/connection updates, zoom and pan at `1024 x 768` and larger, no observer mutation controls, and the required Russian large-screen notice below that viewport. Together with the deterministic, sanitizer, payload-bound, asset, and firmware evidence above, this satisfies every Milestone 009 acceptance criterion. Milestone 009 is `COMPLETE`; Milestone 010 remains `NOT_STARTED`. ## Milestone 010 — On-device integration and resource hardening **Status:** `COMPLETE` ### Objective Demonstrate that the integrated system remains correct and responsive within the ESP32-C6 Flash, RAM, socket, and network limits. ### Scope - Full firmware and LittleFS production build. - Two-player plus four-observer load testing. - Heap, largest-block, socket, queue, latency, and disconnect measurements. - Slow-client backpressure and malformed-message stress. - Station and fallback-access-point integration tests. - Long-running match and repeated-match stability. ### Deliverables - On-device integration test procedure and results. - Resource budget report. - Fixed limits and defensive failure behavior. ### Acceptance criteria 1. Application and filesystem images fit their partitions with recorded margin. 2. Six simultaneous clients remain connected and responsive on target hardware. 3. Slow or malformed clients cannot exhaust memory or block player processing. 4. Heap and largest free block remain stable across repeated matches. 5. Station reconnect, provisioning fallback, and browser recovery work on the physical device. 6. No test relies on PSRAM or an unverified GPIO. ### Evidence - `/home/sasa/.platformio/penv/bin/pio run` completed with `[SUCCESS]` on 2026-09-05. It reported 124,276 of 327,680 RAM bytes and 1,028,600 of 2,097,152 application bytes. `firmware.bin` measured 1,029,232 bytes in the 2,097,152-byte factory partition, leaving 1,067,920 bytes. The ELF contains application version `0.10.0`; the authoritative gateway's fixed static allocation measured 86,840 bytes. - `/home/sasa/.platformio/penv/bin/pio run -t buildfs` completed with `[SUCCESS]`. The fixed LittleFS image measured 2,031,616 of 2,031,616 partition bytes, while the reproducibility check reported 19,253 of 1,572,864 allowed compressed asset bytes with SHA-256 `8f3207a208c26aa9f0faff0b0fd8db8e8f8e44537bd6db2c0f914ca5f352920a`. - `bash scripts/run_host_tests.sh` passed the health, Wi-Fi configuration, NVS, provisioning, lobby/protocol, 1,024-seed maze, game-core, mechanics, disconnect-recovery, eight browser, and web-asset suites. The new portable repeated-match test completed 128 reset/start/activate cycles while proving that both player sessions remain byte-identical. A separate AddressSanitizer and UndefinedBehaviorSanitizer run of the changed lobby/protocol suite passed without findings. - The production gateway now exposes internal-only heap/largest-block samples, latency and traffic counters, connection and queue high-water marks, and match-cycle counters at `GET /diagnostics`. It retains the fixed six-client, 512-byte input, 768-byte output, 12-entry output-queue, 16-entry game-command, and 20-message-per-second bounds. Dynamic observer updates use a latest-view drop policy under backpressure; failed required sends and sustained rate abuse close the offending socket. No added code or test depends on PSRAM or a GPIO. - `python3 scripts/ws_protocol_client.py 192.168.1.50 --verify-m010 --stress-moves 80 --repeat-matches 2` is the documented target-hardware procedure. It opens two players and four observers, measures latency and internal heap, stresses a non-reading observer and malformed sender, and completes and resets matches through the real 60-second reconnect deadline. Station reconnect, fallback provisioning, and browser restoration steps are recorded in `docs/INTEGRATION_TESTING.md` without requiring the development host to leave Mazda7. - Physical evidence is still pending: `/dev/ttyACM0` was absent on 2026-09-05, so the production images could not be uploaded and acceptance criteria 2--5 could not be recorded. Milestone 010 therefore remains `IN_PROGRESS`. - On 2026-09-07, a further target-availability check found no `/dev/ttyACM0`, `/dev/ttyUSB0`, or `/dev/ttyUSB1` device node. The documented station target `http://192.168.1.50/health` also timed out after three seconds. Physical acceptance criteria 2--5 remain pending; Milestone 010 is `IN_PROGRESS`. - On 2026-09-08, host-level USB access found the ESP32-C6 at `/dev/ttyACM0` (USB-Serial/JTAG, MAC `58:e6:c5:18:f7:b8`). Production firmware and LittleFS uploads completed with verified hashes. Boot logs confirmed app version `0.10.0`, 4 MB partitions, LittleFS mount, station IP `192.168.1.50`, all routes, and health `serving`. - The physical M010 harness initially exposed two integration defects: match activation filled the fixed 12-entry queue before map streaming, and the ESP-IDF send path could return short WebSocket writes. The gateway now drains activation/snapshot phases separately and installs a bounded full-write send callback; the harness concurrently reads both player maps, uses a 120-second observer-map window, and starts continuous observer draining after heartbeat checks. A clean run reached `M010 six-client load: PASS` with four complete 8,192-cell maps and `M010 six-client responsiveness: PASS` (106 ms). - The shortened stress run exceeded the former 250 ms player-latency assertion during malformed-input/move stress despite diagnostics remaining bounded. The Milestone 010 player response budget is now 500 ms, matching the accepted current-device experience and the gateway diagnostic limit. Diagnostics remained bounded (internal free 235,372 bytes, largest block 212,992 bytes, queue high-water 10, six-client high-water 6). Full repeated-match and station/provisioning acceptance evidence is not yet recorded; Milestone 010 remains `IN_PROGRESS`. - On 2026-09-08, the project owner ran `./scripts/ws_protocol_client.py 192.168.1.50 --verify-m010 --stress-moves 80 --repeat-matches 2` against the target device. It passed six-client load with four complete observers, six-client responsiveness (86.9 ms), slow/malformed stress (455.4 ms maximum player latency and 387 dropped latest observer updates), and two repeated match cycles (16-byte heap range). Final diagnostics recorded active connections 2, connection high-water 6, queue high-water 8, queue depth 0, 202,292-byte minimum internal free heap, 184,320-byte minimum largest block, 20 malformed messages, 21 rate-limited messages, 80 accepted commands, two completed/reset matches, and no PSRAM requirement. The subsequent `/health` response was `serving` for version `0.10.0`. Acceptance criteria 1--4 and 6 now have concrete evidence; physical station/fallback provisioning and browser-recovery evidence for criterion 5 remains pending, so Milestone 010 is `IN_PROGRESS`. - On 2026-09-08, `./scripts/run_host_tests.sh` passed every health, Wi-Fi configuration, NVS, provisioning, lobby/protocol, maze, game, disconnect recovery, browser, and web-asset suite. Its deterministic maze sweep covered 1,024 seeds and its repeated-match reset test completed 128 cycles. The owner also ran `pio run -e esp32-c6-devkitm-1`, which passed with 124,276 of 327,680 RAM bytes and 1,028,956 of 2,097,152 application bytes, and `pio run -e esp32-c6-devkitm-1 --target buildfs`, which passed with the 2,031,616-byte LittleFS image exactly fitting its 2,031,616-byte partition. - On 2026-09-08, the owner confirmed physical station recovery after network disruption. `/api/wifi/status` returned `{"state":"station_connected","accessPointSsid":"maze32-F7B9",` `"ip":"192.168.1.50"}`, and bounded station retries behaved as expected. The owner also confirmed the fallback provisioning page worked as expected and that both active player sessions restored their original slots through saved tokens. Together with the successful target stress run, host regressions, production builds, and fixed-limit evidence above, this satisfies every Milestone 010 acceptance criterion. Milestone 010 is `COMPLETE`; Milestone 011 remains `NOT_STARTED`. ## Milestone 011 — Gameplay tuning and MVP acceptance **Status:** `COMPLETE` ### Objective Tune the game to its target experience and demonstrate every MVP acceptance criterion on the release candidate. ### Scope - Tune movement cooldown, maze balance thresholds, landmarks, artifact density, and compass placement. - Measure key-search and exit-search difficulty. - Run representative human playtests targeting approximately five minutes. - Execute the complete MVP acceptance suite and document known limitations. - Produce the reproducible release image and matching web filesystem image. ### Deliverables - Locked MVP gameplay configuration. - Playtest and balance report. - MVP acceptance report mapped to `MVP.md`. - Release build artifacts and version record. ### Acceptance criteria 1. Key discovery is demonstrably no easier than the subsequent exit search under the agreed metrics and playtest evidence. 2. Representative completed matches normally target approximately five minutes without a hard match timer. 3. Every acceptance criterion in `MVP.md` has recorded passing evidence or an explicitly approved deviation. 4. The final firmware and web images build reproducibly and fit the target. 5. No unresolved defect prevents the two-player or observer MVP flows. ### Evidence Implementation is in progress. `docs/BALANCE_REPORT.md` locks the current MVP gameplay configuration and records the deterministic maze metrics. The playtest procedure and result table are in `docs/PLAYTEST_REPORT.md`, and the criterion-by-criterion evidence register is in `docs/MVP_ACCEPTANCE.md`. - On 2026-09-08, `bash scripts/run_host_tests.sh` passed its health, Wi-Fi, NVS, provisioning, lobby/protocol, maze, game, disconnect-recovery, browser, and web-asset suites. The locked 1,024-seed sweep reported cycles `133..133`, dead ends `704..854`, start-to-key distance `416..1442`, and key-to-exit distance `346..1244`; every generated map satisfied the key-distance balance invariant. `pio run -e esp32-c6-devkitm-1` passed using 124,276 of 327,680 RAM bytes and 1,028,956 of 2,097,152 application bytes. The matching `buildfs` command passed using 2,031,616 of 2,031,616 LittleFS bytes. `docs/MVP_ACCEPTANCE.md` records version `0.10.0`, build hashes, and the 19,253-byte reproducible web payload. - On 2026-09-08, the project owner confirmed the representative physical two-player playtests worked as expected with no gameplay defects or balance issues. The owner explicitly approved an evidence deviation for acceptance criterion 2: no match count or elapsed-time statistics are required. The approval, its scope, and the absence of a hard timer are recorded in `docs/PLAYTEST_REPORT.md`; criterion 12 of `MVP.md` is mapped as passing by that approved deviation in `docs/MVP_ACCEPTANCE.md`. Together with the deterministic balance evidence, complete mapped MVP evidence, and matching release-candidate images, this satisfies every Milestone 011 acceptance criterion. Milestone 011 is `COMPLETE`; Milestone 012 remains `NOT_STARTED`. ## Milestone 012 — Combinatorial humorous name suggestions **Status:** `COMPLETE` ### Objective Replace the small fixed set of humorous player-name suggestions with a random browser-side generator that combines a Russian adjective and noun in the manner of Docker's random container names. ### Scope - Define bounded Russian adjective and noun vocabularies suitable for the player-facing lobby. - Generate valid UTF-8 name suggestions by combining one adjective and one noun, with a hyphen between them. - Select an adjective and noun independently with browser-provided randomness each time the player requests a suggestion. - Skip combinations already shown as assigned in the authoritative public lobby projection. - Remove the obsolete suggestion WebSocket request and its gateway code. - Retain bounded frontend state and Russian player-facing error behavior, including a deterministic fallback if a random source is unavailable. - Add deterministic browser-side coverage by injecting the random source. ### Deliverables - Combinatorial humorous-name generator in the player application. - Browser tests covering the generator's selection rules. - Removed suggestion protocol and gateway implementation. ### Acceptance criteria 1. Each name suggestion has the form `adjective-noun`, uses valid UTF-8, and satisfies the lobby name-validation limits. 2. The generator provides substantially more combinations than the previous five fixed suggestions and independently randomizes its adjective and noun for each request. 3. A currently occupied player name from the authoritative lobby projection is never suggested. 4. Exhaustion displays a bounded Russian frontend error. 5. All existing host tests and the targeted browser generator tests pass. 6. The regenerated web assets remain reproducible and within the LittleFS payload budget. ### Evidence - The browser test `frontend name suggestions randomize both words and remain bounded` passed. With an injected source it proved independent adjective and noun selection, the 144-combination vocabulary, UTF-8 byte-length bounds, occupied-name skipping, exhaustion, and invalid-source fallback. - `node --check web/player.js` and `bash scripts/run_host_tests.sh` passed. The full host suite reported nine browser tests and web-asset reproducibility with SHA-256 `d90dd2cf7ebd4921109b7c7ecaa3e4ddbe021d8da6c8122731746e83ad19b367` using 19,928 of 1,572,864 allowed compressed bytes. - `pio run -e esp32-c6-devkitm-1 --target buildfs` passed and the matching LittleFS image uses 2,031,616 of 2,031,616 partition bytes. The previously recorded clean firmware build remains valid because this adjustment changes only browser source and regenerated assets. - Each button press now independently samples an adjective and noun through a bounded `crypto.getRandomValues` path, falling back to `Math.random` only when strong browser randomness is unavailable. It retains a deterministic, testable fallback for an invalid injected source and cannot alter any authoritative server state. This satisfies every revised Milestone 012 acceptance criterion. Milestone 012 is `COMPLETE`; Milestone 013 remains `NOT_STARTED`. ## Milestone 013 — Random humorous login headlines **Status:** `COMPLETE` ### Objective Make the login screen feel more lively by selecting a humorous Russian headline at random whenever a player opens the page. ### Scope - Expand the login screen's bounded set of player-facing Russian humorous headlines. - Select one headline using browser-provided randomness when the login page is initialized. - Provide a safe fallback when strong browser randomness is unavailable. - Preserve the existing responsive layout, accessibility semantics, and static asset-size constraints. - Add deterministic browser tests by injecting or stubbing the random source. ### Deliverables - Expanded login-headline vocabulary in the browser application. - Random headline selection with a bounded fallback. - Browser tests for selection, fallback behavior, and visible Russian copy. ### Acceptance criteria 1. The login screen has a substantially expanded set of humorous Russian headlines, all appropriate for player-facing UI. 2. On separate page initializations, the application selects a headline from the configured set using browser-provided randomness rather than a fixed sequence. 3. If the preferred random source is unavailable or fails, the login screen still displays a valid configured headline. 4. The selected headline remains readable and does not break the login layout at the responsive sizes defined in `docs/UI_STYLE.md`. 5. Targeted browser tests and `bash scripts/run_host_tests.sh` pass. 6. The regenerated web asset is deterministic for a given source tree and remains within the LittleFS payload budget. ### Evidence - The browser test `login headline selection is randomizable, Russian, and has a fallback` passed. It verifies eight locally authored, appropriate Russian two-line headlines, deterministic injected selection, fallback to the first valid headline for an invalid source, and initialization-time rendering into the existing heading structure. - `node --check web/player.js` and `bash scripts/run_host_tests.sh` passed. The full host suite reported ten browser tests and web-asset reproducibility with SHA-256 `ca5a637087748707367bd6ccec7885d0ff6ad20bd4713df528403e13f26fa540` using 20,281 of 1,572,864 allowed compressed bytes. - `pio run -e esp32-c6-devkitm-1 --target buildfs` passed; its LittleFS image uses 2,031,616 of 2,031,616 partition bytes. The layout preserves the existing `h1`, line break, and highlighted span, so no responsive or accessibility structure changed. - Each login-page initialization selects its headline through the bounded browser-random helper. Strong browser randomness is preferred, and the helper falls back safely when unavailable. This satisfies every Milestone 013 acceptance criterion. Milestone 013 is `COMPLETE`; Milestone 014 remains `NOT_STARTED`. ## Milestone 014 — Compact square maze and gameplay rebalance **Status:** `COMPLETE` ### Objective Reduce the default maze from `128 x 64` (8,192 cells) to a square `32 x 32` maze (1,024 cells), exactly one eighth of the original area, and rebalance the game so that its exploration, competition, and fairness requirements remain meaningful at the smaller scale. ### Scope - Change the canonical and implementation-default maze dimensions to `32 x 32` while retaining central symmetry and bounded generation. - Recalculate cycle density, exit separation, key-placement distance, landmarks, artifacts, and compass placement for the reduced area. - Preserve the rule that finding the key is no easier than the subsequent search for an exit under the project's deterministic balance metric. - Update player and observer presentation requirements for a square map. - Update protocol bounds, tests, diagnostics, and balance documentation that depend on the former `128 x 64` default. - Revalidate memory, static-asset, and firmware partition budgets. ### Deliverables - `32 x 32` authoritative maze generation and gameplay configuration. - Rebalanced object placement and deterministic balance thresholds. - Updated canonical gameplay, architecture, UI, generation, balance, and MVP acceptance documentation. - Updated host and browser tests for the compact square map. ### Acceptance criteria 1. The default authoritative map is exactly `32 x 32`, containing 1,024 cells, and no production default remains dependent on `128 x 64` dimensions. 2. A 1,024-seed deterministic sweep produces connected, centrally symmetric maps with valid starts, exits, objects, and no generation failure. 3. Rebalanced key and exit placement preserves the invariant that key discovery is no easier than the subsequent exit search under the documented deterministic metric. 4. Object counts and cycle density are scaled for the smaller map and remain bounded, symmetric where required, reachable, and free of collisions. 5. Player and observer views render the complete square map correctly at the responsive sizes defined in `docs/UI_STYLE.md`. 6. All host and browser tests pass, and firmware plus web images build within their partition budgets. 7. The project owner confirms through representative two-player testing that the compact map remains fair, understandable, and free of gameplay-blocking defects, or explicitly approves an evidence deviation. ### Evidence Implementation is complete and host-verified; representative two-player confirmation remains pending. - On 2026-09-08, `bash scripts/run_host_tests.sh` passed every health, Wi-Fi, NVS, provisioning, lobby/protocol, maze, game, disconnect-recovery, browser, and web-asset suite. The deterministic 1,024-seed sweep generated connected, centrally symmetric `32 x 32` maps without failure, reported cycles `19..19`, dead ends `74..128`, start-to-key distances `93..422`, key-to-exit distances `84..352`, start-to-exit distances `26..242`, at most two generation attempts, and preserved `start_to_key >= key_to_exit` for every seed. Fixed maze plus workspace storage measured 5,596 of 65,536 bytes. - The default configuration now uses 1,024 cells, two cross-half connection pairs, eight extra cycle pairs, 22-cell minimum exit separation, two landmark pairs, and one vision-artifact pair. The three-cell base view, ten-successful-move artifact effect, 150 ms movement cooldown, compass rule, and 200 ms draw window remain unchanged. Observer serialization passed with 1,024 cells, 12 objects, 88 bounded chunks, and a 490-byte largest chunk. - The browser tests passed with explicit `32 x 32` dimension bounds and a square-map fit scale. Web-asset reproduction reported SHA-256 `3461d20742e1999c1489465d7f3a68317d56b3acfc5ac5003341e1cf2d478eed` using 20,314 of 1,572,864 allowed compressed bytes. - A clean `pio run -e esp32-c6-devkitm-1` build passed for application version `0.14.0`, using 58,868 of 327,680 RAM bytes and 1,027,934 of 2,097,152 application bytes. `firmware.bin` measured 1,028,560 bytes with SHA-256 `428e7690bed50d30caf61aca174d838f00d7de1a603f14ea669e996e3d167123`. - `pio run -e esp32-c6-devkitm-1 --target buildfs` passed. The fixed LittleFS image measured 2,031,616 of 2,031,616 partition bytes with SHA-256 `467f0a07413c83bff5ea246b5063289eb61f8bf40b95d16d639b3241ceda8a9d`. - Acceptance criteria 1--6 have concrete evidence. Criterion 7 requires the project owner's representative two-player confirmation or an explicitly approved deviation, so Milestone 014 remains `IN_PROGRESS`. - On 2026-09-08, the project owner confirmed representative two-player testing of the compact `32 x 32` map: gameplay is fair and understandable, all expected flows work as intended, and no gameplay-blocking defect was found. Together with the deterministic sweep, browser, host, firmware, and filesystem evidence above, this satisfies every Milestone 014 acceptance criterion. Milestone 014 is `COMPLETE`; Milestone 015 remains `NOT_STARTED`. ## Milestone 015 — Pre-reader child-friendly game experience **Status:** `COMPLETE` ### Objective Redesign the mobile player experience so that children of early primary-school age and younger can understand and enjoy the game without needing to read, while preserving server authority, hidden-state protection, and all gameplay rules. ### Scope - Replace text-dependent status comprehension with large, distinct pictograms, shapes, colors, motion, and optional local sound cues. - Give each player a friendly visual character and color identity instead of relying on the labels `P1` and `P2`. - Enlarge and dynamically frame the visible maze so that the playable area is the dominant mobile-screen element, including when the player is near a map edge and receives fewer visible cells. - Add a large on-screen joystick designed for one-thumb control. Its movable circular handle follows the thumb within a bounded base, includes a forgiving dead zone, and returns visibly to the center when released. - Map joystick direction to the four existing orthogonal movement commands and map displacement magnitude to a bounded command-repeat rate: a small intentional displacement walks slowly, while a larger displacement moves at the fastest server-approved rate. The browser never bypasses the authoritative movement cooldown or predicts movement locally. - Retain swipe and keyboard input as equivalent optional alternatives without displaying the existing directional-arrow buttons during normal mobile play. - Introduce a short, replayable, wordless onboarding sequence that demonstrates movement, walls, keys, exits, compasses, vision artifacts, and meeting the other player through animation and guided interaction. - Communicate pickup, blocked movement, compass direction, enhanced vision, opened exits, victory, defeat, draw, pause, and reconnection with immediate icon-first animation and sound feedback. - Keep concise Russian text as an optional secondary aid for readers and accessibility technology, never as the only way to understand a required action or game state. - Provide visible mute and reduced-motion controls, avoid rapid flashes, and ensure that meaning never depends on color alone. - Preserve bounded local assets, offline operation, responsive behavior, and the existing authoritative command and projection contracts. ### Deliverables - Updated child-oriented visual language and mobile layout specification in `docs/UI_STYLE.md`. - Large icon-first player HUD, maze viewport, and touch-control design. - Friendly player characters and locally stored visual/audio feedback assets. - Wordless interactive onboarding that demonstrates dragging, holding, and releasing the joystick, plus a replay control. - Deterministic browser tests and viewport-based visual regression evidence. - Facilitated usability-test guide for pre-readers and early readers. ### Acceptance criteria 1. At the canonical `412 x 916` mobile viewport, the visible maze is the dominant element and uses the available play area without a large inactive void around the current projection. 2. Movement is possible through an always-visible on-screen joystick with a touch area of at least `160 x 160` CSS pixels. Its handle tracks a mouse, touch, or pointer drag, remains bounded within the base, and snaps to the center on release, cancellation, pause, disconnect, or loss of focus. 3. Joystick displacement maps monotonically to a documented bounded repeat rate: increasing displacement never produces slower movement, the dead zone sends no command, and maximum displacement cannot exceed the authoritative server movement limit. Direction is resolved to exactly one of the four existing orthogonal commands, with hysteresis preventing rapid direction flicker near diagonal boundaries. 4. A child can distinguish their character, carried key, enhanced vision, compass direction, exit state, blocked movement, and match result without reading text or recognizing `P1`/`P2`. 5. First-time onboarding demonstrates joystick drag distance, direction, release-to-stop, walls, and every required interaction through animation and guided input; it can be replayed and can be skipped by an adult. 6. Essential state uses at least two visual channels such as icon plus shape, motion, or pattern; color is never the sole signal. 7. Optional sound feedback has an obvious icon-only mute control, and reduced motion preserves all information without rapid flashing or disorienting effects. 8. Player-facing text remains Russian, semantic labels remain available to assistive technology, and the interface does not require reading for normal play. 9. Browser tests cover joystick dead-zone, displacement-to-rate mapping, direction hysteresis, release and cancellation, icon-driven state transitions, onboarding, mute, reduced motion, and the supported mobile viewport; all existing host tests continue to pass. 10. The compressed local web payload remains within the 1.5 MiB asset budget and the LittleFS partition, without internet or third-party runtime assets. 11. Facilitated observation with representative pre-readers confirms that they can discover the joystick, move slowly and quickly, stop reliably, react to a wall, recognize a key objective, and follow a compass cue without spoken reading instructions, or the project owner explicitly approves a documented evidence deviation. ### Evidence The implementation and automated verification are complete. The project owner approved the required evidence deviation after manual gameplay, as recorded below. - The player screen now uses circle and diamond character shapes instead of `P1`/`P2`, icon-and-shape ownership markers for keys and exits, dynamic projection sizing from 32 to 72 CSS pixels per cell, a 180 CSS-pixel joystick (172 on phones), and an icon-only visible mute, reduced-motion, and onboarding-replay control group. The mobile directional-button cluster stays hidden while keyboard and swipe input remain available. - The joystick clamps its handle to the circular base, has a 20-pixel dead zone and 18% axis hysteresis, emits only cardinal movement intentions, and schedules repeats from 360 to 170 ms. It resets on pointer release or cancellation, pause, disconnect, browser blur, and hidden-document events; the fastest local repeat remains above the 150 ms authoritative cooldown. - First active player projection now opens an eight-step, icon-first Russian walkthrough for drag, release, walls, key, vision, compass, exit, and the other player. It can be replayed or skipped locally. The new `docs/PRE_READER_USABILITY.md` records the final privacy-safe observation protocol without collecting child-identifying data. - On 2026-09-09, `bash scripts/run_host_tests.sh` passed every host suite, including 15 browser tests. The focused browser coverage verifies joystick cardinal direction and bounded travel, dead zone, monotonic repeat rate, hysteresis, dynamic mobile cell sizing, onboarding bounds, reset paths, mute and reduced-motion controls, local assets, Russian UI, and existing protocol/observer constraints. Web asset reproducibility reported SHA-256 `13f95418169fb38d277d86f3324a4dbc3fa8b5e5e2e9fb7bde9439aa2f7b75e4` using 23,950 of 1,572,864 allowed compressed bytes. - On 2026-09-09, `pio run -e esp32-c6-devkitm-1` passed with 58,868 of 327,680 RAM bytes and 1,027,934 of 2,097,152 application bytes. Its `firmware.bin` SHA-256 was `87cfffaaa9b119794707c9caedc3da7f3f015d540fc1149d74e7cc0a1ecd2841`. `pio run -e esp32-c6-devkitm-1 --target buildfs` passed; the fixed LittleFS image used 2,031,616 of 2,031,616 bytes and SHA-256 `ea6813e603d883a1f6b79a9f9efbd20e7d02f010052210716a89d7e863716415`. - On 2026-09-09, the project owner manually exercised the gameplay and confirmed that all host, firmware, and filesystem checks above pass. The owner also found the separate on-screen joystick imitation uncomfortable and insufficiently responsive for real play. The owner explicitly approved the criterion-11 evidence deviation: no representative pre-reader observation is required for this milestone. Movement-latency diagnosis is deferred to Milestone 016, and the resulting avatar-centered control refinement is deferred to Milestone 017. Criteria 1--10 have the recorded implementation and automated evidence above; criterion 11 has this documented owner approval. Milestone 015 is therefore `COMPLETE`. ## Milestone 016 — End-to-end movement latency diagnosis **Status:** `COMPLETE` ### Objective Measure and attribute the delay from a player's touch to the corresponding authoritative map update, determine whether the unacceptable delays originate in the local Wi-Fi network, browser input and rendering, ESP firmware, or their interaction under load, and produce evidence that selects the remediation for Milestone 017. ### Scope - Define separate latency intervals for local pointer feedback, browser command scheduling, WebSocket uplink, ESP receive/parse/game processing, outbound queueing and serialization, WebSocket downlink, complete-view assembly, DOM update, and the next painted frame. - Add bounded diagnostic correlation data and timing counters where existing metrics cannot isolate an interval. Diagnostics must not make browser time authoritative, expose hidden maze state, or create unbounded logs, queues, allocations, or protocol messages. - Measure ICMP and application-level round-trip baselines on the same local Wi-Fi path, including distribution and jitter rather than only an average. - Exercise accepted movement, blocked movement, direction changes, sustained movement, release, and cooldown rejection with two active players, first without observers and then at the six-session limit with four observers. - Compare WebSocket acknowledgement time with the time at which the matching authoritative view has been completely assembled and painted. Record view chunk count, socket buffered bytes, gateway queue depth, send duration, and browser parse, layout, and paint cost for the same test intervals. - Profile the current sequential per-session view broadcast and the browser's complete maze DOM replacement without changing their behavior during the baseline run. - Classify each observed delay by its dominant interval, document reproducible test conditions, and select bounded frontend, protocol, firmware, and/or network remediations for Milestone 017. ### Deliverables - A documented pointer-to-paint latency model with unambiguous measurement points and command/view correlation. - A repeatable local-device measurement procedure and bounded diagnostic instrumentation for browser and firmware. - Results for baseline and maximum supported session load, including latency percentiles, maxima, network jitter, queue high-water marks, view sizes, and browser rendering costs. - A root-cause report that ranks measured contributors and records the selected remediation scope and performance budget for Milestone 017. - Automated tests for correlation, bounded diagnostics, counter reset/wrap behavior, and preservation of authoritative and hidden-state contracts. ### Acceptance criteria 1. Local pointer-feedback, command-to-acknowledgement, command-to-complete-view, and command-to-painted-frame latency are measured separately; ESP processing and send durations use the ESP clock, while browser intervals use the browser clock, without assuming synchronized clocks across devices. 2. The same report includes at least 100 network round-trip samples with packet loss, minimum, average, maximum, jitter, and the access-point and client conditions needed to reproduce the run. 3. Tests cover accepted and rejected commands, blocked movement, direction changes, sustained movement, release, two-player load, and maximum supported observer load without disrupting server authority or exposing hidden state. 4. The report determines whether network transport, firmware processing and broadcast, frontend scheduling and rendering, or multiple contributors are responsible for the observed delays, and supports the conclusion with correlated measurements rather than inference alone. 5. Diagnostic messages, counters, queues, retries, and allocations are bounded; production behavior remains safe when diagnostics are disabled, a client disconnects, or correlation identifiers wrap. 6. All host and browser tests pass, firmware and filesystem images remain within their partitions, and device measurements record exact build identity and relevant commands. ### Evidence Implementation and initial device validation are complete. The following planning-time observations justify the milestone but do not satisfy its controlled measurement criteria: - On 2026-09-09, the project owner reported intermittent input delays up to approximately 500 ms and approved active browser and WebSocket measurements against the device at `192.168.1.50`. - A live `/diagnostics` sample with two connections and 24 accepted game commands reported no dropped, failed, malformed, oversized, or rate-limited messages; maximum handler time was 7,800 microseconds and maximum send time was 1,571 microseconds. This low-load sample does not measure end-to-end latency or rule out sequential broadcast delays under session load. - A 20-packet ICMP sample to `192.168.1.50` had zero loss but measured `60.673 / 147.866 / 246.995 ms` minimum/average/maximum round-trip time with `49.350 ms` deviation. The network alone therefore already exceeds the ideal 100 ms target in this setup, but correlation is still required before assigning the reported 500 ms delays. - Static inspection found that each accepted movement synchronously broadcasts authoritative view chunks to sessions, while the player browser waits for a complete view and replaces the maze DOM before the updated frame is visible. These are candidate contributors to measure, not yet established root causes. - The browser now stores at most 256 local movement samples and correlates each sent movement request with its acknowledgement's authoritative `stateSequence`, the complete matching view, and the next painted frame. The gateway includes that state sequence in accepted game acknowledgements; no browser timestamp is sent to the ESP and no hidden state is added to a player projection. `docs/M016_LATENCY_PROCEDURE.md` specifies the required controlled runs and interval boundaries. - On 2026-09-09, the focused browser test plus `bash scripts/run_host_tests.sh` passed with 16 browser tests, including bounded latency correlation. Firmware and filesystem builds passed: application usage was 1,027,966 of 2,097,152 bytes and the fixed LittleFS image used 2,031,616 of 2,031,616 bytes. - On 2026-09-09, the diagnostics firmware and filesystem image were flashed to the confirmed ESP32-C6FH4 on `/dev/ttyACM0`; the health endpoint reported `serving`. A six-session smoke run recorded 10 accepted commands. Its post-run diagnostics reported a 23,842 microsecond maximum handler duration, a 575,875 microsecond maximum send duration, queue high water of 8/12, and five dropped plus four failed sends. - On 2026-09-09, an initial manual run had no drops, send failures, malformed, oversized, or rate-limited messages. Before the run, diagnostics reported 8 accepted commands, a 10,779 microsecond maximum handler duration, and a 1,591 microsecond maximum send duration. Afterwards, it reported 165 accepted commands, a 9,800 microsecond maximum handler duration, a 1,572 microsecond maximum send duration, queue high water of 4/12, and no message drop or failure. One browser retained 65 correlated samples: its command-to-acknowledgement values ranged from 19 to 548 ms and its command-to-painted-view values ranged from 30 to 551 ms. An interrupted 18-packet ICMP sample had zero loss but measured `35.904 / 121.461 / 219.597 ms` minimum/average/maximum round trip with `51.715 ms` deviation. These partial data point to Wi-Fi transport and/or browser scheduling rather than the measured ESP handler or send path. - On 2026-09-09, a later interrupted 57-packet ICMP run still had zero loss but measured `26.403 / 114.039 / 343.368 ms` minimum/average/maximum round trip with `54.637 ms` deviation. A complete 100-request `/health` application-round-trip run measured 7.5 ms minimum, 186.7 ms average, 508.0 ms p95, 713.3 ms p99, and 985.4 ms maximum. The M010 six-session script again timed out waiting for the first player `join` response, so it produced no valid application-load samples. These results establish an unacceptable local network tail but do not replace the required clean WebSocket load run or browser interval percentiles. - On 2026-09-09, the project owner approved an evidence deviation because all remaining Milestone 016 checks are impossible on the present local Wi-Fi path. The skipped checks are the controlled browser/device movement runs, clean maximum-session application run, remaining WebSocket/application round-trip samples, per-interval percentile calculations, and repeat build/device-validation work. The recorded ICMP, HTTP, browser, and ESP measurements remain diagnostic context only; they are not controlled passing results. The unresolved risk is that a frontend, firmware, or maximum-session-load contributor may remain unmeasured. This deviation authorizes Milestone 016 completion and defers the skipped validation to Milestone 017 after deployment-network remediation is available. ## Milestone 017 — Avatar-centered drag steering and latency remediation **Status:** `NOT_STARTED` ### Objective Replace the separate on-screen joystick with direct dragging of the centered player avatar, keep the avatar's small local deflection as immediate wordless direction and speed feedback while the authoritative map moves underneath it, and apply the measured Milestone 016 remediations so normal local play feels immediate and never approaches the observed 500 ms delay. ### Scope - Make the visible centered player avatar the movement handle, backed by a generous transparent touch target that does not obscure nearby maze cells. - Keep the avatar visually near the viewport center. During a drag, offset it only within a small bounded radius in the drag direction; on release or any reset condition, animate it back to the exact center. - Convert drag angle to exactly one of the four existing cardinal movement intentions with a dead zone and diagonal hysteresis. Map displacement magnitude monotonically to a bounded movement cadence: a small intentional offset moves slowly and a larger offset approaches, but never exceeds, the authoritative 150 ms cooldown. - Move the maze beneath the centered avatar only when server-approved state is received. Local avatar deflection is input feedback and must never predict a successful cell transition, inventory change, visibility change, or result. - Coalesce rapidly changing movement intent and bound in-flight commands so a slow socket, delayed acknowledgement, direction reversal, or release cannot leave stale movement queued in the browser or gateway. - Apply the frontend, protocol, firmware, and/or deployment-network changes selected by Milestone 016. Optimize the measured bottlenecks, including view assembly, rendering, or broadcast only when supported by its evidence. - Preserve keyboard and swipe alternatives, observer read-only behavior, hidden-state protection, Russian accessibility labels, mute and reduced motion behavior, and all pause, disconnect, and focus-loss reset paths. - Update the canonical UI and pre-reader usability documents and validate the interaction on representative mobile touch hardware. ### Deliverables - Avatar-centered drag control with bounded deflection, direction hysteresis, displacement-based speed, release-to-center animation, and safe reset paths. - Authoritative map-follow behavior and bounded latest-intent command flow. - The evidence-selected latency optimizations across browser, protocol, firmware, and/or documented network setup. - Updated `docs/UI_STYLE.md`, `docs/PRE_READER_USABILITY.md`, Russian semantic labels, deterministic browser tests, host tests, and device test procedure. - Before-and-after latency report using the Milestone 016 measurement method. ### Acceptance criteria 1. At the canonical `412 x 916` viewport, touching the player avatar or its transparent target starts control without a separate joystick. The target is at least `96 x 96` CSS pixels, the visible avatar remains unobscured, and its center cannot move more than the documented small deflection radius. 2. Avatar deflection follows the pointer by the next rendered frame during normal operation, indicates the resolved direction and relative speed without text, and returns to the exact center on release, cancellation, pause, disconnect, blur, hidden-document events, and lost pointer capture. 3. The map moves beneath the near-centered avatar only from server-approved projections. The browser never predicts maze position, collision outcome, inventory, visibility, timers, or match results. 4. Deflection magnitude maps monotonically to a documented bounded cadence, the dead zone sends no movement, diagonal hysteresis prevents direction flicker, and the fastest cadence cannot bypass the 150 ms server cooldown. 5. At most one bounded current movement intention is awaiting completion or replacement per player. Direction changes and release discard stale local intent so delayed network delivery cannot create a long client-side command backlog. 6. In the controlled local Wi-Fi test conditions established by Milestone 016, accepted input-to-authoritative-painted-update latency is at most 100 ms at `p95` and at most 150 ms for the maximum sample in both the two-player case and the two-player/four-observer case. No accepted sample reaches 500 ms. 7. If the deployment network cannot meet criterion 6 before application work, the documented network remediation is applied and the full test is repeated; application latency is reported separately so network improvements cannot conceal a frontend or firmware regression. 8. Browser tests cover pointer capture, touch targeting, bounded offset, dead-zone and speed mapping, diagonal hysteresis, stale-intent coalescing, every reset path, authoritative map motion, keyboard and swipe alternatives, reduced motion, and supported mobile viewports. 9. All host tests pass, firmware and web images remain within partition budgets, and the latency instrumentation records no dropped messages, unbounded queue growth, hidden-state disclosure, or observer mutation under maximum supported session load. 10. The project owner confirms in representative mobile gameplay that a child can discover movement by dragging the avatar, understand direction and relative speed from its deflection, stop reliably, and experience responsive control, or explicitly approves a documented evidence deviation. ### Evidence Not started. This milestone records the project owner's 2026-09-09 decisions: the child drags the centered player icon; the icon deflects slightly as the control handle while the authoritative map moves below it; displacement controls speed; approximately 100 ms is ideal, 150 ms is acceptable, and delays near 500 ms are unacceptable. Implementation must wait for Milestone 016 to identify and prioritize the actual latency contributors.