Restore the image-proxy and wallpaper work 266357d0 reverted, and land the permissions change it was meant to carry
266357d0 was built with the wrong index. Its tree came from a stale local HEAD rather than from the staged one, so it carried none of the permissions work its m...
A group per app, a console to grant them, and master notes as widgets
Three changes that turned out to be one. THE HOME PAGE. "My Master Note" and "Our Master Note" were bespoke panes rendered straight out of page.tsx, gated on n...
Stop force-cache pinning "this org has no wallpapers" forever
The console backdrop and the member Wallpaper editor both read the tenant's `config.json` for `backgroundImageCount`, and both asked for it with `cache: 'force-...
The grid fetched every page twice, then blanked itself to do it
Opening /leads showed the rows, dropped back to a loading state, and showed them again. It was fetching twice. STOMP raises onConnect on the INITIAL connect ex...
Stop the image proxy serving an hour-old copy of another org's branding
Theme asset URLs are stable and say nothing about their contents: an org's simple logo is always `<alias>/img/logo.svg`. Every org is also seeded with the base ...
Plans for BL2 (the KAMO_NATIVE invoice) and SW8 (in-home contract compliance)
Choose the KamoDesktop resolution, and stop hiding why a launch failed
The remote desktop tracks the browser window and always has, so there was no way to have MORE desktop than window. A picker now sets a fixed size, stored per br...
Check in the useUserInfo shared-store handoff
The "no .md in repos" rule is retired. This handoff doc had been kept local-only; it belongs next to the code it describes.
Format every phone number a signature prints
The company lines went out as whatever the org record stored — "5555555555" — next to a member's line in the same block, so a signature could show a formatted n...
One formatter for every number, US and international alike
The formatter every phone surface shared kept the LAST TEN DIGITS of whatever it was handed and dressed them as a NANP number, so "+44 20 7946 0958" rendered as...
The billing proxy route, and MANAGE_BILLING_PROVIDER's four TS mirrors
Task 8, the kamo-internal half. Six mirrors of the rights architecture; four of them live here. 3. app/lib/rightsHierarchy.ts MANAGE_BILLING_PROVIDER ...
`?bug=<id>` opens that report's drawer
Where the new status-change email lands. It has to name the report rather than the tab: the status filter defaults to New, and that email is by definition about...
Glimmering unread cards, a live badge, and per-status counts
THE BADGE IS ABOUT YOU NOW, NOT ABOUT THE QUEUE. It counted every NEW report platform-wide, so it only ever fell when somebody moved a status — reading the queu...
A window only announces its own conversation
The messageReceived handler dispatches chat:message-received, which is what makes ChatNotificationListener raise a chat window, and plays the arrival tone. Neit...
Show what THIS phone server costs, not just the org total
The extensions tab showed only the organization-wide figure, so an empty KamoPBX server displayed "21 billable extensions — $63.00 / month". The 21 were on a Fr...
Detach the chat navigator from the window it maximizes
The conversation navigator was a child prop of ToolWindowShell, rendered inside the maximized window's content box. Clicking a row in it opens that conversation...
Company phone (main/toll free) on a member's signature, with their extension
The Content tab of **************** gains two switches: Company Phone (Main) and Company Phone (Toll Free). Enabled, they print the organization's published lin...
Offer Finish setup on a KamoPBX server that was never provisioned
The card told the admin their phone system was "still being set up" and to wait. That was never going to clear: KamoPBX provisioning runs synchronously when the...
Format the numbers in call history
The History tab was the one softphone surface rendering a raw provider number: the incoming-call banner and every number in SoftphoneCRM already run through for...
Keep long app descriptions inside the Apps & Features row
The collapsed accordion summary already set nowrap + ellipsis on the description, but nothing in the flex chain could shrink: a flex item defaults to min-width:...
Remove the vendors tab, which could only ever show its own mocks
The three pickers classified vendors by id range — 200s appraiser, 300s title, 400s escrow. `commerce_vendors.uid` is INT8 DEFAULT unique_rowid(), about 19 digi...
Tighten the masthead band and sit both sides on the bottom edge
The launchpad's first band was costing more height than it earned. The page container's uniform inset gave the masthead a 40px top gutter, and the header carrie...
Call history names the far side, not the member themselves
A history row exists to answer "who was this call with", and that side flips with the direction. HistoryRow read `fromName || fromNumber` unconditionally, so ev...
ServiceJobApi.createJob, and the back-link builder
Restores the client half of SW6c Task 2, which landed as 157fa714 and was dropped by a history rewrite in a concurrent session; the create screen itself survive...
The service-request grid and client read the real endpoints
Restores the rest of SW6c Task 4, which landed as d58a6aba and was dropped by a history rewrite in a concurrent session; the detail page survived inside bddd361...
One back-link that resolves, instead of four that never did
Every service screen's back button was `/commerce?tab=service`. There has never been a tab with that id: `PipelineView.tsx:69-70` builds them as `market-${marke...
Screenshots AND videos on a bug report
Reporters were describing screen recordings in prose and then posting the file into a chat, because the report form only took stills. It now takes MP4, MOV and ...
Restore the pipeline Quotes tab and wire quote conversion
`POST /quotes/{uid}/convert` shipped in SW4 and nothing has ever called it: `quotesApi` had ten methods and `convert` was not among them, so the endpoint was li...
The browser tab is named after the workspace, not the host
Enter Wienerschnitzel or Wendy's and the tab read "KamoCRM", while nothing inside the workspace said KamoCRM anywhere. ABC Mortgage was correct, which made it l...
Stop the prescribing-disclosure test being flaky
It passed alone and failed inside the full suite. The effect calls **************** and sets state in a .then(), so settling the mock's promise, running the con...
Mirror VIEW/MANAGE_ORDERS and VIEW/MANAGE_PRICING
The client half of closing the order-mutation hole. SecurityService now gates 39 order, pricing and tax endpoints on four new rights; these are the two mirrors ...
Show why the exchange network is unreachable
The outside-records panel could only say "not connected", which reads as an outage to wait out. The server now sends the reason alongside -- this practice has n...
Mirror the EHR stage, and guard the mirror
ServiceType.ts is a hand-maintained mirror of the Java enum and nothing was checking it. RoleRightType has had a parity guard since three EHR rights went missin...
Say what the prescribing safety check did not cover
The panel rendered a safety block only when there were warnings, so a prescription that came back clean said nothing at all -- and this platform has no licensed...
The agreement sample quotes seats at catalog prices
The default agreement template shipped a worked example that contradicted the catalog on every line it mentioned: 50 users on a 25-seat plan, 25 GB of storage p...
The platform save dropped every KamoPBX field client-side
Entering the credentials in the UI reported success and changed nothing. systemConfigApi.update serialises an explicit allowlist rather than the caller's object...
Close out SW6b
Eight departures. The one that mattered was in SW6a: PUT /{uid} accepted totals and sign-off verbatim, which made both of this plan's central rules decorative u...
The six work-order tabs persist
SW6b Task 4. Labour, parts, time, vendors, sign-off and notes were React `useState` on `commerce/service/[id]` and lost everything on reload. They now write to ...
Two icon buttons were announced with no name at all
MUI's Tooltip spreads its aria-label onto its DIRECT child. Both of these wrap the button in a <span> — the standard workaround so the tooltip still fires while...
A failed clinical load must not render as an empty result
Two of these shipped before anyone noticed, and the guard found two more in the newest panel the moment it ran — failing to load what a patient's other practice...
A failed load was rendering as a clinical fact
Two screens turned a fetch failure into an empty result, which on a chart is not a neutral outcome — it is an assertion. The trend graph caught an error and se...
Pin the reason dialog, and let app/patients be tested at all
app/patients/** was in no vitest include pattern, so a test written beside the clinical console's own kit was collected by nothing and would have passed by neve...
Eight clinical reasons were being collected by window.prompt
Stopping a medication, closing an order loop, cancelling an appointment, merging two patients, declaring two records not a match, withdrawing portal access, dec...
Plan SW6b, making the six tabs persist
Labour and parts become ServiceJobLine rows on the job, not ServiceLineItem rows: OrderLineItem.order is optional=false, so a ServiceLineItem cannot exist witho...
Platform settings card, and stop showing FreePBX copy
Three problems on the KamoPBX phone-server page, all mine: - The PHONE_SERVER_ID card was ungated, so every provider — KamoPBX, RingCentral and Teams include...
Close out SW6a
Nine departures. The plan's Spring claim was measured false, its sketched test was near-tautological, and its ServiceTrade-labels-are-new claim was wrong — ther...
The work-order grid reads real service jobs
Every row in this grid was a dead link by construction. generateMockWorkOrders minted ids String(8000 + i), the row click pushed /commerce/service/8001, and the...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.