The drop beacon reports which path ICE actually chose
The beacon shipped iceCandidateType as a permanently-null field. That is worse than omitting it: it makes the telemetry look more informative than it is, and th...
Keys belong on Stripe creates only, not on mutations
Round 1 review (Ruling 15) found the Critical: a stable idempotency key on an update/cancel/attach/detach is not a safeguard, it is a bug. Those calls already s...
Memoize the app-wide unread value, fix a self-retriggering effect
UnreadContext published a fourteen-member object literal as its context value, so React saw the context change on every render of the provider and woke every co...
Stop polling for badges nobody is looking at, and stop waking every consumer
Two always-mounted providers ran a one-minute poll with no visibility gate. LegalPackageCount had none at all; TrainingPendingCount had a visibilitychange re-re...
The auto-reconnect reload-looped every page into a blank screen
I shipped this and it took the desktop down: a blank white page and a tab loading forever. Disabled in-cluster immediately; this is the real fix. `ng-switch` s...
A phone-less org should not open a call socket
The softphone opened its SockJS/STOMP connection to media.<domain> unconditionally at boot. getCapabilities() is awaited a few lines above and is the backend's ...
One day picker, shared by the calendar and the settings tab
The two surfaces had each grown their own answer to "which days?". The settings tab put Workweek Days, Weekend Days and Every Day behind a dropdown; the calenda...
Stop re-merging the whole catalogue on every request
getRequestConfig runs per request, and for any non-English member it called mergeWithFallback over the entire 23,252-key catalogue every time — rebuilding a res...
Badge a minimized window with its server unread count
The one component that sees both the window list and the unread maps — the engine knows nothing about conversations and the unread context knows nothing about w...
Set an absolute unread count from the server
Replaces hexhead:incoming, which nothing ever dispatched. Its +1 would have counted a re-delivered snapshot as a new message, and its spawn-if-missing branch wo...
Messages and calls, on the existing timeline component
CommunicationsTimeline and EmailEntry take an optional endpointBase and keep the lead path as the default, so every existing call site is byte-for- does not car...
A patient's calls, texts and email — on the platform's own spine
Not a second timeline. LeadCommunicationsService now takes a CommsSubject and the lead overload delegates to it, so /leads keeps its exact behaviour drift. A pa...
Stop losing calls to session expiry, teardown and stale devices
Four guardrails that share two files, so they share a commit. A live call now counts as session activity. ClientActivityTracker equated presence with input eve...
Read-fence SMS/AI mark-reads, fold sessionType into sameSessions
Three review findings against Task 21, all plan-mandated: - markSmsRead/markAiRead had no read fence: a refresh or an inbound push resolving between the opti...
Make every Stripe write idempotent, and fail the build on a new one without a key
A socket timeout on a Stripe write that actually succeeded is indistinguishable, from our side, from one that did not; without an idempotency key the retry crea...
One arriving message should not re-render the whole history
Batch 5 of the client-side performance audit. MessageList maps every loaded message to a MessageBubble, and MessageBubble was not memoized. With 300 messages o...
Take AG Grid's 187 KB of CSS off the critical path of every route
Batch 4 of the client-side performance audit, and the last of the big ones. app/globals.css opened with two @imports of ag-grid's stylesheets, and the root lay...
Commit the processed flag with the money write, not ahead of it
markProcessed was REQUIRES_NEW, so it committed processed=true while the handler's money writes were still uncommitted in the suspended outer transaction. JpaTr...
Widen the comms spine to an owner, in the order that keeps it safe
LEAD_CONTACT_POINTS and LEAD_COMMUNICATIONS gain the document manager's (assoc_type, assoc_object_id) pair so an account, a patient or a commerce record can own...
Key the communications spine on an owner, not a lead
The spine was built lead-shaped — LEAD_CONTACT_POINTS.LEAD_UID, LEAD_COMMUNICATIONS.LEAD_UID — and the association it actually needs is broader. An account owns...
SMS and AI counts, and a per-surface split
One MediaService snapshot covers three surfaces that belong on two different nav indicators, so the totals are separated from the server's declared sessionType ...
A thrown hexhead can no longer end a live call
SoftphoneTool never listened for the cancelable tool:requestClose that MessageTool uses to protect unsent text, so scoring the remove goal on a minimized softph...
Stop sending the whole message catalogue to the sign-in handoff
Batch 3 of the client-side performance audit. The root layout passed every message to NextIntlClientProvider on every route. The catalogue is 1.05 MB compact —...
Confirm before a refresh takes a live call
The SIP session lives in the document and nothing on the server holds a handle to it, so a reload ends the call outright — with no BYE, leaving the far end on s...
Rebuild the availability editor around the day it describes
The editor was a stack of form rows that never showed what it was making. It now opens with the thing itself: a 24-hour rail with the block painted on it, in th...
Split the tool windows out of the shell, stop the idle timer storm
Batch 2 of the client-side performance audit. As with batch 1, nothing a member can see or do changes. The tool registry imported all ten tool windows eagerly,...
One address for a conversation on any of five surfaces
chat/support/social share the chat: namespace because they share one unread snapshot; SMS and AI get their own, because a VoipConversation UUID and a media sess...
A refresh mid-call no longer re-dials the customer
The tool-window snapshot replayed props verbatim, which is right for state and wrong for an instruction. `autoDial: true` survived into the restored window and ...
Routes for the notification spine's counts and mark-reads
The PUT routes answer 204 and must not call .json() on it — that turns a successful mark-read into a 500.
The workspace picker shows each org's logo
Two letters standing in for a logo was a workaround for the response not carrying one. It does now, resolved server-side from the folder the org's theme was pro...
The sign-in response carries each workspace's logo
The picker drew two letters per row because the response held a name and an alias and no image. A list of workspaces is read by mark long before it is read by n...
Set up RingCentral with a JWT, and assign members their own number
Three screens, one change. The phone server's overview tab replaces "Connect with RingCentral" with the credential form the JWT flow actually needs — client id...
Consistent error handling and layering on two review findings
Review findings on Tasks 14 and 15: - SystemBugController held a SystemBugRepository field and queried it directly for the new-bug count, while every other r...
Authenticate RingCentral with a JWT, per phone server
RingCentral was the last provider whose credentials came from a Kamo-wide registration plus two browser consent hops — one for the org, one per member. Both are...
Add the RingCentral JWT and direct-number columns
Order 0, because the shared-library entities map both columns from the moment this build starts: every runner that loads a PhoneExtension or a MemberVoipOAuthTo...
Guard the unread-lookup itself, not just the broadcast
Review finding on Task 9: persistInbound is @Transactional, and only the broadcast() call inside the per-member loop was exception-safe (it swallows its own fai...
Give a member a RingCentral JWT and an extension its own number
Two columns, both nullable so nothing is backfilled and no existing row changes meaning. **************** holds a member's own RingCentral JWT. RingCentral onl...
Implementation plans for the softphone line window
Two plans, because the spec covers two subsystems and the first ships working software on its own. The guardrails plan is seven tasks that need no new browsing...
Missed-call and voicemail counts for the softphone indicator
Reported as separate lines rather than a total: the icon shows the sum and its dropdown shows the breakdown, so a pre-summed wire value would make the dropdown ...
@kamo/tool-core 0.1.2
Brings raisedBy: 'incoming' and the quietRaise event, so an arriving message can be absorbed by a minimized window's hexhead instead of re-opening it.
Stop shipping the shell code no route can reach
Batch 1 of the client-side performance audit. Every change is behaviour-preserving; nothing a member can see or do is different afterwards. I18nProviders impor...
Design the softphone line window
A call cannot be moved into a window after it starts — an RTCPeerConnection and its MediaStream are not transferable between browsing contexts, and there is no ...
Rebuild the console on the house design language
The first pass was wrong and it was wrong in a way that should have been obvious before a line was written: I built Tailwind divs, MUI Tabs and hardcoded rgba c...
Prove library HQL parses on the Hibernate this service actually runs
kamo-shared-library builds on Boot 3.3.4 / Hibernate 6.5; BillingService runs Boot 3.1.5 / Hibernate 6.2.13. The library's own HQL tests therefore pass vacuousl...
Unreviewed-response counts and a mark-read
Keyed by session guid, which is what a tool window carries. Not audited as a disclosure: it returns counts and never a word of a transcript, and recording a bad...
An unread snapshot, a mark-read, and a live frame
Its own NATS subject rather than the existing message one: the browser's handler for /topic/voip/message/ plays the arrival sound and opens a tool window for ev...
Availability for presenters only, and a way to understand it
Three things, all about the same layer. WHO SEES IT. The availability toggle was shown to everybody. Declaring hours is only meaningful for somebody a webinar ...
Make a replayed Stripe event a no-op
Adds BillingWebhookEventClaimService on its own bean, with REQUIRES_NEW on every method, and wires it into processEvent ahead of the dispatch switch. A losing c...
GET /api/webinar/presenting
The webinar types the CALLER is a presenter for. The calendar asks this before it offers an availability layer at all: hours declared by a member no type lists ...
Like what you see shipping?
All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.
