- Shipped
- 2 Septemba 2026, 01:46 UTC
- Author
- Kamo
- Commit
- 9ae1555
The chat unread badge was invisible everywhere — the Chat button, the Interaction Center, the hexhead — and the reason was not the badge. All three read one count, and that count was being cleared within a frame or two of every message arriving, by the window the message itself had opened. ChatNotificationListener answers an incoming message by raising a chat window. `raisedBy: 'incoming'` only reaches the tool store, where it decides that a MINIMIZED window stays minimized; a conversation with no window at all still gets one. That window was then on screen, un-minimized and in a foreground tab, which was the whole of "the member is reading this" — so it registered as viewed, marked the conversation read, and took the badge with it. Nothing was broken downstream; there was simply never anything to show. Support settled this in "on screen is not the same as read" and called it a read gate: a conversation the member asked for is read as soon as it is on screen; one that raised itself is not read until they touch it. `tool:userFocus` — only ever dispatched from the shell's mousedown/touchstart — is the signal, and `isFocused` cannot stand in for it, because it is derived from z-order and an auto-raised window is topmost by construction. Same rule, now for chat. The composed answer moves to **************** so there is one statement of it rather than an expression inline in a hook, and both raisers — the live notification and the sign-in auto-opener — flag their windows `autoOpened`. Signing in to six unread chats used to clear all six badges before the member had read a word of any of them. @kamo/chat-core 0.1.17 is the other half: the core marked read on start, after its history load and at the tail of a catch-up without asking isViewing() at all, so the gate alone would have been overruled by the load it gates.