The mortgage application sits in a pane, under its own ribbon
Ports the shell from kamo-login and kamo-register: a card inset 15px from the browser edge with a 14px radius, a soft shadow, and a red ribbon across its top-ri...
Send a new starter to the board, and open the advert they came for
A team member whose employment is not ACTIVE is already confined to the restricted tier, and the home screen already bounced them somewhere. It now matters WHER...
Carry the applied-for job onto the account being created
The careers board sends candidates to /team?jobId=<uid>. That uid is parked in an apex cookie as before, but it is now READ BACK at submit and sent in the regis...
Retire applying without an account, and notify both sides of one that lands
The public board's Apply button is now a link into registration, so this drops the endpoints behind the old form: /{orgRef}/apply/{uid} in both content types, /...
Record the advert a candidate registered for, and welcome them when hired
Registration on the /team path now accepts appliedJobId — the uid kamo-register parked when the careers board sent the candidate to /team?jobId= — and writes it...
Apply from inside the workspace, and say so to both sides
The public careers board's Apply button is now a link into registration, so every application is filed against a session. Two fields carry the candidate from th...
The chat bubble knows the applicant's name as they type it
Somebody who has just filled in "First name" and opens the chat to ask a question should not be asked who they are a second time, and the agent picking that con...
Seat every screen in the same pane, under a per-step ribbon
Ports kamo-login's shell: every route now renders inside .kamo-pane, a card inset 15px from the browser edge with a 14px radius, a soft shadow and overflow:hidd...
The support bubble uses the key its product was published with
The chat key is per-PRODUCT, not per-deployment. A lender picks a web chat integration when they publish an application, and the public endpoint resolves that i...
Publishing an application is a decision, and it comes with a chat
**Vendors & Products no longer prints a link for every product it has.** It printed one for every mortgage product the moment that product existed, which meant ...
Publish an application deliberately, and give its page a chat key
`PUT **************** publishes or retires a product's public page and records which web chat its support widget speaks through. Enabling without one is refused...
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...
A product's application is published, not assumed
`LeadVendorProduct` gains `applicationEnabled` and `webChatIntegrationId`. A product existing in the CRM is not a decision to publish a form the whole internet...
Twenty-two languages, and the chat bubble
The application is the one surface on this platform whose reader has never signed in to anything and may not read English. It now offers the same twenty-two lan...
Fade the glimmer out at two corners, and cap it at half
The sheen now dies completely at the top-right and bottom-left corners and peaks, at half opacity, over the top-left and bottom-right. Each revolution reads as ...
Run a glimmer round the pane's gap every five seconds
A conic gradient centred on the pane, spun once per --glimmer-period, painted twice: a sweep filling the 15px gap, and a 2px rim on the pane's own border. The r...
The mark returns to the rail, governed by the Apps Site Logo Overlay
Reverts the relocation in 47e6e39. The invisible-logo problem it worked around is real — a mark drawn in the company's own brand colour can vanish on a rail pai...
One scrollbar, for every scroller in the app
The terminal tool window had a scrollbar worth keeping — 32px, with a button for each END of the scrollback as well as the usual step arrows, because in a long ...
Send the first-time-buyer answer with the application
The question was on the form and the answer went nowhere — there was no column for it until the shared library gained one. It changes which programmes are worth...
Serve the public mortgage application, addressed by product
Three unauthenticated endpoints under **************** plus the CRUD behind the organization's own state licences. **The organization is named by the PATH, nev...
Record whether a borrower says this is their first home
A first-time buyer is eligible for programmes nobody else can use, so a lead that does not carry the answer costs the officer a call to ask it. The public appli...
Rebuild the mortgage application as a real, white-labelled form
The page this replaces was a prototype wearing a production URL. It posted to `http://localhost:8082`, so no application it ever collected reached anything. It ...
Seat every screen in a rounded pane under a corner ribbon
Every route now renders inside .kamo-pane: a card inset 15px from the browser edge with a 14px radius, a soft shadow, and overflow:hidden, so a page's own conte...
Give a lender somewhere to put the truth about itself
The public mortgage application page composed its legal footer out of nothing — a hardcoded California licence, an invented NMLS number, a fictional address — a...
Publish the policy addresses, the real state licences, and a link per product
Three things the public loan-application site could not previously be told, and one it was told wrongly. **Company Info gains Privacy Policy Address and Terms ...
Start a coder session from the Linux Desktop menu, at a model and effort you keep
The icon already opened a remote desktop and a remote terminal. The third thing people actually go to that machine for was reachable only by opening the Interac...
Finish the personal calendars panel, and put it where a member can reach it
PersonalProviderSection was scaffolding that nothing rendered, and it would not have worked if it had: it read GET /member/{id}, a route that did not exist, so ...
Let a member connect their OWN calendars and contacts
The personal calendars panel asked for GET **************** which was never mapped -- so it failed on every open -- and for Google and Microsoft it had no way t...
Gate the user account on platform rights, not on which org owns it
Authored in a concurrent session on this machine and committed here at the user's request; recorded so the provenance is not guessed at later. The user account...
Give every provider's folder tree the same shape before any client sees it
Reporting a folder's SPECIAL-USE attribute was not enough, because a server only sends one if it is configured to — and the KamoMail Dovecot in production sends...
Make Connect via OAuth on the CalDAV/CardDAV tab actually connect
The button raised alert('OAuth flow not yet implemented - configure credentials manually via the API'). It now opens the real Google Workspace / Microsoft 365 c...
Connect Google and Microsoft calendars over OAuth, on the app the mailbox already uses
The CalDAV/CardDAV tab's Connect via OAuth button raised an alert saying the flow was not implemented and to configure credentials through the API. It now runs ...
Retry the member reads that fail first after a DDL
The member-by-id load is close to the first statement an authenticated request performs, so a catalog-version bump hits it before it hits anything else. That is...
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...
Retry a transactional method in a NEW transaction, and stop rights failing open
Two fixes that only make sense together. @RetryOnDbConflict retries a method OUTSIDE its transaction, so each attempt begins a fresh one. Retrying inside the t...
Say what the Free plan does not charge, beside what the paid ones do
Now that Starter and Business each carry a one-time charge on the card, a Free card that says nothing about it invites exactly the wrong inference — the reader ...
Expose what a seat change will cost before it is made
Raising a subscription's seat count now raises an invoice — a per-seat startup fee follows the person, so ten hires on a $99 plan is $990 and a large account ru...
Send an organization's mail as its own domain when its SPF says we may
The From address was chosen by asking who hosts the organization's MAILBOXES. That is a different question from the one that matters, and it was wrong in both d...
Let the console ask what a seat change costs before making it
changeSeats now raises an invoice for the startup fee on new heads, which turns a number in a dialog into a charge — ten hires on a $99 plan is $990, and on a l...
Record which domains authorize us, so their mail can go out as them
Transactional mail currently picks its From address by asking who hosts an organization's MAILBOXES. That is the wrong question. Whether we may send as chinilaw...
Record whether a domain's SPF authorizes us to send as it
Two columns beside the ownership and SSL flags, answering the third DNS question about a domain: may we put its name on the From line of the transactional mail ...
A put-away terminal's hexhead says whether it wants you
A member with six terminals minimized has one question about each: is that one waiting for me? The head now answers it in a colour. green working — nothing ...
Carry a terminal's activity state to the console
TerminalSummary gains `state`: busy, or waiting for its member. Read off the session's transcript — neither of which is knowable from here, which is why it trav...
Give a head the app's own tooltip, placed from where it sits
The label was the browser's native `title` box: unstyled, and put wherever the other 291 hover targets in this chrome use, arrow and all. A head is not a norma...
State the startup fee, and stop claiming there isn't one
The site said "$0 setup fees" three times in the pricing page's metadata and listed "No mandatory onboarding fees" as an advantage over HubSpot. All of it is no...
Show the startup fee before it is charged, and where a deal overrides it
calculate-price has always returned a setupFee and the order wizard printed it nowhere, so a charge that runs to four figures on a large order reached the custo...
Seed the $99 per-seat startup fee on every paid tier
The loader passed null for setupFee on all four plans, so a freshly seeded environment priced onboarding at nothing while production charges $99 a seat — the ki...
Bill the startup fee on both routes to a plan, and record negotiated terms
Two routes reach the same plan and only one of them was going to charge the new per-seat startup fee. A sales-written order bills through SubscriptionService, w...
Charge the startup fee, per seat, and let one customer out of it
The startup fee has been half-built for as long as the model has existed. SubscriptionPlan carried a setupFee, SubscriptionOrder carried a setupFee, OrderLineTy...
Retry the database errors Yugabyte expects a client to retry
Applying DDL bumps the catalog version, and every physical backend still holding the old one fails its next statement with SQLState 40001 — "schema version mism...
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.
