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

79 lines
4.2 KiB
Markdown

# 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. |