- Phase 5 plan 6 of 6 complete and ready for verification - Record 23 min / 3 tasks / 12 files and the fill-boundary test decisions
28 KiB
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.
- Phase 1: Framework kernel foundation - Config, plugin registry, event bus, container and CLI skeleton boot the
summerbinary (completed 2026-09-16) - 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)
- Phase 3: First vertical slice — genres end to end -
GET /_fonoteka/api/v1/genrespasses the parity diff through every layer (completed 2026-09-17) - Phase 4: CLI scaffolding, i18n and mail - Scaffolding commands, translated/pluralized strings, mail templates (completed 2026-09-18)
- Phase 5: Data layer full fidelity - All 25 models and their squashed migrations with fillable/hidden/cast/soft-delete discipline (completed 2026-09-18)
- Phase 6: HTTP routing, auth groups and rate limiting - Three auth groups, named rate buckets, OAuth-safe middleware structure
- Phase 7: User plugin and authentication - Registration, login, JWT, organizations, personal tokens, must-change-password
- Phase 8: OAuth2.1 authorization server - zitadel/oidc server for fonoteka-mcp and the ChatGPT connector
- Phase 9: Backend admin authentication and schema pipeline - Admin roles, fields.yaml/columns.yaml, relation manager
- Phase 10: Admin Vue SPA - Login, navigation, lists, forms and relation manager for five controllers
- 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):
summer buildregenerates 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.- 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. - 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. - 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. - The
summerCLI 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
- 01-01-PLAN.md — Build and boot the separate hello app through the shared command kernel
Wave 2 (blocked on Wave 1 completion)
- 01-02-PLAN.md — Add layered config, optional plugin composition, services and typed events
Wave 3 (blocked on Wave 2 completion)
- 01-03-PLAN.md — Add plugin scaffolding, rich output and the watch rebuild loop
Wave 4 (blocked on Wave 3 completion)
- 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):
- 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.
- The replay-and-diff harness runs a recorded fixture against an arbitrary
httptest.Serverbackend and reports a byte-level diff. - 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. go vetandgo test ./...are green, and the harness's own integration tests run against testcontainers Postgres.
Plans: 5 plans
Plans: Wave 1
- 02-01-PLAN.md — Record and replay one fixture through the
summerCLI
Wave 2 (blocked on Wave 1 completion)
- 02-02-PLAN.md — Capture safe proxy sessions and manifest routes with strict diffs
Wave 3 (blocked on Wave 2 completion)
- 02-03-PLAN.md — Record all 154 PHP routes and real Nuxt/MCP flows
Wave 4 (blocked on Wave 3 completion)
- 02-04-PLAN.md — Wire honest Go replay with testcontainers Postgres
Wave 5 (blocked on Waves 1–4 completion)
- 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):
- 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, andAutoMigrateis not the schema source. - A plugin registers a route group on
net/httpServeMux 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). - A JWT-guarded
GET /_fonoteka/api/v1/genresroute returns a realGenrerow read from Postgres. - 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
- 03-01-PLAN.md — Boot the Postgres-backed JWT genre route through both plugins
Wave 2 (blocked on Wave 1 completion)
- 03-02-PLAN.md — Port active-collection counts, validation and Polish ordering
Wave 3 (blocked on Wave 2 completion)
- 03-03-PLAN.md — Add typed params, isolated rollback and the first real parity pass
Wave 4 (blocked on Wave 3 completion)
- 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):
summer make:plugin,make:model,make:migration,make:command,make:jobandmake:admin-controllereach generate stubs that compile and passgo vet.- A translation key
vendor.plugin::group.keyresolves for pl and en, including a CLDR plural form, loaded from per-plugin per-locale YAML files with parameter substitution. - A plugin registers a mail template and layout by dotted name with the per-locale suffix convention, and it renders via
html/templatethrough a driver interface (SMTP via go-mail) in a test send.
Plans: 4 plans
Plans:
Wave 1
- 04-01-PLAN.md — Generate compiling plugin artifacts, registry and model import checks
Wave 2 (blocked on Wave 1 completion)
- 04-02-PLAN.md — Load namespaced translations, plurals, parameters and locale fallbacks
Wave 3 (blocked on Wave 2 completion)
- 04-03-PLAN.md — Register, render and send plugin mail through pluggable drivers
Wave 4 (blocked on Wave 3 completion)
- 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):
- 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=fonotekarolls back only that plugin's last migration. - 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. - 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.
- 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. - Paginated responses use the exact
{data, meta{current_page,last_page,per_page,total}}envelope with nolinkskey; 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: 6 plans
Plans: Wave 1
- 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)
- 05-02-PLAN.md — Album/Collection slice: migrations, models, casts, validation engine, write services
Wave 3 (blocked on Wave 2 completion)
- 05-03-PLAN.md — Secrets slice: encrypted cast, key management, organisations, credentials/OAuth tables
- 05-04-PLAN.md — Attachments: system_files, blob storage, thumbnails, delete lifecycle
Wave 4 (blocked on Wave 3 completion)
- 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)
- 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):
- The same handler serves both a JWT-authenticated request under
/_fonoteka/api/v1and 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. - All seven named rate-limit buckets are enforced with the documented keys and limits, including a route stacking two limiters.
- Response conventions hold under test: empty arrays serialize as
[], timestamps carry+00:00, tri-state booleans keepnull, conditional keys are omitted not nulled; an OAuth-group route carries no house envelope or error middleware, verified by route-registration inspection. - 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).
- OpenAPI is generated from swaggo/swag annotations on handlers and
openapi-typescriptproduces valid TypeScript types from it; CORS and JSON body-size limits match the PHP deployment.
Plans: TBD
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):
- 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.
- Organization fields appear on the user payload through a fire-and-collect event the fonoteka plugin populates, without the user plugin importing fonoteka.
- 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.
- 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_localewith header fallback even while the lock is active.
Plans: TBD
Phase 8: OAuth2.1 authorization server
Goal: An RFC 8414/6749/7591-compliant OAuth2.1 server on zitadel/oidc serves fonoteka-mcp and the ChatGPT connector unchanged, including exact WWW-Authenticate and protected-resource-metadata headers. Security-load-bearing — bearer tokens, PKCE 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):
- 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). - RFC 7591 dynamic client registration and RFC 8707 resource parameter tolerance both work against a real client registration call.
- A 401 on a protected route carries the exact
WWW-Authenticateand protected-resource-metadata header contract, verified against fonoteka-mcp's actual discovery flow, not just a Go unit test. - Connected apps can be listed and revoked;
OAuthClient/OAuthAuthCode/OAuthRefreshTokenmodels persist correctly; fonoteka-mcp completes its install and auth flow unchanged; client-secret comparison usescrypto/subtle.ConstantTimeCompare.
Plans: TBD Research flag: yes
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):
- A backend admin user with a role logs in separately from frontend users, and navigation/controller access is gated by the permissions registry.
fields.yamlfor 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.columns.yamlparses into a JSON list schema with searchable/sortable/relation columns and datetime/switch renderers.- The relation-manager schema supports search/link/unlink/manage-or-view lists for Collections' editors tab, replacing the
partialfield entirely. - Admin CRUD endpoints expose
listExtendQuery/formExtendQuery/formBeforeCreate/formBeforeUpdate/relationExtendManageQueryhooks, bulk delete runs each record's lifecycle hooks, and the Settings model binds to a settings screen through the same schema pipeline.
Plans: TBD Research flag: yes
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):
- An admin logs in through the SPA and sees only the navigation items their permissions allow.
- Each of the five controllers (Albums, Artists, Collections, Genres, Styles) renders a working list and form generated from its JSON schema.
- The Collections form's relation manager lets an admin search, link and unlink an editor.
- API calls in the SPA use TypeScript types generated from the OpenAPI document, with no hand-maintained duplicate type.
Plans: TBD UI hint: yes
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):
- River runs on the shared
*sql.DB(riverdatabasesql, transactional enqueue) with a separateriverpgxv5client for LISTEN/NOTIFY dispatch, 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. - The queue worker runs via
summer queue:workand the scheduler runs recurring commands viasummer schedule:run. - 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. - 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.
- Typesense sync is scoped by
collection_idbehind a settings kill-switch and degrades gracefully without DB/config.
Plans: TBD Research flag: yes
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):
- Collections CRUD, the
me/contextactive-collection switch (returning an opaque channel name), editor invitation/acceptance, share-link regeneration and public token views all pass the parity diff. - Albums CRUD, ratings, reservations, photo upload, manual cover URL and Discogs cover price all pass the parity diff.
- 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.
- Artists/genres/styles lookup endpoints used by the Albums UI pass the parity diff.
- 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):
- Wishlist items, subscriptions,
public-wishlist/{token}views and purchase/digest triggers pass the parity diff. - 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).
- 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.
- 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.
- 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):
- 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.
- The wishlist digest job coalesces a 30-minute window and deletes its queue row on completion.
- The
reindexcommand asserts zerocollection_id-0 documents before and after and can drop the legacy index. - The Discogs client enforces its proactive rate threshold, bounded wait budget, retry-after fallback and host-locked cover fetch.
- AI cover recognition works through both Anthropic and OpenAI-compatible adapters with per-credential model/base-URL overrides.
- Feedback submissions and sitemap output work; the ported
oauth-client/prune-notifications/reindexcommands 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):
- All 154 routes are registered on the correct auth groups with identical paths, methods and status codes.
- The parity harness runs green across recorded fixtures for all 154 routes.
vue-fonoteka-appruns unchanged against the Go backend for a full manual session (browse, edit, upload, invite, OAuth-connect an MCP client).fonoteka-mcpcompletes 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 | 0/TBD | Not started | - |
| 7. User plugin and authentication | 0/TBD | Not started | - |
| 8. OAuth2.1 authorization server | 0/TBD | Not started | - |
| 9. Backend admin authentication and schema pipeline | 0/TBD | Not started | - |
| 10. Admin Vue SPA | 0/TBD | Not started | - |
| 11. Jobs, realtime and search infrastructure | 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 | - |