Files
hub/docs/decisions/ADR-017-hub-first-roles.md
glm-5.1 2d7f9c11cb Resolve 22 open questions via 4 ADRs; add dev spoke questions (OQ-61, OQ-62)
ADR-014 (Docker-first deployment): resolves OQ-21, OQ-22, OQ-23,
OQ-34, OQ-35, OQ-36, OQ-37. Docker is the primary deployment model.
Redis/Postgres in same network. Config via mounted volumes. Single-
container restart for v1. Migrations block startup.

ADR-015 (Dev spoke, not opencode): resolves OQ-16, OQ-17, OQ-26,
OQ-28, OQ-51, OQ-55. Replaces opencode integration with a compiled
dev spoke binary. Hub owns session format. Opencode compat is an
import tool, not an architectural constraint. Adds OQ-61 (dev spoke
operations) and OQ-62 (dev spoke distribution).

ADR-016 (Hub-own schema): resolves OQ-18, OQ-19. Hub defines its own
canonical message/part format. JSONB is implicitly versioned. Flat
parts for v1. Compaction is a hub concern (pruning), not opencode's.

ADR-017 (Hub-first roles): resolves OQ-26, OQ-28, OQ-51 (overlapping
with ADR-015). Hub is database-first for roles. Seeded by migrations.
No file sync needed. hub.createRole for custom roles.

Also narrowed: OQ-04 (service accounts), OQ-05 (git SSO out of scope),
OQ-08 (spoke-side concern), OQ-09 (v1: reconnect only), OQ-11 (dev
spoke replaces container spoke), OQ-29 (hub-only concern), OQ-41
(gitea ops are optional spoke concern).

Deferred: OQ-52 (memory), OQ-55 (anthropic import).

Net result: 15 resolved, 7 narrowed, 2 deferred out of 62 total.
39 remain open, down from 60 in the original tracker.
2026-05-26 05:40:54 +00:00

4.2 KiB

ADR-017: Hub-first role definitions (database, not files)

  • Status: Accepted
  • Date: 2026-05-26
  • Deciders: alkdev

Context

The original architecture (from agent-roles.md) defined a three-phase role system:

  1. Phase 1 (current): Roles defined in .opencode/agents/*.md markdown files
  2. Phase 2: A roles.sync operation that ingests .opencode/agents/*.md files into a roles table
  3. Phase 3: Database-authoritative roles, with markdown files only for version control editing

This phased approach was designed around opencode's convention of file-based agent definitions. With ADR-015 (dev spoke instead of opencode integration), opencode is no longer a core dependency. The hub manages its own roles.

Decision

The hub is database-first for roles from day one. There is no Phase 1 or Phase 2 transition from files to database. Roles are defined in the roles table in Postgres, seeded by migrations for the built-in roles (architect, decomposer, coordinator, implementation-specialist, code-reviewer, architecture-reviewer, research-specialist, poc-specialist).

Built-in roles

The hub's migration files seed the standard SDD roles:

Role Mode Key Permission Pattern
architect primary read, write, webSearch — no bash
architecture-reviewer subagent read, grep — read-only
code-reviewer subagent read, grep, bash (read-only)
coordinator primary read, worktree_*, bash (limited) — no implementation
decomposer primary read, taskgraph — no bash
implementation-specialist primary read, write, edit, bash, webSearch — scoped to worktree
poc-specialist primary read, write, edit, bash, webSearch — scoped to research worktree
research-specialist subagent webSearch, read, write — no bash

Role API

Roles are managed via hub operations:

  • hub.listRoles — list available roles
  • hub.getRole — get role definition by name
  • hub.createRole — create a custom role (requires admin scope)
  • hub.updateRole — update role definition (requires admin scope)

Custom roles can be created at runtime via hub.createRole. No file sync is needed.

Opencode agent mapping

When importing opencode sessions (if an opencode-spoke is built later), the mapping from opencode's agent field to the hub's roleName is:

Opencode agent Hub roleName
"build" "implementation-specialist"
"plan" "decomposer"
"general" "coordinator"
"explore" "research-specialist"

This mapping is a compat concern in the import tool, not a core architecture concern.

Consequences

Positive: No file-based role sync system to build. No .opencode/agents/ directory dependency. Roles are queryable, type-safe, and managed through the hub's operation interface. Custom roles can be created programmatically. The hub doesn't need a roles.sync operation.

Negative: Role definitions can't be easily version-controlled in markdown files alongside the code. Role creation requires the hub API (or seeding via migrations). If role editing in a text editor is desired later, a roles.export/roles.import operation can be built, but this is not a v1 concern.

Open questions resolved by this decision

OQ Resolution
OQ-26 No roles.sync from .opencode/agents/*.md needed. Hub is database-first. Role definitions are seeded by migrations.
OQ-28 No Agent.generate() equivalent needed for v1. Custom roles are created via hub.createRole.
OQ-51 No file→DB migration needed. Hub started in the database-first state.

Open questions narrowed by this decision

OQ Narrowing
OQ-04 Service account provisioning is now a generalized question: hub.createAccount operation for programmatic creation. For v1, manual creation with keypal CLI is sufficient. LLM-specific email conventions (like glm-5.1@alk.dev) are deployment-specific, not core architecture.
OQ-05 SSO with Gitea is out of scope for a generalized hub. Git provider integration (Gitea, GitHub, etc.) is a spoke concern via operations, not through SSO. For v1, Gitea is accessed via the dev spoke's git operations, not via shared auth.