docs(07-08): complete the avatar bucket publish plan

Serve and Handler now publish the uploads bucket; assembled avatar
POST is 200. Record the gap-closure outcome.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-09-23 10:50:01 +02:00
parent 44900f0d16
commit b74484eabc
2 changed files with 127 additions and 0 deletions

View File

@@ -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: []

View File

@@ -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*