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.
4.9 KiB
4.9 KiB
ADR-012: Agent vs Role vs Account Terminology
Status
Proposed
Context
The codebase and documentation use "agent" in multiple overlapping senses:
- OpenCode "agent": A behavioral specification defining what tools, permissions, model, and prompt an LLM session uses. OpenCode's
.opencode/agents/*.mdfiles define these. - Philosophical "agency": An ill-defined notion of autonomy or self-direction.
- Principal-agent "agent": In the legal sense, an entity that acts on behalf of a principal.
- 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
agentfield 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:
-
sessions.agentName→sessions.roleName- The field stores which behavioral role is active, not which account
- OpenCode's
agentfield on messages maps to ourroleName
-
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
-
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
-
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"
-
OpenCode import mapping: OpenCode's
session.agent→ oursessions.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.trustLevelreads 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
agentfield →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