The roster no longer names a voided organization
An organization voided from Platform -> Configuration -> Organizations (orgs.is_active = FALSE) does not exist anywhere but the console's Void tab. findOwnedOrg...
The faces answer says how many people are in the conversation
GET **************** now carries `participants`: the console puts a face beside each message only when three or more people are in a conversation, since between...
A face for every message in every chat window
GET **************** answers, for one conversation, the photo to draw beside each message: everybody on it now AND everybody who ever wrote in it (a roster forg...
A meeting link in the conversation, always on the platform's Kamo Meet
POST **************** lets either side of an Exec2Exec conversation add a meeting link, behind the same gate as sending a message. The meeting is created as the...
Faces on the desk β a client's photo, last sign-in and presence
The desk's roster now carries, for every client, their own photo (users.avatar_id, built on the operator's live theme host β the only one their console loads im...
A conversation reaches the phone
A message on an Exec2Exec line now goes to the recipients' phones as well as their consoles: the customer's phone when KamoCRM replies, every desk member's phon...
The desk's count of client messages not yet viewed
GET **************** returns how many messages from clients this EXEC2EXEC_ACCESS holder has not viewed, across every conversation, and each roster row carries ...
Seat the whole desk on every chat, show who is on it, close the chat feeds
- Standing membership: every explicit EXEC2EXEC_ACCESS holder sits on every Exec2Exec conversation. Exec2ExecDeskReconciler seats and unseats from the grant...
Serve the executive channel
A conversation with its own audience rule and no ticket machinery, following SystemBug's shape: its own package, its own controller, its own visibility, and Med...
No empty tables, numbered tables, and one table per member
Four rules, all of them things a member reported seeing broken. EMPTY TABLES. A lobby showed three tables while one game was being played. Two had no seats at ...
Show the hand nobody made you show
A pot taken with everybody folding is the one hand at the table whose contents only its owner knows, and there was no way to say what it was. Showing a bluff is...
Opening a table buys you into it, and the last one out closes it
Both ends of a table's life allowed the same thing: a table with nobody at it. OPENING ONE COST NOTHING AND SEATED NOBODY. A host could put a table in front of...
The plies, and how long each one took
Two things the move panel needs, neither of them on the published frame. `GET /games/{id}/moves` hands over every ply in order. It is a separate read rather th...
A server-authoritative Texas Hold'em, and the Cheddar wallet seam
Up to ten players a table, started from any conversation, with anyone else in the organization invitable into it. Every rule lives here; the browser owns none. ...
The chat entry is a door to the pair's board, and you can ask them back
Two colleagues who have played once have a board together for good. So the chat entry stops offering to introduce them: `gameWith` answers with the SHARED game ...
The inviter plays immediately, and the invitee is told when it is their turn
An invitation used to produce a board the inviter could look at and not touch, waiting on somebody who might be away from their desk. Now the game is running fr...
A whole game of chess, server-authoritative, over NATS
The rules live in ONE place and it is this one. ChessEngine implements the whole ruleset β move generation, legality under pin and check, castling, en passant, ...
A reporter filter, for platform team members only
The board is global β every organization sees every report β and what protects a customer is SystemBugIdentityPolicy withholding WHO filed a row. A control that...
An approval settles the member's achievement
Approving a submission now tells SecurityService to recount that member's SPREAD_THE_WORD standing, so the trophy lands in the same second as the decision rathe...
The queue a member works through, and the queue an admin reviews
MediaService's Growth Hub surface at /api/media/marketing/growth. It owns this for the same reason it owns tracking, the scoreboard, materials and Canva β and b...
Relay an unlock to the member it happened to, and to nobody else
SecurityService publishes **************** on core NATS; this puts it on **************** Same arrangement as the training badge, and for the same underlying re...
Open the board to every org, withhold who filed each report
The tenancy check is gone from visibleOr404 and from all four queries, so every organization can read, comment on and vote on every report. SystemBugIdentityPol...
The thread page names its reader, and drops an unused helper
`viewerMemberId` on the thread page, so the console can tell the viewer's own reactions from everybody else's without a second call to /api/user-info. A ref fil...
The thread behind a report, and the two statuses that need one
`SystemBugThreadService` is the whole feature: a report's MediaSession, the status events written into it with the reason their author gave, the comments anybod...
Let the reporter confirm their own fix, and carry a denial's reason
Adds POST /{id}/verify-fix β the one status change in this feature that is not gated on MANAGE_SYSTEM_BUGS. A separate endpoint rather than a hole in the one be...
Relay coder usage onto a per-member topic
menu subscribes to over the existing chat STOMP connection. Built as a twin of the training relay β same subject shape, same single-token rule, same core-NATS-...
File a website's chats under the topic its integration chose
createSupportSession took the topic from whatever title the visitor's browser sent β the widget hardcodes "General Inquiry" β and when no topic matched that nam...
Let an integration carry a key a hosted Kamo page can resolve
`publicSiteKey` was persisted only for SPECIFIC_AGENT integrations, because the multi-tenant MLOS portal was the only page that had to resolve its key by Host r...
Send the sender's avatar with the message and its push
The chat notification carried `senderAvatar: ""` with the comment "avatar resolved on frontend", and the push carried no avatar at all. A browser can resolve on...
Send an org's own hostname as the meeting OAuth redirect
The same rule the mailbox flow now follows, for Zoom and Teams meetings: an organization on its OWN app registered that app against api.<its own domain>, so tha...
Make MediaService correct on more than one pod, and run two
MediaService has only ever run a single replica, so a deploy of it was a total chat outage and an OOM was the same. It could not run two, for reasons that were ...
An EMAIL type, and every push now names its org
EmailService raises the notification off IMAP IDLE and asks MediaService to deliver it through the internal dispatch endpoint that exists for exactly this β eve...
Let a god-mode operator file a system bug privately
Posting a report now offers a Publish switch to an operator with an open god-mode window. It defaults to off: the report stays with god mode until somebody rele...
Resolve Canva, Meta, X and meeting credentials per organization
Every flow here now reads whichever app the org is on β its own registration if it brought one, otherwise Kamo's platform app. The Canva and Meta entry points ...
Ring a member's phone for an incoming call
VoipStompRelayController relayed voip.call.* ringing events to the browser and deliberately never pushed them, reserving incoming-call alerting for 'the future ...
Send the quote with a reply, not just the pointer
media_objs_msg.parent_id has been there all along and this endpoint has accepted parentId on the way in since threading was modelled, but a reply crossed the wi...
Leave a group chat, and keep join/leave lines in the transcript
Leaving is DELETE /sessions/{guid}/membership, not removeMember aimed at yourself β that route refuses self-removal on purpose. Removal is done TO somebody and ...
Name a group chat, star a conversation, remove somebody from a room
Three routes, and each refusal is a rule somebody has to be able to tell apart from an outage β so each carries its own code rather than a shared 4xx. DELETE *...
Email the reporter when their bug or enhancement changes status
Triage was silent. Somebody filed a report and then had to keep opening the tab to find out whether anyone had looked at it β which is the same as not being tol...
Per-viewer read state, a live badge, and per-status counts
THE BADGE WAS ABOUT THE QUEUE, NOT ABOUT YOU. /new-count counted every NEW report platform-wide, so it could only fall when somebody moved a status β reading th...
Attach a video to a bug report, not only a screenshot
A still cannot show the three clicks before the thing broke, so reporters were describing recordings in prose and posting the file into a chat instead. The atta...
React to a message with an emoji, and correct one you sent
POST **************** sets or clears the caller's emoji and answers with the message's whole reaction set, so a client that raced itself converges on the server...
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 ...
Tell a member about a webinar booked on their behalf
Not gated on a webinar right, unlike the ticket counters: the case this exists for is one where the member took no action at all, so requiring the right to requ...
Count unreviewed bug reports for platform admins
Zero for an ungated caller rather than a 403: the nav polls this on every page load for every member, and an error they must routinely swallow is how real error...
Route unread snapshots to their own STOMP topic
Everything on voip.> went to one topic, whose browser subscriber plays the SMS arrival sound and opens a tool window for every frame. A badge refresh there woul...
Refresh unread badges when a visitor message lands
persistInbound announces its own message (publishRealtimeEvent=false, so a visitor's reply does not render as the owner's), and in doing so also skipped the unr...
Resolve bookable times from member availability
Availability is now computed rather than stored. MemberAvailabilityService owns the arithmetic: expand each presenter's declared windows over the type's booking...
Naming a site is optional
It was required, and requiring it was wrong. A site is a hint for whoever picks the report up β a member who is not certain which of five surfaces they were loo...
File a bug or an enhancement
The type is refused rather than defaulted on a write: an enhancement quietly filed as a bug sends the AI session hunting for a fault that was never there. It is...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.