Files
summercms/.planning/phases/03-first-vertical-slice-genres-end-to-end/03-01-SUMMARY.md
Jakub Zych 20bd8fe8fc docs(03-01): complete boot JWT genre route plan
Tasks completed: 2/2
- Initialize the real app database through plugin migrations
- Serve the seeded genre list behind real cross-plugin JWT middleware

SUMMARY: .planning/phases/03-first-vertical-slice-genres-end-to-end/03-01-SUMMARY.md
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 20:06:03 +02:00

8.8 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
03-first-vertical-slice-genres-end-to-end 01 api
postgres
gorm
gormigrate
jwt
hs256
servemux
middleware
genres
phase provides
02-api-parity-harness-bootstrap App-owned newTarget/seedHooks seam and recorded GET genres fixture
phase provides
01-framework-kernel-foundation party.Activate, backpack Publish/Lookup, compass SUMMER_ overlay, bonfire commands
Shared pgx-stdlib *sql.DB plus GORM handle with ICU pl-PL locale check
Per-plugin gormigrate history tables and 15 canonical genre seed rows
surf Group/Get compiled onto net/http ServeMux with named middleware
bouncer HS256 verifier and jwt.auth / inv.must-change-password pipeline
JWT-protected GET /_fonoteka/api/v1/genres returning seeded data
03-02
03-03
03-04
05
06
07
11
added patterns
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/go-gormigrate/gormigrate/v2 v2.1.7
gorm.io/gorm v1.31.2
gorm.io/driver/postgres v1.6.3
github.com/jackc/pgx/v5 v5.10.0
One *sql.DB shared by GORM and app services; River listener pool is a Phase 11 seam
Optional pact capabilities discovered in Activate order
Named middleware resolved at Assemble; missing names fail boot with plugin ID and name
Generated main stays framework-generic; fonoteka.go/app.Handler is the in-process test seam
created modified
lagoon/connection.go
lagoon/migrations.go
lagoon/commands.go
surf/router.go
surf/serve.go
bouncer/jwt.go
bouncer/context.go
../fonoteka.go/app/app.go
../fonoteka.go/plugins/golem15/user/plugin.go
../fonoteka.go/plugins/golem15/fonoteka/plugin.go
../fonoteka.go/plugins/golem15/fonoteka/genre_handler.go
../fonoteka.go/parity/genre_smoke_test.go
pact/capabilities.go
internal/build/build.go
cmd/summer/main.go
go.mod
../fonoteka.go/main.go
../fonoteka.go/go.mod
GORM and app services share one pgx-stdlib *sql.DB; the River LISTEN/NOTIFY pool is a Phase 11 seam and is not created in lagoon.Open
Generated app main stays framework-generic (lagoon.RuntimeCommands + surf.ServeCommand); fonoteka.go/app.Handler is the in-process boot seam for parity tests
Empty golem15.user.jwt.secret fails Boot; tests use a fixed test-only HS256 secret and do not issue tokens through a production API
Pattern: lagoon.Open/Use then Publish(*sql.DB, *gorm.DB) before Assemble; UserProvider Lookups GORM per request
Pattern: Group(prefix, surf.Use(names...), cb) compiles to ServeMux GET patterns; CORS wraps the mux so OPTIONS bypasses jwt.auth
Pattern: dedicated GenreDTO with initialized data slice, Cache-Control no-cache, private, album_count 0 until Plan 02
DATA-01
DATA-02
HTTP-01
HTTP-02
QA-04
17 min 2026-09-17

Phase 03 Plan 01: Boot the Postgres-backed JWT genre route Summary

Shared pgx-stdlib pool, per-plugin gormigrate, HS256 jwt.auth, and GET /_fonoteka/api/v1/genres serving the 15 seeded rows

Performance

  • Duration: 17 min
  • Started: 2026-09-17T17:47:49Z
  • Completed: 2026-09-17T18:04:34Z
  • Tasks: 2
  • Files modified: 52

Accomplishments

  • Fresh ICU pl-PL Postgres migrates golem15.user then golem15.fonoteka with separate history tables and the 15 canonical genre slugs; libc/non-Polish databases fail before migrate; no AutoMigrate.
  • surf compiles Laravel-like groups onto http.ServeMux with recover → CORS → locale → named auth → must-change-password → org slot → no-op rate limit → handler.
  • bouncer verifies pinned HS256 tokens (exp + sub required) and golem15.user registers jwt.auth; golem15.fonoteka registers inv.must-change-password (423) and mounts GET /_fonoteka/api/v1/genres.
  • A valid test-only token returns 200 with a nonempty data array from Postgres; missing/malformed/expired/wrong-alg/bad-sig/missing-sub/unknown-user tokens never reach the handler.

Task Commits

Each task was committed atomically (framework summercms.go then app fonoteka.go):

  1. Task 1: Initialize the real app database through plugin migrations
    • d0d845052bb5de883646c699eeeb0d18aad23708 (feat, summercms.go)
    • 55f110e7d7968977c3abaa3d73905dc7408de30a (feat, fonoteka.go)
  2. Task 2: Serve the seeded genre list behind real cross-plugin JWT middleware
    • 4ee4c4a2fc27d644cdfac9131cb369e8617df861 (feat, summercms.go)
    • d7915a80e655329bb726c64dc2488c9a1de9a48c (feat, fonoteka.go)

Plan metadata: (this commit)

Files Created/Modified

  • lagoon/connection.go — shared pgx stdlib pool, ICU locale check, GORM on postgres.Config{Conn: sqlDB}
  • lagoon/migrations.go — per-plugin gormigrate sets and history tables
  • surf/router.go / surf/serve.go — Group/Get/Assemble and signal-aware serve
  • bouncer/jwt.go / bouncer/context.go — HS256 verifier and unexported user context key
  • pact/capabilities.go — HasMigrations, HasMiddleware, HasRoutes, HasModels
  • ../fonoteka.go/app/app.go — in-process Handler seam used by genre smoke tests
  • ../fonoteka.go/plugins/golem15/user/ — users migration, jwt.auth, UserProvider
  • ../fonoteka.go/plugins/golem15/fonoteka/ — schema/seed, 423 gate, genre DTO handler
  • ../fonoteka.go/parity/genre_smoke_test.go — valid token plus reject/423 cases; corpus stays pending

Decisions Made

  • One *sql.DB for GORM and application queries; do not create a River listener pool until Phase 11.
  • Generated main.go cannot import fonoteka/app; surf.ServeCommand and app.Handler both call surf.Assemble.
  • Empty JWT secret fails Boot (SUMMER_GOLEM15__USER__JWT__SECRET); issuing stays out of production APIs.

Deviations from Plan

Auto-fixed Issues

1. [Rule 3 - Blocking] Generated main cannot share the fonoteka/app import path

  • Found during: Task 2 (serve / parity seam)
  • Issue: Plan text says generated main and parity tests call the same importable app package, but summer build is framework-generic and must not import Fonoteka.
  • Fix: Keep generated main on lagoon.RuntimeCommands + surf.ServeCommand; put app.Handler(cfg, pluginIDs, sqlDB) in fonoteka.go/app for tests.
  • Files modified: internal/build/build.go, ../fonoteka.go/app/app.go, ../fonoteka.go/main.go
  • Verification: summer build regenerates ServeCommand; genre smoke boots app.Handler
  • Committed in: 4ee4c4a / d7915a8 (Task 2)

2. [Rule 1 - Bug] go mod tidy drops toolchain go1.27.0

  • Found during: Task 1 and Task 2
  • Issue: tidy removes the pinned toolchain line required by project convention.
  • Fix: Restore toolchain go1.27.0 after tidy in framework and plugin modules.
  • Files modified: go.mod, plugin go.mod files
  • Verification: modules still build under Go 1.27
  • Committed in: Task 1 and Task 2 commits

3. [Discretion] JWT malformed/bad-signature copy from library source

  • Found during: Task 2
  • Issue: Plan asked to capture malformed/bad-signature PHP bodies on the isolated Phase 2 server before finalizing translations.
  • Fix: Used the php-open-source-saver/jwt-auth (Namshi) messages already named in CONTEXT D-10: Wrong number of segments and Token Signature could not be verified.
  • Files modified: bouncer/jwt.go
  • Verification: genre smoke table-driven 401 bodies
  • Committed in: 4ee4c4a (Task 2)

Total deviations: 3 auto-fixed (1 blocking, 1 bug, 1 copy-source discretion) Impact on plan: No scope creep. QA-04 parity-green remains Plan 03; this plan keeps the corpus pending.

Issues Encountered

None that blocked the slice. go mod tidy dropping toolchain is now a known restore step.

User Setup Required

None - no external service configuration required beyond the existing Postgres ICU pl-PL database and SUMMER_DATABASE__DSN / SUMMER_GOLEM15__USER__JWT__SECRET env vars documented in ../fonoteka.go/README.md.

Next Phase Readiness

Ready for 03-02-PLAN.md (active-collection album counts, non_empty validation, Polish ordering). Do not mark the genres corpus route ported until Plan 03. QA-04 stays pending until that parity diff is green.

Self-Check: PASSED

  • Key files exist on disk (lagoon/connection.go, surf/router.go, bouncer/jwt.go, ../fonoteka.go/app/app.go, ../fonoteka.go/parity/genre_smoke_test.go)
  • git log --grep=03-01 returns Task 1 and Task 2 commits
  • Acceptance: 15 seeded genres, ICU locale fail, shared *sql.DB, JWT 200/401/423, missing middleware names plugin+name, framework does not import Fonoteka, serve on both binaries, go vet/go test ./... green in both repos

Phase: 03-first-vertical-slice-genres-end-to-end Completed: 2026-09-17