Runtime Guardrails
Required architecture rules for identity resolution, RBAC, skill safety, and performance.
Canonical identity flow
Always resolve contact identity through:
contacts.getOrCreateBySender(senderId)
Do not create contacts or contact-role assignments ad hoc in other runtime functions. That canonical mutation is responsible for preserving the default public role assignment and preventing access drift.
Skill access control
Runtime access decisions must be based on role grants only:
contactRolesskillRoleGrants
Do not query skills directly to decide access. Use role-scoped loaders instead:
skills.discoverForContactskills.loadForContact
Runtime is read-only for skills
Agent tools must not create or modify skill definitions. Skill writes belong to explicit sync or admin paths such as:
skills.syncCanonical
Migration compatibility
During the current transition period, keep both senderId and contactId on the key runtime tables:
inboundSignalsagentRunscontactMemoryescalationRequests
New writes should include contactId whenever it is available.
Query safety
Use indexed joins in hot paths:
by_contactby_roleby_skillby_senderby_slug
Avoid full-table scans where an index already exists.
Fail closed
If role resolution fails, or no grants are found, return no restricted skills. Access checks should deny by default rather than trying to recover optimistically.
Observability
Log the events that matter operationally:
- skill discovery failures
- skill load failures
- denied access decisions
The logs should stay concise enough for production diagnosis without turning request traces into noise.