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

26 KiB

status, created, last_updated
status created last_updated
open 2026-05-18 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 HighDone: @alkdev/pubsub
Call protocol (PendingRequestMap) call-graph.md HighDone: @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:

// 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:

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:

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:

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