Files
hub/docs/reviews/core-library-extraction-sync-2026-05-18.md
glm-5.1 2b63cda1c7 Setup repo: migrate architecture specs, code stubs, and tasks from alkhub_ts
Copy architecture docs, ADRs, storage domain specs, research, reviews,
and 56 storage architecture tasks from the alkhub_ts monorepo. Adapt for
standalone @alkdev/hub repo structure (src/ not packages/hub/).

Sanitize all sensitive information:
- Replace private IPs (10.0.0.1) with localhost defaults
- Remove internal server hostnames (dev1, ns528096)
- Replace /workspace/ private paths with npm package references
- Remove hardcoded credentials from examples
- Rewrite infrastructure.md without private network details

Add Deno project scaffolding: deno.json (pinned deps), .gitignore,
AGENTS.md, entry point. Migrate existing code stubs (crypto, config
types, logger) with updated import paths.
2026-05-25 10:56:32 +00:00

521 lines
26 KiB
Markdown

---
status: open
created: 2026-05-18
last_updated: 2026-05-18
---
# Core Library Extraction Sync Review
Review of the impact of extracting three core libraries — `@alkdev/operations`, `@alkdev/pubsub`, and `@alkdev/taskgraph` — on the alkhub_ts codebase and architecture documentation. These packages are now published on npm and replace in-repo code plus implement previously "not started" functionality.
---
## Summary
Three packages were extracted from (or designed for) this codebase and are now platform-agnostic npm packages:
| Package | Version | Replaces in `packages/core/` | New Capabilities |
|---------|---------|-------------------------------|------------------|
| `@alkdev/operations` | 0.1.0 | `operations/` (7 files) + `mcp/` (3 files) | Call protocol (PendingRequestMap), ResponseEnvelope, access control enforcement, CallError, SchemaAdapter, subscribe helper, SSE subscription handling |
| `@alkdev/pubsub` | 0.1.0 | `pubsub/` (5 files) | EventEnvelope, WebSocket client+server+worker event targets, 13 operators (was 3), inlined Repeater, `prefix`/`close()` on Redis ET |
| `@alkdev/taskgraph` | 0.0.2 | Nothing (new) | TaskGraph class, analysis (critical path, parallel groups, bottlenecks, risk, cost-benefit), frontmatter parsing |
The decision has been made to **remove `packages/core/` as a package entirely**. Its remaining modules (config, logger, crypto) will be relocated — most likely into hub directly, since spokes that need config can import `@alkdev/operations` config types or we create a minimal `@alkhub/config` package. The first spokes won't need provider key storage; eventual "hub-like spokes" will be addressed as a federation concern later.
---
## 1. Code Changes
### 1.1 Delete from `packages/core/`
All of these are replaced by npm packages:
**`core/pubsub/`** — replaced by `@alkdev/pubsub`:
- `create_pubsub.ts`
- `typed_event_target.ts`
- `redis_event_target.ts`
- `operators.ts`
- `mod.ts`
**`core/operations/`** — replaced by `@alkdev/operations`:
- `types.ts`
- `registry.ts`
- `env.ts`
- `scanner.ts`
- `validation.ts`
- `from_schema.ts`
- `from_openapi.ts`
- `mod.ts`
**`core/mcp/`** — replaced by `@alkdev/operations/from-mcp`:
- `wrapper.ts`
- `loader.ts`
- `mod.ts`
**Tests and fixtures** — for deleted modules:
- `tests/operations/registry.test.ts`
- `tests/operations/scanner.test.ts`
- `tests/pubsub/redis_event_target.test.ts`
- `tests/mcp/loader.test.ts`
- `tests/fixtures/registry.ts`
- `tests/fixtures/operations/demo/greet.ts`
- `tests/fixtures/operations/other/calculate.ts`
### 1.2 Relocate from `packages/core/`
These have no external replacement and need to be relocated:
| Module | Lines | Destination |
|--------|-------|-------------|
| `core/config/types.ts` | 169 | Hub package (or a thin `@alkhub/config` if spokes need shared config types) |
| `core/logger/mod.ts` | 27 | Hub package (logtape config is hub-specific anyway) |
| `core/utils/crypto.ts` | 119 | Hub package (encryption key management is hub-only) |
### 1.3 Delete `packages/core/` as a package
Once modules are relocated, remove:
- `packages/core/deno.json`
- `packages/core/mod.ts`
- The `"core"` entry from root `deno.json` workspace array
### 1.4 Update dependency declarations
**Root `deno.json`**:
- Remove `"packages/core"` from workspace array
- Add `@alkdev/operations`, `@alkdev/pubsub`, `@alkdev/taskgraph` to imports (if needed at root level)
**New `packages/hub/deno.json`** (when created):
- Add: `@alkdev/operations`, `@alkdev/pubsub`, `@alkdev/taskgraph`, `@alkdev/typebox`, `@alkdev/drizzlebox`, `hono`, `drizzle-orm`, `ioredis`, `logtape`, `@hono/mcp`, `ai`, `keypal`
- Remove (no longer direct): `@repeaterjs/repeater` (inlined in @alkdev/pubsub), `@modelcontextprotocol/sdk` (optional peer in @alkdev/operations)
**New `packages/spoke/deno.json`** (when created):
- Add: `@alkdev/operations`, `@alkdev/pubsub` (client event target only), `@alkdev/typebox`, `logtape`
### 1.5 Breaking API Changes
| Change | Impact | Migration |
|--------|--------|-----------|
| `registry.execute()` returns `ResponseEnvelope<T>` not `T` | All callers must `unwrap()` or access `.data` | `import { unwrap } from "@alkdev/operations"` |
| `OperationEnv` functions return `Promise<ResponseEnvelope>` not `Promise<unknown>` | All nested call sites | Same |
| `OperationContext` drops `stream`/`pubsub` fields | Handlers using these (none exist yet) | Use `PendingRequestMap.subscribe()` for subscriptions |
| `createPubSub` uses `PubSubEventMap` not `PubSubPublishArgsByKey` | Any pubsub usage | `createPubSub<{ eventType: PayloadType }>()` — publishes with `publish(type, id, payload)` |
| `createRedisEventTarget` takes `prefix` and has `close()` | Redis setup code | Add `prefix: "alk:events:"`, call `close()` on shutdown |
| Scanner uses `ScannerFS` interface, not `Deno.readDir` directly | Spoke scanner | Provide Deno adapter: `{ readdir: (p) => Deno.readDir(p), cwd: () => Deno.cwd() }` |
| `AccessControl` drops `customAuth` field | No code uses it yet | N/A |
| MCP adapter wraps results in `mcpEnvelope()` | MCP consumers | Use `unwrap()` or `isResponseEnvelope()` |
| `assertIsSchema` throws `Error` instead of `AssertionError` | Test code | Already the correct behavior per @alkdev/operations |
---
## 2. Architecture Spec Updates
### 2.1 AGENTS.md — Major Update
**Provenance table** — Replace all "Copied from predecessor project" and "Forked from graphql-yoga" entries:
| Module | Current Status | New 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** (client + server + worker) |
| 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" | Remains (to relocate) |
| Logger | "Needs proper config" | Remains (to relocate) |
| Storage | "Not started" | Not started (unchanged) |
**Key Patterns section** — Update:
- Operations: Reference `@alkdev/operations` package, add ResponseEnvelope and call protocol
- PubSub: Reference `@alkdev/pubsub` package, update from "graphql-yoga (MIT)" to standalone package with EventEnvelope pattern
- New: Task graph operations via `@alkdev/taskgraph`
**Reference Dependencies table** — Add:
| `@alkdev/operations` | `npm:@alkdev/operations@^0.1.0` | Operations, call protocol, MCP adapter, ResponseEnvelope |
| `@alkdev/pubsub` | `npm:@alkdev/pubsub@^0.1.0` | PubSub, EventEnvelope, event targets (Redis/WS/Worker) |
| `@alkdev/taskgraph` | `npm:@alkdev/taskgraph@^0.0.2` | Task graph, analysis, frontmatter |
Remove:
- `graphql-yoga` row (source now in `@alkdev/pubsub`)
Update:
- `graphology` row: note it's now a transitive dep of `@alkdev/taskgraph`, no longer a direct dep of this project
**Workspace Structure** — Remove `core/` package:
```
packages/
hub/ — Hono API server, storage (Drizzle+Postgres), auth, coordination, Redis events
spoke/ — Self-registering runner: websocket connection, dispatch, operation provider
```
Add note about external dependencies:
```
External @alkdev packages (npm):
@alkdev/operations — Operations registry, call protocol, MCP adapter, ResponseEnvelope
@alkdev/pubsub — PubSub, event targets (Redis/WS/Worker), operators
@alkdev/taskgraph — Task graph construction, analysis, frontmatter
```
**Constraints section** — Add:
- `@alkdev/pubsub`, `@alkdev/operations`, `@alkdev/taskgraph` are the canonical implementations — do not duplicate their code in-repo
### 2.2 overview.md — Major Update
**"What Exists" section** — Replace entirely:
| Module | Location | Status |
|--------|----------|--------|
| Operations system | `@alkdev/operations` | Published v0.1.0 |
| PubSub (createPubSub + operators) | `@alkdev/pubsub` | Published v0.1.0 |
| TypedEventTarget | `@alkdev/pubsub` | Published v0.1.0 |
| Redis EventTarget | `@alkdev/pubsub` | Published v0.1.0 |
| WebSocket EventTarget (client+server) | `@alkdev/pubsub` | Published v0.1.0 |
| Worker EventTarget | `@alkdev/pubsub` | Published v0.1.0 |
| MCP client adapter | `@alkdev/operations/from-mcp` | Published v0.1.0 |
| Call protocol (PendingRequestMap, CallHandler) | `@alkdev/operations` | Published v0.1.0 |
| Access control (enforceAccess) | `@alkdev/operations` | Published v0.1.0 |
| ResponseEnvelope | `@alkdev/operations` | Published v0.1.0 |
| SchemaAdapter (Zod/Valibot) | `@alkdev/operations/from-typemap` | Published v0.1.0 |
| SSE subscription handling | `@alkdev/operations/from-openapi` | Published v0.1.0 |
| Task graph + analysis | `@alkdev/taskgraph` | Published v0.0.2 |
| Config types | `packages/core/` | Stub — needs relocation |
| Logger | `packages/core/` | Stub — needs relocation |
**"What Needs Implementation"** — Remove completed items, keep remaining:
| Component | Spec | Priority |
|-----------|------|----------|
| ~~WebSocket EventTarget~~ | ~~spoke-runner.md~~ | ~~High~~**Done: `@alkdev/pubsub`** |
| ~~Call protocol (PendingRequestMap)~~ | ~~call-graph.md~~ | ~~High~~**Done: `@alkdev/operations`** |
| Storage (Drizzle+Postgres tables, migrations) | storage/ | High |
| Hub HTTP server (Hono) | hub-architecture.md | High |
| OpenAI proxy (Hono) | agent-sessions.md | High |
| Logger configuration | — | Medium |
| Hub config system | hub-config.md | Medium |
| MCP server (@hono/mcp) | mcp-server.md | Medium |
| Agent sessions (AI SDK) | agent-sessions.md | Medium |
| Coordination operations | coordination.md | Medium |
| Call graph storage | call-graph.md, storage/ | Medium |
| Operation graph | call-graph.md | Low |
| Call templates | call-graph.md | Low |
### 2.3 packages.md — Major Rewrite
**Remove `@alkhub/core` section entirely.** Add a new section for external `@alkdev/*` packages:
```
### `@alkdev/operations` (npm package)
Operations registry, call protocol, MCP adapter, ResponseEnvelope. Platform-agnostic.
Exports:
. — types, registry, call protocol (PendingRequestMap, buildCallHandler), subscribe, access control, error, env, scanner, validation, from_schema, response-envelope
./from-mcp — MCP tool adapter (ioredis optional peer)
./from-typemap — Zod/Valibot schema adapters (@alkdev/typemap optional peer)
./from-openapi — OpenAPI/SSE/HTTP service adapter
### `@alkdev/pubsub` (npm package)
PubSub, event targets, operators. Platform-agnostic.
Exports:
. — createPubSub, types, operators, repeater
./event-target-redis — Redis adapter (ioredis optional peer)
./event-target-websocket-client — Spoke-side WebSocket adapter
./event-target-websocket-server — Hub-side WebSocket adapter
./event-target-worker — Web Worker adapter (host + thread)
### `@alkdev/taskgraph` (npm package)
Task graph construction, analysis, frontmatter. Platform-agnostic.
Exports:
. — TaskGraph, analysis functions, schema, error types, frontmatter
```
**`@alkhub/hub` dependencies**: Add `@alkdev/operations`, `@alkdev/pubsub`, `@alkdev/taskgraph`. Remove `@repeaterjs/repeater` (inlined). Update: `ioredis` is optional (only if Redis ET is used directly; the package uses it).
**`@alkhub/spoke` dependencies**: Add `@alkdev/operations`, `@alkdev/pubsub`.
**Rules section** — Update rule 1: "core is transport-agnostic" becomes "packages should be transport-agnostic". Remove rule about core being persistence-agnostic (hub still is). Update dependency direction:
```
spoke → @alkdev/operations, @alkdev/pubsub
hub → @alkdev/operations, @alkdev/pubsub, @alkdev/taskgraph
hub ←/→ spoke (communicate via call protocol over WebSocket)
```
### 2.4 call-graph.md — Significant Update
**PendingRequestMap section** — Replace the schematic with actual `@alkdev/operations` API:
```ts
// From @alkdev/operations
import { PendingRequestMap } from "@alkdev/operations"
const prm = new PendingRequestMap({ eventTarget })
await prm.call(operationId, input, { deadline, identity })
const stream = prm.subscribe(operationId, input, { idleTimeout, identity })
prm.respond(requestId, output) // output must be ResponseEnvelope
prm.emitError(requestId, code, message, details?)
prm.complete(requestId)
prm.abort(requestId)
```
Key API differences from the doc:
- `call()` returns `Promise<ResponseEnvelope>` (not `Promise<unknown>`)
- `subscribe()` returns `AsyncIterable<ResponseEnvelope>`
- `respond()` requires output to be a `ResponseEnvelope`
- Deadline and idle timeout are built in
- Constructor takes optional `EventTarget` for pluggable transport
**CallHandler section** — Reference `buildCallHandler` from `@alkdev/operations`:
```ts
import { buildCallHandler } from "@alkdev/operations"
const handler = buildCallHandler({ registry, eventTarget })
```
**buildEnv section** — Remove `callMap` parameter. In `@alkdev/operations`, `buildEnv`:
- No longer takes `callMap` — uses `PendingRequestMap` internally
- Sets `trusted: true` on nested context
- Returns env functions that return `Promise<ResponseEnvelope>`
**Dependencies section** — Replace graphology direct deps. Graphology is now a transitive dependency through `@alkdev/taskgraph`. Call graph storage still uses graphology for runtime operations but should prefer `@alkdev/taskgraph`'s `TaskGraph` class when applicable.
### 2.5 operations.md — Major Rewrite
This doc needs significant restructuring since most of what it describes is now in `@alkdev/operations`.
**Key changes**:
- Remove "In-repo location: `packages/core/operations/`" — now external package
- Component descriptions should reference `@alkdev/operations` exports
- Schema Adapters section: Replace raw `@alkdev/typemap` dynamic import description with `SchemaAdapter` pattern
- Remove SSE Subscription Handler Fix from open issues — fixed in `@alkdev/operations/from-openapi`
- Update Call Protocol Integration section to reference `@alkdev/operations` API
- Add ResponseEnvelope concept (universal result wrapper: local/http/mcp)
- Add CallError/InfrastructureErrorCode concept
- Update access control: `enforceAccess` is now in the package, with `trusted` bypass
**New concepts to document**:
- `ResponseEnvelope<T>` with source discriminant (`"local"` | `"http"` | `"mcp"`)
- `subscribe()` helper for subscription operations
- `ScannerFS` interface (Deno runtime agnostic)
- `OpenAPIServiceRegistry` class for managing HTTP services
- `parseSSEFrames()` for SSE subscription handling
### 2.6 pubsub-redis.md — Major Rewrite
This doc describes code that's now in `@alkdev/pubsub`. Key changes:
- **Source location**: `@alkdev/pubsub` npm package, not `packages/core/pubsub/`
- **createPubSub API**: Uses `PubSubEventMap` (simple `{ [eventType: string]: payload }`) not `PubSubPublishArgsByKey`
- **EventEnvelope**: New concept — `{ type, id, payload }` is the cross-process message format. Reserved `__` prefix for control messages.
- **Redis EventTarget**: Now accepts `prefix` option (e.g., `"alk:events:"`) and has `close()` method. No need for serializer workaround to add prefix.
- **WebSocket EventTarget**: No longer "Not started" / "Deferred". Document both client and server adapters.
- **Worker EventTarget**: New adapter for Web Workers (host + thread).
- **Operators**: 13 operators, not 3. New: `take`, `reduce`, `toArray`, `batch`, `dedupe`, `window`, `flat`, `groupBy`, `chain`, `join`.
- **Repeater**: Inlined, no longer depends on `@repeaterjs/repeater` externally.
- **Prior Art section**: Update to reflect `@alkdev/pubsub` is a standalone package, not forked code in-repo.
### 2.7 storage/tasks.md — Update Graphology Section
**"Graphology Integration" section** — Replace direct graphology usage with `@alkdev/taskgraph`:
Instead of:
```
1. Load all tasks + task_dependencies rows for a project from the DB
2. Build a graphology DirectedGraph in memory
3. Run graph algorithms as needed
```
Use:
```
1. Load all tasks + task_dependencies rows for a project from the DB
2. Build a TaskGraph via TaskGraph.fromRecords(tasks, edges)
3. Run analysis functions as needed (criticalPath, parallelGroups, bottlenecks, riskPath, etc.)
```
**Frontmatter parsing** — Reference `@alkdev/taskgraph`'s `parseFrontmatter` and `serializeFrontmatter` functions instead of custom parsers. Note: `parseTaskFile` and `parseTaskDirectory` are Node.js only (use `node:fs/promises`).
**References section** — Update graphology reference to point to `@alkdev/taskgraph` package.
**NAPI note** — The doc says "Why not taskgraph NAPI for v1". This is now resolved: `@alkdev/taskgraph` is pure TypeScript (graphology-based), and the Rust CLI (`taskgraph`) is for offline analysis. The TS package handles runtime graph ops.
### 2.8 hub-architecture.md — Update Component Table
- Operations row: `@alkdev/operations` not `core/operations/`
- PubSub row: `@alkdev/pubsub` not `core/pubsub/`
- Call protocol row: `@alkdev/operations` not `core/` (see call-graph.md)
- WebSocket adapter: "pending" → "available in `@alkdev/pubsub`"
### 2.9 hub-config.md — Update Redis EventTarget Example
Update `createRedisEventTarget` example to include `prefix`:
```ts
createRedisEventTarget({
publishClient,
subscribeClient,
prefix: "alk:events:",
})
```
### 2.10 hub-startup.md — Update References
- PendingRequestMap + CallHandler: note these come from `@alkdev/operations`
- PubSub setup: reference `@alkdev/pubsub` with `prefix` option
### 2.11 spoke-runner.md — Update References
- WebSocketEventTarget: `@alkdev/pubsub/event-target-websocket-client`
- PendingRequestMap: `@alkdev/operations`
- Scanner: `@alkdev/operations` with `ScannerFS` Deno adapter
- SchemaAdapters: `@alkdev/operations/from-typemap`
- `FromSchema()` / `FromOpenAPI()`: `@alkdev/operations/from-schema` / `@alkdev/operations/from-openapi`
### 2.12 ADR-013 — Update Paths
- Update `packages/core/operations/scanner.ts` references to `@alkdev/operations/scanner`
- Update `packages/core/operations/from_schema.ts` references to `@alkdev/operations/from_schema`
- Update `packages/core/operations/from_openapi.ts` references to `@alkdev/operations/from_openapi`
- Update scanner enhancement task to reference `SchemaAdapter` pattern from `@alkdev/operations/from-typemap`
### 2.13 docs/research/migration/ — Update or Archive
Both `operations.md` and `pubsub.md` in this directory describe planned extractions that are now **complete**. Options:
- **Archive**: Move to `docs/research/migration/completed/` with a status note
- **Update**: Rewrite as "completed migration" docs showing before/after
Recommend: Archive both. They served their purpose and the current API surface is documented in the `@alkdev/*` package READMEs and this review.
### 2.14 docs/reviews/docs-consistency-review-2026-04-17.md — Superseded Entries
Several findings from the previous review are now resolved by the extractions:
| Finding | Original Issue | 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 in `@alkdev/operations` |
| I6 | `OperationContext.stream` never populated | **Resolved**: `stream` field removed from context in `@alkdev/operations` |
| I7 | `@repeaterjs/repeater` version mismatch risk | **Resolved**: Inlined in `@alkdev/pubsub`, no external dep |
---
## 3. What's Now Unblocked
| Component | Previous Status | Now Available In |
|-----------|-----------------|------------------|
| Call protocol (PendingRequestMap, CallHandler) | Not started | `@alkdev/operations` |
| WebSocket transport (client + server) | Not started | `@alkdev/pubsub` |
| WebSocket connection management (backpressure, SpokeEventTarget) | Not started | `@alkdev/pubsub` |
| Access control enforcement (checkAccess, enforceAccess) | Not started | `@alkdev/operations` |
| Task graph operations (topo sort, cycles, critical path, risk) | Not started | `@alkdev/taskgraph` |
| ResponseEnvelope (source tracking) | Not started | `@alkdev/operations` |
| Schema conversion (Zod/Valibot) | Not started | `@alkdev/operations/from-typemap` |
| SSE subscription handling | Broken | `@alkdev/operations/from-openapi` |
| Error model (CallError, InfrastructureErrorCode) | Not started | `@alkdev/operations` |
| EventEnvelope (structured cross-process messages) | Not started | `@alkdev/pubsub` |
## 4. What Still Needs Implementation
All of these are hub or spoke level concerns that can now be built on top of the extracted packages:
| Component | Depends On | Spec |
|-----------|------------|------|
| Storage (Drizzle+Postgres tables, migrations) | `@alkdev/typebox`, `@alkdev/drizzlebox`, `drizzle-orm` | storage/ |
| Hub HTTP server (Hono) | `@alkdev/operations`, `@alkdev/pubsub`, `hono` | hub-architecture.md |
| Spoke WebSocket client | `@alkdev/operations`, `@alkdev/pubsub/event-target-websocket-client` | spoke-runner.md |
| Hub WebSocket server (spoke management) | `@alkdev/operations`, `@alkdev/pubsub/event-target-websocket-server` | spoke-runner.md |
| OpenAI proxy | `hono`, AI SDK | agent-sessions.md |
| Auth (keypal) | Hono middleware | — |
| MCP server (@hono/mcp) | `@alkdev/operations`, `@hono/mcp` | mcp-server.md |
| Agent sessions (AI SDK) | `@alkdev/operations`, AI SDK, storage | agent-sessions.md |
| Coordination operations | `@alkdev/operations`, storage | coordination.md |
| Call graph storage | `@alkdev/operations`, storage | storage/call-graph.md |
| Hub config loader | `@alkdev/operations` (config types) | hub-config.md |
| Logger configuration | logtape | — |
---
## 5. Package Dependency Graph (New)
```
@alkdev/operations → @alkdev/typebox, @alkdev/pubsub, @logtape/logtape
→ (optional peers): @alkdev/typemap, @modelcontextprotocol/sdk
@alkdev/pubsub → (no runtime deps)
→ (optional peer): ioredis (for ./event-target-redis)
@alkdev/taskgraph → @alkdev/typebox, graphology (+plugins), yaml
@alkhub/hub → @alkdev/operations, @alkdev/pubsub, @alkdev/taskgraph,
@alkdev/typebox, @alkdev/drizzlebox, hono, drizzle-orm,
ioredis, ai, keypal, logtape, @hono/mcp
@alkhub/spoke → @alkdev/operations, @alkdev/pubsub, @alkdev/typebox, logtape
```
No `@alkhub/core` package. Config types, logger, and crypto utils live in `@alkhub/hub` (or a thin shared package if spokes need config types — this can be decided when implementing the spoke).
---
## 6. Open Decisions
### 6.1 Where do config types go?
`core/config/types.ts` has `HubConfig`, `SpokeConfig`, `BaseConfig`, `PostgresConfig`, `RedisConfig`, `HttpConfig`, `AuthConfig`. These are used by both hub and spoke.
Options:
- **A**: Move to `@alkhub/hub`. Spokes that need config types import them from their own copy or a minimal `@alkhub/config` package.
- **B**: Create `@alkdev/config` npm package. Platform-agnostic like the other `@alkdev/*` packages.
- **C**: Put config types in `@alkdev/operations`. They're already TypeBox schemas and operations already depend on `@alkdev/typebox`.
**Recommendation**: A for now. First spokes won't need hub config. Re-evaluate when a spoke actually needs shared config types. The spoke config types are already minimal (`SpokeConfig` has `hub.url` and `hub.auth.tokenFile`).
### 6.2 Logger and crypto?
`core/logger/mod.ts` (27 lines) and `core/utils/crypto.ts` (119 lines) are hub-specific concerns. Move them into `@alkhub/hub` directly.
### 6.3 How to handle `ScannerFS` for Deno?
`@alkdev/operations` uses an abstract `ScannerFS` interface. The spoke needs a Deno adapter:
```ts
import { scanOperations } from "@alkdev/operations"
const DenoFS: ScannerFS = {
readdir: async (path) => Deno.readDir(path),
cwd: () => Deno.cwd(),
}
const operations = await scanOperations("./operations", DenoFS)
```
This is minimal (~3 lines) and can live in the spoke package.
### 6.4 Research migration docs?
`docs/research/migration/operations.md` and `docs/research/migration/pubsub.md` describe extraction plans that are now complete. They should be archived or removed — they're historical context, not current documentation.
### 6.5 Previous consistency review findings?
The `docs-consistency-review-2026-04-17.md` has several findings that are now resolved by the extractions (C5, I2, I5, I6, I7 at minimum). These should be marked resolved in that document or superseded by this review.
---
## 7. Suggested Execution Order
1. **Delete replaced code** from `packages/core/` (operations, pubsub, mcp dirs + their tests)
2. **Update `packages/core/deno.json`** — remove deleted exports and dependencies
3. **Relocate remaining core modules** (config, logger, crypto) into `packages/hub/`
4. **Remove `packages/core/`** from workspace
5. **Update architecture docs** (overview, packages, call-graph, operations, pubsub-redis as priority)
6. **Update AGENTS.md** — provenance, key patterns, reference deps, workspace structure
7. **Update storage/tasks.md** — taskgraph references
8. **Update secondary docs** (hub-architecture, hub-config, hub-startup, spoke-runner, ADR-013)
9. **Archive research/migration docs** or mark as completed
10. **Update docs-consistency-review-2026-04-17.md** — mark superseded findings as resolved