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 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 ...
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...
Address an org's OAuth callbacks to its own domain
An OAuth redirect URI is held by a third party. A member pastes it into Google's or Microsoft's console, and from then on the value the service sends on the aut...
A detection rule an org cannot switch off, and the letter it sends
PROGRESSIVE_LOGIN_LOCKOUT joins DetectionRuleType. Every other value on that enum describes something a tenant opts into; this one describes a control SecurityS...
Share the delivery consumer, so a second pod is not refused
Caught in production the moment services went to two replicas on 2026-09-04. Every pod carrying this class logged, once, at boot: [Webhook] Failed to subscri...
A distributed lock, so a scheduled sweep can survive a second replica
There is no ShedLock anywhere on this platform and there never has been. All 97 @Scheduled sweeps across 18 services are correct only because each service runs ...
A logo overlay for the sign-in and sign-up sites
Two more columns in the vocabulary home_logo_overlay already established: a token, not a hex, so an org that rebrands keeps an overlay matching its new colours....
Add a queue-group subscribe, so work is shared rather than pinned to one pod
Companion to subscribeEphemeral. Two different jobs, two different consumers: subscribeEphemeral every pod must see every message (anything ending in a STO...
Restore this file's CRLF line endings
The previous commit rewrote it with a script that normalised it to LF, turning a 90-line addition into a whole-file diff. No content change.
Add an ephemeral subscribe, so more than one pod can serve a conversation
A durable push consumer admits exactly ONE subscriber. The second pod to bind the same name is refused with [SUB-90012] Consumer is already bound to a subscript...
The two reads the platform Users list is built on
A paged, searched finder over USERS, and a batched membership count. The finder is unfiltered by isFake on purpose. Reviewing and clearing that flag is part of...
A right for user accounts, and a parked address for changing one
Three additions, all of them the shared half of work that lands in SecurityService and kamo-internal. PlatformRightType.MANAGE_USERS, appended. A user account ...
Gate Publish on god ELIGIBILITY, not an open window
The Publish flag shipped gated on godModeActive — GD plus a deliberately opened 30-minute break-glass window. Every god-eligible operator then found the switch ...
System_bug.published needs a column DEFAULT, or it is never created
The field was declared `@Column(nullable = false)` with no columnDefinition, so ddl-auto emitted `ADD COLUMN published boolean not null` — which Postgres REFUSE...
Three public transactional templates, and the org patch keys behind them
The careers site now writes to people who have no account, which is new for this platform: every other transactional template goes to a member. Three canonical ...
The Publish flag on a system bug, and the query rule behind it
A system bug already crossed the tenancy line in one direction. This adds the second visibility rule on top of it: a report a god-mode operator filed privately ...
The schema behind apply-first, the network, alerts and retention
member_id on hr_job_application becomes NULLABLE, which is the whole of apply-first: five steps stood between pressing Apply and answering the first question, a...
An empty platform registration is not "connect with Kamo's app"
isPlatformEnabled now also requires a client id, matching what OrgOAuthClientController reports as platformEnabled. Without that the meetings screen and the bri...
An organization can bring its own OAuth2 client
Until now the client id and secret for every OAuth provider came from one platform-wide registration, so an org could only connect what Kamo had registered — an...
The two columns a public job board needs
publish_externally decides whether an advert reaches careers.<apex> at all. It is a separate axis from status: an internal-only posting is fully AVAILABLE and m...
Careers is a platform alias, not an org's own
OrgResolutionService.byHost reads a leading label that is not a known platform alias as an ORGANIZATION's alias within the domain behind it, so without this ent...
The schema behind a row of footer badges
The email signature footer held one image, named by footer_image_source and sized by a percentage. It now holds a ROW of them: the three built-in sources plus a...
A dispatch names the terminal it is running in
Send to AI is hosted in a Remote Terminal now, so the answer has to say which one — the console opens that window, and opening it is what starts the session. B...
Job postings and applications — the schema behind /hr/careers
Two entities, six enums and two repositories under com.kamo.z.shared.hr.careers, plus the three shared types the feature had to widen. JobPosting is three grou...
Wire the accept-gate mock into the FFA spend test
acceptNextLead consults LeadAcceptEligibilityService before it reaches any of the credit logic these cases are about, and the test builds the service by reflect...
Widen the accept gate, obfuscate masked contact info, let free-for-all leads be claimed
Three related changes to the lead-assignment core. **The accept gate now asks two questions, over every assigned lead.** LeadAcceptEligibilityService replaces ...
Record a dev-machine terminal in the access log
A member opening a shell on desktop-1 now has an event type of its own. That machine holds a cluster-admin kubeconfig and its accounts have passwordless package...
Send an account's company alongside its contact for customer pickers
Six customer pickers read AccountSummaryDTO — the order editor, the service-job form, the subscription detail and wizard, and the pipeline signups tab — and eve...
Add JUST_CALL as a phone provider type
Appended at ordinal 5. **************** is EnumType.ORDINAL and carries no CHECK constraint, so appending is safe and inserting anywhere else would silently re-...
Comping an account now drops its cached entitlements
SecurityService answers /api/security/entitlements/me out of Redis with a 15-minute TTL, invalidated by messages on kamo.entitlements.invalidate. Every self-ser...
Editing a draft order's seats or cycle now reprices it
updateDraftOrder set seatCount and billingCycle and saved. Both are price drivers, and neither the order's money fields nor its line items were touched, so the ...
A paper colour per block
**************** holding a palette ID — "lemon", "glass" — and never a hex value. Stored the way notes and launchpad panes store theirs, for the reason that ma...
A comped order now grants the add-ons it sold
Comping an account granted the plan and nothing else. provisionCompSubscription copied plan, seats, cycle, source and status onto the AccountSubscription and ne...
Bind content_json as JSON, or every insert fails
REPORTED: "Failed to create sales script". The server said exactly why: column "content_json" is of type jsonb but expression is of type character varying ...
IMPERSONATE, so an operator can enter a tenant as one of its members
Appended to PlatformRightType rather than inserted: getSortOrder() returns ordinal(), so a right added in the middle renumbers every right below it in the conso...
An archived market's scripts leave the Interaction Center
findMarketIdsWithActiveScripts asked only whether the SCRIPT was active, so a market the organization had retired still appeared as a sub-tab for as long as any...
The script a member reads on a call, as ordered blocks
Two tables and two rights. SALES_SCRIPTS hangs off a lead market the way a LeadVendor does; SALES_SCRIPT_BLOCKS holds the parts, each one a Lexical document plu...
PdfRedactor, so a covered word is actually gone
A filled rectangle drawn over an account number hides it from the eye and from nothing else: the glyphs stay in the page's content stream, and a copy-paste or a...
Flush the link, so a refused write reaches the catch that reports it
link() is @Transactional(REQUIRES_NEW), so its transaction commits in the proxy after the method body returns. A plain save() of an entity whose id is generated...
Return the contact's country, state and time zone
LeadDTO carried neither Lead.country, Lead.subdivision nor Lead.timezone, so the three fields were write-only: UpdateLeadRequest accepted them and LeadService w...
An address is several lines, so send it as several lines
Splitting the suite out of addressLine was not enough — it left the street and the city sharing a line with the unit printed beneath them, which is worse than t...
A setup fee on a single-price quote, and the suite on its own line
Two things a quote could not state. A one-off setup fee lived only on QuoteOption, where each alternative carries its own. A flat sheet has no option to hang o...
Expose repricing as a static so a backfill can reuse it
Quote money columns are written, not computed on read, so an engine correction leaves existing rows carrying the old arithmetic until something saves them. A mi...
Pin the quote letter's dotted templateKey, as the HR family already is
The constant is not the templateKey. CanonicalTemplateLoader.load derives only the FILENAME from the constant (QUOTE_SENT -> quote-sent.json) and reads template...
Restore the timestamp that every lead association depends on
A method was inserted between @CreationTimestamp and the dateCreated field it annotates, so the annotation bound to subject() instead. These entities are read b...
A unit price is a MONTHLY rate, so the term multiplies it
The engine treated an option's line-item sum as the price for the whole billing period and divided down to reach a monthly figure. It is the other way round. P...
Let an event invite its guests, and put it on their calendar
Saving an event told nobody. The guest list was materialized and the audience stored, and the people on it heard nothing until a reminder fired — which reaches ...
A three-year deal totals three years, not one month of it
Two faults, both showing a monthly figure where the term's value belonged. 1. The headline took periodTotal + setup. On a MONTHLY option committed to 36 mon...
Je, unaona nini kuhusu usafiri?
Kila moja ya hizi updates ardhi katika nafasi yako ya kazi moja kwa moja. Kuanza bure na kuangalia kukua wiki baada ya wiki.