diff --git a/PLAN.md b/PLAN.md index 41efbe1..058610d 100644 --- a/PLAN.md +++ b/PLAN.md @@ -512,6 +512,54 @@ Phased, each phase ends with something runnable. Hardcoded everything. Qt window spawns FrankenPHP, hits `GET /api/ping`, opens an SSE stream, prints incoming events to a `Text` element. Goal: prove the transport on Linux. ~1 day. +#### Concrete spec + +Lives in `spike/`. Removed when Phase 1's framework skeleton supersedes it. **No Symfony yet** — bare PHP behind FrankenPHP, the smallest thing that exercises both transport channels. + +Layout: + +```text +spike/ + README.md # how to run, what it proves, expected output + run.sh # builds (if needed) and runs FrankenPHP + the Qt host + Caddyfile # binds 127.0.0.1:8080, enables Mercure, routes index.php + .env.local # MERCURE_PUBLISHER_JWT_KEY (dev-only static key) + .gitignore # bin/, build/ + bin/frankenphp # downloaded static binary, gitignored + php/ + index.php # GET /api/ping → returns pong, publishes to Mercure + qt/ + CMakeLists.txt # minimal Qt 6 + QML project + main.cpp # QGuiApplication + QQmlApplicationEngine + spawns frankenphp child + Main.qml # window: status indicator, Ping button, event log + Mercure.qml # tiny SSE client (text/event-stream parser via QNetworkReply) +``` + +Flow: + +1. `./run.sh` builds the Qt binary (if not built) and runs it. +2. Qt host starts and spawns `bin/frankenphp run --config Caddyfile` as a child process. +3. Once `GET /api/ping` succeeds, QML opens an SSE connection to `/.well-known/mercure?topic=app://ping`. +4. Clicking the "Ping" button triggers `GET /api/ping`. The handler returns `{ "pong": true, "now": ... }` and publishes the same payload to Mercure. +5. The event arrives on the SSE stream and is appended to the visible log. + +Hardcoded for the spike: + +- Backend URL: `http://127.0.0.1:8080`. +- Mercure topic: `app://ping`. +- Mercure JWT: dev-only static key in `.env.local`. +- No auth on `/api/ping`. +- FrankenPHP static binary version pinned in `run.sh`. + +Done criteria: + +- Click "Ping" → response text updates **and** an event line appears in the log within ~50 ms. +- Killing `bin/frankenphp` externally → Qt host visibly shows the connection dropping. +- Re-running `./run.sh` → everything reconnects. +- A brief writeup in `spike/README.md` of what the spike proved and any surprises. + +Out of scope (lands in Phase 1+): optimistic updates, `Last-Event-ID` resume, per-session secret, single-instance lock, packaging, Symfony. + ### Phase 1 — Framework skeleton (dev mode from day one) - `framework/php` Symfony bundle with `Publisher`, `HealthController`, `SessionAuthenticator`.