docs(14.2): capture phase context

This commit is contained in:
Jakub Zych
2026-10-07 19:58:21 +02:00
parent c972ebd0cf
commit 286603db8d
2 changed files with 285 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
# Phase 14.2: Local Fonoteka frontend on local SummerCMS backend - Context
**Gathered:** 2026-10-07
**Status:** Ready for planning
<domain>
## Phase Boundary
A developer runs the existing `vue-fonoteka-app` (Nuxt 4) locally against the local `fonoteka.go` binary, with no PHP involved. They sign in and use the core flows: browse collections and albums, search, edit, rate, upload a cover, and add and remove wishlist entries. The phase delivers four things: a repeatable local data seed, a single start script, a guard that fails closed when Nuxt would reach PHP or production, and proof of the flows (Playwright against Go plus one manual session). The production data import (`summer import:winter`), the nginx swap and the production soak belong to Phase 20.
Repos: `fonoteka.go` (seed command, start script, dev env template, any Go fixes), `vue-fonoteka-app` (small non-breaking edits only, see D-11), and `summercms.go` only if a framework helper is missing, plus the phase check script under `summercms.go/scripts/`.
</domain>
<decisions>
## Implementation Decisions
### Local data
- **D-01:** Local data comes from `fonoteka migrate` followed by a new seed command. The Phase 20 `import:winter` runner is not pulled forward, and PHP data is not imported in this phase.
- **D-02:** The seed is an application command, `fonoteka dev:seed` (final name at the planner's discretion), in the fonoteka plugin, not a framework seeder hook. It refuses to run unless the app environment is local/dev, and it is idempotent: a second run changes nothing. It creates:
- a verified user;
- an owned collection;
- a few albums;
- a wishlist entry;
- a personal API token (`inv_` prefix) so fonoteka-mcp can be pointed at the local stack.
- **D-03:** Seeded albums get bundled test cover images (small, embedded or generated, no network), attached through the normal attach path into `storage/app/uploads/public`. Their `/storage/app/uploads/public/...` URLs then resolve through the Nuxt `/storage` proxy right away.
- **D-04:** The seeded user's credentials come from `E2E_EMAIL` / `E2E_PASSWORD`, the same variables the Nuxt Playwright suite uses, with a fallback to `demo@example.com` / `changeme`. That way the seed and the Playwright login match without extra wiring.
### Stack startup
- **D-05:** A single script, `fonoteka.go/scripts/dev-stack.sh`, uses host services and adds no docker compose. It:
1. checks that the services are reachable;
2. runs `summer build`, `migrate` and `dev:seed`;
3. reindexes search after the seed;
4. starts `fonoteka serve`;
5. runs the fail-closed guard (D-09);
6. starts `pnpm dev` in the sibling `vue-fonoteka-app`.
- **D-06:** Postgres, Typesense and Centrifugo are all required. The script refuses to start, and names the missing service, if any of them is unreachable. Realtime is expected to work locally, which means `NUXT_PUBLIC_CENTRIFUGO_WS_URL` is set and the app origin `http://localhost:3119` is in Centrifugo's allowed origins (documented).
- **D-07:** Go serves on `:8423`, which is distinct from PHP's `:8422` and from the framework default `:8080`. PHP and Go can then run side by side, and a stale origin is obvious.
- **D-08:** Settings and secrets live in a gitignored `fonoteka.go/.env.dev`, with a committed `.env.dev.example` that holds placeholders only. It covers the DSN, JWT secret, app key, Typesense and Centrifugo keys, and the Nuxt-side values. The script sources it and fails, listing each missing key. The script passes `NUXT_DEV_BACKEND_ORIGIN`, `NUXT_INTERNAL_API_ORIGIN` and `NUXT_PUBLIC_CENTRIFUGO_WS_URL` as environment variables on the `pnpm dev` process. It never writes `vue-fonoteka-app/.env`, so the developer's PHP-targeted setup stays intact.
### PHP fall-through guard
- **D-09:** The guard has three parts:
1. **Preflight:** before Nuxt starts, the script probes the Go origin for a Go-only marker and refuses to continue if the marker is missing, if the target is `:8422`, or if a PHP backend answers.
2. **Pinned SSR origin:** `NUXT_INTERNAL_API_ORIGIN` is always set explicitly to the Go origin, so SSR can never fall back to `https://plytarium.com`.
3. **Post-start probe:** once Nuxt is up, the script probes through Nuxt's proxy (`http://localhost:3119/...`) and confirms the marker comes back, which proves the proxy reaches Go.
- **D-10:** The marker is a route only the Go binary serves (an existing framework health/readiness route, or the smallest new one) that stays **outside the parity manifest**. No manifest route's response changes, and no new header is added to API responses.
- **D-11:** `vue-fonoteka-app` may receive **small non-breaking changes** (user's decision). The constraint: the app must still run without issues against the PHP backend. Gaps found during the session are fixed in Go first, with a parity case added where a recorded PHP response exists. An app change is acceptable only when it leaves PHP behavior unchanged (for example an env-switched test helper, a new backend-agnostic spec, or docs and `.env.example` notes). Commits in `vue-fonoteka-app` go to its own repo, which is a submodule of the PHP `fonoteka` repo.
- **D-12:** Failures show up through the Go request log, which is redacted by sunscreen and records each request's path and status, and through Nuxt's normal behavior. The script tees both processes into labeled log files, and the run docs say to watch them for 4xx/5xx and 404 responses. No 404 tally or dev-only error UI is added.
### Flow proof
- **D-13:** The flows are proven twice:
- **Playwright:** the existing Playwright e2e suite runs against the Go stack.
- **Manual session:** one checklist-driven session, recorded as UAT evidence. It covers login, browse collections and albums, search, edit, rate, cover upload, wishlist add/remove, and realtime working.
- **D-14:** The Playwright seed helper (`tests/e2e/helpers/runTinker.ts`) gets a non-breaking switch. When `E2E_BACKEND=go` is set, it seeds by running the Go binary's `dev:seed` (binary path from `E2E_GO_BIN` or similar) instead of `php artisan tinker`. When the switch is unset, today's PHP behavior is unchanged.
- **D-15:** The Go gate is the core-flow subset of specs: `auth.setup`, `login`, `smoke`, `fonoteka-item` (browse/edit/rate/cover), `public-share`, `realtime`, `i18n`, plus whichever specs cover search and wishlist. The researcher maps each flow to a spec. `fonoteka-ai-assist`, `fonoteka-bulk-add`, `offline`, `registration-invitation` and `responsive` are run and reported but do not block, because they need external keys or are outside the core flows.
- **D-16:** If no existing spec covers a core flow (wishlist add/remove and search are the likely candidates), a new spec is added to `vue-fonoteka-app`. It must be backend-agnostic, so it passes against both PHP and Go.
- **D-17:** The last plan of the phase (unit tests last, project rule) adds Go unit tests for `dev:seed` (environment refusal, idempotency, created shape) and for the marker route. It also adds `summercms.go/scripts/check-phase14.2.sh`, following the existing `check-phase*.sh` pattern, which:
- runs `go vet` and `go test ./...`;
- exercises the guard against a stale `:8422` origin and against an unset internal origin, and expects a refusal each time;
- runs the Go-gate Playwright subset (D-15) against the started stack.
### Claude's Discretion
- The exact command name and flags of the seed command, the number of seeded albums and their content, and how the test images are produced.
- Which Go-only route serves as the marker, and how a PHP answer is detected in the preflight.
- The script structure: subcommands, how processes are supervised and stopped, log file locations, and whether `summer build` runs on every start.
- The run docs (a section in `fonoteka.go/README.md` or a dedicated `RUNNING-LOCALLY.md`) and the keys in `.env.dev.example`.
- How River jobs run locally (`work_in_serve: true` is the current default and is fine).
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### Phase scope and neighbors
- `.planning/ROADMAP.md` § Phase 14.2: goal and success criteria 1–4.
- `.planning/REQUIREMENTS.md`: QA-05, the run-unchanged requirement.
- `.planning/phases/20-cutover/20-CONTEXT.md`: D-01/D-02 (`import:winter` stays in Phase 20), D-06 (the nginx locations, which match the Nuxt proxy list), D-13 (14.2 is a hard prerequisite of 20) and D-15 (the production version of the manual session checklist).
- `.planning/phases/14-domain-jobs-and-external-integrations/14-CONTEXT.md`: the jobs, Discogs and AI context behind the specs that do not block (D-15).
- `.planning/phases/14.1-oauth-identities-and-fonoteka-me-routes/`: the last ported routes; the manifest has zero pending entries.
### Frontend contract (vue-fonoteka-app, at /media/nvme/dev/golem15/fonoteka/vue-fonoteka-app)
- `nuxt.config.ts`: `NUXT_DEV_BACKEND_ORIGIN`, the `internalApiOrigin` fallback chain (falls back to plytarium.com), the `routeRules` / `nitro.devProxy` / `vite.server.proxy` lists (`/_user/api`, `/_journal/api`, `/_fonoteka/api`, `/_feedback/api`, `/plugins/golem15/feedback`, `/api/realtime`, `/storage`) and the dev port 3119.
- `.env.example`: the documented dev env keys, the Centrifugo setup steps and the `E2E_*` variables.
- `playwright.config.ts`, `tests/e2e/*.spec.ts` and `tests/e2e/helpers/runTinker.ts`: the e2e suite and the tinker seed helper to switch (D-14).
### Backend (fonoteka.go)
- `fonoteka.go/README.md`: database setup (Polish ICU collation), the `SUMMER_*` env mapping, and the migrate/serve commands.
- `fonoteka.go/config/http.yaml` (CORS, body limits), `config/storage.yaml` (`/storage/app/uploads/public`), `config/search.yaml` (Typesense on :8181), `config/realtime.yaml`, `config/queue.yaml` (`work_in_serve`).
- `fonoteka.go/parity/manifest.yaml`: the marker route must stay outside it (D-10).
- `summercms.go/modules/surf/serve.go`: the `serve` address flag (default `:8080`) and the uploads bucket publishing.
- `summercms.go/scripts/check-phase14.sh` (and the other `check-phase*.sh` scripts): the pattern for D-17.
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- The Nuxt dev proxy already sends every backend path to a single `NUXT_DEV_BACKEND_ORIGIN`. Pointing it at Go needs no CORS, because the browser stays same-origin.
- `fonoteka migrate`, `fonoteka:reindex` (Phase 14) and `fonoteka serve` cover the start sequence. The reindex command asserts zero `collection_id`-0 documents.
- The lagoon attach path and the uploads bucket (`file://./storage/app/uploads/public`) give the seeded covers PHP-identical public URLs.
- The `golem15.user` API tokens (`inv_` prefix) let the seed mint an MCP-usable token.
- The sunscreen redacting slog handler is already in the generated main (Phase 14-01) and serves as the request log for D-12.
### Established Patterns
- Phase check scripts live in `summercms.go/scripts/check-phase<N>.sh`.
- The generated `main.go` / `plugins.gen.go` come from `summer build`. Do not hand-edit them.
- Commit rules: one logical change per commit, planning docs separate from code, no co-author tags. `vue-fonoteka-app` commits go to its own repo.
### Integration Points
- `fonoteka.go/scripts/dev-stack.sh` (new), `.env.dev.example` (new), `.gitignore` (`.env.dev`).
- The fonoteka plugin's console commands (registers `dev:seed`).
- `vue-fonoteka-app/tests/e2e/helpers/runTinker.ts` (`E2E_BACKEND=go` switch) and any new backend-agnostic specs.
- Centrifugo's local allowed origins (operator config, documented).
</code_context>
<specifics>
## Specific Ideas
- Ports: Nuxt on `:3119`, Go on `:8423`, PHP stays on `:8422`, Typesense on `:8181`.
- During the phase, the PHP-connected frontend must keep working with no issues. Every app edit is checked against PHP as well as Go.
</specifics>
<deferred>
## Deferred Ideas
- Bringing the Phase 20 `import:winter` runner forward to load real local PHP data was considered and not chosen. It stays in Phase 20.
- A 404/5xx tally by path at script shutdown was offered and not chosen. It can be added later if the logs prove too noisy.
### Reviewed Todos (not folded)
- `2026-10-01-benchmark-the-application-on-wintercms-vs-summercms.md`: a benchmark, not local-run glue (Phase 20 lists it as deferred too).
- `nest-framework-packages-under-modules.md`, `bonfire-duplicate-command-names.md`, `backend-admin-api-tokens.md`: framework work unrelated to the local Nuxt run; matched by keyword only.
</deferred>
---
*Phase: 14.2-local-fonoteka-frontend-on-local-summercms-backend*
*Context gathered: 2026-10-07*

View File

@@ -0,0 +1,142 @@
# Phase 14.2: Local Fonoteka frontend on local SummerCMS backend - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-10-07
**Phase:** 14.2-local-fonoteka-frontend-on-local-summercms-backend
**Areas discussed:** Local data, Stack startup, PHP fall-through guard, Flow proof
---
## Local data
| Option | Description | Selected |
|--------|-------------|----------|
| Seed command | Fresh migrate plus an app dev seed; import:winter stays in Phase 20 | ✓ |
| Pull import:winter forward | Build the Phase 20 D-02 runner now and import local PHP MariaDB | |
| Register through the UI | No seed; create the data by hand | |
| Option | Description | Selected |
|--------|-------------|----------|
| App command, idempotent | `fonoteka dev:seed`, refuses non-local env, safe to re-run | ✓ |
| App command, destructive reset | Truncates the domain tables first | |
| Framework summer seed hook | A generic seeder capability in summercms.go | |
| Option | Description | Selected |
|--------|-------------|----------|
| Yes, bundled test images | Covers attached through the normal attach path, no network | ✓ |
| No covers | Cover upload is the only image check | |
| Option | Description | Selected |
|--------|-------------|----------|
| Same E2E_* vars | Seed reads E2E_EMAIL/E2E_PASSWORD with a demo fallback | ✓ |
| Own SUMMER_* keys | Separate config namespace | |
**User's choice:** the recommended option each time.
---
## Stack startup
| Option | Description | Selected |
|--------|-------------|----------|
| One script, host services | scripts/dev-stack.sh using existing Postgres/Typesense | ✓ |
| docker compose for services + script | Committed compose.yaml | |
| Docs only | No orchestration script | |
| Option | Description | Selected |
|--------|-------------|----------|
| PG required; Typesense + Centrifugo optional | Search fallback and realtime no-op | |
| PG + Typesense required | Centrifugo optional | |
| All three required | Full production parity, realtime included | ✓ |
| Option | Description | Selected |
|--------|-------------|----------|
| Distinct port, e.g. :8423 | Runs side by side with PHP :8422 | ✓ |
| Reuse :8422 | Existing .env files work as they are | |
| Framework default :8080 | No override | |
| Option | Description | Selected |
|--------|-------------|----------|
| Gitignored dev env file | .env.dev plus a committed .env.dev.example | ✓ |
| Your shell env | No new file | |
| Option | Description | Selected |
|--------|-------------|----------|
| Env vars on the pnpm dev process | Never writes vue-fonoteka-app/.env | ✓ |
| Write the app's .env | Overwrites the PHP-targeted .env | |
**User's choice:** all three services required (not the recommended option), otherwise the recommended options.
---
## PHP fall-through guard
| Option | Description | Selected |
|--------|-------------|----------|
| Preflight probe + pinned SSR origin | Probe Go before Nuxt starts, pin NUXT_INTERNAL_API_ORIGIN, probe through the Nuxt proxy after start | ✓ |
| Preflight probe only | No check through the proxy | |
| Docs + manual check | Devtools instructions | |
| Option | Description | Selected |
|--------|-------------|----------|
| Existing Go-only route | Marker route outside the parity manifest | ✓ |
| Dev-only response header | X-Summer-Backend when env is local | |
| Option | Description | Selected |
|--------|-------------|----------|
| No app edits; fix Go only | The app runs unchanged | |
| Docs-only edits allowed | Plus .env.example comments | |
| Small app fixes allowed | Frontend fixes where the frontend is wrong | |
**User's choice (free text):** "Small non-breaking changes are allowed (PHP connected frontend still needs to run without issues)".
| Option | Description | Selected |
|--------|-------------|----------|
| Go request log + Nuxt as is | Labeled log files, watch for 4xx/5xx | ✓ |
| Plus an unported-route 404 tally | Summary at shutdown | |
---
## Flow proof
| Option | Description | Selected |
|--------|-------------|----------|
| Playwright on Go + manual session | Existing specs against Go plus one checklist session | ✓ |
| Manual checklist only | UAT evidence only | |
| Playwright only | No manual session | |
| Option | Description | Selected |
|--------|-------------|----------|
| E2E_BACKEND=go switch | Non-breaking helper switch to dev:seed | ✓ |
| Pre-seeded, helper untouched | Tinker-gated specs skip | |
| Option | Description | Selected |
|--------|-------------|----------|
| Core-flow subset | Named specs gate; AI, bulk-add and offline do not block | ✓ |
| Whole suite must pass | Includes specs that need external keys | |
| Option | Description | Selected |
|--------|-------------|----------|
| Add a backend-agnostic spec | Passes against both PHP and Go | ✓ |
| Manual checklist covers it | No new specs | |
| Option | Description | Selected |
|--------|-------------|----------|
| scripts/check-phase14.2.sh + Go tests | Seed/marker unit tests, guard refusal checks, Playwright subset | ✓ |
| Go tests only | Script and Playwright not part of the gate | |
---
## Claude's Discretion
- Seed command name, fixture content and test image production.
- Marker route choice and PHP detection in the preflight.
- Script structure, process supervision, log locations.
- Where the run docs live and the keys in .env.dev.example.
## Deferred Ideas
- Pulling import:winter forward (not chosen; stays in Phase 20).
- 404/5xx tally at script shutdown (not chosen).