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.
This commit is contained in:
41
tasks/architecture/storage/add-account-deactivation.md
Normal file
41
tasks/architecture/storage/add-account-deactivation.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: add-account-deactivation
|
||||
name: Add Account Deactivation Mechanism
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W02: The `accounts` table has no `enabled`/`suspended` column. Combined with `organizations.ownerId → RESTRICT`, an org owner's account cannot be deleted, and there's no way to deactivate when an employee leaves. Other tables (`api_keys`, `clients`) already have `enabled` columns — add consistency.
|
||||
|
||||
Add a `status` enum column (`active`/`suspended`/`deactivated`) to the `accounts` table spec. Document how deactivation interacts with cascade constraints and active sessions.
|
||||
|
||||
## Decision (D5)
|
||||
|
||||
Use a `status` enum (`active` | `suspended` | `deactivated`), not a boolean. More extensible — allows distinguishing admin-suspended from user-deactivated in the future. This is especially important because RESTRICT cascade on audit_logs.ownerId means accounts with audit entries can never be hard-deleted; deactivation is the path forward.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `identity.md` accounts table includes `status` pgEnum (`active`/`suspended`/`deactivated`) with NOT NULL default `active`
|
||||
- [ ] Interaction with cascade constraints documented (e.g., deactivated accounts can still own orgs but cannot authenticate)
|
||||
- [ ] `table-reference.md` updated with the new column
|
||||
- [ ] Consistency with `api_keys.enabled` and `clients.enabled` patterns noted
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W02
|
||||
- docs/decisions/storage-spec-phase1-resolutions.md#D5
|
||||
- docs/architecture/storage/identity.md (accounts table)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
37
tasks/architecture/storage/add-audit-log-context.md
Normal file
37
tasks/architecture/storage/add-audit-log-context.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: add-audit-log-context
|
||||
name: Add Session and Org Context to Audit Logs
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W12: `audit_logs` has `ownerId` and `keyId` but no `sessionId` or `orgId`. For LLM accounts in sessions, session correlation is a traceability gap. Multi-tenant auditing requires org filtering.
|
||||
|
||||
Add `sessionId` (nullable FK → sessions.id, SET NULL) and `orgId` (nullable FK → organizations.id, SET NULL) to the `audit_logs` table spec. Expand `action` types to cover account, membership, and organization lifecycle events, or document the `action` enum as extensible.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `identity.md` audit_logs table includes nullable `sessionId` and `orgId` columns
|
||||
- [ ] FK cascade behavior documented (SET NULL for both)
|
||||
- [ ] `table-reference.md` cascade table includes the two new FK entries
|
||||
- [ ] `action` enum either expanded with lifecycle event types or documented as extensible
|
||||
- [ ] `table-reference.md` enum section updated
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W12
|
||||
- docs/architecture/storage/identity.md:103-117
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: add-call-graph-edges-indexes-cascades
|
||||
name: Add call_graph_edges indexes and cascade documentation
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
`call_graph_edges` has no indexes and no cascade entries in `table-reference.md`. Both `sourceId` and `targetId` reference `call_graph_nodes.id` with CASCADE (from `call-graph.md`), but this is undocumented in the cross-cutting reference. Without indexes, graph traversal queries (find children, find parents) will require sequential scans.
|
||||
|
||||
Additionally, the relationship between `call_graph_nodes.parentRequestId` and `call_graph_edges` is ambiguous: do they store the same parent-child relationship redundantly, or serve different purposes?
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `table-reference.md` index table includes: `idx_call_graph_edges_source_id` on `(sourceId)`, `idx_call_graph_edges_target_id` on `(targetId)`
|
||||
- [ ] Consider and document whether unique constraint on `(sourceId, targetId, edgeType)` is needed to prevent duplicates
|
||||
- [ ] `table-reference.md` cascade table includes: `call_graph_edges.sourceId → call_graph_nodes.id` with CASCADE, `call_graph_edges.targetId → call_graph_nodes.id` with CASCADE
|
||||
- [ ] `call-graph.md` is updated with the index definitions
|
||||
- [ ] The `parentRequestId` vs `call_graph_edges` relationship is clarified in `call-graph.md`: document whether `parentRequestId` is a convenience shortcut or redundant with edges
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C10
|
||||
- docs/architecture/storage/call-graph.md:32-41
|
||||
- docs/architecture/storage/table-reference.md (missing entries)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
36
tasks/architecture/storage/add-caller-account-id.md
Normal file
36
tasks/architecture/storage/add-caller-account-id.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
id: add-caller-account-id
|
||||
name: Add callerAccountId to Call Graph Nodes
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W19: `call_graph_nodes.identity` stores `{ id, scopes, resources }` as a JSONB snapshot, but there's no FK to `accounts.id`. Querying "all calls made by account X" requires JSONB containment, which is slow without a GIN index.
|
||||
|
||||
Add a `callerAccountId` text column with FK → accounts.id (SET NULL) for efficient querying, or add a GIN index on `identity` if JSONB queries are the intended access pattern.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `call-graph.md` adds `callerAccountId` column (text, nullable, FK → accounts.id, ON DELETE SET NULL), OR
|
||||
- [ ] Alternative documented: GIN index on `identity` column with justification
|
||||
- [ ] If `callerAccountId` added: `table-reference.md` cascade table updated with the new FK
|
||||
- [ ] Query pattern documented: "all calls by account X" uses `callerAccountId`
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W19
|
||||
- docs/architecture/storage/call-graph.md:20
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
41
tasks/architecture/storage/add-cross-project-dep-guard.md
Normal file
41
tasks/architecture/storage/add-cross-project-dep-guard.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: add-cross-project-dep-guard
|
||||
name: Add DB-level guard for cross-project task dependencies
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
"Tasks can only depend on tasks within the same project" is declared in `tasks.md:217` but only "enforced at the application level." `task_dependencies` has FK columns with no `projectId` or check constraint. Application-level enforcement is vulnerable to race conditions, direct SQL access, or bugs.
|
||||
|
||||
Choose one:
|
||||
- **(A)** Add a DB trigger that checks `dependsOnTaskId` and `taskId` belong to the same project
|
||||
- **(B)** Add a denormalized `projectId` column to `task_dependencies` with a composite FK
|
||||
- **(C)** Document the risk explicitly and specify that the sync operation validates project scope within a transaction (SELECT FOR SHARE)
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] The chosen enforcement mechanism is documented in `tasks.md`
|
||||
- [ ] If option A: trigger definition is specified; `tasks.md` notes the trigger name and behavior
|
||||
- [ ] If option B: `projectId` column is added to `task_dependencies` schema; composite FK documented; `table-reference.md` updated
|
||||
- [ ] If option C: the risk is documented and the sync validation pattern is specified (SELECT FOR SHARE + INSERT in same transaction)
|
||||
- [ ] The cross-project dependency invariant is restated in `tasks.md` with the enforcement mechanism
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C09
|
||||
- docs/architecture/storage/tasks.md:217
|
||||
- docs/architecture/storage/tasks.md:357
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
52
tasks/architecture/storage/add-missing-cascade-entries.md
Normal file
52
tasks/architecture/storage/add-missing-cascade-entries.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
id: add-missing-cascade-entries
|
||||
name: Add missing FK cascade entries to table-reference.md
|
||||
status: completed
|
||||
depends_on: [resolve-sessions-accountid-cascade, resolve-audit-logs-ownerid-cascade, split-operations-into-definitions-and-registrations, add-call-graph-edges-indexes-cascades]
|
||||
scope: moderate
|
||||
risk: critical
|
||||
impact: project
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Five FK relationships are documented in per-domain docs but **absent** from the cascade reference table in `table-reference.md:53-83`:
|
||||
|
||||
| Missing Relationship | Source Doc |
|
||||
|---|---|
|
||||
| `mappings.workspaceId → workspaces.id` | coordination.md:19 |
|
||||
| `detections.sessionId → sessions.id` | coordination.md:36 |
|
||||
| `call_graph_edges.sourceId → call_graph_nodes.id` | call-graph.md:39 |
|
||||
| `call_graph_edges.targetId → call_graph_nodes.id` | call-graph.md:41 |
|
||||
| `api_keys.rotatedToId → api_keys.id` | identity.md:80 |
|
||||
|
||||
Without documented cascade behavior, PostgreSQL defaults to `RESTRICT`, which may not be the intended behavior for all of these.
|
||||
|
||||
This task depends on other cascade decisions being resolved first: C01 (sessions.accountId, audit_logs.ownerId), C03 (op specs cascade rationale), and C10 (call_graph_edges cascade behavior). Once those are stable, add all missing entries in a consistent pass.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `mappings.workspaceId → workspaces.id` cascade is added to table-reference.md with explicit onDelete behavior and rationale
|
||||
- [ ] `detections.sessionId → sessions.id` cascade is added with explicit onDelete behavior and rationale
|
||||
- [ ] `call_graph_edges.sourceId → call_graph_nodes.id` cascade is added (CASCADE, as documented in call-graph.md)
|
||||
- [ ] `call_graph_edges.targetId → call_graph_nodes.id` cascade is added (CASCADE, as documented in call-graph.md)
|
||||
- [ ] `api_keys.rotatedToId → api_keys.id` cascade is added — review recommends SET NULL (old key keeps its data, rotation link broken if new key is deleted)
|
||||
- [ ] All cascade entries include rationale column explaining the design decision
|
||||
- [ ] No other FK relationships documented in per-domain docs are missing from the cascade table
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C14
|
||||
- docs/architecture/storage/table-reference.md:53-83
|
||||
- docs/architecture/storage/coordination.md:19,36
|
||||
- docs/architecture/storage/call-graph.md:39,41
|
||||
- docs/architecture/storage/identity.md:80
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
45
tasks/architecture/storage/add-missing-indexes-identity.md
Normal file
45
tasks/architecture/storage/add-missing-indexes-identity.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
id: add-missing-indexes-identity
|
||||
name: Add Missing Indexes — Identity & Project Domain
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W03 (partial): Add missing indexes for the identity and project domain tables. Also remove the redundant index identified in the review.
|
||||
|
||||
Missing indexes:
|
||||
- `projects`: `idx_projects_org_id` on `(orgId)` — find projects for an org
|
||||
- `workspaces`: `idx_workspaces_project_id` on `(projectId)` — find workspaces for a project
|
||||
- `spokes`: `idx_spokes_name` on `(name)` — look up spoke by name
|
||||
|
||||
Redundant index to remove:
|
||||
- `api_keys`: `idx_api_keys_key_hash` is redundant with `unq_api_keys_key_hash` (UNIQUE constraint auto-creates an index)
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `table-reference.md` indexes section includes `idx_projects_org_id`, `idx_workspaces_project_id`, `idx_spokes_name`
|
||||
- [ ] Per-domain docs (`projects.md`, `spokes.md`) reference the new indexes
|
||||
- [ ] `idx_api_keys_key_hash` removed from `table-reference.md` with a note that UNIQUE constraint covers it
|
||||
- [ ] All new indexes have documented purpose (query pattern)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W03
|
||||
- docs/architecture/storage/table-reference.md:87-145
|
||||
- docs/architecture/storage/identity.md
|
||||
- docs/architecture/storage/projects.md
|
||||
- docs/architecture/storage/spokes.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
id: add-missing-indexes-observability
|
||||
name: Add Missing Indexes — Call Graph & Observability Domain
|
||||
status: completed
|
||||
depends_on: [add-call-graph-edges-indexes-cascades]
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W03 (partial): Add missing indexes for call graph tables. These are closely related to C10 (call graph edges missing indexes and cascade documentation) — the cascade documentation should be resolved first, then indexes added here.
|
||||
|
||||
Missing indexes:
|
||||
- `call_graph_nodes`: `idx_call_graph_nodes_created_at` on `(createdAt)` — time-range queries
|
||||
- `call_graph_nodes`: `idx_call_graph_nodes_operation_created` on `(operationId, createdAt)` — operation + time queries
|
||||
- `call_graph_edges`: `idx_call_graph_edges_source_id` on `(sourceId)` — graph traversal (children)
|
||||
- `call_graph_edges`: `idx_call_graph_edges_target_id` on `(targetId)` — graph traversal (parents)
|
||||
|
||||
Note: The edge indexes are also called out in C10. This task focuses on adding them to the spec docs; C10's critical task resolves the cascade documentation and `parentRequestId` ambiguity.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `table-reference.md` indexes section includes all four call graph indexes
|
||||
- [ ] `call-graph.md` per-domain doc references the new indexes
|
||||
- [ ] Index purposes documented (graph traversal for edges, time-range/compound for nodes)
|
||||
- [ ] Cross-reference to C10 resolution noted if cascade entries were already added
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W03
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C10
|
||||
- docs/architecture/storage/table-reference.md
|
||||
- docs/architecture/storage/call-graph.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
42
tasks/architecture/storage/add-missing-indexes-sessions.md
Normal file
42
tasks/architecture/storage/add-missing-indexes-sessions.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
id: add-missing-indexes-sessions
|
||||
name: Add Missing Indexes — Sessions & Coordination Domain
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W03 (partial): Add missing indexes for sessions and coordination domain tables.
|
||||
|
||||
Missing indexes:
|
||||
- `sessions`: `unq_sessions_slug` — UNIQUE constraint on `slug` not listed (unlike other UNIQUEs)
|
||||
- `sessions`: `idx_sessions_parent_id` on `(parentId)` — find child sessions of coordinator
|
||||
- `detections`: `idx_detections_session_id` on `(sessionId)` — find detections for a session (table currently has no indexes at all)
|
||||
- `mappings`: `idx_mappings_workspace_id` on `(workspaceId)` — workspace-scoped mapping queries
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `table-reference.md` indexes section includes `unq_sessions_slug`, `idx_sessions_parent_id`, `idx_detections_session_id`, `idx_mappings_workspace_id`
|
||||
- [ ] Per-domain docs (`sessions.md`, `coordination.md`) reference the new indexes
|
||||
- [ ] `detections` table now has at least one index documented
|
||||
- [ ] All new indexes have documented purpose (query pattern)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W03
|
||||
- docs/architecture/storage/table-reference.md:87-145
|
||||
- docs/architecture/storage/sessions.md
|
||||
- docs/architecture/storage/coordination.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
48
tasks/architecture/storage/add-partial-indexes.md
Normal file
48
tasks/architecture/storage/add-partial-indexes.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: add-partial-indexes
|
||||
name: Add Partial Indexes and Call Graph Index Improvements
|
||||
status: completed
|
||||
depends_on: ["add-missing-indexes-identity", "add-missing-indexes-observability", "add-missing-indexes-sessions"]
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Four related index/constraint improvements across different domains:
|
||||
|
||||
1. **S02**: Add partial indexes for common access patterns — active API keys (`WHERE revoked_at IS NULL AND enabled = true`), connected spokes (`WHERE status = 'connected'`), non-archived sessions, active tasks (`WHERE status IN ('pending', 'in-progress', 'blocked')`).
|
||||
|
||||
2. **S04**: Add `accounts.displayName` index for user search/autocomplete UIs. Without it, user search requires full table scans.
|
||||
|
||||
3. **S17**: Add `call_graph_nodes.startedAt` index alongside or instead of `createdAt` for p99 latency analysis queries.
|
||||
|
||||
4. **S18**: Add unique constraint on `call_graph_edges(sourceId, targetId, edgeType)` to prevent duplicate edges from retries/reconnections.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Partial indexes defined in `table-reference.md` for: active API keys, connected spokes, non-archived sessions, active tasks
|
||||
- [ ] `displayName` index added to accounts table in `identity.md` and `table-reference.md`
|
||||
- [ ] `startedAt` index added to call_graph_nodes in `call-graph.md` and `table-reference.md`
|
||||
- [ ] Unique constraint on `(sourceId, targetId, edgeType)` added to call_graph_edges in `call-graph.md` and `table-reference.md`
|
||||
- [ ] All indexes documented with WHERE clauses/purpose in per-domain docs
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S02
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S04
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S17
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S18
|
||||
- docs/architecture/storage/table-reference.md
|
||||
- docs/architecture/storage/identity.md
|
||||
- docs/architecture/storage/call-graph.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
36
tasks/architecture/storage/add-spoke-reconnecting-state.md
Normal file
36
tasks/architecture/storage/add-spoke-reconnecting-state.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
id: add-spoke-reconnecting-state
|
||||
name: Add Spoke 'reconnecting' Status or Document DB-Free Approach
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W06: The spoke status enum is only `connected`/`disconnected`. The spoke-runner.md describes a reconnection flow, but there's no `reconnecting` state. A spoke with a dropped WebSocket shows `disconnected`, indistinguishable from a permanently offline spoke.
|
||||
|
||||
Either add `reconnecting` to the spoke status enum, or document that reconnection is handled at the application layer (WebSocket reconnect timer) without a DB state change.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Either `reconnecting` added to `spokes.status` enum in `spokes.md` and `table-reference.md`, OR
|
||||
- [ ] Document explicitly that reconnection is application-layer-only (no DB state change) with rationale
|
||||
- [ ] `spoke-runner.md` reconnection flow section references the decision
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W06
|
||||
- docs/architecture/storage/spokes.md:18
|
||||
- docs/architecture/spoke-runner.md:130-136
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
32
tasks/architecture/storage/cascade-decisions.md
Normal file
32
tasks/architecture/storage/cascade-decisions.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
id: cascade-decisions
|
||||
name: Cascade Decisions
|
||||
status: completed
|
||||
depends_on: [resolve-sessions-accountid-cascade, resolve-audit-logs-ownerid-cascade, split-operations-into-definitions-and-registrations, add-call-graph-edges-indexes-cascades, add-missing-cascade-entries]
|
||||
scope: moderate
|
||||
risk: critical
|
||||
impact: phase
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
All FK cascade resolution tasks. This group consolidates every decision about ON DELETE/ON UPDATE behavior for foreign keys across the storage layer. Getting these wrong causes data loss or orphaned rows, so all cascade policies must be resolved and documented before any implementation begins.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] All dependent tasks are completed
|
||||
- [ ] Cross-references between completed tasks are consistent
|
||||
- [ ] No remaining contradictions in the covered domain
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
50
tasks/architecture/storage/clarify-session-spec-details.md
Normal file
50
tasks/architecture/storage/clarify-session-spec-details.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
id: clarify-session-spec-details
|
||||
name: Clarify Sessions Spec — Part Types, Slug, Indexing, Nesting
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: trivial
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Five closely related documentation gaps in `sessions.md` that affect implementers:
|
||||
|
||||
1. **S07**: `FilePartData[]` is referenced in ToolState (`sessions.md:132`) but never defined. Clarify whether it's the same as the `file` part type's data shape.
|
||||
|
||||
2. **S08**: The AI SDK UIMessage part type mapping (`sessions.md:145-152`) covers 6 types but omits `step-finish`, `patch`, `snapshot`, `compaction`, `agent`. Document whether these are excluded from the UIMessage view or add mappings.
|
||||
|
||||
3. **S09**: `sessions.slug` generation strategy is undocumented. Is it human-provided, auto-generated, or random? This matters for API design and uniqueness enforcement.
|
||||
|
||||
4. **S10**: A composite index `(session_id, type)` on parts would support queries like "all tool-call parts in session X." Document whether this index is needed or whether existing indexes suffice.
|
||||
|
||||
5. **S11**: The `agent` part type implies sub-agent delegation which might need nesting, but parts have no `parentId`. Document whether parts are flat or nesting might be needed.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `FilePartData` type defined or cross-referenced in sessions.md
|
||||
- [ ] Omitted AI SDK part types (`step-finish`, `patch`, `snapshot`, `compaction`, `agent`) explicitly documented as excluded or mapped
|
||||
- [ ] `sessions.slug` generation strategy documented (human-provided vs auto-generated)
|
||||
- [ ] Parts `(session_id, type)` index considered and either added or documented as unnecessary
|
||||
- [ ] Flat vs nested parts semantics documented — if nesting may be needed, note it as a future concern
|
||||
- [ ] All changes in `sessions.md`; `table-reference.md` updated if indexes change
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S07
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S08
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S09
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S10
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S11
|
||||
- docs/architecture/storage/sessions.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
39
tasks/architecture/storage/clean-resolved-open-questions.md
Normal file
39
tasks/architecture/storage/clean-resolved-open-questions.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: clean-resolved-open-questions
|
||||
name: Move Resolved Open Questions to Decisions Section
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W15: Several open questions in README.md are resolved by per-domain docs or ADRs but remain listed as open:
|
||||
- Q2 (operation spec cleanup): Resolved — DELETE aligns with ephemeral spoke model
|
||||
- Q4 (workspaces vs directories): Marked as "Resolved" but still present in open questions
|
||||
- Q14 (`accounts.role` → `accessLevel`): Renamed in identity.md, referenced in ADR-012
|
||||
|
||||
Move resolved items to a "Resolved Decisions" section with cross-references to the resolving documents.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] README.md has a "Resolved Decisions" section
|
||||
- [ ] Q2, Q4, Q14 moved to the new section with cross-references to resolving docs
|
||||
- [ ] Open Questions section only contains still-unresolved items
|
||||
- [ ] Each resolved entry references the doc/ADR that resolved it
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W15
|
||||
- docs/architecture/storage/README.md:197-225
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
54
tasks/architecture/storage/config-hub-startup-system.md
Normal file
54
tasks/architecture/storage/config-hub-startup-system.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: config-hub-startup-system
|
||||
name: Specify Hub Config System and Startup Sequence
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: broad
|
||||
risk: high
|
||||
impact: project
|
||||
level: planning
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Architecture specifications completed 2026-04-23. The original task was split into two concerns:
|
||||
|
||||
1. **Config system** → [docs/architecture/hub-config.md](../../docs/architecture/hub-config.md) — schemas, file format, two-layer key model (master key via Docker secret + data encryption keys in encrypted config file), `_encrypted` wrapper pattern, `alkhub-config` CLI tool.
|
||||
|
||||
2. **Startup sequence** → [docs/architecture/hub-startup.md](../../docs/architecture/hub-startup.md) — 11-step ordered startup, fail-fast on missing prerequisites, health check contract with step-level progress, graceful shutdown mirroring startup.
|
||||
|
||||
ADR-008 revised to replace env var pattern with Docker secret + encrypted config file pattern.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- **No env vars for secrets** — Hard rule. No important keys or config in environment variables. Sensitive values arrive via Docker secret (master key) or encrypted config fields (postgres, redis, data encryption keys).
|
||||
- **Two-layer keys** — Master key (Docker secret) decrypts config file. Data encryption keys (`v1:base64,v2:base64`, inside the encrypted config) are used for `client_secrets`. Independent rotation schedules.
|
||||
- **Whole-value encryption** — `postgres` and `redis` sections are each encrypted as a single `EncryptedData` blob, not field-by-field. This leaks less topology information.
|
||||
- **Fail-fast startup** — No retry loops. If Postgres, Redis, or config is unavailable, exit immediately. Container orchestrator handles restarts.
|
||||
- **Config read-once** — Config file is loaded and validated once at startup. Runtime changes require restart.
|
||||
- **Module-scope side effects prohibited** — All initialization inside `startHub()`. No globals, no `export const db = drizzle(pool)`.
|
||||
|
||||
## Acceptance Criteria — Completed
|
||||
|
||||
- [x] HubConfig TypeBox schema specified — hub-config.md § Config Schema Hierarchy
|
||||
- [x] SpokeConfig / BaseConfig schema hierarchy specified — hub-config.md
|
||||
- [x] Master key provisioning mechanism documented — Docker secret `/run/secrets/hub_master_key`, hub-config.md § Master Key Provisioning
|
||||
- [x] Config loading startup sequence specified — hub-startup.md § Startup Sequence (11 steps)
|
||||
- [x] Multi-key encryption format documented — hub-config.md § Multi-Key Format; services.md updated to reference hub-config.md
|
||||
- [x] Naming consistent — "data encryption keys" (in config), not env var references. ADR-008 revised. services.md updated.
|
||||
- [x] crypto.ts bridge documented — hub-config.md § loadConfig (master key → decrypt config → resolveEncryptionKeys for data keys). Master key is a passphrase string consumed by PBKDF2; data encryption keys are base64 values used directly.
|
||||
- [x] Docker deployment notes — hub-config.md § Master Key Provisioning, infrastructure.md updated with Docker secret + config file mounting
|
||||
|
||||
## Cascade Updates
|
||||
|
||||
The following docs were updated to align with the no-env-vars pattern:
|
||||
|
||||
- `docs/architecture/storage/services.md` — replaced `HUB_ENCRYPTION_KEY` env var references with data encryption key ring from hub config
|
||||
- `docs/architecture/infrastructure.md` — replaced `docker run -e DATABASE_URL=... -e REDIS_URL=...` with Docker secret + config file mounting
|
||||
- `docs/architecture/storage/README.md` — removed `Deno.env.get()` from DB connection code; updated test setup to use test config files
|
||||
- `docs/decisions/ADR-008-secrets-encrypted-at-rest-with-key-versioning.md` — revised from env var to Docker secret + two-layer key model
|
||||
|
||||
## Open Items
|
||||
|
||||
- `SpokeConfig.auth` field format is blocked on spoke-runner.md WebSocket auth design (hub-config.md Open Question #3)
|
||||
- Both specs are draft-stage — need another review pass before marking stable
|
||||
32
tasks/architecture/storage/data-integrity-specs.md
Normal file
32
tasks/architecture/storage/data-integrity-specs.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
id: data-integrity-specs
|
||||
name: Data Integrity Specs
|
||||
status: completed
|
||||
depends_on: [enforce-parts-session-id-invariant, document-mappings-task-denorm-invariant, define-sync-field-split, specify-task-body-append-concurrency, add-cross-project-dep-guard, resolve-org-dual-ownership]
|
||||
scope: moderate
|
||||
risk: high
|
||||
impact: phase
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
All data integrity / enforcement mechanism tasks. This group ensures that invariants, denormalization constraints, concurrency guarantees, and ownership models are specified at the spec level before any code is written. Fixing integrity bugs after implementation is orders of magnitude harder than preventing them.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] All dependent tasks are completed
|
||||
- [ ] Cross-references between completed tasks are consistent
|
||||
- [ ] No remaining contradictions in the covered domain
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
37
tasks/architecture/storage/define-call-graph-retention.md
Normal file
37
tasks/architecture/storage/define-call-graph-retention.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: define-call-graph-retention
|
||||
name: Define Call Graph Retention Policy
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W09: Call graph data grows unboundedly — every operation invocation creates a node and edges. CASCADE handles cleanup on node deletion, but nothing deletes old nodes. README.md acknowledges this as Open Question #5 but no approach is specified.
|
||||
|
||||
Specify the intended approach: TTL-based deletion, archival to cold storage, or aggregation + deletion. Even a phased notation ("v1: manual cleanup, v2: automatic TTL") helps implementers understand the plan.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `call-graph.md` includes a "Retention Policy" section
|
||||
- [ ] Short-term approach specified (e.g., manual cleanup or no cleanup for v1)
|
||||
- [ ] Long-term approach outlined (e.g., TTL-based, aggregation + deletion)
|
||||
- [ ] README.md Open Question #5 resolved with cross-reference to call-graph.md section
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W09
|
||||
- docs/architecture/storage/call-graph.md
|
||||
- docs/architecture/storage/README.md (Open Question #5)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: define-session-version-semantics
|
||||
name: Define sessions.version Semantics and Default
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W10: The `sessions.version` column is `text NOT NULL` described as "Schema version (opencode compat)" but no valid values, default, or versioning scheme is defined. README.md Open Question #1 on versioning `data` columns is unresolved.
|
||||
|
||||
Define the initial version value (e.g., `"1"`), document what `version` governs (the `data` JSONB shape? the message/parts schema? opencode compatibility only?), and specify the default for hub-direct sessions vs opencode imports.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Valid version values defined (e.g., `"1"`, with convention for future updates)
|
||||
- [ ] Documented what `version` governs (data schema, session schema, or opencode compat only)
|
||||
- [ ] Default value for hub-direct sessions specified
|
||||
- [ ] Default value for opencode-imported sessions specified
|
||||
- [ ] `sessions.md` updated with the versioning contract
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W10
|
||||
- docs/architecture/storage/sessions.md:24
|
||||
- docs/architecture/storage/README.md (Open Question #1)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
37
tasks/architecture/storage/define-sync-field-split.md
Normal file
37
tasks/architecture/storage/define-sync-field-split.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: define-sync-field-split
|
||||
name: Define sync vs runtime field split for tasks
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: high
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
The task sync does a full upsert, but the Authority Model says runtime status mutations go through `hub.task.updateStatus`. If sync blindly writes frontmatter `status`, it can clobber runtime state. Example: Agent sets `task.status = 'in-progress'` via hub operation, then decomposer edits the task file (still has `status: pending`), then sync runs and overwrites `in-progress` back to `pending`.
|
||||
|
||||
Define the sync field split explicitly in `tasks.md`: sync upserts **authored fields** (slug, name, path, scope, risk, impact, level, priority, tags, assignee, due, body, fileCreatedAt, fileModifiedAt, depends_on) and must **not overwrite runtime-managed fields** (status, startedAt, completedAt). Runtime fields are only mutated via `hub.task.*` operations.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `tasks.md` has an explicit "Field Authority Split" section listing authored fields vs runtime-managed fields
|
||||
- [ ] The sync flow specification is updated: upsert uses `ON CONFLICT DO UPDATE SET` only for authored fields
|
||||
- [ ] Runtime fields (status, startedAt, completedAt) are explicitly excluded from the sync upsert
|
||||
- [ ] The Authority Model table in tasks.md is updated to include the field-level split
|
||||
- [ ] A warning is added: "Sync must never write `status`, `startedAt`, or `completedAt` — these are owned by hub operations"
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C07
|
||||
- docs/architecture/storage/tasks.md:296-325
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: document-accesslevel-authorization
|
||||
name: Document accessLevel Authorization and Identity Cross-References
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: trivial
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Two documentation gaps in the identity domain:
|
||||
|
||||
1. **S01**: Who can change `accounts.accessLevel`? Can a `user` self-promote? The assumed invariants for application-level access control are undocumented.
|
||||
|
||||
2. **S06**: `identity.md:12` lists FK targets but omits `sessions.accountId`. Add it for completeness so the identity doc is a full reference.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `identity.md` documents authorization rules for `accessLevel` changes (e.g., only `admin` can promote, users cannot self-promote)
|
||||
- [ ] `identity.md` FK target list includes `sessions.accountId → accounts.id`
|
||||
- [ ] Rules are consistent with ADR-012 terminology
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S01
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S06
|
||||
- docs/architecture/storage/identity.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
id: document-api-key-expiration-behavior
|
||||
name: Document API Key Expiration Behavior
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: single
|
||||
risk: trivial
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
**S05**: Does an expired API key return "key expired" or a generic "authentication failed"? Without documentation, implementers may leak key state to attackers by returning specific error messages. Recommend documenting that expired keys return a generic authentication failure to avoid information disclosure.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `identity.md` or `services.md` documents API key expiration error behavior
|
||||
- [ ] Recommendation stated: expired keys return generic auth failure, not "key expired"
|
||||
- [ ] Consistent with ADR-008 (key rotation) and keypal integration notes
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S05
|
||||
- docs/architecture/storage/identity.md (api_keys table)
|
||||
- docs/decisions/ADR-008
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: document-cross-table-status-mapping
|
||||
name: Document Cross-Table Status Enum Disambiguation
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W11: Three tables have `status` with overlapping values (`completed`, `failed`, `aborted` appear on `mappings`, `call_graph_nodes`, and `tasks`), but the meanings differ (e.g., `mappings.completed` ≠ `tasks.completed`). `table-reference.md` only contrasts `mappings.active` vs `call_graph_nodes.pending/running` — it doesn't contrast `tasks` statuses with the others.
|
||||
|
||||
Add cross-table state mapping documentation. When a task goes `in-progress`, there should be an active mapping; when a task is `completed`, the mapping becomes `completed`. Document valid combinations and the semantic differences between same-named statuses in different tables.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `table-reference.md` has a cross-table status mapping section (table or diagram)
|
||||
- [ ] Each shared status value (`completed`, `failed`, `aborted`) has per-table semantic definition
|
||||
- [ ] Valid cross-table status combinations documented (e.g., task `in-progress` ⟹ mapping `active`)
|
||||
- [ ] `tasks.status` lifecycle contrasted with `mappings.status` lifecycle
|
||||
- [ ] Note that `mappings.completed` and `tasks.completed` have different semantic scope
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W11
|
||||
- docs/architecture/storage/table-reference.md:147-164
|
||||
- docs/architecture/storage/coordination.md:23
|
||||
- docs/architecture/storage/tasks.md:84-86
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
36
tasks/architecture/storage/document-edge-type-semantics.md
Normal file
36
tasks/architecture/storage/document-edge-type-semantics.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
id: document-edge-type-semantics
|
||||
name: Document Call Graph Edge Type Semantics
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W05: `call_graph_edges.edgeType` lists three values (`triggered`, `depends_on`, `requested_by`) with no explanation. Only `triggered` (parent-child) is discussed in the architecture doc. `depends_on` and `requested_by` are novel and undocumented. It's unclear whether the set is exhaustive or extensible.
|
||||
|
||||
Document each edge type's semantics, or state that `edgeType` is an extensible text field with initial values and define what each means.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `call-graph.md` documents each edge type: `triggered`, `depends_on`, `requested_by`
|
||||
- [ ] Whether the enum is exhaustive or extensible is explicitly stated
|
||||
- [ ] If extensible: document the convention for adding new edge types
|
||||
- [ ] `table-reference.md` enum section updated to reflect the decision
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W05
|
||||
- docs/architecture/storage/call-graph.md:41
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: document-jsonb-column-boundaries
|
||||
name: Document JSONB Column Boundaries (metadata vs data)
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W01: The `commonCols.metadata` and per-table `data` JSONB columns overlap with no documented boundary. `api_keys` stores `scopes`/`resources`/`tags` inside `metadata`; `accounts` has both `data` (preferences, avatar) and `metadata` (arbitrary) with overlapping purposes. An implementer cannot determine which column to use for what.
|
||||
|
||||
Define and document the boundary: `data` holds structured domain-specific data with known TypeScript types; `metadata` holds opaque key-value pairs for subsystem use with a namespacing convention (e.g., `_keypal.scopes`).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `README.md` documents the boundary between `commonCols.metadata` and per-table `data` columns
|
||||
- [ ] Namespacing convention for `metadata` is specified (e.g., `_subsystem.key`)
|
||||
- [ ] Each table that has both columns lists what belongs in each (`identity.md` for `accounts`, `api_keys`)
|
||||
- [ ] Update `keypal` usage note: `api_keys.metadata._keypal.scopes` pattern documented
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W01
|
||||
- docs/architecture/storage/README.md:73
|
||||
- docs/architecture/storage/identity.md:85-88, :23
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: document-mappings-task-denorm-invariant
|
||||
name: Document mappings.task denormalization sync strategy
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
The `mappings` table has both `taskId` (FK → tasks.id) and `task` (denormalized display name). No mechanism keeps them in sync. If `taskId` points to a task whose `slug` or `name` changes, `mappings.task` becomes stale. When `taskId` is SET NULL (task deleted), what happens to `task`?
|
||||
|
||||
The review recommends documenting the invariant: "`mappings.task` is set to `tasks.slug` at insert time and is **not** automatically updated when the task's slug changes. When `taskId` is SET NULL (task deleted), `task` should also be SET NULL. This is a cache, not a source of truth." Alternatively, remove the denormalized column and use a VIEW that joins.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `coordination.md` documents the invariant for `mappings.task`: it is a cache of `tasks.slug`, set at insert time, not auto-updated
|
||||
- [ ] The behavior when `taskId` is SET NULL is documented: `task` should also be SET NULL (application-level or trigger)
|
||||
- [ ] If keeping the column: add a note that stale `task` values are acceptable (cache semantics)
|
||||
- [ ] If removing the column: replace with a VIEW definition and update coordination.md
|
||||
- [ ] `tasks.md:209` reference to `mappings.task` is updated to be consistent
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C06
|
||||
- docs/architecture/storage/coordination.md:22
|
||||
- docs/architecture/storage/tasks.md:209
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
38
tasks/architecture/storage/document-mappings-valid-shapes.md
Normal file
38
tasks/architecture/storage/document-mappings-valid-shapes.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: document-mappings-valid-shapes
|
||||
name: Document Valid Column Combinations for Mappings Table
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W20: The `mappings` table stores three conceptually different relationships in one table: session→spoke, session→parent session, session→task. All nullable FKs allow any combination, including invalid ones. The table name `mappings` doesn't convey what's mapped.
|
||||
|
||||
Document the valid column combinations (polymorphic association shapes): `sessionId` always NOT NULL; `taskId` only for task-scoped mappings; `parentSessionId` only for coordinator children. This makes it a polymorphic association table with documented shapes.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `coordination.md` documents valid column combinations (mapping shapes)
|
||||
- [ ] Shape 1: Session→Spoke (`sessionId` + `spokeId`, no `taskId`/`parentSessionId`)
|
||||
- [ ] Shape 2: Session→Parent (`sessionId` + `parentSessionId`, no `taskId`)
|
||||
- [ ] Shape 3: Session→Task (`sessionId` + `taskId`, no `parentSessionId`)
|
||||
- [ ] Invalid combinations documented (e.g., `taskId` + `parentSessionId` on same row)
|
||||
- [ ] `sessionId` noted as always NOT NULL for any mapping
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W20
|
||||
- docs/architecture/storage/coordination.md:10-27
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: document-org-ownership-transfer
|
||||
name: Document Org Ownership Transfer Workflow
|
||||
status: completed
|
||||
depends_on: [resolve-org-dual-ownership]
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W16: RESTRICT prevents deletion of accounts that own organizations, but no ownership transfer mechanism is documented. This depends on C13 (dual ownership model) being resolved first, since the transfer workflow depends on whether `ownerId` or `membershipLevel: "owner"` is authoritative.
|
||||
|
||||
Add transfer documentation: "Before deleting an account, transfer all owned organizations via `org.transferOwnership` operation." Document the transfer pattern in identity.md.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `identity.md` documents the `org.transferOwnership` operation/workflow
|
||||
- [ ] Precondition: transfer must happen before account deletion (RESTRICT constraint)
|
||||
- [ ] Transfer includes updating `organizations.ownerId` to the new owner
|
||||
- [ ] Transfer includes updating `organization_members.membershipLevel` if membership-based ownership is in use
|
||||
- [ ] Dependency on C13 resolution noted (the authoritative ownership field determines what gets transferred)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W16
|
||||
- docs/architecture/storage/identity.md:44
|
||||
- docs/architecture/storage/table-reference.md:56
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
36
tasks/architecture/storage/document-payload-redaction.md
Normal file
36
tasks/architecture/storage/document-payload-redaction.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
id: document-payload-redaction
|
||||
name: Document Call Graph Payload Redaction Strategy
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W08: The `input` and `output` JSONB columns on `call_graph_nodes` store full call payloads. Operations like `hub.register` (which receives auth tokens) would store API keys and secrets in cleartext. The truncation strategy (10KB) addresses size, not sensitive data. No redaction is mentioned.
|
||||
|
||||
Add a section to `call-graph.md` on sensitive data handling. Options: operation handlers mark fields as redacted; call graph writer applies field-level redaction by convention (fields named `password`, `token`, `secret`, `key`); truncation strategy extended with redaction pass.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `call-graph.md` has a "Sensitive Data Handling" section
|
||||
- [ ] Redaction strategy is specified (field-level by convention, handler-driven, or both)
|
||||
- [ ] Default redacted field names defined (e.g., `password`, `token`, `secret`, `key`, `apiKey`, `authorization`)
|
||||
- [ ] Redaction applies before DB write (not on read)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W08
|
||||
- docs/architecture/storage/call-graph.md:22-23
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
37
tasks/architecture/storage/document-sha256-tradeoff.md
Normal file
37
tasks/architecture/storage/document-sha256-tradeoff.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: document-sha256-tradeoff
|
||||
name: Document SHA-256 API Key Hashing Trade-Off
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W13: API keys are stored as SHA-256 hashes — a fast hash, not a deliberately slow KDF (bcrypt/Argon2). If the database is compromised, SHA-256 hashes can be brute-forced faster than slow hashes. However, API keys are high-entropy machine-generated strings (128-bit+), making brute-force infeasible even with a fast hash. No ADR documents this trade-off.
|
||||
|
||||
Add documentation to ADR-010 or relevant section explaining why SHA-256 is acceptable for high-entropy API keys.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] ADR-010 (or a new ADR) documents the SHA-256 vs KDF trade-off
|
||||
- [ ] Rationale includes: high-entropy keys make brute-force infeasible, O(1) verification latency at high throughput
|
||||
- [ ] Explicitly states this is acceptable because keys are machine-generated, unlike human passwords
|
||||
- [ ] Cross-reference from `identity.md` api_keys section to the ADR
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W13
|
||||
- docs/architecture/storage/identity.md:74
|
||||
- docs/decisions/ADR-010
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: document-system-account-email-convention
|
||||
name: Document System Account Email Convention
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: single
|
||||
risk: trivial
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
**S03** (reframed): System-generated accounts (LLMs, bots, services) need email addresses for attribution in git commits, audit logs, and platform identity. To prevent collision between human and system-generated accounts, deployments need a documented convention.
|
||||
|
||||
The email domain/pattern is **deployment-configurable** — do NOT hardcode any specific domain in architecture documentation. Example conventions: `{model}@llm.example.com`, `{model}@system.example.com`, or any pattern the deployment chooses. The key requirement is that the convention is documented so implementers know how system accounts are identified.
|
||||
|
||||
## Decision
|
||||
|
||||
See `docs/decisions/storage-spec-phase1-resolutions.md` (D6): email reservation is a deployment concern, not an architecture hardcode.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `identity.md` documents the configurable email convention pattern for system accounts
|
||||
- [ ] Documentation explicitly states that NO specific domain is hardcoded — the pattern is deployment-specific
|
||||
- [ ] A note is added to the accounts table or email column description explaining the convention
|
||||
- [ ] Example pattern is shown (e.g., `{model}@system.example.com`) with a clear "example only" annotation
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S03
|
||||
- docs/architecture/storage/identity.md
|
||||
- docs/decisions/storage-spec-phase1-resolutions.md#D6
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: enforce-parts-session-id-invariant
|
||||
name: Document and enforce parts.sessionId denormalization invariant
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: high
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
The invariant stated in `sessions.md:96`: "when inserting a part, always set `sessionId` to the message's `sessionId`. Never update `messages.sessionId` without updating all child parts." However, there is no DB trigger, no CHECK constraint, no application-level transaction pattern, and no immutability guarantee for `sessionId`.
|
||||
|
||||
The review recommends declaring `sessionId` on both `messages` and `parts` as **immutable after creation**, which eliminates the update problem. Define the application-level contract for part insertion and add an explicit "IMMUTABLE" note.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `sessions.md` documents that `sessionId` on both `messages` and `parts` is **IMMUTABLE after creation**
|
||||
- [ ] The column spec for `messages.sessionId` and `parts.sessionId` includes an IMMUTABLE annotation
|
||||
- [ ] The application-level contract for part insertion is documented: "read the message's `sessionId` and set it on the part within the same transaction"
|
||||
- [ ] The denormalization rationale is preserved but the invariant enforcement mechanism is explicit
|
||||
- [ ] Note added: "No DB trigger enforces this — immutability is respected by application layer; direct SQL must not update sessionId"
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C04
|
||||
- docs/architecture/storage/sessions.md:96
|
||||
- docs/architecture/storage/sessions.md:105
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
36
tasks/architecture/storage/enhance-detections-table.md
Normal file
36
tasks/architecture/storage/enhance-detections-table.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
id: enhance-detections-table
|
||||
name: Add Resolution Tracking and Dedup to Detections
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W21: The `detections` table has no resolution tracking (resolved/acknowledged/false-positive), no deduplication (persistent `MODEL_DEGRADATION` creates new row every check interval), no session end correlation, and `anomalyType` value set is unclear.
|
||||
|
||||
Add `resolvedAt` timestamp column. Add a UNIQUE constraint on `(sessionId, anomalyType)` with upsert semantics, or document that deduplication is handled at the application level. Specify whether `anomalyType` is extensible.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `coordination.md` detections table includes nullable `resolvedAt` column
|
||||
- [ ] Deduplication strategy documented: UNIQUE on `(sessionId, anomalyType)` with upsert, OR application-level dedup with rationale
|
||||
- [ ] `anomalyType` documented as extensible or closed enum with initial values
|
||||
- [ ] Relationship to session end noted (auto-resolve on session close?)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W21
|
||||
- docs/architecture/storage/coordination.md:29-39
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
41
tasks/architecture/storage/fix-adr-terminology.md
Normal file
41
tasks/architecture/storage/fix-adr-terminology.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: fix-adr-terminology
|
||||
name: Fix ADR Terminology Inconsistencies
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W14: Three terminology inconsistencies across ADRs and docs:
|
||||
- ADR-009 says "organization_members (membership with **roles**)" — contradicts ADR-012's rename to `membershipLevel`
|
||||
- ADR-012:55 uses `accounts.role: "service"` in rationale, despite mandating the rename to `accessLevel`
|
||||
- `agent-roles.md` also uses `accounts.role: "service"`
|
||||
|
||||
Update ADR-009 to say "membership with levels." Update ADR-012:55 and agent-roles.md to use `accounts.accessLevel: "service"`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] ADR-009 updated: "membership with levels" instead of "membership with roles"
|
||||
- [ ] ADR-012:55 updated: `accounts.accessLevel: "service"` instead of `accounts.role: "service"`
|
||||
- [ ] `agent-roles.md` updated: `accounts.accessLevel: "service"` instead of `accounts.role: "service"`
|
||||
- [ ] No remaining references to the old terminology (`accounts.role`, `membershipLevel` as "roles")
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W14
|
||||
- docs/decisions/ADR-009:13
|
||||
- docs/decisions/ADR-012:55
|
||||
- docs/architecture/agent-roles.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
37
tasks/architecture/storage/fix-parts-timestamps-spec.md
Normal file
37
tasks/architecture/storage/fix-parts-timestamps-spec.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: fix-parts-timestamps-spec
|
||||
name: Fix Parts Table Timestamp NOT NULL and onUpdate Spec
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W22: The `parts` table defines its own `id`, `metadata`, `createdAt`, `updatedAt` instead of using `commonCols`, but the spec only says "defaults to `now()`" without specifying NOT NULL or `$onUpdate`. If the Drizzle implementation omits `$onUpdate`, parts rows never have `updatedAt` updated on modification. If timestamps are not NOT NULL, they can become NULL.
|
||||
|
||||
The `parts` table spec must explicitly state that `createdAt` and `updatedAt` are NOT NULL and that `updatedAt` includes `$onUpdate(() => new Date())`. Either replicate these details from `commonCols` with an explicit override note for `id`, or reference `commonCols` with the `id` exception documented.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `sessions.md` parts table: `createdAt` and `updatedAt` specified as `NOT NULL`
|
||||
- [ ] `updatedAt` specified with `$onUpdate(() => new Date())`
|
||||
- [ ] Note added explaining why `parts` uses custom columns instead of `commonCols` (sortable ID vs UUIDv4)
|
||||
- [ ] `id` column exception documented (sortable ID, not UUIDv4 from commonCols)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W22
|
||||
- docs/architecture/storage/sessions.md:99-107
|
||||
- docs/architecture/storage/README.md:69-82
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
35
tasks/architecture/storage/fix-path-index-locale.md
Normal file
35
tasks/architecture/storage/fix-path-index-locale.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
id: fix-path-index-locale
|
||||
name: Fix Path Index for LIKE Pattern Matching
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W17: `WHERE path LIKE 'implementation/%'` can use a B-tree index only with the `C` locale or `text_pattern_ops`. With the default locale, LIKE pattern matching may not use the index, causing sequential scans on the tasks table.
|
||||
|
||||
Specify that the `path` index should use `text_pattern_ops`: `CREATE INDEX idx_tasks_path ON tasks (path text_pattern_ops)`, or document the locale dependency.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `tasks.md` path index uses `text_pattern_ops` in the spec
|
||||
- [ ] `table-reference.md` indexes section updated for `idx_tasks_path` with `text_pattern_ops`
|
||||
- [ ] Note added about locale dependency (default locale vs C locale)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W17
|
||||
- docs/architecture/storage/tasks.md:83, :101
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
38
tasks/architecture/storage/improve-mappings-spec.md
Normal file
38
tasks/architecture/storage/improve-mappings-spec.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: improve-mappings-spec
|
||||
name: Improve Mappings Spec — Project Scoping and Status Lifecycle
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: trivial
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Two improvements to the `mappings` table specification in `coordination.md`:
|
||||
|
||||
1. **S13**: Finding all active mappings for a project's tasks requires a JOIN through `sessions.projectId` or `tasks.projectId`. Either add a denormalized `projectId` column for direct project-scoped queries, or document that the JOIN pattern is acceptable and intentional.
|
||||
|
||||
2. **S14**: Unlike `tasks.status` which has an explicit lifecycle diagram, `mappings.status` transitions are unspecified. Add a lifecycle diagram or state machine showing valid transitions (e.g., `active → completed | failed | aborted`).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Either `projectId` column added to mappings table spec, or JOIN pattern documented as intentional
|
||||
- [ ] `mappings.status` lifecycle diagram added to `coordination.md` with valid transitions
|
||||
- [ ] `table-reference.md` updated if schema changes
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S13
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S14
|
||||
- docs/architecture/storage/coordination.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
32
tasks/architecture/storage/index-strategy.md
Normal file
32
tasks/architecture/storage/index-strategy.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
id: index-strategy
|
||||
name: Index Strategy
|
||||
status: completed
|
||||
depends_on: [add-missing-indexes-identity, add-missing-indexes-sessions, add-missing-indexes-observability, add-partial-indexes, fix-path-index-locale]
|
||||
scope: moderate
|
||||
risk: low
|
||||
impact: component
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
All index-related tasks. This group ensures complete index coverage across the storage layer — covering missing indexes for identity, sessions, and observability tables, partial indexes for common query patterns, and fixing the path index locale issue. Index decisions are low-risk but high-impact for query performance.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] All dependent tasks are completed
|
||||
- [ ] Cross-references between completed tasks are consistent
|
||||
- [ ] No remaining contradictions in the covered domain
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
id: rename-task-dependencies-column
|
||||
name: Rename taskId to dependentTaskId in task_dependencies
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: single
|
||||
risk: trivial
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
**S16**: The column name `taskId` in `task_dependencies` is generic and could be confused as "this task" rather than "the dependent task." The paired column is `dependsOnTaskId`. Renaming `taskId` to `dependentTaskId` makes the FK direction unmistakable — `dependentTaskId` depends on `dependsOnTaskId`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `tasks.md` uses `dependentTaskId` instead of `taskId` in the `task_dependencies` table spec
|
||||
- [ ] `table-reference.md` updated with the renamed column
|
||||
- [ ] All cross-references updated (FK targets, cascade entries)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S16
|
||||
- docs/architecture/storage/tasks.md
|
||||
- docs/architecture/storage/table-reference.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: resolve-audit-logs-ownerid-cascade
|
||||
name: Resolve audit_logs.ownerId NOT NULL + SET NULL contradiction
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: critical
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
`audit_logs.ownerId` is declared `text NOT NULL` in `identity.md:112` but has `onDelete: SET NULL` in `table-reference.md:71`. PostgreSQL will reject the DELETE because it cannot nullify a NOT NULL column.
|
||||
|
||||
## Decision (D1)
|
||||
|
||||
**Change cascade to RESTRICT**. Audit trails should prevent account deletion — this follows the "audit/traceability data: RESTRICT" cascade pattern. Accounts with audit entries cannot be hard-deleted. Account deactivation (via the new `status` column, D5) is the path for handling departed users.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `audit_logs.ownerId` cascade is changed from `SET NULL` to `RESTRICT` in `table-reference.md:71`
|
||||
- [ ] `identity.md` documents that `ownerId` is NOT NULL with RESTRICT cascade, explaining that accounts with audit entries cannot be deleted
|
||||
- [ ] The rationale is documented: "audit trails must preserve accountability; RESTRICT prevents data integrity violations and ensures audit completeness"
|
||||
- [ ] Note about operational implication: "accounts with audit entries are deactivated (status: suspended/deactivated) rather than deleted"
|
||||
- [ ] Cross-reference to D5 (account deactivation via status column)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C01
|
||||
- docs/decisions/storage-spec-phase1-resolutions.md#D1
|
||||
- docs/architecture/storage/identity.md:112
|
||||
- docs/architecture/storage/table-reference.md:71
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
36
tasks/architecture/storage/resolve-key-version-redundancy.md
Normal file
36
tasks/architecture/storage/resolve-key-version-redundancy.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
id: resolve-key-version-redundancy
|
||||
name: Resolve client_secrets KeyVersion Redundancy
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W07: `client_secrets` has both a standalone `keyVersion` column (integer NOT NULL DEFAULT 1) AND `keyVersion` embedded in the `value` JSONB (`EncryptedData.keyVersion`). These can diverge with no documented invariant.
|
||||
|
||||
Either remove the standalone column (read from `value.keyVersion`), or document that the standalone column is authoritative and must be kept in sync with the JSONB value.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Decision documented: remove standalone column OR document authoritative source
|
||||
- [ ] If keeping standalone: add invariant that standalone column and `value.keyVersion` are always in sync
|
||||
- [ ] `services.md` updated to reflect the decision
|
||||
- [ ] `table-reference.md` updated if column is removed
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W07
|
||||
- docs/architecture/storage/services.md:71, :82-86
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: resolve-message-id-adr-contradiction
|
||||
name: Resolve ADR-003 vs sessions.md on message IDs
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: high
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Three-way inconsistency on message IDs:
|
||||
1. **ADR-003** states: "Parts and messages tables use sortable timestamp-based IDs instead of commonCols.id"
|
||||
2. **sessions.md** defines `messages` using `commonCols` (UUIDv4)
|
||||
3. **table-reference.md** only mentions `parts` for sortable IDs
|
||||
|
||||
The composite index `idx_messages_session_id_created_at_id` on `(session_id, created_at, id)` relies on `created_at` for ordering, making UUIDv4 sortable IDs unnecessary — but this contradicts ADR-003's stated rationale.
|
||||
|
||||
## Decision (D2)
|
||||
|
||||
**Option B chosen**: Amend ADR-003 to state that only `parts` uses sortable IDs. `messages` keeps UUIDv4 and relies on the composite index for ordering.
|
||||
|
||||
**Rationale**:
|
||||
- Composite index provides efficient ordering without requiring sortable IDs on messages
|
||||
- Simpler opencode conversation import — opencode uses UUIDv4 message IDs natively
|
||||
- Parts benefit more from sortable IDs (they're frequently paginated/ordered within a message)
|
||||
- The `created_at` column in the composite index provides the ordering property that sortable IDs would give
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] ADR-003 is amended: scope sortable IDs to `parts` only, explain that `messages` uses composite index for ordering
|
||||
- [ ] The `messages` table column spec in sessions.md keeps `commonCols` (UUIDv4)
|
||||
- [ ] table-reference.md note on sortable IDs is updated: only `parts` uses sortable IDs, `messages` uses composite index
|
||||
- [ ] The ordering rationale is explicitly stated in ADR-003 amendment
|
||||
- [ ] All three sources are consistent after changes
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C02
|
||||
- docs/decisions/ADR-003
|
||||
- docs/decisions/storage-spec-phase1-resolutions.md#D2
|
||||
- docs/architecture/storage/sessions.md:42-46
|
||||
- docs/architecture/storage/table-reference.md:48
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
43
tasks/architecture/storage/resolve-org-dual-ownership.md
Normal file
43
tasks/architecture/storage/resolve-org-dual-ownership.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
id: resolve-org-dual-ownership
|
||||
name: Resolve dual ownership model for organizations
|
||||
status: completed
|
||||
depends_on: [resolve-sessions-accountid-cascade]
|
||||
scope: narrow
|
||||
risk: high
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Two competing ownership concepts with no documented relationship:
|
||||
1. `organizations.ownerId` — a single FK to one account (`identity.md:44`)
|
||||
2. `organization_members.membershipLevel: "owner"` — can exist on multiple rows (`identity.md:58`)
|
||||
|
||||
Can `ownerId` point to an account with `membershipLevel: "member"` (not "owner")? Can an org have zero members with `membershipLevel: "owner"` but a non-null `ownerId`? An implementer cannot determine which field is authoritative for ownership queries.
|
||||
|
||||
The resolution of `sessions.accountId` cascade (C01) affects this because if account deletion is enabled (nullable FKs), the org ownership transfer workflow depends on knowing which field is authoritative.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `identity.md` documents the invariant relating `organizations.ownerId` and `organization_members.membershipLevel`
|
||||
- [ ] One of the following invariants is chosen and documented:
|
||||
- "`ownerId` is always a member with `membershipLevel: 'owner'` (enforced by app logic). If all owner-level members are removed, `ownerId` must be transferred first."
|
||||
- "`ownerId` is the creator; `membershipLevel: 'owner'` is a separate authorization concept."
|
||||
- [ ] The account deletion workflow interacts correctly with the chosen invariant (e.g., RESTRICT on ownerId means account deletion blocked if account owns an org)
|
||||
- [ ] `table-reference.md` cascade rationale for `organizations.ownerId → accounts.id` (RESTRICT) is updated with the invariant reference
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C13
|
||||
- docs/architecture/storage/identity.md:44
|
||||
- docs/architecture/storage/identity.md:58
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: resolve-sessions-accountid-cascade
|
||||
name: Resolve sessions.accountId NOT NULL + SET NULL contradiction
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: critical
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
`sessions.accountId` is declared `text NOT NULL` in `sessions.md:17` but has `onDelete: SET NULL` in `table-reference.md:80`. PostgreSQL will reject the DELETE because it cannot nullify a NOT NULL column. This is a foundational schema decision — the resolution determines whether sessions survive account deletion and affects downstream cascade work.
|
||||
|
||||
## Decision (D1)
|
||||
|
||||
**Make the column nullable** with `SET NULL` cascade. Orphaned sessions (account deleted) are still valuable data for audit and debugging. This follows the "live session data: nullable FK + SET NULL" cascade pattern.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `sessions.accountId` is declared `text` (nullable) in `sessions.md` with an explicit note explaining why
|
||||
- [ ] `table-reference.md` cascade entry for `sessions.accountId → accounts.id` uses `SET NULL` and the column is documented as nullable
|
||||
- [ ] No other doc references `sessions.accountId` as `NOT NULL` without acknowledging the nullable change
|
||||
- [ ] The doc note explains the semantic: "orphaned sessions preserve conversation history for audit and debugging"
|
||||
- [ ] Any downstream tasks that depend on this decision (e.g., resolve-org-dual-ownership) can proceed
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C01
|
||||
- docs/decisions/storage-spec-phase1-resolutions.md#D1
|
||||
- docs/architecture/storage/sessions.md:17
|
||||
- docs/architecture/storage/table-reference.md:80
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
id: resolve-sessions-rolename-validation
|
||||
name: Resolve sessions.roleName validation strategy (FK or intentional omission)
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
`sessions.roleName` is bare `text` with no FK to `roles.name` and no documented reason why. Open questions:
|
||||
- Is this intentional to support file-based roles in Phase 1 (before DB sync)?
|
||||
- What happens if the role name has a typo?
|
||||
- What about sessions referencing a role that was deleted?
|
||||
|
||||
Choose one:
|
||||
- **(A)** Add FK → `roles.name` with `onDelete: SET NULL` — role deletions detach sessions
|
||||
- **(B)** Document why the FK is intentionally omitted: "Role definitions may come from `.opencode/agents/*.md` files before DB sync; application-level validation checks against known role names at session creation time."
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Decision is documented with rationale in `sessions.md` and `roles.md`
|
||||
- [ ] If option A: `sessions.roleName` FK cascade is added to `table-reference.md`; sessions.md column spec updated; `roles.md` notes that role.name deletions SET NULL on dependent sessions
|
||||
- [ ] If option B: `sessions.md` documents the intentional omission and the application-level validation strategy; `roles.md` notes that `name` is used as a reference target by sessions
|
||||
- [ ] The Phase 1→Phase 2 migration path for role validation is noted (file-based → DB FK)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C05
|
||||
- docs/architecture/storage/sessions.md:26
|
||||
- docs/architecture/storage/table-reference.md:100-101
|
||||
- docs/architecture/storage/roles.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
69
tasks/architecture/storage/review-cascade-data-integrity.md
Normal file
69
tasks/architecture/storage/review-cascade-data-integrity.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: review-cascade-data-integrity
|
||||
name: Review Cascade Decisions and Data Integrity Specs
|
||||
status: completed
|
||||
depends_on: [cascade-decisions, data-integrity-specs, resolve-sessions-accountid-cascade, resolve-audit-logs-ownerid-cascade, add-missing-cascade-entries, define-sync-field-split, enforce-parts-session-id-invariant, specify-task-body-append-concurrency, resolve-org-dual-ownership, resolve-message-id-adr-contradiction]
|
||||
scope: moderate
|
||||
risk: critical
|
||||
impact: project
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Independent review of the cascade decisions chain and data integrity specifications — the highest-risk path in the architecture spec. This review covers:
|
||||
|
||||
1. **Cascade consistency**: The D1 cascade policy defaults (RESTRICT for audit, SET NULL for live data, CASCADE for ephemeral config) must be consistently applied across every FK relationship in `table-reference.md`. This is the foundation the entire storage layer sits on.
|
||||
|
||||
2. **NOT NULL / SET NULL contradictions**: The original C01 contradiction (`sessions.accountId NOT NULL + SET NULL`, `audit_logs.ownerId NOT NULL + SET NULL`) was resolved, but the resolution must be verified in all referencing docs.
|
||||
|
||||
3. **Field authority splits**: The authored-vs-runtime field split in `tasks.md` must cover all columns, and the sync upsert must explicitly exclude runtime-managed fields.
|
||||
|
||||
4. **Denormalization invariants**: `parts.sessionId` immutability and `organizations.ownerId ⊆ membershipLevel='owner'` must be documented and their enforceability verified.
|
||||
|
||||
5. **Operations/registrations split**: The two-table design must be consistent across `spokes.md`, `call-graph.md`, `coordination.md`, and `table-reference.md`.
|
||||
|
||||
All 10 dependent tasks are completed but none have Summary sections filled. This review must verify that the spec documents actually reflect the decisions, not just that tasks were marked complete.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Every FK in `table-reference.md` has an explicit `onDelete` action with a rationale — no gaps
|
||||
- [ ] No remaining `NOT NULL + SET NULL` contradictions exist in any doc
|
||||
- [ ] `sessions.accountId` is declared `text` (nullable) in `sessions.md` with the orphan-preservation rationale
|
||||
- [ ] `audit_logs.ownerId` cascade is RESTRICT in `table-reference.md` with the audit-integrity rationale
|
||||
- [ ] The D1 cascade policy table in `storage-spec-phase1-resolutions.md` is consistent with `table-reference.md` entries
|
||||
- [ ] `tasks.md` Field Authority Split lists ALL columns as either authored or runtime-managed with no gaps
|
||||
- [ ] `parts.sessionId` immutability is documented with application-level enforcement note in `sessions.md`
|
||||
- [ ] `organizations.ownerId` invariant (always references a member with `membershipLevel: 'owner'`) is documented in `identity.md`
|
||||
- [ ] `hub.task.addNote` specifies DB-level concatenation (`COALESCE(body, '') || $note`) in `tasks.md`
|
||||
- [ ] ADR-003 correctly scopes sortable IDs to `parts` only (not `messages`)
|
||||
- [ ] `operations` / `operation_registrations` table schemas in `spokes.md` match `table-reference.md` entries
|
||||
- [ ] Spoke disconnect lifecycle (deactivate registrations, keep definitions) is documented in `spokes.md`
|
||||
|
||||
## References
|
||||
|
||||
- docs/decisions/storage-spec-phase1-resolutions.md (D1 cascade policy)
|
||||
- docs/architecture/storage/table-reference.md (cascade reference table)
|
||||
- docs/architecture/storage/identity.md (accounts, orgs, audit_logs)
|
||||
- docs/architecture/storage/sessions.md (sessions, parts, messages)
|
||||
- docs/architecture/storage/tasks.md (field authority split, addNote concurrency)
|
||||
- docs/architecture/storage/spokes.md (operations/registrations split)
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md (original review that spawned these tasks)
|
||||
|
||||
## Notes
|
||||
|
||||
This is the most critical review in the architecture. The cascade decisions chain was the highest-risk path identified by `taskgraph risk-path`. Getting these wrong means either data loss in production or account deletion failures that cascade unexpectedly.
|
||||
|
||||
## Summary
|
||||
|
||||
Review completed 2026-04-23. 12 acceptance criteria checked: 9 PASS, 3 FAIL (2 critical, 1 warning-level).
|
||||
|
||||
**Critical issues found and fixed:**
|
||||
1. `detections.resolvedBy → accounts.id (SET NULL)` FK was documented in coordination.md but missing from the canonical cascade table in table-reference.md → **Fixed**: added to cascade table.
|
||||
2. `tasks.projectId` was missing from the Field Authority Split in tasks.md → **Fixed**: added to authored fields with note about project context.
|
||||
|
||||
**Warnings noted:**
|
||||
- Audit FKs use SET NULL for nullable columns vs D1 RESTRICT default → **Fixed**: updated D1 rationale to clarify "RESTRICT on NOT NULL FKs; SET NULL on nullable FKs"
|
||||
- No remaining NOT NULL + SET NULL contradictions
|
||||
|
||||
All fixes committed in same change set as this review.
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
id: review-operations-schema-stabilization
|
||||
name: Review Operations Schema Split and Storage Spec Stabilization Gate
|
||||
status: completed
|
||||
depends_on: [split-operations-into-definitions-and-registrations, spec-adr-consistency, security-specs, cascade-decisions, data-integrity-specs]
|
||||
scope: broad
|
||||
risk: high
|
||||
impact: project
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Final review of the storage spec stabilization gate — verifying that the spec is truly implementable and that cross-references are consistent. This covers:
|
||||
|
||||
### 1. Operations Schema (Definitions + Registrations Split)
|
||||
|
||||
The D3 decision split `operation_specs` into two tables. This is the only broad-scope, high-risk task in the set. It changes the fundamental data model for how the hub routes calls. The review must verify consistency across ALL docs that reference operations:
|
||||
|
||||
- `spokes.md` — table schemas for `operations` and `operation_registrations`
|
||||
- `call-graph.md` — references to operation routing
|
||||
- `coordination.md` — references to spoke operations
|
||||
- `table-reference.md` — FK entries, indexes, status enums
|
||||
- ADR-006 — must reflect the definitions/registrations split
|
||||
|
||||
### 2. Storage Spec Stabilization Gate
|
||||
|
||||
The `storage-spec-stabilization` task is THE gate. Its acceptance criteria are:
|
||||
1. All dependent tasks are completed ✓
|
||||
2. Cross-references between completed tasks are consistent (needs verification)
|
||||
3. No remaining contradictions in the covered domain (needs verification)
|
||||
|
||||
All 26 dependent tasks are marked completed but none have Summary sections filled. The review must verify that the spec docs are in a state where implementation can begin without ambiguity.
|
||||
|
||||
### 3. Doc Status
|
||||
|
||||
All architecture docs still have `status: draft`. If the spec is truly stabilized, these should be promoted. The review should confirm readiness for `status: stable`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `operations` table schema in `spokes.md` is complete and consistent with `table-reference.md`
|
||||
- [ ] `operation_registrations` table schema is complete with polymorphic FK documented
|
||||
- [ ] ADR-006 reflects the definitions/registrations split (or is superseded)
|
||||
- [ ] All `operation_specs` references across docs are updated to `operations`/`operation_registrations`
|
||||
- [ ] Call routing flow (definition lookup → active registrations → dispatch) is documented
|
||||
- [ ] Spoke disconnect lifecycle is unambiguous in `spokes.md`: deactivate registrations, keep definitions
|
||||
- [ ] Cross-references between all per-domain docs and `table-reference.md` are consistent
|
||||
- [ ] No remaining contradictions in cascade behavior, status enums, or FK relationships
|
||||
- [ ] `spokes.md`, `call-graph.md`, `coordination.md` consistently use the new naming
|
||||
- [ ] A recommendation on doc status promotion (`draft` → `stable`) is provided
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/storage/spokes.md
|
||||
- docs/architecture/storage/call-graph.md
|
||||
- docs/architecture/storage/coordination.md
|
||||
- docs/architecture/storage/table-reference.md
|
||||
- docs/decisions/ADR-006-operation-specs-as-capabilities.md
|
||||
- docs/decisions/storage-spec-phase1-resolutions.md (D3)
|
||||
- All per-domain architecture docs in docs/architecture/storage/
|
||||
|
||||
## Notes
|
||||
|
||||
This is the stabilization gate review. If this passes, storage implementation can begin. If it finds issues, they must be resolved before implementation starts — fixing spec contradictions during implementation is orders of magnitude more expensive than fixing them now.
|
||||
|
||||
## Summary
|
||||
|
||||
Review completed 2026-04-23. 10 acceptance criteria checked: 7 PASS, 3 FAIL (2 critical, 1 requiring judgment).
|
||||
|
||||
**Critical issues found and fixed:**
|
||||
1. `operations.accessLevel` in D3 vs `operations.accessControl` in spokes.md — inconsistent naming with different semantics → **Fixed**: updated D3 to use `accessControl` (matching spokes.md and operations.md).
|
||||
2. ADR-002 still referenced `operation_specs.inputSchema/outputSchema` → **Fixed**: updated to `operations.inputSchema/outputSchema`.
|
||||
3. `call_graph_nodes.operationId` had no FK declaration and was ambiguous (FK vs free-text identifier) → **Fixed**: made it a nullable FK to `operations.id` with SET NULL cascade, added to cascade table, clarified in call-graph.md.
|
||||
|
||||
**Warnings noted (deferred to implementation phase):**
|
||||
- `spoke-runner.md` uses `runnerId` instead of `spokeId` (acknowledged in README, terminology cleanup deferred)
|
||||
- No end-to-end call routing flow documented (multi-provider dispatch unspecified — design gap for implementation)
|
||||
- D3 column list doesn't fully match spokes.md (D3 is the decision, spokes.md is the spec — spokes.md is authoritative)
|
||||
|
||||
**Gate recommendation**: After fixes, storage spec stabilization should pass. 2 hours of additional doc work on call routing flow is recommended before hub.call implementation.
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
id: review-security-encryption-config
|
||||
name: Review Security Specs, Key Rotation, and Config Integration
|
||||
status: completed
|
||||
depends_on: [security-specs, specify-key-rotation-protocol, specify-client-config-validation, config-hub-startup-system]
|
||||
scope: moderate
|
||||
risk: critical
|
||||
impact: project
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Independent review of security-critical specifications and their consistency with the new config system. This review covers two interrelated areas:
|
||||
|
||||
### 1. Key Rotation and Encryption Protocol
|
||||
|
||||
The key rotation protocol in `services.md` specifies multi-key format, crash-safe re-encryption transactions, and background sweep. The other session updated `services.md` to reference the two-layer key model from `hub-config.md` instead of `HUB_ENCRYPTION_KEY` env vars. This review must verify:
|
||||
|
||||
- `services.md` now consistently references the config-driven data encryption keys (not env vars)
|
||||
- The key rotation protocol in `services.md` is compatible with the `EncryptionKeyRing` concept in `hub-config.md`
|
||||
- No stray env var references (`HUB_ENCRYPTION_KEY`, `DATABASE_URL`, `REDIS_URL`) remain in any storage doc
|
||||
- The `_encrypted` wrapper pattern in `hub-config.md` for postgres/redis credentials is consistent with how `client_secrets` are stored at rest
|
||||
|
||||
### 2. Config System Consistency with Storage
|
||||
|
||||
The config system is now specified in `docs/architecture/hub-config.md` and `docs/architecture/hub-startup.md`. ADR-008 was revised. This review must verify cross-document consistency:
|
||||
|
||||
- `storage/README.md` no longer references `Deno.env.get()` for DB credentials
|
||||
- `storage/services.md` references the two-layer key model correctly
|
||||
- `infrastructure.md` Docker deployment uses config file + secret, not `-e` flag env vars
|
||||
- The startup sequence in `hub-startup.md` initializes the encryption key ring (Step 7) before any subsystem that needs it
|
||||
- `identity.md` (api_keys, keypal) and `services.md` (client_secrets) are compatible with the config-driven startup
|
||||
|
||||
Both `hub-config.md` and `hub-startup.md` are draft-stage — this review should assess whether they're consistent enough with the storage docs to proceed with implementation.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Key rotation protocol in `services.md` is complete and references the config-driven key model (not env vars)
|
||||
- [ ] No env var references for sensitive values remain in any storage doc (`HUB_ENCRYPTION_KEY`, `DATABASE_URL`, `REDIS_URL`, etc.)
|
||||
- [ ] The two-layer key model (master key → config decryption → data encryption keys) is consistently described across `hub-config.md`, `services.md`, and `ADR-008`
|
||||
- [ ] `storage/README.md` uses config-driven connection patterns (no `Deno.env.get()` for DB credentials)
|
||||
- [ ] `infrastructure.md` Docker deployment uses Docker secret + config file mount (not `-e` env vars)
|
||||
- [ ] `hub-startup.md` Step 7 (encryption key ring) happens before subsystem initialization that needs keys
|
||||
- [ ] Client config validation timing is documented: validated on write (TypeBox schema), startup advisory validation on read
|
||||
- [ ] SHA-256 tradeoff for API keys is documented with the high-entropy machine-generated rationale
|
||||
- [ ] Payload redaction strategy for call graph nodes is documented
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/hub-config.md (config system, two-layer keys, _encrypted wrapper)
|
||||
- docs/architecture/hub-startup.md (startup sequence, encryption key ring init)
|
||||
- docs/architecture/storage/services.md (key rotation protocol, client_secrets, clients config)
|
||||
- docs/architecture/storage/identity.md (api_keys, audit_logs)
|
||||
- docs/architecture/storage/README.md (config references)
|
||||
- docs/architecture/infrastructure.md (Docker deployment)
|
||||
- docs/decisions/ADR-008-secrets-encrypted-at-rest-with-key-versioning.md (revised)
|
||||
- packages/core/config/types.ts (spoke-only config, to be extended)
|
||||
- packages/core/utils/crypto.ts (encrypt/decrypt/generateEncryptionKey)
|
||||
|
||||
## Notes
|
||||
|
||||
The config system blocker (`config-hub-startup-system`) is now completed — it produced `hub-config.md` and `hub-startup.md`. This review checks whether the storage docs are fully consistent with those new specs and whether any env var references leaked through.
|
||||
|
||||
## Summary
|
||||
|
||||
Review completed 2026-04-23. 9 acceptance criteria checked: 7 PASS, 2 FAIL (1 critical, 1 related to same issue).
|
||||
|
||||
**Critical issue found and fixed:**
|
||||
1. `Deno.env.get("DATABASE_URL")` in storage/README.md contradicts the "no env vars for secrets" rule → **Fixed**: replaced with `ALKHUB_DRIZZLE_KIT_URL` (non-sensitive CLI convenience var) and a dev-only placeholder URL with `changeme` credentials instead of real ones.
|
||||
|
||||
**Warnings fixed:**
|
||||
- `PAYLOAD_TRUNCATION_THRESHOLD` env var in call-graph.md → **Fixed**: changed to `HubConfig.callGraph.payloadTruncationThreshold`
|
||||
- Hardcoded `user:pass` in drizzle.config.ts snippet → **Fixed**: replaced with `alkhub:changeme` placeholder
|
||||
- `storage-spec-phase1-resolutions.md` historical `HUB_ENCRYPTION_KEY` reference → **Fixed**: added "(Superseded — see ADR-008 revised and hub-config.md)"
|
||||
|
||||
Two-layer key model is consistent across hub-config.md, services.md, and ADR-008. Key rotation protocol is crash-safe. Startup sequence correctly orders encryption key ring before subsystem initialization.
|
||||
32
tasks/architecture/storage/security-specs.md
Normal file
32
tasks/architecture/storage/security-specs.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
id: security-specs
|
||||
name: Security Specs
|
||||
status: completed
|
||||
depends_on: [specify-key-rotation-protocol, specify-client-config-validation, document-payload-redaction, specify-payload-truncation, document-sha256-tradeoff]
|
||||
scope: moderate
|
||||
risk: high
|
||||
impact: phase
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
All security-related spec fixes. This group addresses security-critical specification gaps: key rotation protocol, client config validation rules, payload redaction and truncation policies, and the sha256 indexing tradeoff. These must be specified before implementation to avoid shipping insecure defaults.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] All dependent tasks are completed
|
||||
- [ ] Cross-references between completed tasks are consistent
|
||||
- [ ] No remaining contradictions in the covered domain
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
32
tasks/architecture/storage/spec-adr-consistency.md
Normal file
32
tasks/architecture/storage/spec-adr-consistency.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
id: spec-adr-consistency
|
||||
name: Spec/ADR Consistency
|
||||
status: completed
|
||||
depends_on: [resolve-message-id-adr-contradiction, fix-adr-terminology, clean-resolved-open-questions, resolve-sessions-rolename-validation, define-session-version-semantics]
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: phase
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
All spec/ADR consistency fixes. This group eliminates contradictions between ADRs, table specs, and other architecture documents. Inconsistent terminology and conflicting claims cause implementors to make wrong assumptions, so all docs must agree before they are treated as authoritative.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] All dependent tasks are completed
|
||||
- [ ] Cross-references between completed tasks are consistent
|
||||
- [ ] No remaining contradictions in the covered domain
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: specify-client-config-validation
|
||||
name: Specify client config validation timing and schema evolution
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: high
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
"Validated against the TypeBox schema for this type **on write**" in `services.md:19` is ambiguous:
|
||||
1. Who validates? Drizzle insert schema? API handler? DB trigger? Direct SQL bypasses application validation.
|
||||
2. Schema evolution: when code deployment changes a client type's TypeBox schema, existing DB rows may become invalid.
|
||||
3. No re-validation on read is documented.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `services.md` specifies: "validate on write (API handler layer) + warn on read (start-up validation pass with logging, not blocking)"
|
||||
- [ ] Schema evolution contract is documented: new fields MUST be `Type.Optional()`; breaking changes MUST use a new client `type` string (e.g., `llm-provider-v2`)
|
||||
- [ ] Whether a `configSchemaVersion` in `metadata` is needed is decided and documented
|
||||
- [ ] The validation chain is explicit: API handler validates → Drizzle insert → DB stores; direct SQL bypass noted as risk
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C12
|
||||
- docs/architecture/storage/services.md:19
|
||||
- docs/decisions/ADR-007
|
||||
- docs/architecture/storage/README.md Open Question #10
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
40
tasks/architecture/storage/specify-key-rotation-protocol.md
Normal file
40
tasks/architecture/storage/specify-key-rotation-protocol.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
id: specify-key-rotation-protocol
|
||||
name: Specify secret key rotation protocol and multi-key format
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: critical
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Lazy re-encryption in `services.md:94-97` is insufficiently specified for a security-critical operation:
|
||||
1. `HUB_ENCRYPTION_KEY` (singular env var) — how are old and new keys stored simultaneously during rotation?
|
||||
2. If process crashes between decrypt and re-encrypt-update, is the secret left in the old key version?
|
||||
3. What happens if a secret with `keyVersion=1` is accessed after the old key is removed? Permanent data loss.
|
||||
4. No background sweep — old-key-version secrets persist indefinitely. If old key is compromised, those secrets remain vulnerable.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `services.md` specifies multi-key storage format: e.g., `HUB_ENCRYPTION_KEYS=v1:base64key,v2:base64key` or a key file
|
||||
- [ ] The re-encryption transaction is documented: decrypt → encrypt → UPDATE in a single DB transaction, with crash-safety note
|
||||
- [ ] A warning about the vulnerability window is added: old-key secrets not yet re-encrypted are at risk if old key is compromised
|
||||
- [ ] Decision documented: whether a background re-encryption sweep is required or deferred
|
||||
- [ ] Error handling for missing key version is documented: log error, skip re-encryption, alert operator
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C11
|
||||
- docs/architecture/storage/services.md:94-97
|
||||
- docs/decisions/ADR-008
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
37
tasks/architecture/storage/specify-payload-truncation.md
Normal file
37
tasks/architecture/storage/specify-payload-truncation.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: specify-payload-truncation
|
||||
name: Specify Call Graph Payload Truncation Details
|
||||
status: completed
|
||||
depends_on: [document-payload-redaction]
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
W18: The truncation strategy says "truncate payloads larger than 10KB" but doesn't specify: when truncation happens (on write? after call completes?), what `preview` contains (first N bytes? N characters?), whether 10KB is configurable, or how object storage reference URLs are structured.
|
||||
|
||||
Specify: (a) truncation happens on write to DB (in-flight calls have full payloads); (b) `preview` is the first 1024 bytes of the JSON-serialized payload; (c) threshold is configurable per operation type or via hub config; (d) defer object storage details but add a placeholder section.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `call-graph.md` specifies truncation timing: on DB write, not in-flight
|
||||
- [ ] `preview` field defined: first 1024 bytes of JSON-serialized payload
|
||||
- [ ] 10KB threshold documented as configurable (hub config or per-operation-type)
|
||||
- [ ] Placeholder section for object storage reference (deferred)
|
||||
- [ ] Cross-reference to redaction strategy (this task depends on W08 resolution)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#W18
|
||||
- docs/architecture/storage/call-graph.md:30
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: specify-task-body-append-concurrency
|
||||
name: Specify concurrency model for hub.task.addNote
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: single
|
||||
risk: high
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
`hub.task.addNote` appends a timestamped note section to `body`. In a multi-agent system, read-modify-write is a race condition: Agent A reads body, Agent B reads body, both append, Agent A writes, Agent B overwrites A's addition.
|
||||
|
||||
The review recommends specifying: `hub.task.addNote` must use DB-level concatenation (`UPDATE tasks SET body = body || $note WHERE id = $taskId`), not a read-modify-write cycle. Or use optimistic locking with `updatedAt`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `tasks.md` specifies the concurrency model for `hub.task.addNote` explicitly
|
||||
- [ ] The recommended approach is DB-level concatenation: `UPDATE tasks SET body = body || $note WHERE id = $taskId`
|
||||
- [ ] If optimistic locking is offered as an alternative, it is documented with the `updatedAt` check pattern
|
||||
- [ ] The spec notes that the append-only approach (no read-modify-write) eliminates the race condition
|
||||
- [ ] A note on `body` being nullable is addressed: `COALESCE(body, '') || $note`
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#C08
|
||||
- docs/architecture/storage/tasks.md:249-266
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
34
tasks/architecture/storage/specify-task-enum-pgenum.md
Normal file
34
tasks/architecture/storage/specify-task-enum-pgenum.md
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
id: specify-task-enum-pgenum
|
||||
name: Specify Task Enum Values as Drizzle pgEnum
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: single
|
||||
risk: trivial
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
**S15**: The categorical enum values (`scope`, `risk`, `impact`, `level`, `priority`, `status`) are documented as text strings in `tasks.md` but not referenced as Drizzle `pgEnum` types. Specify that these should be `pgEnum` for type safety, with the decomposer template consuming the same definitions. This ensures DB-level constraints match the application-level typing.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `tasks.md` specifies that `scope`, `risk`, `impact`, `level`, `priority`, and `status` should use `pgEnum` in the Drizzle schema
|
||||
- [ ] Enum value lists in `tasks.md` match the `pgEnum` definitions
|
||||
- [ ] Note added that the decomposer template should consume these same enum definitions
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md#S15
|
||||
- docs/architecture/storage/tasks.md
|
||||
- docs/architecture/storage/table-reference.md (enum reference)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
id: split-operations-into-definitions-and-registrations
|
||||
name: Split operation_specs into operations (definitions) + operation_registrations
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: broad
|
||||
risk: high
|
||||
impact: phase
|
||||
level: 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 disconnect** → `operation_registrations.status` set to `inactive`. Definitions survive. Reconnection re-activates or creates new registration.
|
||||
- **On admin spoke-row deletion** → `operation_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 convention** → `operations.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 indexes** → `operations` 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
|
||||
32
tasks/architecture/storage/storage-spec-stabilization.md
Normal file
32
tasks/architecture/storage/storage-spec-stabilization.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
id: storage-spec-stabilization
|
||||
name: Storage Spec Stabilization
|
||||
status: pending
|
||||
depends_on: [cascade-decisions, data-integrity-specs, spec-adr-consistency, index-strategy, security-specs, add-account-deactivation, add-audit-log-context, add-caller-account-id, add-spoke-reconnecting-state, clarify-session-spec-details, define-call-graph-retention, document-accesslevel-authorization, document-system-account-email-convention, document-api-key-expiration-behavior, document-cross-table-status-mapping, document-edge-type-semantics, document-jsonb-column-boundaries, document-mappings-valid-shapes, document-org-ownership-transfer, enhance-detections-table, fix-parts-timestamps-spec, improve-mappings-spec, rename-task-dependencies-column, resolve-key-version-redundancy, specify-task-enum-pgenum, split-operations-into-definitions-and-registrations, config-hub-startup-system, review-cascade-data-integrity, review-security-encryption-config, review-operations-schema-stabilization]
|
||||
scope: broad
|
||||
risk: critical
|
||||
impact: project
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
THE top-level meta-task for stabilizing the storage architecture. This is the gate that must pass before implementation of the storage layer can begin. It depends on all five sub-groups: cascade decisions, data integrity specs, spec/ADR consistency, index strategy, and security specs. When this is complete, the storage spec is implementable without contradictions or critical gaps.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] All dependent tasks are completed
|
||||
- [ ] Cross-references between completed tasks are consistent
|
||||
- [ ] No remaining contradictions in the covered domain
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/storage-architecture-review-2026-04-21.md
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
50
tasks/sync/archive-migration-research.md
Normal file
50
tasks/sync/archive-migration-research.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
id: archive-migration-research
|
||||
name: Archive or Update Migration Research Docs
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: trivial
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
The `docs/research/migration/` directory contains two files describing planned extractions that are now **complete**:
|
||||
|
||||
- `operations.md` — Planned extraction of `packages/core/operations/` and `packages/core/mcp/` into `@alkdev/operations`
|
||||
- `pubsub.md` — Planned extraction of `packages/core/pubsub/` into `@alkdev/pubsub`
|
||||
|
||||
Both contain detailed current-state analysis, migration steps, and open questions that are now resolved. They served their purpose but are now outdated and potentially confusing.
|
||||
|
||||
### Options
|
||||
|
||||
**A. Archive** (recommended): Move both files to `docs/research/migration/completed/` with a status note at the top. This preserves them for historical reference without cluttering the active research directory.
|
||||
|
||||
**B. Update**: Rewrite as "completed migration" docs showing before/after. More work for less value — the review doc already captures the migration impact.
|
||||
|
||||
**C. Delete**: Remove entirely. Loses historical context.
|
||||
|
||||
### Also: Mark resolved findings in consistency review
|
||||
|
||||
`docs/reviews/docs-consistency-review-2026-04-17.md` has several findings that are now resolved by the extractions. Add resolution notes:
|
||||
|
||||
| Finding | Original Issue | New Resolution |
|
||||
|---------|---------------|----------------|
|
||||
| C5 | PendingRequestMap is in core, not hub | Resolved: now in `@alkdev/operations` |
|
||||
| I2 | `env.ts` has PendingRequestMap interface only | Resolved: full implementation in `@alkdev/operations` |
|
||||
| I5 | `OperationContext.pubsub` typed as unknown | Resolved: `pubsub` field removed from context |
|
||||
| I6 | `OperationContext.stream` never populated | Resolved: `stream` field removed from context |
|
||||
| I7 | `@repeaterjs/repeater` version mismatch risk | Resolved: inlined in `@alkdev/pubsub`, no external dep |
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `docs/research/migration/operations.md` is archived or clearly marked as completed
|
||||
- [ ] `docs/research/migration/pubsub.md` is archived or clearly marked as completed
|
||||
- [ ] Consistency review findings C5, I2, I5, I6, I7 are marked as resolved with resolution notes
|
||||
- [ ] No active doc references `docs/research/migration/` as authoritative for current state
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/core-library-extraction-sync-2026-05-18.md (Section 2.13, 2.14)
|
||||
77
tasks/sync/delete-replaced-core-code.md
Normal file
77
tasks/sync/delete-replaced-core-code.md
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
id: delete-replaced-core-code
|
||||
name: Delete Replaced Core Code and Update Package Config
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Remove all code from `packages/core/` that has been replaced by the `@alkdev/operations` and `@alkdev/pubsub` npm packages. This is the first step in the core library extraction sync — clearing out the old code before relocating what remains and updating the package structure.
|
||||
|
||||
### Files to delete
|
||||
|
||||
**PubSub module** (replaced by `@alkdev/pubsub`):
|
||||
- `packages/core/pubsub/create_pubsub.ts`
|
||||
- `packages/core/pubsub/typed_event_target.ts`
|
||||
- `packages/core/pubsub/redis_event_target.ts`
|
||||
- `packages/core/pubsub/operators.ts`
|
||||
- `packages/core/pubsub/mod.ts`
|
||||
|
||||
**Operations module** (replaced by `@alkdev/operations`):
|
||||
- `packages/core/operations/types.ts`
|
||||
- `packages/core/operations/registry.ts`
|
||||
- `packages/core/operations/env.ts`
|
||||
- `packages/core/operations/scanner.ts`
|
||||
- `packages/core/operations/validation.ts`
|
||||
- `packages/core/operations/from_schema.ts`
|
||||
- `packages/core/operations/from_openapi.ts`
|
||||
- `packages/core/operations/mod.ts`
|
||||
|
||||
**MCP module** (replaced by `@alkdev/operations/from-mcp`):
|
||||
- `packages/core/mcp/wrapper.ts`
|
||||
- `packages/core/mcp/loader.ts`
|
||||
- `packages/core/mcp/mod.ts`
|
||||
|
||||
**Tests and fixtures** (for deleted modules):
|
||||
- `packages/core/tests/operations/registry.test.ts`
|
||||
- `packages/core/tests/operations/scanner.test.ts`
|
||||
- `packages/core/tests/pubsub/redis_event_target.test.ts`
|
||||
- `packages/core/tests/mcp/loader.test.ts`
|
||||
- `packages/core/tests/fixtures/registry.ts`
|
||||
- `packages/core/tests/fixtures/operations/demo/greet.ts`
|
||||
- `packages/core/tests/fixtures/operations/other/calculate.ts`
|
||||
- `packages/core/tests/operations/` (directory)
|
||||
- `packages/core/tests/pubsub/` (directory)
|
||||
- `packages/core/tests/mcp/` (directory)
|
||||
- `packages/core/tests/fixtures/operations/` (directory)
|
||||
|
||||
### Config updates
|
||||
|
||||
**`packages/core/deno.json`**:
|
||||
- Remove exports: `"./operations"`, `"./pubsub"`, `"./mcp"`
|
||||
- Remove imports: `"@repeaterjs/repeater"`, `"@modelcontextprotocol/sdk"`, `"ioredis"`, `"@alkdev/typebox/value"`
|
||||
- Keep `"@alkdev/typebox"` (still needed by config types)
|
||||
- Keep `"@std/*"` imports if used by remaining modules
|
||||
- Keep `"@logtape/logtape"` (still needed by logger)
|
||||
|
||||
**`packages/core/mod.ts`**:
|
||||
- Remove re-exports of operations, pubsub, mcp
|
||||
- Keep re-exports of config, logger (and utils/crypto if it stays)
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] All listed files and directories are deleted
|
||||
- [ ] `packages/core/deno.json` has no references to deleted modules
|
||||
- [ ] `packages/core/mod.ts` has no references to deleted modules
|
||||
- [ ] `deno check packages/core/` passes on remaining code
|
||||
- [ ] No orphaned imports in remaining core files (config, logger, utils/crypto)
|
||||
- [ ] Root `deno.json` workspace still includes `packages/core` (package removal is a separate task)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/core-library-extraction-sync-2026-05-18.md (Section 1.1, 1.3)
|
||||
56
tasks/sync/relocate-core-remaining-modules.md
Normal file
56
tasks/sync/relocate-core-remaining-modules.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
id: relocate-core-remaining-modules
|
||||
name: Relocate Config, Logger, and Crypto to Hub
|
||||
status: completed
|
||||
depends_on: [delete-replaced-core-code]
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Move the three remaining modules from `packages/core/` into `packages/hub/`, then remove the `packages/core/` package entirely. These modules are hub-specific concerns:
|
||||
|
||||
- **`config/types.ts`** (169 lines) — TypeBox schemas for HubConfig, SpokeConfig, BaseConfig, PostgresConfig, RedisConfig, etc. Both hub and spoke need config types. For now, these go into hub; if spokes need shared config types later, we can extract them into a thin `@alkdev/config` or inline them in the spoke package.
|
||||
- **`logger/mod.ts`** (27 lines) — Logtape wrapper. Currently a stub (only logs `["logtape", "meta"]` category). Needs proper configuration — that's a hub startup concern.
|
||||
- **`utils/crypto.ts`** (119 lines) — AES-256-GCM encryption/decryption, PBKDF2 key derivation, key generation. Hub-only (encryption key management lives in the hub).
|
||||
|
||||
After relocating, `packages/core/` is empty and should be removed from the workspace.
|
||||
|
||||
### Steps
|
||||
|
||||
1. Create `packages/hub/src/config/` directory and move `config/types.ts` there
|
||||
2. Create `packages/hub/src/logger/` directory and move `logger/mod.ts` there
|
||||
3. Create `packages/hub/src/utils/` directory and move `utils/crypto.ts` there
|
||||
4. Update all imports in moved files (they'll use `@alkdev/typebox` etc. from npm)
|
||||
5. Add `packages/hub/deno.json` with proper dependencies
|
||||
6. Update `packages/hub/mod.ts` to export the new modules
|
||||
7. Remove `packages/core/` entirely
|
||||
8. Remove `"packages/core"` from root `deno.json` workspace array
|
||||
9. Verify `deno check packages/hub/` passes
|
||||
|
||||
### Note on SpokeConfig
|
||||
|
||||
SpokeConfig types are used by spokes, but for now the spoke package can duplicate the small number of fields it needs (SpokeConfig is just hub.url + hub.auth.tokenFile). When we implement the spoke, we'll decide whether to:
|
||||
- A) Duplicate the few spoke-relevant fields in the spoke package
|
||||
- B) Create a thin shared `@alkdev/config` npm package
|
||||
- C) Have the spoke import hub's config types (not ideal, creates coupling)
|
||||
|
||||
This decision can be deferred until spoke implementation begins.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `packages/hub/src/config/types.ts` exists with config TypeBox schemas
|
||||
- [ ] `packages/hub/src/logger/mod.ts` exists with logtape wrapper
|
||||
- [ ] `packages/hub/src/utils/crypto.ts` exists with AES-256-GCM functions
|
||||
- [ ] `packages/hub/deno.json` has correct dependencies and exports
|
||||
- [ ] `packages/core/` directory is deleted
|
||||
- [ ] Root `deno.json` workspace no longer includes `packages/core`
|
||||
- [ ] `deno check packages/hub/` passes
|
||||
- [ ] No references to `@alkhub/core` or `packages/core` remain in the codebase
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/core-library-extraction-sync-2026-05-18.md (Section 1.2, 1.3, 6.1, 6.2)
|
||||
53
tasks/sync/review-sync-completeness.md
Normal file
53
tasks/sync/review-sync-completeness.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: review-sync-completeness
|
||||
name: Review Sync Completeness and Cross-Reference Consistency
|
||||
status: completed
|
||||
depends_on: [delete-replaced-core-code, relocate-core-remaining-modules, update-agents-and-overview, update-packages-and-dependencies, update-call-graph-and-operations-docs, update-pubsub-doc, update-secondary-docs, update-tasks-doc-for-taskgraph, archive-migration-research]
|
||||
scope: broad
|
||||
risk: high
|
||||
impact: phase
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Final review gate after all sync tasks are completed. Verify that the codebase and documentation are consistent with the core library extractions. This is the stabilization gate before resuming feature development.
|
||||
|
||||
### Checks
|
||||
|
||||
1. **No stale references**: Search entire `docs/` directory for:
|
||||
- `packages/core/operations/` → should be `@alkdev/operations`
|
||||
- `packages/core/pubsub/` → should be `@alkdev/pubsub`
|
||||
- `packages/core/mcp/` → should be `@alkdev/operations/from-mcp`
|
||||
- `core/operations/` → should be `@alkdev/operations`
|
||||
- `core/pubsub/` → should be `@alkdev/pubsub`
|
||||
- `PubSubPublishArgsByKey` → should be `PubSubEventMap`
|
||||
- `@repeaterjs/repeater` as a direct dependency → should note it's inlined in `@alkdev/pubsub`
|
||||
- `"Not started"` for WebSocket EventTarget → should note it's implemented
|
||||
- `"Not started"` for Call Protocol → should note it's implemented
|
||||
|
||||
2. **Package structure**: Verify `packages/core/` is deleted, workspace config is correct, hub package has proper deno.json
|
||||
|
||||
3. **Type check**: `deno check` passes across the workspace
|
||||
|
||||
4. **AGENTS.md accuracy**: Read through AGENTS.md and verify every claim is current
|
||||
|
||||
5. **overview.md accuracy**: Read through overview.md and verify every status is current
|
||||
|
||||
6. **Cross-reference integrity**: Every doc that mentions a module has the correct package name
|
||||
|
||||
7. **Breaking changes documented**: Verify that all breaking API changes from the review doc (Section 1.5) are reflected in relevant architecture docs
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] No stale `packages/core/operations/` or `packages/core/pubsub/` references in docs
|
||||
- [ ] No stale `Not started` status for WebSocket EventTarget or Call Protocol
|
||||
- [ ] `packages/core/` is deleted from workspace
|
||||
- [ ] `deno check` passes on workspace
|
||||
- [ ] AGENTS.md is accurate and current
|
||||
- [ ] overview.md status table is accurate
|
||||
- [ ] All breaking API changes (ResponseEnvelope, buildEnv, ScannerFS, etc.) are documented in relevant specs
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/core-library-extraction-sync-2026-05-18.md (full document)
|
||||
62
tasks/sync/update-agents-and-overview.md
Normal file
62
tasks/sync/update-agents-and-overview.md
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
id: update-agents-and-overview
|
||||
name: Update AGENTS.md and overview.md for Extraction
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Update the two highest-visibility project orientation docs to reflect that operations, pubsub, and mcp are now external npm packages, not in-repo code. These are the docs that developers and agents read first.
|
||||
|
||||
### AGENTS.md changes
|
||||
|
||||
**Provenance table** — Replace all "Copied from predecessor project" / "Forked from graphql-yoga" / "Not started" entries:
|
||||
|
||||
| Module | Current Status | Updated Status |
|
||||
|--------|---------------|----------------|
|
||||
| Operations system | "Working, 7 tests passing" | Extracted to `@alkdev/operations` v0.1.0 |
|
||||
| PubSub (createPubSub) | "Working" | Extracted to `@alkdev/pubsub` v0.1.0 |
|
||||
| PubSub (operators) | "Working" | Extracted to `@alkdev/pubsub` v0.1.0 |
|
||||
| TypedEventTarget | "Forked from graphql-yoga" | Extracted to `@alkdev/pubsub` v0.1.0 |
|
||||
| Redis EventTarget | "Working, 5 tests passing" | Extracted to `@alkdev/pubsub` v0.1.0 |
|
||||
| WebSocket EventTarget | "Not started" | Implemented in `@alkdev/pubsub` v0.1.0 |
|
||||
| MCP client | "Working, 1 test passing" | Extracted to `@alkdev/operations/from-mcp` v0.1.0 |
|
||||
| Call protocol | "Not started" | Implemented in `@alkdev/operations` v0.1.0 |
|
||||
| Config types | "Needs hub config" | Relocated to hub package |
|
||||
| Logger | "Needs proper config" | Relocated to hub package |
|
||||
| Storage | "Not started" | Not started (unchanged) |
|
||||
|
||||
**Key Patterns section** — Update to reference `@alkdev/operations` and `@alkdev/pubsub` instead of in-repo code. Add taskgraph pattern. Add ResponseEnvelope.
|
||||
|
||||
**Workspace Structure** — Remove `core/` package. Add external deps section.
|
||||
|
||||
**Reference Dependencies table** — Add `@alkdev/operations`, `@alkdev/pubsub`, `@alkdev/taskgraph`. Remove `graphql-yoga`. Update `graphology` note to transitive dep.
|
||||
|
||||
**Constraints section** — Add constraint: do not duplicate code from `@alkdev/*` packages in-repo.
|
||||
|
||||
### overview.md changes
|
||||
|
||||
**"What Exists" section** — Replace entirely with current state (extracted packages at v0.1.0, remaining stubs still in packages/core/).
|
||||
|
||||
**"What Needs Implementation"** — Remove WebSocket EventTarget, Call protocol (both done). Add taskgraph integration. Keep everything else.
|
||||
|
||||
**Architecture Docs section** — Update descriptions of operations, pubsub, call-graph docs.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] AGENTS.md provenance table reflects extraction to npm packages
|
||||
- [ ] AGENTS.md key patterns reference `@alkdev/operations` and `@alkdev/pubsub`
|
||||
- [ ] AGENTS.md workspace structure removes `core/` from packages list
|
||||
- [ ] AGENTS.md reference dependencies table includes 3 new packages and removes graphql-yoga
|
||||
- [ ] overview.md "What Exists" lists npm package locations
|
||||
- [ ] overview.md "What Needs Implementation" removes completed items
|
||||
- [ ] All internal cross-references between docs are consistent
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/core-library-extraction-sync-2026-05-18.md (Section 2.1, 2.2)
|
||||
65
tasks/sync/update-call-graph-and-operations-docs.md
Normal file
65
tasks/sync/update-call-graph-and-operations-docs.md
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
id: update-call-graph-and-operations-docs
|
||||
name: Update call-graph.md and operations.md for @alkdev/operations API
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
These two architecture docs describe the call protocol and operations system, both of which are now implemented in `@alkdev/operations`. They need significant updates to reflect the actual API surface.
|
||||
|
||||
### call-graph.md changes
|
||||
|
||||
1. **PendingRequestMap section** — Replace the schematic implementation with actual `@alkdev/operations` API:
|
||||
- `new PendingRequestMap({ eventTarget })` constructor with optional EventTarget
|
||||
- `call(operationId, input, { deadline, identity })` returns `Promise<ResponseEnvelope>`
|
||||
- `subscribe(operationId, input, { idleTimeout, identity })` returns `AsyncIterable<ResponseEnvelope>`
|
||||
- `respond(requestId, output)` requires `isResponseEnvelope(output)`
|
||||
- `emitError(requestId, code, message, details?)`, `complete(requestId)`, `abort(requestId)`
|
||||
- Built-in deadline and idle timeout support
|
||||
|
||||
2. **CallHandler section** — Reference `buildCallHandler` from `@alkdev/operations`
|
||||
|
||||
3. **buildEnv section** — Remove `callMap` parameter. New API:
|
||||
- `buildEnv({ registry, context })` — sets `trusted: true` on nested context
|
||||
- No `callMap` parameter — `PendingRequestMap` handles call routing
|
||||
- Returns env functions that return `Promise<ResponseEnvelope>`
|
||||
|
||||
4. **Dependencies section** — Replace direct `graphology` deps. `graphology` is now a transitive dep through `@alkdev/taskgraph`. For call graph storage, the hub can still use `graphology` directly or use `@alkdev/taskgraph`'s `TaskGraph` class.
|
||||
|
||||
5. **CallEventSchema** — Cross-reference that the TypeBox schemas are in `@alkdev/operations` and may differ slightly from what's documented. Add note to verify against package source.
|
||||
|
||||
6. **Transport mapping table** — Update WebSocket row: `@alkdev/pubsub/event-target-websocket-server` for hub, `@alkdev/pubsub/event-target-websocket-client` for spoke
|
||||
|
||||
### operations.md changes
|
||||
|
||||
1. Remove "In-repo location: `packages/core/operations/`" — now `@alkdev/operations` npm package
|
||||
2. Update component descriptions to reference `@alkdev/operations` exports
|
||||
3. Add `ResponseEnvelope` concept (universal result wrapper: `local/http/mcp`)
|
||||
4. Add `CallError` / `InfrastructureErrorCode` concept
|
||||
5. Add `subscribe()` helper for subscription operations
|
||||
6. Add `ScannerFS` interface (Deno/Node agnostic)
|
||||
7. Update Schema Adapters section to use `SchemaAdapter` pattern from `@alkdev/operations/from-typemap`
|
||||
8. Remove "SSE Subscription Handler Fix" from open issues — fixed in `@alkdev/operations/from-openapi`
|
||||
9. Update Call Protocol Integration section to reference `@alkdev/operations` API
|
||||
10. Update `buildEnv` to remove `callMap` parameter
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] call-graph.md PendingRequestMap section shows actual `@alkdev/operations` API
|
||||
- [ ] call-graph.md buildEnv section has no `callMap` parameter
|
||||
- [ ] call-graph.md dependencies reference `@alkdev/taskgraph` transitively
|
||||
- [ ] call-graph.md transport table references `@alkdev/pubsub` event targets
|
||||
- [ ] operations.md references `@alkdev/operations` package throughout
|
||||
- [ ] operations.md open issues no longer include fixed items
|
||||
- [ ] operations.md documents ResponseEnvelope and CallError
|
||||
- [ ] No references to `packages/core/operations/` remain in either doc
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/core-library-extraction-sync-2026-05-18.md (Section 2.4, 2.5)
|
||||
38
tasks/sync/update-packages-and-dependencies.md
Normal file
38
tasks/sync/update-packages-and-dependencies.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: update-packages-and-dependencies
|
||||
name: Update packages.md Dependency Structure
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Update `docs/architecture/packages.md` to reflect the new dependency structure: `@alkhub/core` is removed, hub and spoke import directly from `@alkdev/*` npm packages.
|
||||
|
||||
### Key changes
|
||||
|
||||
1. Remove `@alkhub/core` section entirely
|
||||
2. Add sections for the 3 external `@alkdev/*` packages with their exports
|
||||
3. Update `@alkhub/hub` dependencies: add `@alkdev/operations`, `@alkdev/pubsub`, `@alkdev/taskgraph`; remove `@repeaterjs/repeater` (inlined in pubsub)
|
||||
4. Update `@alkhub/spoke` dependencies: add `@alkdev/operations`, `@alkdev/pubsub`
|
||||
5. Update dependency direction: `spoke → @alkdev/operations, @alkdev/pubsub`, `hub → @alkdev/operations, @alkdev/pubsub, @alkdev/taskgraph`
|
||||
6. Update rules: replace "core is transport-agnostic" with "packages should be transport-agnostic"
|
||||
7. Note that `ioredis` is now an optional peer dep of `@alkdev/pubsub` (only if Redis ET is used), not a direct dep of hub
|
||||
8. Note that `@modelcontextprotocol/sdk` is an optional peer dep of `@alkdev/operations` (only if MCP adapter is used)
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `@alkhub/core` section is removed
|
||||
- [ ] 3 external `@alkdev/*` package sections are documented with exports
|
||||
- [ ] Hub and spoke dependency lists are updated
|
||||
- [ ] Dependency direction diagram is updated
|
||||
- [ ] Rules section reflects new structure (no core package)
|
||||
- [ ] Storage location decision section still applies (unchanged)
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/core-library-extraction-sync-2026-05-18.md (Section 2.3)
|
||||
45
tasks/sync/update-pubsub-doc.md
Normal file
45
tasks/sync/update-pubsub-doc.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
id: update-pubsub-doc
|
||||
name: Update pubsub-redis.md for @alkdev/pubsub
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
`docs/architecture/pubsub-redis.md` describes code that's now in `@alkdev/pubsub`. The doc needs a substantial rewrite to reflect the new package's API surface, including EventEnvelope, WebSocket and Worker event targets, the `prefix`/`close()` Redis API, and the expanded operator set.
|
||||
|
||||
### Key changes
|
||||
|
||||
1. **Source location** — Change from `packages/core/pubsub/` to `@alkdev/pubsub` npm package
|
||||
2. **createPubSub API** — Document `PubSubEventMap` pattern (simple `{ [eventType: string]: payload }`) replacing `PubSubPublishArgsByKey`. `publish(type, id, payload)` always takes 3 explicit args.
|
||||
3. **EventEnvelope** — New concept: `{ type, id, payload }` is the universal cross-process message format. Types starting with `__` are reserved for adapter control messages (used by WS event targets for `__subscribe`/`__unsubscribe`).
|
||||
4. **Redis EventTarget** — Add `prefix` option (e.g., `"alk:events:"`) and `close()` method. Remove the "configure serializer or wrap event target" workaround — `prefix` is now a parameter.
|
||||
5. **WebSocket EventTarget** — No longer "Not started" / "Deferred". Document both:
|
||||
- `@alkdev/pubsub/event-target-websocket-client` — spoke-side adapter
|
||||
- `@alkdev/pubsub/event-target-websocket-server` — hub-side adapter with `addConnection()`/`removeConnection()`, per-connection `SpokeEventTarget`, backpressure handling
|
||||
6. **Worker EventTarget** — New adapter: host (`createWorkerHostEventTarget(worker)`) and thread (`createWorkerThreadEventTarget()`)
|
||||
7. **Operators** — List all 13: `filter`, `map`, `pipe`, `take`, `reduce`, `toArray`, `batch`, `dedupe`, `window`, `flat`, `groupBy`, `chain`, `join`
|
||||
8. **Repeater** — Inlined, no external `@repeaterjs/repeater` dependency
|
||||
9. **Package exports** — Document the 5 subpath exports: `.`, `./event-target-redis`, `./event-target-websocket-client`, `./event-target-websocket-server`, `./event-target-worker`
|
||||
10. **Prior Art** — Update to reflect `@alkdev/pubsub` is a standalone package, not forked graphql-yoga code
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Source location references `@alkdev/pubsub` throughout
|
||||
- [ ] `createPubSub` API docs use `PubSubEventMap` pattern
|
||||
- [ ] EventEnvelope concept is documented
|
||||
- [ ] Redis EventTarget docs include `prefix` and `close()`
|
||||
- [ ] WebSocket client and server event targets are documented (not marked as deferred)
|
||||
- [ ] Worker event target is documented
|
||||
- [ ] All 13 operators are listed
|
||||
- [ ] No references to `packages/core/pubsub/` remain
|
||||
- [ ] Prior Art section reflects standalone package, not in-repo fork
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/core-library-extraction-sync-2026-05-18.md (Section 2.6)
|
||||
64
tasks/sync/update-secondary-docs.md
Normal file
64
tasks/sync/update-secondary-docs.md
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
id: update-secondary-docs
|
||||
name: Update hub-architecture, hub-config, hub-startup, spoke-runner, ADR-013
|
||||
status: completed
|
||||
depends_on: [update-call-graph-and-operations-docs, update-pubsub-doc]
|
||||
scope: moderate
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Update the secondary architecture docs that reference the old `packages/core/` paths and pre-extraction status. These docs don't need rewrites — just path and status reference updates.
|
||||
|
||||
### hub-architecture.md
|
||||
- Component table: Operations row `core/operations/` → `@alkdev/operations`
|
||||
- PubSub row: `core/pubsub/` → `@alkdev/pubsub`
|
||||
- Call protocol row: `core/` → `@alkdev/operations` (see call-graph.md)
|
||||
- WebSocket adapter: "pending" → "available in `@alkdev/pubsub`"
|
||||
|
||||
### hub-config.md
|
||||
- `createRedisEventTarget` example: add `prefix: "alk:events:"` parameter
|
||||
- Update any references to `PendingRequestMap` location
|
||||
|
||||
### hub-startup.md
|
||||
- Note that PendingRequestMap and CallHandler come from `@alkdev/operations`
|
||||
- PubSub setup references `@alkdev/pubsub` with `prefix` option
|
||||
|
||||
### spoke-runner.md
|
||||
- WebSocket EventTarget: `@alkdev/pubsub/event-target-websocket-client`
|
||||
- PendingRequestMap: `@alkdev/operations`
|
||||
- Scanner: `@alkdev/operations` with `ScannerFS` Deno adapter (show example)
|
||||
- SchemaAdapters: `@alkdev/operations/from-typemap`
|
||||
- `FromSchema()` / `FromOpenAPI()`: `@alkdev/operations/from-schema` / `@alkdev/operations/from-openapi`
|
||||
|
||||
### ADR-013 (schema-system-integration)
|
||||
- Update `packages/core/operations/scanner.ts` → `@alkdev/operations/scanner`
|
||||
- Update `packages/core/operations/from_schema.ts` → `@alkdev/operations/from_schema`
|
||||
- Update `packages/core/operations/from_openapi.ts` → `@alkdev/operations/from_openapi`
|
||||
- Update scanner enhancement task to reference `SchemaAdapter` pattern from `@alkdev/operations/from-typemap`
|
||||
|
||||
### mcp-server.md
|
||||
- No significant changes needed (MCP server is hub-level, references to MCP client are now via `@alkdev/operations/from-mcp`)
|
||||
|
||||
### agent-sessions.md
|
||||
- Update `FromOpenAPI` reference to `@alkdev/operations/from-openapi`
|
||||
|
||||
### coordination.md
|
||||
- Update `FromOpenAPI` reference to `@alkdev/operations/from-openapi`
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] hub-architecture.md component table references external packages
|
||||
- [ ] hub-config.md Redis ET example includes prefix
|
||||
- [ ] hub-startup.md references `@alkdev/operations` for call protocol
|
||||
- [ ] spoke-runner.md references `@alkdev/pubsub` and `@alkdev/operations` throughout
|
||||
- [ ] ADR-013 paths updated to `@alkdev/operations/*`
|
||||
- [ ] No references to `packages/core/operations/` or `packages/core/pubsub/` remain in any secondary doc
|
||||
- [ ] No file says "Not started" for WebSocket EventTarget or Call Protocol
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/core-library-extraction-sync-2026-05-18.md (Section 2.8–2.12)
|
||||
39
tasks/sync/update-tasks-doc-for-taskgraph.md
Normal file
39
tasks/sync/update-tasks-doc-for-taskgraph.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: update-tasks-doc-for-taskgraph
|
||||
name: Update storage/tasks.md for @alkdev/taskgraph
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Update the "Graphology Integration" section and sync flow in `docs/architecture/storage/tasks.md` to reference `@alkdev/taskgraph` instead of direct `graphology` usage.
|
||||
|
||||
### Key changes
|
||||
|
||||
1. **Graphology Integration section** — Replace manual `DirectedGraph` construction with `TaskGraph.fromRecords()`:
|
||||
- Instead of "Load rows → Build a graphology DirectedGraph → Run algorithms", use "Load rows → Build a TaskGraph via `TaskGraph.fromRecords(tasks, edges)` → Run analysis functions"
|
||||
- Reference `@alkdev/taskgraph` analysis functions: `criticalPath()`, `parallelGroups()`, `bottlenecks()`, `riskPath()`, `shouldDecomposeTask()`, `workflowCost()`
|
||||
- Graphology is still a transitive dependency through `@alkdev/taskgraph` but hub code should prefer the `TaskGraph` API
|
||||
|
||||
2. **NAPI note** — Update "Why not taskgraph NAPI for v1" to note that `@alkdev/taskgraph` is now the TypeScript package (pure graphology-based, <5ms for 100 nodes) and the Rust CLI is for offline analysis only. No NAPI needed.
|
||||
|
||||
3. **Frontmatter parsing** — Reference `@alkdev/taskgraph`'s `parseFrontmatter()` and `serializeFrontmatter()` functions for task file I/O. Note: `parseTaskFile()` and `parseTaskDirectory()` are Node.js only (use `node:fs/promises`). For Deno, use `parseFrontmatter()` with Deno file I/O.
|
||||
|
||||
4. **References section** — Update graphology link to reference the @alkdev/taskgraph package instead of local paths.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Graphology Integration section references `TaskGraph.fromRecords()` API
|
||||
- [ ] Analysis functions reference `@alkdev/taskgraph` exports
|
||||
- [ ] NAPI note is updated for current state
|
||||
- [ ] Frontmatter parsing references `@alkdev/taskgraph` functions
|
||||
- [ ] References section updated
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/core-library-extraction-sync-2026-05-18.md (Section 2.7)
|
||||
Reference in New Issue
Block a user