--- status: draft last_updated: 2026-04-20 --- # Table Schemas: Roles Behavioral role definitions. For cross-cutting reference (cascade behavior, index reference, status enums, relations), see [table-reference.md](./table-reference.md). For the full account-role-session model, see [../../agent-roles.md](../../agent-roles.md). For the terminology decision, see [ADR-012](../../../decisions/ADR-012-agent-vs-role-vs-account.md). ### `roles` Behavioral role definitions that any account can fill during a session. Roles define what operations are available, what permissions are granted, and what scope constraints apply. Currently defined in `.opencode/agents/*.md` files; this table enables database storage and runtime permission resolution. | Column | Type | Notes | |--------|------|-------| | commonCols | — | id, metadata, createdAt, updatedAt | | name | text NOT NULL UNIQUE | Role identifier (e.g., "architect", "implementation-specialist") | | description | text | Human-readable description | | mode | text NOT NULL | `primary` (user-facing) or `subagent` (spawned by coordinator) | | temperature | real | Model sampling temperature (default: 0.2 for subagents, 0.3 for primary) | | permissions | jsonb NOT NULL DEFAULT `[]` | Permission ruleset — array of `{ action, permission, pattern }` rules, evaluated first-match | | tools | jsonb NOT NULL DEFAULT `{}` | Tool availability map — `{ toolName: boolean }` for enabled/disabled tools | | prompt | text | System prompt template | | parentId | text | FK → roles.id — Parent role for inheritance. onDelete: SET NULL — deleting a parent detaches children. | | scopes | jsonb NOT NULL DEFAULT `[]` | API key scopes this role requires (string array, used during permission resolution) | | data | jsonb | Additional role-specific configuration (model selection, max steps, etc.) | **Indexes**: `unq_roles_name` UNIQUE on `(name)`, `idx_roles_parent_id` on `(parentId)`, `idx_roles_mode` on `(mode)`. **`permissions` shape**: A `Permission.Ruleset` — an ordered array of rules evaluated first-match: ```ts type PermissionRule = { action: "allow" | "deny" | "ask"; // What to do when this rule matches permission: string; // e.g., "edit", "read", "bash", "webSearch" pattern: string; // Glob pattern for path-based matching (e.g., "src/**", "*") }; type PermissionRuleset = PermissionRule[]; ``` Example for implementation-specialist: ```json [ { "action": "allow", "permission": "read", "pattern": "**" }, { "action": "allow", "permission": "write", "pattern": "src/**" }, { "action": "allow", "permission": "edit", "pattern": "src/**" }, { "action": "allow", "permission": "bash", "pattern": "deno *" }, { "action": "deny", "permission": "bash", "pattern": "*" }, { "action": "allow", "permission": "webSearch", "pattern": "*" } ] ``` **`tools` shape**: A simple boolean map for which tools are available to this role: ```json { "read": true, "write": true, "edit": true, "glob": true, "grep": true, "bash": true, "webSearch": true, "webfetch": true } ``` **Role inheritance**: When a role has a `parentId`, the child role inherits `permissions` and `tools` from the parent, with the child's values taking priority. Specifically: - `permissions`: The parent's ruleset is prepended before the child's ruleset. First match wins, so child rules override parent rules for the same pattern. - `tools`: Union of parent and child tool sets. If both define a tool, the child's value takes priority. - `temperature`, `prompt`, `model`, `scopes`: Child values override parent values entirely (no merging). - Max inheritance depth: 3 levels. Circular inheritance is prevented at role creation time. **`data` shape**: Additional configuration that varies by role: ```ts type RoleData = { model?: { // Override model selection providerID: string; // e.g., "anthropic", "openai" modelID: string; // e.g., "claude-opus-4-5-20250101" }; steps?: number; // Max agentic steps per turn topP?: number; // Top-P sampling parameter color?: string; // Display color for UI hidden?: boolean; // Don't show in role selection UI source?: "builtin" | "file" | "database"; // Where this role definition came from filePath?: string; // Source file path (for file-based roles) }; ``` **OpenCode compatibility**: When importing from `.opencode/agents/*.md`, the YAML frontmatter maps to: - `description` → from frontmatter `description` - `mode` → from frontmatter `mode` - `temperature` → from frontmatter `temperature` - `tools` → from frontmatter `tools` - `permissions` → converted from frontmatter `permission` (OpenCode uses `Permission.Ruleset` format) - `prompt` → from markdown body content - `data.model` → from frontmatter `model` - `data.steps` → from frontmatter `steps` - `data.source` → `"file"` - `data.filePath` → path relative to project root **Migration path**: Phase 1 uses `.opencode/agents/*.md` files. Phase 2 adds a `roles.sync` operation that reads files and upserts into this table. Phase 3 makes the database authoritative with files as a version-controlled editing surface. **Sessions reference**: `sessions.roleName` is a free-form string that references `roles.name` by convention, but there is no FK constraint. Sessions may use role names not yet in the `roles` table (e.g., file-based roles not yet synced). A FK constraint may be added in Phase 3 when the database becomes authoritative.