Files
hub/tasks/architecture/storage/split-operations-into-definitions-and-registrations.md
glm-5.1 2b63cda1c7 Setup repo: migrate architecture specs, code stubs, and tasks from alkhub_ts
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.
2026-05-25 10:56:32 +00:00

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:

  1. 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.complete operation).

  2. 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 disconnectoperation_registrations.status set to inactive. Definitions survive. Reconnection re-activates or creates new registration.
  • On admin spoke-row deletionoperation_registrations CASCADE (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 operations rows from spec, no registration until a client connects.
  • Namespace conventionoperations.namespace/name store post-remap identifiers. operation_registrations stores the pre-remap identifiers for traceability.
  • Call routing → lookup operation by namespace+name, then find active registrations, dispatch to provider.
  • Partial unique indexesoperations has unique on (namespace, name) (no more spoke-scoped partial unique). operation_registrations has partial unique on (operationId, providerType, providerId) WHERE status = 'active'.

Acceptance Criteria

  • spokes.md documents the operations table schema with all columns
  • spokes.md documents the operation_registrations table schema with all columns
  • operation_specs references are replaced with operations/operation_registrations across all docs
  • table-reference.md has 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.md disconnect 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.md Open Question #2 is resolved with a cross-reference to the decision
  • The review item W04 (pre/post-remap namespace) is resolved: operations stores post-remap, operation_registrations stores pre-remap
  • call-graph.md and coordination.md references to operation_specs are 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