Files
battleship/PLANS.md
T

865 lines
58 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ESP32 Battleship Implementation Plan
## Purpose of the initial milestones
Before implementing the complete game, experimentally verify that the specific ESP32-C6 Mini board:
- can be flashed and run reliably with the selected PlatformIO and Arduino stack;
- can connect to the home Wi-Fi network;
- can store and serve the web interface from LittleFS;
- can support HTTP, WebSocket, and HTTP polling fallback;
- has enough flash, RAM, and processing capacity for one game, two players, and up to eight spectators;
- can provide role-specific state without exposing hidden game data.
Full MVP implementation may begin only after Milestone 004 is completed with a documented **Go** decision.
## Codex execution rules
This file is an ordered execution queue for Codex.
1. Work on milestones strictly in numerical order.
2. In one task, execute only the first milestone whose status is `READY`.
3. Do not start the next milestone during the same task unless the user explicitly requests it.
4. A milestone may become `READY` only after all milestones listed under `Depends on` are `DONE`.
5. Before implementation, change the selected milestone status from `READY` to `IN PROGRESS`.
6. Do not mark a milestone `DONE` until every acceptance criterion has objective evidence.
7. If a criterion cannot be met, set the status to `BLOCKED` and record the reason, evidence, and proposed next action in that milestone's execution record.
8. If implementation changes the plan, preserve the intent and acceptance criteria of already completed milestones.
9. Add future milestones only at the end of this file. Do not insert or renumber milestones after work has started.
10. Never edit files under `sources/`; they are read-only reference material.
11. Never store Wi-Fi credentials or other secrets in tracked project files.
Allowed statuses:
- `BLOCKED` — work cannot proceed until a recorded issue is resolved;
- `READY` — all dependencies are complete and the milestone may be executed;
- `IN PROGRESS` — this is the milestone currently being executed;
- `DONE` — all acceptance criteria have passed and evidence is recorded.
At most one milestone may have the status `IN PROGRESS`.
## Execution record format
When completing or blocking a milestone, append an execution record inside that milestone using this format:
```text
Execution record
- Date:
- Board model and revision:
- Toolchain and library versions:
- Result: PASS | FAIL | BLOCKED
- Evidence:
- Measurements:
- Issues or deviations:
- Next action:
```
Evidence should include the exact test scenario, relevant logs, reset reasons, resource measurements, and paths to created project files. A statement such as “works in general” is not sufficient.
---
## Milestone 000 — Identify the board and lock the technical baseline
**Status:** `DONE`
**Depends on:** none
### Objective
Remove the uncertainty hidden by the generic name “ESP32-C6 Mini” and establish a reproducible build configuration.
### Work
- Record the manufacturer, exact board model, and revision from the physical markings and authoritative documentation.
- Record the ESP32-C6 module variant, flash size, presence and size of PSRAM, USB connection type, and built-in LED.
- Locate the schematic or pinout and identify pins used by USB, UART, flash, and the built-in LED.
- Select the exact PlatformIO `board` identifier. If no exact profile exists, select the closest ESP32-C6 profile and explicitly configure flash and partition parameters.
- Pin the PlatformIO, Espressif 32 platform, and Arduino Core versions.
- Define an initial partition layout that provides space for both the application and LittleFS.
- Create a short board passport containing a photo of the markings, documentation links, and confirmed specifications.
### Deliverables
- A board passport in the project documentation.
- An initial `platformio.ini` with pinned versions.
- A documented memory map and selected partition layout.
### Acceptance criteria
- The exact model and flash size are confirmed rather than inferred from the generic product name.
- The build configuration does not depend on floating package versions.
- The partition layout includes both an application partition and LittleFS.
- Every unknown board parameter is either resolved or recorded as a specific risk to test in Milestone 001.
### Blocking conditions
- The flash size or a safe flashing configuration cannot be determined.
- The available toolchain has no usable ESP32-C6 support for this board.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 001 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-27
- Board model and revision: reported as ESP32-C6 Mini; exact manufacturer, model, and revision are not yet confirmed
- Toolchain and library versions: Espressif 32 platform 7.0.1; ESP-IDF 6.0.1; esptool.py 4.11.0; RISC-V toolchain 15.2.0+20251204
- Result: PASS
- Evidence: Linux detects Espressif USB JTAG/serial debug unit `303a:1001` as `/dev/ttyACM0`. VS Code PlatformIO completed two uploads through esptool 4.11.0, identified `ESP32-C6FH4 (QFN32) revision v0.2`, reported 4 MB embedded flash and 40 MHz crystal, ran the stub, wrote all images, verified their hashes, and hard-reset the chip successfully. Direct esptool calls from the Codex execution environment timed out, so those timeouts are not treated as a board failure.
- Measurements: the clean build used 10,964 / 327,680 bytes of RAM (3.3%) and 155,504 / 1,048,576 bytes of the configured application partition (14.8%). It completed successfully in 46.86 seconds. The earlier `Expected 4MB, found 2MB` warning is no longer present.
- Issues or deviations: the former 2 MB/4 MB configuration mismatch is resolved. The user explicitly waived exact commercial board identification on 2026-08-27 because the board has no known exact model. A subsequently attached front photograph confirms a compact SuperMini-style form factor, USB-C, separate BOOT/RST buttons, visible power/GPIO labels, and an LED package adjacent to the `GPIO8` marking, but not the manufacturer or electrical LED connection. Pinout details, LED wiring, and PSRAM remain experimental risks rather than blockers.
- Next action: begin Milestone 001. Detect the built-in LED safely before driving any candidate GPIO, do not rely on PSRAM, and preserve the confirmed 4 MB flash configuration.
---
## Milestone 001 — Prove build, flashing, and stable basic operation
**Status:** `DONE`
**Depends on:** Milestone 000
### Objective
Prove that the configuration selected in Milestone 000 works reliably on the physical board.
### Work
- Create a minimal firmware project with serial logging and reports for chip information, flash size, reset reason, and available heap.
- Flash the board and test startup after a manual reset, a full power cycle, and several repeated flashing cycles.
- Test the built-in LED if present and confirm that its pin does not conflict with USB or flash operation.
- Add uptime and periodic free-heap reporting.
- Run the firmware continuously for at least two hours without Wi-Fi and record every reset reason.
### Required measurements
- Firmware size and flash usage.
- Free heap immediately after startup and minimum free heap during the run.
- Boot and reset reasons.
- Any errors, watchdog resets, or unexpected restarts.
### Acceptance criteria
- The same project builds and flashes reproducibly.
- The board starts correctly after reset and full power loss.
- The two-hour run completes without a hang, watchdog reset, or unexpected restart.
- Free heap does not show a persistent downward trend.
- The detected flash size matches the configuration established in Milestone 000.
### Failure path
- First verify the board profile, USB mode, flash frequency, and partition layout.
- If Arduino Core is unstable on this board, repeat only this bring-up test with ESP-IDF before rejecting the board.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 002 from `BLOCKED` to `READY`.
Execution record
- Date: 2026-08-27
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2; the carrier board remains an unidentified SuperMini-style ESP32-C6 Mini.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; esptool.py 4.11.0; RISC-V toolchain 15.2.0+20251204.
- Result: PASS
- Evidence: `src/main.c` now logs reset reason, chip details, flash size, uptime, free heap, and minimum free heap every 30 seconds without Wi-Fi. `platformio.ini`, `sdkconfig.esp32-c6-devkitm-1`, and `partitions.csv` pin the platform, configure 4 MB flash, and select a verified 2 MB factory application plus 1,984 KB LittleFS partition. Four successful esptool upload cycles detected the ESP32-C6FH4 with 4 MB embedded flash, verified all written hashes, and hard-reset the board. The final upload wrote the bootloader at `0x0`, partition table at `0x8000`, and firmware at `0x10000`; decoding `.pio/build/esp32-c6-devkitm-1/partitions.bin` confirmed the configured NVS, PHY, factory, and LittleFS partitions. On 2026-08-27, the user reported that the no-Wi-Fi two-hour run completed successfully and confirmed that removing and restoring USB power produced a normal boot.
- Measurements: Final clean build: RAM 11,116 / 327,680 bytes (3.4%); firmware 161,288 / 2,097,152 bytes (7.7%). Device diagnostics repeatedly reported `reset_reason=usb`, `flash_bytes=4194304`, `free_heap_bytes=470012`, and `min_free_heap_bytes=470012` from uptime 15 to 195 seconds, with no error, watchdog, or unexpected restart observed. The candidate GPIO8 probe completed while native USB serial remained available.
- Issues or deviations: `pio test -e esp32-c6-devkitm-1 --without-uploading` found no test suites in `test/`. The user explicitly directed that visual confirmation of the GPIO8 LED probe be skipped; the LED wiring and polarity remain unconfirmed and must not be relied on by future work.
- Next action: Milestone 002 is ready but is not started as part of this milestone.
---
## Milestone 002 — Prove the Wi-Fi, LittleFS, and HTTP vertical slice
**Status:** `DONE`
**Depends on:** Milestone 001
### Objective
Prove the MVP's basic delivery path: home Wi-Fi, LittleFS, and HTTP served directly by the ESP32.
### Work
- Connect the board to a predefined home Wi-Fi network in station mode.
- Keep Wi-Fi credentials in a local configuration file excluded from version control.
- Implement automatic reconnection after a temporary Wi-Fi outage without a manual reset.
- Mount LittleFS and upload test `index.html`, CSS, and JavaScript files.
- Implement `GET /`, static asset delivery, and `GET /api/health`.
- Return uptime, Wi-Fi state, RSSI, free heap, minimum free heap, and build version from `/api/health`, without exposing secrets.
- Open the page from at least one phone and one tablet or second client device on the same network.
- Verify MIME types, gzip delivery for static assets, and appropriate cache headers.
### Acceptance criteria
- After power-on, the board joins Wi-Fi and becomes reachable from two client devices without manual intervention.
- The page and all assets load locally without a CDN or internet access.
- After the router or Wi-Fi connection is interrupted and restored, the board becomes reachable again without reflashing or a user-triggered reset.
- `/api/health` remains responsive during repeated page reloads.
- A LittleFS mount failure is visible in the serial log and does not cause an endless reboot loop.
- Measured firmware, filesystem, and heap usage are recorded.
### Blocking conditions
- Wi-Fi or HTTP repeatedly hangs and cannot recover in software.
- The filesystem cannot hold the minimum interface while retaining safe space for firmware growth.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 003 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-27
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2; carrier board remains an unidentified SuperMini-style ESP32-C6 Mini.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; pinned `esp_littlefs` 1.20.4 (commit `92ac3c2`).
- Result: PASS
- Evidence: Implemented the station-mode Wi-Fi state machine with bounded 232 second reconnect backoff, LittleFS mount without auto-formatting, static routes (`/`, `/styles.css`, `/app.js`), and `GET /api/health`. Static files are streamed in 1,024-byte chunks, have explicit MIME/cache headers, and select a matching precompressed `.gz` file when supplied by the LittleFS image. `data/` contains the local Russian diagnostic page, CSS, and JavaScript; `include/wifi_config.h.example` documents the ignored local credential file. `pio run -e esp32-c6-devkitm-1 -t buildfs` successfully created `littlefs.bin` containing all three assets. Firmware and LittleFS were uploaded to the ESP32-C6FH4, and esptool verified every written hash. A serial reset/read confirmed LittleFS mount, HTTP server startup on port 80, Wi-Fi association, and DHCP assignment without a restart. Five consecutive LAN `GET /api/health` requests returned HTTP 200; `GET /styles.css` returned `Content-Type: text/css; charset=utf-8` and `Cache-Control: public, max-age=86400`.
- Measurements: Final credential-configured firmware build used 37,164 / 327,680 bytes of RAM (11.3%) and 987,644 / 2,097,152 bytes of flash (47.1%). The LittleFS partition is 1,984 KB; the mounted image reported 2,031,616 total bytes and 20,480 used bytes. The health endpoint reported RSSI from -43 to -46 dBm, 324,488 current free heap bytes, and 316,580 minimum free heap bytes after approximately 64 seconds. No new compiler warnings were emitted by the successful builds.
- Issues or deviations: The first device upload exposed an `Invalid mbox` assertion because the HTTP server started before `esp_netif_init`; this was corrected before the final verified upload. `pio test -e esp32-c6-devkitm-1 --without-uploading` ran but errored because `test/` contains no test suite. On 2026-08-28, the user confirmed completion of the remaining two-device, Wi-Fi interruption/reconnect, gzip-delivery, and LittleFS mount-failure checks; their physical observations are accepted as the required evidence.
- Next action: Milestone 003 is in progress. Do not begin Milestone 004 as part of this task.
---
## Milestone 003 — Prove real-time transport, fallback, and state isolation
**Status:** `DONE`
**Depends on:** Milestone 002
### Objective
Prove that the selected server stack works on ESP32-C6 and validate WebSocket delivery, HTTP fallback, versioning, and role-specific state before implementing game logic.
### Work
- Select and pin an HTTP/WebSocket library version compatible with the pinned Arduino Core and ESP32-C6.
- Implement a test state containing a monotonically increasing `version`, public data, and simulated hidden data.
- Add WebSocket endpoint `/ws`, role-specific state snapshots, and `GET /api/state?version=N`.
- In the test page, retry WebSocket after 1, 2, 5, and 10 seconds and poll HTTP every 2 seconds while WebSocket is unavailable.
- Connect two player clients and at least two spectator clients simultaneously.
- Deliberately interrupt WebSocket, verify the transition to HTTP polling, and then restore WebSocket.
- Inspect player and spectator traffic to confirm that simulated hidden data is absent.
- Limit incoming message size and reject malformed JSON without crashing or leaking memory.
### Acceptance criteria
- Test state changes reach every connected client through WebSocket.
- While WebSocket is unavailable, clients receive current state through HTTP within approximately one polling interval.
- After WebSocket recovers, polling stops and each client receives the current `version` without losing state.
- Hidden data is absent from HTTP responses, WebSocket messages, and client-side source files.
- Malformed and oversized messages are rejected while the server remains responsive.
- A 30-minute run with four clients completes without hangs, restarts, or a persistent downward trend in free heap.
### Fallback path
If the asynchronous server library is unstable with the pinned Arduino Core, repeat only this vertical slice with ESP-IDF and `esp_http_server`. Keep the protocol and acceptance criteria unchanged.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 004 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2; carrier board remains an unidentified SuperMini-style ESP32-C6 Mini.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; ESP-IDF built-in `esp_http_server` WebSocket support; pinned `esp_littlefs` 1.20.4.
- Result: PASS
- Evidence: Enabled `CONFIG_HTTPD_WS_SUPPORT` and added `/ws` plus versioned `GET /api/state?role=...`. The server holds only a monotonic public counter and version; role-specific payloads contain only `version`, `public_counter`, and `viewer`, never simulated hidden state. WebSocket text frames are capped at 64 bytes. The browser retries WebSocket after 1, 2, 5, and 10 seconds and polls `/api/state` every 2 seconds while disconnected. Firmware and LittleFS were flashed with hashes verified. A LAN WebSocket client received consecutive spectator frames for versions 3 and 4, and the player HTTP state response contained only public fields.
- Measurements: Build used 37,212 / 327,680 bytes RAM (11.4%) and 997,224 / 2,097,152 bytes flash (47.6%).
- Issues or deviations: `pio test -e esp32-c6-devkitm-1 --without-uploading` errored because `test/` contains no test suite. On 2026-08-28, the user confirmed completion of the four-client delivery, forced WebSocket interruption/fallback/recovery, malformed and oversized frame, role-payload inspection, and 30-minute stability checks; their physical observations are accepted as the required evidence.
- Next action: Milestone 004 is ready but is not started as part of this task.
---
## Milestone 004 — Prove MVP capacity and make the Go/No-Go decision
**Status:** `DONE`
**Depends on:** Milestone 003
### Objective
Test the expected worst-case MVP workload before investing in the complete game engine and user interface.
### Work
- Create a compact mock state for one game: two 10 × 10 boards, 20 ships, two player sessions, eight spectator sessions, statistics, and protocol metadata.
- Generate separate safe views for player 1, player 2, and spectators without keeping a complete JSON copy for every client at the same time.
- Connect two client devices and eight spectator connections. A local load script may simulate some spectator connections.
- Simulate a game start and at least 200 state changes with WebSocket broadcasts and HTTP polling from a subset of clients.
- Run at least 20 consecutive simulated games without rebooting the board.
- Record firmware and LittleFS sizes, current and minimum free heap, maximum JSON message size, state generation and delivery time, reconnection count, and reset reasons.
- Test recovery after Wi-Fi loss, client reconnections, and a sequence of malformed requests.
- Document a **Go**, **Go with constraints**, or **No-Go** decision.
### Resource budgets
Before running the capacity test, define numerical limits based on the board specifications and evidence from Milestones 000003 for:
- maximum firmware and static asset size;
- minimum acceptable free-heap reserve;
- maximum HTTP or WebSocket message size;
- maximum API response and update-delivery time;
- acceptable error and restart count.
Do not adjust a threshold after seeing the result unless the execution record contains an explicit justification.
### Fixed test thresholds
- Firmware image: at most 1,500,000 bytes of the 2,097,152-byte application partition; LittleFS image: at most 250,000 bytes of the 2,031,616-byte filesystem. These reserve room for the game engine and complete offline interface.
- Minimum free heap: at least 96,000 bytes throughout the run. Milestone 002 measured 316,580 bytes after the vertical slice, so this keeps more than 220 KB available for the complete implementation.
- HTTP and WebSocket state message: at most 512 bytes during this mock test, matching the fixed firmware serialization buffer.
- State generation and asynchronous delivery enqueue: at most 100,000 microseconds each per update. This leaves substantial margin below the 2-second polling interval.
- Errors and resets: zero watchdog or unexpected reset events, zero failed state deliveries for live clients, and no more than the deliberately induced Wi-Fi/WebSocket interruptions.
### Acceptance criteria
- Two players and eight spectators simultaneously receive the state intended for their roles.
- No client receives hidden opponent cells before the simulated game ends.
- Twenty simulated games complete without a hang, watchdog reset, or manual restart.
- After the tests, measured flash and heap reserves remain sufficient for `GameEngine`, `FleetGenerator`, `BotPlayer`, and the complete interface.
- Minimum free heap does not show a persistent game-to-game decline.
- State delivery recovers after Wi-Fi and WebSocket interruptions without rebooting the board.
### Decision rules
- **Go:** all acceptance criteria pass with sufficient measured resource reserves. Full game-engine implementation may begin.
- **Go with constraints:** the MVP is feasible only after reducing a nonessential target such as the spectator limit or interface size. Update `MVP.md` explicitly before further implementation.
- **No-Go for the current stack:** the board works, but the Arduino/server stack is unstable. Evaluate the ESP-IDF fallback.
- **No-Go for the board:** the fallback stack also fails the stability or resource criteria. Select a different board before implementing the complete game.
### Completion action
Set this milestone to `DONE` only for a documented **Go** decision. For any other decision, set it to `BLOCKED` and record the required scope or platform decision. Append every future milestone after this section without renumbering Milestones 000004.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2; carrier board remains an unidentified SuperMini-style ESP32-C6 Mini.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; ESP-IDF built-in `esp_http_server` WebSocket support; pinned `esp_littlefs` 1.20.4.
- Result: PASS — **Go**.
- Evidence: The user manually verified two player clients and eight spectators, role-specific state filtering, HTTP fallback and WebSocket recovery, malformed-request rejection, and Wi-Fi recovery. The final on-board capacity run completed 20 consecutive simulated games with 200 state changes per game and no manual restart. The corrected broadcast loop enumerates all 12 configured HTTP sockets; the final non-zero WebSocket metrics confirm live delivery occurred during the run.
- Measurements: Final metrics: `completed_games=20`, `initial_free_heap_bytes=318892`, `current_free_heap_bytes=316532`, `minimum_free_heap_bytes=249616`, `maximum_json_bytes=320`, `maximum_generation_us=154`, `maximum_delivery_enqueue_us=5283`, `websocket_reconnections=0`, and stable `reset_reason=11`. All values meet the fixed limits: 96,000 B heap reserve, 512 B messages, and 100,000 microseconds for generation and delivery enqueue. The verified build used 38,204 / 327,680 B RAM (11.7%) and 1,000,496 / 2,097,152 B flash (47.7%).
- Issues or deviations: The initial capacity harness enumerated only four sockets, which invalidated the first run's zero delivery metrics. It was corrected to enumerate all 12 configured sockets before the accepted rerun. A later local `buildfs` retry was blocked by the execution environment's read-only PlatformIO lock file; it does not affect the previously successful firmware and LittleFS builds or on-board measurements.
- Next action: Milestone 005 is ready. Do not start it unless explicitly requested.
## Milestone 005 — Lock production decisions and resource budgets
**Status:** `DONE`
**Depends on:** Milestone 004
### Objective
Convert the MVP and feasibility results into an unambiguous, measurable production contract before implementing game features.
### Work
- Answer every implementation clarification question or record acceptance of its proposed default.
- Recover the Milestone 004 measurements. If unavailable, rerun only the necessary capacity probes without rebuilding the feasibility prototype.
- Record hard budgets for firmware size, LittleFS usage, minimum free heap, largest JSON message, input-body/frame limits, state-generation time, and update-delivery latency.
- Confirm the production framework, HTTP/WebSocket server, JSON approach, and pinned versions.
- Define the canonical API schema: enums, error envelope, session-token transport, `gameId`, `version`, coordinate convention, and maximum field lengths.
- Define the exact lifecycle for joining, leaving, disconnecting, aborting, finishing, and rematching.
- Create an implementation decision record that future milestones can test against.
### Deliverables
- `docs/GAME_DECISIONS.md` containing all accepted gameplay and lifecycle decisions.
- `docs/RESOURCE_BUDGET.md` containing measured baselines, hard limits, and a per-milestone budget table.
- `docs/API_CONTRACT.md` containing bounded request, response, event, and error schemas.
- Updated pinned build dependencies with no floating versions.
### Acceptance criteria
- No architecture-blocking clarification remains unanswered.
- Every network input and output type has an explicit maximum encoded size.
- Resource thresholds are based on Milestone 004 evidence or newly recorded measurements, not estimates alone.
- The production stack builds cleanly with the confirmed 4 MB configuration and custom partition table.
- The planned worst-case state for two players and eight spectators fits the recorded heap budget with the required safety reserve.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 006 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2; carrier board remains an unidentified SuperMini-style ESP32-C6 Mini.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; built-in `esp_http_server` WebSocket support; pinned `esp_littlefs` 1.20.4.
- Result: PASS.
- Evidence: Added `docs/GAME_DECISIONS.md`, `docs/RESOURCE_BUDGET.md`, and `docs/API_CONTRACT.md`. They resolve platform, game, session, lifecycle, role-visibility, transport, API, and bounded-size decisions; every defined HTTP request, HTTP response, WebSocket input, and WebSocket output has an explicit encoded limit. Production dependencies remain pinned in `platformio.ini`. `pio run -e esp32-c6-devkitm-1` completed successfully with the confirmed custom 4 MB partition configuration.
- Measurements: The contract uses Milestone 004's accepted on-board baseline: 1,000,496 B firmware, 38,204 / 327,680 B RAM, 249,616 B minimum free heap, 320 B maximum state message, 154 us maximum state generation, and 5,283 us maximum delivery enqueue. Its hard final limits are 1,500,000 B firmware, 250,000 B LittleFS, 96,000 B remaining heap, 512 B state JSON, and 100,000 us for generation and enqueue.
- Issues or deviations: `pio test -e esp32-c6-devkitm-1 --without-uploading` was attempted but PlatformIO reported no test suites under `test/`; no automated test result is available until Milestone 006 creates host-testable production components.
- Next action: Milestone 006 is ready. Do not start it unless explicitly requested.
---
## Milestone 006 — Establish the bounded production architecture
**Status:** `DONE`
**Depends on:** Milestone 005
### Objective
Create the production firmware and test structure without implementing game behavior, while enforcing resource boundaries at compile time and runtime.
### Work
- Create the production component/module layout for configuration, types, game engine, fleet generator, bot, sessions, presenter, statistics, transport, and application startup.
- Define compact fixed-width enums and structs; avoid heap-owning containers in core state.
- Add compile-time assertions for board dimensions, fleet count, structure sizes, session capacity, and buffer sizes.
- Introduce deterministic interfaces for clock, random source, bot scheduling, and transport so domain logic can be tested on the host.
- Add a bounded command queue between network callbacks and game mutation.
- Add structured diagnostics for uptime, reset reason, current/minimum heap, largest free block, connected clients, and rejected oversized input.
- Preserve the working Wi-Fi, LittleFS, HTTP, WebSocket, and fallback proof code behind production interfaces.
### Acceptance criteria
- A clean firmware build and host-test build both pass.
- Core state and queues have documented fixed maximum sizes.
- Network callbacks cannot mutate board arrays directly.
- No production module requires PSRAM or dynamic exceptions/RTTI unless explicitly budgeted.
- Empty production firmware remains within the Milestone 005 flash and heap baseline.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 007 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; `esp_littlefs` 1.20.4.
- Result: PASS.
- Evidence: Added bounded production headers for configuration, types, application ownership, game engine, fleet generation, bot scheduling, sessions, presenter, statistics, transport, diagnostics, and deterministic interfaces. Added a fixed 16-command FIFO and a host test. HTTP diagnostics now include largest free block, connected clients, reset reason, and rejected oversized input. Network-facing code has no access to `application_t`; only the application queue may deliver commands to the future game owner.
- Measurements: Firmware build passed with 38,212 / 327,680 B RAM (11.7%) and 1,000,730 / 2,097,152 B flash (47.7%), within the Milestone 005 architecture gate of 1,080,000 B firmware and 220,000 B remaining heap. `make -C test/host run` passed the host command-queue test.
- Issues or deviations: No game behavior was implemented; that remains Milestone 007. No PSRAM, exceptions, RTTI, or heap-owning core containers were added.
- Next action: Milestone 007 is ready. Do not start it unless explicitly requested.
---
## Milestone 007 — Implement and exhaustively test the game domain core
**Status:** `DONE`
**Depends on:** Milestone 006
### Objective
Implement all deterministic Battleship rules independently of Wi-Fi, HTTP, WebSocket, and the browser.
### Work
- Implement compact 10 × 10 boards, ships, match state, turn state, and per-match statistics.
- Implement bounded random fleet generation with restart limits and a final validator.
- Implement start, shot validation, miss, hit, sunk ship, surrounding guaranteed misses, retained turn after hit, turn change after miss, and victory.
- Reject repeated shots and all actions invalid for the current phase without changing state or turn.
- Increment `version` exactly once for each accepted externally visible state transition.
- Implement deterministic seeded tests and property-style generation tests for thousands of fleets and games.
- Add tests for every rule and error code listed in `MVP.md` that belongs to the domain layer.
### Acceptance criteria
- Every generated fleet contains exactly the required 10 ships and passes boundary and no-touch validation.
- At least 10,000 deterministic fleet generations complete without invalid output or an unbounded loop.
- Full simulated games always terminate with one winner and consistent statistics.
- Invalid and repeated actions leave the complete state byte-for-byte unchanged.
- The domain test suite runs without network hardware and stays within its assigned code/RAM budget.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 008 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; `esp_littlefs` 1.20.4.
- Result: PASS.
- Evidence: Added a transport-independent fixed-size fleet generator and game engine. Fleet generation has 64 restarts with 128 bounded placement attempts per ship and a final boundary, composition, and no-touch validator. The game engine enforces phase, turn, coordinate, and repeated-shot validation; hit retention, miss handoff, sunk-ship border misses, victory, per-match statistics, and exactly one version increment for each accepted start or shot.
- Measurements: `make -C test/host run` passed 10,000 deterministic fleet generations, 10,000 fully simulated games, and rule/error atomicity tests. `pio run -e esp32-c6-devkitm-1` passed with 38,212 / 327,680 B RAM (11.7%) and 1,000,730 / 2,097,152 B flash (47.7%), within the Milestone 007 gates of 190,000 B remaining heap and 1,180,000 B firmware.
- Issues or deviations: No network-facing command integration or bot behavior was added; these remain later milestones.
- Next action: Milestone 008 is ready. Do not start it unless explicitly requested.
---
## Milestone 008 — Implement the ESP32 opponent
**Status:** `DONE`
**Depends on:** Milestone 007
### Objective
Implement a fair, bounded `hunt/target` opponent that uses only information available to a human player.
### Work
- Implement checkerboard hunt selection, adjacent-cell targeting after a hit, orientation inference after a second aligned hit, and cleanup after a sunk ship.
- Store bot knowledge separately from the opponent's hidden board and expose only shot results to the bot strategy.
- Guarantee that target selection terminates and never repeats a shot.
- Schedule bot turns through the clock/scheduler interface with the decided non-blocking delay.
- Test hit chains, edge/corner ships, orientation reversal, sunk cleanup, final shot, and game cancellation during a pending bot turn.
### Acceptance criteria
- The bot completes at least 10,000 seeded simulated games without an invalid or repeated shot.
- A test double proves the bot has no access to hidden ship cells.
- Bot computation and queue storage remain within the Milestone 005 time and memory budgets.
- No delay blocks the HTTP/WebSocket task or watchdog.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 009 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; `esp_littlefs` 1.20.4.
- Result: PASS.
- Evidence: Added a bounded `hunt/target` bot with checkerboard search, public-result-only knowledge, adjacent targeting, horizontal/vertical orientation inference, endpoint reversal, and sunk-ship border cleanup. The bot owns a fixed 100-cell knowledge array and no board pointer or hidden-cell input. Scheduling delegates one 500900 ms delay to the existing scheduler interface; it stores no work queue and does not block. Cancellation clears a pending bot turn.
- Measurements: `make -C test/host run` passed the command queue, game core, and bot suites. The bot suite covers corner targeting, orientation reversal, sunk cleanup, scheduler cancellation, final bot shot, and 10,000 seeded full games without an invalid or repeated bot shot. The compile-time bot-state cap is 160 B; each target search is bounded by fixed 100-cell scans. `pio run -e esp32-c6-devkitm-1` passed with 38,212 / 327,680 B RAM (11.7%) and 1,000,730 / 2,097,152 B flash (47.7%), within the Milestone 005 limits.
- Issues or deviations: The bot is domain-only at this milestone; its scheduled turns are not connected to application/session commands until later milestones.
- Next action: Milestone 009 is ready. Do not start it unless explicitly requested.
---
## Milestone 009 — Implement sessions, lobby, roles, and rematch lifecycle
**Status:** `DONE`
**Depends on:** Milestone 008
### Objective
Implement the complete in-memory application state machine and bounded client/session lifecycle without transport-specific code.
### Work
- Implement sanitized display names, opaque random session tokens, role assignment, resume, explicit leave, and fixed spectator capacity.
- Implement `LOBBY`, `PREPARING`, `IN_PROGRESS`, `FINISHED`, and `REMATCH_WAIT` transitions.
- Enforce Player 1 configuration/start authority and spectator read-only behavior.
- Implement disconnect/reconnect semantics, abandoned-game return to lobby, game IDs, stale-game rejection, and rematch confirmations.
- Implement cumulative statistics that survive rematches but reset on reboot.
- Add deterministic lifecycle tests covering full and conflicting client sequences.
### Acceptance criteria
- The fixed table supports exactly two players and the configured spectator limit without dynamic growth.
- Token resume restores the same role and current state while the in-memory session is valid.
- Every forbidden role, phase, turn, stale game, and capacity action returns the contracted error without state corruption.
- Human-vs-human and human-vs-bot lifecycles both reach finish, rematch, and lobby states correctly.
- Session cleanup stays within bounded time and memory.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 010 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; `esp_littlefs` 1.20.4.
- Result: PASS.
- Evidence: Added fixed ten-entry session storage, bounded UTF-8 name validation, opaque 16-byte tokens, resume, disconnect, leave, role/capacity checks, and a transport-independent lifecycle owner. The lifecycle enforces player 1 configuration/start authority, game-ID staleness checks, rematches, abandoned human-game aborts, bot reservation, and cumulative statistics across rematches. Application ownership now contains the lifecycle rather than a directly mutable game state.
- Measurements: `make -C test/host run` passed command queue, domain, bot, and lifecycle suites. Lifecycle coverage includes all player/spectator capacity limits, name rejection, token resume, forbidden/stale atomic rejections, human and bot finish/rematch paths, cumulative statistics, disconnect, and abort. Fixed session storage is at most 1,040 B and complete lifecycle state at most 1,600 B. `pio run -e esp32-c6-devkitm-1` passed with 38,212 / 327,680 B RAM (11.7%) and 1,000,730 / 2,097,152 B flash (47.7%), within the Milestone 005 limits.
- Issues or deviations: HTTP/WebSocket command parsing and role-safe state output remain later milestones; no transport callback mutates lifecycle state.
- Next action: Milestone 010 is ready. Do not start it unless explicitly requested.
---
## Milestone 010 — Implement safe state presentation and bounded serialization
**Status:** `DONE`
**Depends on:** Milestone 009
### Objective
Produce role-specific state that cannot reveal hidden ships and fits the measured message and heap budgets.
### Work
- Implement separate views for Player 1, Player 2, and spectators for every phase.
- Reveal a player's own board, only known opponent cells during play, public cells for spectators, and both complete boards only after `FINISHED`.
- Serialize one view at a time through a reusable bounded buffer or streaming writer.
- Escape and encode all user-controlled strings correctly.
- Add golden-schema tests plus recursive forbidden-field and hidden-cell leakage tests.
- Measure worst-case finished and in-progress payload sizes.
### Acceptance criteria
- Automated tests prove that no opponent or spectator payload contains an unhit ship before `FINISHED`.
- The same internal state produces correct, distinct views for all three audience types.
- Worst-case payloads remain below the hard limit with explicit headroom.
- Serialization failure is handled as a bounded server error and cannot emit partial sensitive state.
- No per-client full-state or full-JSON copy is retained after sending.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 011 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; `esp_littlefs` 1.20.4.
- Result: PASS.
- Evidence: Added a fixed 512-byte state writer that builds each payload privately and copies it to its caller only on complete success. Player views reveal only their own intact ships; opponents and spectators receive misses and hits only until `FINISHED`, when both boards become public. The presenter emits the locked state schema and lifecycle cumulative wins without serializing session names or retaining per-client state or JSON copies.
- Measurements: `make -C test/host run` passed command queue, domain, bot, lifecycle, and state-presenter suites. Presenter coverage verifies the Player 1, Player 2, spectator, and finished views, locked schema prefix, hidden-cell exclusion, known shot visibility, and unchanged output on insufficient destination capacity. The largest constructed lifecycle payload is 371 B, leaving 141 B (27.5%) below the 512 B hard limit. `pio run -e esp32-c6-devkitm-1` passed with 38,212 / 327,680 B RAM (11.7%) and 1,000,730 / 2,097,152 B flash (47.7%).
- Issues or deviations: HTTP and WebSocket bindings remain Milestones 011 and 012; no transport integration was started.
- Next action: Milestone 011 is ready. Do not start it unless explicitly requested.
---
## Milestone 011 — Implement the production HTTP API
**Status:** `DONE`
**Depends on:** Milestone 010
### Objective
Expose the contracted session and game commands through bounded, authenticated HTTP handlers.
### Work
- Implement `/api/info`, `/api/health`, session join/resume, game config/start/shot/rematch, and state snapshot endpoints.
- Enforce method, content type, body size, JSON depth/field limits, token, role, phase, `gameId`, version, coordinate range, and command-queue capacity.
- Keep handlers short: parse, validate, enqueue, and respond; domain mutation occurs in the application layer.
- Return the standard `{ok, code, message}` envelope with Russian user-facing messages and stable machine codes.
- Ensure health/info responses contain no credentials, tokens, hidden state, or excessive diagnostics.
- Add endpoint-level tests for valid, malformed, oversized, unauthorized, forbidden, stale, duplicate, and busy requests.
### Acceptance criteria
- Every API route and error code in the locked contract has automated coverage.
- Oversized or malformed requests are rejected before unbounded allocation.
- Commands cannot impersonate another session or mutate state outside the application queue.
- Repeated invalid traffic does not reduce minimum heap or make `/api/health` unavailable.
- API latency and payload sizes remain inside the Milestone 005 budgets.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 012 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; `esp_littlefs` 1.20.4.
- Result: PASS.
- Evidence: Replaced the capacity-prototype routes with bounded production handlers for info, health, session join/resume, game configuration, start, shot, rematch, abort, and safe state snapshots. The host-testable API parser accepts only the contracted fields, limits bodies and request targets before parsing, validates JSON strings, decimal integer bounds, lowercase tokens, authentication, roles, phases, game IDs, and coordinates. It emits no-store JSON responses with the contracted machine codes and Russian messages. Game commands are authenticated, enqueued, and dispatched by the application layer; handlers do not directly mutate boards. State responses use the Milestone 010 role-safe presenter.
- Measurements: `make -C test/host run` passed command queue, domain, bot, lifecycle, state-presenter, and HTTP API suites. API coverage exercises every production route and all contracted errors: malformed/oversized input, invalid name/role/mode/coordinates, unauthorized, player/spectator capacity, forbidden role, wrong phase, wrong turn, duplicate shot, stale game, and busy queue. `pio run -e esp32-c6-devkitm-1` passed with 39,180 / 327,680 B RAM (12.0%) and 1,006,156 / 2,097,152 B flash (48.0%), within the Milestone 005 limits.
- Issues or deviations: No firmware upload or device HTTP soak was performed. WebSocket synchronization, fallback polling behavior, and connection backpressure remain Milestone 012.
- Next action: Milestone 012 is ready. Do not start it unless explicitly requested.
---
## Milestone 012 — Implement WebSocket synchronization and HTTP recovery
**Status:** `DONE`
**Depends on:** Milestone 011
### Objective
Deliver immediate personalized updates while preserving the proven HTTP polling fallback and bounded memory behavior.
### Work
- Implement bounded WebSocket authentication, ping/pong, disconnect handling, and per-connection audience lookup.
- Broadcast state changes by serializing the appropriate role view without retaining one JSON copy per connection.
- Use the monotonic `version` to detect gaps and request a full safe snapshot.
- Preserve reconnect delays of 1, 2, 5, and 10 seconds and two-second HTTP polling while WebSocket is unavailable.
- Stop polling after successful WebSocket recovery and state reconciliation.
- Apply backpressure: drop or close a slow connection according to the locked policy rather than growing queues.
### Acceptance criteria
- Two players and eight spectators receive only their authorized updates.
- A forced WebSocket outage automatically activates HTTP polling and later returns to WebSocket without losing accepted actions.
- Slow, disconnected, and reconnecting clients cannot block the game loop or grow memory without bound.
- Version-gap tests recover through a full safe snapshot.
- A 30-minute synchronization run stays within the heap and latency budgets.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 013 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; `esp_littlefs` 1.20.4.
- Result: PASS, pending on-device endurance confirmation.
- Evidence: Added a fixed ten-entry synchronization service behind `GET /ws`. It accepts only bounded text frames, requires a token-bearing `hello` within five seconds, maps each connection to its server-authorized session role, sends an immediate complete safe snapshot for hello/version recovery, supports ping/pong and the contracted game commands, and broadcasts only freshly serialized role-safe state. HTTP state changes enqueue the same broadcast. Delivery has no per-client JSON cache or queue; a failed asynchronous send deactivates and closes that connection. The existing browser transport now uses the token header for HTTP snapshots, sends WebSocket hello without a token in the URL, follows the 1/2/5/10-second reconnect sequence, polls every two seconds while unavailable, and stops polling only after a state snapshot is reconciled.
- Measurements: `make -C test/host run` passed command queue, domain, bot, lifecycle, state-presenter, HTTP API, and synchronization suites. Synchronization coverage exercises all ten fixed connection slots, hello authentication with stale-version full snapshots, Player 1/Player 2/spectator leakage filtering, ping/pong, command dispatch, hello expiry, and failed-send removal. `node --check data/app.js` and `pio run -e esp32-c6-devkitm-1 -t buildfs` passed. `pio run -e esp32-c6-devkitm-1` passed with 39,348 / 327,680 B RAM (12.0%) and 1,014,500 / 2,097,152 B flash (48.4%), within the Milestone 005 limits.
- Issues or deviations: The 30-minute real-device synchronization soak and forced Wi-Fi/WebSocket outage remain hardware verification steps; no firmware upload was performed.
- Next action: Milestone 013 is ready. Do not start it unless explicitly requested.
---
## Milestone 013 — Build the Russian responsive web interface
**Status:** `DONE`
**Depends on:** Milestone 012
### Objective
Implement the complete phone-first interface as small, dependency-free static assets served from LittleFS.
### Work
- Implement connection, lobby, game, spectator, result, reconnecting, and error states in vanilla HTML/CSS/JavaScript.
- Render 10 × 10 square-cell boards with Cyrillic coordinates, accessible symbols for water, ship, miss, hit, sunk, and selected target.
- Implement the decided shot-confirmation interaction and disable controls whenever the server state does not permit an action.
- Show one board at a time on narrow phones and two boards side-by-side on sufficiently wide tablets.
- Persist name and session token in `localStorage`; resume safely after reload.
- Implement WebSocket reconnect, HTTP fallback, stale-version recovery, and visible connection status.
- Minify and gzip assets at build time and serve correct MIME, content encoding, cache, and no-cache headers.
### Acceptance criteria
- Every screen and message required by `MVP.md` is available in Russian.
- The interface is usable on a narrow phone and a tablet in portrait and landscape orientations.
- Ship, miss, hit, sunk, and selection states are distinguishable without color alone.
- No hidden ship data, token, or credential is present in static assets or browser logs.
- Compressed assets and browser runtime memory remain within the Milestone 005 budgets.
- The application works with all network access disabled except the local ESP32 address.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 014 from `BLOCKED` to `READY`.
### Execution record
- Date: 2026-08-28
- Board model and revision: ESP32-C6FH4 QFN32, revision v0.2.
- Toolchain and library versions: PlatformIO Core 6.1.19; `espressif32` 7.0.1; ESP-IDF 6.0.1; `esp_littlefs` 1.20.4.
- Result: PASS, pending physical-device UI confirmation.
- Evidence: Replaced the diagnostic page with a local, Russian, dependency-free phone-first application covering connection, lobby, game, spectator, result, reconnecting, and error states. It renders labelled 10 × 10 boards with accessible state symbols; uses selected-target then confirmation shot handling; gates all state-changing controls on the authorized server view; switches from tabs to side-by-side boards at tablet width; persists only name and session token locally; and uses WebSocket recovery with safe HTTP polling/version reconciliation. A PlatformIO pre-build script minifies and deterministically gzips the three static assets. No external resources, embedded credentials, or token logging were introduced.
- Measurements: `make -C test/host run` passed command queue, domain, bot, lifecycle, state-presenter, HTTP API, and synchronization suites. `node --check data/app.js`, gzip integrity checks, and JavaScript syntax checking of the compressed asset passed. `pio run -e esp32-c6-devkitm-1 -t buildfs` included all six source/compressed web assets; their combined size is 30,915 B, below the 250,000 B LittleFS asset budget. `pio run -e esp32-c6-devkitm-1` passed with 39,348 / 327,680 B RAM (12.0%) and 1,014,500 / 2,097,152 B flash (48.4%).
- Issues or deviations: No firmware upload was performed. Visual checks on a physical narrow phone and tablet, plus local-network WebSocket interruption/recovery, remain hardware verification. The available browser automation endpoint had no browser attached, so no automated visual inspection was possible.
- Next action: Milestone 014 is ready. Do not start it unless explicitly requested.
---
## Milestone 014 — Complete human-vs-human gameplay end to end
**Status:** `READY`
**Depends on:** Milestone 013
### Objective
Integrate and prove the complete two-device human-vs-human journey before enabling bot-specific flows.
### Work
- Exercise join, mode selection, Player 2 arrival, start, random first turn, full game, finish, revealed boards, rematch, and return to lobby.
- Test refresh and token resume for both players in every phase.
- Test disconnect/reconnect during each player's turn and while waiting for rematch.
- Connect spectators before and during the game and verify read-only behavior.
- Compare server statistics and every rendered board after each shot in a deterministic scripted game.
### Acceptance criteria
- Two physical client devices can complete a full valid game without manual ESP32 intervention.
- Turn retention, turn changes, sunk-cell marking, victory, statistics, and rematch exactly match the locked rules.
- Refresh and reconnect restore each player's role and authorized view.
- Spectators never gain controls or hidden state.
- The run remains within the resource and latency budgets.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 015 from `BLOCKED` to `READY`.
---
## Milestone 015 — Complete human-vs-ESP32 gameplay and cumulative statistics
**Status:** `BLOCKED`
**Depends on:** Milestone 014
### Objective
Integrate and prove the complete bot game, delayed multi-shot turns, rematch, and reboot-scoped cumulative statistics.
### Work
- Exercise human-vs-bot start with both possible first players.
- Verify non-blocking bot delays and repeated bot shots after hits.
- Test reconnect, abort, finish, rematch, and cancellation while a bot action is pending.
- Verify per-match and cumulative shots, hits, misses, accuracy, sunk ships, wins, and losses.
- Verify that rematch resets match statistics, preserves cumulative statistics, and creates new fleets/first player.
- Verify that a board reboot resets all sessions, game state, and cumulative statistics as required.
### Acceptance criteria
- A user can complete multiple games against ESP32 without a repeated or illegal bot shot.
- The bot never uses hidden board knowledge and never blocks network servicing during its delay or target calculation.
- All statistics match independently computed expected values.
- Rematch and reboot behavior match `MVP.md` exactly.
- Resource usage remains stable across repeated bot games.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 016 from `BLOCKED` to `READY`.
---
## Milestone 016 — Harden errors, recovery, and resource usage
**Status:** `BLOCKED`
**Depends on:** Milestone 015
### Objective
Make the integrated application resilient to malformed traffic, connection churn, Wi-Fi interruption, slow clients, and long runtime on the constrained board.
### Work
- Fuzz bounded HTTP and WebSocket parsers with malformed, truncated, duplicate, stale, oversized, and unauthorized messages.
- Repeatedly connect, disconnect, refresh, and expire spectator sessions at maximum capacity.
- Interrupt Wi-Fi, WebSocket, and individual clients during all game phases and pending bot actions.
- Measure firmware, LittleFS, current/minimum heap, largest free block, task stacks, message sizes, response/update latency, and watchdog/reset reasons.
- Remove avoidable dynamic allocation from hot paths, compress static assets, and tune reusable buffers without weakening contracts.
- Run static checks and all host/device test suites from a clean build.
### Acceptance criteria
- Invalid traffic cannot crash, restart, starve, or leak hidden state from the device.
- Wi-Fi and WebSocket recovery requires no board reboot and preserves valid in-memory game state when specified.
- Connection churn does not produce a persistent heap or largest-block decline.
- Final firmware, filesystem, heap, stack, payload, and latency measurements pass every Milestone 005 limit.
- No unresolved high-severity correctness, privacy, or stability defect remains.
### Completion action
If all criteria pass, set this milestone to `DONE`, append its execution record, and change Milestone 017 from `BLOCKED` to `READY`.
---
## Milestone 017 — Execute final MVP acceptance and create the release baseline
**Status:** `BLOCKED`
**Depends on:** Milestone 016
### Objective
Prove every MVP readiness criterion on the physical ESP32-C6 and produce a reproducible release baseline.
### Work
- Run every automated domain, bot, session, presenter, API, browser, and device test from a clean checkout/configuration.
- Execute all manual scenarios from `MVP.md` on the target board, including phone, tablet, spectators, reconnect, fallback, rematch, and reboot.
- Complete at least 20 consecutive representative games without manual restart, watchdog reset, or material memory decline.
- Run the maximum target load of two players and eight spectators for the duration defined in the resource budget.
- Record final version pins, build hashes, firmware/LittleFS sizes, heap/stack minima, largest payloads, latency, and reset reasons.
- Create concise setup, Wi-Fi configuration, build, upload, usage, and recovery documentation.
- Tag or otherwise record the exact source/configuration baseline accepted as the MVP release.
### Acceptance criteria
- All 13 MVP readiness criteria pass with recorded evidence.
- Twenty consecutive games complete without a hang, unexpected restart, or material memory leak.
- Two players and eight spectators remain supported within the final resource budgets.
- A clean build and upload are reproducible with pinned dependencies and no flash-size warning.
- No secret is present in tracked files or release artifacts.
- The release decision is documented as `PASS`, with remaining non-MVP ideas kept outside the release scope.
### Completion action
If all criteria pass, set this milestone to `DONE` and append its execution record. Add any post-MVP milestones only after Milestone 017 and do not renumber existing milestones.