Reference
Rye Agent Operations Guide
Execute agent reads and writes safely with repeatable SQL patterns that preserve auditability.
Source file: docs/agent-ops-guide.md
Rye Agent Operations Guide
Schema Setup
All Rye objects live in the rye schema. Set the search path at the start of each session or transaction:
SET search_path = rye, public, pg_catalog;
All Rye functions include SET search_path in their definitions, so calling rye.record_event(...) works regardless of session state. But for queries against tables and views (SELECT * FROM nodes), the search path must include rye.
Safe Read Path
- Use
agent_node_summary(node_id, max_items)for compact context retrieval - Keep
max_itemsconservative (10-20) for context-window efficiency - Use
current_assertionsview for non-superseded facts (never queryassertionsdirectly for current state)
Safe Write Path
Recording events
Use record_event() for all event creation. It handles UUID generation and participant linking in a single call:
SELECT record_event(
p_event_type := 'meeting',
p_summary := 'Quarterly review with Acme',
p_properties := '{"location": "zoom"}',
p_participant_ids := ARRAY['<node_uuid_1>', '<node_uuid_2>']::uuid[],
p_participant_roles := ARRAY['organizer', 'attendee']
);
Do not insert events and event_participants separately. The record_event() function exists to prevent a known RLS interaction where INSERT ... RETURNING id on the events table fails because the event_read_policy requires participants to exist before the event is visible.
Writing assertions
- Insert assertions directly (subject to RLS and assertion-type gating):
INSERT INTO assertions (assertion_type, assertion_key, subject_node_id, claim, confidence)
VALUES ('sentiment', 'default', '<node_uuid>', '{"score": 0.8}', 0.9);
- For single-valued facts, supersede existing active assertions with
supersede_assertion(...):
SELECT supersede_assertion(
p_old_assertion_id := '<old_assertion_uuid>',
p_new_assertion_type := 'task_status',
p_new_subject_node_id := '<task_uuid>',
p_new_subject_edge_id := NULL,
p_new_claim := '{"status": "in_progress"}',
p_new_assertion_key := 'default',
p_new_source_event_id := '<event_uuid>',
p_new_confidence := 0.9
);
- Use
assertion_key = 'default'for singleton facts - Do not run direct
UPDATE assertions; RLS allows supersession updates only through scoped function context
Assertion-type restrictions
Some assertion types are write-gated by role:
| assertion_type | Roles that can INSERT |
|---|---|
financial_terms | deal_manager, admin |
compensation | hr_admin, admin |
| All others | Any role |
Attempting to insert a gated type with the wrong role raises an RLS violation.
Auditability
- Log agent queries using
log_agent_query(...):
SELECT log_agent_query(
'triage-bot',
'What changed on Acme?',
'Returned customer summary',
ARRAY['<node_uuid>'::uuid]
);
- Link all writes to related nodes via
event_participants
Node Classification
When creating nodes with team scoping, always set classification in attrs:
INSERT INTO nodes (node_type, label, attrs)
VALUES ('task', 'Build feature X',
'{"classification": "internal", "teams": ["engineering"]}'
);
Nodes with teams but no classification will be rejected. Nodes without teams or classification are visible to all users (public).
Role Expectations
Session variables must be set per transaction:
SET LOCAL "app.current_user_id" = 'user:alice';
SET LOCAL "app.current_teams" = 'engineering,sales';
SET LOCAL "app.current_role" = 'team_member';
Internal supersession context flags (app.write_path, app.supersede_assertion_id) are set by database functions, not client SQL.
Views
All views use security_invoker = true (PostgreSQL 15+), which means RLS policies are evaluated using the calling session’s permissions, not the view owner’s. This is required for security — without it, views would bypass RLS when owned by a superuser.
| View | Purpose |
|---|---|
current_assertions | Non-superseded assertions only |
node_context | Full node context with edges and assertions |
nodes_secure | Nodes with field-level redaction applied |