Claude: changes for issue #160 #161
No reviewers
Labels
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Grandiras/Ponente!161
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "claude/issue-160"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #160.
A profile change on /profile went to the member's app.bsky.actor.profile record, which is the point of the Atmosphere — and was also the page's only offer. A name and a picture chosen for the people on one private board are not always the ones somebody wants under a post on Bluesky, so the page now asks which of the two a save is about, per save, with the network one still the default. The Ponente-only target writes the Members row and stops: no token, no scope, no record, no account touched. It therefore also works on a deployment configured AtProto:ProfileScope=none and for a member who signed in before this app started asking — which is why the "sign in again" sentence now sits under a disabled option rather than in place of the whole form. It is the same two columns rather than a second pair, so every screen that draws a face keeps reading what it always read; Member.UsesLocalProfile says whose answer they are, and is what MemberService.RefreshProfileAsync, the startup backfill and /dev/login now check before overwriting them. The picture has no repository to live in, so it goes into a new MemberAvatar table (encrypted like everything else, no navigation property so it can never ride along with a member read) and is served by /avatar/{did} behind the allowlist, at a URL carrying a version that changes with the picture. The way back is the refresh button, which for a member holding a local profile reads "use my Atmosphere profile" instead — for them the two are the same act. Also generalises the board-access option cards to .choice-list/.choice-option, since the profile page is now the second thing that needs that shape. profile.spec.js reaches a save end to end for the first time: the local target needs no token, so the dev sign-in can set a name and a picture, watch the nav header take them, fetch the picture back off /avatar/{did}, and check both survive the next sign-in.🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.
📜 Live transcript — updating every ~5 minutes while the task runs.
✅ Verdict: approve — no blocking issues found.
This is a large, carefully constructed feature (local/Ponente-only profiles alongside the existing Atmosphere-profile editing) and it holds together well under close reading.
What I verified:
ProfileService.LoadAsynccorrectly short-circuits to the local profile before creating an AT Proto client at all whenUsesLocalProfileis set, matching the docs' claim that the repository is never read in that case.RefreshProfileAsyncguard (if (member.UsesLocalProfile) return;) and the parallel guard inline inRefreshCachedCopyAsync/SaveAsynccorrectly prevent any lookup-driven path from clobbering a local profile, and the flag is set/cleared only inSetProfileAsync, which is the one path that's supposed to change it.SaveLocalAsync's "unchanged" check only fires when already local (member.UsesLocalProfile && ...), so switching from network to local with a matching name/avatar correctly still writes and flips the flag rather than reporting Unchanged — consistent with the code comment.DatabaseInitializer's backfill filters!UsesLocalProfilein SQL before materializing, which is legitimate since that column is deliberately left unencrypted for exactly this reason./avatar/{did}is behind the same allowlist policy as the rest of the board, consistent with the stated design (any allowlisted member may view another member's avatar, same as everywhere else in the app).ValueConverter/ValueComparerpair forMemberAvatar.Bytesis correctly wired to avoid the reference-equality re-encrypt-on-every-save trap the comment describes.access-*→choice-*) is applied consistently acrossBoardAccess.razor,app.css, andaccess.spec.js.auth.md,data.md,orientation.md) and the localization tables were updated in both locales for every new string, matching CLAUDE.md's conventions.I found nothing to block on. One very minor, non-blocking observation: the
ValueComparer<byte[]>hash function inKanbanDbContext.cscombines every byte individually viaAggregate, which is O(n) over up to a 1 MB avatar on every save-change-tracking pass — cheap relative to the AES-GCM work already happening on the same bytes, so not worth blocking on, just worth knowing if avatar size limits ever grow.⏱ 1m 59s · 9 turns · tokens: 82.1k in / 10.6k out (+810.7k cache-read)
📜 Full session transcript