Skip to main content

Data Model and Schema Specification

Complete database schema for River Agents -- 11 core tables in the river_agents schema within the shared PostgreSQL instance, full JSONB column schemas (including the AgentContext runtime object), indexing strategy, foreign key cascade rules, and three-layer tenant isolation enforcement.

Quick Navigation

Schema Overview

TableOwning ServicePrimary Responsibility
agentsAgent Management (Backend :8005)Core agent definition, lifecycle state, and current version reference
agent_versionsAgent Management (Backend :8005)Immutable configuration snapshots; one record per deploy action
agent_version_toolsAgent Management (Backend :8005)Tool bindings per agent version
agent_version_data_sourcesAgent Management (Backend :8005)Data source bindings per agent version
agent_triggersTrigger Ingestion (Backend :8005)Trigger configurations attached to an agent
agent_executionsExecution Runner (Backend :8005)One record per agent run; central Temporal workflow state
agent_logsExecution Logging (Backend :8005)Turn-level reasoning and tool call records within an execution
approval_requestsGovernance (Backend :8005)HITL approval gate records generated during execution
agent_templatesAgent Management (Backend :8005)Pre-built agent configuration templates
governance_policiesGovernance (Backend :8005)Policy definitions applied to agent versions
audit_logsAudit (Backend :8005)Write-once immutable audit event records

Schema namespace: All tables use the river_agents. prefix. Cross-schema references to IAM tables (organizations, workspaces, users) and platform tables (data_sources) use fully qualified names. No database-level foreign keys cross schema boundaries; integrity is enforced at the application layer.

Versioning invariant: agent_versions records are never mutated after creation. Any configuration change (instruction set edit, tool change, trigger update) creates a new agent_versions record with an incremented version_number. Active executions are pinned to the agent_version_id that was current at trigger ingestion time -- not at the time of each LLM call.


Entity Relationship Diagram

Cross-schema references (not shown): agents.org_id -> iam.organizations.id, agents.workspace_id -> iam.workspaces.id, agents.owner_user_id -> iam.users.id, agent_version_data_sources.data_source_id -> platform.data_sources.id.


Table Definitions

agents

Root entity. Stores identity, lifecycle state, accumulated long-term memory, and a pointer to the current deployed version.

ColumnTypeConstraintsDescription
idSERIALPKSurrogate primary key
org_idINTNOT NULL, FK organizations.idOrganization scope; every query must filter by this
workspace_idINTNOT NULL, FK workspaces.idWorkspace scope within org
owner_user_idINTNOT NULL, FK users.idUser responsible for this agent
current_version_idINTNULLABLE, FK agent_versions.idNULL when agent is in draft with no deployed version
template_idINTNULLABLE, FK agent_templates.idSet if agent was created from a template; NULL otherwise
nameVARCHAR(255)NOT NULLHuman-readable display name
descriptionTEXTNULLABLEAgent description
business_functionVARCHAR(100)NOT NULLAgent category: customer_support, sales_lead_qualification, finance_reconciliation, risk_compliance, data_analyst_insights, operations_monitoring, executive_decision_support, custom_enterprise
domainVARCHAR(100)NULLABLEBusiness domain tag (e.g., "billing", "operations") for workspace-level grouping
statusVARCHAR(50)NOT NULL, DEFAULT 'draft'Lifecycle state; valid values listed below
action_levelVARCHAR(50)NOT NULLAutonomy level: read_respond, recommend, act_with_approval, fully_automated
governance_levelVARCHAR(20)NOT NULL, DEFAULT 'standard'Policy strictness applied to this agent: standard, strict, custom
health_statusVARCHAR(20)NOT NULL, DEFAULT 'unknown'healthy, degraded, critical, or unknown; recalculated after each run on a 5-minute cycle
long_term_contextJSONBNOT NULL, DEFAULT '{}'Accumulated knowledge from past runs; injected into system prompt on each execution; updated non-blocking after run completion
notification_configJSONBNOT NULL, DEFAULT '{}'Agent-level alert channel and trigger overrides; merged with workspace defaults
created_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Creation timestamp
updated_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Last modification
deleted_atTIMESTAMPTZNULLABLESoft delete timestamp; NULL means active

Valid status values: draft, configured, validated, deployed, active, running, awaiting_approval, paused, archived

Implementation note: long_term_context is written non-blocking after execution completes. A failed write does not fail the run. The river-agent microservice receives this field in the context bundle on each turn call but does not write it directly -- all writes go through Backend :8005.


agent_versions

Immutable configuration snapshot per deploy action. No updated_at or deleted_at columns -- these records are never modified after creation.

ColumnTypeConstraintsDescription
idSERIALPKSurrogate primary key
agent_idINTNOT NULL, FK agents.idParent agent reference
version_numberINTNOT NULLMonotonically increasing integer per agent
instruction_setTEXTNOT NULLThe agent's goal in natural language; input to the system prompt builder in river-agent
action_levelVARCHAR(50)NOT NULLread_respond, recommend, act_with_approval, or fully_automated; copied from agent at snapshot time
governance_levelVARCHAR(20)NOT NULL, DEFAULT 'standard'Policy strictness for this version; copied from agent at snapshot time
max_turnsINTNOT NULL, DEFAULT 15Maximum reasoning loop iterations; enforced by Backend :8005, not river-agent
llm_timeout_secondsINTNOT NULL, DEFAULT 120Per-turn LLM inference timeout enforced by river-agent
tool_timeout_secondsINTNOT NULL, DEFAULT 30Per-tool execution timeout enforced at TLO Gateway
approval_configJSONBNOT NULL, DEFAULT '{}'Approval rules, auto-approve conditions, escalation config; see JSONB spec
notification_configJSONBNOT NULL, DEFAULT '{}'Run completion and failure notification channel configuration for this version
deployment_stateVARCHAR(20)NOT NULL, DEFAULT 'draft'draft, active, or archived; only one version per agent holds active at a time
created_byINTNOT NULL, FK users.idUser who deployed this version
created_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Version creation timestamp; immutable

Unique constraint: UNIQUE (agent_id, version_number)


agent_version_tools

Junction table binding tools to a specific version. One row per tool per version.

ColumnTypeConstraintsDescription
idSERIALPKSurrogate primary key
agent_version_idINTNOT NULL, FK agent_versions.idVersion this binding belongs to
tool_nameVARCHAR(255)NOT NULLTool identifier matching the Tool Registry registration name
config_overridesJSONBNOT NULL, DEFAULT '{}'Version-specific overrides for tool defaults (e.g., per-tool timeout, retry count)
acl_settingsJSONBNOT NULL, DEFAULT '{}'Per-tool ACL override for this version (e.g., restrict execute_query to specific data source IDs)

Unique constraint: UNIQUE (agent_version_id, tool_name)


agent_version_data_sources

Junction table binding data sources to a specific version. One row per data source per version.

ColumnTypeConstraintsDescription
idSERIALPKSurrogate primary key
agent_version_idINTNOT NULL, FK agent_versions.idVersion this binding belongs to
data_source_idINTNOT NULL, FK data_sources.idReference to platform data source record
access_levelVARCHAR(50)NOT NULL, DEFAULT 'read'read or read_write; enforced by TLO Gateway ACL check on every tool call
schema_filterJSONBNOT NULL, DEFAULT '{}'Restricts visible tables and columns to a subset; empty object means full schema access

Unique constraint: UNIQUE (agent_version_id, data_source_id)


agent_triggers

Trigger configurations attached to an agent. Mutations to a deployed or active agent's triggers create a new agent_versions draft rather than updating the existing version.

ColumnTypeConstraintsDescription
idSERIALPKSurrogate primary key
agent_idINTNOT NULL, FK agents.idParent agent reference
trigger_typeVARCHAR(50)NOT NULLmanual, scheduled, event, api, threshold, or workflow
trigger_configJSONBNOT NULLType-specific configuration; see JSONB spec per trigger type
is_activeBOOLEANNOT NULL, DEFAULT trueSoft disable without deleting; inactive triggers are not evaluated
last_fired_atTIMESTAMPTZNULLABLEUpdated by the Trigger Ingestion Service after each successful dispatch
created_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Trigger creation timestamp
deleted_atTIMESTAMPTZNULLABLESoft delete timestamp

agent_executions

One record per agent run. Central state record for the Temporal workflow. The context_snapshot column enables approval gate hibernation and resumption.

ColumnTypeConstraintsDescription
idSERIALPKSurrogate primary key; used as Temporal workflow ID suffix (agent-run-{id})
agent_idINTNOT NULL, FK agents.idParent agent reference (denormalized for query performance)
agent_version_idINTNOT NULL, FK agent_versions.idVersion pinned at trigger ingestion; never updated during execution
org_idINTNOT NULLDenormalized from agents.org_id for partition-friendly queries
workspace_idINTNOT NULLDenormalized from agents.workspace_id
trigger_typeVARCHAR(50)NOT NULLTrigger type that initiated this run
trigger_sourceVARCHAR(255)NULLABLEIdentifier of the specific trigger source (e.g., "cron:daily-8am", "webhook:zendesk")
trigger_payloadJSONBNULLABLERaw payload from the triggering event
statusVARCHAR(50)NOT NULL, DEFAULT 'queued'Current execution status; valid values below
turn_countINTNOT NULL, DEFAULT 0Number of reasoning turns completed; incremented per turn
tokens_consumedINTNOT NULL, DEFAULT 0Cumulative LLM tokens across all turns
final_outputJSONBNULLABLEAgent's final response or output artifact; set on terminal state transition
context_snapshotJSONBNULLABLESerialized AgentContext written when execution pauses at an approval gate; cleared on resume
error_codeVARCHAR(100)NULLABLEMachine-readable error code on terminal failed status
error_messageTEXTNULLABLEHuman-readable error description
started_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Execution start timestamp
completed_atTIMESTAMPTZNULLABLETerminal state timestamp

Valid status values: queued, running, completed, failed, cancelled, approval_pending, budget_exhausted, max_turns_exceeded, approval_expired


agent_logs

Turn-level audit trail for every reasoning step, tool call, and observation within an execution. High-write table with BIGINT primary key. Append-only; rows are never updated.

ColumnTypeConstraintsDescription
idBIGINTPK, AUTO_INCREMENTSurrogate primary key
execution_idINTNOT NULL, FK agent_executions.idParent execution reference
turn_numberINTNOT NULLZero-based reasoning turn index within the execution
log_typeVARCHAR(50)NOT NULLEntry type; valid values below
contentJSONBNOT NULLType-specific payload; see JSONB spec
model_usedVARCHAR(100)NULLABLELLM model identifier for reasoning entries
tokens_inINTNOT NULL, DEFAULT 0Input token count
tokens_outINTNOT NULL, DEFAULT 0Output token count
latency_msINTNULLABLEElapsed milliseconds (tool calls: execution time; reasoning: LLM inference time)
tool_nameVARCHAR(255)NULLABLETool name for tool_call and tool_result entries; NULL for reasoning entries
statusVARCHAR(50)NOT NULL, DEFAULT 'success'success, error, blocked, or suggested
created_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Log entry creation timestamp

Valid log_type values: reasoning, tool_call, tool_result, observation, approval_requested, approval_resolved, error


approval_requests

HITL approval gate records. One record per approval gate event per execution. A PATCH /api/v1/approvals/{id} resolution writes an audit event synchronously before sending the Temporal workflow signal.

ColumnTypeConstraintsDescription
idSERIALPKSurrogate primary key
execution_idINTNOT NULL, FK agent_executions.idExecution that triggered this gate
agent_version_idINTNOT NULL, FK agent_versions.idVersion at time of request (for governance audit)
turn_numberINTNOT NULLTurn at which the approval gate was triggered
tool_nameVARCHAR(255)NOT NULLTool call that triggered the gate
tool_argumentsJSONBNOT NULLArguments as originally proposed by the LLM
modified_argumentsJSONBNULLABLEApprover-supplied replacement arguments; set only on edited_approved resolution
reasoning_summaryTEXTNULLABLEAgent's explanation for why it wants to take this action; surfaced in the approval modal
risk_contextTEXTNULLABLEPolicy evaluation result and any warnings computed before the gate was triggered
statusVARCHAR(50)NOT NULL, DEFAULT 'pending'pending, approved, rejected, edited_approved, or expired
assigned_approver_roleVARCHAR(100)NULLABLERole required to approve; NULL means any member with agent:approve permission
assigned_approver_idINTNULLABLE, FK users.idSpecific approver; NULL means role-based assignment
resolved_byINTNULLABLE, FK users.idUser who resolved the request
resolution_noteTEXTNULLABLERequired for rejected; optional for approved
created_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Request creation timestamp
resolved_atTIMESTAMPTZNULLABLEResolution timestamp
expires_atTIMESTAMPTZNOT NULLAuto-expire timestamp; computed from workspace approval_timeout_hours setting

agent_templates

Pre-built configuration templates. Platform-provided templates have org_id = NULL; workspace-specific custom templates have a non-NULL org_id.

ColumnTypeConstraintsDescription
idSERIALPKSurrogate primary key
org_idINTNULLABLE, FK organizations.idNULL for platform templates; non-NULL for org-scoped custom templates
nameVARCHAR(255)NOT NULLTemplate display name
categoryVARCHAR(100)NOT NULLbusiness_function value this template targets
descriptionTEXTNULLABLEFull description shown on Template Detail page
icon_nameVARCHAR(50)NULLABLELucide icon identifier for the template card
icon_bgVARCHAR(50)NULLABLEBackground color class
icon_colorVARCHAR(50)NULLABLEIcon foreground color class
configurationJSONBNOT NULLComplete agent configuration payload used to pre-fill the Guided Wizard; see JSONB spec
capabilitiesJSONBNOT NULL, DEFAULT '[]'Array of capability descriptor objects shown on Template Detail
integrationsJSONBNOT NULL, DEFAULT '[]'Recommended data source types and tool names
governance_defaultVARCHAR(50)NOT NULL, DEFAULT 'act_with_approval'Default action_level for agents instantiated from this template
is_activeBOOLEANNOT NULL, DEFAULT trueInactive templates are hidden from the Template Library
is_platform_templateBOOLEANNOT NULL, DEFAULT falseTrue for templates maintained by the platform team
created_byINTNULLABLE, FK users.idNULL for platform templates
created_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Creation timestamp
updated_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Last update timestamp
deleted_atTIMESTAMPTZNULLABLESoft delete

governance_policies

Policy definitions applied to agent versions. A policy with workspace_id = NULL applies org-wide. Policy evaluation runs on every tool call dispatch; the enforcement_action determines whether the call is blocked, gated, logged, or alerted.

ColumnTypeConstraintsDescription
idSERIALPKSurrogate primary key
org_idINTNOT NULL, FK organizations.idOrganization scope
workspace_idINTNULLABLE, FK workspaces.idNULL for org-wide policies; non-NULL for workspace-specific
nameVARCHAR(255)NOT NULLPolicy display name
descriptionTEXTNULLABLEPolicy description
scopeVARCHAR(50)NOT NULL, DEFAULT 'workspace'org or workspace
condition_expressionTEXTNOT NULLPolicy condition in the RiverCore policy language (WHEN/THEN/WITH DSL)
enforcement_actionVARCHAR(50)NOT NULLblock, gate, log, or alert
versionINTNOT NULL, DEFAULT 1Policy version; incremented on each edit
activeBOOLEANNOT NULL, DEFAULT trueInactive policies are not evaluated at runtime
created_byINTNOT NULL, FK users.idUser who created this policy
created_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Creation timestamp
updated_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Last update timestamp

audit_logs

Write-once immutable audit records. No updated_at, no deleted_at, no soft delete. The application layer has no DELETE or UPDATE path against this table. BIGINT primary key for high-volume write performance.

ColumnTypeConstraintsDescription
idBIGINTPK, AUTO_INCREMENTSurrogate primary key
org_idINTNOT NULLOrganization scope (denormalized for partition-friendly queries)
workspace_idINTNULLABLEWorkspace scope; NULL for org-level events
agent_idINTNULLABLE, FK agents.id SET NULLNULL for events not tied to a specific agent; SET NULL on agent delete so audit entries survive
execution_idINTNULLABLE, FK agent_executions.id SET NULLNULL for events outside an execution context
actor_user_idINTNULLABLE, FK users.idNULL for system-initiated events
actor_typeVARCHAR(50)NOT NULLhuman, agent, or system
event_typeVARCHAR(100)NOT NULLNamespaced event category (e.g., agent.created, run.started, approval.resolved, policy.violated, security.auth)
actionVARCHAR(255)NOT NULLSpecific action description (e.g., "Agent deployed to production")
event_payloadJSONBNOT NULLFull event context; content varies by event_type
outcomeVARCHAR(50)NOT NULLsuccess, failure, or blocked
ip_addressVARCHAR(45)NULLABLESource IP address of the actor; captured for security events
user_agentTEXTNULLABLEBrowser or API client user agent; captured for security events
created_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Event timestamp; not modifiable

JSONB Schema Specifications

agent_triggers.trigger_config

The schema varies by trigger_type. The trigger_type column is the discriminator.

scheduled trigger:

{
"cron_expression": "0 8 * * 1-5",
"timezone": "America/New_York",
"cooldown_seconds": 300,
"payload": {}
}

event trigger:

{
"event_types": ["ticket.created", "ticket.updated_by_customer"],
"source": "zendesk_webhook",
"payload_conditions": {
"priority": { "$in": ["high", "urgent"] },
"channel": { "$ne": "internal" }
}
}

payload_conditions uses MongoDB-style comparison operators evaluated against the incoming event payload. Supported operators: $eq, $ne, $in, $nin, $gt, $lt, $gte, $lte, $exists.

threshold trigger:

{
"metric_source": "datadog",
"metric_name": "system.cpu.user",
"condition": "avg > 90",
"evaluation_window_seconds": 300,
"cooldown_seconds": 600
}

api trigger:

{
"api_key_id": "key_abc123",
"rate_limit_per_minute": 60,
"payload_schema": {
"type": "object",
"required": ["event_type"],
"properties": {
"event_type": { "type": "string" },
"data": { "type": "object" }
}
}
}

workflow trigger:

{
"signal_name": "data_collected",
"parent_workflow_id_pattern": "agent-run-*",
"output_key": "$.report_url"
}

manual trigger:

{
"input_schema": {
"type": "object",
"properties": {
"customer_id": { "type": "integer" },
"issue_summary": { "type": "string" }
},
"required": ["customer_id"]
}
}

agent_versions.approval_config

{
"require_approval_for": ["issue_refund", "update_ledger_status", "revoke_access"],
"auto_approve_conditions": [
{
"tool": "draft_response",
"condition": "confidence_score > 0.95"
}
],
"approver_roles": ["admin", "editor"],
"approver_user_id": null,
"expiry_hours": 24,
"escalation_timeout_minutes": 60,
"escalation_channel": "pagerduty"
}

require_approval_for is an array of tool names. Any tool in this list triggers an approval gate regardless of the agent's action_level. auto_approve_conditions bypass the gate for specific tools when the stated condition is satisfied.


agents.notification_config and agent_versions.notification_config

These two columns have the same schema. The version-level config overrides the agent-level config for the duration of runs using that version.

{
"channels": {
"slack": {
"workspace_id": "T12345",
"channel_id": "C67890",
"channel_name": "#agent-alerts"
},
"email": {
"recipients": ["admin@company.com", "team-lead@company.com"]
},
"pagerduty": {
"service_id": "P12345",
"severity": "high"
}
},
"triggers": {
"on_failure": true,
"on_approval_needed": true,
"on_completion": false,
"on_health_degraded": true,
"on_policy_violation": true
}
}

agent_executions.context_snapshot

Written when an execution pauses at an approval gate. Contains sufficient state to resume the Temporal workflow from the exact point of interruption. The governance_token field is re-validated on resume to ensure the governance context has not changed during the wait.

{
"agent_id": "42",
"version": 3,
"execution_id": "8821",
"config": {
"instruction_set": "Monitor customer support tickets and draft responses...",
"action_level": "act_with_approval",
"governance_level": "strict",
"data_sources": [
{ "id": 7, "name": "Zendesk", "type": "saas_api", "access": "read_write" },
{ "id": 12, "name": "Confluence KB", "type": "saas_api", "access": "read_only" }
],
"tools": ["search_knowledge_base", "get_ticket_history", "draft_response", "issue_refund"],
"approval_rules": {
"require_approval_for": ["issue_refund", "draft_response"]
}
},
"runtime": {
"turn_count": 4,
"conversation_history": [
{ "turn": 1, "type": "reasoning", "content": "I need to check the customer history first..." },
{ "turn": 1, "type": "action", "tool": "get_ticket_history", "args": { "customer_id": "C-123" } },
{ "turn": 1, "type": "observation", "result": { "tickets": 3, "refunds": 0 } }
],
"pending_tool_call": {
"tool_name": "issue_refund",
"arguments": { "amount": 49.99, "charge_id": "ch_abc123" }
},
"trigger_payload": { "event": "ticket.created", "ticket_id": 9912 }
},
"memory": {
"last_processed_ticket": "TKT-9911",
"common_issues": ["billing", "product_defect", "shipping"],
"escalation_patterns": ["legal_threat", "data_breach", "executive_complaint"]
},
"governance_token": "gt_eyJhbGc...",
"snapshot_at": "2024-04-14T10:23:11Z"
}

runtime.pending_tool_call is the exact tool call that triggered the approval gate. On approved resolution, this is the call that will execute (possibly with modified_arguments from the approver substituted for arguments).


agent_logs.content

Content varies by log_type.

reasoning type:

{
"thought": "Customer is a VIP with no prior refunds. Purchase was 12 days ago. Policy allows refunds up to 30 days.",
"next_action": "execute tool: issue_refund",
"confidence": 0.91,
"model_used": "claude-sonnet-4-6",
"tokens_in": 2840,
"tokens_out": 187
}

tool_call type:

{
"tool_name": "issue_refund",
"arguments": { "amount": 49.99, "charge_id": "ch_abc123" },
"governance_decision": "APPROVAL_REQUIRED",
"dispatch_id": "disp_7f3a91"
}

tool_result type:

{
"tool_name": "issue_refund",
"dispatch_id": "disp_7f3a91",
"output": { "refund_id": "TX-882", "status": "processed" },
"approval_id": 884,
"resolution": "approved",
"duration_ms": 312
}

approval_requested type:

{
"approval_request_id": 884,
"tool_name": "issue_refund",
"arguments": { "amount": 49.99, "charge_id": "ch_abc123" },
"expires_at": "2024-04-15T10:23:11Z"
}

approval_resolved type:

{
"approval_request_id": 884,
"resolution": "edited_approved",
"resolved_by": 102,
"modified_arguments": { "amount": 25.00, "charge_id": "ch_abc123" },
"reviewer_note": "Partial refund per policy section 4.2"
}

error type:

{
"error_type": "tool_timeout",
"message": "Tool execute_query did not respond within 30 seconds",
"retry_count": 2,
"recoverable": true
}

agent_templates.configuration

The full pre-fill payload applied to the Guided Wizard when a user selects a template.

{
"name_template": "{{category}} Agent",
"description_template": "Autonomous {{category}} agent for enterprise operations.",
"instruction_set_template": "You are a {{category}} specialist. Your goal is to monitor incoming tickets, retrieve relevant customer context, draft responses, and escalate when needed...",
"business_function": "customer_support",
"action_level": "act_with_approval",
"governance_level": "strict",
"recommended_tools": [
"search_knowledge_base",
"get_ticket_history",
"draft_response",
"issue_refund"
],
"recommended_data_source_types": ["ticketing", "knowledge_base", "crm"],
"default_triggers": {
"events": ["ticket.created", "ticket.updated_by_customer"]
},
"default_approval_rules": {
"require_approval_for": ["send_response", "issue_refund"],
"approver_roles": ["admin", "editor"]
}
}

Indexing Strategy

Index NameTableColumnsTypePurpose
idx_agents_workspace_statusagents(workspace_id, status)BTREEAgent Library page filter queries
idx_agents_org_deletedagents(org_id, deleted_at)BTREE PARTIAL (deleted_at IS NULL)Tenant-scoped soft-delete filtering
idx_agent_versions_agentagent_versions(agent_id, version_number DESC)BTREEVersion history list and rollback queries
idx_agent_executions_agent_startedagent_executions(agent_id, started_at DESC)BTREEPer-agent run history page
idx_agent_executions_workspace_statusagent_executions(workspace_id, status, started_at DESC)BTREEGlobal Runs Management page filtering
idx_agent_executions_versionagent_executions(agent_version_id)BTREEFK support for version-scoped queries
idx_agent_logs_execution_turnagent_logs(execution_id, turn_number)BTREETurn-by-turn log viewer (most read index)
idx_agent_logs_content_ginagent_logscontentGINFull-text search within log content
idx_approval_requests_executionapproval_requests(execution_id, status)BTREEGovernance tab approval history
idx_approval_requests_approverapproval_requests(assigned_approver_id, status)BTREE PARTIAL (status = 'pending')Pending approval notification queries
idx_audit_logs_org_createdaudit_logs(org_id, created_at DESC)BTREEAudit trail page chronological queries
idx_audit_logs_agentaudit_logs(agent_id, created_at DESC)BTREEPer-agent scoped audit trail
idx_audit_logs_executionaudit_logs(execution_id)BTREEExecution-scoped audit entries
idx_agent_triggers_agent_typeagent_triggers(agent_id, trigger_type, is_active)BTREETrigger evaluation at dispatch time

Implementation note: idx_agent_logs_execution_turn is the most heavily read index in the system during active runs. The WebSocket emitter and monitoring service query this index on every turn. It must be a BTREE on (execution_id, turn_number) -- GIN or HASH would not support the sequential turn-order access pattern.


Foreign Key Cascade Rules

RelationshipOn Parent DeleteRationale
agents -> agent_versionsRESTRICTVersions are compliance artifacts; agent deletion must be soft delete only
agents -> agent_executionsRESTRICTExecution history must be retained; soft delete only
agents -> agent_triggersCASCADETriggers are owned by the agent; physical delete of an agent (only via admin tooling) removes its triggers
agent_versions -> agent_version_toolsCASCADETool bindings are part of the version snapshot
agent_versions -> agent_version_data_sourcesCASCADESame as tool bindings
agent_executions -> agent_logsCASCADELog entries have no meaning without their parent execution
agent_executions -> approval_requestsRESTRICTApproval records are compliance artifacts; must be retained after execution
audit_logs -> agentsSET NULLAudit entries must survive agent deletion; agent_id set to NULL while event_payload retains the agent name at event time
audit_logs -> agent_executionsSET NULLSame rationale as above

Physical delete policy: No application code path issues DELETE statements against agents, agent_versions, agent_executions, approval_requests, or audit_logs. All removal operations are soft deletes (deleted_at) or status transitions (status = 'archived'). Physical deletes on these tables are reserved for admin data retention tooling only and must produce an audit_logs entry before executing.


Tenant Isolation Enforcement

Tenant isolation operates at three concurrent layers. All three must be active simultaneously; failure of any one layer does not degrade to unprotected access.

Layer 1 -- Application query scoping: Every repository method in Backend :8005 accepts org_id and workspace_id parameters and includes them in the WHERE clause of every query. These values are extracted from validated JWT claims by TLO Gateway and propagated as request headers (X-Org-ID, X-Workspace-ID). No query against River Agents tables is issued without both parameters.

Layer 2 -- PostgreSQL Row-Level Security: RLS policies are enabled on agents, agent_executions, and audit_logs. The policy condition uses current_setting('app.org_id'), which Backend :8005 sets via SET LOCAL app.org_id = {org_id} at the start of each database transaction. If the application-layer header injection fails, RLS prevents any cross-tenant data access at the database level.

Layer 3 -- river-agent context bundle scoping: The AgentContext object passed to river-agent :8007 on each turn contains an explicit workspace_id field. Tool calls referencing data sources are validated against agent_version_data_sources, which are already scoped to the version's workspace. The river-agent microservice has no direct database access and cannot issue unscoped queries.

Isolation failure detection: Any query that returns a row with a mismatched org_id triggers an audit_logs entry with event_type = 'security.cross_tenant_access_attempt' and outcome = 'blocked'. This event is forwarded to the Security Alert Stream in real time.