# ADR-012: Agent vs Role vs Account Terminology ## Status Proposed ## Context The codebase and documentation use "agent" in multiple overlapping senses: 1. **OpenCode "agent"**: A behavioral specification defining what tools, permissions, model, and prompt an LLM session uses. OpenCode's `.opencode/agents/*.md` files define these. 2. **Philosophical "agency"**: An ill-defined notion of autonomy or self-direction. 3. **Principal-agent "agent"**: In the legal sense, an entity that acts on behalf of a principal. 4. **MCP/LLM "agent"**: A general term for an LLM-powered system that takes actions. Meanwhile, our `accounts` table has a `role` column with values `admin`, `user`, `service` — which is a _different_ "role" concept (access level, not behavioral specification). This creates confusion: - When we say "agent permissions," do we mean the behavioral spec (OpenCode sense) or the access level (account sense)? - When an LLM creates a Gitea commit, who is the "agent"? The LLM? The human who delegated? The account the LLM uses? - When we import OpenCode sessions, their `agent` field maps to... what in our model? ## Decision We adopt the following terminology: | Term | Definition | Storage | |------|-----------|---------| | **Account** | An identity in the system (human, service, or LLM). Owns resources, authenticates. | `accounts` table | | **Role** | A behavioral specification that any account can fill. Defines permissions, tools, model params. | `roles` table (future), currently `.opencode/agents/*.md` | | **Session** | A unit of work where an account fills a role. Binds account + role for a duration. | `sessions` table | ### Specific naming changes: 1. **`sessions.agentName`** → **`sessions.roleName`** - The field stores which behavioral role is active, not which account - OpenCode's `agent` field on messages maps to our `roleName` 2. **`accounts.role`** → **`accounts.accessLevel`** - Renamed to avoid confusion with behavioral roles - Values remain: `admin`, `user`, `service` - This is a different concept from the behavioral role 3. **`organization_members.role`** → **`organization_members.membershipLevel`** - Yet another "role" concept — org membership level - Values remain: `owner`, `admin`, `member` - Renamed for the same reason: avoid collision with behavioral roles 4. **New term**: When we need to say "an LLM acting autonomously", we say **"LLM in a role"** or **"session with an LLM account"**, not "agent" 5. **OpenCode import mapping**: OpenCode's `session.agent` → our `sessions.roleName` ### Rationale - **"Role" is what you fill, not what you are**. A human can fill the implementer role. An LLM can fill the implementer role. The role defines behavior, not identity. - **"Account" provides accountability**. Every session, API call, and audit entry traces back to an account. Whether that account is human or LLM is indicated by `accounts.accessLevel: "service"`. - **"Agent" is ambiguous**. The philosophical and legal senses conflict. The OpenCode sense conflates behavior with identity. Avoiding it removes confusion. - **The principal-agent framework maps naturally**. When a coordinator (principal) delegates to an implementer (agent), both have accounts. The accountability flows through the accounts, not through some notion of "agency." - **Permission intersection makes sense**. `Session permissions = Role.permissions ∩ Account.scopes ∩ SpokeType.trustLevel` reads clearly. `Agent.permissions ∩ ...` would be unclear. ## Consequences ### Positive - Clear separation between identity (account) and behavior (role) - Unambiguous accountability trail (every action → account) - Natural mapping of OpenCode's `agent` field → `roleName` - No philosophical confusion about "agency" ### Negative - Three columns renamed: `sessions.agentName` → `sessions.roleName`, `accounts.role` → `accounts.accessLevel`, `organization_members.role` → `organization_members.membershipLevel` - Need to be consistent about this in all new documentation and code - OpenCode's `.opencode/agents/` directory name stays (it's their convention), but we refer to the contents as "role specs" not "agent specs" - Migration needed for existing code/docs that use the old column names ### Terminology Summary | Old/Ambiguous Term | Canonical Term | Storage Location | Values | |---|---|---|---| | `accounts.role` | `accounts.accessLevel` | `accounts.accessLevel` | admin, user, service | | `sessions.agentName` | `sessions.roleName` | `sessions.roleName` | architect, implementation-specialist, ... | | `organization_members.role` | `organization_members.membershipLevel` | `organization_members.membershipLevel` | owner, admin, member | | behavioral "agent" (OpenCode) | role | `roles` table (planned) | architect, implementation-specialist, ... | ### Neutral - OpenCode import just maps `agent` → `roleName` — this is a data mapping, not a semantic conflict