diff --git a/.planning/phases/07-user-plugin-and-authentication/07-08-PLAN.md b/.planning/phases/07-user-plugin-and-authentication/07-08-PLAN.md index 6efba2e..343a6f1 100644 --- a/.planning/phases/07-user-plugin-and-authentication/07-08-PLAN.md +++ b/.planning/phases/07-user-plugin-and-authentication/07-08-PLAN.md @@ -12,6 +12,7 @@ files_modified: - ../fonoteka.go/parity/genre_security_test.go - ../fonoteka.go/plugins/golem15/user/avatar_test.go autonomous: true +gap_closure: true requirements: [] user_setup: [] diff --git a/.planning/phases/07-user-plugin-and-authentication/07-08-SUMMARY.md b/.planning/phases/07-user-plugin-and-authentication/07-08-SUMMARY.md new file mode 100644 index 0000000..2eb23a0 --- /dev/null +++ b/.planning/phases/07-user-plugin-and-authentication/07-08-SUMMARY.md @@ -0,0 +1,126 @@ +--- +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*