Keep a security model's app grants off the catalog
The Allowed Apps tab showed "Not Available" for every app and snapped back whenever another option was picked. loadModels() runs from a mount-only effect, so i...
Choose the new org's apps while creating it
Adds an Apps & Features step between Type and Basics. It lists the apps the chosen branch type's security model actually offers -- the forced ones as "Always in...
37 unenforced becomes 32 — CRM contacts and leads are gated
Contacts: all 26 endpoints of EmailService's ContactRestController now take the verb that names them. They checked ownership carefully and no right at all, with...
Stop the letter-fallback org logos from flashing
An org with no logo shows its initial, and on /network that initial blinked continuously. Three things stacked up. PlatformRightsProvider wraps the whole app, ...
41 unenforced becomes 37 — the document verbs are gated
CREATE, EDIT, DELETE_DOCUMENTS and MANAGE_STORAGE now gate DocsService's ImagingController and DocumentController and ConversionService's upload endpoints. The ...
Sixth cluster triaged — documents span four services, and must not be seeded
CREATE, EDIT, DELETE_DOCUMENTS and MANAGE_STORAGE all have real live features, so nothing here is deletable. VIEW_DOCUMENTS is client-only in navRegistry, the s...
God accounts are protected on the platform admins list
Two rules the server enforces and this only mirrors: a god account's platform rights can be changed by another god and nobody else, and a god account can never ...
43 unenforced becomes 41 — the two notes placeholders are deleted
Mirrors the shared-lib removal of SHARE_NOTES (72) and MANAGE_NOTES_SETTINGS (73): the TS constants and the registry array, the one parent-map edge in both righ...
Rebuild — the i18n guard raced the dictionary push
No source change. 9a5b5167 failed on eleven missing background-slideshow keys because the components reached main at 01:47 and the strings reached kamo-translat...
46 unenforced becomes 43 — the notes endpoints are gated
Fifth cluster. The Notes backend is KBService, not SecurityService: the entity sits under donotmodify/dao/beans/security/ and kamo-internal proxies /api/notes/*...
Stop a page-2 request from cancelling the folder you just opened
Junk, Trash, Archive and every custom folder showed the same mail — whatever was on screen before them. The server was not the problem: replaying listMessages a...
Let a granted administrator actually see Platform Administration
The section gate and the settings menu topic still asked useUserInfo's hasPlatformAdminAccess, which is owner-of-the-top-level-org or an open god-mode window an...
Send an operator with no Configuration tab somewhere real
SYSTEM_USER and MANAGE_UNIVERSE gate surfaces that are not tabs on this page, so somebody holding only those is a platform administrator the page guard admits a...
Platform Admins console gates every platform surface
Renames the System User tab to Platform Admins and turns it into what it was always standing in for: a list of the people who may operate the platform, each hol...
48 unenforced becomes 46 — meet create and join are gated
Fourth cluster of the 48, and it split three ways rather than the two the design predicted. The design attributed the enforced MANAGE_MEET_SETTINGS check to Ch...
Draw the callback markers as soon as the summary loads
The summary lands after the grid has drawn its rows, and AG Grid keeps the cellRenderer a column was built with and only re-creates a cell when its row changes....
A deleted upload now falls back instead of going silent
The previous commit claimed a retired sound "degrades to the layer beneath it rather than to silence". It did not. `isKnownSoundId` accepts a custom id on shape...
Move the callback marker beside the contact name, add a Callback filter
The marker sits with the name because that is who the call is to, rather than in the id column where it pushed the ids out of line. The filter offers All / Sch...
Upload your own sound effects, at all three layers
The system could only ever play the thirty-nine tones it shipped with. Now any layer can add its own file — the platform's catalogue, an organization's, a membe...
48 unenforced — the six PLOS rights are gated now
Gating the LOS proxy made PLOS_* enforced, but they stayed on the unenforced list, so the editors were showing 'Not yet enforced' against six live gates. Third ...
Let the sender edit the meeting invite email subject
The invitation subject was fixed at "<host> from <org> invited you to a meeting" and built inside the send route, so nobody could say what the meeting was actua...
Third cluster triaged — Meet splits in two
All five Meet rights have real running features, so none is a chat-channel case: kamomeet-deployment is live and Meet-jibri is deployed, making recording real i...
Put the callback marker after the lead id
Leading the cell with the marker pushed the id right on any row with a call waiting, so the ids no longer lined up down the column while scrolling.
Gate the personal-loan proxy on the PLOS rights
KamoLOS enforces nothing — no rights check anywhere in its source — so this proxy is the authorization boundary for the module, the position denyUnless occupies...
The meeting reminder reaches the bell, and pops once
MeetReminderPopup drew its own Snackbar from its own subscription to **************** Nothing else knew a reminder had happened: the bell stayed at zero, the de...
Second cluster triaged — PLOS is implement, and cheap right now
The six PLOS_* rights have a real feature: KamoLOS is a full loan service. Not deletable. But nothing enforces any of them — no rights check anywhere in KamoLOS...
One calendar reminder, announced once
A reminder was arriving twice. The deck picks it up as a notification, and the older path picked it up as well: CalendarWebSocketContext opened a second STOMP c...
Subscribe to the course count instead of asking for it every minute
NavTop sums two counts into one obligation badge, and only the legal half was live. A member assigned a course — or excused from one by HR — kept the old number...
Drop the proxy route for an endpoint that never existed
/api/notes/statistics forwarded to a KBService endpoint NotesController does not implement, and nothing in the app ever called it — so the only thing it could d...
Catch a template-built i18n key that next-intl cannot reach
The catalogue's keys contain dots, and they were written into en.json as literal flat keys. next-intl resolves by splitting on dots, so every one of the 46 even...
The bell tells the truth in every tab, not just the one you clicked in
The nav-top bell was live for arrivals and nothing else. Reads, dismissals, snoozes and deletions were applied to whichever tab the member clicked in and announ...
The player is a module singleton, not a React value
Firing a sound read a context, which made every call site's POSITION IN THE TREE part of its correctness. A component rendered above SoundProvider got a no-op a...
Drop the three chat-channel rights from the frontend
Mirrors the shared-lib deletion: the enum mirror, its registry array, both hierarchy edges (ACCESS_CHAT -> CREATE_CHANNELS -> MANAGE_CHANNELS and ACCESS_CHAT ->...
A note is owned by a member, not by an account
Mirrors the API: NoteDTO now carries memberId in place of userId. A member is the signed-in account inside ONE organisation, so opening a different org shows a ...
One event-sound system, three layers, wired to what actually fires
There were two sound systems and neither knew about the other: four mp3s behind useChatSounds, and four synthesised tones behind notificationSound with a five-o...
First cluster triaged — chat channels are vestigial
Implementing gates for the 57 started with chat and the cluster came back empty. CREATE_CHANNELS, MANAGE_CHANNELS and MODERATE_CHAT have no feature: chat is rea...
Make 'do not hand-edit the unenforced list' enforceable
The list has been wrong four times (58 -> 52 -> 47 -> 57) and every single time because it was edited by reasoning instead of regenerated. The file already said...
Re-derive the audit structurally — 57 decorative
The count moved 58 -> 52 -> 47 -> 57 because every version hard-coded a list of gate-helper names and the codebase has more than anyone can recall. Six were mis...
Dock popup windows beside the page scrollbar, not on top of it
Tool windows are fixed to the viewport and dock right-to-left from its edge, and the layout was told about exactly one thing occupying that edge: the interactio...
The SHARE_DOCS_* client-side-only gap is closed
resolve-alias in DocsService and the two member-search endpoints in SecurityService now enforce these four server-side, mirroring the dialog's isTeamMember-or-r...
Give the deck its slide back, without the lurch
Dropping `layout` to stop the card lurching onto the edge of the screen also took the sibling slide with it: dismissing a card left the rest snapping into its p...
Stop the reminder lurching to the screen edge when an option opens
The card kept framer's `layout` after it grew a fold-out options panel. `layout` animates size and position by transform, so that one height change was being an...
47 unenforced — five more were falsely reported
CREATE_ORG_CALENDAR_EVENTS is enforced: MemberCalendarService throws OrgCalendarForbiddenException when a member without it writes to the org calendar. The chec...
52 unenforced, not 58 — six were falsely reported
The audit's gate detection recognised hasRight and checkRight but not denyUnless, hasAnyRight, sessionHasRight or forbidden. Six rights that ARE enforced were t...
Stagger the cascade, add branch bulk actions, explain the rule
The cascade highlight now travels the chain instead of flashing every affected row at once. enabledAncestors already arrives nearest-first, so the index IS the ...
Make the emoji picker's search actually filter
The picker's search matched the query against the translated category name only, and fell back to pushing every emoji into the results when nothing matched — so...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.