Charge what the catalog says, once, for the right quantity
Five defects that all moved real money, plus the sub-resource IDOR beneath them. Billing cycles. ANNUAL prices were sent to Stripe at the stored per-month rate...
Pick the agent to reassign a ticket to, instead of typing their id
Assign and reassign opened a text box you typed a raw member id into — which meant already knowing the id of the person you wanted, for an action whose whole pu...
Validate who a ticket may be handed to by hand
assignToAgent and reassignAgent checked only that the id resolved to a member. Assignment adds that member to the ticket's chat session, so an id from another o...
Design for the platform billing redesign
Records the audit finding that measurement, entitlement and money are three pipelines that were each built well and never connected, plus the 17 confirmed defec...
Verify god mode against the database, not a forged header
AccountOrgScopeInterceptor short-circuited every org and member check on the raw X-God-Mode-Active header, and kamo-internal's billing proxy copies that header ...
Auto Assignment offers the ticket, it does not allocate it
Auto Assignment pages every available support agent and goes to the first one who accepts. It was implemented as "silently pick the least-busy agent who is onli...
Run the topic's strategy for widget chats too
A chat opened in the web widget is the same unit of work as one filed inside the app, but this path skipped the topic's assignment strategy entirely — the only ...
Decide the sign-in auto-open on the server, not in this tab
The per-tab sessionStorage flag was not a record of anything. Signing in on a second device replayed every conversation the first had already popped, and cleari...
Endpoints for the sign-in auto-open handover
GET /sessions/chat/auto-open returns the chat conversations this member's client should raise; POST /sessions/{guid}/auto-opened records that it did. The durabl...
Durable record of a conversation the client raised by itself
MediaSessionMember gains AUTO_OPENED_AT, beside msgIdLastViewed because it is the same grain — a fact about one member's relationship to one session. That one a...
Overhaul the preferences panel
Four dropdowns and a switch on a panel washed top to bottom in the tenant colour at full strength, so every control fought its own background. Each select hid t...
Replace the mailbox spinners with a skeleton that assembles
Every loading state on /messages was a Loader2 on an otherwise empty pane — the boot, the message list, the folder tree, the message body and the next page all ...
Restore the CRLF endings on the calendar types
Same script-normalisation slip as the attendee repository: the file was rewritten through Python, which converted every line ending and turned a handful of type...
Don't let one failed request silence the sign-in auto-open
Unread chats are auto-opened once per tab-session, guarded by a sessionStorage flag. That flag was written up-front, before the fetch that tells a chat session ...
Add and remove guests from the event dialog
The last thing the dialog could not do. It could display who was already on an event, but there was no way to put anyone there — the only guest control was "upd...
Manage an event's guest list
The dialog could show who was already on an event but never add or remove anyone; the only guest operation was re-notifying people who were somehow already ther...
Restore the CRLF endings on the attendee repository
The previous commit rewrote the file through a script that normalised its line endings, so a twelve-line addition arrived as a fifty-line rewrite. That buries t...
Batched guest lookup, and delete-by-event
The event dialog shows who an event is with, and the list endpoint returns a month of events at a time. Asking for guests per event is a query per row; findByEv...
Tell the member what the event collides with, as they set it
The most useful thing a scheduling dialog can say is "you already have something then", and the endpoint to answer it existed all along with nothing calling it....
Let the conflicts endpoint exclude the event being edited
ConflictDetectionService has always taken an excludeEventId; this endpoint passed null. Without it an event overlaps itself, so the moment the event dialog star...
Stop typing an unseen support session as a plain chat
A live activity frame for a session not already in the list synthesized a row hard-coded to 'CHAT'. For a support ticket that put it in the wrong sub-tab, showe...
Put the session type on the activity frame too
The unread/ACTIVITY broadcast is published for every session type and carried no indication of which. The chats list, having nothing to go on, typed an unseen s...
Stop a support ticket opening a plain chat window full of JSON
Two faults, one symptom. Assignment makes the agent a member of the ticket's media session, so the incoming-message notification reached them and ChatNotificati...
Tell the client which kind of conversation a message arrived in
Every member of a media session receives the incoming-message notification, and assigning a support ticket makes the agent a member of that ticket's session. Th...
Declare the calendar tokens at :root, and rebuild the event dialog
The tokens were declared on the calendar wrapper element, which reads as the tidy thing to do and is wrong. MUI dialogs and menus render through a portal into d...
Actually override the operator label ink
The previous attempt raised specificity, which was the wrong diagnosis: tailwind.config.js sets `important: true`, so every declaration in .operator-label ships...
Rebuild the event dialog around when, not around fields
Ten identically weighted outlined fields in a column, in the order the columns happen to sit in the table: title, description, location, calendar, all-day, star...
Auto Assignment strategy, and open an assigned conversation automatically
Topics settings gains the fourth strategy. The list rows and the edit dialog share the same label, and the key shipped to kamo-translation-dictionary first — me...
AUTO_ASSIGNMENT, and raise an assigned conversation on the agent's screen
A chat request and a submitted ticket are the same unit of work; the topic's strategy is the only thing that decides how either is handed over. The PRE_TICKET s...
Widen the support topic assignment_strategy range CHECK to 0..3
SupportAssignmentStrategyType is @Enumerated(ORDINAL), so Hibernate emitted CHECK (assignment_strategy >= 0 AND assignment_strategy <= 2) on CREATE TABLE rather...
AUTO_ASSIGNMENT strategy and a durable record of an unseen handover
AUTO_ASSIGNMENT(3) is appended to SupportAssignmentStrategyType — assign immediately to an agent who is genuinely online, preferring whoever holds the least wor...
Make every line of the hero readable
The kicker and the three readout labels were painted by .operator-label's own dark ink, not by the colour set beside it: a class and an sx rule carry the same s...
Make the pinned-app eyebrow actually white
The previous attempt overrode .operator-label's ink with a class-qualified sx rule, which could never have worked: tailwind.config.js sets `important: true`, so...
A pinned app's chip launches the app, not its settings
The leading chip of a pinned band was linking to the app's settings landing — the same place its first entry already goes. It now borrows that app's nav option ...
Restore CRLF endings on the three session files
The previous commit rewrote them with LF, inflating a ~56-line change into a 700-line whole-file diff. Content is unchanged; this puts the endings back so the h...
One writer of the *** TTL, and it is the activity tracker
/api/user-info wrote the session TTL straight to Redis, bypassing every service-side rule — and useUserInfo polls it every five minutes forever, so that one lin...
Reading a session no longer renews it
The expiration on the Redis record IS the session; when it lapses the member is signed out. This service holds its own copy of KSessionService against that SHAR...
Reading a session no longer renews it
The expiration on the Redis record IS the session; when it lapses the member is signed out. This service holds its own copy of KSessionService against that SHAR...
Reading a session no longer renews it
The expiration on the Redis record IS the session; when it lapses the member is signed out. This service holds its own copy of KSessionService against that SHAR...
Make reads read-only, leaving one writer of the *** TTL
The expiration on the Redis record IS the session: when it lapses the member is signed out. Reading a session no longer touches it. Only genuine interaction ext...
Sync the organisation calendar to phones, without opening a hole
The shared calendar has no owner, so the account-scoped DAV listing could never return it: company events were plainly there on the web and invisible on every p...
Refresh planner statistics after the weekly GeoLite rebuild
YugabyteDB does not auto-analyze, and the staging+rename swap replaces geolite_blocks wholesale -- so after every Sunday sync the planner had no statistics for ...
Add and backfill accounts.date_created
Hibernate cannot add this one itself: ddl-auto emits it NOT NULL, which CockroachDB refuses on a table that already has rows. So it is added nullable here, back...
Return the account's creation date to the account list
buildAccountSummary never emitted dateCreated, so the field the list's Date Created column reads was simply absent from the response. Now that Account records t...
Record when an account was created
Account carried no creation timestamp at all — not a null column, no column. The account list has a Date Created column and orders by it, so that column has bee...
Pin an Enabled App to the home launchpad
Each Enabled Apps topic now carries a pin toggle, in the Settings sub-option menu and on its section header on the settings landing page. A pinned app gets its ...
A live chat is claimed through the popup, never allocated
Routing chat requests through round-robin was the wrong model and it broke the thing it was meant to fix. An assigned ticket leaves the claimable queue (Support...
Geolite range index must be ASC, not YugabyteDB's default HASH
The IP lookup is a range scan: WHERE network_start <= ? ORDER BY network_start DESC LIMIT 1 YugabyteDB partitions the LEADING index column by HASH unless tol...
Geolite range index must be ASC, not YugabyteDB's default HASH
The IP lookup is a range scan: WHERE network_start <= ? ORDER BY network_start DESC LIMIT 1 YugabyteDB partitions the LEADING index column by HASH unless tol...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.