fix(channel.member.list): always populate user_id/channel_id from schema #35
No reviewers
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_collab!35
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/member-list-defensive-completeness"
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?
Summary
Closes #34.
channel.member.listreaduser_idandchannel_idonly from the JSONcm.datablob. They're also schema columns onchannel_members— the authoritative source — so any row whosedataJSON didn't serialize those fields (buggy migration, manual SQL, test fixture) returned a response missing the identity fields, and the client rendered "User undefined" / "Unknown" via them.user_id-keyed fallback paths.What changed
crates/hero_collab_server/src/handlers/channel.rs::member_listnow alsoSELECTscm.user_idandcm.channel_idfrom the schema columns and unconditionally overwrites those keys in the response JSON. The PK + FK constraints guarantee schema columns can't legitimately disagree with the data blob; if they do, it's corruption and the schema is right.Plus the response merge:
Test plan
data='{}'): response now correctly populatesuser_id,channel_id,user_namefrom JOIN. Client renders correctly.Scope discipline
I considered also consolidating the two client-side renderers' divergent fallbacks (
'Unknown'vs'User ' + m.user_id) into a shared helper that prefers the server-provideduser_name, but reverted that change after self-review: with the server hardened to always includeuser_id, both renderers work correctly, and the divergent fallback strings only surface in code paths that the server fix already eliminates. Adding a helper for cosmetic consistency would have introduced abstraction beyond what the bug requires — exactly the kind of "while we're here, let's tidy" creep that the project guidelines call out.🤖 Generated with Claude Code
The handler read user_id and channel_id only via the JSON `cm.data` blob. Since `channel_members` rows have user_id and channel_id as schema columns (PK + FK to users.id), the data blob was redundant for identity — but it was the SOLE source the client saw. If a row's `data` was ever written without those fields properly serialised (buggy migration, manual SQL, test fixture), the response carried no user_id and the client's `member.user_id`-keyed lookup fell through to "User undefined" / "Unknown" fallbacks. Symptom from dogfooding: a `channel_members.data` row written without `user_id` (an SQL fixture insert during testing) caused the channel detail panel to render the affected member as "User undefined". Fix: read `cm.user_id` and `cm.channel_id` from schema columns alongside the data blob, then unconditionally overwrite the corresponding JSON fields with the canonical values before returning. Schema columns can't legitimately disagree (FK enforces equality); disagreement would only ever indicate corruption, in which case the schema column is the right answer. Verified: a deliberately malformed `data='{}'` row now returns `{user_id, channel_id, user_name}` populated from the JOIN, so the client's existing `m.user_id` lookups work regardless of `data` shape. Scope is intentionally limited to server-side defensive completeness. The client-side renderers (Members modal at chat-app.js:3121, channel detail panel at chat-app.js:3817) work correctly when `m.user_id` is present in the response, so no client change is needed once the server is hardened — they were both behaving exactly per their inputs under the previous contract. Cleaning up their slightly-divergent fallback strings ('Unknown' vs 'User <id>') is cosmetic-only and deliberately not bundled here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>