- Shipped
- August 14, 2026 at 11:02 PM UTC
- Author
- kamo
- Commit
- 2729e2d
There were two sound systems and neither knew about the other: four mp3s behind useChatSounds, and four synthesised tones behind notificationSound with a five-option picker on the notifications page. Between them they covered chat and notifications. Everything else in the application — a lead being offered, a call connecting, an upload failing, a session about to expire — was silent, and there was nowhere to say otherwise. Both are deleted. In their place: one catalogue of 46 events, one palette of 39 synthesised tones, and three layers of settings that resolve field by field. WHY THE MERGE IS PER FIELD. A member who changes one event's volume keeps their organization's tone for it and the platform's for everything else. The obvious alternative — the top layer holding a full copy — freezes a member's sounds at whatever shipped the day they first opened the page, and nothing an administrator changes afterwards ever reaches them. WHY THE TONES ARE SYNTHESISED. A sound file has to be fetched, so the first time an event fires is the one time it is silent, on exactly the slow connection where the visual cue was also missed. These are oscillators: nothing to ship, nothing to cache, the same size in 22 locales, re-tunable without a binary. Every one is more than one voice or one voice that moves — a static beep is indistinguishable from every other noise a computer makes, and a test enforces it. WHY A CATALOGUE RATHER THAN CALL SITES. An undeclared key does not compile, and a declared event that nothing fires fails a test that greps the whole application for it. That guard already earned its place: it caught ui.action.success shipping as a settings row that did nothing, and it is why the catalogue holds no meetings, e-sign or ticket-resolved events — kamo-internal has no producer for them, and a row that does nothing is what the old system's problem looked like. Storage is one table for all three scopes (EVENT_SOUND_PROFILES, applied and verified in Yugabyte before the shared-lib push). Device routing deliberately stays in localStorage with the microphone and camera: the headset on one desk is not the headset on another. Only the choice of tone roams with the account. UI. The signature is the inheritance spine — three segments per row, PLT/ORG/YOU, filled where a layer has an opinion and ringed where it won. A single "inherited" badge cannot express "my organization chose the tone and I changed only the volume", which is the state members actually end up in. Waveforms are sampled from the real spec, so the picker shows the shape of a sound before you commit to hearing it fifty times a day, and the sweep is timed to the actual playback. Settings live where the thing they configure lives: platform defaults on **************** (added to the settings menu), the organization's layer with its labels and colours on Behavior & Labeling, and the member's own beside their speaker in Audio & Video. Wired at 30 call sites across chat, AI, notifications, mail, SMS, social, calendar, telephony, leads, support, uploads, conversion, campaigns, timecard, training, documents and connectivity. Bursty sources are throttled per event — a socket firehose played faithfully is a fault, not a notification. Gates: tsc clean, 1851 tests pass (25 new), next build compiles, i18n guard clean across 2330 files. Dictionary pushed first, since kamo-internal CI clones it.