ADP takes per-org credentials, not a platform registration
ADP shipped classified PLATFORM_OAUTH on the strength of ADP publishing a Marketplace ISV model, where Kamo would hold one partner client id, secret and certifi...
On screen is not the same as read
A support conversation was marked read the instant its window loaded, so the one kind of window that must NOT count as read — the kind that raises itself, from ...
Unread means the requestor spoke, or the ticket just became yours
The badge counted "live tickets nobody has picked up, plus the ones I hold" — message-blind, and it only ever fell when a ticket reached a terminal status. So i...
One definition of an unread support conversation
The navtop badge and the ticket list have twice drifted apart by answering different questions. findUnreadStateForViewer is the single answer both now read, so ...
Widen the expanded rail's scrollbar to 20px
At 10px it was slimmer than it needed to be — the rail has the width to spare, and a wider bar is a wider grab target. The thumb's transparent inset doubles wit...
Give the expanded rail a scrollbar instead of end arrows
The up/down arrows exist because a collapsed rail is 80px of tile with no room to spend on anything else. Expanded, the rail is 248px and the options read as a ...
Use the real payroll vendor logos on the provider cards
Adds the ten vendor assets and renders them in place of the lettermark placeholders. These are WORDMARKS, not square icons like the email provider set, and the...
The needs-attention mark stops lying when you open a ticket
Clicking a row cleared its amber dot while the badge beside it stayed put, and a refresh brought the dot back. The badge was right and the dot was wrong. A tic...
Mark the items the navtop badge is actually counting
The badge read "5 support items need attention" and the list marked none of them. Both numbers were right about their own question and neither was the question ...
The ticket list marks the same items the badge counts
countForMember sized a set it built privately, so the list had no way to ask which tickets were in it and approximated instead — it flagged unread chat messages...
Carry whether a ticket is one of the items the support badge counts
The navtop badge and the ticket list were answering different questions and nobody could tell. The badge counts items awaiting attention — overwhelmingly ticket...
Payroll Provider Setup, and map people to payroll on enrollment
Replaces the placeholder HRS Overview tab with the real thing. The tab is now "Provider Setup" and the screen is "Payroll Provider Setup", following the email p...
Index and REVOKE the payroll-provider tables
What Hibernate cannot express for the new hr_timecard_payroll_* tables. Two REVOKEs. PayrollDispatch and PayrollSyncIssue refuse updates and deletes in Java th...
Size an attachment by what the provider's API actually carries
The composer held every social DM to a flat 10 MB, which is Telegram's photo limit applied to four providers that do not share it. Each now gets its own ceiling...
Nightly payroll-provider sync job
Drives TimecardService's **************** which syncs every organization whose next sync is due. Cron lives here rather than in TimecardService because that de...
The authorization boundary for payroll provider setup
Relays the new **************** surface and enforces who may reach it. TimecardService holds the engine and no session; this holds the session and decides. Secu...
The payroll-provider domain behind the timecard engine
Adds the persistence spine for connecting an organization's timecards to a third-party payroll system: the connection and its encrypted per-org config, the OAut...
Side a message by which side sent it, not by who is reading
A support conversation has exactly two sides, and they are not "me" and "everyone else". One side is the person who asked for help — always exactly one member. ...
A ticket can never be answered by the member who raised it
A support conversation has two sides — someone asking and someone answering — and nothing enforced that they were different people. A member holding ACCEPT_SUPP...
A wall at "now", and a card that explains a cancelled punch
Nobody can record time they have not worked yet. Dragging an edge, sliding a whole span, nudging with the arrow keys, the magnet, the right-click block and the ...
Drop the speed dial heading
The tab is reached by its own icon and the rows say what they are, so a title and a sentence of explanation above them only cost vertical space in a panel that ...
Give the support and social windows the member chat's composer
A support ticket and a social DM are the same thing as a member chat — a media session with messages on it — but they were answered through a bare text field. A...
Send an outbound attachment to the endpoint its type belongs to
Every social attachment went out through the adapter's sendImage, which on Telegram is /sendPhoto. A PDF or a zip posted there is rejected by the provider, and ...
Department speed dial hung on Loading forever
The loader effect listed departmentsState in its deps and set it to 'loading' on entry, so React tore that effect instance down immediately, the cleanup flipped...
Make Delete actually delete, and select exactly one pill
Delete did nothing. The handler was hung off the pill's focus, and the pill never had focus: preventDefault() in the pointerdown handler — which is there to sto...
Speed dial lists the org's department extensions
The tab shipped six invented entries — Sales Team on 1001, a Conference Room permanently ringing — so dialling one reached whatever happened to answer that exte...
Widen chat, social, ai-chat and compose windows to the support size
The support ticket window opens at 460px while member chat, social chat, AI chat and the email composer all sat at 360-420. Same dock, same kind of content — th...
Show unseen messages on every queue row, not just joined ones
The badge and the row tint were already here — they were simply never populated for most of the list. getUnreadFor read only the live per-session unread map, wh...
Stamp unseen counts on the ticket list, and keep the queue live
getTickets now bulk-loads an unseen-message count per row for the caller, beside the visitor rows it already loads that way, and getTicketDetail computes its ow...
Count unseen messages for a viewer who is not in the conversation
Unread has always been derived from the viewer's own MediaSessionMember row, so a conversation they had not joined produced no count at all. On a support queue ...
Delete, magnets, a right-click menu, and a clock that asks which
Six additions to the period graph and the nav clock. DELETE removes the focused pill. Backspace too, because on a Mac the key labelled "delete" IS Backspace an...
Relay the punch void
Passthrough for POST /punches/{id}/void, the editor's delete key and the machinery behind merging two adjacent spans. Same authority as /correct, and for the s...
A punch can be voided without being deleted
Four nullable columns and a second write-once transition on TimecardPunch. A void says the punch should never have existed — a double tap on the clock, an aband...
The void columns on hr_timecard_punch
Mirrors four new nullable columns on the TimecardPunch entity. A void is neither a delete nor a correction: it says the punch should never have existed — a doub...
Size the org logo to 80% of the join column
The join screen capped the branding logo at 40px tall with objectFit contain, so it rendered letterboxed inside the 300px column and looked undersized no matter...
Repair the kamouniverse backup, broken since the Yugabyte cutover
Renaming the dead CockroachBackupMissing alert in the previous commit made it fire immediately: no universe backup had succeeded since the 2026-08-09 migration....
Retire CockroachDB now that YugabyteDB owns the data
CockroachDB v24.2.4 was pinned to the last release under the old free BSL terms and was replaced by YugabyteDB on 2026-08-09. Nothing has connected to it since;...
One Plans & Billing screen instead of two that each answered half
The platform had two billing screens. /settings/account?tab=billing answered what the organization pays; Plans & Billing on the member profile answered what a p...
Point at YugabyteDB, and stop `mvn test` migrating production
The datasource still named port 26257. That is the retired CockroachDB, which is still running and still answering, so a run against it succeeds completely and ...
Show the market onboarding panel when no CRM markets exist
/leads rendered the lead grid with an empty, disabled market tab bar for an org that had never configured a CRM market. It now shows the same onboarding panel /...
Start again, and stop the billing migration deleting its own index
KamoInitializer could not start at all. Hibernate detects CockroachDialect, whose supportsInsertReturning() and **************** are both false, so an @Id carry...
Open at the newest message, and scroll up for the rest
Opening a long conversation showed the oldest loaded message first and then travelled down to the present while the member waited. Both chat surfaces scrolled t...
The period graph, and a timecard you edit as a draft
Replaces the one-day timeline on **************** with a whole pay period, and changes what editing one feels like. The pills carry no text. They used to carry...
Relay the editor's period range
Passthrough for GET /members/{id}/range, the period graph's read. from/to are forwarded only when present so TimecardService can resolve the member's own pay pe...
Raise a support conversation when somebody replies in it
Closing a support window is not leaving the conversation, but it was treated as if it were. ChatNotificationListener returned early on every SUPPORT_TICKET noti...
One conversation, many hands — join on open, name every message, re-open on a reply
A support ticket is worked by whoever is helping, not only by whoever it is currently stamped with. Assignment decides who the work is offered to and whose list...
Quote the feed queries' aliases, and stop counting your own support messages against you
Two independent defects in the Chats feed, both invisible from the Java side. The native chat/support/social queries aliased columns in camelCase without quoti...
Linkable tabs, a working Add Member, and a full history tab
Every tab on /account/view/{id} is a real route now — /members, /history and the rest are links somebody can send, bookmark, or land on. Old ?tab= links redirec...
Create ACCOUNT_HISTORY
Mirrors the AccountHistory entity in kamo-shared-library. Written explicitly rather than left to ddl-auto because the defaults matter: ID and DATE_CREATED carry...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.