222 KiB
maze32 Implementation Plan
Plan rules
- Milestones may be planned and added in batches.
- Milestone numbering starts at 1 and increases without gaps. Headings use the
zero-padded form
Milestone 001,Milestone 002, and so on. - Milestones are executed strictly in ascending numerical order. Work on a
milestone may begin only after every preceding milestone is
COMPLETE. - At most one milestone may have status
IN_PROGRESS. - 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.
- Every milestone contains an objective, scope, deliverables, acceptance criteria, evidence, and status.
- Allowed statuses are
NOT_STARTED,IN_PROGRESS,BLOCKED, andCOMPLETE. - A milestone may be marked
COMPLETEonly when every acceptance criterion is satisfied and concrete evidence is recorded in the milestone. - 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.
- A
NOT_STARTEDmilestone explicitly records that completion evidence is not yet available. Expected evidence may be listed in advance. - If later work invalidates recorded evidence, reopen the affected milestone and change its status before continuing.
- 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.mdARCHITECTURE.mdPLANS.mdAGENTS.mddocs/BOARD_PASSPORT.mdplatformio.inipartitions.csvsdkconfig.defaults
Acceptance criteria
- All canonical project documentation is in English.
MVP.mdcaptures all agreed roles, gameplay rules, networking behavior, technical constraints, exclusions, and acceptance criteria.ARCHITECTURE.mddefines server authority, component boundaries, state machines, role-specific projection, protocol direction, storage, and verification strategy.- The build profile pins PlatformIO
espressif32@7.0.1, ESP-IDF, the compatibleesp32-c6-devkitm-1profile, and 4 MB Flash. - The partition layout is contiguous and ends exactly at
0x400000. sdkconfig.defaultsexplicitly selects 4 MB Flash and 16 LwIP sockets.- The board passport matches the canonical configuration and does not claim an unverified physical board model, GPIO mapping, LED, or PSRAM.
- No file under
sources/is modified. - PlatformIO can parse and resolve the canonical project configuration.
Evidence
MVP.mdcontains the complete English product and acceptance specification.ARCHITECTURE.mdcontains the English runtime, component, state-machine, protocol, memory, failure, and verification contracts.- On 2026-09-03,
/home/sasa/.platformio/penv/bin/pio project configexited successfully and resolved environmentesp32-c6-devkitm-1withespressif32@7.0.1, ESP-IDF, explicit 4 MB build/upload sizes,partitions.csv, LittleFS, andsdkconfig.defaults. PlatformIO subsequently emitted a telemetry-cache permission warning during process shutdown; it did not prevent configuration parsing. - On 2026-09-03, an
awkcontinuity and end-address check overpartitions.csvreported the factory partition ending at0x210000, the LittleFS partition ending at0x400000, and no gaps or overlaps from NVS through the end of Flash. - On 2026-09-03,
rgconfirmedCONFIG_ESPTOOLPY_FLASHSIZE_4MB=yandCONFIG_LWIP_MAX_SOCKETS=16in bothsdkconfig.defaultsand the generated ESP-IDF configuration. docs/BOARD_PASSPORT.mdrecords 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 undersources/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
- A clean PlatformIO build succeeds with the locked environment.
- The application image fits the 2 MiB factory partition.
- The LittleFS image fits the 1,984 KiB filesystem partition.
- Web-asset generation is deterministic and does not require manual edits.
- 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 configparsed the updated configuration successfully with the locked platform, ESP-IDF, 4 MB Flash, root partition table, LittleFS, pinnedesp_littlefs, bounded LittleFS settings, and the build hook. - On 2026-09-03,
./scripts/run_host_tests.shreportedhealth state tests: PASSandweb asset reproducibility: PASS (611eacdb06b81bdb064a9074352ff4c6f82d3af3ad3d01e65705387744b86b21). - After
/home/sasa/.platformio/penv/bin/pio run --target clean, running/home/sasa/.platformio/penv/bin/pio runon 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 reportedfirmware.bin uses 350864/2097152 bytes in partition factory. - Running
/home/sasa/.platformio/penv/bin/pio run --target buildfson 2026-09-03 regenerateddata/index.html.gz, built the LittleFS image, and reported[SUCCESS]. The independent build hook reportedlittlefs.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 remained950c60077c381d4484c11b43762bc5c8378587b6cd91924c344cfbc406771bb9. 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.binfound firmware version0.2.0and 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/ttyACM0wrote and hash-verified the 2,031,616-byte LittleFS image at0x210000. A subsequent application upload identified the target asESP32-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, andHealth 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
- First boot exposes the fallback provisioning interface.
- Valid credentials are stored in NVS and used after reboot.
- Failed station connection returns to a usable fallback access point.
- Invalid requests cannot corrupt stored configuration.
- Network reconfiguration cannot leave an active game silently running.
Evidence
- On 2026-09-03,
./scripts/run_host_tests.shreportedhealth state tests: PASS,wifi configuration tests: PASS,configuration NVS tests: PASS,provisioning protocol tests: PASS, andweb 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-1completed 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 reportedfirmware.bin uses 985920/2097152 bytes in partition factory. /home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1 --target buildfscompleted with[SUCCESS]and reportedlittlefs.bin uses 2031616/2031616 bytes in partition littlefs. The image contains deterministic compressed application and provisioning pages; its SHA-256 wasf3e529542737221d46146f46ff57eb3a88a1b3c5b25c9f4298b9d3e3e22a7d3a.- 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 at192.168.4.1,Provisioning endpoints ready, andHealth state: serving. - The isolated
milestone-003-device-testenvironment exercised the real NVS and network manager without accessing local Wi-Fi secrets. One continuous target log reportedcredentials saved; restarting, reset reasonSW_CPU,credentials survived restart, station connection to the persisted test SSID with three bounded retries,Station connection timed out or exhausted retries; enabling fallback, andfailed 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, received192.168.1.50, reportedNetwork state: station_connectedandStation connected: IP=192.168.1.50, registered the HTTP and provisioning routes, and reachedHealth state: serving. - Live requests over
Mazda7returned{"state":"station_connected",...,"ip":"192.168.1.50"}from/api/wifi/status, a healthy version0.3.0response 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 toMazda7, 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-1countdown.
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
- Two players and four observers can connect concurrently within fixed limits.
- Duplicate player names and excess role claims are rejected safely.
- Page refresh restores the same session and player slot.
- Only Player 1 can start, and only with both player slots occupied.
- Malformed, oversized, unauthorized, and stale messages do not mutate state.
- Both players receive one authoritative countdown and activation instant.
Evidence
- On 2026-09-04,
./scripts/run_host_tests.shreportedhealth state tests: PASS,wifi configuration tests: PASS,configuration NVS tests: PASS,provisioning protocol tests: PASS,lobby and protocol tests: PASS, andweb 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/ttyACM0completed 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 reportedfirmware.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 buildfscompleted with[SUCCESS]and reportedlittlefs.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
Mazda7access point was unavailable. A liveGET /healthreturned{"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 --verifyreportedmalformed 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), andoversized 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 64map. - 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
- The same configuration and seed always produce the same valid maze.
- Every tested map is fully reachable and rotationally symmetric.
- Every tested map contains cycles and multiple cross-half connections.
- Player-relative starts and required objects have equal symmetric metrics.
- Keys are outside their owners' initial views.
- 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 configexited successfully and resolved the production, Milestone 003 device-test, and isolatedmilestone-005-device-testenvironments from the lockedespressif32@7.0.1, ESP-IDF, 4 MB Flash, root partition, and root sdkconfig baseline. - On 2026-09-04,
./scripts/run_host_tests.shreported all preceding tests asPASS,maze bounded regeneration failure: PASS (3/3 attempts), andmaze seed sweep: PASS (1024 seeds, combined hash=a264b7d133f8bad4). Generating every default128 x 64seed 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,undefinedandASAN_OPTIONS=detect_leaks=0repeated the 1,024-seed suite without an address or undefined-behavior finding; leak detection was disabled because the sandbox executes underptrace. - 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
7396c6426181da3eand printed a complete16 x 8ASCII maze with walls, opposite starts, symmetric keys and exits, compasses, landmarks, and four vision artifacts.docs/MAZE_GENERATION.mdrecords the construction, validation, configuration, reproducibility, and resource contracts. /home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1completed 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 reportedfirmware.bin uses 1005904/2097152 bytes in partition factory./home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1 --target buildfsalso completed with[SUCCESS]and reportedlittlefs.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 seed1513226245generated in one attempt with hash485280a746e8900cand loggedcells=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, andartifacts=1085/1085. - The same physical run logged
fixed=42332/65536 bytes, runtime=183/250 ms, heap_delta=0followed byM005 physical maze test: PASS. Production firmware was restored and hash-verified afterward; a liveGET /healthover 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
- Accepted inputs move exactly one valid orthogonal cell.
- Invalid, premature, or rate-limited commands do not mutate state.
- Both players may occupy the same cell and generate one meeting event.
- Player payloads contain no hidden cells, objects, or stale opponent state.
- Observer payloads contain the complete current state.
- 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 configexited successfully and resolved the production and isolatedmilestone-006-device-testenvironments fromespressif32@7.0.1, ESP-IDF, theesp32-c6-devkitm-1profile, 4 MB Flash, and the root partition and sdkconfig files. ./scripts/run_host_tests.shpassed every preceding suite and reportedgame 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), andgame visibility and payload security: PASS (r3=49/16, r6=64/49, observer=64). The same suite serialized a complete default128 x 64observer snapshot as 345 chunks and reported a maximum chunk size of 405 bytes against the 512-byte limit. A separate build with-fsanitize=address,undefinedrepeated the game-core suite without an address or undefined-behavior finding; leak detection was disabled because the sandbox executes underptrace.- 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 8fixture. 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-1completed with[SUCCESS]and reported 47,508 of 327,680 RAM bytes and 1,005,274 of 2,097,152 application bytes in use. The productionfirmware.binmeasured 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 buildfscompleted with[SUCCESS]; the independent partition check reportedlittlefs.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 reportedfirmware.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/ttyACM0identified 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=0andM006 physical game test: PASS. - The production image was restored and hash-verified after the isolated test.
A live
GET /healthreturned{"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
- Inventory and ground-key state never duplicate or lose a key.
- Meeting behavior returns or swaps carried keys atomically.
- A player cannot use the opponent's key or exit to win.
- Vision effects last exactly ten successful moves and ignore failed moves.
- Compass activation points toward only the activating player's own exit.
- Victory requires the outward move, and exits within 200 ms produce a draw.
Evidence
bash scripts/run_host_tests.shcompleted successfully. Its new deterministic mechanics suite reportedgame 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, andgame 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 64observer 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 underptrace. PLATFORMIO_SETTING_ENABLE_TELEMETRY=no /home/sasa/.platformio/penv/bin/pio project configparsed the lockedespressif32@7.0.1, ESP-IDF,esp32-c6-devkitm-1,partitions.csv, andsdkconfig.defaultsconfiguration, including the isolatedmilestone-007-device-testenvironment./home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1completed with[SUCCESS], reporting 47,508 of 327,680 RAM bytes and 1,005,274 of 2,097,152 application bytes; the independent partition check reportedfirmware.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, withfirmware.bin uses 1020800/2097152 bytes in partition factory./home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1 --target buildfscompleted with[SUCCESS]; the partition check reportedlittlefs.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 loggedM007 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, andM007 physical mechanics test: PASS.- The normal firmware was restored and hash-verified after the isolated test.
A live
GET /healthreturned{"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 remainsNOT_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
- Keyboard and swipe actions produce equivalent server commands.
- The client renders only state received in the player projection.
- Hidden and previously seen cells are not retained on screen.
- Inventory, effects, hints, pause, and results are unambiguous.
- Refresh restores the active player without changing position or inventory.
- The player flow remains usable without page scrolling at a
412 x 916CSS-pixel viewport and at representative tablet and desktop viewports. - Player-facing UI copy and browser errors are in Russian while protocol and system-log text remain in English.
- The application requires no runtime internet access or third-party UI dependency.
- The complete compressed web payload is no larger than 1.5 MiB and fits the unchanged LittleFS partition.
- 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.shcompleted 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 underptrace.python3 scripts/verify_web_assets.pyreportedweb asset reproducibility: PASSwith digestf321d5bb1b4ad8cfcdf306c5a5e696f3b2b00b54792c727fd639671e585b573dand 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.jsandxmllint --noout web/icons.svgalso completed successfully.pio run -e esp32-c6-devkitm-1completed with[SUCCESS], reporting 47,508 of 327,680 RAM bytes and 1,005,660 of 2,097,152 application bytes. The independent partition check reportedfirmware.bin uses 1006288/2097152 bytes in partition factory.pio run -e esp32-c6-devkitm-1 --target buildfscompleted with[SUCCESS]; the independent partition check reportedlittlefs.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/ttyACM0and the correspondinguploadfscommand both completed with[SUCCESS]and hash-verified every write. The ESP32-C6FH4 revision 0.2 boot log reported application version0.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 remainedk49, so the device correctly entered itsmaze32-F7B9fallback AP while the host remained onMazda7; 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-256606b98e11cbc479bf11bffa9a38d62ddfebce0689f0e3ebf148521190a032fd5;cmpreturned 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 availableand 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_PROGRESStoBLOCKED; 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 916phone 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 isCOMPLETE; Milestone 009 remainsNOT_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
- Four observers can view the complete live state without control authority.
- Observer disconnects do not pause or mutate a match.
- Either player disconnect immediately pauses gameplay and effect progression.
- Reconnection within 60 seconds restores and resumes the same state.
- Reconnection timeout ends the match in a draw.
- Observer attempts to send mutation commands are rejected and audited.
- Observer mode works at
1024 x 768CSS 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.shcompleted 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 returnedread_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 768notice. Web asset reproducibility passed with SHA-256e8d097c7aa1a56cf84748078176b50cc8e2718082edfda35625b6d4436a1fd67and 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 ofscripts/ws_protocol_client.py, andgit diff --checkalso passed. /home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1completed with[SUCCESS], reporting 124,148 of 327,680 RAM bytes and 1,023,742 of 2,097,152 application bytes. The independent partition check reportedfirmware.bin uses 1024368/2097152 bytes in partition factory. The correspondingbuildfscommand completed with[SUCCESS]and reportedlittlefs.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 availableand 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 768and 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 isCOMPLETE; Milestone 010 remainsNOT_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
- Application and filesystem images fit their partitions with recorded margin.
- Six simultaneous clients remain connected and responsive on target hardware.
- Slow or malformed clients cannot exhaust memory or block player processing.
- Heap and largest free block remain stable across repeated matches.
- Station reconnect, provisioning fallback, and browser recovery work on the physical device.
- No test relies on PSRAM or an unverified GPIO.
Evidence
/home/sasa/.platformio/penv/bin/pio runcompleted 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.binmeasured 1,029,232 bytes in the 2,097,152-byte factory partition, leaving 1,067,920 bytes. The ELF contains application version0.10.0; the authoritative gateway's fixed static allocation measured 86,840 bytes./home/sasa/.platformio/penv/bin/pio run -t buildfscompleted 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-2568f3207a208c26aa9f0faff0b0fd8db8e8f8e44537bd6db2c0f914ca5f352920a.bash scripts/run_host_tests.shpassed 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 2is 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 indocs/INTEGRATION_TESTING.mdwithout requiring the development host to leave Mazda7.- Physical evidence is still pending:
/dev/ttyACM0was 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 remainsIN_PROGRESS. - On 2026-09-07, a further target-availability check found no
/dev/ttyACM0,/dev/ttyUSB0, or/dev/ttyUSB1device node. The documented station targethttp://192.168.1.50/healthalso timed out after three seconds. Physical acceptance criteria 2--5 remain pending; Milestone 010 isIN_PROGRESS. - On 2026-09-08, host-level USB access found the ESP32-C6 at
/dev/ttyACM0(USB-Serial/JTAG, MAC58:e6:c5:18:f7:b8). Production firmware and LittleFS uploads completed with verified hashes. Boot logs confirmed app version0.10.0, 4 MB partitions, LittleFS mount, station IP192.168.1.50, all routes, and healthserving. - 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: PASSwith four complete 8,192-cell maps andM010 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 2against 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/healthresponse wasservingfor version0.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 isIN_PROGRESS. - On 2026-09-08,
./scripts/run_host_tests.shpassed 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 ranpio 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, andpio 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/statusreturned{"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 isCOMPLETE; Milestone 011 remainsNOT_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
- Key discovery is demonstrably no easier than the subsequent exit search under the agreed metrics and playtest evidence.
- Representative completed matches normally target approximately five minutes without a hard match timer.
- Every acceptance criterion in
MVP.mdhas recorded passing evidence or an explicitly approved deviation. - The final firmware and web images build reproducibly and fit the target.
- 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.shpassed its health, Wi-Fi, NVS, provisioning, lobby/protocol, maze, game, disconnect-recovery, browser, and web-asset suites. The locked 1,024-seed sweep reported cycles133..133, dead ends704..854, start-to-key distance416..1442, and key-to-exit distance346..1244; every generated map satisfied the key-distance balance invariant.pio run -e esp32-c6-devkitm-1passed using 124,276 of 327,680 RAM bytes and 1,028,956 of 2,097,152 application bytes. The matchingbuildfscommand passed using 2,031,616 of 2,031,616 LittleFS bytes.docs/MVP_ACCEPTANCE.mdrecords version0.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 ofMVP.mdis mapped as passing by that approved deviation indocs/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 isCOMPLETE; Milestone 012 remainsNOT_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
- Each name suggestion has the form
adjective-noun, uses valid UTF-8, and satisfies the lobby name-validation limits. - The generator provides substantially more combinations than the previous five fixed suggestions and independently randomizes its adjective and noun for each request.
- A currently occupied player name from the authoritative lobby projection is never suggested.
- Exhaustion displays a bounded Russian frontend error.
- All existing host tests and the targeted browser generator tests pass.
- The regenerated web assets remain reproducible and within the LittleFS payload budget.
Evidence
- The browser test
frontend name suggestions randomize both words and remain boundedpassed. 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.jsandbash scripts/run_host_tests.shpassed. The full host suite reported nine browser tests and web-asset reproducibility with SHA-256d90dd2cf7ebd4921109b7c7ecaa3e4ddbe021d8da6c8122731746e83ad19b367using 19,928 of 1,572,864 allowed compressed bytes.pio run -e esp32-c6-devkitm-1 --target buildfspassed 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.getRandomValuespath, falling back toMath.randomonly 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 isCOMPLETE; Milestone 013 remainsNOT_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
- The login screen has a substantially expanded set of humorous Russian headlines, all appropriate for player-facing UI.
- On separate page initializations, the application selects a headline from the configured set using browser-provided randomness rather than a fixed sequence.
- If the preferred random source is unavailable or fails, the login screen still displays a valid configured headline.
- The selected headline remains readable and does not break the login layout
at the responsive sizes defined in
docs/UI_STYLE.md. - Targeted browser tests and
bash scripts/run_host_tests.shpass. - 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 fallbackpassed. 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.jsandbash scripts/run_host_tests.shpassed. The full host suite reported ten browser tests and web-asset reproducibility with SHA-256ca5a637087748707367bd6ccec7885d0ff6ad20bd4713df528403e13f26fa540using 20,281 of 1,572,864 allowed compressed bytes.pio run -e esp32-c6-devkitm-1 --target buildfspassed; its LittleFS image uses 2,031,616 of 2,031,616 partition bytes. The layout preserves the existingh1, 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 remainsNOT_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 32while 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 64default. - Revalidate memory, static-asset, and firmware partition budgets.
Deliverables
32 x 32authoritative 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
- The default authoritative map is exactly
32 x 32, containing 1,024 cells, and no production default remains dependent on128 x 64dimensions. - A 1,024-seed deterministic sweep produces connected, centrally symmetric maps with valid starts, exits, objects, and no generation failure.
- Rebalanced key and exit placement preserves the invariant that key discovery is no easier than the subsequent exit search under the documented deterministic metric.
- Object counts and cycle density are scaled for the smaller map and remain bounded, symmetric where required, reachable, and free of collisions.
- Player and observer views render the complete square map correctly at the
responsive sizes defined in
docs/UI_STYLE.md. - All host and browser tests pass, and firmware plus web images build within their partition budgets.
- 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.shpassed 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 symmetric32 x 32maps without failure, reported cycles19..19, dead ends74..128, start-to-key distances93..422, key-to-exit distances84..352, start-to-exit distances26..242, at most two generation attempts, and preservedstart_to_key >= key_to_exitfor 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 32dimension bounds and a square-map fit scale. Web-asset reproduction reported SHA-2563461d20742e1999c1489465d7f3a68317d56b3acfc5ac5003341e1cf2d478eedusing 20,314 of 1,572,864 allowed compressed bytes. - A clean
pio run -e esp32-c6-devkitm-1build passed for application version0.14.0, using 58,868 of 327,680 RAM bytes and 1,027,934 of 2,097,152 application bytes.firmware.binmeasured 1,028,560 bytes with SHA-256428e7690bed50d30caf61aca174d838f00d7de1a603f14ea669e996e3d167123. pio run -e esp32-c6-devkitm-1 --target buildfspassed. The fixed LittleFS image measured 2,031,616 of 2,031,616 partition bytes with SHA-256467f0a07413c83bff5ea246b5063289eb61f8bf40b95d16d639b3241ceda8a9d.- 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 32map: 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 isCOMPLETE; Milestone 015 remainsNOT_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
P1andP2. - 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
- At the canonical
412 x 916mobile viewport, the visible maze is the dominant element and uses the available play area without a large inactive void around the current projection. - Movement is possible through an always-visible on-screen joystick with a
touch area of at least
160 x 160CSS 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. - 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.
- 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. - 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.
- Essential state uses at least two visual channels such as icon plus shape, motion, or pattern; color is never the sole signal.
- Optional sound feedback has an obvious icon-only mute control, and reduced motion preserves all information without rapid flashing or disorienting effects.
- Player-facing text remains Russian, semantic labels remain available to assistive technology, and the interface does not require reading for normal play.
- 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.
- The compressed local web payload remains within the 1.5 MiB asset budget and the LittleFS partition, without internet or third-party runtime assets.
- 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.mdrecords the final privacy-safe observation protocol without collecting child-identifying data. - On 2026-09-09,
bash scripts/run_host_tests.shpassed 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-25613f95418169fb38d277d86f3324a4dbc3fa8b5e5e2e9fb7bde9439aa2f7b75e4using 23,950 of 1,572,864 allowed compressed bytes. - On 2026-09-09,
pio run -e esp32-c6-devkitm-1passed with 58,868 of 327,680 RAM bytes and 1,027,934 of 2,097,152 application bytes. Itsfirmware.binSHA-256 was87cfffaaa9b119794707c9caedc3da7f3f015d540fc1149d74e7cc0a1ecd2841.pio run -e esp32-c6-devkitm-1 --target buildfspassed; the fixed LittleFS image used 2,031,616 of 2,031,616 bytes and SHA-256ea6813e603d883a1f6b79a9f9efbd20e7d02f010052210716a89d7e863716415. - 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
- 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.
- 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.
- 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.
- 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.
- Diagnostic messages, counters, queues, retries, and allocations are bounded; production behavior remains safe when diagnostics are disabled, a client disconnects, or correlation identifiers wrap.
- 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
/diagnosticssample 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.50had zero loss but measured60.673 / 147.866 / 246.995 msminimum/average/maximum round-trip time with49.350 msdeviation. 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.mdspecifies the required controlled runs and interval boundaries. - On 2026-09-09, the focused browser test plus
bash scripts/run_host_tests.shpassed 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 reportedserving. 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 msminimum/average/maximum round trip with51.715 msdeviation. 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 msminimum/average/maximum round trip with54.637 msdeviation. A complete 100-request/healthapplication-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 playerjoinresponse, 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: COMPLETE
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
- At the canonical
412 x 916viewport, touching the player avatar or its transparent target starts control without a separate joystick. The target is at least96 x 96CSS pixels, the visible avatar remains unobscured, and its center cannot move more than the documented small deflection radius. - 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.
- 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.
- 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.
- 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.
- 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
p95and 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. - 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.
- 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.
- 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.
- 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
Implementation and automated validation are in progress. 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.
- On 2026-09-09, the browser control was changed from a separate joystick to a
96 by 96 CSS-pixel transparent target around the centered avatar. The visible
avatar deflects at most 28 CSS pixels, uses a 12-pixel dead zone and diagonal
hysteresis, and schedules cardinal repeats from 360 ms to 170 ms. It resets
on release, cancellation, lost pointer capture, pause, disconnect, blur, and
hidden-document events.
LatestIntentFlowkeeps one sent move in flight and retains at most one replacement intention; release discards the replacement. The map continues to render only complete server-authoritative projections. - On 2026-09-09,
node --test test/web/test_player_app.jspassed all 17 tests, including avatar bounds, cadence, direction hysteresis, latest-intent coalescing, reset paths, authoritative projections, keyboard/swipe alternatives, observer immutability, and bounded diagnostics.bash scripts/run_host_tests.shpassed all host tests and deterministic web-asset verification (2ca96e65b3aa06385028c420c7e605b2e422f7d0c57cae2d81e277a53ecdf6b3, 25,121 of 1,572,864 allowed compressed bytes). - On 2026-09-09,
/home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1passed with 58,868 of 327,680 RAM bytes and 1,027,966 of 2,097,152 application bytes./home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1 --target buildfspassed and reported a fixed LittleFS image of 2,031,616 of 2,031,616 bytes. - On 2026-09-09, an edge-projection correction translated the received maze so
the authoritative
selfcell remains below the centered avatar. Drag displacement now starts at pointer-down, preventing a touch near the transparent target edge from selecting an unintended direction.bash scripts/run_host_tests.shpassed with 18 browser tests and deterministic web-asset verification (907d0323bd48120dd51034014df6bb21de375631adf9559af88d16a52f9958cf, 25,346 of 1,572,864 allowed compressed bytes). Firmware and filesystem rebuilds passed with 58,868 of 327,680 RAM bytes, 1,027,966 of 2,097,152 application bytes, and a 2,031,616-byte LittleFS image.
Milestone 018 — Mobile directional controls and viewport refinement
Status: COMPLETE
Objective
Add a familiar, always-visible four-direction control cluster to the mobile player view and refine the compact layout so a player can move without covering the centered avatar or the most useful part of the maze with their finger.
Scope
- Before implementation, reconcile
MVP.md,ARCHITECTURE.md, anddocs/UI_STYLE.mdwith the project owner's decision to show directional buttons during normal phone play. This explicitly supersedes the current phone-specific rule that hides the directional-button cluster. - Present four separate up, down, left, and right buttons as a conventional cross-shaped directional pad in the lower-right control zone. Keep the center of the cross inactive so diagonal input cannot be implied.
- Give every direction a large, distinct touch target, Russian accessible label, and visible normal, pressed, disabled, and pending states. Prevent browser scrolling, text selection, double-tap zoom, and stuck input during normal button use.
- Send exactly one of the four existing orthogonal movement intentions from each button interaction. Reuse the bounded latest-intent flow and server cooldown; the browser must not predict movement or create a second movement authority path.
- Retain avatar drag, directional swipe, and keyboard movement as optional equivalent inputs. Directional buttons become the obvious default touch control without removing the alternatives completed in earlier milestones.
- Rebalance the compact player HUD, maze viewport, key-drop action, secondary controls, and transient notices so the map remains dominant and the player, nearby maze cells, and important feedback remain visible while a thumb rests on the directional pad.
- Keep the directional pad and key-drop action in separate lower-screen zones, respect display safe areas and handed reach, and avoid overlap between movement, drop-key, mute, reduced-motion, onboarding, or connection controls.
- Eliminate page-level scrolling and accidental overflow in supported portrait
and landscape player layouts, including the live approximately
412 x 915phone viewport as well as the canonical412 x 916validation viewport. - Preserve Russian player-facing copy, observer read-only behavior, hidden-state protection, local assets, reduced-motion behavior, and the existing firmware and LittleFS budgets.
Deliverables
- Updated mobile-control and responsive-layout contracts in
MVP.md,ARCHITECTURE.md, anddocs/UI_STYLE.md. - Responsive four-button directional pad integrated with the existing bounded movement-intent path.
- Refined compact HUD, maze, key-drop, utility-control, and notice layout for portrait and landscape mobile play.
- Russian accessible names and complete visual interaction states for all four direction buttons.
- Deterministic browser tests and representative mobile-browser visual and touch verification evidence.
Acceptance criteria
- At
412 x 915and412 x 916CSS pixels in portrait orientation, all four directional buttons, the key-drop action, essential player status, and the maze are visible without page-level horizontal or vertical scrolling. - The directional buttons form an immediately recognizable cross with a
non-interactive center. Each visible button has a touch target of at least
48 x 48CSS pixels, remains inside the display safe area, and does not overlap another control at any supported viewport. - A thumb operating any directional button does not cover the centered player avatar. The current cell and enough adjacent maze context to understand the next orthogonal move remain visible during button use.
- Pressing or holding up, down, left, or right produces only the matching existing cardinal movement intention through the bounded latest-intent flow. Release, cancellation, pause, disconnect, blur, hidden-document events, and lost pointer capture stop or clear button-generated intent.
- Button input obeys the authoritative movement cooldown, never predicts a successful move, and remains behaviorally equivalent to avatar drag, swipe, and keyboard input for accepted and rejected movement.
- Normal, pressed, disabled, and pending states are visually distinct without
relying on color alone. The buttons expose the Russian accessible labels
Двигаться вверх,Двигаться вниз,Двигаться влево, andДвигаться вправо. - Button use does not cause page scrolling, text selection, double-tap zoom, a context menu, diagonal commands, duplicate command streams, or movement that continues after release.
- The refined HUD and overlays keep the map as the dominant screen region; key-drop, mute, reduced-motion, onboarding, connection state, errors, and transient notices remain reachable and do not obscure one another in portrait or landscape orientation.
- Browser tests cover all four directions, tap and hold behavior, pointer and touch cancellation, reset paths, cooldown and latest-intent bounds, coexistence with drag/swipe/keyboard input, accessibility labels, safe-area layout, control overlap, and zero page overflow at supported viewports.
- All host tests pass, compressed web assets remain within the 1.5 MiB budget and LittleFS partition, firmware remains within the factory partition, and no player projection or observer-authority contract changes.
- The project owner confirms on a representative mobile browser that the four buttons are comfortable and familiar, the central play area stays readable while moving, every essential control fits on screen, and normal play no longer requires dragging the avatar, or explicitly approves a documented evidence deviation.
Evidence
Implementation and automated validation are in progress. The following planning-time observation motivates the milestone:
- On 2026-09-10, the project owner reported that dragging the centered player
is unfamiliar and uncomfortable because the finger hides useful maze space,
and requested visible up, down, left, and right buttons plus a better-fitting
mobile layout. Read-only inspection of the live player page at approximately
412 x 915CSS pixels found a96 x 96centered avatar drag target, four directional buttons present but hidden, and a document height of 923 CSS pixels, slightly exceeding the 915 CSS-pixel viewport. Implementation and completion verification are deferred until Milestone 017 isCOMPLETE. - On 2026-09-10,
MVP.md,ARCHITECTURE.md, anddocs/UI_STYLE.mdwere reconciled to make the four-button lower-right directional pad the primary phone control while preserving avatar drag, swipe, and keyboard alternatives. The inactive centre prevents diagonal input. Each button is 62 by 62 CSS pixels on compact phones (and at least 52 by 52 CSS pixels in compact landscape), has Russian accessible text, and uses pointer capture plus a 170 ms bounded hold repeat through the existing latest-intent flow. The compact application viewport is explicitly clipped to100dvhto prevent page scrolling; the lower-left key action, lower-right pad, top-right utility controls, and raised notices occupy separate zones. - On 2026-09-10,
node --test test/web/test_player_app.jspassed all 20 browser tests.bash scripts/run_host_tests.shpassed all host tests and deterministic web-asset verification (ecf9429fd873e27df67774616ec68684df19d88cce6913d53b0764b53e8e95cd, 25,939 of 1,572,864 allowed compressed bytes)./home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1passed with 58,868 of 327,680 RAM bytes and 1,027,966 of 2,097,152 application bytes. The matching--target buildfscommand passed and produced the fixed 2,031,616-byte LittleFS image.
Milestone 019 — Reference-derived SVG icon system
Status: COMPLETE
Objective
Replace the current player and gameplay pictograms with a coherent local SVG set derived from the project owner's supplied visual reference, and establish yellow and blue as the two unmistakable player identities across players, matching keys, and matching exits.
Scope
- Before changing application markup, styling, or rendering, manually recreate the supplied raster-reference silhouettes as clean, editable SVG artwork. Do not embed, trace at runtime, or ship the raster reference itself.
- Create SVG symbols for the six supplied concepts: running player, key, closed arched exit, vision crystal, opened arched exit with outward arrow, and blocked-movement spikes.
- Treat the reference as visual direction rather than a source of product requirements. Map its artwork only to existing maze32 gameplay states; the spikes communicate a rejected move or wall collision and must not introduce a trap, damage, or any other new mechanic.
- Review and approve the standalone SVG set before using it to replace icons in the browser application. Keep source paths understandable, view boxes consistent, silhouettes legible at gameplay sizes, and all colors controlled through project-owned CSS or explicit documented SVG tokens.
- Reconcile
MVP.md,ARCHITECTURE.md,docs/UI_STYLE.md, and supporting UI documentation before implementation. Replace the current blue/pink identity contract with Player 1 yellow and Player 2 blue across every player, key, exit, status, map, onboarding, result, and observer presentation. - Give each player's matching key and exit the same yellow or blue ownership identity as its owner. Preserve different silhouettes and state treatments for players, keys, closed exits, and opened exits so meaning never depends on color alone.
- Replace the applicable current pictograms in the player HUD, maze, key-drop control, onboarding, transient feedback, results, and observer view with the approved SVG symbols. Retain or restyle icons not represented by the supplied sample only as needed to keep the complete icon family visually coherent.
- Preserve Russian accessible labels, icon-first comprehension, reduced-motion behavior, server authority, hidden-state rules, offline operation, and the existing compressed-asset and Flash budgets.
- Verify that yellow remains distinguishable from warning states and blue from pause states by pairing semantic colors with shape, outline, animation, or labels, as appropriate.
Deliverables
- A project-owned, editable SVG source set for the player, key, closed exit, vision crystal, opened exit, and blocked-movement symbols.
- A documented mapping from each reference-derived SVG symbol and yellow/blue variant to existing maze32 entities and UI states.
- Updated color and icon contracts in
MVP.md,ARCHITECTURE.md,docs/UI_STYLE.md, and affected supporting UI documentation. - Replacement icons in player, observer, onboarding, HUD, feedback, and result presentations without embedded raster images or external dependencies.
- Deterministic SVG validation, browser tests, asset-budget checks, and representative mobile and desktop visual-verification evidence.
Acceptance criteria
- Before application icon replacement begins, the six reference-derived SVG sources exist as editable vector paths, pass XML validation, have consistent documented view boxes, contain no embedded raster data, and are approved by the project owner against the supplied visual reference.
- The running figure, key, closed arched exit, faceted vision crystal, opened arched exit with outward arrow, and spikes remain recognizable at every rendered application size without relying on accompanying text.
- Player 1 is yellow and Player 2 is blue in every player and observer view. Each player's key and exit carry the matching ownership color consistently, including ground, inventory, closed, opened, onboarding, and result states.
- Player, key, closed exit, and opened exit remain distinguishable by both silhouette and state treatment in monochrome inspection; color is never the only way to identify object type, ownership, or exit state.
- The spikes appear only as blocked-movement or wall-collision feedback. No trap object, damage rule, hidden-state field, protocol message, or server state is added.
- Yellow player identity cannot be confused with warnings, and blue player identity cannot be confused with pause state: each conflicting semantic role has an additional icon, outline, pattern, motion, or Russian label.
- All applicable current player, key, exit, vision-artifact, and blocked-movement pictograms are replaced across the maze, HUD, key-drop action, onboarding, transient feedback, results, and observer presentation; no obsolete duplicate icon remains visible in a reachable state.
- SVG symbols render without clipping, distortion, unexpected inherited
color, or loss of contrast at the canonical
412 x 916player viewport, compact landscape, representative tablet and desktop player viewports, and observer viewports of1024 x 768or larger. - Player-facing accessible labels remain in Russian, the icon-first onboarding remains understandable, reduced motion preserves all state information, and the application loads every icon locally without an internet dependency.
- Automated checks validate SVG structure, required symbol identifiers, yellow/blue ownership mapping, all closed/open and carried/ground variants, absence of embedded raster content, blocked-feedback semantics, and the removal of obsolete reachable icons.
- All host and browser tests pass, the compressed web payload remains within the 1.5 MiB budget and LittleFS partition, and firmware remains within the factory partition without changing authoritative game or projection data.
- The project owner confirms on representative mobile and desktop browsers that the new icons match the supplied examples closely enough, yellow and blue ownership is immediately understandable, keys and doors are assigned to the correct player, and every icon is clear at normal gameplay size, or explicitly approves a documented evidence deviation.
Evidence
Implementation and automated verification are in progress. The following evidence satisfies the source, integration, and automated acceptance criteria; the project-owner visual confirmation remains outstanding:
- On 2026-09-10, the project owner supplied a raster visual reference containing a yellow running figure, yellow key, red closed arched door, cyan faceted crystal, green opened arched door with an outward arrow, and red spikes. The owner requested that local SVG artwork be created from these examples before icon replacement, and selected yellow and blue as the two player colors.
- On 2026-09-10,
web/icons.svgwas replaced with project-owned editable SVG symbolsplayer,key,exit-closed,crystal,exit-open, andspikes. Each reference-derived symbol uses the documented64 x 64view box andcurrentColor; the sprite contains no<image>,foreignObject, or embedded raster data.xmllint --noout web/icons.svgpassed. - The player, key, closed/opened exit, vision-artifact, blocked-movement,
onboarding, avatar, lobby, observer, and result renderers now use the new
SVG symbols where the supplied reference applies. Player 1 is yellow
(
#ffd33d) and Player 2 is blue (#41b6ff); their keys and exits share the matching ownership color, while dashed secondary treatments and distinct silhouettes preserve non-color identification. Amber warnings and periwinkle pause semantics remain distinct from player identities. Spikes are used only in the client-side blocked-movement notice. node --test test/web/test_player_app.jspassed all 21 tests, including the new deterministic SVG-structure, ownership-color, local-asset, and blocked-feedback checks.bash scripts/run_host_tests.shpassed all host suites and deterministic asset verification (c7f3bd8217e51e81b46eaeaf2636f5cd1cf85a7d8e27b07cb5293ea97a1f3f41, 26,510 of 1,572,864 allowed compressed bytes)./home/sasa/.platformio/penv/bin/pio run -e esp32-c6-devkitm-1passed with 58,868 of 327,680 RAM bytes and 1,027,966 of 2,097,152 application bytes. The matching--target buildfscommand passed and reported the fixed 2,031,616-byte LittleFS image within its partition.- On 2026-09-10,
pio run -e esp32-c6-devkitm-1 --target uploadfs --upload-port /dev/ttyACM0wrote and hash-verified the LittleFS image to the confirmed ESP32-C6FH4. A read-only live-page check at412 x 916CSS pixels confirmed a412 x 916document with no overflow and references to the localplayerandspikessymbols. It did not create or alter a player session, so full active-match visual confirmation remains for the project owner. - On 2026-09-10, the project owner confirmed after representative player
testing that the new icons are satisfactory. This closes the remaining
visual-confirmation criterion; Milestone 019 is
COMPLETE.
Milestone 020 — Playful visual feedback and top message rail
Status: COMPLETE
Objective
Replace distracting gameplay warning text with immediate visual feedback and move the remaining player-facing notices into a playful top-of-screen message rail that keeps children engaged without obscuring the maze or controls.
Scope
- Before implementation, reconcile
MVP.md,ARCHITECTURE.md,docs/UI_STYLE.md, and supporting player-UI documentation with the new feedback hierarchy and copy rules. - When the server rejects a movement intention because of a wall or maze boundary, shake the visible maze briefly in the attempted cardinal direction. Do not show a toast, tooltip, spike notice, or any textual warning for the blocked move.
- Keep blocked-movement feedback local and presentation-only. It must not move
the authoritative player position, reveal a hidden wall, infer unseen maze
state, or change the existing
blockedprotocol response. - When movement is rejected with
rate_limited, briefly darken and blur only the maze image so the player understands that input is arriving too quickly. Do not display the current server-speed warning or any replacement text. - Bound, coalesce, and safely clear shake and blur feedback. Repeated blocked or rate-limited responses may restart or extend one current effect only within a documented maximum duration; they must not create animation queues, timers, DOM nodes, or a permanently blurred maze.
- Move connection loss, reconnection, session, lobby, protocol, and other messages that still require text to a dedicated rail at the top of the screen, above the application top bar and the player status panel. Respect display safe areas and do not cover essential status or controls.
- Rewrite player-facing messages as short, varied, child-friendly Russian
jokes or playful reactions. Remove visible engineering language such as
WebSocket, protocol packets, request limits, and internal server details; retain technical detail only in diagnostics or the secondary connection panel where appropriate. - Define priority, deduplication, replacement, persistence, and dismissal rules for the top message rail. Connection loss remains visible until recovery; lower-priority messages are bounded and cannot flood or indefinitely delay a more important connection state.
- Keep the rail legible but lively through icon, color, and restrained motion. Avoid rapid flashing, excessive movement, or overlapping announcements, and preserve Russian live-region semantics for assistive technology.
- In reduced-motion mode, replace maze shaking with a single non-animated directional border pulse or equivalent static wall-impact cue. The rate-limit darkening and blur may remain because they do not imply movement, but their transitions must be effectively immediate.
- Preserve optional sound and haptic feedback, authoritative server behavior, observer read-only behavior, hidden-state protection, offline assets, and the existing firmware and LittleFS budgets.
Deliverables
- Updated feedback, message-placement, reduced-motion, and humorous-copy contracts in the canonical and supporting UI documentation.
- Direction-aware maze-shake feedback for blocked movement with no visible blocked-move text.
- Bounded maze darkening and blur for
rate_limitedmovement with no visible rate-limit text. - Responsive top message rail with safe-area placement, bounded prioritization, deduplication, persistent connection state, and playful Russian copy.
- Deterministic browser tests plus representative mobile and desktop visual, motion, reduced-motion, and accessibility verification evidence.
Acceptance criteria
- A
blockedresponse causes one brief shake of the maze in the attempted direction without moving the authoritative map position. No blocked-move text, toast, tooltip, spike notice, or generic error notification becomes visible. - The blocked effect uses only the direction of the matching outstanding movement intention and the server rejection. It does not inspect, retain, infer, or expose hidden maze geometry.
- A
rate_limitedmovement response darkens and blurs only the maze region for a documented bounded duration. The HUD, direction pad, key action, top bar, and message rail remain readable, and no rate-limit warning text appears. - Repeated
blockedorrate_limitedresponses coalesce into at most one active effect of each type, use a bounded number of timers and DOM nodes, and restore the exact normal maze presentation after the documented maximum duration, disconnect, pause, result, screen change, or application reset. - With reduced motion enabled, blocked movement produces a single static or non-animated directional border cue instead of shaking. Rate-limit feedback remains understandable without animation, sound, vibration, or text.
- Every remaining player-facing transient or persistent system message appears in the top message rail above the application top bar and player status panel. No ordinary notice remains anchored near the bottom of the maze or behind mobile controls.
- At
412 x 915,412 x 916, compact landscape, representative tablet and desktop player viewports, and observer viewports of1024 x 768or larger, the message rail respects safe areas, does not create page overflow, and does not cover essential status, map controls, dialogs, or observer tools. - Visible messages are concise Russian, child-friendly, non-offensive, and
playful. Reachable normal-operation copy contains no
WebSocket, packet, protocol, queue, rate-limit, request-processing, or similar engineering terminology. - Connection loss produces a persistent high-priority playful message until reconnection. Recovery replaces it with a short positive message; repeated reconnect attempts do not stack duplicates or hide the current connection state.
- The rail maintains a documented bounded message count and priority order, deduplicates repeated events, exposes appropriate Russian live-region semantics, and never rapidly flashes or animates indefinitely.
- Browser tests cover directional wall shake, absence of blocked text, rate-limit blur and absence of rate-limit text, effect coalescing and reset paths, reduced motion, top placement, safe areas, message priority and deduplication, connection persistence, playful-copy constraints, supported viewports, and observer presentation.
- All host tests pass, compressed web assets remain within the 1.5 MiB budget and LittleFS partition, firmware remains within the factory partition, and no protocol, game-authority, projection, or hidden-state contract changes.
- The project owner confirms on a representative mobile browser that wall collisions and excessive input are understandable without warning text, the top messages do not obstruct play, and the humorous copy keeps the experience lively without becoming repetitive or distracting, or explicitly approves a documented evidence deviation.
Evidence
The following planning-time observation motivated the milestone:
- On 2026-09-10, the project owner requested removal of the visible
Там стена. Очень убедительная.blocked-movement warning in favor of shaking the maze, replacement of the server-speed warning with temporary maze darkening and blur, and relocation of connection and other necessary messages above the top panel. The owner also required playful, non-engineering Russian copy intended to retain children's attention. Static inspection found the current blocked andrate_limitedresponses feeding the shared notice path, while.noticeis positioned near the bottom of the viewport. Implementation and completion verification are deferred to this milestone. - On 2026-09-10, the feedback and rail contracts were reconciled in
MVP.md,ARCHITECTURE.md,docs/UI_STYLE.md, anddocs/PLAYER_UI.md. The browser records each outstanding move direction, uses it only after the matchingblockedresponse to restart one 340 ms directional maze effect, and uses one restartable 520 ms maze-onlyrate_limitedeffect. Both timers are cleared by control reset, disconnect, pause, result, and reset paths. The map projection offset is held in CSS variables during the effect, preserving the centered authoritative player position at board edges. node --test test/web/test_player_app.jspassed: 22 tests, 0 failures. The focused deterministic checks include the message rail placement and live semantics, direction correlation, no blocked/rate-limit text, maze-only blur, and reduced-motion behavior.bash scripts/run_host_tests.shpassed all portable host suites, the same 22 browser tests, and the deterministic web-asset check:web asset reproducibility: PASS (..., 27068/1572864 bytes)./home/sasa/.platformio/penv/bin/pio runpassed foresp32-c6-devkitm-1: RAM58868/327680bytes and factory flash1027966/2097152bytes./home/sasa/.platformio/penv/bin/pio run -t buildfspassed and produced the configured2031616/2031616byte LittleFS image.- On 2026-09-10, the project owner completed a live UI check and confirmed that
the feedback, message rail, and animations work as expected. This satisfies
the remaining visual-confirmation criterion; Milestone 020 is
COMPLETE.
Milestone 021 — Mobile player focal-point repositioning
Status: COMPLETE
Objective
Move the player avatar away from the geometric center of the mobile maze into a responsive, unobstructed focal position so hands operating the lower controls do not hide the player or the most useful nearby maze context.
Scope
- Before implementation, reconcile
MVP.md,ARCHITECTURE.md,docs/UI_STYLE.md, and supporting player-UI documentation with the new mobile player-anchor contract. This supersedes the current requirement to keep the player at the exact center of the map viewport. - Define a responsive safe play area that excludes the fixed player-status panel, Milestone 020 top message rail, directional pad, key-drop action, utility controls, safe-area insets, and other persistent overlays.
- In portrait mobile layouts, place the authoritative player focal point above the maze's geometric center while retaining useful visible context on every side. At the canonical compact viewport, target approximately 30% of the map region's height from its top rather than 50%.
- In compact landscape layouts, choose the player focal point from the largest unobstructed map region and bias it away from the directional-pad hand zone. Do not apply the portrait offset blindly when it would reduce usable context.
- Translate only the server-provided projection so its authoritative
selfcell appears beneath the responsive focal point. Do not request, synthesize, retain, or infer extra maze cells to fill space created by the offset. - Keep the visible player icon aligned exactly with the authoritative
selfcell after every projection, resize, orientation change, safe-area change, and projection-edge case. - Preserve the four-button directional pad as the primary phone input. Avatar drag remains an optional compatibility input, but normal mobile play must not require touching or covering the player icon.
- Keep the optional avatar-drag deflection relative to the new focal point and return it there on release, cancellation, pause, disconnect, blur, hidden document, lost pointer capture, and application reset.
- Integrate the new focal point with Milestone 020 maze shake and blur effects. Temporary feedback transforms must compose with the projection offset and must not cause the player or map to jump when an effect begins or ends.
- Preserve observer full-map positioning, desktop behavior where appropriate, reduced-motion behavior, authoritative server ownership, hidden-state protection, offline assets, and existing memory and Flash budgets.
Deliverables
- Updated player-anchor, responsive-layout, input, and projection-presentation contracts in canonical and supporting UI documentation.
- A bounded responsive focal-point calculation for portrait and compact landscape mobile layouts.
- Player and server-projection rendering aligned to the new focal point, including edge projections and orientation changes.
- Compatibility with the directional pad, optional avatar drag, top message rail, blocked-movement shake, and rate-limit blur.
- Deterministic layout tests plus representative mobile portrait and landscape visual and touch verification evidence.
Acceptance criteria
- At
412 x 915and412 x 916CSS pixels in portrait orientation, the resting player center is horizontally centered and positioned at approximately 30% of the usable map region's height from its top, with a documented tolerance of no more than five percentage points. It is not at the map's 50% geometric center. - The player focal point lies completely outside the directional pad, key-drop, utility-control, top-message, and status-panel exclusion zones, including safe-area insets. The visible player icon and its outline do not overlap any persistent control.
- While a representative thumb operates every directional-pad button, the player icon and its current cell remain visible and enough adjacent server-provided maze context remains unobstructed to understand the next orthogonal move.
- Compact landscape orientation computes a stable focal point within the largest unobstructed map region and away from the directional-pad hand zone. Rotating between portrait and landscape does not leave the player beneath a control or outside the visible map shell.
- The rendered player stays aligned with the authoritative
selfcell to within one CSS pixel after movement projections, viewport resize, orientation changes, dynamic safe-area changes, and projections touching each maze edge or corner. - Repositioning translates only cells contained in the current authoritative projection. The browser does not expose, retain, infer, decorate, or fill hidden maze geometry, and no protocol or server projection changes.
- Direction-pad, swipe, keyboard, and optional avatar-drag inputs continue to send the same bounded cardinal intentions. Avatar drag deflects relative to the new focal point and every existing reset path returns it exactly there.
- Milestone 020 directional shake and rate-limit blur compose with the focal offset without changing the underlying authoritative position, accumulating transforms, exposing hidden cells, or producing a jump when feedback clears.
- The player layout requires no horizontal or vertical page scrolling at the supported mobile viewports. The maze remains the dominant visual region and the shifted projection does not create a distracting inactive void.
- Browser tests cover focal-point calculation, exclusion-zone avoidance, portrait and landscape placement, safe areas, resize and rotation, all map edges and corners, one-pixel player/self alignment, input compatibility, drag reset paths, Milestone 020 transform composition, zero overflow, and absence of hidden-state expansion.
- All host tests pass, compressed web assets remain within the 1.5 MiB budget and LittleFS partition, firmware remains within the factory partition, and observer layout and authority remain unchanged.
- The project owner confirms on a representative mobile browser that the new player position is comfortable, the operating hand no longer hides the player or critical nearby maze context during directional-button play, and portrait and landscape layouts remain easy to understand, or explicitly approves a documented evidence deviation.
Evidence
The following planning-time observation motivated the milestone:
- On 2026-09-10, the project owner reported that the exactly centered mobile
player is uncomfortable because fingers hide the maze and the player. A
read-only inspection of the open player page at
412 x 915CSS pixels found the96 x 96avatar target centered at(206, 525.5)and the map shell also centered at(206, 525.5), confirming exact geometric overlap. The player requested a planning milestone only; implementation and completion verification were deferred until Milestone 020 wasCOMPLETE. - On 2026-09-10,
MVP.md,ARCHITECTURE.md,docs/UI_STYLE.md, anddocs/PLAYER_UI.mdwere reconciled to define a 30%-height portrait focal point and a compact-landscape left-biased unobstructed focal point. The browser now composes the received-projection translation with that focal offset, places the optional avatar drag target at the same point, and recalculates both after resize and orientation change. It continues to render only received projection cells; the Milestone 020 feedback transform uses these composed map-offset variables unchanged. node --test test/web/test_player_app.jspassed: 23 tests, 0 failures. The added deterministic focal-point checks cover portrait and compact landscape placement plus projection-offset composition at a projection edge.bash scripts/run_host_tests.shpassed all portable host suites, all 23 browser tests, and the deterministic asset check:web asset reproducibility: PASS (..., 27367/1572864 bytes)./home/sasa/.platformio/penv/bin/pio runpassed foresp32-c6-devkitm-1: RAM58868/327680bytes and factory flash1027966/2097152bytes./home/sasa/.platformio/penv/bin/pio run -t buildfspassed and produced the configured2031616/2031616byte LittleFS image.- On 2026-09-10, a project-owner live UI check found that the original 40% map-region target still appeared centered on the full phone screen because the map begins below the status area. The owner requested a more elevated player position, so the active milestone target and implementation were adjusted to 30% of map height; mobile confirmation remains required.
- On 2026-09-10, the project owner manually checked the revised mobile layout
and confirmed that the raised, transparent player handle no longer obscures
nearby maze passages and that the layout works as expected. This satisfies
the remaining manual confirmation criterion; Milestone 021 is
COMPLETE.
Milestone 022 — Persistent bush-wall breakthrough
Status: COMPLETE
Objective
Add an always-available perseverance mechanic for both players: five deliberate attempts against the same interior wall permanently open a passage through its bushes, with convincing staged vegetation reactions and a final breakthrough effect, while preserving ESP authority, hidden-state protection, bounded resources, and fair rules.
Scope
- Do not start implementation until every preceding milestone is
COMPLETE. - Before implementation, reconcile
MVP.md,ARCHITECTURE.md,docs/UI_STYLE.md, and supporting player-UI documentation with mutable in-match maze topology. The new contract supersedes the current rule that a rejected wall movement can never change position. - Keep the ESP authoritative for impact counting, timeout handling, passage mutation, player movement, projection updates, and all breakthrough events. Browsers continue to send ordinary bounded cardinal movement intentions and never decide that a wall has weakened or opened.
- Track one bounded perseverance streak per active player. A qualifying streak consists of five server-accepted movement attempts made from the same cell in the same cardinal direction against the same still-closed interior wall.
- Reset that player's streak when 3,000 ms or more elapse after the previous
qualifying impact, the player makes a successful move, the player attempts a
different direction, the player is paused or disconnected, or the match is
reset or completed. Rejected
rate_limited, malformed, stale, duplicate, and unauthorized commands neither increment nor complete a streak. - On the fifth qualifying impact, atomically open the wall in both adjacent cells and complete the originally requested move into the neighboring cell. The passage remains open for the rest of the active match and can be used by either player from either side without further impacts.
- Never allow a breakthrough through the maze's outer perimeter or into an invalid cell. Key, artifact, exit, result, visibility, and occupancy rules apply to the fifth-attempt movement exactly as they do to an ordinary move.
- Do not combine progress from two players or from opposite sides of a wall. Each player has an independent streak, but a completed opening is one shared authoritative topology change visible and usable according to normal game rules.
- Represent permanent opened-wall state with a bounded structure sized from the configured maximum maze dimensions. Clear it when the active match is discarded; persistence across reboot is not required.
- Extend the protocol only as needed to deliver server-approved impact stages to the initiating player and topology changes to affected projections. Sequence or revision data must make duplicate, reordered, or delayed events harmless. A player must never receive coordinates, wall state, impact progress, or decorations outside that player's current allowed projection; observers remain read-only and may receive the full authoritative state.
- Reconcile the mechanic with Milestone 020 feedback: impacts one through four replace the generic blocked shake with increasingly strong but restrained bush bending, rustling, and leaf-shedding cues; the fifth uses a short parting or flattening animation, a bounded leaf burst, and immediate movement through the new gap. No warning toast or engineering-style text is shown.
- Make the permanent opening visually read as a trampled bush passage when it is inside the current server-approved projection. Use offline CSS/SVG assets, bounded DOM nodes or canvas work, no runtime downloads, and no unbounded particle allocation.
- Coalesce rapid feedback and cancel transient effects on pause, disconnect, result, screen change, projection replacement, and application reset. Sound and vibration remain optional enhancements and cannot be the only feedback.
- In reduced-motion mode, show server-approved static deformation stages and a final non-animated opened-bush state without shaking, swaying, or flying particles. Preserve clear color-independent silhouettes and contrast.
- Keep the mechanic equally available to both player roles with identical thresholds and timing. Add deterministic checks that it introduces no role-specific rule or information advantage even though player actions may make the live topology asymmetric after match start.
- Preserve the existing firmware, compressed web-asset, and LittleFS budgets.
Deliverables
- Updated gameplay, authority, projection, protocol, accessibility, and visual contracts in canonical and supporting documentation.
- Bounded authoritative per-player perseverance tracking with an exact five-impact threshold and 3,000 ms expiry.
- Atomic permanent interior-wall opening and fifth-attempt movement, including shared bidirectional use and safe handling of game objects and results.
- Server-approved staged bush-impact feedback, final breakthrough animation, permanent trampled-passage presentation, and reduced-motion equivalents.
- Deterministic host and browser tests plus representative two-player, observer, mobile, reduced-motion, and physical-device verification evidence.
Acceptance criteria
- Four qualifying attempts against the same closed interior wall do not change authoritative topology or player position. The fifth qualifying attempt atomically opens that wall in both adjacent cells and moves the initiating player exactly one cell through the new passage.
- A perseverance streak increments only for server-accepted attempts from the same player, cell, and cardinal direction against the same closed interior wall. Tests cover exact threshold boundaries and every non-qualifying input class.
- The streak resets after a gap of 3,000 ms or more, any successful move, or an attempted direction change, and on pause, disconnect, result, match reset, and application reset. Tests cover just-inside, exact-boundary, and just-outside timeout behavior using a deterministic clock.
- Rate-limited, malformed, stale, duplicate, observer, and otherwise unauthorized commands never weaken or open a wall. Duplicate or reordered protocol events cannot replay an impact, animation, topology mutation, or movement.
- Outer-perimeter walls and invalid destinations can never be opened. The fifth-attempt move obeys the same occupancy, object pickup, exit, visibility, result, sequencing, and broadcast contracts as an ordinary successful move.
- Once opened, the passage remains open bidirectionally until the active match ends and either player can traverse it normally. Progress is not pooled between players or opposite approaches, while completed topology is shared.
- Player projections reveal only currently allowed cells and passage state. Impact progress is sent only to the initiating player when permitted, and no client can infer an unseen wall, remote impact, opening, object, or route; observer sessions remain read-only.
- Impacts one through four produce distinct, restrained server-approved bush deformation cues whose intensity communicates progress without text. The fifth produces a convincing short vegetation-parting or flattening effect, a bounded leaf effect, and synchronized movement through the gap.
- A breached wall inside the received projection is recognizable as a permanent trampled-bush passage from either side. It remains understandable without sound, vibration, text, or color alone and does not obscure the player, objects, adjacent passages, controls, or top message rail.
- Feedback uses a documented fixed maximum of timers, generated elements, and particles per client. Repeated input coalesces safely, and all transient effects clear on pause, disconnect, result, screen change, projection replacement, and application reset without leaving stale transformations.
- With reduced motion enabled, the same authoritative progress is clear from static deformation stages and the final opened state; no shake, sway, travel animation, or flying particle is required to understand the event.
- Deterministic host tests cover counting, reset rules, timeout boundaries, atomic topology mutation and movement, perimeter protection, two-player independence and shared passage use, object/result interactions, message ordering, bounded storage, symmetry of rules, and hidden-state protection.
- Browser tests cover all five visual stages, event deduplication and ordering, permanent passage rendering from both sides, effect cleanup, supported mobile and desktop viewports, observer presentation, and reduced-motion and non-color cues.
- All host tests pass, compressed web assets remain within the 1.5 MiB budget and LittleFS partition, firmware remains within the factory partition, and physical-device testing confirms stable two-player play under repeated and simultaneous wall impacts.
- The project owner confirms on a representative mobile browser that five impacts feel deliberate but not tedious, the bushes visibly resist before giving way, the final breakthrough and movement feel synchronized, and the permanent gap is obvious and enjoyable, or explicitly approves a documented evidence deviation.
Evidence
The following planning-time decisions define this milestone:
- On 2026-09-10, the project owner selected a permanent breakthrough rather than a one-time squeeze or temporary opening. The owner accepted five hits against one wall from one cell and one direction, with progress reset after 3 seconds, movement, or a direction change, and required the mechanic to be always available to both players.
- Implementation, automated verification, physical-device checks, and owner
acceptance are deferred until all preceding milestones are
COMPLETE. - 2026-09-10 implementation evidence:
bash scripts/run_host_tests.shpassed, includinggame persistent bush-wall breakthrough: PASS (five impacts, shared passage).node --test test/web/test_player_app.jspassed all 23 browser tests and confirmed the bounded impact and trampled-passage presentation.
Milestone 023 — Top-down living hedge SVG sample library
Status: COMPLETE
Objective
Design and obtain project-owner approval for a lightweight SVG sample library that makes maze walls read as varied living hedges viewed from above, with recognizable leaves, twigs, depth, and matching damaged and permanently breached variants for perseverance mode, without integrating the artwork into the running game in this milestone.
Scope
- Do not start this milestone until every preceding milestone is
COMPLETE. - Treat this as a visual exploration and asset-approval milestone only. Do not replace the current maze renderer, change gameplay, modify the protocol, or ship the samples in browser or firmware assets during this milestone.
- Before drawing, add a concise hedge-art contract to
docs/UI_STYLE.mdthat defines top-down perspective, scale, palette, outline weight, contrast, organic variation, tiling rules, and the relationship between intact, impacted, and permanently breached bushes. - Create editable, hand-reviewable SVG source artwork with semantic groups and stable symbol identifiers. Do not embed raster images, external fonts, remote resources, scripts, generated metadata, or tool-specific binary data.
- Explore at least six visibly different intact straight-hedge samples. Vary leaf clusters, branch paths, gaps, highlights, and silhouette rhythm while keeping every sample unmistakably part of one visual family.
- Include representative joins and boundary situations needed by a cell-based maze: continuous straight runs, perpendicular corners, crossings of adjacent wall segments, exposed ends, and shared edges. Samples must rotate or mirror cleanly for all four cardinal directions without directional lighting errors.
- Make individual leaves and small branch forks distinguishable at the actual supported maze scales of 34, 40, and 46 CSS pixels per cell. Avoid both flat green bars and noisy texture that turns into visual static on a phone.
- Use at least three vegetation tones plus restrained branch and shadow tones to create depth. Maintain an identifiable hedge silhouette without relying on green hue alone and keep players, keys, doors, passages, and interactive feedback visually dominant.
- Draw a coherent perseverance sequence for the same representative wall: intact, four increasing impact stages, the moment of breakthrough, and the final permanent trampled opening. Branch bending, displaced leaves, broken twigs, compressed foliage, and the cleared center must evolve consistently rather than appearing as unrelated pictures.
- Provide at least three final breached-hedge samples with different organic edge shapes. Each must clearly show a traversable gap from a top-down view, retain torn or flattened bushes on both sides, and remain recognizable when rotated or mirrored.
- Keep openings visually aligned to the authoritative cell passage and wide enough for the player icon without implying a larger traversable area. The samples must not encode coordinates, hidden passages, objects, ownership, or other game state.
- Present all concepts together on one neutral comparison sheet and also as isolated SVG symbols on representative dark maze cells. Include previews at 1x and high-density mobile rendering scales, with no smoothing assumptions that are unavailable in ordinary browsers.
- Record approximate raw and compressed byte costs for each candidate family so later integration can select variety without threatening the 1.5 MiB web asset budget or LittleFS partition. Prefer reusable leaf, twig, shadow, and damage components over repeated paths where that improves size and clarity.
- Check color-vision robustness, grayscale silhouette, normal contrast, and reduced-motion suitability. This milestone defines static frames only; animation timing and runtime particle behavior remain governed by Milestone 022 and are outside this milestone's implementation scope.
- End with an explicit project-owner selection of the preferred intact hedge family, damage progression, breached variants, density, and palette. Runtime integration requires a separately planned later milestone after approval.
Deliverables
- Updated hedge-art and sample-review contract in
docs/UI_STYLE.md. - Editable SVG symbol sheet containing intact straight segments, joins, ends, four impact stages, breakthrough frame, and permanent breached variants.
- Neutral comparison sheet plus representative dark-cell previews at 34, 40, and 46 CSS pixel cell sizes and normal and high-density mobile scales.
- Brief asset manifest describing symbol identifiers, reuse rules, rotation and mirroring constraints, accessibility checks, and raw and compressed sizes.
- Recorded project-owner selection and any requested revisions; no browser or firmware integration changes.
Acceptance criteria
- The sample library contains at least six intact straight-hedge variants, representative continuous runs, corners, shared-edge joins, and exposed ends, all drawn as editable vector artwork with stable semantic identifiers.
- Every intact sample resembles a living hedge viewed directly from above and contains clearly distinguishable leaf clusters and branching structure. No candidate is merely a solid, gradient, or textured green bar.
- The candidates form one coherent visual family while showing enough silhouette, foliage, branch, highlight, and gap variation to prevent obvious repetition across a representative maze preview.
- Leaves and primary branch forks remain recognizable at 34, 40, and 46 CSS pixel cell sizes at 1x rendering. At high density they gain clarity without changing geometry, producing seams, or becoming disproportionately detailed.
- Straight segments, corners, joins, and ends connect without visible gaps, doubled borders, accidental knots, or discontinuous lighting when rotated or mirrored into every cardinal orientation.
- At least three vegetation tones plus restrained branch and shadow tones provide top-down depth. Grayscale and representative color-vision checks preserve the hedge silhouette and do not reduce the legibility of players, keys, doors, passages, or feedback.
- One representative wall has a visually continuous set of intact, impact stages one through four, breakthrough, and permanent-open frames. Each stage shows plausible cumulative bending or damage and is distinguishable from adjacent stages without explanatory text.
- At least three permanent breached variants show an unambiguous traversable center with torn, bent, or flattened vegetation on both sides. They remain understandable from either side after rotation or mirroring and do not look like an intact or merely shadowed wall.
- Every breached opening aligns to one authoritative wall passage, is visually wide enough for the existing player icon, and does not suggest access to an adjacent closed cell or reveal any hidden maze state.
- SVG files contain no raster payloads, external dependencies, scripts, unnecessary editor metadata, or unbounded duplication. The manifest records raw and compressed size for each candidate family and identifies reusable components.
- The comparison sheet shows all candidates on a neutral background and the preferred candidates on representative dark maze cells at all target sizes. Review does not require running or modifying the game.
- No runtime HTML, CSS, JavaScript, firmware, protocol, generated web bundle, or LittleFS image changes are part of this milestone. Automated checks confirm SVG validity, local-only dependencies, stable identifiers, and deterministic optimization output.
- The project owner selects or explicitly rejects the intact hedge family,
impact sequence, breached variants, density, and palette after reviewing the
sample sheet at representative mobile scale. Any requested revisions are
recorded before the milestone can become
COMPLETE.
Evidence
The following planning-time request defines this milestone:
- On 2026-09-10, the project owner requested a future sample-drawing milestone because maze walls should look like varied living hedges from above rather than simple green sticks. The owner specifically required visible leaves and branches plus matching broken-bush samples for permanent breakthroughs in perseverance mode, and requested planning only with no immediate artwork or runtime implementation.
- Artwork creation, review previews, validation, owner approval, and any later runtime integration are deferred. Milestone 022 remains the sole active milestone at planning time.
- 2026-09-10 implementation evidence:
docs/hedge-sample-library.svgprovides six intact hedge families, joins, four impact stages, a breakthrough frame, three permanent breaches, and 34/40/46 CSS-pixel dark-cell previews. The sheet is 6,495 raw bytes and 1,884 gzip-9 bytes; it is review-only and is not included in runtime assets. - 2026-09-10 verification:
bash scripts/run_host_tests.shpassed.node --test test/web/test_player_app.js test/web/test_hedge_svg.jspassed all 24 tests, including the local-only SVG validation of stable identifiers, reusable components, and forbidden dependencies. - Project-owner visual selection remains outstanding. Until the owner selects
or rejects the intact family, impact progression, breach variants, density,
and palette, this milestone remains
IN_PROGRESS; no runtime integration is authorized. - 2026-09-10 project-owner selection: the submitted review library is approved
without revision. The approved set includes all six intact hedge variants,
the shown four-stage impact progression and breakthrough frame, all three
breach variants, and the density and palette defined in
docs/UI_STYLE.md. This satisfies the required owner review. Runtime integration remains a separately planned milestone.
Milestone 024 — Runtime living hedge rendering
Status: COMPLETE
Objective
Replace the maze's simple green wall strokes and provisional perseverance damage styling with the project-owner-approved Milestone 023 living-hedge SVG family, so intact walls, impacts, and permanent breakthroughs form one varied, legible top-down environment on player and observer screens without changing authoritative gameplay or exposing hidden state.
Scope
- Do not start this milestone until every preceding milestone is
COMPLETE. Use only the intact families, impact sequence, breakthrough frame, breached variants, density, and palette approved in Milestone 023. Any substantive art redesign requires returning to a separately planned sample-review milestone. - Reconcile
docs/UI_STYLE.md,docs/PLAYER_UI.md, andARCHITECTURE.mdwith the runtime hedge-rendering contract before changing browser assets. - Derive a compact runtime SVG sprite from
docs/hedge-sample-library.svg; keep the review source untouched and do not make runtime pages depend on files underdocs/. Preserve stable semantic symbol identifiers where practical and remove comparison-sheet-only content. - Package the runtime sprite locally with the compressed web application. Do not add remote images, fonts, libraries, network requests, embedded raster data, or runtime SVG scripts.
- Replace the four solid CSS cell borders with a dedicated decorative edge layer that draws each server-provided closed wall once. Canonicalize shared edges so two adjacent visible cells do not produce doubled foliage, darker overlaps, seams, or inconsistent variants.
- Draw walls at projection boundaries only from the current server-provided cell and passage bits. Do not assume what lies beyond an unseen edge, fill unknown space, retain an explored map, or generate decorative geometry for cells absent from the current projection.
- Select among the six approved intact variants with a documented deterministic function of the currently visible canonical wall edge and orientation. The same edge must keep its variant across rerenders and appear consistently to both players and observers when their approved projections include it, with no new protocol or authoritative game-state field.
- Rotate or mirror approved symbols for north, east, south, and west while preserving their shared-edge center, non-directional lighting, foliage thickness, and joins. Compose approved corner, crossing, and end treatments only from wall edges explicitly present in the received projection.
- Preserve exact maze geometry and hit testing. Decorative foliage may extend visually into neighboring cells by a bounded amount, but it cannot change traversability, pointer targets, player focal-point calculation, map bounds, projection translation, or page overflow.
- Keep hedge artwork behind players, keys, doors, artifacts, landmarks, and interaction feedback. Leaves and branches must remain recognizable at 34, 40, and 46 CSS-pixel cells without obscuring the passage center or reducing object and player contrast.
- Connect Milestone 022 server-approved impact stages one through four to the matching approved static SVG frames on the exact visible wall that received the impact. Preserve the existing bounded timer, event correlation, deduplication, and cleanup behavior; the browser never increments damage or predicts a breakthrough locally.
- Present the approved breakthrough frame only when justified by the matching
authoritative response and transition to one of the approved permanent
breached variants when the received
openedpassage state changes. Derive this transition only from current and immediately preceding visible server-approved state, held in bounded memory and discarded on projection replacement or reset. - Render one canonical permanent breach decoration for a shared opened edge so the gap aligns from both adjacent cells and from both traversal directions. Breached foliage must remain visible while the opened edge is in the current projection and disappear when that edge is no longer projected.
- Preserve reduced-motion behavior by using the approved static impact frames and an immediate permanent breach state, with no shaking, swaying, moving leaves, or travel animation. Normal motion may use restrained transforms of the approved shapes but must not allocate unbounded particles or DOM nodes.
- Use bounded reusable DOM or CSS structures. A rerender must have a documented maximum number of hedge elements derived from the maximum visible projection, remove obsolete edges, and never accumulate duplicate SVG instances, animation timers, styles, or event listeners.
- Apply the same hedge family to the observer's full-map renderer without changing its read-only behavior, pan or zoom controls, minimum viewport, or authoritative snapshot contents. At overview zoom levels, simplify details gracefully while retaining continuous wall and breached-gap silhouettes.
- Preserve Russian player-facing copy, Milestone 020 visual feedback, Milestone 021 focal-point positioning, Milestone 022 game rules, offline operation, accessibility, and existing firmware and filesystem budgets.
Deliverables
- Updated runtime hedge-rendering, layering, variation, projection-safety, and reduced-motion contracts in canonical and supporting documentation.
- Optimized local runtime SVG sprite derived reproducibly from the approved Milestone 023 source, with deterministic size and dependency validation.
- A deduplicated decorative edge renderer for player and observer maps using the six intact hedge variants and approved joins and end treatments.
- Runtime wiring for the four approved impact stages, breakthrough frame, and three deterministic permanent breached variants.
- Deterministic browser tests, asset checks, host regression results, size evidence, and representative mobile, desktop, observer, and physical-device visual verification.
Acceptance criteria
- Every closed edge in the received player or observer projection is rendered with an approved living-hedge SVG variant rather than a solid CSS green border. Visible leaves, twigs, irregular foliage, depth tones, and outline remain recognizable at supported gameplay scales. Horizontal and vertical hedge center lines coincide with their authoritative cell edges, never pass through cell centers, and leave open floor at least as clear as the former solid green wall presentation.
- Every visible shared wall has exactly one canonical decorative edge. Adjacent cells, full rerenders, projection updates, and observer zoom changes produce no doubled foliage, dark overlap, seam, flicker, or mismatched variant.
- The documented deterministic selection maps canonical visible wall edges to all six approved intact variants with representative distribution. The same edge and orientation produce the same variant across rerenders and roles, while no random state, hidden topology, or new protocol field is required.
- North, east, south, and west walls plus continuous runs, corners, crossings, exposed ends, and projection boundaries align to within one CSS pixel at 34, 40, and 46 CSS-pixel cells and at representative device pixel ratios.
- The edge layer is derived exclusively from cells and passage/opened bits in the current server-approved projection. Tests confirm no unknown cell, missing edge, hidden route, remote breach, or explored-map decoration is inferred, retained, serialized, or rendered.
- Hedge artwork does not alter authoritative movement, wall collision, visibility, object pickup, exits, results, input hit targets, observer read-only behavior, or player focal positioning. No gameplay or protocol contract changes are introduced.
- Players, keys, exits, artifacts, landmarks, controls, and feedback remain above the hedge layer and clearly legible in normal color, grayscale, and representative color-vision checks. Decorative overhang creates no page or map-shell overflow and captures no pointer input.
- Each server-approved perseverance impact selects the matching approved stage on the exact currently visible wall. Stages do not advance from local input, duplicate or reordered messages, impacts on another edge, or events outside the current projection.
- A matching fifth-impact transition briefly uses the approved breakthrough frame in normal-motion mode and settles on one approved permanent breached variant from the next authoritative opened-edge projection. The visual gap aligns bidirectionally and cannot be mistaken for a closed wall.
- All three approved breached variants appear through a stable deterministic mapping. They remain visible only while their authoritative opened edge is projected, survive ordinary rerenders without changing variant, and leave no stale decoration after reset, match end, disconnect, or projection loss.
- Reduced-motion mode uses static impact stages and changes immediately to the permanent breach without maze shake, foliage sway, moving leaf fragments, or breakthrough travel animation. Meaning does not depend on motion, sound, vibration, text, or green hue alone.
- The renderer has a tested fixed upper bound for hedge elements, transient effects, timers, and memory based on the maximum supported projection. Repeated snapshots and impacts do not accumulate nodes, listeners, styles, or timers, and obsolete edge elements are removed deterministically.
- Observer rendering uses the same approved family and canonical edge logic, keeps full-map pan and zoom responsive, and preserves continuous intact and breached silhouettes at overview and inspection scales.
- Automated browser tests cover variant selection, shared-edge deduplication, every orientation and join, projection boundaries, hidden-state isolation, layering, impact and breach transitions, cleanup, reduced motion, observer zoom, supported cell sizes, and zero viewport overflow.
- The runtime SVG is local, valid, script-free, raster-free, deterministically generated, and included in web-asset reproducibility checks. All host tests pass, compressed web assets remain within the 1.5 MiB budget and LittleFS partition, and firmware remains within the factory partition.
- The project owner confirms on representative mobile and observer screens that the maze resembles a varied living hedge from above, repetition is not distracting, leaves and branches are visible, objects remain easy to find, impacts visibly damage the correct bush, and permanent openings are clear, or explicitly approves a documented evidence deviation.
Evidence
The following planning-time state defines this milestone:
- On 2026-09-10, the project owner requested a separate future milestone to integrate the living-hedge samples generated and approved by Milestone 023. The approved review source contains six intact variants, four impact stages, one breakthrough frame, three permanent breach variants, and topology examples, but remains outside runtime assets at planning time.
- Runtime SVG derivation, browser integration, tests, resource measurements, physical-device verification, and owner acceptance are deferred to this milestone. No implementation was performed while creating the plan.
- 2026-09-11 implementation evidence: the local
web/hedges.svgruntime sprite contains the approved intact, impact, breakthrough, and breach symbols. Player and observer maps derive a bounded canonical hedge-edge list only from their current projections; a full 32 by 32 projection is bounded to 2,112 edge elements. The renderer uses deterministic edge hashes and one restartable breakthrough timer, with immediate reduced-motion settlement. - 2026-09-11 verification:
bash scripts/run_host_tests.shpassed.node --test test/web/test_player_app.js test/web/test_hedge_svg.jspassed all 26 browser tests.python3 scripts/verify_web_assets.pypassed with a 29,912 / 1,572,864 byte compressed payload. - 2026-09-11 skipped check:
pio runcould not acquire/home/sasa/.platformio/platforms.lockbecause the global PlatformIO home is read-only. Firmware size verification, physical-device visual testing, and project-owner mobile and observer acceptance remain required. Milestone 024 therefore remainsIN_PROGRESS. - 2026-09-11 user-run verification:
./scripts/run_host_tests.shpassed all host and bundled player browser checks;node --test test/web/test_player_app.js test/web/test_hedge_svg.jspassed all 26 tests; andpython3 ./scripts/verify_web_assets.pyconfirmed deterministic assets at 29,912 / 1,572,864 bytes. - 2026-09-11 physical-device diagnostic: host
192.168.1.103/24reached the connected ESP at192.168.1.50with two ICMP replies and no packet loss, butcurl --noproxy '*' http://192.168.1.50/healthwas refused on TCP port 80. The device is reachable but its HTTP server is not listening; no flash or reset was performed. Physical visual acceptance remains pending. - 2026-09-11 physical-device repair and verification: boot logs identified
ESP_ERR_HTTPD_HANDLERS_FULLafter adding the hedge sprite route. The bounded URI handler capacity was raised from 13 to 14. A writable temporary PlatformIO home built and uploaded the application and LittleFS image through/dev/ttyACM0to the ESP32-C6FH4; every upload hash was verified. Firmware uses 1,029,600 / 2,097,152 factory bytes (49.1% flash) and 59,940 / 327,680 bytes RAM (18.3%); LittleFS uses 2,031,616 / 2,031,616 bytes. After reboot,GET /healthreturned200with{"status":"serving","healthy":true,"version":"0.14.0","errorCode":0}, while GET requests for/and/hedges.svgboth returned200(3,378 and 1,106 bytes respectively). Project-owner visual acceptance of the mobile and observer hedge rendering remains required before the milestone can becomeCOMPLETE. - 2026-09-11 project-owner mobile review rejected the first runtime hedge presentation because foliage obscured the wall boundaries and made open routes substantially harder to understand than the former green strokes. The corrective contract limits foliage to 20% of a cell, adds a continuous high-contrast spine only to closed edges, and raises the contrast of the inset cell floor. Repeat physical-device visual acceptance remains required.
- 2026-09-11 corrective verification: a local rendered maze preview confirmed
that the continuous spine restores exact closed-edge continuity while opened
edges remain uninterrupted passages.
node --test test/web/test_player_app.js test/web/test_hedge_svg.jspassed all 26 tests,bash scripts/run_host_tests.shpassed, and web-asset reproducibility passed at 30,012 / 1,572,864 compressed bytes. A clean PlatformIO build passed with firmware at 1,029,600 / 2,097,152 bytes and RAM at 59,940 / 327,680 bytes;pio run -t buildfsalso passed for the 2,031,616-byte LittleFS image. - 2026-09-11 deployment was not attempted because no
/dev/ttyACM*or/dev/ttyUSB*device was present and the ESP HTTP endpoint at192.168.1.50:80was unreachable from the host. The already-open browser tab still shows its previously loaded assets. Upload and repeat project-owner mobile visual acceptance remain required, so Milestone 024 staysIN_PROGRESS. - 2026-09-11 repeat project-owner review rejected the first readability
correction: its added bright spine and emphasized floor insets produced a
confusing rectangular grid, while the player still appeared to stand on a
hedge. Browser geometry inspection found two root causes: rotating a
horizontal SVG wrapper into a vertical edge left its center displaced by
half a cell on both axes, and SVG instances had no
160 x 40view box, so source-coordinate foliage overflowed far beyond its owning cell edge. The next correction removes the spine and floor emphasis, restores the quiet cell surface, constrains every SVG to one edge, and aligns vertical wrapper centers with authoritative boundaries. Repeat physical-device visual acceptance remains required. - 2026-09-11 second corrective verification: a rendered 7 by 7 maze at the
minimum gameplay cell scale showed each
160 x 40hedge constrained to one edge, vertical and horizontal centers aligned, the player wholly inside its cell, and continuous open-floor routes without the rejected rectangular overlay.bash scripts/run_host_tests.shpassed, including all bundled browser checks, and web-asset reproducibility passed at 29,979 / 1,572,864 compressed bytes. A clean PlatformIO build passed with firmware at 1,029,600 / 2,097,152 bytes and RAM at 59,940 / 327,680 bytes; the configured 2,031,616-byte LittleFS image also built successfully.
Milestone 025 — Mobile welcome and lobby scrolling
Status: COMPLETE
Objective
Allow welcome and lobby content to scroll naturally on supported mobile viewports while keeping the active player match fixed and its touch controls unchanged.
Scope
- Remove the viewport-wide mobile scroll lock that prevents welcome and lobby actions from being reached.
- Apply scroll locking only while the player match screen is active.
- Preserve safe-area padding, desktop layout, observer behavior, and gameplay map and control gesture handling.
Deliverables
- Screen-specific mobile scrolling CSS and state wiring.
- Deterministic browser-source tests for mobile overflow, safe-area padding, and active-match scroll locking.
Acceptance criteria
- At 412 by 915, 412 by 916, smaller supported mobile viewports, and compact landscape, overflowing welcome and lobby content can be vertically scrolled and the launch button can be reached.
- Bottom safe-area padding remains present on welcome and lobby content.
- An active player match remains viewport-fixed, and its map and directional controls retain their existing non-scrolling touch behavior.
- Automated tests cover the screen-specific scroll-lock contract and pass.
Evidence
- On 2026-09-12, the mobile-wide
html, bodyand#appscroll lock was replaced with amatch-activeclass toggled only by the active player match screen. Welcome and lobby retain their natural document height and their safe-area bottom padding; the match retains its bounded map, fixed viewport, andtouch-action: nonecontrol surface. - On 2026-09-12,
node --test test/web/test_player_app.js test/web/test_hedge_svg.jspassed all 25 tests, including the new screen-specific mobile scrolling contract for 412 by 915, 412 by 916, smaller portrait, and compact-landscape viewport cases. - On 2026-09-12,
bash scripts/run_host_tests.shpassed all host and bundled browser tests, andpython3 scripts/verify_web_assets.pypassed with a deterministic 29,999 / 1,572,864 byte compressed web payload. - On 2026-09-12,
/home/sasa/.platformio/penv/bin/pio run --target buildfssucceeded. The generated LittleFS image uses 2,031,616 / 2,031,616 bytes. - The in-app browser was not opened because the project owner explicitly instructed that it must not be used for this change.
- On 2026-09-12, the project owner checked the lobby UI and confirmed that it works as expected. This supplies the requested visual acceptance evidence for mobile scrolling and reachable lobby actions.
Milestone 026 — Optional help overlay at match start
Status: COMPLETE
Objective
Transition directly from the server-authoritative countdown to the active maze without automatically opening the optional player help overlay.
Scope
- Remove only the automatic help-opening behavior from the active player-view transition.
- Preserve the existing help replay button, help navigation, close controls, countdown timing, input controls, and server protocol.
- Validate the transition on mobile and desktop layouts.
Deliverables
- Updated player transition wiring.
- Automated tests for direct maze activation and opt-in help behavior.
- Mobile and desktop browser verification evidence.
Acceptance criteria
- When the countdown finishes, the active player maze becomes visible and its enabled controls are immediately usable without a help-overlay flash.
- The help overlay remains closed after the countdown.
- The existing help button opens the overlay, and its close and final-step controls return the player to the active maze.
- Existing countdown timing, authoritative match-start messages, controls, and mobile and desktop layouts remain unchanged.
- Automated tests and browser verification pass.
Evidence
- On 2026-09-12,
renderSnapshot()was changed to dismiss the countdown modal after an active player projection without invokingshowOnboarding(). The existingonboarding-replaybutton and the skip and final-step close paths remain unchanged. - On 2026-09-12,
node --test test/web/test_player_app.js test/web/test_hedge_svg.jspassed all 26 tests. The new countdown-completion test verifies direct match-screen activation and enabled controls, no automatic help opening, and the preserved optional open and close paths. - On 2026-09-12,
bash scripts/run_host_tests.shpassed, andpython3 scripts/verify_web_assets.pypassed with a deterministic 29,984 / 1,572,864 byte compressed web payload. - On 2026-09-12,
/home/sasa/.platformio/penv/bin/pio run --target buildfssucceeded with the LittleFS image using 2,031,616 / 2,031,616 bytes. - In-app browser verification is pending because the current in-app browser connection is unavailable.
- On 2026-09-12, the project owner checked the optional help overlay and confirmed that it is not shown automatically when a game starts. This supplies the missing visual acceptance evidence for Milestone 026.
Milestone 027 — Playful breakthrough movement and prominent hedge gaps
Status: COMPLETE
Objective
Make a successful perseverance breakthrough feel exciting and immediately understandable to children by synchronizing the player's movement through the wall with a short playful vegetation effect, and by enlarging and sharpening the permanent opening so it is as easy to recognize as an ordinary maze passage on supported mobile and desktop screens.
Scope
- Do not start implementation until every preceding milestone is
COMPLETE. Milestone 026 is stillIN_PROGRESSwhen this plan is created, so this milestone must remainNOT_STARTEDuntil its visual acceptance is recorded. - Preserve the Milestone 022 authoritative rule: the ESP opens the wall and moves the player on the fifth qualifying impact. The browser may animate only a received server-approved breakthrough and must never predict an opening, delay the authoritative position, or change collision behavior.
- Reconcile the breakthrough presentation in
docs/UI_STYLE.md,docs/PLAYER_UI.md, andARCHITECTURE.mdbefore changing runtime assets. - Replace the understated fifth-impact transition with one short, child-friendly sequence: the hedge compresses away from the impact point, the player performs a readable squash-and-stretch hop or energetic dash through the opening, and a small bounded burst of leaves and soft sparkle shapes follows the motion. Keep the effect cheerful rather than violent, alarming, or realistic.
- Synchronize the effect with the authoritative origin and destination cells. The player must begin at the old visible position, cross the exact opened edge, and settle at the new server-approved position without teleport-like flicker, duplicate avatars, or a frame spent on top of a closed hedge.
- Keep the complete breakthrough sequence brief enough not to slow repeated play, target approximately 350--550 ms in normal-motion mode, and do not block directional input longer than required to reconcile the authoritative snapshot. Later snapshots must safely supersede an unfinished animation.
- Increase the clear center of every permanent breached-hedge variant. At the minimum supported cell size, the visible gap must occupy at least 60% of the traversable edge span, with the remaining foliage pulled into two distinct side clusters rather than forming a faint interruption in an otherwise continuous wall.
- Give breached edges an unmistakable top-down silhouette using bent branches, displaced leaves, and a subtle trampled-ground or scattered-leaf cue on both sides of the opening. Do not add a bright line, rectangular floor grid, arrow, warning text, or color-only marker.
- Ensure the enlarged decoration reads as open space from either direction and in horizontal and vertical orientations. It must not resemble a thin closed wall, cover the passage center, overlap the player, key, artifact, exit, or door, or make adjacent closed edges ambiguous.
- Keep authoritative maze geometry, pathfinding, visibility, hit testing, object interactions, and protocol contents unchanged unless a documented contract gap proves that an existing breakthrough event cannot identify the required visible edge and movement transition.
- Reuse the local SVG hedge family where practical. Any new SVG shapes must be editable, script-free, raster-free, locally packaged, and consistent with the approved top-down leaves, twigs, outlines, and palette.
- Bound all temporary leaves, sparkles, DOM nodes, timers, and animation state. Coalesce or replace repeated effects and clear them on projection loss, screen change, pause, disconnect, result, reset, and reduced-motion changes.
- In reduced-motion mode, immediately place the player at the authoritative destination and switch to the enlarged permanent gap with a brief static emphasis only. Do not require translation, shake, squash, flying leaves, sparkle motion, sound, vibration, or color to communicate success.
- Apply the improved permanent gap to player and observer maps. The observer does not need player travel animation but must see the same clear opened-edge silhouette at overview and inspection zoom levels.
- Preserve supported mobile layouts, directional controls, player focal-point positioning, Russian player-facing copy, offline operation, and firmware and LittleFS budgets.
Deliverables
- Updated breakthrough motion, permanent-gap, layering, timing, cleanup, and reduced-motion contracts in canonical and supporting documentation.
- Revised horizontal and vertical permanent-breach SVG variants with a larger clear center and distinct displaced-foliage silhouettes.
- A bounded, server-triggered player breakthrough animation with synchronized hedge compression, player movement, and playful leaf and sparkle accents.
- Player and observer renderer updates that use the more prominent permanent breach without changing authoritative maze geometry.
- Deterministic browser tests, web-asset validation, host regression results, size evidence, and representative mobile, desktop, observer, reduced-motion, and physical-device visual verification.
Acceptance criteria
- A fifth qualifying impact still causes exactly one server-authoritative wall opening and movement. The browser starts the effect only from the matching authoritative breakthrough response and never predicts, repeats, or delays gameplay state.
- In normal-motion mode, the player visibly travels from the authoritative origin cell through the exact breached edge to the authoritative destination in one coherent 350--550 ms sequence, without teleport flicker, duplicate avatars, stale positions, or overlap with an apparently closed wall.
- The sequence includes a cheerful and visually coherent hedge compression or parting action, player squash-and-stretch hop or dash, and a bounded leaf and sparkle accent. Its meaning remains clear with sound and vibration disabled.
- At 34, 40, and 46 CSS-pixel cell sizes, the clear center of each permanent breached variant spans at least 60% of the traversable edge and is visibly wider than the pre-milestone breach. Automated geometry checks cover every horizontal and vertical variant.
- Every permanent breach reads as an open passage at a glance from both sides and in all orientations. Its two separated foliage clusters, bent branches, and restrained ground debris remain distinguishable from an intact wall in normal color, grayscale, and representative color-vision checks.
- The enlarged gap does not obscure or overlap the player, keys, artifacts, doors, exits, controls, neighboring passage centers, or closed-edge silhouettes, and it introduces no map-shell or page overflow.
- Unrelated successful movement retains its existing behavior. The special travel effect is used only for a confirmed fifth-impact breakthrough and cannot replay from duplicate, delayed, reordered, or stale events.
- A newer authoritative snapshot safely completes, replaces, or cancels an unfinished visual transition while leaving exactly one player at the latest approved cell and the correct current projected topology.
- The implementation documents and tests fixed upper bounds for temporary nodes, particles, timers, and queued effects. Pause, disconnect, result, reset, screen change, and projection loss leave no stale transforms, decorations, classes, or animation state.
- Reduced-motion mode performs no travel, shake, squash, flying-leaf, or sparkle animation. It immediately shows the authoritative destination and the same prominent permanent gap with a non-motion, non-color-only cue.
- Player and observer maps render the same deterministic breached-edge family. Observer pan and zoom remain responsive, and openings stay legible at overview and inspection scales without changing observer read-only behavior.
- Browser tests cover authoritative triggering, timing, origin-to-destination alignment, all breach variants and orientations, the minimum 60% gap, deduplication, interrupted transitions, cleanup, reduced motion, supported mobile and desktop viewports, observer rendering, layering, and overflow.
- All host and browser tests pass; SVG and compressed-web-asset validation passes; the payload remains within the 1.5 MiB web budget and LittleFS partition; and firmware remains within the factory partition.
- Physical-device testing confirms stable two-player play during repeated and simultaneous breakthroughs, with no input lockup, stale avatar, lost projection, accumulated effects, or visible performance regression.
- The project owner confirms on a representative mobile browser that the breakthrough effect is fun and understandable for children, the movement feels connected to breaking the bushes, and the resulting passage is immediately obvious and at least as readable as an ordinary open route, or explicitly approves a documented evidence deviation.
Evidence
- On 2026-09-12, the project owner reported that the current permanent breach is too small and visually weak, and requested a larger, sharper gap plus a child-friendly special effect for player movement through a broken hedge.
- Planning-time inspection of the active mobile game at
http://192.168.1.50/confirmed that the small interruption and remaining near-continuous foliage silhouette make the breach difficult to distinguish from an intact hedge at gameplay scale. - On 2026-09-12, implementation began after Milestone 026 was completed. The player renderer now recognizes only a newly opened current-projection edge paired with an adjacent authoritative self move, uses a single replaceable 480 ms runner with four decorative accents, and clears it on newer views, control reset, screen change, and reduced motion. Permanent breach symbols now reserve a 96 / 160 (60%) clear centre.
- On 2026-09-12,
node --test test/web/test_player_app.js test/web/test_hedge_svg.jspassed all 30 tests, including authoritative opening detection, fixed 480 ms travel, four-accent bounds, reduced-motion cleanup, and all three 60%-centre breach variants. - On 2026-09-12,
bash scripts/run_host_tests.shpassed all portable and bundled browser tests.python3 scripts/verify_web_assets.pypassed with a deterministic 31,071 / 1,572,864 byte compressed web payload, and/home/sasa/.platformio/penv/bin/pio run --target buildfssucceeded with a 2,031,616 / 2,031,616 byte LittleFS image. - Mobile, desktop, observer, reduced-motion, and physical-device visual
acceptance remain required before this milestone can be marked
COMPLETE.
Milestone 028 — Compact settings menu
Status: COMPLETE
Objective
Reduce persistent browser-interface clutter on every supported viewport by consolidating secondary connection and player-preference controls into one compact settings menu opened by a gear button beside the Wi-Fi connection indicator.
Scope
- Do not start implementation until every preceding milestone is
COMPLETE. Milestone 027 isCOMPLETEwhen this plan is created; this milestone remainsNOT_STARTEDbecause implementation was not requested. - Keep the Wi-Fi connection indicator visible and place one persistent gear-icon menu button next to it on welcome, lobby, active-player, observer, pause, and result screens where the corresponding secondary actions are available.
- Remove the separate persistent buttons for connection details, sound, reduced motion, and help. Expose those actions as clearly labelled Russian items in the gear-button popover.
- Move the Wi-Fi settings action out of the connection-details panel and into the same popover. Do not duplicate the Wi-Fi settings action elsewhere in the normal connected application flow.
- Preserve the existing behavior and current state of connection details, sound, reduced motion, the optional help overlay, and Wi-Fi settings. The sound and reduced-motion items must visibly and accessibly communicate their current on/off state.
- Keep primary gameplay actions outside the settings menu, including movement, avatar drag, key drop, lobby participation, match start, observer navigation, and result actions.
- Make the popover usable with touch, pointer, and keyboard. Give its trigger an accessible Russian name and exposed open/closed state; provide predictable focus order; close it after an applicable navigation action, on outside activation, and with Escape; and restore focus to the trigger when appropriate.
- Prevent the popover from covering essential controls or escaping the viewport. Anchor it to the top-right status area and allow it to reposition or constrain its height on narrow portrait, compact landscape, desktop, and safe-area-inset layouts.
- Preserve Russian player-facing copy, offline operation, observer read-only behavior, server-authoritative game state, supported browser layouts, and firmware and LittleFS budgets.
Deliverables
- Updated compact-settings-menu behavior and responsive-layout contracts in
docs/UI_STYLE.mdand any affected supporting UI documentation. - One reusable gear-triggered settings popover with connection details, sound, reduced motion, help, and Wi-Fi settings actions shown only when applicable.
- Removal of the superseded standalone secondary-control buttons and of the duplicated Wi-Fi settings entry from the connection-details panel.
- Deterministic browser tests for visibility, state reflection, action routing, keyboard and pointer dismissal, focus behavior, responsive placement, and absence of duplicate controls.
- Browser, host-regression, compressed-web-asset, LittleFS, and representative mobile and desktop visual-verification evidence.
Acceptance criteria
- Every supported screen and viewport shows at most one persistent secondary settings control: a gear button beside the visible Wi-Fi connection indicator. The previous standalone connection-details, sound, reduced-motion, and help buttons are absent.
- Opening the gear button displays the applicable connection details, sound, reduced motion, help, and Wi-Fi settings actions with clear Russian labels. No primary gameplay or lobby action is moved into the menu.
- Connection details show the same live information as before, and their former standalone round button is not rendered.
- Sound and reduced-motion actions retain their existing behavior and reflect their current on/off state visually and through accessible state. Changing either setting updates the menu item without creating another persistent control.
- Help opens the existing optional help overlay without changing its content, navigation, close behavior, or the direct countdown-to-maze transition.
- Wi-Fi settings remain reachable from the menu through the existing configuration flow, and the action is not duplicated in the connection-details panel or elsewhere in the normal connected application flow.
- The trigger and popover work with touch, pointer, and keyboard. The trigger has an accessible Russian name and accurate expanded state; focus order is predictable; Escape and outside activation dismiss the popover; and focus returns safely without trapping the player.
- Opening, using, and closing the popover does not issue gameplay commands, change authoritative game state, enable observer input, or interfere with keyboard, swipe, directional-pad, avatar-drag, key-drop, pause, or result controls.
- At all supported mobile, compact-landscape, tablet, and desktop sizes, the closed layout is more compact than the current standalone-control layout. The open popover stays within safe areas and the viewport, remains readable, introduces no page or map overflow, and does not permanently cover essential controls.
- Automated tests cover screen applicability, absence of standalone and duplicate controls, state reflection, every routed action, dismissal, focus restoration, supported responsive layouts, and regression behavior.
- All host and browser tests pass; compressed-web-asset validation passes; the payload remains within the 1.5 MiB web budget and LittleFS partition; and firmware remains within the factory partition.
- Physical-device verification confirms that the menu and all five secondary actions work in the served application, and the project owner confirms on representative mobile and desktop browsers that the interface is less distracting and materially more compact.
Evidence
- On 2026-09-13, planning-time inspection of the active game at
http://192.168.1.50/identified the separate round connection-details button besideESP на связиand the standalone sound, reduced-motion, and help controls in the active-player layout. - On 2026-09-13, the project owner chose a gear icon beside the connection indicator, required all referenced secondary controls and Wi-Fi settings to move into its popover without duplicating the Wi-Fi action, and required the compact layout on all screen sizes.
- On 2026-09-13, implementation began with one header gear button, a bounded safe-area-aware settings popover, and the removal of the superseded details, sound, reduced-motion, and help buttons. The popover retains live connection information and Wi-Fi navigation on every screen, while player preferences and optional help appear only during an active player match.
- On 2026-09-13,
node --test test/web/test_player_app.js test/web/test_hedge_svg.jspassed all 31 tests. The compact-settings test covers the sole trigger, removed standalone controls, state labels, player-screen applicability, Escape and outside dismissal, and safe-area constrained placement. - On 2026-09-13,
bash scripts/run_host_tests.shpassed all host and bundled browser checks.python3 scripts/verify_web_assets.pypassed with a deterministic 31,420 / 1,572,864 byte compressed payload, and/home/sasa/.platformio/penv/bin/pio run --target buildfssucceeded with a 2,031,616 / 2,031,616 byte LittleFS image. - Mobile, desktop, and physical-device visual verification of all settings actions were pending until the project-owner check below.
- On 2026-09-13, the project owner checked the UI in every available mode and confirmed that the settings menu is present and works as expected. This supplies the required visual and physical-device acceptance evidence.
Milestone 029 — Low-overhead live connection quality
Status: COMPLETE
Objective
Show a compact, continuously understandable indication of the browser's live round-trip response to the ESP on every application screen, with an honest breakdown between measured ESP handling time and the remaining communication delay, without reducing gameplay responsiveness or consuming unbounded device resources.
Scope
- Do not start implementation until every preceding milestone is
COMPLETE. Milestone 028 isCOMPLETEwhen this plan is created; this milestone remainsNOT_STARTEDbecause implementation was not requested. - Reuse the existing versioned WebSocket heartbeat request and acknowledgement path. Correlate each probe with its unique request ID and use the browser's monotonic clock to measure complete application round-trip time.
- Measure heartbeat handling duration only with the ESP's monotonic clock, from bounded request handling to acknowledgement preparation, and return that duration in the matching acknowledgement. Never subtract an ESP timestamp from a browser timestamp or require clock synchronization.
- Derive the communication remainder as the non-negative difference between
browser-measured round-trip time and ESP-measured handling time. Label it
Связь, not pureWi-Fi, because it can also include TCP/WebSocket transport, task scheduling, and outbound queueing. Label the measured device intervalESPand the full round tripОтклик. - Keep one compact persistent quality indicator beside the existing connection status and settings trigger. Show a connection-quality symbol and rounded total milliseconds without expanding the header or displacing primary content.
- Make the
СвязьandESPvalues available in the existing connection details reached through the settings menu and in the indicator's accessible Russian description. Do not expose developer diagnostics or misleading claims about which person or device is at fault. - Show the indicator on welcome, lobby, active-player, observer, countdown, pause, and result screens. Before the first valid sample, while a sample is stale, and while disconnected, show explicit compact unknown or offline states instead of retaining an old number as if it were current.
- Define documented quality thresholds from representative physical-device measurements. Use stable good, delayed, and slow states with smoothing and hysteresis so normal jitter does not make the indicator flicker. State must not rely on color alone.
- Keep probing strictly bounded and lower priority than gameplay: at most one heartbeat may be outstanding per browser, normal cadence may not exceed one probe every five seconds, no retry burst is allowed, and a probe or its response must be skipped or dropped rather than delay a move, projection, pause, resume, or result message.
- Suspend probes while the WebSocket is closed and while the document is hidden. Clear pending measurements on disconnect, session reset, and socket replacement, then take one fresh sample after a successful connection before resuming the bounded cadence.
- Keep a small fixed-size sample window in the browser only. Do not persist or transmit latency history, player names, session tokens, network names, or hidden game state as part of this feature.
- Preserve observer read-only behavior, server-authoritative gameplay, existing connection-loss messaging, offline operation, Russian player-facing copy, responsive layouts, and firmware and LittleFS budgets.
Deliverables
- Updated measurement, protocol, quality-state, copy, and responsive-placement
contracts in
ARCHITECTURE.md,docs/UI_STYLE.md,docs/PLAYER_UI.md, and the Milestone 016 latency procedure where applicable. - A bounded heartbeat round-trip sampler and ESP handling-duration field with documented units, range validation, correlation, timeout, and stale-sample behavior.
- A compact always-applicable connection-quality indicator showing total
milliseconds, plus
СвязьandESPdetails integrated with the existing settings and connection-details interface. - Deterministic protocol, gateway, browser, accessibility, responsive-layout, scheduling, smoothing, timeout, reconnect, and backpressure tests.
- Before-and-after maximum-session latency, queue, traffic, heap, web-asset, firmware, LittleFS, and physical-device visual-verification evidence.
Acceptance criteria
- On every supported application screen, the header contains one compact
quality symbol and a rounded current total such as
85 мсbeside the existing connection status and gear trigger, without moving or obscuring primary game, lobby, observer, pause, or result controls. - Each displayed total is calculated from the browser monotonic send and matching-heartbeat-acknowledgement times. Responses with a missing, duplicate, expired, malformed, or mismatched request ID cannot update the indicator.
- The matching heartbeat acknowledgement carries a bounded ESP handling
duration measured solely with the ESP monotonic clock. The browser validates
the field and derives
Связь = max(0, Отклик - ESP)without comparing clocks from different devices. - Connection details show the same sample as three Russian-labelled values:
Отклик,Связь, andESP. Supporting text explains thatСвязьis a transport remainder and cannot isolate radio-only Wi-Fi delay or assign blame with certainty. - Before the first sample, after the documented stale timeout, and while disconnected, the indicator uses distinct unknown, stale, or offline text and shape states. It never presents an old millisecond value as current.
- Good, delayed, and slow thresholds, smoothing window, hysteresis, rounding, sample timeout, and stale timeout are fixed in canonical documentation and covered at every boundary by deterministic tests. Meaning remains clear in grayscale and without relying on animation or color alone.
- Each browser has at most one heartbeat in flight, sends no more than one normal probe per five seconds, keeps only the documented fixed number of recent samples, and produces no immediate retry loop after timeout, malformed input, disconnect, background suspension, or reconnect.
- Heartbeat work is constant and bounded on the ESP, causes no broadcast or authoritative-state mutation, and cannot occupy queue capacity needed by gameplay or lifecycle messages. Under backpressure its acknowledgement is discarded before any move, view, pause, resume, or result message.
- Pointer feedback, keyboard, swipe, directional-pad, avatar-drag, key-drop, rendering, and observer navigation remain responsive while sampling. The Milestone 017 controlled two-player and two-player/four-observer latency budgets continue to pass, with no increased dropped gameplay messages or queue high-water regression attributable to probes.
- Visibility changes, socket replacement, disconnect, reconnection, session reset, screen changes, and result transitions leave no orphan timer, duplicate probe, stale accessible label, or retained pending correlation.
- The indicator and its details remain readable and within the viewport on supported narrow portrait, compact-landscape, tablet, and desktop layouts, including safe-area insets and all player and observer states.
- Automated protocol, gateway, browser, host, and web-asset tests pass. The compressed payload remains within the 1.5 MiB web budget and LittleFS partition, and firmware remains within the factory partition.
- Physical-device testing with two players and four observers confirms the displayed total and split track controlled fast and delayed conditions, reconnect states recover correctly, and continuous sampling causes no perceptible input delay, animation stutter, disconnect, or gameplay regression.
- The project owner confirms on representative mobile and desktop browsers that the indicator is compact, understandable in every application state, and useful for recognizing a delayed ESP response without distracting from play, or explicitly approves a documented evidence deviation.
Evidence
- On 2026-09-13, planning inspection confirmed that the protocol already
supports a correlated
heartbeatacknowledgement and that the browser uses monotonic timing with a bounded 256-sample diagnostic tracker for accepted movement acknowledgements and painted projections. - On 2026-09-13, the project owner requested a compact icon with a millisecond value in every application state and asked to separate communication delay from ESP processing when this can be done without complicating or slowing gameplay.
- Planning analysis established that a single round trip cannot measure pure radio-only Wi-Fi latency. This milestone therefore specifies a directly measured ESP handling interval and an honestly labelled communication remainder, while keeping the displayed headline value equal to the complete browser-to-ESP application round trip.
- On 2026-09-13, implementation began with a single in-flight browser
heartbeat sampled no more often than every five seconds, a five-sample
median, 15-second stale timeout, and explicit unknown, stale, and offline
indicator states. Heartbeat acknowledgements now include bounded ESP-only
handlingMs, and the browser derives the non-negativeСвязьremainder. - On 2026-09-13,
node --test test/web/test_player_app.js test/web/test_hedge_svg.jspassed all 33 tests, including bounded heartbeat correlation, malformed-field rejection, smoothing, stale state, indicator copy, and compact header placement. - On 2026-09-13,
bash scripts/run_host_tests.shpassed.python3 scripts/verify_web_assets.pypassed with a deterministic 32,503 / 1,572,864 byte compressed payload./home/sasa/.platformio/penv/bin/pio runpassed with firmware at 1,029,808 / 2,097,152 bytes and/home/sasa/.platformio/penv/bin/pio run --target buildfspassed with a 2,031,616 / 2,031,616 byte LittleFS image. - Physical-device latency measurements and mobile and desktop visual acceptance were pending until the project-owner check below.
- On 2026-09-13, the project owner manually checked the quality indicator and
accepted
ESP: 0 мсas the correct rounded display for the measured sub-millisecond heartbeat handling interval. The project owner found no UI issue and accepted the indicator on the available application screens.
Milestone 030 — Explicit session exit and slot release
Status: COMPLETE
Objective
Let an authenticated player or observer deliberately leave through the top-right settings menu, revoke only that browser's session, immediately free its slot, and return it to the name-entry screen with its previous display name prefilled. A player leaving any non-waiting match phase, including a finished result, must authoritatively cancel that match and return every remaining participant to the lobby, while an observer departure or a lobby departure must not reset gameplay.
Scope
- Do not start implementation until every preceding milestone is
COMPLETE. Milestone 029 isCOMPLETEwhen this plan is created; this milestone remainsNOT_STARTEDbecause implementation was not requested. - Add one clearly labelled Russian
Выйти из игрыaction to the existing gear-triggered settings menu. Show it only while the browser owns an authenticated player or observer session; do not show it on the unauthenticated welcome and name-entry screen. - Require a modal confirmation before sending an exit command. For a player in countdown, active, finish-pending, paused, or finished state, clearly warn that the current match will return everyone to the waiting lobby and the player's slot will be freed. For a player in the lobby or an observer in any phase, explain that only this browser's place will be released.
- Treat opening or cancelling the confirmation as browser-local UI only. While it is open, suppress input from that browser without pausing or otherwise mutating authoritative state; cancelling must restore the previous screen, focus, controls, and session unchanged.
- Introduce a versioned authenticated explicit-leave protocol command and a correlated success response. It must be distinct from an unexpected socket disconnect, page reload, or transient connection loss, which retain the existing 60-second player reconnection contract.
- On an accepted player leave during countdown, active, finish-pending, paused, or finished state, stop all match timers, clear the current authoritative match, return the lobby to its waiting state, retain every other connected session and role, revoke the leaving session, free its player slot immediately, and broadcast the cancellation and fresh lobby state to the remaining players and observers.
- On an accepted player leave while already in the waiting lobby, revoke only that player's session and free its slot.
- On an accepted observer leave in any phase, revoke only that observer's session and free its observer slot. Never pause, reset, finish, restart, or otherwise mutate the match because an observer leaves.
- Preserve the roles and sessions of all participants who did not leave. If Player 1 leaves, Player 2 remains in its existing slot and a later player may claim the newly free Player 1 slot and become the lobby owner under the existing ownership rule.
- Invalidate the departed session token on the ESP before confirming success. The revoked token must never restore its former role, slot, position, inventory, or reconnect reservation, and a duplicate or replayed leave must be harmless.
- After confirmed success, remove the session token from browser-local storage, clear all session, match, input, timer, heartbeat, animation, and pending command state, and show the welcome/name-entry screen without requiring a page reload.
- Store the last successfully used display name separately from the temporary session token in a bounded browser-local value. Prefill it on return to the name-entry screen, but still apply normal name validation and duplicate-name rejection if the user tries to join again.
- Do not claim that the slot was released before the ESP acknowledges the explicit leave. If the command cannot be delivered or confirmed, show a concise Russian error and retain honest reconnect behavior rather than silently turning a network failure into a successful exit.
- Bound command size, correlation state, acknowledgement waiting, and cleanup. The leave path must not disclose hidden maze state, permit one participant to revoke another session, bypass observer read-only gameplay rules, or create a new unbounded queue or retry loop.
- Preserve the compact header, live connection-quality indicator, Wi-Fi and preference actions, responsive layouts, offline operation, Russian player-facing copy, and firmware and LittleFS budgets.
Deliverables
- Updated explicit-leave, match-cancellation, slot-release, session-revocation,
and reconnect contracts in
MVP.md,ARCHITECTURE.md,docs/PROTOCOL.md,docs/UI_STYLE.md, anddocs/PLAYER_UI.md. - A bounded authenticated leave command and response implemented across the protocol parser, lobby, authoritative game lifecycle, gateway, and browser.
- A settings-menu exit action with role- and phase-appropriate confirmation, failure handling, focus restoration, and successful return to the name-entry screen.
- Separate bounded persistence for the last player display name and complete cleanup of the revoked temporary session.
- Deterministic protocol, lobby, gateway, game-lifecycle, browser, accessibility, race, replay, reconnect, responsive-layout, and regression tests.
- Host, browser, web-asset, firmware, LittleFS, maximum-session, and physical-device verification evidence.
Acceptance criteria
- An authenticated player or observer sees exactly one
Выйти из игрыitem in the gear menu on every applicable screen. An unauthenticated browser sees no exit item, and primary game controls remain outside the menu. - Selecting the item opens a keyboard- and touch-accessible confirmation modal and sends no protocol command until explicit confirmation. Cancelling, pressing Escape, or activating the modal's cancel action leaves the session and authoritative state unchanged and safely restores focus.
- A player confirmation during countdown, active, finish-pending, paused, or finished state explicitly says that the match will return everyone to the waiting lobby and the slot will be freed. Confirmation produces exactly one authoritative cancellation, stops every relevant timer, clears the current match, and returns all remaining connected participants to a fresh waiting lobby.
- The leaving player's session is removed and its slot becomes available in the same accepted server transition. Every other player's and observer's session, role, and connection remain valid, and the resulting lobby snapshot consistently shows the newly free slot.
- A player leaving the waiting lobby releases only that player's session and slot. A player leaving a completed result performs the same authoritative return-to-lobby transition as another non-waiting player exit, without disconnecting any remaining participant.
- An observer leaving during any phase releases only that observer session and observer slot. Match phase, players, timers, maze, objects, inventory, and results remain byte-for-byte unchanged except for the expected lobby connection revision or participant count.
- The explicit leave command authenticates the sending session and cannot name or revoke another token or slot. Duplicate, stale, reordered, malformed, unauthorized, and replayed requests cause no second cancellation, no extra slot release, and no unrelated state mutation.
- An accepted explicit leave never starts the 60-second reconnect reservation. A normal page reload, accidental socket loss, or unconfirmed exit continues to follow the existing reconnect and pause rules without behavioral change.
- Only after the correlated success response, the departing browser deletes its temporary session token, clears all role and gameplay state, stops all input, animation, heartbeat, reconnect, and pending-command work, and shows the welcome/name-entry screen without a full reload or stale map flash.
- The last successfully used display name survives separately from the revoked token and is prefilled in the name field after exit and on a later visit. It is bounded to the existing name limit, contains no token data, and receives the normal validation and uniqueness checks when reused.
- If the leave request cannot be sent, times out, loses its socket, or receives a rejection, the browser does not report a successful release. It presents concise Russian recovery information, retains bounded state, and neither retries indefinitely nor corrupts a later reconnect or successful leave.
- If Player 1 leaves, Player 2 remains in Player 2's slot and a new player can immediately claim the free Player 1 slot, becoming the lobby owner and gaining start authority only through the existing server rule. Equivalent behavior applies when Player 2 leaves.
- The exit item and confirmation fit supported narrow portrait, compact-landscape, tablet, and desktop layouts, respect safe areas, avoid page and map overflow, and remain distinguishable without relying only on color, sound, vibration, or animation.
- Automated tests cover every role and phase, confirmation and cancellation, authoritative match cancellation, retained participants, immediate slot reuse, token revocation, remembered-name prefill, disconnect races, idempotence, authorization, cleanup, accessibility, and responsive layout.
- All host and browser tests pass; compressed-web-asset validation passes; the payload remains within the 1.5 MiB web budget and LittleFS partition; and firmware remains within the factory partition.
- Physical-device testing confirms that a player can cancel a non-waiting match, including a finished result, and immediately release its slot, an observer can leave without affecting play, the remaining browsers receive the correct lobby state, and the departing browser returns to a prefilled name-entry screen without stale controls, delayed input, or reconnect side effects.
- The project owner confirms on representative mobile and desktop browsers that the exit action is easy to find but difficult to trigger accidentally, its consequences are clear, and all role-specific outcomes match the intended behavior, or explicitly approves a documented evidence deviation.
Evidence
- On 2026-09-13, planning inspection confirmed that unexpected player socket loss currently pauses an active match and reserves the player session for a 60-second reconnect, while observer disconnect does not affect gameplay. Therefore immediate voluntary slot release requires an explicit authenticated lifecycle command rather than client-only token deletion or socket closure.
- On 2026-09-13, the project owner required a settings-menu exit action with a confirmation modal, complete session-token revocation, immediate slot release, and return to the name-entry screen with the previous player name prefilled.
- The project owner also required an unfinished match to be cancelled for all participants when a player leaves, while a lobby player or observer departure must affect only the departing session.
- On 2026-09-13, implementation began with an authenticated
leavecommand, immediate server-side session revocation, player-match cancellation, and a browser confirmation flow that clears the temporary token only after the correlated acknowledgement. - On 2026-09-13, a corrective lifecycle inspection found that the explicit
player-leave handler excluded
MAZE32_GAME_PHASE_FINISHEDfrom its match reset condition. It therefore revoked the departing result-screen session but did not broadcastreset, which could leave remaining clients behind an unusable result modal. The handler now resets for a player leaving every non-waiting lobby phase, including a completed result; it keeps waiting-lobby player exits and observer exits session-scoped. The reset is broadcast before the fresh lobby snapshot, and browser reset handling hides the modal and renders the waiting lobby. - On 2026-09-13,
node --test test/web/test_player_app.jspassed 31 tests, including the result-phase leave regression that verifies the confirmation warning, server reset condition, correlated token cleanup, modal dismissal, and lobby rendering.bash scripts/run_host_tests.shpassed all suites; its lobby/protocol suite reported explicit leave session revocation and slot reuse as passing, including invalid-token restoration after either Player 1 or Player 2 leaves, retained player and observer sessions, observer-only leave, duplicate leave idempotence, and immediate replacement of both player slots.python3 scripts/verify_web_assets.pypassed with deterministic payload3fb28e1ae55deba0ac592bcd99d81b7e95271604a5b35d7f293c4b8bffef6143at 33,183 / 1,572,864 bytes./home/sasa/.platformio/penv/bin/pio runpassed at 1,030,256 / 2,097,152 factory bytes, and/home/sasa/.platformio/penv/bin/pio run --target buildfspassed at 2,031,616 / 2,031,616 LittleFS bytes. - On 2026-09-13, the project owner checked the corrected result-screen exit flow in the browser and confirmed that the issue was fixed: a remaining participant is returned from the result modal to a usable waiting lobby when the other player explicitly leaves.
Milestone 031 — Authoritative persistent player trails
Status: IN_PROGRESS
Objective
Show a persistent dotted record of every successful route used by each player during the current match, using the established yellow and blue ownership colors and a mixed two-color treatment for shared passages. Each player may see trail data only inside the current server-approved visibility projection, while observers may see both complete trails. Trails must survive refresh and reconnection without adding client requests or materially increasing ESP, network, or rendering load.
Scope
- Do not start implementation until every preceding milestone is
COMPLETE. Milestone 030 isIN_PROGRESSwhen this plan is created, so this milestone must remainNOT_STARTEDuntil Milestone 030 is complete. - Keep trail ownership authoritative on the ESP. A browser-only history cannot provide consistent trails to another player and observers after refresh or reconnection, and must not be used as the source of truth.
- Record only accepted authoritative player transitions between cells. Mark ordinary successful moves and the successful fifth-impact movement through a newly opened hedge. Do not mark blocked, rejected, rate-limited, stale, queued-but-superseded, or otherwise unsuccessful movement attempts.
- Represent an undirected traversed cell edge with a compact two-bit ownership mask: Player 1, Player 2, both, or neither. Use a fixed-size per-match layout derived from the bounded maze dimensions; do not allocate per step, store an ordered movement history, or create a collection that grows with match duration.
- Preserve the trail for the complete current match, including countdown-to- active lifecycle, pause, normal reconnect, and page refresh. Clear it exactly when authoritative match state is reset, cancelled, replaced by a new match, or cleared by device reboot.
- Piggyback compact trail ownership on the existing role-appropriate view projection and sequencing path. Do not add polling, a new browser request, an extra message per movement, or a second trail-specific broadcast.
- Send player clients only the trail edges whose endpoints and passage are inside their current authorized projection. Never serialize trail coordinates outside the current visibility radius, and do not let the browser retain or reconstruct hidden maze geometry from past projections.
- Send observers the complete authoritative trail together with their existing full-map projection. Preserve observer read-only behavior and existing snapshot chunk, message-size, queue, and backpressure bounds.
- Reconstruct the visible trail entirely in the frontend from each complete server-approved projection. Prefer a fixed small number of batched SVG paths or an equivalently bounded drawing layer rather than one DOM element per dot, step, or historical event.
- Render a dotted yellow route for Player 1 and a dotted blue route for Player 2. When both players used the same passage, render an unmistakable mixed yellow-and-blue dotted pattern on the same centerline rather than overwriting one owner or blending them into an unrelated third color.
- Draw trails along traversable passage centers below players, keys, artifacts, exits, doors, feedback, and controls but above the quiet floor. Trails must not resemble walls, obscure objects, imply that a closed hedge is passable, or expose a breakthrough until its opened edge is present in the same authoritative projection.
- Keep current-player and other-player ownership understandable without color alone by using a documented dot rhythm, outline, alternation, or equivalent secondary cue consistent with the established player icon system. Shared edges must remain recognizable in grayscale and at supported cell sizes.
- Preserve server-authoritative movement, fog of war, match results, explicit session exit, reconnect behavior, compact controls, connection-quality sampling, reduced-motion behavior, offline operation, Russian player-facing copy, and firmware and LittleFS budgets.
Deliverables
- Updated trail-state, visibility, protocol, lifecycle, ownership, layering,
and responsive-rendering contracts in
MVP.md,ARCHITECTURE.md,docs/PROTOCOL.md,docs/UI_STYLE.md, anddocs/PLAYER_UI.md. - A fixed-size authoritative two-player traversed-edge representation with deterministic initialization, successful-move updates, reconnect retention, and match-reset cleanup.
- A compact backward-safe view encoding that carries only role-authorized trail ownership inside existing view chunks and broadcasts.
- A bounded player and observer trail renderer with yellow, blue, and mixed two-color dotted treatments and no per-move accumulation of DOM nodes, timers, animations, or client history.
- Deterministic game, protocol, visibility, serialization, browser, rendering, lifecycle, reconnect, backpressure, size, and regression tests.
- Before-and-after maximum-session traffic, queue, heap, render-time, compressed-web-asset, firmware, LittleFS, and representative physical-device visual-verification evidence.
Acceptance criteria
- Every accepted authoritative in-maze move marks exactly one undirected edge for the moving player's ownership. Ordinary passage moves and a successful hedge breakthrough are marked; blocked, rejected, stale, duplicate, rate-limited, and superseded attempts leave the trail unchanged.
- Trail storage has a documented compile-time maximum derived from the bounded maze cell count and consumes a fixed number of bits per possible canonical edge and player. Repeated traversal, loops, backtracking, and arbitrarily long matches cannot grow heap use or trail state.
- Player 1 traversal produces the established yellow dotted treatment, Player 2 traversal produces the established blue dotted treatment, and traversal of the same edge by both players produces one clearly mixed yellow-and-blue pattern that preserves both ownership meanings without a third blended color.
- A player projection contains and renders trail ownership only for passages fully inside that player's current server-approved visibility. It contains no coordinates, ownership bits, inferred continuation, or retained drawing outside the current projection, including edges previously visible to that browser.
- Each player sees both players' trails when those used passages are inside the current permitted projection. The opponent trail cannot reveal the opponent's current or historical route outside that projection.
- An observer receives and renders the complete authoritative trails of both players across the full maze, including shared edges, without gaining any command authority or changing player projections.
- Refreshing or reconnecting a player or observer during the same match reconstructs the correct role-authorized trail from the fresh authoritative view without browser-persisted trail history. Pause and resume do not add, remove, duplicate, or reorder trail ownership.
- A normal match reset, explicit player-leave cancellation, completed-match restart, new maze, or device reboot clears all trail ownership before the next match view. Session restoration alone never clears it.
- Trail data is carried only in the existing view sequencing and chunking flow. The implementation introduces no trail polling, new periodic request, per-move trail message, additional broadcast pass, or independent retry queue, and incomplete or stale view chunks never partially update a trail.
- Every encoded view remains within the existing 512-byte WebSocket message limit and published chunk-count and queue bounds. Deterministic worst-case player and observer projections with every edge owned by both players serialize successfully without hidden-state disclosure.
- The frontend uses a documented fixed upper bound of drawing layers and DOM nodes independent of move count. Projection replacement removes obsolete geometry, and refresh, reconnect, reset, leave, result, and screen changes leave no stale paths, timers, or retained hidden trail data.
- Trails follow the center of actual open or server-opened passages and sit below all players, objects, exits, hedge art, breakthrough effects, and controls. They do not create overflow, cover interaction targets, resemble walls, or make an intact hedge appear open.
- Yellow, blue, and shared trail states remain distinguishable at 34, 40, and 46 CSS-pixel cells, in normal color and grayscale, and on supported narrow portrait, compact-landscape, tablet, and observer layouts without relying on sound, vibration, animation, or color alone.
- Under the maximum supported two-player/four-observer load and a worst-case fully marked maze, accepted movement latency, outgoing queue high-water, dropped player messages, heap minima, observer responsiveness, and browser rendering remain within the established Milestone 010 and Milestone 017 budgets with no material regression attributable to trail processing.
- Automated game, protocol, browser, and host tests cover both owners, shared edges, repeated traversal, loops, breakthroughs, rejected movement, visibility clipping, observer completeness, chunk boundaries, worst-case size, refresh, reconnect, pause, reset, explicit leave, stale views, layering, cleanup, accessibility, and responsive layouts.
- All host and browser tests pass; compressed-web-asset validation passes; the payload remains within the 1.5 MiB web budget and LittleFS partition; and firmware remains within the factory partition.
- Physical-device testing confirms correct trail continuity during a complete two-player match, refresh and reconnect reconstruction, mixed shared passages, observer completeness, reset cleanup, and stable gameplay with no perceptible input delay, rendering stutter, disconnect, or accumulated resource use.
- The project owner confirms on representative player and observer displays that the dotted trails make used routes easy to follow, shared passages are clearly attributed to both players, fog of war remains intact, and the map stays readable and responsive, or explicitly approves a documented evidence deviation.
Evidence
- On 2026-09-13, the project owner required Player 1 and Player 2 routes to remain visible until the end of the current match, survive refresh and reconnect, and include only successful authoritative movement.
- The project owner required each player to see trail data only within the current permitted visibility projection, while observers see both complete trails, and selected a mixed two-color dotted treatment when both players use the same passage.
- Planning analysis found that frontend-only state cannot consistently satisfy cross-client, observer, refresh, and reconnect requirements without violating fog of war. The milestone therefore limits ESP state to fixed-size per-edge ownership bits, piggybacks authorized data on existing projections, and delegates all trail drawing to a bounded frontend layer without additional requests or broadcasts.
- Implementation and verification have not started, as requested by the project owner.
- On 2026-09-13, implementation added a fixed 512-byte packed two-bit owner mask for the at-most 2,048 canonical maze edges. Accepted in-maze movement, including a completed hedge breakthrough, marks its canonical edge; rejected and outward moves do not. Each existing view cell carries only authorized east and south ownership masks, so no trail request, broadcast, or chunk type was added. The browser replaces one non-interactive SVG layer per projection with yellow, blue, and dual-rhythm shared dotted paths.
- On 2026-09-13,
bash scripts/run_host_tests.shpassed all suites, including authoritative shared-edge ownership and visibility coverage. The worst-case observer serialization remained bounded at 88 chunks and 501 / 512 bytes.node --test test/web/test_player_app.jspassed 32 tests, including trail mask validation and bounded rendering treatment.python3 scripts/verify_web_assets.pypassed with deterministic payload245f402b654fdec986974d62d7f7cd3190fd79293a36c7c1e289200127d98f73at 33,635 / 1,572,864 bytes./home/sasa/.platformio/penv/bin/pio runpassed with firmware at 1,030,784 / 2,097,152 bytes, and/home/sasa/.platformio/ penv/bin/pio run --target buildfspassed at 2,031,616 / 2,031,616 bytes. - On 2026-09-13, trail presentation was softened without changing protocol or
authority: stroke width is 0.065 cells, opacity is 58%, and both ownership
rhythms use shorter, more widely spaced dots.
node --test test/web/test_player_app.js, LittleFS build, and web-asset validation passed with payload5546c680b4d527815381735ec1e9016e02f0002efa865cb09e45b84e8f697cecat 33,638 / 1,572,864 bytes.