Files
Dragan Spiridonov eca8d86b37 fix(mcp,workers): wire daemon to kernel after fleet_init (#491 Bug 1+4b)
Issue #491 (Jordi Izquierdo) — on a vanilla v3.9.31 install, every
domain-dependent worker tick fails with "<domain> not available" and
`aqe learning loop-health` shows `learningWorker: never-ran`. Root
cause: the MCP-hosted daemon constructs its WorkerManager in
`src/mcp/entry.ts` *before* the kernel exists, so it starts with
StubWorkerDomainAccess (getDomainAPI returns undefined for every
domain) and a private InMemoryWorkerMemory that no dashboard ever
reads from. The kernel is built inside `handleFleetInit` later in the
same process, but the daemon is never told about it.

Fix:
  - Promote `setKernel`/`setMemory` from concrete-class-only methods
    to the public WorkerManager interface so consumers can find them.
  - Add `setMemory(memory: WorkerMemory)` to WorkerManagerImpl
    (mirrors the existing `setKernel`). Broadens the internal
    `memory` field from InMemoryWorkerMemory to the WorkerMemory
    interface so a shared HybridMemoryBackend is type-correct.
  - Hoist `KernelReference` to interfaces.ts as
    `WorkerKernelReference` so the interface signature is properly
    typed without re-introducing the kernel↔worker circular
    dependency the local declaration was avoiding.
  - In `handleFleetInit`, after `pluginLoader.loadAll()`, call
    `getDaemon().getWorkerManager().setKernel(state.kernel)` and
    `setMemory(state.kernel.memory)`. Wrapped in try/catch so a
    workless smoke install (no daemon module bundled) still gets a
    working kernel + queen.

Why an interface change, not the cast Bolivar used: the public
WorkerManager interface didn't expose setKernel/setMemory at all, so
the call site looked like it was reaching for a private method —
exactly the kind of seam that previous releases dropped on the
assumption that the bridge superseded it. Making it part of the
public contract means the next maintainer can't accidentally remove
the wiring without breaking a typed interface.

Integration test: tests/integration/mcp/fleet-init-wires-daemon.test.ts
pins three assertions:
  1. Before fleet_init, the daemon's WorkerManager returns undefined
     for `getDomainAPI('learning-optimization')` (the Bug 1 failure
     mode).
  2. After fleet_init, it returns a real plugin API.
  3. The WorkerManager's memory is the same instance as the kernel's
     memory (Bug 4b), so worker writes reach the dashboard's reads.
Verified red→green: with the wiring reverted, all three fail with
"expected undefined to be defined"; with the wiring in place, all
three pass in ~210ms.

Note: Bugs 2, 3, and 4a are addressed in follow-up commits in this
series.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 08:21:35 +00:00
..