Chat and bug emails link to the console the recipient can reach
ChatEmailNoticeService named the recipient org's first domain whatever its state. The reported case: Exec2Exec letters to makeitpopsigns.com, sent before its DN...
A customer's message reaches the desk, not only the thread
An executive wrote into their Exec2Exec chat and nobody at KamoCRM heard. announce() told the session's own members, and an operator is only a member of a conve...
An announce that throws must not roll back the buy-in
`announceToConversation` runs INSIDE the transaction that seats the host and charges them β unlike `announceTable`, which the controller calls after the fact. T...
Colleagues in the invite list, and an invitation from a chat that arrives
Two reported defects, both in who a table can reach. **The invite dialog offered accounts.** `invitableFor` read `members.findByOrganizationId`, and an organiz...
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...
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...
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()...
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...
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 ...
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....
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 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...
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 ...
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...
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 β ...
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...
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...
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 ...
Stop deleting the only chat pod before starting its replacement
Deploys replaced the only pod of each service with nothing to catch the requests in flight. Three settings, applied across the fleet: - preStop sleeps 10s befo...
Publish needs GD, not an open god-mode window
The switch was invisible to god-eligible operators. It was gated on **************** β GD plus an opened break-glass window β and holding GD is not the same fac...
Build against the two-field SystemBugDispatchDTO
Unrelated to the OAuth work in the previous commit β MediaService main has not compiled since kamo-shared-library ad78dfa8 added terminalSessionId and terminalT...
The incoming-call deep link must use kamocopilot://
KamoMobile's kamomobile:// scheme is step 2 of a rename that has not happened. The published binary registers kamocopilot alone, and a deep link to an unregiste...
Announce a message only once it can be read
POST /sessions/{guid}/messages is @Transactional, and the NATS publish of the message has always waited for the commit β with a comment saying exactly why: publ...
Refuse an edit once another member has read the message
PATCH /api/media/messages/{id} was deliberately ungated on receipts, unlike the DELETE beside it. The reasoning written into it was that unsend makes read words...
Spend the site deletes before the replacements are written
Editing a report came back "The bug could not be saved: Internal Server Error" whenever the reporter left a site ticked β so every edit about the title, the des...
Forward removedByMemberId to the browser
The relay copies named fields off ChatMessageDto onto the STOMP frame, so a field it does not copy is dropped even once the DTO declares it. MEMBER_REMOVED now ...
A relay only forwards its own conversation
ChatSessionSubscriptionManager publishes to a topic named after the session it subscribed FOR, never after the session the frame came from. That is safe only wh...
An attachment that cannot be read says so instead of 500ing
The imaging proxy already answers 410 for revoked access and for a removed message, but let openRange's IOException escape uncaught β so the one case where the ...
A reaction toggled twice at once is a no-op, not a 500
setReaction read, then saved, then caught the constraint violation the read was supposed to prevent β which does not work: the violation surfaces at flush, afte...
A child org's lobby offers its own backgrounds, not the platform's
The folder holding an org's meeting backgrounds and theme config was derived from a hostname β the request's, or the org domain the sidecar passed β by taking t...
One phone, one alert β collapse duplicate push destinations
Members were getting the same chat message three-plus times on their phone. Registrations are unique on (device_token_id, org_id, push_kind), NOT on the push t...
Route getChatSessions' unread ambiguity through OwnAuthorshipAmbiguity
MediaController.getChatSessions hand-rolled the pre-extraction version of the "was this written by somebody else?" rule (rowVisitor != null && callerId ********...
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...
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...
Build the search pattern outside the query, and parse the query in a test
likePattern turns an absent search term into '%' so no null parameter ever reaches a string expression, and escapes % and _ in the term so a search for a litera...
Stamp every unread frame with the instant its counts were read
The four places that built a member's unread snapshot β the REST read, the frame sent when a message arrives, the frame sent when a session is marked read, and ...
Resolve a public chat's owner without walking a lazy association
getChatSessions runs outside a transaction, so reaching the session owner through the visitor's lazy session proxy worked only for as long as open-in-view stays...
Stop reading the org's system member as the web-chat visitor
Four reported faults, one cause. A widget visitor has no Member, so the org's SYSTEM member β the owner, who works the queue β stands in for them as the ticket'...
Size the pod above its own JVM heap ceiling [skip ci]
The image starts the JVM with -XX:MaxRAMPercentage=70 -XX:+AlwaysPreTouch, so the heap alone may take 70% of the container limit and pre-touch keeps every commi...
The public chat reads its pages without the count either
Both sites took .getContent() and dropped the total on the floor, so both were paying for the same discarded COUNT over every object in the session that just 50...
Size the pod for the heap the JVM is already allowed to take
The container ran with a 1Gi limit while the image starts the JVM with -XX:MaxRAMPercentage=70 -XX:+AlwaysPreTouch. Read off the live process, that is MaxHeapSi...
Creating a topic takes CREATE_SUPPORT_TOPICS, not its parent
The gate was already there and refusing callers β on MANAGE_SUPPORT, the parent β so CREATE_SUPPORT_TOPICS read as decorative in every editor while being exactl...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.