Stop a failed welcome email from destroying the member
MemberCreationService documented steps 4-6 as best effort, and for the emails it was not true. sendVerificationEmail is @Transactional, so it joined the member-...
Restore the transaction the attachment overload left behind
baf3c88 added an attachment parameter to sendToUser by moving the whole body into a new six-argument overload. The @Transactional stayed on the five-argument si...
The composer is a sub-page, and collapsing no longer eats your text
Four reported problems, one of them destructive. COLLAPSING A PART DESTROYED IT. Collapse UNMOUNTED the editor, and KbArticleEditor is seeded once from a value...
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 was disabled on every row
Gated on canEnterOrg, which is the VIEWER's own membership — and a platform operator has no membership in the organizations impersonation exists for. So the but...
Archived is a SUB-TAB of Sales Scripts, and pad the page
Two bugs, both mine. ARCHIVED WAS A PEER TAB. Active Scripts and Archived Scripts are two views of one screen, not two screens, and they belong as sub-tabs the...
Pin direct_media=no, and correct the "(+) cannot override" claim
FreePBX's PJSip driver defaults direct_media to YES (generateDefaultDeviceSettings, PJSip.class.php:210) and the GraphQL addExtension mutation has no field to o...
Stop an attachment holding an IMAP connection open forever
getAttachment returned Jakarta Mail's lazy IMAPInputStream straight out of withStore, which puts the Store back in the pool the instant the lambda returns. Two ...
Add 8777 to the WebRTC endpoint roster
Aiden Perry's extension was created from KamoCRM, which cannot write this file — so it registered over WSS with no DTLS or ICE and its calls would have carried ...
Show a pasted screenshot instead of a broken image
Jakarta Mail hands back a MIME header exactly as it arrived, folds and all: RFC 5322 lets a mail header run over several lines by breaking it at a CRLF followed...
Reconcile per-extension WebRTC blocks instead of hand-editing a roster
An extension created through FreePBX's API gets no WebRTC block, so it registers over WSS and then has no DTLS or ICE — the browser softphone shows a connected ...
Apply FreePBX config after every write, or the extension never reaches Asterisk
Extension 8777 (Aiden Perry, kamocrm) was created from KamoCRM on 2026-09-02 and never rang. The FreePBX GraphQL `addExtension` mutation had written `users`, `d...
Impersonate, so an operator can open an org as one of its members
Shown beside Enter on every /network view for a platform administrator holding the new IMPERSONATE right. A different right from SYSTEM_USER and deliberately no...
Enter a tenant organization as one of its members, from /network
A second door beside the same-org god-mode one. Gated on the new IMPERSONATE platform right via @RequirePlatformRight, where the existing door is gated on the s...
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...
The owner can see the tab that grants the right
REPORTED: "I am also an admin and I don't see the new Sales Scripts tab under /settings/features/crm." Correct, and it was invisible to EVERY organization, not ...
Admit the organization owner, or nobody can turn this on
A brand-new right is granted to NOBODY on the day it deploys. These two are deliberately absent from **************** — seeding them from an existing right woul...
Un-skip the parity guards, and pin without blocking the page
TWO PARITY TESTS HAVE NEVER RUN. Both resolved RoleRightType.java through **************** — that is ~/Java/kamo-shared-library, while the checkout is *********...
Price every line against the seat count, everywhere it is shown
The row Amount under 'What you are quoting' was still previewLineTotal, so the quantity cell showed the quote's seat count while the Amount beside it priced the...
Reprice the live totals when the seat count changes
A line that opts into the quote's shared 'Seats / units' driver is priced against it, not against its own quantity. The editor's live rail summed the lines with...
Putting a script back into use should not need a password
ConfirmationDialog always makes you type a challenge word. That is right for archiving — the script leaves every agent's panel mid-shift — and right for deletin...
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...
Mirror the two rights, and parent MANAGE on VIEW client-side
checkRight matches plain strings, so the gates worked without this — but the security role editor cascades grants through RIGHT_PARENT, and with the pair missin...
Author scripts in CRM settings, read them on a call
Two CRM settings tabs behind MANAGE_SALES_SCRIPTS — the live scripts and the archive — plus a Sales Scripts tab in the Interaction Center behind VIEW_SALES_SCRI...
Serve the scripts an agent reads on a call
/api/kb/sales-scripts, gated on the two new rights. Reads take VIEW_SALES_SCRIPTS or MANAGE_SALES_SCRIPTS, writes take MANAGE. SessionHelper.hasRight fails clos...
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...
Move a message's controls to the side facing the conversation
The hover rail sat on the message's outer edge — right for your own messages, left for everyone else's. It now sits on the inner side, the one facing the middle...
Redact when the document is served, not only when it is signed
The redaction was applied at flatten time, which is after signing. Everything before that — the whole signing session — was served the raw source: fetchDocument...
Open a workspace on its OWN domain, not the host the sign-in was served from
A member who signed in on login.kamocrm.com, picked a workspace from the chooser, and landed inside it — on internal.kamocrm.com, wearing the platform's hostnam...
An em-dash in a comment, not its escape
— is processed by javac before the file is even tokenised, so it compiled to the same character it renders as here — but the source read as the escape.
Fill a rectangle, honour a highlight's opacity, and redact
Three things about the way a prepared document renders the designer's marks. A rectangle was drawn as an outline, so the fill a member now picks to cover somet...
Put the designer's annotations into the signed document
flatten() applied blank pages, rotations, modifiers, field values, page order and certificates. It never drew the annotations. A rectangle, a highlight or an ar...
OrgBranding gained a sixth component and this stub kept passing five
Broken on main since e9faf11. The image build runs mvn package -DskipTests, so nothing has been compiling the test tree and no red build ever said so — but it m...
Draw a rectangle's fill in the signer's view
The designer can now give a rectangle a fill and an opacity, which is how a member covers something up. This renderer drew fill="none", so the signer saw an emp...
Make the rectangle tool something you can hide things with
The rectangle tool has been in the annotation toolbar all along, but it drew an outline: fill="none" in every renderer. There was nothing to hide anything with,...
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...
Report a failed association at WARN, not at DEBUG
Lead association swallows its failures on purpose — a problem attaching a message must never break the sweep that was reading the mailbox — so the log line is t...
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...
Print the whole quote, not just its first page
The print stylesheet reset the Dialog's container and its Paper to static flow but left the Dialog ROOT alone — the element this component labels quote-print-po...
Keep an option card and its lines off a page break
The per-option line table was added without the page-break rules its sibling table has, so a long option could be cut across pages mid-row.
Link the articles a draft names
The authoring convention is to name a sibling article by putting its title in italics — "Read *Security Roles Explained* before building them". 181 spans across...
Rebuild against kamo-shared-library HEAD
Ships the lead contact location fix, which lives in the shared library: LeadDTO now returns the lead's own country, subdivision and timezone, so the three Prima...
Save the Primary Contact country, state and time zone
The Primary Contact card's country, state and time zone are top-level Lead fields — neither members of primaryContact nor of addressMailing — and the two wire b...
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...
Hold withTermsUnchanged to carrying every field
The method rebuilds UpdateQuoteRequest positionally, so a field added to that record and forgotten here is dropped from every save by a member without EDIT_QUOT...
Stop dropping setupFee on save, and unbreak the build
withTermsUnchanged rebuilds UpdateQuoteRequest positionally for a member who may not rewrite the terms. setupFee was added to that record and not added here, so...
Read either address shape so the block cannot go blank
Reading only the new addressLines makes the address disappear when the API has not been rebuilt yet and is still sending addressLine plus addressDetails. A blan...
Read either address shape so the block cannot go blank
Reading only the new addressLines made the address disappear from the printed quote outright, because this shipped ahead of the API that sends it and a server s...
Rebuild against kamo-shared-library HEAD
Ships the quote document work, all of which lives in the shared library: - a one-off setup fee on a single-price quote (commerce_quotes.setup_fee, which is a...
Print the address as the lines it is written on
Splitting the suite into its own field left the street and the city still sharing a line, with the unit printed beneath them. The address now arrives already sp...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.