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.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
status: reviewed
|
status: reviewed
|
||||||
last_updated: 2026-05-25
|
last_updated: 2026-05-26
|
||||||
---
|
---
|
||||||
|
|
||||||
# Open Questions Tracker
|
# Open Questions Tracker
|
||||||
@@ -19,7 +19,7 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
| Theme | Questions | Focus |
|
| Theme | Questions | Focus |
|
||||||
|-------|-----------|-------|
|
|-------|-----------|-------|
|
||||||
| 1. Authentication & Authorization | OQ-01–OQ-05 | Auth models, permissions, SSO |
|
| 1. Authentication & Authorization | OQ-01–OQ-05 | Auth models, permissions, SSO |
|
||||||
| 2. Spoke Connectivity & Lifecycle | OQ-06–OQ-11 | Spoke provisioning, WebSocket, concurrent ops |
|
| 2. Spoke Connectivity & Lifecycle | OQ-06–OQ-11, OQ-61–OQ-62 | Spoke provisioning, WebSocket, concurrent ops, dev spoke |
|
||||||
| 3. Data Integrity & Lifecycle | OQ-12–OQ-15 | Deletion, retention, truncation, FK enforcement |
|
| 3. Data Integrity & Lifecycle | OQ-12–OQ-15 | Deletion, retention, truncation, FK enforcement |
|
||||||
| 4. Session & Schema Design | OQ-16–OQ-19 | Message schema, compaction, versioning, nesting |
|
| 4. Session & Schema Design | OQ-16–OQ-19 | Message schema, compaction, versioning, nesting |
|
||||||
| 5. Configuration & Infrastructure | OQ-20–OQ-25 | Config reload, CI/CD, SSL, tokenEnv, secret refs |
|
| 5. Configuration & Infrastructure | OQ-20–OQ-25 | Config reload, CI/CD, SSL, tokenEnv, secret refs |
|
||||||
@@ -29,6 +29,15 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
| 9. Cross-Cutting Implementation Gaps | OQ-38–OQ-50 | Startup, config, logger, Gitea, keypal, auth, schemas |
|
| 9. Cross-Cutting Implementation Gaps | OQ-38–OQ-50 | Startup, config, logger, Gitea, keypal, auth, schemas |
|
||||||
| 10. Future / Low Priority | OQ-51–OQ-60 | Phase 3+, memory, versioning, visualization |
|
| 10. Future / Low Priority | OQ-51–OQ-60 | Phase 3+, memory, versioning, visualization |
|
||||||
|
|
||||||
|
### Resolved by ADRs
|
||||||
|
|
||||||
|
| ADR | Questions Resolved |
|
||||||
|
|-----|-------------------|
|
||||||
|
| [ADR-014](../decisions/ADR-014-docker-first-deployment.md) | OQ-21, OQ-22, OQ-34, OQ-35, OQ-36, OQ-37 |
|
||||||
|
| [ADR-015](../decisions/ADR-015-dev-spoke-not-opencode.md) | OQ-16, OQ-17, OQ-26, OQ-28, OQ-51, OQ-55 |
|
||||||
|
| [ADR-016](../decisions/ADR-016-hub-own-schema.md) | OQ-18, OQ-19 (confirmed) |
|
||||||
|
| [ADR-017](../decisions/ADR-017-hub-first-roles.md) | OQ-26, OQ-28, OQ-51 (overlaps with ADR-015) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Theme 1: Authentication & Authorization
|
## Theme 1: Authentication & Authorization
|
||||||
@@ -39,7 +48,7 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
- **Status**: open
|
- **Status**: open
|
||||||
- **Priority**: high — blocks all authenticated endpoints
|
- **Priority**: high — blocks all authenticated endpoints
|
||||||
- **Question**: Should the hub use API keys with the keypal pattern, a simpler token auth stopgap, or something else? This affects every authenticated endpoint in the system.
|
- **Question**: Should the hub use API keys with the keypal pattern, a simpler token auth stopgap, or something else? This affects every authenticated endpoint in the system.
|
||||||
- **Cross-references**: OQ-02 (WebSocket auth), OQ-24 (MCP auth)
|
- **Cross-references**: OQ-02 (WebSocket auth), OQ-43 (MCP auth)
|
||||||
|
|
||||||
### OQ-02: How does WebSocket authentication work for spoke connections?
|
### OQ-02: How does WebSocket authentication work for spoke connections?
|
||||||
|
|
||||||
@@ -47,7 +56,7 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
- **Status**: open
|
- **Status**: open
|
||||||
- **Priority**: high — blocks all spoke connections
|
- **Priority**: high — blocks all spoke connections
|
||||||
- **Question**: Should the spoke authenticate via token in the first message after connect, token in the query string, or token in the subprotocol header? This also affects the `SpokeConfig.auth` format — the config system currently supports `tokenFile` but the actual auth protocol is undefined.
|
- **Question**: Should the spoke authenticate via token in the first message after connect, token in the query string, or token in the subprotocol header? This also affects the `SpokeConfig.auth` format — the config system currently supports `tokenFile` but the actual auth protocol is undefined.
|
||||||
- **Cross-references**: OQ-01 (API auth model), OQ-18 (spoke config auth field), [infrastructure.md](infrastructure.md) Security section
|
- **Cross-references**: OQ-01 (API auth model), OQ-46 (spoke config auth field), [infrastructure.md](infrastructure.md) Security section
|
||||||
|
|
||||||
### OQ-03: How are permissions enforced at the call protocol layer?
|
### OQ-03: How are permissions enforced at the call protocol layer?
|
||||||
|
|
||||||
@@ -56,20 +65,21 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
- **Priority**: high
|
- **Priority**: high
|
||||||
- **Resolution**: `OperationContext.identity` carries the resolved permissions from `sessions.data.scope`. The `CallHandler` evaluates `AccessControl.requiredScopes` against the session's resolved scope. The principal-agent framework ensures delegated permissions are properly intersected.
|
- **Resolution**: `OperationContext.identity` carries the resolved permissions from `sessions.data.scope`. The `CallHandler` evaluates `AccessControl.requiredScopes` against the session's resolved scope. The principal-agent framework ensures delegated permissions are properly intersected.
|
||||||
|
|
||||||
### OQ-04: How are LLM service accounts provisioned?
|
### OQ-04: How are service accounts provisioned?
|
||||||
|
|
||||||
- **Origin**: [agent-roles.md](agent-roles.md) OQ-6
|
- **Origin**: [agent-roles.md](agent-roles.md) OQ-6
|
||||||
- **Status**: open
|
- **Status**: narrowed
|
||||||
- **Priority**: medium
|
- **Priority**: medium
|
||||||
- **Question**: LLM accounts currently require manual creation (e.g., `glm-5.1@alk.dev`). Should there be an automated provisioning flow (`hub.createAccount` operation), or is manual provisioning sufficient for v1?
|
- **Question**: Does the hub need a `hub.createAccount` operation for programmatic service account creation, or is manual creation (with keypal CLI) sufficient for v1? LLM-specific email conventions (e.g., `glm-5.1@alk.dev`) are deployment-specific, not core architecture. Git attribution for LLM accounts uses the account's `giteaUsername` — this is a config concern, not an auth architecture concern.
|
||||||
- **Cross-references**: OQ-03 (permission enforcement)
|
- **Narrowed by**: [ADR-017](../decisions/ADR-017-hub-first-roles.md) — LLM accounts are service accounts with specific scopes, same pattern as any other automated identity. V1: manual creation. Future: `hub.createAccount` operation.
|
||||||
|
|
||||||
### OQ-05: Should SSO be shared with Gitea?
|
### OQ-05: Should the hub integrate with git providers via SSO?
|
||||||
|
|
||||||
- **Origin**: [hub-architecture.md](hub-architecture.md) OQ-3
|
- **Origin**: [hub-architecture.md](hub-architecture.md) OQ-3
|
||||||
- **Status**: open
|
- **Status**: narrowed
|
||||||
- **Priority**: medium
|
- **Priority**: low
|
||||||
- **Question**: Gitea at `git.alk.dev` uses its own auth. Should `api.alk.dev` share sessions with Gitea, or maintain separate auth? This affects user experience but is not blocking for v1.
|
- **Question**: Originally: should `api.alk.dev` share sessions with Gitea? Narrowed: git provider integration (Gitea, GitHub, etc.) is a spoke concern via operations, not through SSO. The dev spoke exposes git operations. SSO with any specific git provider is out of scope for a generalized hub. For v1, git access is through the dev spoke's git operations, not shared auth.
|
||||||
|
- **Narrowed by**: [ADR-015](../decisions/ADR-015-dev-spoke-not-opencode.md) and [ADR-017](../decisions/ADR-017-hub-first-roles.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -80,8 +90,8 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
- **Origin**: [spoke-runner.md](spoke-runner.md) OQ-1
|
- **Origin**: [spoke-runner.md](spoke-runner.md) OQ-1
|
||||||
- **Status**: open
|
- **Status**: open
|
||||||
- **Priority**: high — blocks spoke provisioning
|
- **Priority**: high — blocks spoke provisioning
|
||||||
- **Question**: Does the hub tell the spoke which git repo to clone, or does the spoke come pre-configured with a project? This is fundamental to the spoke provisioning workflow.
|
- **Question**: Does the hub tell the spoke which git repo to clone, or does the spoke come pre-configured with a project? For the dev spoke specifically: the spoke binary connects to the hub, receives project/workspace context via `hub.register`, and clones/checks out the relevant repo. The exact protocol needs specification.
|
||||||
- **Cross-references**: OQ-07 (source sync)
|
- **Cross-references**: OQ-07 (source sync), OQ-61 (dev spoke operations)
|
||||||
|
|
||||||
### OQ-07: How does source sync work for external compute?
|
### OQ-07: How does source sync work for external compute?
|
||||||
|
|
||||||
@@ -94,17 +104,17 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
### OQ-08: Can a spoke handle concurrent operations?
|
### OQ-08: Can a spoke handle concurrent operations?
|
||||||
|
|
||||||
- **Origin**: [spoke-runner.md](spoke-runner.md) OQ-5
|
- **Origin**: [spoke-runner.md](spoke-runner.md) OQ-5
|
||||||
- **Status**: open
|
- **Status**: narrowed
|
||||||
- **Priority**: medium
|
- **Priority**: medium → low
|
||||||
- **Question**: Can a spoke handle multiple `call.requested` events concurrently? Concurrent processing is better for SUBSCRIPTION operations but introduces complexity in state management on the spoke side.
|
- **Question**: Originally: can a spoke handle multiple `call.requested` events concurrently? Narrowed: the hub doesn't decide this — the spoke does. The hub dispatches `call.requested` and the spoke processes it. A spoke can process concurrently (multiple handlers) or serially (queue). The only hub-side question is whether the hub should respect a spoke-advertised concurrency limit in its `hub.register` payload (default: 1). This is a minor spoke registration enhancement, not an architectural question.
|
||||||
- **Cross-references**: OQ-09 (operation list freshness)
|
- **Cross-references**: OQ-09 (operation list freshness)
|
||||||
|
|
||||||
### OQ-09: When does a spoke re-register its operations?
|
### OQ-09: When does a spoke re-register its operations?
|
||||||
|
|
||||||
- **Origin**: [spoke-runner.md](spoke-runner.md) OQ-6
|
- **Origin**: [spoke-runner.md](spoke-runner.md) OQ-6
|
||||||
- **Status**: open
|
- **Status**: narrowed
|
||||||
- **Priority**: medium
|
- **Priority**: low
|
||||||
- **Question**: Does the spoke re-register on reconnect only, or does it push updates when its local registry changes? This affects the hub's operation routing and the RunnerPool design.
|
- **Resolution**: For v1, re-register on reconnect only. Spokes disconnect and reconnect (the call protocol handles abort cascading for in-flight calls). Push-based registry updates are a v2 enhancement. The `hub.register` call on reconnect is sufficient for v1.
|
||||||
|
|
||||||
### OQ-10: What is the design for the hub-side WebSocket handler?
|
### OQ-10: What is the design for the hub-side WebSocket handler?
|
||||||
|
|
||||||
@@ -114,13 +124,29 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
- **Question**: What is the full design for the hub-side WebSocket handler? This includes: Hono WebSocket upgrade handler, per-connection `WebSocketEventTarget`, per-connection `PendingRequestMap`, spoke lifecycle management (connect/register/heartbeat/disconnect), identity/authentication integration, and reconnection state recovery. Currently described as "an architectural task that needs deeper design" with no spec.
|
- **Question**: What is the full design for the hub-side WebSocket handler? This includes: Hono WebSocket upgrade handler, per-connection `WebSocketEventTarget`, per-connection `PendingRequestMap`, spoke lifecycle management (connect/register/heartbeat/disconnect), identity/authentication integration, and reconnection state recovery. Currently described as "an architectural task that needs deeper design" with no spec.
|
||||||
- **Cross-references**: OQ-02 (WebSocket auth), OQ-06 (spoke project context — constrains handler message types), OQ-08 (concurrent operations)
|
- **Cross-references**: OQ-02 (WebSocket auth), OQ-06 (spoke project context — constrains handler message types), OQ-08 (concurrent operations)
|
||||||
|
|
||||||
### OQ-11: Container spoke lifecycle
|
### OQ-11: Dev spoke and compute spoke lifecycle
|
||||||
|
|
||||||
- **Origin**: [spoke-runner.md](spoke-runner.md) OQ-2, [hub-architecture.md](hub-architecture.md) Components table
|
- **Origin**: [spoke-runner.md](spoke-runner.md) OQ-2, [hub-architecture.md](hub-architecture.md) Components table
|
||||||
- **Status**: open
|
- **Status**: narrowed
|
||||||
- **Priority**: low
|
- **Priority**: low
|
||||||
- **Question**: Container spoke extends base spoke with Docker container lifecycle management and opencode integration. Design is deferred until base spoke is working.
|
- **Question**: Originally: "Container spoke extends base spoke with Docker container lifecycle management and opencode integration." Narrowed by [ADR-015](../decisions/ADR-015-dev-spoke-not-opencode.md): the dev spoke is a compiled Deno binary (not an opencode container) that exposes dev operations over the standard call protocol. Compute spokes (GPU, vast.ai) are separate spoke types. The container spoke concept is deferred — the dev spoke replaces it for v1.
|
||||||
- **Cross-references**: OQ-06 (project context), OQ-07 (source sync)
|
- **Cross-references**: OQ-06 (project context), OQ-61 (dev spoke operations)
|
||||||
|
|
||||||
|
### OQ-61: What operations does the dev spoke expose?
|
||||||
|
|
||||||
|
- **Origin**: [ADR-015](../decisions/ADR-015-dev-spoke-not-opencode.md)
|
||||||
|
- **Status**: open
|
||||||
|
- **Priority**: medium
|
||||||
|
- **Question**: The dev spoke replaces opencode's tool suite. What operations does it expose? Minimum: `dev.bash.exec`, `dev.fs.read`, `dev.fs.write`, `dev.fs.list`, `dev.git.status`, `dev.git.diff`, `dev.git.commit`, `dev.git.clone`, `dev.git.checkout`. Web search may be a hub-native operation or a separate spoke. The exact operation set and their input/output schemas need specification.
|
||||||
|
- **Cross-references**: OQ-06 (project context), OQ-11 (dev spoke lifecycle)
|
||||||
|
|
||||||
|
### OQ-62: How is the dev spoke distributed and configured?
|
||||||
|
|
||||||
|
- **Origin**: [ADR-015](../decisions/ADR-015-dev-spoke-not-opencode.md)
|
||||||
|
- **Status**: open
|
||||||
|
- **Priority**: medium
|
||||||
|
- **Question**: The dev spoke is a compiled Deno binary. How is it distributed (Docker image, binary download, package manager)? How is it configured (hub URL, auth token, project context)? Does it use `SpokeConfig` from hub-config.md or a separate config format? The spoke doesn't have Postgres or Redis — just a WebSocket connection to the hub and local tools.
|
||||||
|
- **Cross-references**: OQ-06 (project context), OQ-46 (spoke config auth field)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -156,7 +182,6 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
- **Status**: open
|
- **Status**: open
|
||||||
- **Priority**: medium
|
- **Priority**: medium
|
||||||
- **Question**: `providerId` in `spokes` table references different parent tables depending on `spokeType` (either `dev_env_spokes` or `compute_spokes`). Current approach is application-layer enforcement. Alternatives (two nullable FK columns, DB triggers) are deferred.
|
- **Question**: `providerId` in `spokes` table references different parent tables depending on `spokeType` (either `dev_env_spokes` or `compute_spokes`). Current approach is application-layer enforcement. Alternatives (two nullable FK columns, DB triggers) are deferred.
|
||||||
- **Cross-references**: None currently
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -165,34 +190,31 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
### OQ-16: Session/message schema finalization
|
### OQ-16: Session/message schema finalization
|
||||||
|
|
||||||
- **Origin**: [agent-sessions.md](agent-sessions.md) Schema Research Needed section, [storage/sessions.md](storage/sessions.md)
|
- **Origin**: [agent-sessions.md](agent-sessions.md) Schema Research Needed section, [storage/sessions.md](storage/sessions.md)
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-016](../decisions/ADR-016-hub-own-schema.md)
|
||||||
- **Priority**: high — blocks session storage implementation
|
- **Priority**: high → medium (unblocked, narrower scope)
|
||||||
- **Question**: The message/part schema needs more iteration. Opencode's drizzle+sqlite schema uses a message tree format with parent/child parts that needs reconciliation with AI SDK `UIMessage` part types. Which exact subset of opencode's part types does the hub use? How do we handle the session `data` column shapes (formally type-constrained or application-layer guidance)?
|
- **Resolution**: The hub defines its own canonical message/part format based on AI SDK `UIMessage` + parts. Opencode's format is an import concern, not an architectural constraint. The hub's format stays close to opencode's for import compatibility but is self-determined. The remaining design work is specifying the hub's exact part types and session data shapes — this is an implementation task, not an open architectural question.
|
||||||
- **Cross-references**: OQ-17 (compaction), OQ-19 (part nesting)
|
- **Cross-references**: OQ-17 (compaction), OQ-19 (part nesting)
|
||||||
|
|
||||||
### OQ-17: Session message compaction
|
### OQ-17: Session message compaction
|
||||||
|
|
||||||
- **Origin**: [agent-sessions.md](agent-sessions.md), [storage/README.md](storage/README.md) OQ-2
|
- **Origin**: [agent-sessions.md](agent-sessions.md), [storage/README.md](storage/README.md) OQ-2
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-016](../decisions/ADR-016-hub-own-schema.md)
|
||||||
- **Priority**: medium
|
- **Priority**: medium → low
|
||||||
- **Question**: Need to define what compaction means for hub-direct AI SDK sessions. Opencode has a `compaction` agent/part type. The hub needs a strategy for long-running sessions that accumulate many messages.
|
- **Resolution**: Compaction is an opencode concept (LLM-driven summarization). The hub may need message **pruning** (server-side truncation of old messages for API response size), but this is different from compaction. For v1, full message history is served. Pruning is a potential future optimization, not a current design concern.
|
||||||
- **Cross-references**: OQ-16 (schema finalization)
|
|
||||||
|
|
||||||
### OQ-18: Message data versioning
|
### OQ-18: Message data versioning
|
||||||
|
|
||||||
- **Origin**: [storage/README.md](storage/README.md) OQ-1
|
- **Origin**: [storage/README.md](storage/README.md) OQ-1
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-016](../decisions/ADR-016-hub-own-schema.md)
|
||||||
- **Priority**: medium
|
- **Priority**: medium
|
||||||
- **Question**: Should the `data` column format be versioned for forward compatibility? Opencode has a `version` column on sessions. If the data shape evolves, old records need to be readable.
|
- **Resolution**: The hub's `data` JSONB columns are implicitly versioned by their TypeBox schema evolution. No separate `version` column is needed. Each schema change is documented in migration history, and the TypeBox schemas in the codebase are the source of truth. Opencode's `version` column was an opencode concern, not a hub pattern.
|
||||||
- **Cross-references**: OQ-45 (client config schema evolution), OQ-16 (schema finalization)
|
|
||||||
|
|
||||||
### OQ-19: Part nesting
|
### OQ-19: Part nesting
|
||||||
|
|
||||||
- **Origin**: [storage/sessions.md](storage/sessions.md)
|
- **Origin**: [storage/sessions.md](storage/sessions.md)
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-016](../decisions/ADR-016-hub-own-schema.md)
|
||||||
- **Priority**: low
|
- **Priority**: low
|
||||||
- **Question**: Currently flat parts with `messageId` FK. If nesting becomes necessary (e.g., tool results containing sub-parts), it would require a `parentId` column on `parts`. Not needed for v1 but should be considered in schema design.
|
- **Resolution**: Flat parts with `messageId` FK for v1. If nesting becomes necessary for the hub's own use cases (e.g., tool results containing sub-parts), a `parentId` column can be added. No need to carry opencode's tree structure.
|
||||||
- **Cross-references**: OQ-16 (schema finalization)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -209,25 +231,23 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
### OQ-21: Config file generation for CI/CD
|
### OQ-21: Config file generation for CI/CD
|
||||||
|
|
||||||
- **Origin**: [hub-config.md](hub-config.md) OQ-2
|
- **Origin**: [hub-config.md](hub-config.md) OQ-2
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-014](../decisions/ADR-014-docker-first-deployment.md)
|
||||||
- **Priority**: high — blocks deployment
|
- **Priority**: high → resolved
|
||||||
- **Question**: The `alkhub-config` CLI requires the master key to encrypt values. How does CI/CD get the master key? Options: (a) CI has access to the master key secret, (b) config files are pre-encrypted and stored in a private repo, (c) encryption happens at deploy time on the host.
|
- **Resolution**: In the Docker deployment model, config files are pre-encrypted by the operator (using `alkhub-config`) and mounted at runtime. The Docker secret provides the master key. CI/CD doesn't need the master key — it doesn't encrypt. Config files are built into the Docker image or mounted as volumes. The `alkhub-config` CLI runs on the operator's machine, not in CI/CD.
|
||||||
- **Cross-references**: OQ-20 (config reload)
|
|
||||||
|
|
||||||
### OQ-22: Multiple config file layers
|
### OQ-22: Multiple config file layers
|
||||||
|
|
||||||
- **Origin**: [hub-config.md](hub-config.md) OQ-4
|
- **Origin**: [hub-config.md](hub-config.md) OQ-4
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-014](../decisions/ADR-014-docker-first-deployment.md)
|
||||||
- **Priority**: low
|
- **Priority**: low → resolved
|
||||||
- **Question**: Should the config loader support a base config + overlay pattern (e.g., `/etc/alkhub/config.json` + `/etc/alkhub/config.local.json`)? Useful for dev vs. prod.
|
- **Resolution**: Docker Compose handles environment variation via different config files mounted at different paths. Dev: local decrypted config. Prod: pre-encrypted config mounted as read-only volume. No overlay system needed — the Docker model makes this straightforward with volume mounts.
|
||||||
- **Cross-references**: OQ-20 (config reload)
|
|
||||||
|
|
||||||
### OQ-23: What are the production SSL/TLS requirements for PostgresConfig?
|
### OQ-23: What are the production SSL/TLS requirements for PostgresConfig?
|
||||||
|
|
||||||
- **Origin**: [hub-config.md](hub-config.md) PostgresConfig section
|
- **Origin**: [hub-config.md](hub-config.md) PostgresConfig section
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-014](../decisions/ADR-014-docker-first-deployment.md)
|
||||||
- **Priority**: medium
|
- **Priority**: medium → low
|
||||||
- **Question**: `PostgresConfig.ssl` is currently `Type.Optional(Type.Boolean())` — "true = enable SSL with default CA verification". For production, TLS to Postgres is essential. What detailed SSL config is needed (CA certs, client certs, verify modes, custom CA)? Should we use a `PostgresSslConfig` object or a connection string-based approach?
|
- **Resolution**: In the Docker deployment model, Postgres and the hub run in the same Docker network. TLS between containers in the same network is not required — Docker network policies handle isolation. TLS termination happens at the reverse proxy (nginx/caddy) for external traffic. `PostgresConfig.ssl: boolean` is sufficient for v1. If a future deployment topology puts Postgres on a different network, a `PostgresSslConfig` object can be added, but same-network Docker deployment doesn't need it.
|
||||||
|
|
||||||
### OQ-24: HTTPServiceConfig.auth.tokenEnv deprecation
|
### OQ-24: HTTPServiceConfig.auth.tokenEnv deprecation
|
||||||
|
|
||||||
@@ -250,10 +270,9 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
### OQ-26: Role import/sync operation
|
### OQ-26: Role import/sync operation
|
||||||
|
|
||||||
- **Origin**: [agent-roles.md](agent-roles.md) OQ-1, [storage/README.md](storage/README.md) OQ-9 (partial), [storage/roles.md](storage/roles.md)
|
- **Origin**: [agent-roles.md](agent-roles.md) OQ-1, [storage/README.md](storage/README.md) OQ-9 (partial), [storage/roles.md](storage/roles.md)
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-017](../decisions/ADR-017-hub-first-roles.md)
|
||||||
- **Priority**: medium
|
- **Priority**: medium → resolved
|
||||||
- **Question**: Should there be a `roles.sync` operation that reads `.opencode/agents/*.md` and syncs them to the `roles` table? Phase 2 of the role transition plan. Files are the authoring surface; database is the source of truth at runtime.
|
- **Resolution**: The hub is database-first for roles from day one. No `roles.sync` from `.opencode/agents/*.md` is needed. Role definitions are seeded by migrations for built-in roles. Custom roles are created via `hub.createRole`. Opencode's `.opencode/agents/` file format is an opencode concern, not a hub concern.
|
||||||
- **Cross-references**: OQ-27 (role inheritance resolution), OQ-28 (dynamic roles)
|
|
||||||
|
|
||||||
### OQ-27: Role inheritance with permission resolution
|
### OQ-27: Role inheritance with permission resolution
|
||||||
|
|
||||||
@@ -261,22 +280,21 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
- **Status**: open
|
- **Status**: open
|
||||||
- **Priority**: medium
|
- **Priority**: medium
|
||||||
- **Question**: When a role has a `parentId`, its permissions are unioned with the parent's, with the child's rules taking priority in case of conflict. Max depth: 3 levels. Circular inheritance is prevented at role creation time. The description exists but the implementation is not yet specified.
|
- **Question**: When a role has a `parentId`, its permissions are unioned with the parent's, with the child's rules taking priority in case of conflict. Max depth: 3 levels. Circular inheritance is prevented at role creation time. The description exists but the implementation is not yet specified.
|
||||||
- **Cross-references**: OQ-26 (role sync)
|
- **Cross-references**: OQ-26 (role sync — resolved, but inheritance still needs implementation)
|
||||||
|
|
||||||
### OQ-28: Dynamic role creation
|
### OQ-28: Dynamic role creation
|
||||||
|
|
||||||
- **Origin**: [agent-roles.md](agent-roles.md) OQ-3
|
- **Origin**: [agent-roles.md](agent-roles.md) OQ-3
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-017](../decisions/ADR-017-hub-first-roles.md)
|
||||||
- **Priority**: low
|
- **Priority**: low → resolved
|
||||||
- **Question**: Opencode supports `Agent.generate()` for on-the-fly role creation. The hub currently only supports predefined roles. Should dynamic role creation be supported? Decision: start with predefined, add later if needed.
|
- **Resolution**: The hub supports `hub.createRole` for programmatic role creation. Opencode's `Agent.generate()` pattern (on-the-fly LLM-driven role creation) is not a hub concern. Roles are DB records, created via hub operations.
|
||||||
- **Cross-references**: OQ-26 (role sync)
|
|
||||||
|
|
||||||
### OQ-29: Per-session role switching
|
### OQ-29: Per-session role switching
|
||||||
|
|
||||||
- **Origin**: [agent-roles.md](agent-roles.md) OQ-4
|
- **Origin**: [agent-roles.md](agent-roles.md) OQ-4
|
||||||
- **Status**: open
|
- **Status**: narrowed
|
||||||
- **Priority**: medium
|
- **Priority**: medium
|
||||||
- **Question**: Should a session be able to change roles mid-conversation? Opencode supports this. Our current model binds role at session creation. Decision: support `session.updateRole` operation, but this requires re-evaluating and storing new resolved permissions in `sessions.data.scope`.
|
- **Question**: Originally: should a session be able to change roles mid-conversation, like opencode supports? Narrowed by [ADR-015](../decisions/ADR-015-dev-spoke-not-opencode.md): this is a hub-only concern. The hub binds role at session creation. `session.updateRole` is a potential operation if needed, but v1 roles are bound at creation. No opencode agent model to reconcile.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -288,7 +306,7 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
- **Status**: open
|
- **Status**: open
|
||||||
- **Priority**: high
|
- **Priority**: high
|
||||||
- **Question**: The database is the source of truth for tasks; markdown files are the authoring surface. The sync operation (files → database) exists conceptually but is not yet implemented. This blocks the SDD workflow from using database-backed task tracking.
|
- **Question**: The database is the source of truth for tasks; markdown files are the authoring surface. The sync operation (files → database) exists conceptually but is not yet implemented. This blocks the SDD workflow from using database-backed task tracking.
|
||||||
- **Cross-references**: OQ-26 (role sync — similar pattern)
|
- **Cross-references**: OQ-26 (role sync — resolved, similar pattern)
|
||||||
|
|
||||||
### OQ-31: Bulk task status updates
|
### OQ-31: Bulk task status updates
|
||||||
|
|
||||||
@@ -318,31 +336,30 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
### OQ-34: Background migration vs. startup migration
|
### OQ-34: Background migration vs. startup migration
|
||||||
|
|
||||||
- **Origin**: [hub-startup.md](hub-startup.md) OQ-1
|
- **Origin**: [hub-startup.md](hub-startup.md) OQ-1
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-014](../decisions/ADR-014-docker-first-deployment.md)
|
||||||
- **Priority**: medium
|
- **Priority**: medium → resolved
|
||||||
- **Question**: Should migrations block startup, or should they run in the background while the hub serves with the old schema? Recommendation: block for now (simpler, safer). Revisit if startup latency becomes a problem with large migrations.
|
- **Resolution**: Single-container deployment means migrations must complete before the hub serves. Background migration requires schema version negotiation that adds complexity without benefit. Migrations block startup. Docker's restart policy handles failures.
|
||||||
|
|
||||||
### OQ-35: Hot spare / zero-downtime restart
|
### OQ-35: Hot spare / zero-downtime restart
|
||||||
|
|
||||||
- **Origin**: [hub-startup.md](hub-startup.md) OQ-3
|
- **Origin**: [hub-startup.md](hub-startup.md) OQ-3
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-014](../decisions/ADR-014-docker-first-deployment.md)
|
||||||
- **Priority**: low
|
- **Priority**: low → resolved
|
||||||
- **Question**: For production deployments, can we start a new hub process before shutting down the old one? Requires connection draining and session transfer. Deferred — hub is single-instance for now.
|
- **Resolution**: v1 is single-container deployment with Docker restart policy. Zero-downtime restart requires connection draining and session transfer, which is Phase 2. For v1, Docker restart with health checks is sufficient.
|
||||||
- **Cross-references**: [infrastructure.md](infrastructure.md) single-instance model
|
|
||||||
|
|
||||||
### OQ-36: Startup observability
|
### OQ-36: Startup observability
|
||||||
|
|
||||||
- **Origin**: [hub-startup.md](hub-startup.md) OQ-4
|
- **Origin**: [hub-startup.md](hub-startup.md) OQ-4
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-014](../decisions/ADR-014-docker-first-deployment.md)
|
||||||
- **Priority**: low
|
- **Priority**: low → resolved
|
||||||
- **Question**: Should the startup sequence emit pub/sub events so monitoring systems can track progress, or is the `/health` endpoint plus structured logs sufficient? Recommendation: `/health` for now.
|
- **Resolution**: The `/health` endpoint with step-level progress and structured JSON logging to stdout is sufficient. Docker's `HEALTHCHECK` directive and log aggregation handle the rest. No pub/sub startup events needed.
|
||||||
|
|
||||||
### OQ-37: Redis deployment topology
|
### OQ-37: Redis deployment topology
|
||||||
|
|
||||||
- **Origin**: [hub-architecture.md](hub-architecture.md) OQ-1
|
- **Origin**: [hub-architecture.md](hub-architecture.md) OQ-1
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-014](../decisions/ADR-014-docker-first-deployment.md)
|
||||||
- **Priority**: medium
|
- **Priority**: medium → resolved
|
||||||
- **Question**: Redis is deployed on the hub server. For production with many spokes on a compute server, may want Redis closer to containers for lower pub/sub latency. Current approach works for v1 but may need topology changes at scale.
|
- **Resolution**: Redis runs in the same Docker network as the hub. Spokes connect via WebSocket, not Redis. Redis is hub-internal only. Latency between hub and Redis is negligible within the same Docker network. If a future topology needs Redis closer to remote spokes, that would be a spoke-level concern (a spoke-side Redis), not the hub's Redis.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -374,9 +391,9 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
### OQ-41: Gitea operations at startup
|
### OQ-41: Gitea operations at startup
|
||||||
|
|
||||||
- **Origin**: [storage/README.md](storage/README.md) OQ-7
|
- **Origin**: [storage/README.md](storage/README.md) OQ-7
|
||||||
- **Status**: open
|
- **Status**: narrowed
|
||||||
- **Priority**: medium
|
- **Priority**: medium
|
||||||
- **Question**: Load Gitea swagger spec at startup and register ~300 operations via FromOpenAPI. This wires the hub to the Gitea API for repository operations but is not yet implemented.
|
- **Question**: Originally: load Gitea swagger spec at startup and register ~300 operations via FromOpenAPI. Narrowed by [ADR-015](../decisions/ADR-015-dev-spoke-not-opencode.md): Gitea integration is no longer a core hub dependency. Git operations come from the dev spoke (or a separate Gitea spoke via FromOpenAPI). Loading Gitea's OpenAPI spec at startup is optional — a future spoke can provide it. For v1, git operations are exposed through the dev spoke, not hub-native Gitea operations.
|
||||||
|
|
||||||
### OQ-42: Keypal adapter testing
|
### OQ-42: Keypal adapter testing
|
||||||
|
|
||||||
@@ -391,6 +408,7 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
- **Status**: open
|
- **Status**: open
|
||||||
- **Priority**: medium
|
- **Priority**: medium
|
||||||
- **Question**: "The MCP endpoint uses bearer token auth. Each runner gets a token at registration." No detail on token format, rotation, issuance, or how tokens are validated. This connects to OQ-01 (API auth model) and OQ-02 (WebSocket auth).
|
- **Question**: "The MCP endpoint uses bearer token auth. Each runner gets a token at registration." No detail on token format, rotation, issuance, or how tokens are validated. This connects to OQ-01 (API auth model) and OQ-02 (WebSocket auth).
|
||||||
|
- **Cross-references**: OQ-01 (API auth model), OQ-02 (WebSocket auth)
|
||||||
|
|
||||||
### OQ-44: Reactive vs. call graph `requested` semantics
|
### OQ-44: Reactive vs. call graph `requested` semantics
|
||||||
|
|
||||||
@@ -411,8 +429,8 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
- **Origin**: [hub-config.md](hub-config.md) OQ-3
|
- **Origin**: [hub-config.md](hub-config.md) OQ-3
|
||||||
- **Status**: open
|
- **Status**: open
|
||||||
- **Priority**: high
|
- **Priority**: high
|
||||||
- **Question**: The `SpokeConfig.auth` field format is blocked on the spoke-runner WebSocket auth design (OQ-02). Config system supports `tokenFile` but actual protocol is TBD.
|
- **Question**: The `SpokeConfig.auth` field format is blocked on the spoke-runner WebSocket auth design (OQ-02). Config system supports `tokenFile` but actual protocol is TBD. The dev spoke (ADR-015) will use `tokenFile` to read its auth token from a Docker secret or mounted file.
|
||||||
- **Cross-references**: OQ-02 (WebSocket auth)
|
- **Cross-references**: OQ-02 (WebSocket auth), OQ-62 (dev spoke distribution)
|
||||||
|
|
||||||
### OQ-47: Config schema version
|
### OQ-47: Config schema version
|
||||||
|
|
||||||
@@ -447,19 +465,19 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
|
|
||||||
## Theme 10: Future / Low Priority
|
## Theme 10: Future / Low Priority
|
||||||
|
|
||||||
### OQ-51: Role database-authoritative phase (Phase 3)
|
### OQ-51: Role database-authoritative (Phase 3)
|
||||||
|
|
||||||
- **Origin**: [agent-roles.md](agent-roles.md) Phase 3, [storage/roles.md](storage/roles.md)
|
- **Origin**: [agent-roles.md](agent-roles.md) Phase 3, [storage/roles.md](storage/roles.md)
|
||||||
- **Status**: open
|
- **Status**: resolved by [ADR-017](../decisions/ADR-017-hub-first-roles.md)
|
||||||
- **Priority**: low
|
- **Priority**: low → resolved
|
||||||
- **Question**: Eventually, role definitions should be primarily in the database with files only for version control. This is Phase 3 and not blocking for v1.
|
- **Resolution**: The hub is database-first from day one. There is no Phase 1 (file-based) or Phase 2 (file sync). Roles are defined in the `roles` table from the start, seeded by migrations. Markdown files are not part of the hub's role system.
|
||||||
|
|
||||||
### OQ-52: Memory across sessions
|
### OQ-52: Memory across sessions
|
||||||
|
|
||||||
- **Origin**: [agent-roles.md](agent-roles.md) OQ-7
|
- **Origin**: [agent-roles.md](agent-roles.md) OQ-7
|
||||||
- **Status**: open
|
- **Status**: deferred
|
||||||
- **Priority**: low
|
- **Priority**: low
|
||||||
- **Question**: Should LLM accounts have persistent memory across sessions? This is separate from session message history. Could be a `memories` table or vector store. Deferred — separate feature.
|
- **Question**: Should LLM accounts have persistent memory across sessions? This is separate from session message history. Could be a `memories` table or vector store. Deferred — separate feature with no current requirement.
|
||||||
|
|
||||||
### OQ-53: Task versioning
|
### OQ-53: Task versioning
|
||||||
|
|
||||||
@@ -478,9 +496,9 @@ Cross-cutting compilation of all unresolved questions across the hub architectur
|
|||||||
### OQ-55: Anthropic conversation import
|
### OQ-55: Anthropic conversation import
|
||||||
|
|
||||||
- **Origin**: [storage/README.md](storage/README.md) OQ-6
|
- **Origin**: [storage/README.md](storage/README.md) OQ-6
|
||||||
- **Status**: open
|
- **Status**: deferred by [ADR-015](../decisions/ADR-015-dev-spoke-not-opencode.md)
|
||||||
- **Priority**: low
|
- **Priority**: low
|
||||||
- **Question**: Import script for Anthropic conversations is deferred. Export format is documented.
|
- **Question**: Import script for Anthropic conversations. This is a nice-to-have research tool for re-importing past conversations, not a core feature. The format has likely changed since it was last relevant. Not shipped with the codebase.
|
||||||
|
|
||||||
### OQ-56: ADR-013 out-of-scope items
|
### OQ-56: ADR-013 out-of-scope items
|
||||||
|
|
||||||
@@ -527,13 +545,13 @@ These questions block each other or share resolution paths:
|
|||||||
|
|
||||||
2. **Spoke Connectivity Chain**: OQ-06 → OQ-10 — Spoke provisioning can't work without the hub-side WebSocket handler. Resolve OQ-10 first.
|
2. **Spoke Connectivity Chain**: OQ-06 → OQ-10 — Spoke provisioning can't work without the hub-side WebSocket handler. Resolve OQ-10 first.
|
||||||
|
|
||||||
3. **Session Schema Chain**: OQ-16 → OQ-17 → OQ-18 → OQ-19 — Schema finalization blocks compaction and versioning design. Resolve OQ-16 first.
|
3. **Implementation Bootstrap**: OQ-38 → OQ-39 → OQ-40 — Hub startup implementation needs hub config types and proper logger config. These are the minimum viable path to a running hub.
|
||||||
|
|
||||||
4. **Implementation Bootstrap**: OQ-38 → OQ-39 → OQ-40 — Hub startup implementation needs hub config types and proper logger config. These are the minimum viable path to a running hub.
|
4. **Config Security Chain**: OQ-24 → OQ-25 → OQ-50 — Token env deprecation and secret reference resolution are intertwined. OQ-24 must be resolved (remove tokenEnv) before OQ-25 can be validated. After OQ-25, the key rotation background sweep (OQ-50) becomes more important because more secrets flow through `client_secrets`.
|
||||||
|
|
||||||
5. **Config Security Chain**: OQ-24 → OQ-25 → OQ-50 — Token env deprecation and secret reference resolution are intertwined. OQ-24 must be resolved (remove tokenEnv) before OQ-25 can be validated. After OQ-25, the key rotation background sweep (OQ-50) becomes more important because more secrets flow through `client_secrets`.
|
5. **Data Lifecycle Chain**: OQ-12 → OQ-13 → OQ-14 — Operation deletion strategy, call graph retention, and payload truncation interact. OQ-12 determines whether operations can be removed at all.
|
||||||
|
|
||||||
6. **Data Lifecycle Chain**: OQ-12 → OQ-13 → OQ-14 — Operation deletion strategy, call graph retention, and payload truncation interact. OQ-12 determines whether operations can be removed at all.
|
6. **Dev Spoke Chain**: OQ-61 → OQ-62 → OQ-06 — Dev spoke operations and distribution need specification before spoke provisioning can be fully designed. OQ-11 is narrowed by ADR-015 but not resolved.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -543,45 +561,45 @@ These questions block each other or share resolution paths:
|
|||||||
|----|----------|--------|----------|--------|
|
|----|----------|--------|----------|--------|
|
||||||
| OQ-01 | API authentication model | hub-architecture | high | open |
|
| OQ-01 | API authentication model | hub-architecture | high | open |
|
||||||
| OQ-02 | WebSocket auth for spokes | spoke-runner | high | open |
|
| OQ-02 | WebSocket auth for spokes | spoke-runner | high | open |
|
||||||
| OQ-03 | Permission enforcement at call protocol | agent-roles | high | resolved |
|
| OQ-03 | Permission enforcement at call protocol | agent-roles | high | **resolved** |
|
||||||
| OQ-04 | LLM account provisioning | agent-roles | medium | open |
|
| OQ-04 | Service account provisioning | agent-roles | medium | **narrowed** |
|
||||||
| OQ-05 | SSO with Gitea | hub-architecture | medium | open |
|
| OQ-05 | Git provider SSO integration | hub-architecture | low | **narrowed** |
|
||||||
| OQ-06 | Spoke project context | spoke-runner | high | open |
|
| OQ-06 | Spoke project context | spoke-runner | high | open |
|
||||||
| OQ-07 | Source sync for external compute | spoke-runner | medium | open |
|
| OQ-07 | Source sync for external compute | spoke-runner | medium | open |
|
||||||
| OQ-08 | Concurrent spoke operations | spoke-runner | medium | open |
|
| OQ-08 | Concurrent spoke operations | spoke-runner | low | **narrowed** |
|
||||||
| OQ-09 | Spoke operation list freshness | spoke-runner | medium | open |
|
| OQ-09 | Spoke operation list freshness | spoke-runner | low | **narrowed** |
|
||||||
| OQ-10 | Hub-side WebSocket handler design | spoke-runner | high | open |
|
| OQ-10 | Hub-side WebSocket handler design | spoke-runner | high | open |
|
||||||
| OQ-11 | Container spoke lifecycle | spoke-runner, hub-architecture | low | open |
|
| OQ-11 | Dev spoke and compute spoke lifecycle | spoke-runner, hub-architecture | low | **narrowed** |
|
||||||
| OQ-12 | Operation deletion vs. call graph FK | call-graph, storage/spokes | high | open |
|
| OQ-12 | Operation deletion vs. call graph FK | call-graph, storage/spokes | high | open |
|
||||||
| OQ-13 | Call graph retention policy | storage/call-graph, storage/README | medium | open |
|
| OQ-13 | Call graph retention policy | storage/call-graph, storage/README | medium | open |
|
||||||
| OQ-14 | Call graph payload truncation config | storage/call-graph | medium | open |
|
| OQ-14 | Call graph payload truncation config | storage/call-graph | medium | open |
|
||||||
| OQ-15 | Polymorphic FK for `providerId` | storage/spokes | medium | open |
|
| OQ-15 | Polymorphic FK for `providerId` | storage/spokes | medium | open |
|
||||||
| OQ-16 | Session/message schema finalization | agent-sessions, storage/sessions | high | open |
|
| OQ-16 | Session/message schema finalization | agent-sessions, storage/sessions | medium | **resolved (ADR-016)** |
|
||||||
| OQ-17 | Session message compaction | agent-sessions, storage/README | medium | open |
|
| OQ-17 | Session message compaction | agent-sessions, storage/README | low | **resolved (ADR-016)** |
|
||||||
| OQ-18 | Message data versioning | storage/README | medium | open |
|
| OQ-18 | Message data versioning | storage/README | medium | **resolved (ADR-016)** |
|
||||||
| OQ-19 | Part nesting | storage/sessions | low | open |
|
| OQ-19 | Part nesting | storage/sessions | low | **resolved (ADR-016)** |
|
||||||
| OQ-20 | Config reload without restart | hub-config, hub-startup | medium | open |
|
| OQ-20 | Config reload without restart | hub-config, hub-startup | medium | open |
|
||||||
| OQ-21 | CI/CD config generation | hub-config | high | open |
|
| OQ-21 | CI/CD config generation | hub-config | high | **resolved (ADR-014)** |
|
||||||
| OQ-22 | Multiple config file layers | hub-config | low | open |
|
| OQ-22 | Multiple config file layers | hub-config | low | **resolved (ADR-014)** |
|
||||||
| OQ-23 | PostgresConfig SSL details | hub-config | medium | open |
|
| OQ-23 | PostgresConfig SSL details | hub-config | low | **resolved (ADR-014)** |
|
||||||
| OQ-24 | HTTPServiceConfig.auth.tokenEnv deprecation | hub-config, operations | high | open |
|
| OQ-24 | HTTPServiceConfig.auth.tokenEnv deprecation | hub-config, operations | high | open |
|
||||||
| OQ-25 | Secret reference resolution ordering | hub-config | medium | open |
|
| OQ-25 | Secret reference resolution ordering | hub-config | medium | open |
|
||||||
| OQ-26 | Role import/sync operation | agent-roles, storage/README, storage/roles | medium | open |
|
| OQ-26 | Role import/sync operation | agent-roles, storage/README, storage/roles | medium | **resolved (ADR-017)** |
|
||||||
| OQ-27 | Role inheritance with permission resolution | agent-roles | medium | open |
|
| OQ-27 | Role inheritance with permission resolution | agent-roles | medium | open |
|
||||||
| OQ-28 | Dynamic role creation | agent-roles | low | open |
|
| OQ-28 | Dynamic role creation | agent-roles | low | **resolved (ADR-017)** |
|
||||||
| OQ-29 | Per-session role switching | agent-roles | medium | open |
|
| OQ-29 | Per-session role switching | agent-roles | medium | **narrowed** |
|
||||||
| OQ-30 | Task storage and sync implementation | storage/README | high | open |
|
| OQ-30 | Task storage and sync implementation | storage/README | high | open |
|
||||||
| OQ-31 | Bulk task status updates | storage/tasks | medium | open |
|
| OQ-31 | Bulk task status updates | storage/tasks | medium | open |
|
||||||
| OQ-32 | Cross-project task dependencies | storage/tasks | low | open |
|
| OQ-32 | Cross-project task dependencies | storage/tasks | low | open |
|
||||||
| OQ-33 | Task embeddings | storage/tasks | low | open |
|
| OQ-33 | Task embeddings | storage/tasks | low | open |
|
||||||
| OQ-34 | Background vs. startup migration | hub-startup | medium | open |
|
| OQ-34 | Background vs. startup migration | hub-startup | medium | **resolved (ADR-014)** |
|
||||||
| OQ-35 | Hot spare / zero-downtime restart | hub-startup | low | open |
|
| OQ-35 | Hot spare / zero-downtime restart | hub-startup | low | **resolved (ADR-014)** |
|
||||||
| OQ-36 | Startup observability | hub-startup | low | open |
|
| OQ-36 | Startup observability | hub-startup | low | **resolved (ADR-014)** |
|
||||||
| OQ-37 | Redis deployment topology | hub-architecture | medium | open |
|
| OQ-37 | Redis deployment topology | hub-architecture | medium | **resolved (ADR-014)** |
|
||||||
| OQ-38 | Hub startup implementation | hub-startup | high | open |
|
| OQ-38 | Hub startup implementation | hub-startup | high | open |
|
||||||
| OQ-39 | Hub-specific config in operations package | operations | high | open |
|
| OQ-39 | Hub-specific config in operations package | operations | high | open |
|
||||||
| OQ-40 | Logger configuration | operations | medium | open |
|
| OQ-40 | Logger configuration | operations | medium | open |
|
||||||
| OQ-41 | Gitea operations at startup | storage/README | medium | open |
|
| OQ-41 | Gitea operations at startup | storage/README | medium | **narrowed** |
|
||||||
| OQ-42 | Keypal adapter testing | storage/README | medium | open |
|
| OQ-42 | Keypal adapter testing | storage/README | medium | open |
|
||||||
| OQ-43 | MCP endpoint authentication detail | mcp-server | medium | open |
|
| OQ-43 | MCP endpoint authentication detail | mcp-server | medium | open |
|
||||||
| OQ-44 | Reactive vs. call graph requested semantics | call-graph | medium | open |
|
| OQ-44 | Reactive vs. call graph requested semantics | call-graph | medium | open |
|
||||||
@@ -591,20 +609,22 @@ These questions block each other or share resolution paths:
|
|||||||
| OQ-48 | Cross-doc terminology migration | storage/README | low | open |
|
| OQ-48 | Cross-doc terminology migration | storage/README | low | open |
|
||||||
| OQ-49 | ADR-012 migration | decisions/ADR-012 | medium | open |
|
| OQ-49 | ADR-012 migration | decisions/ADR-012 | medium | open |
|
||||||
| OQ-50 | Key rotation background sweep | decisions/storage-spec-phase1 | high | open |
|
| OQ-50 | Key rotation background sweep | decisions/storage-spec-phase1 | high | open |
|
||||||
| OQ-51 | Role database-authoritative (Phase 3) | agent-roles, storage/roles | low | open |
|
| OQ-51 | Role database-authoritative (Phase 3) | agent-roles, storage/roles | low | **resolved (ADR-017)** |
|
||||||
| OQ-52 | Memory across sessions | agent-roles | low | open |
|
| OQ-52 | Memory across sessions | agent-roles | low | **deferred** |
|
||||||
| OQ-53 | Task versioning | storage/tasks | low | open |
|
| OQ-53 | Task versioning | storage/tasks | low | open |
|
||||||
| OQ-54 | High-contention task notes | storage/tasks | low | open |
|
| OQ-54 | High-contention task notes | storage/tasks | low | open |
|
||||||
| OQ-55 | Anthropic conversation import | storage/README | low | open |
|
| OQ-55 | Anthropic conversation import | storage/README | low | **deferred** |
|
||||||
| OQ-56 | ADR-013 out-of-scope items | decisions/ADR-013 | low | open |
|
| OQ-56 | ADR-013 out-of-scope items | decisions/ADR-013 | low | open |
|
||||||
| OQ-57 | Call graph visualization | call-graph | low | open |
|
| OQ-57 | Call graph visualization | call-graph | low | open |
|
||||||
| OQ-58 | Stream deduplication | call-graph | medium | open |
|
| OQ-58 | Stream deduplication | call-graph | medium | open |
|
||||||
| OQ-59 | `requested_by` edge in flowgraph | call-graph | low | open |
|
| OQ-59 | `requested_by` edge in flowgraph | call-graph | low | open |
|
||||||
| OQ-60 | Full ujsx call templates | call-graph | low | open |
|
| OQ-60 | Full ujsx call templates | call-graph | low | open |
|
||||||
|
| OQ-61 | Dev spoke operations | ADR-015 | medium | open |
|
||||||
|
| OQ-62 | Dev spoke distribution and config | ADR-015 | medium | open |
|
||||||
|
|
||||||
### High Priority Open Questions (Blocking)
|
### High Priority Open Questions (Blocking)
|
||||||
|
|
||||||
These 11 questions block core functionality and should be resolved first:
|
These questions block core functionality and should be resolved first:
|
||||||
|
|
||||||
| ID | Question | Blocks |
|
| ID | Question | Blocks |
|
||||||
|----|----------|--------|
|
|----|----------|--------|
|
||||||
@@ -613,8 +633,6 @@ These 11 questions block core functionality and should be resolved first:
|
|||||||
| OQ-06 | Spoke project context | Spoke provisioning |
|
| OQ-06 | Spoke project context | Spoke provisioning |
|
||||||
| OQ-10 | Hub-side WebSocket handler design | All spoke functionality |
|
| OQ-10 | Hub-side WebSocket handler design | All spoke functionality |
|
||||||
| OQ-12 | Operation deletion vs. call graph FK | Operation lifecycle |
|
| OQ-12 | Operation deletion vs. call graph FK | Operation lifecycle |
|
||||||
| OQ-16 | Session/message schema finalization | Session storage implementation |
|
|
||||||
| OQ-21 | CI/CD config generation | Deployment |
|
|
||||||
| OQ-24 | HTTPServiceConfig.auth.tokenEnv deprecation | Security (env var leak) |
|
| OQ-24 | HTTPServiceConfig.auth.tokenEnv deprecation | Security (env var leak) |
|
||||||
| OQ-38 | Hub startup implementation | All functionality |
|
| OQ-38 | Hub startup implementation | All functionality |
|
||||||
| OQ-39 | Hub-specific config in operations package | Hub startup |
|
| OQ-39 | Hub-specific config in operations package | Hub startup |
|
||||||
@@ -630,8 +648,15 @@ Suggested order for resolving the high-priority questions, based on dependency c
|
|||||||
3. **OQ-02** — WebSocket auth (unblocks OQ-10, OQ-46)
|
3. **OQ-02** — WebSocket auth (unblocks OQ-10, OQ-46)
|
||||||
4. **OQ-10** — Hub-side WebSocket handler (enables spokes)
|
4. **OQ-10** — Hub-side WebSocket handler (enables spokes)
|
||||||
5. **OQ-24** — tokenEnv deprecation (security fix)
|
5. **OQ-24** — tokenEnv deprecation (security fix)
|
||||||
6. **OQ-16** — Session/message schema (enables storage)
|
6. **OQ-12** — Operation deletion strategy (data integrity)
|
||||||
7. **OQ-12** — Operation deletion strategy (data integrity)
|
7. **OQ-06** — Spoke project context (spoke provisioning)
|
||||||
8. **OQ-21** — CI/CD config generation (deployment)
|
8. **OQ-50** — Key rotation sweep (production secret management)
|
||||||
9. **OQ-06** — Spoke project context (spoke provisioning)
|
|
||||||
10. **OQ-50** — Key rotation sweep (production secret management)
|
### Questions Resolved by ADRs
|
||||||
|
|
||||||
|
| ADR | Questions Resolved | Key Decisions |
|
||||||
|
|-----|-------------------|---------------|
|
||||||
|
| [ADR-014](../decisions/ADR-014-docker-first-deployment.md) | OQ-21, OQ-22, OQ-23, OQ-34, OQ-35, OQ-36, OQ-37 | Docker as primary deployment; Redis/Postgres same network; config via mounted volumes; single-container restart; migrations block startup |
|
||||||
|
| [ADR-015](../decisions/ADR-015-dev-spoke-not-opencode.md) | OQ-16, OQ-17, OQ-26, OQ-28, OQ-51, OQ-55 | Dev spoke replaces opencode integration; hub owns session format; opencode compat is import tool; opencode-spoke is optional future |
|
||||||
|
| [ADR-016](../decisions/ADR-016-hub-own-schema.md) | OQ-18, OQ-19 | Hub defines own canonical schema; JSONB implicitly versioned; flat parts for v1; import is compat tool |
|
||||||
|
| [ADR-017](../decisions/ADR-017-hub-first-roles.md) | OQ-26, OQ-28, OQ-51 | Database-first roles; seeded by migrations; `hub.createRole` for custom roles; no `.opencode/agents/` file sync |
|
||||||
52
docs/decisions/ADR-014-docker-first-deployment.md
Normal file
52
docs/decisions/ADR-014-docker-first-deployment.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# ADR-014: Docker-first deployment model
|
||||||
|
|
||||||
|
- **Status**: Accepted
|
||||||
|
- **Date**: 2026-05-26
|
||||||
|
- **Deciders**: alkdev
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The hub needs a deployment model. Several architecture questions were left open about how the hub relates to its infrastructure dependencies (Postgres, Redis), how configuration is managed across environments, and what assumptions the architecture can make about the runtime environment.
|
||||||
|
|
||||||
|
The previous iteration (alkhub_ts) was designed for a specific production infrastructure with specific host IPs and manual setup. The hub (`@alkdev/hub`) is designed to be a generalized, OSS-first project that others can deploy.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
The hub assumes Docker as its primary deployment model. This resolves several open questions:
|
||||||
|
|
||||||
|
1. **Postgres and Redis run in the same Docker network** — No cross-network TLS needed between hub and its data stores. `PostgresConfig.ssl` can remain `boolean` for v1 (same-network communication doesn't require TLS between containers). TLS termination happens at the reverse proxy (nginx/caddy) for external traffic.
|
||||||
|
|
||||||
|
2. **Configuration is encrypted files + Docker secrets** — The `alkhub-config` CLI encrypts config values. CI/CD doesn't need the master key because config files are pre-encrypted and mounted at runtime. The master key is a Docker secret provisioned by the operator. This eliminates the "how does CI/CD get the key?" problem (OQ-21).
|
||||||
|
|
||||||
|
3. **Docker Compose handles environment variation** — Dev vs. prod differences are handled via different Docker Compose files and different mounted config files. No overlay config system needed (OQ-22).
|
||||||
|
|
||||||
|
4. **Single-container restart is sufficient for v1** — No hot spare or zero-downtime restart needed. Docker's restart policy handles failures. Connection draining and session transfer are Phase 2 concerns (OQ-35).
|
||||||
|
|
||||||
|
5. **Startup observability via `/health` + Docker logs** — The `/health` endpoint with step-level progress and structured JSON logging to stdout is sufficient. Docker's `HEALTHCHECK` directive and log aggregation handle the rest. No pub/sub startup events needed (OQ-36).
|
||||||
|
|
||||||
|
6. **Migrations block startup** — Single-container deployment means the hub must have a consistent schema before serving. Background migrations require schema version negotiation that adds complexity without benefit in a single-container model (OQ-34).
|
||||||
|
|
||||||
|
7. **Redis is hub-internal** — Redis runs in the same Docker network as the hub. Spokes connect via WebSocket (not Redis), so Redis topology is a deployment detail, not an architectural concern (OQ-37).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
**Positive**: Deployment model is explicit and consistent. Config, networking, and observability all assume Docker conventions. Open questions about SSL between hub and Postgres, CI/CD key management, config overlays, and zero-downtime restarts are resolved by the Docker model.
|
||||||
|
|
||||||
|
**Negative**: Docker is a hard dependency for production deployment. Developers who want to run the hub outside Docker must provide their own Postgres and Redis, and handle TLS and config management themselves. This is acceptable — the Docker model is the documented production path.
|
||||||
|
|
||||||
|
### Open questions resolved by this decision
|
||||||
|
|
||||||
|
| OQ | Resolution |
|
||||||
|
|----|-----------|
|
||||||
|
| OQ-21 | CI/CD doesn't need the master key — config files are pre-encrypted and mounted at runtime; master key is a Docker secret |
|
||||||
|
| OQ-22 | No overlay config needed — Docker Compose handles environment variation via mounted volumes |
|
||||||
|
| OQ-34 | Block migrations at startup — single-container model requires consistent schema before serving |
|
||||||
|
| OQ-35 | Single-container restart is v1 — Docker restart policy handles failures; zero-downtime is Phase 2 |
|
||||||
|
| OQ-36 | `/health` + Docker logs is sufficient — no pub/sub startup events needed |
|
||||||
|
| OQ-37 | Redis is hub-internal in Docker — same network, not an architectural concern |
|
||||||
|
|
||||||
|
### Open questions narrowed by this decision
|
||||||
|
|
||||||
|
| OQ | Narrowing |
|
||||||
|
|----|-----------|
|
||||||
|
| OQ-23 | `PostgresConfig.ssl: boolean` is sufficient for same-network Docker deployment. TLS between containers is a deployment concern, not an app config concern. |
|
||||||
87
docs/decisions/ADR-015-dev-spoke-not-opencode.md
Normal file
87
docs/decisions/ADR-015-dev-spoke-not-opencode.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# ADR-015: Dev spoke instead of opencode integration
|
||||||
|
|
||||||
|
- **Status**: Accepted
|
||||||
|
- **Date**: 2026-05-26
|
||||||
|
- **Deciders**: alkdev
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The original hub architecture included tight integration with opencode (an external tool) for several capabilities:
|
||||||
|
|
||||||
|
- Agent sessions were modeled on opencode's session/message format
|
||||||
|
- Coordination operations called `opencode.sessionCreate`, `opencode.sessionPromptAsync`, etc.
|
||||||
|
- Agent roles were defined in `.opencode/agents/*.md` files
|
||||||
|
- The `ai-sdk-provider-opencode-sdk` package wrapped opencode as an AI SDK model
|
||||||
|
- Opencode's SQLite database was the reference format for message storage
|
||||||
|
|
||||||
|
This created a two-left-hand problem: the hub's architecture was shaped by opencode's data model and conventions, but the hub is a generalized platform that other users won't run opencode on. The integration surface was large (sessions, messages, roles, tools, git operations) and the conceptual overhead was significant (understanding opencode's model to understand the hub).
|
||||||
|
|
||||||
|
Meanwhile, the hub already has the spoke model for extending capabilities. A "dev env spoke" that exposes bash, file operations, git, and other development tools would provide the same functionality as opencode's tool suite, but connected to the hub via the standard call protocol over WebSocket — just like any other spoke.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Replace the opencode integration with a purpose-built **dev spoke**. The dev spoke is a compiled Deno binary that connects to the hub via WebSocket and exposes development operations (bash, file ops, git, web search) as hub operations. This sits alongside the existing spoke types (client spoke, GPU compute spoke) as just another spoke.
|
||||||
|
|
||||||
|
The hub owns sessions, messages, and roles in its own format. Opencode is no longer a core dependency or a shaping force on the hub's architecture. If opencode compatibility is needed in the future, it comes through an **opencode-spoke** — an optional spoke that wraps an opencode instance and exposes its operations through the standard call protocol, just like any other spoke.
|
||||||
|
|
||||||
|
### What changes
|
||||||
|
|
||||||
|
- **Session model**: Hub defines its own canonical message/part format (based on AI SDK `UIMessage` + parts), not opencode's format. The hub's format stays close to opencode's for import compatibility, but this is a compat concern, not an architectural constraint.
|
||||||
|
|
||||||
|
- **Agent roles**: Hub defines roles in the database (see ADR-017). No `.opencode/agents/*.md` file import is needed. The hub is database-first from day one.
|
||||||
|
|
||||||
|
- **Coordination operations**: `coord.spawn`, `coord.message`, etc. no longer call `opencode.*` operations. They call hub operations that create sessions, send messages, and manage worktrees — implemented by the dev spoke or hub-native logic.
|
||||||
|
|
||||||
|
- **Dev tools**: bash, file operations, git commands come from the dev spoke, not from opencode. The dev spoke is a small, focused binary that does one thing well.
|
||||||
|
|
||||||
|
- **Session hosting**: Agent sessions run via the AI SDK directly in the hub (for architect, decomposer, etc.) or via the dev spoke (for implementation specialist). No opencode container required.
|
||||||
|
|
||||||
|
### What doesn't change
|
||||||
|
|
||||||
|
- The call protocol, call graph, and operation graph are unchanged
|
||||||
|
- WebSocket spoke transport is unchanged (dev spoke is just another spoke)
|
||||||
|
- Session/message storage in Postgres is unchanged (just simpler without opencode import compat shaping the schema)
|
||||||
|
- Auth model, config system, startup sequence — all unchanged
|
||||||
|
- `hub.list`/`hub.search`/`hub.schema`/`hub.call` — unchanged
|
||||||
|
- The hub never "spoke opencode" — it spoke the call protocol
|
||||||
|
|
||||||
|
### Future: opencode-spoke
|
||||||
|
|
||||||
|
An optional opencode-spoke can be built later that:
|
||||||
|
- Connects to the hub via WebSocket (standard spoke protocol)
|
||||||
|
- Wraps an opencode instance
|
||||||
|
- Exposes opencode's operations through FromOpenAPI (opencode's REST spec → typed operations)
|
||||||
|
- Imports opencode's SQLite session data into the hub's Postgres
|
||||||
|
|
||||||
|
This is a spoke-level concern, not a hub architecture concern.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
**Positive**: The hub is self-contained and doesn't depend on opencode's data model, conventions, or implementation. The dev spoke is a bounded, implementable component. The hub's session format, role model, and coordination operations are designed for the hub's needs, not adapted from another project. Opencode users can still integrate via an optional spoke.
|
||||||
|
|
||||||
|
**Negative**: The dev spoke needs to be built. It replaces the "install opencode and connect it" model with "download a binary and connect it." The dev spoke needs its own implementation of bash, file ops, git, and web search — these are well-understood operations but still need implementation. The `ai-sdk-provider-opencode-sdk` package is no longer a dependency.
|
||||||
|
|
||||||
|
### Open questions resolved by this decision
|
||||||
|
|
||||||
|
| OQ | Resolution |
|
||||||
|
|----|-----------|
|
||||||
|
| OQ-16 | Hub defines its own canonical message/part format. Opencode's format is an import concern, not an architectural constraint. The format stays close for compat but the hub owns it. |
|
||||||
|
| OQ-17 | Compaction is a hub concern (pruning/summarization for long sessions), not an opencode "compaction agent" concern. For v1, full message history is served. |
|
||||||
|
| OQ-26 | No `roles.sync` from `.opencode/agents/*.md` needed. Hub is database-first for roles (see ADR-017). |
|
||||||
|
| OQ-28 | No `Agent.generate()` support needed. The hub creates sessions with DB-defined roles. |
|
||||||
|
| OQ-29 | Per-session role switching is a hub-only concern. No opencode agent model to reconcile. |
|
||||||
|
| OQ-51 | No file→DB role migration needed. Hub is database-first from day one. |
|
||||||
|
| OQ-55 | Anthropic conversation import is deferred. Not shipped with the codebase. |
|
||||||
|
|
||||||
|
### Open questions narrowed by this decision
|
||||||
|
|
||||||
|
| OQ | Narrowing |
|
||||||
|
|----|-----------|
|
||||||
|
| OQ-11 | Container spoke is now "dev spoke" — a compiled binary, not an opencode container. Simpler scope, no opencode integration needed. |
|
||||||
|
|
||||||
|
### New open questions
|
||||||
|
|
||||||
|
| ID | Question |
|
||||||
|
|----|----------|
|
||||||
|
| OQ-61 | What operations does the dev spoke expose? |
|
||||||
|
| OQ-62 | How is the dev spoke distributed and configured? |
|
||||||
45
docs/decisions/ADR-016-hub-own-schema.md
Normal file
45
docs/decisions/ADR-016-hub-own-schema.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# 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:
|
||||||
|
|
||||||
|
1. **AI SDK `UIMessage` compatibility** is the primary design constraint — direct agents (architect, decomposer, etc.) produce `UIMessage` format, and the hub's API assembles `messages` + `parts` into `UIMessage` for consumption.
|
||||||
|
|
||||||
|
2. **The hub's `data` JSONB columns** are implicitly versioned by their TypeBox schema evolution. No separate `version` column 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 each `data` shape contains at any point in time.
|
||||||
|
|
||||||
|
3. **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.
|
||||||
|
|
||||||
|
4. **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`, `reasoning` are core; `step-start`, `step-finish`, `snapshot`, `patch` may be added as needed.
|
||||||
|
|
||||||
|
5. **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 `agent` field to the hub's `roleName`, 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. |
|
||||||
79
docs/decisions/ADR-017-hub-first-roles.md
Normal file
79
docs/decisions/ADR-017-hub-first-roles.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# 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. |
|
||||||
Reference in New Issue
Block a user