883 lines
44 KiB
Markdown
883 lines
44 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:** `IN_PROGRESS`
|
||
|
||
### 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 is complete, but the milestone remains `IN_PROGRESS` until the
|
||
required physical-device and current-stable-browser checks can be run.
|
||
|
||
- `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.
|
||
- Live browser validation is blocked. The browser-control runtime returned `No
|
||
browser is available`, and its browser inventory was empty. Therefore the
|
||
required visual and interaction passes at `412 x 916`, representative tablet
|
||
and desktop viewports, and current stable Chrome, Safari, Firefox, and Edge
|
||
cannot yet be recorded. Milestone 008 must not be marked `COMPLETE` until
|
||
those browser checks pass. Milestone 009 remains `NOT_STARTED`.
|
||
|
||
## Milestone 009 — Observer mode and disconnect recovery
|
||
|
||
**Status:** `NOT_STARTED`
|
||
|
||
### 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
|
||
|
||
Completion evidence is not yet available because this milestone has not
|
||
started. Required evidence includes role-authorization tests, four-observer
|
||
browser evidence, and timed reconnect/timeout scenarios.
|
||
|
||
## Milestone 010 — On-device integration and resource hardening
|
||
|
||
**Status:** `NOT_STARTED`
|
||
|
||
### 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
|
||
|
||
Completion evidence is not yet available because this milestone has not
|
||
started. Required evidence includes production build reports, physical-device
|
||
logs, six-client load results, memory graphs or samples, and repeated-match
|
||
stress results.
|
||
|
||
## Milestone 011 — Gameplay tuning and MVP acceptance
|
||
|
||
**Status:** `NOT_STARTED`
|
||
|
||
### 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
|
||
|
||
Completion evidence is not yet available because this milestone has not
|
||
started. Required evidence includes playtest data, the mapped acceptance report,
|
||
final build hashes and sizes, and project-owner approval of any deviations.
|