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.
3.8 KiB
ADR-016: Hub-own schema, opencode compat via import
- Status: Accepted
- Date: 2026-05-26
- Deciders: alkdev
Context
The hub's session/message/part storage was originally designed to closely mirror opencode's drizzle+sqlite schema, so that importing sessions from opencode would be straightforward. This created a tension: the hub's data model was being shaped by an external project's conventions (opencode uses a message tree format with parent/child parts, a version column, a compaction agent type, etc.) rather than by the hub's own needs (AI SDK UIMessage format, hub-specific session metadata, role-based permissions).
With ADR-015 (dev spoke instead of opencode integration), opencode is no longer a core dependency. The hub needs its own canonical data model designed for the hub's needs, with opencode import as a compatibility tool, not an architectural driver.
Decision
The hub owns its own session, message, and part schema. The schema is designed for the hub's needs first:
-
AI SDK
UIMessagecompatibility is the primary design constraint — direct agents (architect, decomposer, etc.) produceUIMessageformat, and the hub's API assemblesmessages+partsintoUIMessagefor consumption. -
The hub's
dataJSONB columns are implicitly versioned by their TypeBox schema evolution. No separateversioncolumn is needed — each schema change is documented in the migration history, and the TypeBox schemas in the codebase are the source of truth for what eachdatashape contains at any point in time. -
Compaction and pruning are hub concerns, not opencode concerns. The hub may need message pruning for API response size, but this is different from opencode's LLM-driven compaction (summarizing old messages to compress context). Hub pruning is server-side truncation of old messages; opencode compaction is an LLM feature that doesn't belong in the hub's core architecture.
-
The hub's part types are a subset of what opencode defines. The hub adds types as it implements features, not because opencode has them.
text,tool,reasoningare core;step-start,step-finish,snapshot,patchmay be added as needed. -
Opencode import remains possible through an import tool that maps opencode's sqlite schema to the hub's postgres schema. This is a compat tool, not a core feature. The import tool maps opencode's
agentfield to the hub'sroleName, opencode's message tree to the hub's flat message+parts model, etc.
Consequences
Positive: The hub's schema is self-determined and evolves based on the hub's needs, not another project's conventions. AI SDK compatibility is a natural fit. Schema versioning is implicit (TypeBox schemas + migrations). Import from opencode is a tool, not an architectural constraint.
Negative: An import tool for opencode's sqlite format will need to be built separately. It's not a priority for v1 and may not be shipped with the codebase. The hub's part type set starts minimal and grows organically, which means early consumers may not find all the part types they expect.
Open questions resolved by this decision
| OQ | Resolution |
|---|---|
| OQ-16 | Hub defines its own canonical format. AI SDK UIMessage + parts is the primary design constraint. Opencode import is a compat tool. |
| OQ-17 | Compaction is an opencode concept. The hub needs pruning (server-side truncation) at most. For v1, full history is served. |
| OQ-18 | JSONB data columns are implicitly versioned by TypeBox schema evolution. No version column needed. |
Open questions resolved by previous decisions (confirmed)
| OQ | Resolution |
|---|---|
| OQ-19 | Flat parts with messageId FK for v1. No parentId needed unless nesting becomes necessary in the hub's own use cases. |