Priority: P1 (High)
Category: Correctness / Presence Management
Issue
The hub maintains _UserConnectionCounts to keep a user present while multiple tabs or connections remain open, but PresenceController.Leave() removes the user from the presence tracker immediately without considering active connection count. One tab can therefore make the user appear offline even when another connection is still active.
Location
src/Chat.Web/Controllers/PresenceController.cs
src/Chat.Web/Hubs/ChatHub.cs
Risk
- Presence state becomes incorrect for multi-tab users
- Users can disappear from room presence while still connected
- Presence change notifications become inconsistent with hub lifecycle
- Debugging connection and presence issues becomes harder
Proposed Fix
Unify presence removal semantics so HTTP leave actions and hub disconnect actions use the same connection-aware model.
Suggested implementation direction:
- Make leave room-scoped instead of removing the whole user presence record, or
- Route presence removal through a shared service that understands active connection count
- Avoid direct
RemoveUserAsync from controller code unless the last connection is known to be closed
- Add tests covering multiple simultaneous connections for the same user
Effort Estimate
1-2 hours
Acceptance Criteria
Reference
Static review finding from architecture and correctness review of src on 2026-05-02
Priority: P1 (High)
Category: Correctness / Presence Management
Issue
The hub maintains
_UserConnectionCountsto keep a user present while multiple tabs or connections remain open, butPresenceController.Leave()removes the user from the presence tracker immediately without considering active connection count. One tab can therefore make the user appear offline even when another connection is still active.Location
src/Chat.Web/Controllers/PresenceController.cssrc/Chat.Web/Hubs/ChatHub.csRisk
Proposed Fix
Unify presence removal semantics so HTTP leave actions and hub disconnect actions use the same connection-aware model.
Suggested implementation direction:
RemoveUserAsyncfrom controller code unless the last connection is known to be closedEffort Estimate
1-2 hours
Acceptance Criteria
Reference
Static review finding from architecture and correctness review of
srcon 2026-05-02