--- phase: 07-user-plugin-and-authentication plan: 08 subsystem: auth tags: [avatar, blob, gocloud, serve, handler] requires: - phase: 07-user-plugin-and-authentication provides: avatar HTTP handlers and attach.OpenBucket/Publish from Phase 5 - phase: 05-data-layer-full-fidelity provides: lagoon/attach OpenBucket, Publish, and mem:// support provides: - surf.ServeCommand and app.Handler publish *blob.Bucket before Assemble - assembled Handler JPEG upload/remove proof that does not hand-publish a memblob affects: [phase-7-verification, phase-12-media] tech-stack: added: [] patterns: - "OpenBucket + Publish sit next to lagoon.Publish on every HTTP boot path" - "assembled tests set storage.uploads.bucket_url=mem://; unit tests may still publish memblob by hand" key-files: created: - surf/serve_test.go - ../fonoteka.go/parity/avatar_assembled_test.go modified: - surf/serve.go - ../fonoteka.go/app/app.go - ../fonoteka.go/parity/migrate_test.go - ../fonoteka.go/parity/genre_security_test.go key-decisions: - "Both CLI serve and in-process Handler must publish *blob.Bucket; unit tests that call attach.Publish themselves cannot stand in for boot" - "Assembled Handler avatar coverage lives in parity/ next to newConfiguredTarget, not in the user plugin package" patterns-established: - "Pattern: empty storage.uploads.bucket_url fails boot as loudly as an empty JWT secret" - "Pattern: TestAvatarAssembled boots app.Handler and curls the real routes; TestUploadAvatar stays handler-unit coverage" requirements-completed: [] duration: 25min completed: 2026-09-23 --- # Phase 7 Plan 08: Avatar bucket publish Summary **Serve and Handler open `storage.uploads.bucket_url` and publish `*blob.Bucket` so assembled avatar POST is 200, not an opaque 500.** ## Performance - **Duration:** 25 min - **Started:** 2026-09-23T08:24:00Z - **Completed:** 2026-09-23T08:49:38Z - **Tasks:** 3 - **Files modified:** 6 ## Accomplishments - `surf.ServeCommand` opens the uploads bucket after `lagoon.Publish` and publishes it before `Assemble`; an empty URL fails boot. - `app.Handler` does the same so UAT and parity replay match CLI serve. - `testConfig` / `testConfigCORS` set `mem://` so assembled tests boot without a filesystem uploads dir. - `TestAvatarAssembled` registers a user, logs in, POSTs a JPEG through `app.Handler`, then POSTs remove — without calling `attach.Publish` itself. ## Task Commits 1. **Task 1: Publish the uploads bucket on serve and Handler** — `44900f0` in `summercms.go`, `bc1adf2` in `fonoteka.go` 2. **Task 2: Assembled-app avatar upload and remove** — `b91b728` in `fonoteka.go` 3. **Task 3: Race gate both modules** — verification only (no code change) **Plan metadata:** this docs commit ## Files Created/Modified - `surf/serve.go` — `publishUploads` after `lagoon.Publish`; `defer bucket.Close()` - `surf/serve_test.go` — empty URL fails; `mem://` stores a lookupable `*blob.Bucket` - `../fonoteka.go/app/app.go` — OpenBucket + Publish on the Handler boot path - `../fonoteka.go/parity/migrate_test.go` — `testConfig` sets `mem://` and `/storage/uploads` - `../fonoteka.go/parity/genre_security_test.go` — same keys on `testConfigCORS` - `../fonoteka.go/parity/avatar_assembled_test.go` — JPEG upload/remove against `app.Handler` ## Decisions Made Avatar 500s were a missing boot-path publish, not a handler bug. Phase 5 shipped `OpenBucket`/`Publish` but never wired them; Phase 7 added HTTP avatar on top. Both serve and Handler must publish, because UAT and corpus replay boot via Handler. Assembled proof belongs next to `newConfiguredTarget` so it cannot import `memblob` and call `attach.Publish`. Existing `TestUploadAvatar` / `TestRemoveAvatar` stay as handler-unit coverage. ## Deviations from Plan ### Auto-fixed Issues **1. Assembled test file location** - **Found during:** Task 2 - **Issue:** Plan `files_modified` listed `avatar_test.go`, but that package cannot reach `app.Handler` without a reverse import. - **Fix:** Added `parity/avatar_assembled_test.go` beside `newConfiguredTarget`, which the plan already allowed. - **Verification:** `go test ./parity/ -run TestAvatarAssembled` passed - **Committed in:** `b91b728` --- **Total deviations:** 1 auto-fixed (test placement) **Impact on plan:** Same proof the plan asked for, on the boot path that hid the gap. No scope creep. ## Issues Encountered None ## User Setup Required None - no external service configuration required. ## Next Phase Readiness UAT blocker closed. `go vet` and `go test -race` are green in both modules. Ready for phase verification — do not auto-advance to Phase 8. ## Self-Check: PASSED - Serve empty-URL boot fails; `mem://` publishes a bucket (`go test ./surf/ -run 'TestPublishUploads|TestServe'`) - Assembled JPEG upload 200 + remove clears `has_avatar` (`TestAvatarAssembled`) - `TestUploadAvatar`, `TestRemoveAvatar`, `TestParityCorpus`, `TestUserAPINuxtFlows` green - `go vet ./... && go test ./... -race` green in `summercms.go` and `fonoteka.go` plus nested plugin packages --- *Phase: 07-user-plugin-and-authentication* *Completed: 2026-09-23*