The new workspace opens once, ready, and as itself
Three faults with one cause, reported after creating Carl's Jr: the new tab said ?org=kamocrm.com, showed Kamo's logo and none of the org's colours — while its ...
Delete findConflictingEvents, which never returned a row
Four independent defects, any one of which emptied the result set: it filtered e.user.id while both callers pass a member id and the two columns never coincide;...
Conflict detection now finds conflicts
Delegates to findBusyForMemberInRange and excludes the event being edited in Java, where a null exclusion cannot poison a SQL predicate. The test it replaces m...
Remove the recurrence control, which discarded what it collected
The switch, frequency select and occurrence count rendered on the calendar page and reached no payload: composeDraft never read them and onSave never sent them....
Settle the loading gate on the gate, not on a "load started" flag
Two follow-ups to the session store, one of them a real defect. ensureLoaded decided foreground-vs-background from an `everLoaded` flag set when the first fore...
A push token may be claimed by one device row, not every past sign-in
Companion to MediaService 8a809b3, which stopped members getting the same chat message three-plus times on their phone. That change made dispatch collapse dupli...
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...
A tab only names an organization it actually has
Opening TacoBell from /network put ?org=kamocrm.com on the new tab. The tab lands on /validate, which renders inside this shell BEFORE its session exists — so t...
Recover a lost background count from the images themselves
commitBackgroundOrder writes the images to STAGING, and imageBgCount is only a record of how many it wrote — which made that one column a single point of failur...
Gate the nine Service Work endpoints on a right
They shipped checking only that the caller had a session, so any member of an org could read and mutate every work order in it regardless of role. ASSIGN_SERVI...
Drop the nine Service Work endpoints from the unguarded baseline
They are gated as of e6e1d02, so their baseline entries no longer match an unguarded endpoint and **************** fails on the stale lines. Requiring the remov...
Stop the customer **************** rights reading every tenant's rows
Every endpoint that accepted a VIEW_OWN_* right as an OR-alternative loaded its record by primary key alone, never checking who it belonged to: getSubscription,...
The org reference belongs in proxy.ts, not a second middleware file
Next 16 replaced middleware with proxy, and this repo already had proxy.ts — so adding middleware.ts alongside it failed the build outright: "Both middleware fi...
The signed-in session becomes per-tab
analytics.auth lived in localStorage, which every tab of this origin shares. Analytics is reached by an OTK handoff from a workspace and each handoff mints a to...
A member's identity and their org stop leaking between tabs
meet.<platform> is shared by every organization without a host of its own, and localStorage is per ORIGIN. The signed-in member's name, email and avatar were wr...
Theme assets come from a host that resolves
The sign-in screen paints an organization's branding from theme.<its own domain>. That domain may not resolve: an org can exist and be signed into before its DN...
One tab, one tenant — stop resolving the org from the shared cookie
Open KamoCRM in one tab and another organization in a second, refresh the first, and it rendered as the second. Close that tab, refresh again, and it came back....
Serve a tenant's assets from a host that resolves, and stop deleting its backgrounds
Two faults found while working out why a new org opened with no logo and no colours. THEME DELIVERY. Assets were requested from theme.<the org's own domain>. A...
Stop standing workload from flashing the browser tab forever
TabAttention fed navGrandTotal into a 1100ms title flasher. Four of its eight addends are queue depth that never reaches zero when a member reads something: sup...
Wire markSmsRead/markAiRead/social mark-read to their windows
Final review found markSmsRead and markAiRead had zero callers, and SocialChatWindow never called markAsRead/beginViewing at all — three of five conversation su...
Gate the voicemail nav count on VIEW_VOICEMAIL
**************** handed any authenticated member holding a MemberVoipConfig row the instance-wide unread-voicemail count with no rights check at all, even thoug...
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 ********...
Give the web media path a TURN relay
The browser SIP path built its peer connection with one hardcoded public STUN /api/voip/turn/credentials for as long as that endpoint has existed — TurnControll...
Stop the app tree's rails putting a scrollbar on the viewport
/settings/account?tab=apps scrolled the whole window, down into empty space below the shell, while the apps column it was supposedly scrolling had its own scrol...
Show one call once in a rollup, and stop 500ing on unworked leads
Two fixes the account rollup made visible. A phone number can be a contact point on more than one owner at the same time, and two leads on one account sharing ...
Close the webhook idempotency gap BL0's final review found
Finding 1 (Critical): a losing claim on a redelivered Stripe event only proved a row existed, not that the prior attempt finished. A claimed row left behind by ...
Make the KamoDesktop SSO link redeemable exactly once
The link is delivered as a URL, so it lands in browser history and in the reopen-closed-tab list, where it long outlives the session it was minted for. Until no...
The account rollup must not widen what a reader may see
The account timeline I added last commit was a way around the per-lead read gate. A member refused a lead's messages on the page that names the lead could open ...
Carry the assignee into the account rollup, over a LEFT join
Reading a lead's communications is gated on who it is assigned to. A rollup that returned uids alone could not apply that gate, so the account timeline built on...
Thump the first real message, not just the second
shouldThump(undefined, n) meant "first render" — but a spawned head's unread starts genuinely undefined, and applyUnreadToHeads's own undefined-vs-0 guard never...
Make every client component safe to render on the server
Next server-renders client components, so `window`, `document`, `localStorage`, `sessionStorage` and `navigator` do not exist during the render that produces th...
Do not key ephemeral per-attempt Stripe creates
Round 2 review (Ruling 17) found "is this a create?" is necessary but not sufficient. The question that matters: can this subject legitimately be created more t...
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...
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...
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...
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...
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...
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 ...
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...
Apply the branding the wizard just collected
Colours, logos and backgrounds chosen while creating an organization were not reaching it. The member opened their new workspace on default branding and had to ...
Answer Stripe webhooks with the right status and never echo an exception
/org/current stops depending on a live Hibernate session
This endpoint is what the workspace shell loads before it renders, and the client refuses a record it cannot read — Organization.fromJSON throws without a domai...
Make the retail webhook signature check fail closed and compare in constant time
validateSignature used to return true three ways without verifying anything: a null secret short-circuited, six of eleven provider types fell through default ->...
Continue stays disabled once a workspace is opening
The button already carried disabled={!selected || submitting}, but submitting was only true for the duration of the request. On success the page sets redirectin...
Only a spent token sends the member back to the start
Every non-ok status fell back to "Please sign in again", which is only true of 401 — the one status that means the selection token is really gone. SecurityServi...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.