664 lines
45 KiB
Markdown
664 lines
45 KiB
Markdown
# Roadmap: SummerCMS (Go)
|
||
|
||
## Overview
|
||
|
||
v1 ports the Płytarium (fonoteka) headless PHP backend to a single Go binary without vue-fonoteka-app or fonoteka-mcp noticing. The journey starts with the smallest possible kernel plus a day-one parity harness, proves the whole stack on one real endpoint (`GET /_fonoteka/api/v1/genres`) before any further kernel design, then broadens outward: full data-layer fidelity, the three-auth-group HTTP layer, the user plugin, OAuth2.1 for MCP/ChatGPT, the admin schema pipeline and its Vue SPA, jobs/realtime/search infrastructure, the 154-route API surface (split into two delivery slices), domain jobs and external integrations, and finally a cutover phase where the parity harness is green on all 154 routes and both real clients run unchanged against the Go backend.
|
||
|
||
## Phases
|
||
|
||
**Phase Numbering:**
|
||
|
||
- Integer phases (1, 2, 3): Planned milestone work
|
||
- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED)
|
||
|
||
Decimal phases appear between their surrounding integers in numeric order.
|
||
|
||
- [x] **Phase 1: Framework kernel foundation** - Config, plugin registry, event bus, container and CLI skeleton boot the `summer` binary (completed 2026-09-16)
|
||
- [x] **Phase 2: API parity harness bootstrap** - Fixture recorder + replay-and-diff harness against the live PHP backend, built on the Phase 1 command kernel (completed 2026-09-17)
|
||
- [x] **Phase 3: First vertical slice — genres end to end** - `GET /_fonoteka/api/v1/genres` passes the parity diff through every layer (completed 2026-09-17)
|
||
- [x] **Phase 4: CLI scaffolding, i18n and mail** - Scaffolding commands, translated/pluralized strings, mail templates (completed 2026-09-18)
|
||
- [x] **Phase 5: Data layer full fidelity** - All 25 models and their squashed migrations with fillable/hidden/cast/soft-delete discipline (completed 2026-09-18)
|
||
- [x] **Phase 6: HTTP routing, auth groups and rate limiting** - Three auth groups, named rate buckets, OAuth-safe middleware structure (completed 2026-09-21)
|
||
- [x] **Phase 7: User plugin and authentication** - Registration, login, JWT, organizations, personal tokens, must-change-password (completed 2026-09-22)
|
||
- [x] **Phase 8: OAuth2.1 authorization server** - direct standard-library `wristband` server for fonoteka-mcp and the ChatGPT connector (completed 2026-09-23)
|
||
- [ ] **Phase 9: Backend admin authentication and schema pipeline** - Admin roles, fields.yaml/columns.yaml, relation manager
|
||
- [x] **Phase 10: Admin Vue SPA** - Login, navigation, lists, forms and relation manager for five controllers (completed 2026-09-27)
|
||
- [ ] **Phase 11: Jobs, realtime and search infrastructure** - River, Centrifugo and Typesense sync brought up before the API phases that need them
|
||
- [ ] **Phase 12: Płytarium API — Collections and Albums** - Core content endpoints ported with byte-level parity
|
||
- [ ] **Phase 13: Płytarium API — wishlist, notifications, CSV, credentials, public routes** - Remaining core API surface
|
||
- [ ] **Phase 14: Domain jobs and external integrations** - CSV/Discogs jobs, wishlist digest, reindex, Discogs client, AI recognition, feedback, sitemap
|
||
- [ ] **Phase 15: Cutover** - Parity harness green on all 154 routes, both real clients run unchanged
|
||
|
||
## Phase Details
|
||
|
||
### Phase 1: Framework kernel foundation
|
||
|
||
**Goal**: The `summer` binary boots from layered YAML config, plugins self-register through one required interface plus optional capability interfaces, a typed event bus and service registry are available, and a CLI command framework with a dev watch loop exists — built only as far as the first vertical slice will need it, per the interleaved-not-sequential kernel approach.
|
||
**Mode:** mvp
|
||
**Depends on**: Nothing (first phase)
|
||
**Repos:** summercms.go
|
||
**Requirements**: KERN-01, KERN-02, KERN-03, KERN-04, KERN-05, KERN-06, KERN-07, KERN-08, KERN-09, CLI-01
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. `summer build` regenerates the blank-import plugin list and produces a binary that boots from a layered YAML config (base files, env overlay directory, per-plugin namespace, environment variables) with dot-path access.
|
||
2. A throwaway plugin registered via the Plugin interface has its Register phase run for all plugins before any Boot phase runs, in `Requires()`-topological order, verified by a test with reordered input.
|
||
3. A plugin can type-assert an optional capability interface (e.g. `HasModels`) and skip integration with another plugin that isn't registered, with no hard import.
|
||
4. The typed event bus supports fire-and-forget, fire-and-collect, and fire-until-handled dispatch, exercised by unit tests; per-request state travels only through `context.Context`, never a package-level global.
|
||
5. The `summer` CLI discovers a registered command and renders rich output (spinner, progress, table, prompts) with non-TTY degradation, and a dev watch loop rebuilds and restarts the binary on source change.
|
||
|
||
**Plans**: 4 plans
|
||
|
||
Plans:
|
||
**Wave 1**
|
||
|
||
- [x] 01-01-PLAN.md — Build and boot the separate hello app through the shared command kernel
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
|
||
- [x] 01-02-PLAN.md — Add layered config, optional plugin composition, services and typed events
|
||
|
||
**Wave 3** *(blocked on Wave 2 completion)*
|
||
|
||
- [x] 01-03-PLAN.md — Add plugin scaffolding, rich output and the watch rebuild loop
|
||
|
||
**Wave 4** *(blocked on Wave 3 completion)*
|
||
|
||
- [x] 01-04-PLAN.md — Complete unit, integration and race-enabled verification
|
||
|
||
### Phase 2: API parity harness bootstrap
|
||
|
||
**Goal**: A fixture recorder captures request/response pairs from the running PHP backend — including real Nuxt and MCP flows — and a replay-and-diff harness with a normalizer for nondeterministic fields can run against any HTTP backend. The recorder and replayer are `summer parity:*` commands on the Phase 1 command kernel (bonfire); recording itself only needs a running PHP backend, but the tooling waits for Phase 1.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 1
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Requirements**: QA-01, QA-02, QA-03
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. The fixture recorder captures a request/response pair from the live PHP backend for at least one route and stores it as a replayable fixture.
|
||
2. The replay-and-diff harness runs a recorded fixture against an arbitrary `httptest.Server` backend and reports a byte-level diff.
|
||
3. The harness's normalizer and assertions explicitly catch the parity classes named in research (nil vs `[]`, date format, tri-state booleans, envelope/conditional keys) on a synthetic test case, not just status codes.
|
||
4. `go vet` and `go test ./...` are green, and the harness's own integration tests run against testcontainers Postgres.
|
||
|
||
**Plans**: 5 plans
|
||
|
||
Plans:
|
||
**Wave 1**
|
||
|
||
- [x] 02-01-PLAN.md — Record and replay one fixture through the `summer` CLI
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
|
||
- [x] 02-02-PLAN.md — Capture safe proxy sessions and manifest routes with strict diffs
|
||
|
||
**Wave 3** *(blocked on Wave 2 completion)*
|
||
|
||
- [x] 02-03-PLAN.md — Record all 154 PHP routes and real Nuxt/MCP flows
|
||
|
||
**Wave 4** *(blocked on Wave 3 completion)*
|
||
|
||
- [x] 02-04-PLAN.md — Wire honest Go replay with testcontainers Postgres
|
||
|
||
**Wave 5** *(blocked on Waves 1–4 completion)*
|
||
|
||
- [x] 02-05-PLAN.md — Complete contract, security and integration tests
|
||
|
||
### Phase 3: First vertical slice — genres end to end
|
||
|
||
**Goal**: `GET /_fonoteka/api/v1/genres` passes the parity diff end to end (config → DB → plugin registry → JWT-guarded routing → GORM → JSON), proving every kernel layer together before any further kernel abstraction. Touches the JWT auth guard on the request path — treat as security-load-bearing and apply the security-review agent even though the guard is a throwaway seeded-token implementation at this stage.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 1, Phase 2
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Requirements**: DATA-01, DATA-02, HTTP-01, HTTP-02, QA-04
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. GORM connects to Postgres over one shared `*sql.DB` (pgx stdlib); a gormigrate migration set runs up and down for a single plugin with per-plugin version tracking, and `AutoMigrate` is not the schema source.
|
||
2. A plugin registers a route group on `net/http` ServeMux with typed params, and a request is served through the full middleware pipeline (recover, CORS, locale, auth group, must-change-password, org context, rate limit, handler).
|
||
3. A JWT-guarded `GET /_fonoteka/api/v1/genres` route returns a real `Genre` row read from Postgres.
|
||
4. The response is diffed byte-for-byte against a fixture recorded from the live PHP backend using the Phase 2 harness, and the diff is green.
|
||
|
||
**Plans**: 4 plans
|
||
|
||
Plans:
|
||
|
||
**Wave 1**
|
||
|
||
- [x] 03-01-PLAN.md — Boot the Postgres-backed JWT genre route through both plugins
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
|
||
- [x] 03-02-PLAN.md — Port active-collection counts, validation and Polish ordering
|
||
|
||
**Wave 3** *(blocked on Wave 2 completion)*
|
||
|
||
- [x] 03-03-PLAN.md — Add typed params, isolated rollback and the first real parity pass
|
||
|
||
**Wave 4** *(blocked on Wave 3 completion)*
|
||
|
||
- [x] 03-04-PLAN.md — Complete unit, integration and security verification
|
||
|
||
### Phase 4: CLI scaffolding, i18n and mail
|
||
|
||
**Goal**: Scaffolding commands generate compiling stubs for every plugin artifact type, and plugins can register translated, CLDR-pluralized, namespaced strings and mail templates rendered through a pluggable driver interface.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 1
|
||
**Repos:** summercms.go
|
||
**Requirements**: CLI-02, I18N-01, I18N-03
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. `summer make:plugin`, `make:model`, `make:migration`, `make:command`, `make:job` and `make:admin-controller` each generate stubs that compile and pass `go vet`.
|
||
2. A translation key `vendor.plugin::group.key` resolves for pl and en, including a CLDR plural form, loaded from per-plugin per-locale YAML files with parameter substitution.
|
||
3. A plugin registers a mail template and layout by dotted name with the per-locale suffix convention, and it renders via `html/template` through a driver interface (SMTP via go-mail) in a test send.
|
||
|
||
**Plans**: 4 plans
|
||
|
||
Plans:
|
||
|
||
**Wave 1**
|
||
|
||
- [x] 04-01-PLAN.md — Generate compiling plugin artifacts, registry and model import checks
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
|
||
- [x] 04-02-PLAN.md — Load namespaced translations, plurals, parameters and locale fallbacks
|
||
|
||
**Wave 3** *(blocked on Wave 2 completion)*
|
||
|
||
- [x] 04-03-PLAN.md — Register, render and send plugin mail through pluggable drivers
|
||
|
||
**Wave 4** *(blocked on Wave 3 completion)*
|
||
|
||
- [x] 04-04-PLAN.md — Verify scaffolding, translation and mail with unit and SMTP integration tests
|
||
|
||
### Phase 5: Data layer full fidelity
|
||
|
||
**Goal**: All 25 Płytarium models and their squashed migration set are ported with matching relations, casts, hooks, and the fillable/hidden/encrypted-cast mass-assignment and serialization discipline. This is security-load-bearing — mass-assignment boundaries and encrypted-at-rest credential casts are named security invariants in the PHP source — apply the security-review agent and the DTO-vs-model convention from the first model onward.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 1, Phase 3
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Requirements**: DATA-03, DATA-04, DATA-05, DATA-06, DATA-07, DATA-08, DATA-09, DATA-10, DATA-11, CLI-03
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. All 25 models exist with matching table names, columns, indexes and defaults, and every Go migration runs up and down individually; the final schema matches PHP's (migrations are squashed per final-state table, not a 1:1 port of PHP's 38 files — the historical migration count is not a target). `summer migrate:rollback --plugin=fonoteka` rolls back only that plugin's last migration.
|
||
2. A many-to-many relation with pivot business columns (`album_artists.sort_order`, `CollectionEditor.role/granted_at/granted_by`) round-trips correctly for a 3+ artist album; belongsTo/hasOne/hasMany relations return ordered results.
|
||
3. The fill boundary of the PHP write services (at minimum Album, Collection and the four credential models) is fuzzed against real Postgres with random extra and server-owned keys, asserting nothing outside the allow-list is persisted (service-level, this phase); a request-DTO-level fuzz over every write endpoint is Phase 12's criterion (the HTTP layer does not exist until Phase 6/12). Serialization honors the hidden deny-list with an explicit per-call override.
|
||
4. The money cast round-trips the PHP ceiling and blank-string cases as a fixed 4-decimal JSON string (never `float64`), and an encrypted-at-rest credential column is AES-GCM encrypted at rest and hidden from serialization.
|
||
5. Paginated responses use the exact `{data, meta{current_page,last_page,per_page,total}}` envelope with no `links` key; another plugin extends a model's lifecycle through the GORM callback registry and a companion migration without editing the owning plugin's file; soft-deletable + uniquely-keyed tables pass a delete-then-recreate test.
|
||
|
||
**Plans**: 11 plans
|
||
|
||
Plans:
|
||
**Wave 1**
|
||
|
||
- [x] 05-01-PLAN.md — Verify models-leaf rule, restructure plugins into Winter layout, ship lagoon write/read-path primitives
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
|
||
- [x] 05-02-PLAN.md — Album/Collection slice: migrations, models, casts, validation engine, write services
|
||
|
||
**Wave 3** *(blocked on Wave 2 completion)*
|
||
|
||
- [x] 05-03-PLAN.md — Secrets slice: encrypted cast, key management, organisations, credentials/OAuth tables
|
||
- [x] 05-04-PLAN.md — Attachments: system_files, blob storage, thumbnails, delete lifecycle
|
||
|
||
**Wave 4** *(blocked on Wave 3 completion)*
|
||
|
||
- [x] 05-05-PLAN.md — Remaining models, D-02 schema-diff proof, test-only DATA-11 fixture plugin, CLI-03 verification
|
||
|
||
**Wave 5** *(blocked on Wave 4 completion)*
|
||
|
||
- [x] 05-06-PLAN.md — Fuzz tests, hidden-marshal coverage, attachment smoke test, security review
|
||
|
||
### Phase 6: HTTP routing, auth groups and rate limiting
|
||
|
||
**Goal**: The three mutually exclusive auth groups (JWT, personal token, public/onboarding) share handlers with correct route subsets, named rate-limit buckets are ported 1:1, and OAuth/RFC routes are structurally exempted from any house envelope or error middleware. Security-load-bearing — auth guard registry, rate limiting and the SSRF-guarded outbound fetch helper all live here; apply the security-review agent.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 3, Phase 5
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Requirements**: HTTP-03, HTTP-04, HTTP-05, HTTP-06, HTTP-07, HTTP-08, HTTP-09
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. The same handler serves both a JWT-authenticated request under `/_fonoteka/api/v1` and a personal-token request under `/api/v1/fonoteka`, with public/onboarding groups reachable without auth; unknown and malformed ids on ownership-scoped resources both return 404.
|
||
2. All five named rate-limit buckets are enforced with the documented keys and limits, including a route stacking two limiters.
|
||
3. Response conventions hold under test: empty arrays serialize as `[]`, timestamps carry `+00:00`, tri-state booleans keep `null`, conditional keys are omitted not nulled; an OAuth-group route carries no house envelope or error middleware, verified by route-registration inspection.
|
||
4. The guarded outbound fetch helper rejects a non-allow-listed host and enforces a byte cap and timeout on a user-supplied cover URL fetch (manual cover URL, Discogs cover).
|
||
5. OpenAPI is generated from swaggo/swag annotations on handlers and `openapi-typescript` produces valid TypeScript types from it; CORS and JSON body-size limits match the PHP deployment.
|
||
|
||
**Plans**: 10 plans
|
||
|
||
Plans:
|
||
**Wave 1** *(parallel)*
|
||
|
||
- [x] 06-01-PLAN.md — Auth-groups slice: router verb/factory growth, bouncer guard registry, real inv_token guard + inv.scope, genres shared under both auth groups
|
||
- [x] 06-04-PLAN.md — SSRF-guarded outbound fetch helper (framework primitive, independent of the other three plans)
|
||
|
||
**Wave 2** *(blocked on 06-01)*
|
||
|
||
- [x] 06-02-PLAN.md — Rate limiting: fixed-window Store/Limiter, trusted-proxy client IP, five fonoteka buckets, PublicShareHeaders, remaining route groups declared
|
||
|
||
**Wave 3** *(blocked on 06-02)*
|
||
|
||
- [x] 06-03-PLAN.md — Contract surface: raw-group enforcement + route table + route:list, wire response helpers, swag/openapi-typescript pipeline, path-scoped CORS, body limits (blocking human-verify checkpoint for production body-size numbers), oauth group declared raw
|
||
|
||
**Wave 4** *(blocked on 06-01..06-04)*
|
||
|
||
- [x] 06-05-PLAN.md — Full unit coverage across both repos, full route-table mutual-exclusivity test, 06-SECURITY-REVIEW.md
|
||
|
||
**Wave 5** *(gap closure; blocked on 06-05)*
|
||
|
||
- [x] 06-06-PLAN.md — Repair personal-token middleware order and prove unauthenticated request 61 is rate-limited
|
||
|
||
**Wave 6** *(gap closure; parallel, blocked on 06-06)*
|
||
|
||
- [x] 06-07-PLAN.md — Make limiter admission atomic and remove attacker-controlled Host from inline keys
|
||
- [x] 06-08-PLAN.md — Reject private IPv4 embedded in NAT64 and 6to4 dial addresses
|
||
- [x] 06-09-PLAN.md — Buffer route responses so partial-write panics yield clean raw/house 500s
|
||
- [x] 06-10-PLAN.md — Restore exact no-newline InvScope 401/403 wire bodies
|
||
|
||
**Wave 7** *(gap closure; blocked on 06-07..06-10)*
|
||
|
||
- [x] 06-11-PLAN.md — Re-run Phase 6 security gates and refresh the stale threat verdict
|
||
|
||
### Phase 7: User plugin and authentication
|
||
|
||
**Goal**: The user plugin is ported with registration, login, JWT issue/refresh, organizations, personal API tokens and the must-change-password lock. Security-load-bearing — password auth, token scope ceilings and the session lock all live here; apply the security-review agent.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 6
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Requirements**: AUTH-01, AUTH-02, AUTH-03, AUTH-04, I18N-02
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. A user can register, log in, log out, reset a forgotten password by email, verify email, and receive/refresh a JWT (golang-jwt) with the same claims and cookie behavior the Nuxt app expects.
|
||
2. Organization fields appear on the user payload through a fire-and-collect event the fonoteka plugin populates, without the user plugin importing fonoteka.
|
||
3. A personal API token is created with a read|write|ai scope ceiling, listed, and revoked; a scope-checking middleware rejects an out-of-scope request.
|
||
4. The must-change-password flag returns 423 on the authenticated surface except the locale and password-change routes, and locale resolves per request from the user's persisted `preferred_locale` with header fallback even while the lock is active.
|
||
|
||
**Plans**: 8 plans
|
||
|
||
Plans:
|
||
**Wave 1**
|
||
|
||
- [x] 07-01-PLAN.md — bouncer JWT lifecycle, password hashing, I18N-02 locale-from-principal, lagoon.Validate extensions
|
||
|
||
**Wave 2** *(blocked on 07-01)*
|
||
|
||
- [x] 07-02-PLAN.md — User/Throttle schema and the core session loop: login/logout/fetch/refresh/register
|
||
|
||
**Wave 3** *(blocked on 07-02)*
|
||
|
||
- [x] 07-03-PLAN.md — Account management: forgot/reset password, activation, update, change-password, avatar, mail
|
||
- [x] 07-04-PLAN.md — Personal API tokens (mint/list/revoke), me/locale, 423-exempt route-table proof
|
||
|
||
**Wave 4** *(blocked on 07-03, 07-04)*
|
||
|
||
- [x] 07-05-PLAN.md — Parity evidence: record and replay the 15 /_user/api/v1 routes and the nuxt-auth flow
|
||
|
||
**Wave 5** *(blocked on 07-05)*
|
||
|
||
- [x] 07-06-PLAN.md — Full unit coverage, 07-VALIDATION.md sign-off
|
||
|
||
**Wave 6** *(gap closure; blocked on 07-06)*
|
||
|
||
- [x] 07-07-PLAN.md — Re-record the logout blacklist under a persistent PHP cache, fix the already-activated HTML-500 quirk, and flip all 15 /_user/api/v1 routes plus both nuxt flows to ported
|
||
|
||
**Wave 7** *(gap closure; blocked on 07-07 UAT)*
|
||
|
||
- [x] 07-08-PLAN.md — Publish the uploads bucket on serve and Handler so assembled avatar POST is 200
|
||
|
||
### Phase 8: OAuth2.1 authorization server
|
||
|
||
**Goal**: A direct standard-library OAuth2.1-style authorization server (`wristband`) implements the RFC 8414/6749/7591/8707 contract needed by fonoteka-mcp and the ChatGPT connector unchanged. The backend preserves its exact Basic invalid-client challenge and existing personal-token 401, while fonoteka-mcp retains ownership of RFC 9728 protected-resource metadata and its rich Bearer challenge. Security-load-bearing — bearer tokens, PKCE, replay-family revocation, and constant-time secret comparison all live here; apply the security-review agent.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 6, Phase 7
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Requirements**: AUTH-05, AUTH-06, AUTH-07
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. RFC 8414 metadata is served unenveloped at its well-known path; authorize with PKCE and a consent screen works, and the token endpoint issues/refreshes authorization_code and refresh_token flows, all as unwrapped RFC 6749 bodies with the PHP cache headers (`Cache-Control: no-store`, `Pragma: no-cache`).
|
||
2. RFC 7591 dynamic client registration and RFC 8707 resource parameter tolerance both work against a real client registration call.
|
||
3. The token endpoint returns exactly `WWW-Authenticate: Basic realm="OAuth"` on `invalid_client`, the backend personal-token 401 remains unchanged with no added challenge, and fonoteka-mcp's own rich Bearer challenge plus protected-resource metadata are verified through its actual discovery flow, not just a Go unit test.
|
||
4. Connected apps can be listed and revoked; `OAuthClient`/`OAuthAuthCode`/`OAuthRefreshToken` models persist correctly; fonoteka-mcp completes its install and auth flow unchanged; client-secret comparison uses `crypto/subtle.ConstantTimeCompare`.
|
||
|
||
**Plans**: 10 plans
|
||
**Research flag:** yes
|
||
|
||
Plans:
|
||
|
||
**Wave 1**
|
||
|
||
- [x] 08-01-PLAN.md — Mount exact connector-visible metadata and establish fail-closed RED verification
|
||
|
||
**Wave 2** *(blocked on 08-01)*
|
||
|
||
- [x] 08-02-PLAN.md — Deliver persistent connector-visible DCR with corrected schema and transaction-scoped stores
|
||
|
||
**Wave 3** *(blocked on 08-02)*
|
||
|
||
- [x] 08-03-PLAN.md — Create durable PKCE-bound authorize requests on the assembled raw route surface
|
||
|
||
**Wave 4** *(blocked on 08-03)*
|
||
|
||
- [x] 08-04-PLAN.md — Implement atomic PKCE-bound authorization-code exchange
|
||
|
||
**Wave 5** *(blocked on 08-04)*
|
||
|
||
- [x] 08-05-PLAN.md — Wire JWT consent and prove the unchanged Nuxt UI contract
|
||
|
||
**Wave 6** *(blocked on 08-05)*
|
||
|
||
- [x] 08-06-PLAN.md — Rotate refresh grants, kill replayed lineages, and manage connected apps
|
||
|
||
**Wave 7** *(parallel; blocked on 08-06)*
|
||
|
||
- [x] 08-07-PLAN.md — Provision confidential clients through the exact operator command
|
||
- [x] 08-08-PLAN.md — Serve the MCP personal-token bootstrap on the existing token surface
|
||
|
||
**Wave 8** *(blocked on 08-07 and 08-08)*
|
||
|
||
- [x] 08-09-PLAN.md — Replay PHP OAuth flows and assemble the self-validating final unchanged-MCP gate
|
||
|
||
**Wave 9** *(blocked on 08-09; blocking security checkpoint)*
|
||
|
||
- [x] 08-10-PLAN.md — Close 103-method coverage, independent security review, and the final fail-closed gate
|
||
|
||
### Phase 9: Backend admin authentication and schema pipeline
|
||
|
||
**Goal**: Backend admin users with roles are separate from frontend users and gate navigation and controller access; `fields.yaml`/`columns.yaml` drive a JSON form/list schema, including a first-class relation-manager schema replacing the one `partial` field. Security-load-bearing — separate admin authentication and permissions registry live here; apply the security-review agent. This is also the least-precedented design surface in the research (one real relation-manager usage in the PHP source, no direct library equivalent).
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 5, Phase 6
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Requirements**: AUTH-08, ADMIN-01, ADMIN-02, ADMIN-03, ADMIN-04, ADMIN-05
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. A backend admin user with a role logs in separately from frontend users, and navigation/controller access is gated by the permissions registry.
|
||
2. `fields.yaml` for a real controller parses (goccy/go-yaml) into a JSON form schema covering text, textarea, checkbox, switch, dropdown (model-method options) and relation (nameFrom, emptyOption), with span/tabs/context/attributes layout hints.
|
||
3. `columns.yaml` parses into a JSON list schema with searchable/sortable/relation columns and datetime/switch renderers.
|
||
4. The relation-manager schema supports search/link/unlink/manage-or-view lists for Collections' editors tab, replacing the `partial` field entirely.
|
||
5. Admin CRUD endpoints expose `listExtendQuery`/`formExtendQuery`/`formBeforeCreate`/`formBeforeUpdate`/`relationExtendManageQuery` hooks, bulk delete runs each record's lifecycle hooks, and the Settings model binds to a settings screen through the same schema pipeline.
|
||
|
||
**Plans**: 12/12 plans executed
|
||
**Research flag:** yes
|
||
|
||
Plans:
|
||
|
||
**Wave 1**
|
||
- [x] 09-01-PLAN.md — Prove the architecture with one production end-to-end Genre list
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
- [x] 09-02-PLAN.md — Complete backend identity lifecycle and operator provisioning
|
||
|
||
**Wave 3** *(blocked on Wave 2 completion)*
|
||
- [x] 09-03-PLAN.md — Compile the typed form-schema contract and Winter scaffolding
|
||
|
||
**Wave 4** *(blocked on Wave 3 completion)*
|
||
- [x] 09-04-PLAN.md — Compile the list contract and the allowlisted query engine
|
||
|
||
**Wave 5** *(blocked on Wave 4 completion)*
|
||
- [x] 09-05-PLAN.md — Deliver schema-projected CRUD and transactional bulk deletion
|
||
|
||
**Wave 6** *(blocked on Wave 5 completion)*
|
||
- [x] 09-06-PLAN.md — Port the Albums admin surface and the collection boundary
|
||
|
||
**Wave 7** *(blocked on Wave 6 completion)*
|
||
- [x] 09-07-PLAN.md — Port the Artists backend controller
|
||
- [x] 09-08-PLAN.md — Complete the Genres controller with form/list parity
|
||
- [x] 09-09-PLAN.md — Port the Styles controller and typed provider/filter behavior
|
||
- [x] 09-10-PLAN.md — Deliver Collections and the typed relation manager
|
||
|
||
**Wave 8** *(blocked on Wave 7 completion)*
|
||
- [x] 09-11-PLAN.md — Complete permissions, navigation, and singleton settings
|
||
|
||
**Wave 9** *(blocked on Wave 8 completion)*
|
||
- [x] 09-12-PLAN.md — Close with security, PostgreSQL, OpenAPI, and acceptance gates
|
||
|
||
### Phase 10: Admin Vue SPA
|
||
|
||
**Goal**: A minimal Vue 3 + TypeScript admin SPA renders login, permission-gated navigation, lists, forms and the relation manager for Albums, Artists, Collections, Genres and Styles, typed from the generated OpenAPI document.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 9
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Requirements**: ADMIN-06
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. An admin logs in through the SPA and sees only the navigation items their permissions allow.
|
||
2. Each of the five controllers (Albums, Artists, Collections, Genres, Styles) renders a working list and form generated from its JSON schema.
|
||
3. The Collections form's relation manager lets an admin search, link and unlink an editor.
|
||
4. API calls in the SPA use TypeScript types generated from the OpenAPI document, with no hand-maintained duplicate type.
|
||
|
||
**Plans**: 5/5 plans complete
|
||
**UI hint**: yes
|
||
|
||
Plans:
|
||
|
||
**Wave 1**
|
||
- [x] 10-01-PLAN.md — Tracer: backend.uri prefix, cookie+CSRF transport, embedded SPA (login, navigation, typed Genres list), framework admin OpenAPI pipeline, fonoteka lang/icons/Collections nav
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
- [x] 10-02-PLAN.md — Backend contract: relation options and saves with labels, backend strings bundle and overrides, messages, declarative toolbar, filter options, fully typed OpenAPI with conformance
|
||
|
||
**Wave 3** *(blocked on Wave 2 completion)*
|
||
- [x] 10-03-PLAN.md — SPA lists, forms, filter bar and settings screen for all five controllers
|
||
|
||
**Wave 4** *(blocked on Wave 3 completion)*
|
||
- [x] 10-04-PLAN.md — Relation manager with picker modal, and shell polish (flyout, collapse, user menu, breadcrumbs, dark mode)
|
||
|
||
**Wave 5** *(blocked on Wave 4 completion)*
|
||
- [x] 10-05-PLAN.md — Unit tests last: Vitest and Go coverage, assembled acceptance, check-phase10.sh gate and security evidence
|
||
|
||
### Phase 10.2: Nest framework packages under modules and write run docs (INSERTED)
|
||
|
||
**Goal:** The 18 beach-named framework packages live under `modules/<name>/` with the same names and a single root `go.mod`; importers in summercms.go, examples, and fonoteka.go use `git.golem15.com/golem15/summercms/modules/<name>`; each module has a short README; the root README is honest run/onboarding docs.
|
||
**Requirements**: TBD
|
||
**Depends on:** Phase 10
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Plans:** 2/2 plans complete
|
||
|
||
Plans:
|
||
|
||
**Wave 1**
|
||
- [x] 10.2-01-PLAN.md — Nest the 18 beach packages under modules/ and rewrite every importer
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
- [x] 10.2-02-PLAN.md — Per-module READMEs, root run docs, and the hygiene/unit-test gate
|
||
|
||
### Phase 10.1: Runtime admin extension point (INSERTED)
|
||
|
||
**Goal:** A plugin extends the compiled admin SPA without a Node rebuild. Controllers declare their own JS/CSS, served same-origin from the plugin's embedded files; `type: widget` fields mount plugin custom elements whose actions the SPA posts; `type: partial` form fields and a list `headerPartial` render server-side through `html/template` and reach the page without any raw-HTML sink; and controllers register named toolbar actions. The framework contract is proven on a nameless fixture plugin, and the application proof is three Albums surfaces: a statistics strip above the list, a Discogs lookup widget on the form, and a Discogs sync toolbar action, both Discogs actions as stubs that Phase 14 replaces.
|
||
**Requirements**: ADMIN-07
|
||
**Depends on:** Phase 10
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. A controller's declared JS/CSS is served from its plugin's embedded files under `{backend.uri}/assets/{vendor}/{plugin}/…` through an exact allowlist, loads only when that controller's list or form opens, and runs under CSP `script-src 'self'`; undeclared files and traversal attempts never leave the plugin tree.
|
||
2. A `type: widget` field mounts the plugin's custom element; its event makes the SPA POST the declared action with the admin cookie and CSRF header, and only the field's declared `fill` keys are patched onto the unsaved form.
|
||
3. A `config_list.yaml` `headerPartial` and a `type: partial` form field render server-side with `html/template` from a controller-supplied view model and reach the DOM only as an allowlisted node tree.
|
||
4. A controller registers named toolbar actions listed in `toolbar.buttons`; a click POSTs the action and toasts while `create` and `delete` keep their behaviour; unknown YAML keys, missing templates, unregistered actions and unknown permissions fail boot.
|
||
5. The Albums list shows a statistics strip scoped to the admin's collection, the Albums form shows a "Load from Discogs" widget whose stub fills Release year and Format, and a "Sync with Discogs" toolbar action toasts from its stub.
|
||
|
||
**Plans:** 4/4 plans executed
|
||
|
||
Plans:
|
||
|
||
**Wave 1**
|
||
- [x] 10.1-01-PLAN.md — Framework Go: pact capability interfaces, cabana widget/partial/toolbar/asset boot rules, cabana-owned action, partial and asset routes, sanitizer, OpenAPI (summercms.go)
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
- [x] 10.1-02-PLAN.md — Framework SPA: plugin asset loader, WidgetField, PartialHost and PartialField, list-header slot, custom toolbar buttons, rebuilt dist (summercms.go/admin)
|
||
|
||
**Wave 3** *(blocked on Wave 2 completion)*
|
||
- [x] 10.1-03-PLAN.md — Application: Albums stats strip, Discogs lookup widget stub, discogsSync toolbar stub, plugin assets and copy (fonoteka.go)
|
||
|
||
**Wave 4** *(blocked on Wave 3 completion)*
|
||
- [x] 10.1-04-PLAN.md — Unit tests last: Go and Vitest coverage, Albums acceptance, check-phase10.1.sh gate, security review and validation evidence (both repos)
|
||
|
||
### Phase 11: Jobs, realtime and search infrastructure
|
||
|
||
**Goal**: River jobs run on the correct dual-driver split, Centrifugo publishing and channel authorization match the existing server, and Typesense sync stays a re-gated pre-filter — all brought up before the API phases that depend on them (album search needs Typesense sync, CSV import needs River jobs, notifications need the realtime publisher). River's dual-driver split and the Centrifugo/Typesense contracts are the least-implemented-and-verified parts of this research pass.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 5, Phase 7
|
||
**Repos:** summercms.go, fonoteka.go
|
||
**Requirements**: JOBS-01, CLI-04, CLI-06, RT-01, RT-02, RT-03, SRCH-01
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. River runs on the shared `*sql.DB` (`riverdatabasesql`, transactional enqueue) with LISTEN/NOTIFY wake-ups on a small separate pgx pool (`riverdatabasesql.NewWithPgxListener`, one River client), verified by a timed test that job pickup is not poll-interval latency; job outcomes (complete, fail, skip) are queryable through a job manager service.
|
||
2. The queue worker runs via `summer queue:work` and the scheduler runs recurring commands via `summer schedule:run`.
|
||
3. The websockets plugin issues connection and subscription JWTs and publishes to the existing Centrifugo server with the same secret, claims and channel names, served at `GET /api/realtime/token`.
|
||
4. A channel-namespace authorizer registry re-validates on every subscribe; a broadcastable model interface with bulk-write suppression emits exactly one summary event for a bulk operation.
|
||
5. Typesense sync is scoped by `collection_id` behind a settings kill-switch and degrades gracefully without DB/config.
|
||
|
||
**Plans:** 4/7 plans executed
|
||
**Research flag:** yes
|
||
|
||
Plans:
|
||
|
||
**Wave 1**
|
||
- [x] 11-01-PLAN.md — conga core: River v0.47.0 on NewWithPgxListener, summer_jobs + River v7 migrations, job manager, conga.Job[T], worker in serve, queue:work/queue:clear, lagoon OnDatabase and Transaction/AfterCommit seams, fonoteka allow-lists (summercms.go, fonoteka.go)
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
- [x] 11-02-PLAN.md — Scheduler: pact.HasSchedule, Daily/Every, River periodic jobs, bonfire.Call, schedule:run (+ --once), fonoteka prune-notifications entry
|
||
- [x] 11-03-PLAN.md — Realtime: lighthouse + Centrifugo driver (token route, subscribe proxy, HTTP client, broadcasts with suppression), fonoteka authorizers, Mount, ws-api bucket, Album binding
|
||
|
||
**Wave 3** *(blocked on Wave 2 completion)*
|
||
- [x] 11-05-PLAN.md — Search: beachcomber + Typesense engine, after-commit sync, fonoteka Album Searchable and settings kill-switch
|
||
- [ ] 11-06-PLAN.md — tide Centrifugo recorder, PHP broadcast goldens (deleted + bulk proven; created/updated pending Phase 12), realtime routes recorded and replayed
|
||
|
||
**Wave 4** *(blocked on Wave 3 completion)*
|
||
- [ ] 11-04-PLAN.md — Web Push (flare VAPID driver, RFC 8291/8292) and websockets:health, websockets:generate-vapid-keys, websockets:test-push
|
||
|
||
**Wave 5** *(blocked on Wave 4 completion)*
|
||
- [ ] 11-07-PLAN.md — Unit tests last: full coverage, failing-when-broken T-11 evidence, check-phase11.sh gate, security review and validation map
|
||
|
||
### Phase 11.1: SummerCMS documentation for humans and AI agents (INSERTED)
|
||
|
||
**Goal:** SummerCMS has a WinterCMS-style documentation set that serves both humans and AI agents. The Markdown source lives in `summercms.go/docs/` and a `summer` CLI command builds it into a static site with sidebar navigation, search, `llms.txt`/`llms-full.txt` and a raw `.md` per page. Every code example compiles and is tested, and a "Coming from WinterCMS" map plus an `acme/blog` porting walkthrough cover the migration path. It documents the framework as it stands after Phase 11 and never names a consuming application.
|
||
**Requirements**: TBD
|
||
**Depends on:** Phase 11
|
||
**Repos:** summercms.go
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. `docs/` holds Markdown pages with frontmatter, grouped into Winter-mirroring sections (Setup, Architecture, Plugins, Backend, Database, Services, Console, API reference), and every framework module is reachable from the sidebar.
|
||
2. A `summer` CLI command builds a self-contained static site with sidebar, on-page TOC, prev/next, client-side search and dark mode, using no Node toolchain. The only new dependency is goldmark unless research names another and it is approved.
|
||
3. The build emits `llms.txt`, `llms-full.txt` and a clean `.md` for every page, and a test asserts all three stay in sync with the page tree.
|
||
4. Every Go example in the docs is compiled and run by `go test ./...`. An identifier checker and an internal link/anchor checker also run there and fail on stale names or broken links.
|
||
5. A "Coming from WinterCMS" concept map and an `acme/blog` porting walkthrough exist, and the walkthrough's code is verified under criterion 4.
|
||
|
||
**Plans:** 0 plans
|
||
|
||
Plans:
|
||
- [ ] TBD (run /gsd-plan-phase 11.1 to break down)
|
||
|
||
### Phase 11.2: Ready to share: summercms.io website and newsletter plugin (INSERTED)
|
||
|
||
**Goal:** summercms.io is ready to share publicly. A fresh Nuxt 4 website in English and Polish runs on a SummerCMS binary. Visitors subscribe for updates through the initial Go version of the Golem15 Newsletter plugin, which confirms each email by double opt-in. The Phase 11.1 docs are served at `/docs` and linked from the site.
|
||
**Requirements**: TBD
|
||
**Depends on:** Phase 11.1
|
||
**Repos:** four new repos under `git.golem15.com/golem15/`: (1) `sm-summercms-app`, the root app that builds the binary and wires the plugins; (2) `vue-summercms-app`, the Nuxt 4 site, held inside the root app; (3) `sm-summercms-plugin`, the site plugin that serves the embedded site at `/`, the docs at `/docs` and any site-specific data, the WinterCMS site-plugin pattern; (4) `sm-newsletter-plugin`, the reusable signup plugin. `summercms.go` changes only if the site needs a framework feature that is missing.
|
||
**Naming convention:** the same as OctoberCMS (`oc-`) and WinterCMS (`wn-`), with the `sm-` prefix: root app `sm-<name>-app`, Vue frontend `vue-<name>-app`, plugin `sm-<name>-plugin`, theme `sm-<name>-theme`. The Go package inside a plugin keeps its plain name (`newsletter`). The meta root `summercms.io` and the framework `summercms.go` keep their names. `fonoteka.go` becomes `sm-fonoteka-app`.
|
||
**Port source:** Golem15.Newsletter, `github.com/golem15com/wn-newsletter-plugin`, checked out at `/media/nvme/dev/golem15/horoskopia.eu/plugins/golem15/newsletter`. The PHP plugin is work in progress. Its audience comes from registered users (it requires `Golem15.User`), and its only public routes are unsubscribe. It has no public subscriber model or signup widget, so this phase adds those in Go. The PHP original is not changed.
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. The newsletter plugin repo exists and ships an initial stub: a public signup endpoint (validated, honeypot and rate-limited, with required consent), double opt-in confirmation mail with a tokenized confirm link, a welcome mail, unsubscribe, and subscriber add, edit and delete in the admin SPA. Composing and sending newsletters is out of scope.
|
||
2. The website repo exists and ships a prerendered English and Polish summercms.io landing page with a signup widget. The widget calls the plugin's API on the same origin and covers the pending, confirmed and error states.
|
||
3. The Phase 11.1 docs are served at `summercms.io/docs`, the landing page links to them, and every link resolves.
|
||
4. The site deploys as one binary with the Nuxt build and the docs embedded, with documented build and run steps.
|
||
|
||
**Context:** `.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-CONTEXT.md`
|
||
|
||
**Plans:** 0 plans
|
||
|
||
Plans:
|
||
- [ ] TBD (run /gsd-plan-phase 11.2 to break down)
|
||
|
||
### Phase 12: Płytarium API — Collections and Albums
|
||
|
||
**Goal**: Collections and Albums endpoints are ported with byte-compatible request/response shapes, including active-context switching, editor invitations, ratings, reservations, cover handling and search.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 5, Phase 6, Phase 7, Phase 11
|
||
**Repos:** fonoteka.go
|
||
**Requirements**: API-01, API-02
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. Collections CRUD, the `me/context` active-collection switch (returning an opaque channel name), editor invitation/acceptance, share-link regeneration and public token views all pass the parity diff.
|
||
2. Albums CRUD, ratings, reservations, photo upload, manual cover URL and Discogs cover price all pass the parity diff.
|
||
3. Album search treats Typesense results as a pre-filter re-gated in SQL, verified by a security test that a stale/mis-scoped search document cannot leak an unauthorized result.
|
||
4. Artists/genres/styles lookup endpoints used by the Albums UI pass the parity diff.
|
||
5. A request-DTO-level fuzz over every write endpoint asserts unknown and server-owned keys are never persisted (inherits the HTTP half of Phase 5 criterion 3; the HTTP layer does not exist until Phase 6/12).
|
||
|
||
**Plans**: TBD
|
||
|
||
### Phase 13: Płytarium API — wishlist, notifications, CSV, credentials, public routes
|
||
|
||
**Goal**: The remaining core API surface — wishlist, notifications, CSV import/export, per-user/org credentials, and onboarding/public/invitation routes — is ported with byte-compatible shapes and their own public rate-limit buckets.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 11, Phase 12
|
||
**Repos:** fonoteka.go
|
||
**Requirements**: API-03, API-04, API-05, API-06, API-07
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. Wishlist items, subscriptions, `public-wishlist/{token}` views and purchase/digest triggers pass the parity diff.
|
||
2. Notifications list/mark-read/prune endpoints pass the parity diff (the realtime token endpoint itself is owned by the websockets plugin, ported in Phase 11).
|
||
3. CSV import runs as a multi-step session (store, show/poll, mapping patch, per-row edit, commit, cancel) and CSV export works on both authenticated groups, all passing the parity diff.
|
||
4. Per-user and per-org Discogs/AI credentials CRUD store encrypted values, honor the org-lock flag, and resolve env-to-org-to-user correctly.
|
||
5. Onboarding, public and invitation-inspection routes are reachable without auth and enforce their own public rate-limit buckets.
|
||
|
||
**Plans**: TBD
|
||
|
||
### Phase 14: Domain jobs and external integrations
|
||
|
||
**Goal**: The domain-specific River jobs (CSV import write, Discogs match, wishlist digest), the reindex command, the Discogs client and AI cover recognition are ported on top of the Phase 11 jobs/realtime/search infrastructure and the Phase 13 API surface they serve.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 11, Phase 13
|
||
**Repos:** fonoteka.go (plus summercms.go only if a framework helper is needed)
|
||
**Requirements**: JOBS-02, JOBS-03, SRCH-02, INTG-01, INTG-02, API-08, CLI-05
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. The CSV import write job and the self-redispatching Discogs match job (240s timeout, self-redispatching with a delay on a Discogs rate-limit error) both complete correctly.
|
||
2. The wishlist digest job coalesces a 30-minute window and deletes its queue row on completion.
|
||
3. The `reindex` command asserts zero `collection_id`-0 documents before and after and can drop the legacy index.
|
||
4. The Discogs client enforces its proactive rate threshold, bounded wait budget, retry-after fallback and host-locked cover fetch.
|
||
5. AI cover recognition works through both Anthropic and OpenAI-compatible adapters with per-credential model/base-URL overrides.
|
||
6. Feedback submissions and sitemap output work; the ported `oauth-client`/`prune-notifications`/`reindex` commands all run correctly.
|
||
|
||
**Plans**: TBD
|
||
|
||
### Phase 15: Cutover
|
||
|
||
**Goal**: The parity harness is green on all 154 routes and `vue-fonoteka-app` and `fonoteka-mcp` run unchanged against the Go backend in daily use — the project's definition of done.
|
||
**Mode:** mvp
|
||
**Depends on**: Phase 2, Phase 8, Phase 9, Phase 10, Phase 12, Phase 13, Phase 14
|
||
**Repos:** fonoteka.go, summercms.go
|
||
**Requirements**: API-09, QA-05
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. All 154 routes are registered on the correct auth groups with identical paths, methods and status codes.
|
||
2. The parity harness runs green across recorded fixtures for all 154 routes.
|
||
3. `vue-fonoteka-app` runs unchanged against the Go backend for a full manual session (browse, edit, upload, invite, OAuth-connect an MCP client).
|
||
4. `fonoteka-mcp` completes its install/auth flow and a representative set of tool calls unchanged against the Go backend.
|
||
|
||
**Plans**: TBD
|
||
|
||
## Progress
|
||
|
||
**Execution Order:**
|
||
Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9 → 10 → 11 → 12 → 13 → 14 → 15
|
||
(Phase 2 depends on Phase 1's command kernel; the two are no longer parallel.)
|
||
|
||
| Phase | Plans Complete | Status | Completed |
|
||
|-------|----------------|--------|-----------|
|
||
| 1. Framework kernel foundation | 4/4 | Complete | 2026-09-16 |
|
||
| 2. API parity harness bootstrap | 5/5 | Complete | 2026-09-17 |
|
||
| 3. First vertical slice — genres end to end | 4/4 | Complete | 2026-09-17 |
|
||
| 4. CLI scaffolding, i18n and mail | 4/4 | Complete | 2026-09-18 |
|
||
| 5. Data layer full fidelity | 6/6 | Complete | 2026-09-18 |
|
||
| 6. HTTP routing, auth groups and rate limiting | 14/14 | Complete | 2026-09-21 |
|
||
| 7. User plugin and authentication | 8/8 | Complete | 2026-09-23 |
|
||
| 8. OAuth2.1 authorization server | 10/10 | Complete | 2026-09-23 |
|
||
| 9. Backend admin authentication and schema pipeline | 12/12 | In Progress| |
|
||
| 10. Admin Vue SPA | 5/5 | Complete | 2026-09-27 |
|
||
| 11. Jobs, realtime and search infrastructure | 4/7 | In Progress| |
|
||
| 11.1. SummerCMS documentation for humans and AI agents | 0/TBD | Not started | - |
|
||
| 11.2. Ready to share: summercms.io website and newsletter plugin | 0/TBD | Not started | - |
|
||
| 12. Płytarium API — Collections and Albums | 0/TBD | Not started | - |
|
||
| 13. Płytarium API — wishlist, notifications, CSV, credentials, public routes | 0/TBD | Not started | - |
|
||
| 14. Domain jobs and external integrations | 0/TBD | Not started | - |
|
||
| 15. Cutover | 0/TBD | Not started | - |
|