Merge self-service profile writes server-side (#4) - #5
Conversation
Route business.UpdateUser's profile write through a new store-level "profile_merge" path that atomically patches the JSONB map field-by-field (empty value clears the key) instead of replacing it. Self-service writes now send only the fields they change and can't clobber a concurrent writer. GDPR anonymization keeps the "profile" replace path, so scrubbing still wipes every field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The profile-merge fix edits two canonical base files (accounts users.go and postgres_users.go) and adds a base test. Regenerate base-manifest.json so base-integrity records the new canonical hashes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CI status: blocked on the platform/agent bundle (PR #3), not on this changeThe issue-#4 code fix is complete and verified — the
Root cause:
Plan: land PR #3, then rebase this PR and re-run CI. No change confined to this PR can make the gate green. |
|
Closing as superseded — issue #4 is already fixed on Commit Rebasing this branch would only conflict my implementation against the already-merged one for no behavioral gain. Nothing here is lost by closing. |
Closes #4.
Summary
profilemap, which the server replaced — so a concurrent write in that window was silently dropped (last-writer-wins).business.UpdateUser's profile write now goes through a new storeprofile_mergepath that does an atomicprofile = (COALESCE(profile,'{}'::jsonb) || $set) - $emptyKeys. The write only touches the keys it sends (empty value clears a key), so it can't clobber a concurrent writer and no client read-modify-write is needed.gdpr.go→store.UpdateUserwith theprofilekey) is left on the replace path, so a scrub still wipes every PII field — the compliance invariant the merge fix had to preserve.Notes for the reviewer
UpdateUserRPC's profile semantics from replace to merge, as the issue's design 1 calls for. The GDPR path callsstore.UpdateUserdirectly with the untouchedprofile(replace) key, so its behavior is unchanged.updateSelfProfile/applyProfilePatchclient code the issue references (module/services/frontend/code/src/features/user-profile/...) does not exist on this branch — it's part of unmerged work — so there is no client read-modify-write here to delete. Once that client lands it can send only the changed fields; the server is now ready for it.Test plan
go test ./pkg/infra/ -run TestUpdateUserProfile— new tests against real Postgres:profilereplace path still wipes the whole map (guards the GDPR scrub invariant)go build ./...,go vet ./pkg/infra/... ./pkg/business/...