Files
hub/docs/decisions/ADR-012-agent-vs-role-vs-account.md
glm-5.1 2b63cda1c7 Setup repo: migrate architecture specs, code stubs, and tasks from alkhub_ts
Copy architecture docs, ADRs, storage domain specs, research, reviews,
and 56 storage architecture tasks from the alkhub_ts monorepo. Adapt for
standalone @alkdev/hub repo structure (src/ not packages/hub/).

Sanitize all sensitive information:
- Replace private IPs (10.0.0.1) with localhost defaults
- Remove internal server hostnames (dev1, ns528096)
- Replace /workspace/ private paths with npm package references
- Remove hardcoded credentials from examples
- Rewrite infrastructure.md without private network details

Add Deno project scaffolding: deno.json (pinned deps), .gitignore,
AGENTS.md, entry point. Migrate existing code stubs (crypto, config
types, logger) with updated import paths.
2026-05-25 10:56:32 +00:00

4.9 KiB

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.agentNamesessions.roleName

    • The field stores which behavioral role is active, not which account
    • OpenCode's agent field on messages maps to our roleName
  2. accounts.roleaccounts.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.roleorganization_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.agentNamesessions.roleName, accounts.roleaccounts.accessLevel, organization_members.roleorganization_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 agentroleName — this is a data mapping, not a semantic conflict