dream(memory): wire ControllerRegistry.set() so MutationGuard/AttestationLog activation stops being dead code

memory-bridge.ts's post-init controller-activation wiring (reasoningBank,
learningSystem, skills, semanticRouter, gnnService, rvfOptimizer,
mutationGuard, attestationLog, guardedVectorBackend) has always gated
registration on `typeof reg.set === 'function'`, falling back to an
unread `reg._controllers` property otherwise. ControllerRegistry never
implemented a public set() method, so every external activation silently
took the dead fallback: registry.get('mutationGuard')/get('attestationLog')
always returned null, permanently disabling write-mutation validation
(fail-open "no guard installed = allow") and audit logging (silent no-op)
on the real store/delete/purge paths. Same bug shape as the already-fixed
#3327 Finding A (reasoningBank.store never existed, so its own
typeof-guard was dead too).

Adds ControllerRegistry.set<T>(name, instance) mirroring the shape
initController() already produces internally. Zero changes needed at any
of the 9 memory-bridge.ts call sites.

5 new discriminating tests reproduce memory-bridge.ts's exact activation
pattern; stash-isolated baseline fails all 5 (TypeError: registry.set is
not a function), candidate passes all 5. Full @claude-flow/memory suite:
650/651 both ways (1 pre-existing environmental failure, byte-identical,
unrelated chmod/root-bypass test). tsc --noEmit: 0 errors.

Evaluated, pending independent adversarial critique.
This commit is contained in:
Claude committed 2026-10-28 06:32:58 +00:00
1 parent bd19b6f984
commit afe2cdd9fc
2 files changed
+108

No files matched your search

@@ -465,6 +465,88 @@ describe('ControllerRegistry', () => {
});
});
// ----- External controller registration (set) -----
//
// Dream Cycle 2026-10-28: `memory-bridge.ts`'s post-init wiring
// constructs several AgentDB-bundled controllers itself (mutationGuard,
// attestationLog, semanticRouter, skills, reasoningBank, learningSystem,
// gnnService, rvfOptimizer, guardedVectorBackend) and has always gated
// registration on `typeof reg.set === 'function'`, falling back to an
// unread `reg._controllers` property otherwise. `ControllerRegistry` had
// no public `set()` until this fix, so every one of those activations
// silently wrote to the dead fallback — `get('mutationGuard')` and
// `get('attestationLog')` always returned null, permanently disabling
// write-mutation validation (fail-open) and audit logging (silent no-op).
describe('external controller registration (set)', () => {
it('makes an externally-constructed instance immediately visible via get()/isEnabled()', async () => {
await registry.initialize({ backend: mockBackend });
const fakeGuard = { validate: () => ({ allowed: true }) };
expect(registry.get('mutationGuard')).toBeNull();
expect(registry.isEnabled('mutationGuard')).toBe(false);
registry.set('mutationGuard', fakeGuard);
expect(registry.get('mutationGuard')).toBe(fakeGuard);
expect(registry.isEnabled('mutationGuard')).toBe(true);
});
it('reproduces the exact memory-bridge.ts activation pattern end-to-end', async () => {
// Mirrors memory-bridge.ts's real activation guard verbatim:
// if (typeof reg.set === 'function') reg.set(name, inst);
// else reg._controllers = { ...(reg._controllers || {}), [name]: inst };
await registry.initialize({ backend: mockBackend });
const reg = registry as unknown as Record<string, unknown>;
const fakeAttestation = { record: vi.fn() };
if (typeof reg.set === 'function') {
(reg.set as (n: ControllerName, i: unknown) => void)('attestationLog', fakeAttestation);
} else {
(reg as any)._controllers = { ...((reg as any)._controllers || {}), attestationLog: fakeAttestation };
}
// Before the fix this fell through to the dead `_controllers` branch
// and get() stayed null forever — the exact production symptom.
expect(registry.get('attestationLog')).toBe(fakeAttestation);
});
it('lists an externally-set controller via listControllers() and getActiveCount()', async () => {
await registry.initialize({ backend: mockBackend });
expect(registry.isEnabled('guardedVectorBackend')).toBe(false);
const activeBefore = registry.getActiveCount();
registry.set('guardedVectorBackend', { lookup: () => [] });
expect(registry.getActiveCount()).toBe(activeBefore + 1);
const entry = registry.listControllers().find((c) => c.name === 'guardedVectorBackend');
expect(entry).toBeDefined();
expect(entry?.enabled).toBe(true);
});
it('shuts down an externally-set controller cleanly on registry.shutdown()', async () => {
await registry.initialize({ backend: mockBackend });
const destroy = vi.fn();
registry.set('mutationGuard', { destroy });
await registry.shutdown();
expect(destroy).toHaveBeenCalledOnce();
expect(registry.getActiveCount()).toBe(0);
});
it('overwrites a prior entry cleanly on re-registration (non-discriminating control)', async () => {
await registry.initialize({ backend: mockBackend });
const first = { id: 'first' };
const second = { id: 'second' };
registry.set('semanticRouter', first);
registry.set('semanticRouter', second);
expect(registry.get('semanticRouter')).toBe(second);
});
});
// ----- Health Check -----
describe('health check', () => {
@@ -431,6 +431,32 @@ export class ControllerRegistry extends EventEmitter {
return false;
}
/**
* Register an already-constructed controller instance directly, outside
* the level-based `initialize()` path.
*
* Dream Cycle 2026-10-28: this method did not exist until now, even though
* `memory-bridge.ts`'s post-init wiring (reasoningBank/learningSystem/
* skills/semanticRouter/gnnService/rvfOptimizer/mutationGuard/
* attestationLog/guardedVectorBackend) has called `typeof reg.set ===
* 'function'` and fallen through to a dead `reg._controllers` property —
* never read anywhere — on every call since that wiring was added. As a
* result `get('mutationGuard')`/`get('attestationLog')` always returned
* null, so `guardValidate()` always took its "no guard installed = allow"
* branch and `logAttestation()` was a permanent no-op. Same shape as the
* already-fixed #3327 Finding A (`reasoningBank.store` never existed, so
* its own `typeof ... === 'function'` guard was dead too).
*/
set<T>(name: ControllerName, instance: T): void {
this.controllers.set(name, {
name,
instance,
level: -1, // externally registered — not part of level-based init
initTimeMs: 0,
enabled: true,
});
}
/**
* Aggregate health check across all controllers.
*/