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