Copy architecture docs, ADRs, storage domain specs, research, reviews, and 56 storage architecture tasks from the alkhub_ts monorepo. Adapt for standalone @alkdev/hub repo structure (src/ not packages/hub/). Sanitize all sensitive information: - Replace private IPs (10.0.0.1) with localhost defaults - Remove internal server hostnames (dev1, ns528096) - Replace /workspace/ private paths with npm package references - Remove hardcoded credentials from examples - Rewrite infrastructure.md without private network details Add Deno project scaffolding: deno.json (pinned deps), .gitignore, AGENTS.md, entry point. Migrate existing code stubs (crypto, config types, logger) with updated import paths.
4.9 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level |
|---|---|---|---|---|---|---|---|
| split-operations-into-definitions-and-registrations | Split operation_specs into operations (definitions) + operation_registrations | completed | broad | high | phase | implementation |
Description
The current operation_specs table conflates two concepts:
-
Operation Definition: The schema/contract — what the operation is named, its input/output types, access level. Shared across multiple provider instances (e.g., 5 opencode instances all provide the same
chat.completeoperation). -
Operation Registration: A specific provider (spoke or client) offering that operation right now. Ephemeral — registrations come and go as spokes connect/disconnect.
This conflation creates problems:
- No way to distinguish "a spoke provides this" from "this operation exists as a concept"
- Disconnect lifecycle ambiguity (delete vs soft-deactivate) becomes a schema design problem
- OpenAPI/MCP spec imports create definitions without any provider; the current schema has no home for these
- Same-named operations from multiple spokes have no natural representation
Design Decision (D3)
Split into two tables:
operations (definitions):
| Column | Type | Notes |
|---|---|---|
| id | text PK | |
| namespace | text | Post-remap identifier (e.g., dev.{spokeId}.fs.read) |
| name | text | |
| type | text | query, mutation, subscription |
| inputSchema | jsonb | TypeBox schema |
| outputSchema | jsonb | TypeBox schema |
| accessLevel | text | |
| description | text | |
| ...commonCols | createdAt, updatedAt |
operation_registrations (instances):
| Column | Type | Notes |
|---|---|---|
| id | text PK | |
| operationId | text FK → operations.id | CASCADE on delete (def deleted → registrations gone) |
| providerType | text | "spoke" or "client" |
| providerId | text FK → spokes.id or clients.id | Polymorphic — see notes |
| status | text | "active" or "inactive" |
| preRemapNamespace | text | Original spoke identifier (e.g., dev.fs.read) for traceability |
| preRemapName | text | Original spoke name |
| metadata | jsonb | Provider-specific metadata |
| registeredAt | timestamp | |
| ...commonCols | createdAt, updatedAt |
Key Implications
- On spoke disconnect →
operation_registrations.statusset toinactive. Definitions survive. Reconnection re-activates or creates new registration. - On admin spoke-row deletion →
operation_registrationsCASCADE (ephemeral config, D1). If no other registrations exist for an operation, the definition may be cleaned up by a separate process. - OpenAPI/MCP imports → create
operationsrows from spec, no registration until a client connects. - Namespace convention →
operations.namespace/namestore post-remap identifiers.operation_registrationsstores the pre-remap identifiers for traceability. - Call routing → lookup operation by namespace+name, then find active registrations, dispatch to provider.
- Partial unique indexes →
operationshas unique on(namespace, name)(no more spoke-scoped partial unique).operation_registrationshas partial unique on(operationId, providerType, providerId) WHERE status = 'active'.
Acceptance Criteria
spokes.mddocuments theoperationstable schema with all columnsspokes.mddocuments theoperation_registrationstable schema with all columnsoperation_specsreferences are replaced withoperations/operation_registrationsacross all docstable-reference.mdhas FK entries for:operation_registrations.operationId → operations.id(CASCADE),operation_registrations.providerId → spokes.id(SET NULL on disconnect — providerId becomes null when spoke row is deleted; or RESTRICT if spoke rows are never deleted)spokes.mddisconnect lifecycle is unambiguous: "deactivates the spoke's operation_registrations (sets status to inactive). Operation definitions persist."- ADR-006 is updated to reflect the definitions/registrations split (or superseded by a new ADR)
README.mdOpen Question #2 is resolved with a cross-reference to the decision- The review item W04 (pre/post-remap namespace) is resolved:
operationsstores post-remap,operation_registrationsstores pre-remap call-graph.mdandcoordination.mdreferences tooperation_specsare updated if applicable
References
- docs/reviews/storage-architecture-review-2026-04-21.md#C03
- docs/reviews/storage-architecture-review-2026-04-21.md#W04
- docs/decisions/storage-spec-phase1-resolutions.md#D3
- docs/architecture/storage/spokes.md
- docs/architecture/storage/table-reference.md
- docs/decisions/ADR-006
- docs/architecture/spoke-runner.md:62
Notes
This task subsumes the original resolve-op-specs-lifecycle and clarify-operation-specs-namespace tasks. Both are resolved by the definitions/registrations split.
Summary
To be filled on completion