Files
battleship/PLANS.md
T

298 lines
17 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:** `READY`
**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`.
---
## Milestone 003 — Prove real-time transport, fallback, and state isolation
**Status:** `BLOCKED`
**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`.
---
## Milestone 004 — Prove MVP capacity and make the Go/No-Go decision
**Status:** `BLOCKED`
**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.
### 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.