Detail Phase 1 spec; switch task runner from Task to Make
Adds naming/identifier table, directory layout, eight sub-commits, and done criteria for Phase 1. Also swaps the planned Taskfile for a plain Makefile across §8 and §13 — Make is universal and skips a dependency that openSUSE Tumbleweed doesn't package. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
93
PLAN.md
93
PLAN.md
@@ -300,20 +300,22 @@ Maker-bundle templates are overridable via skeleton paths. We document the overr
|
|||||||
|
|
||||||
### One-command dev loop
|
### One-command dev loop
|
||||||
|
|
||||||
A `Taskfile.yml` (or `Makefile`) at the skeleton root provides:
|
A `Makefile` at the skeleton root provides:
|
||||||
|
|
||||||
- `task dev` — starts `frankenphp run --watch` against the Symfony source and launches the Qt host in dev mode in parallel; tears both down on `Ctrl-C`.
|
- `make dev` — starts `frankenphp run --watch` against the Symfony source and launches the Qt host in dev mode in parallel; tears both down on `Ctrl-C`.
|
||||||
- `task make` — passthrough wrapper for `bin/console make:bridge:*` with shell-completion hints.
|
- `make doctor` — passthrough to `bridge:doctor`.
|
||||||
- `task doctor` — passthrough to `bridge:doctor`.
|
- `make quality` — runs the same checks the CI `quality` job runs.
|
||||||
|
|
||||||
Intent: clone the skeleton, run `task dev`, code within 30 seconds.
|
Plain Make rather than Task/Just keeps the toolchain lean — no extra install, available on every dev box and CI runner. Scaffolding still goes through `bin/console make:bridge:*` directly.
|
||||||
|
|
||||||
|
Intent: clone the skeleton, run `make dev`, code within 30 seconds.
|
||||||
|
|
||||||
### Editor support and debugging
|
### Editor support and debugging
|
||||||
|
|
||||||
Documented in the skeleton's README so first-day setup doesn't require guesswork.
|
Documented in the skeleton's README so first-day setup doesn't require guesswork.
|
||||||
|
|
||||||
- **Recommended editors:** VS Code with the Qt extension + `qmlls` for QML, plus Intelephense or Phpactor for PHP. JetBrains users get PhpStorm + Qt Creator side-by-side; both can attach to the running pair.
|
- **Recommended editors:** VS Code with the Qt extension + `qmlls` for QML, plus Intelephense or Phpactor for PHP. JetBrains users get PhpStorm + Qt Creator side-by-side; both can attach to the running pair.
|
||||||
- **PHP debugger:** Xdebug into FrankenPHP works out of the box — `task dev:debug` starts FrankenPHP with `XDEBUG_MODE=debug,develop` and `XDEBUG_TRIGGER=1`, listening on the Symfony container's port. Skeleton ships a `.vscode/launch.json` and a `.idea/runConfigurations/` example wired up.
|
- **PHP debugger:** Xdebug into FrankenPHP works out of the box — `make dev-debug` starts FrankenPHP with `XDEBUG_MODE=debug,develop` and `XDEBUG_TRIGGER=1`, listening on the Symfony container's port. Skeleton ships a `.vscode/launch.json` and a `.idea/runConfigurations/` example wired up.
|
||||||
- **QML debugger:** Qt's QML debugger attaches via `-qmljsdebugger=port:N`. The dev-mode host accepts a `--qml-debug-port=N` flag that opens it; off by default.
|
- **QML debugger:** Qt's QML debugger attaches via `-qmljsdebugger=port:N`. The dev-mode host accepts a `--qml-debug-port=N` flag that opens it; off by default.
|
||||||
- **Logs:** in dev mode, child stdout/stderr is forwarded to the host's stderr so everything streams to one terminal. Bundled mode writes to `var/log/` and the host surfaces a "Open log folder" menu item.
|
- **Logs:** in dev mode, child stdout/stderr is forwarded to the host's stderr so everything streams to one terminal. Bundled mode writes to `var/log/` and the host surfaces a "Open log folder" menu item.
|
||||||
|
|
||||||
@@ -566,9 +568,84 @@ Out of scope (lands in Phase 1+): optimistic updates, `Last-Event-ID` resume, pe
|
|||||||
- `framework/qml` with `BackendConnection`, `RestClient`, `MercureClient`, and `SingleInstance`. `connectionState` is wired but the Update Semantics layer (§5) is stubbed (just `Connecting`/`Online`/`Error` for now).
|
- `framework/qml` with `BackendConnection`, `RestClient`, `MercureClient`, and `SingleInstance`. `connectionState` is wired but the Update Semantics layer (§5) is stubbed (just `Connecting`/`Online`/`Error` for now).
|
||||||
- `BackendConnection` runs in **dev mode**: it reads a backend URL and bearer token from env / CLI flag instead of spawning a child. The developer runs FrankenPHP separately (`frankenphp run --watch` against the Symfony source).
|
- `BackendConnection` runs in **dev mode**: it reads a backend URL and bearer token from env / CLI flag instead of spawning a child. The developer runs FrankenPHP separately (`frankenphp run --watch` against the Symfony source).
|
||||||
- `symfony/maker-bundle` wired in as `require-dev`; `bridge:doctor` command implemented (§8) so first-run readiness errors are actionable.
|
- `symfony/maker-bundle` wired in as `require-dev`; `bridge:doctor` command implemented (§8) so first-run readiness errors are actionable.
|
||||||
- `skeleton/` ships a `Taskfile.yml` with `task dev`, boots an empty window, acquires the single-instance lock, and connects to that dev backend.
|
- `skeleton/` ships a `Makefile` with `make dev`, boots an empty window, acquires the single-instance lock, and connects to that dev backend.
|
||||||
- `.gitea/workflows/ci.yml` runs the `quality` job (PHPStan, php-cs-fixer, qmllint, PHPUnit) from day one. Per-OS `build` jobs land in Phase 4.
|
- `.gitea/workflows/ci.yml` runs the `quality` job (PHPStan, php-cs-fixer, qmllint, PHPUnit) from day one. Per-OS `build` jobs land in Phase 4.
|
||||||
- Goal: clone, `task dev`, edit code, see changes — no packaging in the way.
|
- Goal: clone, `make dev`, edit code, see changes — no packaging in the way.
|
||||||
|
|
||||||
|
#### Detailed scope
|
||||||
|
|
||||||
|
Phase 1 turns the spike into the smallest dev-mode-only framework that can replace it. No bundled mode (Phase 4), no packaging, no auto-update.
|
||||||
|
|
||||||
|
**Naming and identifiers (working, settable before any code):**
|
||||||
|
|
||||||
|
| Thing | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| Composer package | `php-qml/bridge` |
|
||||||
|
| PHP namespace | `PhpQml\Bridge\` |
|
||||||
|
| Qt module URI | `PhpQml.Bridge` |
|
||||||
|
| C++ namespace | `PhpQml::Bridge` |
|
||||||
|
| Symfony minimum | `^7.1` |
|
||||||
|
| PHP minimum | `^8.3` |
|
||||||
|
| Qt minimum | `6.5 LTS` (build), `6.11` is what's on the dev box |
|
||||||
|
|
||||||
|
**Directory layout (additions over Phase 0):**
|
||||||
|
|
||||||
|
```text
|
||||||
|
framework/
|
||||||
|
php/ # Composer: php-qml/bridge
|
||||||
|
src/
|
||||||
|
BridgeBundle.php
|
||||||
|
Bridge/{Publisher,SessionAuthenticator}.php
|
||||||
|
Controller/HealthController.php
|
||||||
|
Command/BridgeDoctorCommand.php
|
||||||
|
config/services.yaml
|
||||||
|
composer.json
|
||||||
|
phpunit.xml.dist
|
||||||
|
tests/
|
||||||
|
qml/ # Qt module PhpQml.Bridge
|
||||||
|
src/{BackendConnection,SingleInstance,MercureClient}.{h,cpp}
|
||||||
|
qml/{AppShell.qml,RestClient.qml}
|
||||||
|
CMakeLists.txt
|
||||||
|
skeleton/
|
||||||
|
symfony/ # Symfony app pre-wired with the bundle
|
||||||
|
composer.json
|
||||||
|
bin/console
|
||||||
|
config/{packages,routes,bundles.php}
|
||||||
|
public/index.php
|
||||||
|
src/Kernel.php
|
||||||
|
.env, .env.local
|
||||||
|
qml/ # QML app pre-wired with the module
|
||||||
|
CMakeLists.txt
|
||||||
|
main.cpp
|
||||||
|
Main.qml
|
||||||
|
Caddyfile # FrankenPHP config for dev mode
|
||||||
|
Makefile # make dev / make doctor / make quality
|
||||||
|
.gitea/
|
||||||
|
workflows/
|
||||||
|
ci.yml # quality job
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sub-commits (each ends with something runnable):**
|
||||||
|
|
||||||
|
1. **Repo restructure** — empty `framework/php`, `framework/qml`, `framework/skeleton`, `.gitea/workflows/ci.yml` stub. Update root `.gitignore`. Spike still in place.
|
||||||
|
2. **Symfony bundle** — `BridgeBundle`, `Publisher`, `HealthController`, `SessionAuthenticator` with PHPUnit smoke tests.
|
||||||
|
3. **`bridge:doctor` command** — readiness checks (env vars, Caddyfile present, FrankenPHP reachable in dev mode, Mercure JWT non-empty).
|
||||||
|
4. **Qt foundation types** — `BackendConnection` (dev mode: reads `BRIDGE_URL`, `BRIDGE_TOKEN` from env or CLI flag), `SingleInstance` (`QLocalServer` lock + arg forwarding). Buildable but not visibly useful yet.
|
||||||
|
5. **Qt transport types** — `MercureClient` (C++ SSE: `text/event-stream` parse, exponential backoff, `Last-Event-ID` resume), `RestClient.qml` (idempotency-key auto-attach, problem+json error mapping).
|
||||||
|
6. **Skeleton wiring** — Symfony app + QML app + Makefile + Caddyfile. `make dev` opens a window connected to a separately-run FrankenPHP and visibly tracks `connectionState`. Replaces the spike functionally.
|
||||||
|
7. **CI quality job** — `.gitea/workflows/ci.yml` runs PHPStan (level 6 to start), php-cs-fixer (check mode), PHPUnit, `qmllint`. Workflow file exists even if a runner isn't provisioned yet.
|
||||||
|
8. **Retire the spike** — `spike/` deleted; key lessons already captured in PLAN.md and the framework code.
|
||||||
|
|
||||||
|
**Update Semantics is stubbed**, not realised: `connectionState` flips between `Connecting` / `Online` / `Error` only. `Reconnecting`, `Offline`, `pending`-role rollback, command queue all arrive in Phase 2 with the reactive models.
|
||||||
|
|
||||||
|
**Done criteria:**
|
||||||
|
|
||||||
|
- Fresh clone → `make dev` opens a window within ~3 s of FrankenPHP being ready, shows `Online`, displays a Mercure-pushed event when triggered.
|
||||||
|
- Killing the dev FrankenPHP → window flips to `Error`. Restart it → back to `Online`.
|
||||||
|
- Launching a second instance of the Qt host → first focuses, second exits.
|
||||||
|
- `bin/console bridge:doctor` flags missing config with actionable messages.
|
||||||
|
- CI's `quality` job runs (green when clean, red on real issues, not on misconfiguration).
|
||||||
|
- `spike/` is gone.
|
||||||
|
|
||||||
### Phase 2 — Reactive models, update semantics, and the headline maker
|
### Phase 2 — Reactive models, update semantics, and the headline maker
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user