## Context
An AI agent node scoped to a single object was still loading CRUD tools
for the
whole workspace, inflating every run's prompt to ~200k tokens (~110k on
a
standard seed workspace: 146 tools across 19 objects, 18 of them system
objects). Two mechanisms caused this: the roles permissions cache
force-grants
every system object to every role (`isSystem ? true`), and blanket role
flags
(`canReadAllObjectRecords`, ...) grant all remaining objects. The
per-object
rows written by the agent Permissions tab were additive on top of that,
so
scoping an agent had almost no effect on its tool payload.
## What
**Backend: explicit grants only for the agent node**
- New opt-in flag `requireExplicitObjectGrants` on
`ToolProviderContext`, set
only by the workflow agent executor.
- With the flag, `DatabaseToolProvider` generates CRUD tools exclusively
from
the role's explicit `objectPermission` rows: no row means no tools, and
each
verb gate reads the row directly (`canReadObjectRecords` for find tools,
`canUpdateObjectRecords` for create/update/upsert,
`canSoftDeleteObjectRecords`
for delete). A verb left null is not granted; composed defaults and the
system force-grant can no longer leak through. Composed permissions are
still
used for `restrictedFields`.
- Explicit rows are read from the `flatObjectPermissionMaps` workspace
cache
key, fetched in the same `getOrRecompute` call as `rolesPermissions`: no
extra query.
- Without the flag (chat, MCP, tool index, workspace stats), behavior is
unchanged: composed permissions, verified live (`getToolIndex` for an
Admin
returns the same 245 CRUD tools as before).
- Removed the `CANNOT_ADD_OBJECT_PERMISSION_ON_SYSTEM_OBJECT` guard on
`upsertObjectPermissions` so system objects can be granted explicitly.
**Frontend: grant system objects from the agent Permissions tab**
- The objects picker in the workflow agent side panel ends with a new
"System objects" submenu listing all active system objects; picking one
opens
the same CRUD grant flow as regular objects.
- Permissions granted on system objects now resolve their labels in the
existing permission list and can be deleted (both previously looked up
non-system objects only, which would have hidden such grants).
Result: an agent granted one object ships ~10 tools instead of 146,
cutting the
prompt from ~110k tokens to a few thousand and the per-run cost
accordingly.
## Notes
- Removing the system-object guard affects the whole upsert path: user
roles
can also receive explicit system object rows via the API. A `canRead:
false`
row on a system object now takes effect at the query layer for that
role.
- The agent role is resolved as the first role of the permission config,
matching `getObjectsPermissionsFromRolePermissionConfig` (multi-role is
not
supported yet).
## Tests
- `database-tool.provider.spec.ts`: three new cases for the flag (object
without a row emits nothing, partial row emits only granted verbs,
absent
flag keeps composed behavior even with zero rows, which guards the chat
regression).
- `object-permission.service.spec.ts`: the system-object case now
asserts a
successful upsert.
- Integration: dropped the failing "system object" upsert case and its
snapshot, added a successful system object upsert case. Both suites pass
against a live server.
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23400?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
# Introduction
Even though this would not possible through API at the moment, from
neither API metadata or manifest ( as manifest `permissionsFlag`
declarations etc are done from within a declared role )
Prevent any app to create permissions entities over another app role
from the validation engine itself
## `isEditable`
We might wanna deprecate this column at some point from the entity it
self as now the grain would rather be `what app owns that role ?`
---------
Co-authored-by: Charles Bochet <charles@twenty.com>