Split Apps & Features into Available / Not available / Deprecated
One flat list mixed apps the platform offers with ones it does not and ones it has retired, so the operator had to read every chip to tell them apart. The buck...
Load Allowed Apps from an endpoint tenants can actually read
appCatalogApi.list() is platform-owner-only, so on any tenant org the Branch Types editor showed no apps and the save guard — which exists to stop an empty cata...
A tenant-readable app catalog for Branch Types
Branch Types -> Allowed Apps started loading its app list from /platform-config/app-catalog, which requires a top-level org owner. The host page is gated on CON...
Validate add-on codes before they can grant an app
active_addon_codes was written straight from the request body. That was harmless while nothing read the column, but it is now an authorization input: an add-on ...
Same alias NPE in the initializer seeders
resolveAll() has no entry for a deprecated alias sharing an id with a live app, so looping ServiceType.values() and dereferencing the result would have thrown m...
Stop a page load from persisting the entitlement projection
computeEffectiveFeatures added the caller's Hibernate-managed OrgFeature rows straight to its output and then flipped isActive on them. Its own javadoc calls th...
Stop iterating ServiceType.values() against the resolved catalog
resolveAll() omits the deprecated aliases that share an id with a live app (DOC_MANAGER/DOCS, AI_CHAT/AI_SUPPORT), because those have no catalog row of their ow...
Pin the deprecated-alias exclusion
The catalog key is ServiceType's numeric id, and several retired constants deliberately share one with the app that replaced them — DOC_MANAGER with DOCS on 8, ...
Bind the rename cutover as a Timestamp, not a String
CockroachDB refuses <timestamp(6)> < <varchar> rather than casting, so the app-config repair threw on its first statement every boot: 'unsupported comparison op...
Repair the second name-persisted site of the LOS rename
**************** is @Enumerated(STRING) and holds RoleRightType names, so a default role granting mortgage stores "LOS_VIEW_PIPELINE". That is the third place t...
Point the mortgage surface back at the mortgage rights
Companion to the shared-library change. These 13 controllers all guard mortgage endpoints — HMDA LAR, pipeline, application intake, disclosures, credit orders, ...
Stop mortgage endpoints enforcing a personal-loan right
The LOS_* -> MLOS_* rename was applied to the enum declaration and nowhere else. Because the freed names were immediately re-declared as the personal-loan right...
Rebuild after the Apps & Features strings reached the dictionary
Run 545 failed the i18n key guard on 60 keys. Not a code defect — the kamo-internal commit was pushed at 14:10 and the dictionary strings landed at 14:18, so th...
Bound the LOS rename repair to rows that predate the rename
The repair matched on the string alone and ran on every boot, so it stopped being idempotent the moment anyone granted the personal-loan app: a row holding 'LOS...
Master model honours a runtime retirement; drop a dead guard
provisionMaxedOutMasterModel force-enables every app for the platform org, and that breadth is deliberate — pre-launch dogfooding is what the model is for. But ...
Two more places that answered "is this app offered" from the enum
Both were missed when FeatureController moved onto the app catalog, and both would have disagreed with it the moment an operator published an app. Organization...
One plan-matrix scan per app-catalog request
Also drops the now-unused DevelopmentStageType import and corrects the auto-provision comment, which still described the stage rule the catalog replaced.
Answer the plan-matrix question for every app in one pass
The Apps & Features tab asks whether each app appears in the plan matrix, and the per-app form scans the plan-feature and add-on tables once per app — two dozen...
Drop the unused stageLabel helper
Stage names are rendered from message keys, so the English-only helper had no caller left.
Route the Apps & Features copy through next-intl
kamo-internal serves 22 locales and every user-facing string has to come from a message key — hardcoded English breaks every non-English operator. The tab and t...
Add an Apps & Features tab and fix the Allowed Apps list
The new tab, to the right of Organizations, controls what every organization is offered: availability, development stage, display name, description, an operator...
Serve the app catalog and read features through it
FeatureController derived "is this app offered" from the enum's stage in four separate places, so the answer could only be changed by redeploying the platform. ...
Repair mortgage grants orphaned by the LOS to MLOS rename
ServiceType is persisted in two places and the 2026-08-08 rename only accounted for one. ORG_FEATURES.FEATURE_TYPE stores the numeric id, so keeping mortgage at...
Resolve deprecation through the app catalog
An app the operator retires has to disappear everywhere, not just from the apps page — otherwise it keeps showing up in the security-model editor and in entitle...
Make app availability and stage editable at runtime
Every attribute the apps settings page renders — display name, description, stage, deprecation — was a compile-time constant in the ServiceType enum, and "Avail...
Render Contact sales for add-ons with no list price
PageAddOn.price was typed number and built with Number(addon.pricePerUnit), so an add-on with no list price — the enterprise-negotiated origination systems — re...
Show negotiated pricing instead of $0.00 for enterprise-only add-ons
The origination add-ons carry no list price — they are negotiated per agreement — and formatCurrency(null) renders $0.00, advertising an enterprise module as fr...
Hide unreleased apps from the Plans & Billing catalog
**************** back the tab where a customer chooses what to subscribe to, so it must not offer a product still in the Not Available list — the same rule the ...
Never offer an unreleased app on a buying surface, bundles included
The stage filter only ran for the public marketing proxy. This endpoint also feeds the in-app SubscriptionOrderWizard, which is where a customer actually pays, ...
One properly categorised catalog row per app, on every entitlement root
Nine apps — POS, Marketing Tools, Calculator, Club, Inventory, Legal, Games and the two origination systems — had no catalog representation at all: no pricing r...
Design the platform app catalog behind an Apps & Features tab
Apps on /settings/account?tab=apps are compile-time constants in the ServiceType enum, so "Available" (stage == COMPLETED), the stage itself, the display name a...
Keep the is-prefix on PlatformAccountSummary's wire names too
Same Lombok/Jackson hazard as PlatformOrgRow: isTopLevel() loses its `is` on serialization, so the DTO put `topLevel`/`platformProduct` on the wire while the fr...
Declare security matchers explicitly before the next build breaks startup
kamo-shared-library 1.5.0 was republished today with spring-ws-core as a new transitive dependency, so Spring Boot now auto-registers a second servlet (MessageD...
Keep the is-prefix on the wire for isTopLevel/isPlatformProduct
Lombok generates isTopLevel() for a `boolean isTopLevel` field and Jackson strips the `is` prefix, so the live endpoint was serializing `topLevel` and `platform...
Grandfather orgs already running a gated commerce market
Gating MORTGAGE behind MLOS made the gate unsatisfiable for tenants: MLOS is still PLANNING, and FeatureController.enable refuses a non-COMPLETED app for a chil...
Declare security matchers explicitly so the service can start
The service has not deployed since 2026-07-03; every pod since has crash-looped before the context loaded: This method cannot decide whether these patterns a...
Server-paginated organizations console
The tab loaded every organization in one request and rendered a card each, with the backend fanning out per org, then per account, then per subscription. Search...
Fail closed on apps a plan never mentions
Turns kamo.entitlement.fail-open off now that AppEntitlementCatalogMigration has run against prod and every active plan on both entitlement roots (KamoCRM, Sign...
Seed app entitlement for every root, and stop inventing plan tiers
Two defects found by checking prod before flipping kamo.entitlement.fail-open. Sign Pink is a second entitlement root (isPlatformProduct, with its own FREE/BUS...
Serve the org directory as a filtered, sorted, paginated page
GET /orgs takes page/size/q/sort/dir plus seven filters and returns the house page envelope with estate-wide aggregates. GET /orgs/{orgId} serves full detail fo...
Batched page hydration at a constant query count
Parent titles, primary domain, the plan/market behind the entitlement and the subscription-catalog flag are each one batched IN (:ids) over the whole page, so a...
Directory row/page DTOs and the native query repository
PlatformOrgRow carries the two facets separately: entitlement (inbound subs targeting the org -- what governs it) and billing held (accounts the org owns -- non...
Org directory SQL with per-facet CTEs
Held accounts, inbound subscriptions, members, licensees and children each aggregate in their own CTE before being joined to orgs. Joining them in one pass woul...
Validated, clamped org directory request
Size clamps to [1,100] and an unrecognised sort falls back to title, so a hand-edited URL can neither request the whole estate nor reach the SQL as free text. T...
Derive entitlement mode through one shared, deterministic resolver
TrialAdminService picked its trial subscription with findFirst() over an unordered list, so an org holding both a pending and an active trial could report eithe...
Implementation plan for the platform organizations console
17 TDD tasks across BillingService, the translation dictionary and kamo-internal. Physical table names verified against the live cluster -- the org table is 'or...
Backfill app entitlement rows into the platform plan catalog
Entitlement now resolves on a typed service_type column, but no catalog row carried one, so every plan still resolved to 'everything included' via the fail-open...
Gate mortgage and personal-loan verticals on app entitlement
A MORTGAGE market appeared on /commerce whenever POS was on, regardless of the MLOS app, and /features/enable had no billing check at all — the padlock in the s...
Restore CRLF line endings on ServiceType.ts
The previous commit rewrote the file through a tool that normalised CRLF to LF, churning every line. The 23 sibling files under app/types are still CRLF, so thi...
Sync AppType/ServiceType mirrors with the MLOS rename
ServiceType.LOS became MLOS (mortgage, id 14 retained because ORG_FEATURES persists the id) and the freed LOS key is now the Personal Loan Origination System on...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.