Every catalogued event fires where the event actually happens

Fixkamo-internal
Shipped
August 26, 2026 at 8:22 PM UTC
Author
kamo
Commit
cec7592

Most of the sound catalogue was configurable and inaudible. Two causes, both silent by construction: a sound that never plays looks exactly like one the member switched off, so neither was ever going to be reported as a bug. The engine implemented `playsWhenFocused: false` as `!document.hidden`, which asks about the browser tab. The catalogue documents it as a question about the SURFACE — "a chime for a message you are currently reading is noise" — and for an app-wide handler those are not the same question. One inbox subscription covers every mailbox, one SMS listener every conversation, one leads feed the whole organization; none can see which page the member is on, so every one of them was muted for exactly as long as the member was working. Eight events shipped enabled and never once played: email.received, sms.received, chat.typing, lead.created, lead.credits.changed, task.assigned, training.assigned and notification.withdrawn. A call site now names the surface its event came from (lib/sound/soundSurfaces) and whatever renders that surface declares it on screen (useSoundSurface), so the engine asks the question the catalogue always described. `force` still wins outright where no surface could apply. The other cause was the guard test, which fails only on a key nothing MENTIONS. That is a much weaker rule than "can be heard", so each event was wired at exactly one of its producers and no more. ChatNotificationListener — the application's one app-wide chat arrival — made no sound at all, so a message from someone the member was not already talking to arrived in silence, and the first message of any new conversation was inaudible even after the window opened for it. ui.action.success/error fired from four places in the whole application. Also fired now: both shared toast funnels; upload.completed/failed in MultiFileUploader and on a failed chat attachment; document.saved in BinderEditor; ui.action.error on the three email-send failure paths. conversion.completed was mis-declared `playsWhenFocused: false` — it is an outcome the member is waiting on, like upload.completed beside it. The ~67 components that hold their own snackbar are covered by a drop-in `useSnackbarState` (lib/snackbarSound) that derives the tone from the state transition, so adopting it is one line and no call site changes. It resolves the next value against a ref rather than inside the setState updater: React may call an updater twice, and two sets in the same tick must compare against each other. The leads feed's sounds move out of useLeadsRealtime, which only mounts on the two leads pages — so a lead landing while the member read their mail reached a hook that was not running. They now live in an app-wide LeadsFeedListener gated on VIEW_LEADS. One owner, or the leads pages would chime twice. TASK_CHANGED is org-wide and covers every task mutation, so task.assigned is filtered to tasks assigned TO this member BY someone else, deletions excluded, ids compared as strings because they are int64. Two guards so none of this can rot back: eventCatalog.test.ts now fails on a `playsWhenFocused: false` event fired with neither `force` nor a `surface`, and check-snackbar-sound.mjs fails on a snackbar held in a plain useState. Both were verified by reverting a fix and watching them catch it.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing