Add ChatUserProfile record and IViewerRepository.LoadChatProfilesAsync (AsNoTracking,
projects Name/EmblemId/CountryCode/Rank/DegreeId from Viewer+Info nav refs).
GuildChatController.Messages now injects IViewerRepository instead of SVSimDbContext;
calls LoadChatProfilesAsync to populate users[] (removes direct db access from controller).
GuildChatMessageRepository direction=2 (NEW): change >= start to > start (exclusive).
GuildChatPollTests updated to assert the start message is NOT returned by direction=NEW.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ChangeRoleAsync: Leader-only role management with SubLeader cap check.
Atomic leader transfer loads both member rows + Guild.LeaderViewerId in
one _db.SaveChangesAsync call (no split saves). Emits ChangeLeader (6)
or ChangeSubLeader (7) chat events. Same-role is no-op / returns Ok.
- /guild/change_role controller: calls ChangeRoleAsync, returns full updated
member list (GuildChangeRoleResponse.Members[]).
- /guild/friend_list controller: calls IFriendService.GetFriendsAsync, then
annotates each friend with is_join_guild from Viewer.GuildId column.
- IGuildRepository.UpdateLeaderViewerIdAsync: added for completeness even
though the atomic transfer path bypasses it via direct EF mutation.
- Tests: 8 GuildServiceChangeRoleTests (role changes, cap, atomic transfer,
no-op, permission); 3 GuildChangeRoleFriendListFlowTests (HTTP roundtrip,
is_join_guild); 2 GuildWireShape tests (literal JSON shape); 1508 pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GuildUpdateTask.Parse() reads data[guild] as GuildDetailInfo directly; the previous
GuildDetailSubTree wrapper (data[guild][detail]) caused json[guild_id].ToInt() to
crash on the client. Split GuildUpdateResponse (flat) from GuildUpdateEmblemResponse
(nested, correct for GuildEmblemUpdateTask.Parse). Also surfaces guild_name from the
request through UpdateGuildRequest.Name -> service -> repo so the client rename is honoured.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace double-correlated COUNT subqueries in the member-bucket switch
with a project-filter-project pattern that computes the count once per
row. Add SearchAsync_bucket_2_matches_guilds_with_11_to_25_members to
cover the previously untested medium-bucket range.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>