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 ...
Prove the endpoints actually CONSULT the entitlement, not just that it works
GamesEntitlementTest proves the gate answers correctly. Nothing proved the three controllers ask it — and that distinction is the entire defect this change set ...
A hand shown on the flop is five cards, and gets a name
`showHand` named a hand only with a full five-card board, while `finishHand` scores one from three — and two hole cards plus a three-card flop ARE a five-card h...
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...
A raiser could raise again after being called
One character, and it decided pots. `canRaise` is exactly `!actedThisStreet`, so that boolean is the whole of "may you raise". The service reconstructs it from...
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...
Chess, Hold'em and the wallet never asked whether the org has the app
The companion to the kamo-internal commit, and the half that actually mattered. The browser gate was cosmetic: ChessController, HoldemController and CheddarWall...
A re-let chair was handed the previous occupant's mucked hand
Two defects the linger made reachable, found by reviewing the commit that added it. THE REDACTION. viewFor matched hand-seat rows to seats by seat NUMBER alone...
The felt never saw a showdown, and a fold-out pot was counted twice
Two defects that were invisible for the same reason, and the data a poker table needs to present a result at all. THE SHOWDOWN NOBODY COULD WATCH. finishHand()...
New table 500'd on a ternary that unboxed a null button
Pressing New table answered 500: NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of HoldemTable.getButtonSeat()...
Stop rebuilding the wallet and the guest list on every check
`viewFor` fetched the recipient's Cheddar balance and queried the whole organization's member list — both per RECIPIENT, on every published frame. At a full tab...
The clock could never have fired, and a full all-in re-opened nothing
Two defects found by reading the code back, neither of which any test could see. ## The clock would have thrown on its first tick `HoldemDeadlineSweep` carrie...
Spring Data does not register a NESTED repository interface
MediaService crash-looped on the Hold'em image. The six repositories shipped as nested interfaces inside one `HoldemRepositories` class — one file rather than s...
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 frame stops carrying an archive nothing renders
The window's notation panel is gone — the captured pieces have that space — so `moveList` and `fenHistory` came off the published view. `fenHistory` alone was s...
Map.of throws on a null value, so "no game yet" was a 500
`gameWith` answered with `Map.of("game", …)`, and `Map.of` rejects a null value with a NullPointerException. So the one endpoint whose documented normal answer ...
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...
Let a refusal keep the sentence it was written with
The chess code refuses with reasons meant to be read — "not your turn", "this game is not in play", "illegal move". None of them reached anybody. `server.error....
Drop a field that was always false and a statement that did nothing
`aiThinking` was on every published board and was never once true — the AI answers inside the request that provoked it, so the window that asked already knows i...
Stop letting one bad boot deafen the VOIP relay for the pod's life
The relay subscribed once, from @PostConstruct, and never again. When VOIPService's voip.> stream did not exist — which it did not — both replicas logged exactl...
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, ...
Stop writing "/" as the destination of a review notification
The Growth Hub has no screen of its own — kamo-internal's widgetRegistry says so where it decoders to give the widget an href: "the widget IS the feature". So t...
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...
Parse every declared query, not the two somebody remembered
The three queries this feature added were never parsed by anything. That matters more than a wrong predicate would: Spring Data validates every @Query while it ...
A redacted status event kept its type, and pin the redaction
Two things, both found by writing the test for the actorName path rather than by reading it. messageType was being read off the body AFTER redaction. redactEve...
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...
Stop the presence gate silencing every mail notification
A member with a live MediaService socket was treated as having already seen every non-call push, mail included. That is true of what travels on that socket — a ...
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-...
Credit a signup whose page views were never recorded
**************** was the ONLY thing in the fleet that ever inserted into MARKETING_VISITORS, so the whole conversion pipeline silently depended on a browser bea...
Let a manager accept the web chat they were just paged about
Clicking Accept on a web-chat request closed the popup and reopened it a moment later, unassigned and with no chat window — the same card, over and over, with n...
Pin which support queue a stranger's chat lands in
resolveTopic decides where a visitor from a customer's website reaches a person, and every way it can go wrong goes wrong silently — a chat filed under a plausi...
Don't file chats into a support topic the org deactivated
resolveTopic honoured a configured topic by id alone, so deactivating one left the integration still pointing at it and new website chats still landing there — ...
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...
Tell the widget the org's name so it stops speaking as Kamo
The public-chat bootstrap payload carried orgId but no org name, so the widget had nothing to brand the assistant with and fell back to a hardcoded "Kamo AI". A...
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...
Retry the member load every authenticated request makes
AuthHelper.getCurrentMember was the single most common casualty of a Yugabyte catalog bump in this service — eight of the conflicts logged across both pods in o...
Prove the deploy by digest, not by tag
The preceding commit stops `set image` being a silent no-op. This asserts the outcome: after the rollout, the tag is resolved to a digest at the registry and th...
A rebuild of the same commit deployed nothing and reported success
The image is tagged with the commit SHA, so rebuilding the same commit produces an identical image reference. `kubectl set image` then changes nothing, the Depl...
Build the sender's avatar URL from their org, not the request host
A chat notification's senderAvatar was resolved from X-Forwarded-Host, falling back to the server name. Both kamo-internal's BFF and APIService reach this servi...
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...
Stop ten scheduled jobs from running twice now MediaService has two pods
Scaling MediaService to two replicas would have doubled every scheduled sweep it owns. Two of those send mail — MeetingReminderSweep and ChatEmailNoticeService ...
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.
