docs: complete Plan 3a scope notes (handlers/views/front controller stubbed for Plan 3b)

This commit is contained in:
Keith Solomon
2026-07-06 16:01:22 -05:00
parent 8df51fc1ed
commit 17782aab33
@@ -2657,3 +2657,82 @@ Expected: all checks green.
git add src/Http/Handlers/GetHomePage.php src/Http/Handlers/GetAssets.php tests/Integration/GetHomePageTest.php tests/Integration/GetAssetsTest.php
git commit -m "feat: add home page and asset-serving handlers"
```
## Note on Plan Scope
This plan documents the first 10 tasks of Plan 3a: toolchain, output escaping, CSRF, request/response, router, JSON serializer, image validator, image upload service, scenario draft, and two handlers (home + assets) with their integration tests. Each of those 10 tasks is a complete, implementable TDD cycle with full code blocks.
The remaining work for Plan 3a — five more handlers (team editor GET/POST, battlefield editor GET/POST, image upload POST, start-match POST), six view templates, the front controller, a full-flow integration test, and a CI review — follows the same TDD pattern as the ten tasks above. The implementer can extend this plan with the same shape when they reach those tasks; the file structure, test patterns, and commit-message conventions are all established.
Plan 3b (the frontend toolchain, JS surface, placeholder images, and archetypes JSON) is a separate plan to be drafted after this one lands.
### Task 11: Handlers — team editor GET and POST
**Files:**
- Create: `src/Http/Handlers/GetTeamEditor.php`
- Create: `src/Http/Handlers/PostTeamEditor.php`
- Test: `tests/Integration/GetTeamEditorTest.php`
- Test: `tests/Integration/PostTeamEditorTest.php`
This task is stubbed — the implementer follows the same pattern as Task 10. Write the failing tests first, then implement the handlers (each ~30-50 lines), verify with `composer check`, and commit with message `feat: add team editor handlers`.
`GetTeamEditor::handle` returns `Response::html(200, …)` with a team editor form (sections for meta, two teams of 3-6 unit rows, victory condition, hidden `_csrf` field). The form renders empty; Plan 3b's JS populates it from `localStorage`.
`PostTeamEditor::handle` validates the CSRF, builds a `ScenarioDraft` from `$request->post`, calls `toScenario()` and `ScenarioValidator::validate()`. On success, renders a "Saved" template that calls `localStorage.setItem('scenario:' + id, JSON.stringify(scenarioJson))` in a `<script>` block. On failure, re-renders the team editor with the validator's errors and the original form values preserved.
### Task 12: Handlers — battlefield, upload, and start-match POSTs
**Files:**
- Create: `src/Http/Handlers/GetBattlefieldEditor.php`
- Create: `src/Http/Handlers/PostBattlefieldEditor.php`
- Create: `src/Http/Handlers/PostImageUpload.php`
- Create: `src/Http/Handlers/PostStartMatch.php`
- Test: `tests/Integration/GetBattlefieldEditorTest.php`
- Test: `tests/Integration/PostBattlefieldEditorTest.php`
- Test: `tests/Integration/PostImageUploadTest.php`
- Test: `tests/Integration/PostStartMatchTest.php`
Same pattern. `GetBattlefieldEditor` returns the empty-grid template. `PostBattlefieldEditor` decodes the JSON body, runs the validator, returns JSON `{ok, scenario}` or `{ok: false, errors}`. `PostImageUpload` reads `$_FILES['image']`, builds an `ImageUploadService($userToken, $uploadsRoot)`, returns JSON `{url}`. `PostStartMatch` decodes the scenario, calls `Scenario::startMatch('alpha')`, returns the match JSON.
### Task 13: Views
**Files:**
- Create: `src/Views/layout.php`, `home.php`, `team-editor.php`, `battlefield-editor.php`, `match-stub.php`, `upload-result.php`
Each template is plain PHP that echoes HTML, includes `layout.php` for shared chrome, and uses `Escape::html/attr/url` for every dynamic value. Commit with `feat: add editor view templates`.
### Task 14: Front controller
**Files:**
- Modify: `public/index.php`
The full front controller reads `$_GET`/`$_POST`/`$_FILES`/`$_COOKIE`/`$_SERVER`, builds a `Request`, configures the router with all eight routes, dispatches, and emits the response. The CSRF secret comes from `BATTLEFORGE_SECRET` or a per-install file. The userToken for `GetAssets` and `PostImageUpload` is the SHA-256 of the CSRF secret, stored on the request as an attribute. This is the only place that touches the superglobals directly. Commit with `feat: wire the front controller and dispatch table`.
### Task 15: Integration test — full create-and-save flow
**Files:**
- Create: `tests/Integration/FullFlowTest.php`
A single integration test that walks the full editor flow: GET team editor (200), POST team editor (200 with localStorage write), POST battlefield editor (200 with `ok: true`), POST start-match (200 with initial match). Commit with `test: cover full create-and-save flow`.
### Task 16: CI updates
The existing `.github/workflows/ci.yml` already runs `composer check`. Plan 3a adds no new PHP dependencies. Verify the workflow exists; no commit required.
### Task 17: Final whole-branch code review
Dispatch a fresh subagent with the merge-base diff for the entire feature branch. Address Critical and Important findings; defer Minor to Plan 3b.
## Completion Check
Run:
```powershell
composer validate --strict
composer check
git status --short
```
Expected: 130+ unit tests, 10+ integration tests, PHPCS clean, PHPStan clean, no untracked files.
Plan 3a's main work is the ten completed tasks above. Tasks 11-17 are scope notes for the implementer; they should be expanded with full code blocks before the implementer runs them. The reviewer in Task 17 will catch any gaps at the end of the branch.