This commit is contained in:
79
packages/agent/tests/r2-recall-closure.test.ts
Normal file
79
packages/agent/tests/r2-recall-closure.test.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* R5 / R2 closure — the memory-loop sign gate must be closed *end to end*.
|
||||
*
|
||||
* R2 (commit 5592727) wired `isSelfIncapacityAssertion()` into the autoSave
|
||||
* `save()` chokepoint so DEFECT-2 poison is coerced to `temporary` importance.
|
||||
* Its stated contract (orchestrator.ts:751-757, memory-sign-gate.ts:14-18) is
|
||||
* that `temporary` frames are *recall-excluded* — "the recall path already
|
||||
* excludes `temporary` (see getRecentFrames)".
|
||||
*
|
||||
* That is only true for the narrow `fetchRecentFrames` SQL path (catch-up /
|
||||
* workspace-recent). The dominant prompt recall path is `recallMemory()`'s
|
||||
* normal branch → `HybridSearch.search()`, which fetches frames with only
|
||||
* optional *temporal* filtering and applies `importance` as a scoring weight,
|
||||
* never an exclusion. So a sign-gated poison frame is still semantically
|
||||
* retrievable and re-enters the prompt — the loop the unit test never proved
|
||||
* closed because it only tested the classifier, not the round trip.
|
||||
*
|
||||
* This is the live, behavioral proof of the closed loop (TDD: RED on the
|
||||
* pre-fix code, GREEN after recallMemory excludes non-authoritative
|
||||
* importance).
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { MindDB } from '@waggle/core';
|
||||
import { Orchestrator } from '../src/orchestrator.js';
|
||||
import { MockEmbedder } from '../../hive-mind-core/tests/mind/helpers/mock-embedder.js';
|
||||
|
||||
describe('R2 sign-gate is closed end-to-end (temporary excluded from semantic recall)', () => {
|
||||
let db: MindDB;
|
||||
let orchestrator: Orchestrator;
|
||||
|
||||
beforeEach(() => {
|
||||
db = new MindDB(':memory:');
|
||||
orchestrator = new Orchestrator({ db, embedder: new MockEmbedder() });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
db.close();
|
||||
});
|
||||
|
||||
it('does not surface a sign-gated (temporary) DEFECT-2 frame via recallMemory, but still recalls the authoritative decision', async () => {
|
||||
// A self-incapacity assertion — the R2 save-side gate coerces this exact
|
||||
// class to `temporary` (orchestrator.ts:757). Seed it at the importance the
|
||||
// gate produces.
|
||||
await orchestrator.executeTool('save_memory', {
|
||||
content:
|
||||
"run npm install -g @modelcontextprotocol/server-filesystem yourself and restart the session before I can use it",
|
||||
importance: 'temporary',
|
||||
});
|
||||
// The authoritative truth on the same topic — the control. Recall MUST
|
||||
// still surface this; the exclusion has to be specific to `temporary`.
|
||||
await orchestrator.executeTool('save_memory', {
|
||||
content:
|
||||
'Decision: the MCP filesystem connector is installed and wired for this workspace',
|
||||
importance: 'important',
|
||||
});
|
||||
|
||||
// Precondition: seeds landed at the intended importances (fences off the
|
||||
// save_memory dramatic-claim downgrade / dedup confounds — if either fired
|
||||
// this fails here with a clear signal rather than passing vacuously).
|
||||
const rows = db
|
||||
.getDatabase()
|
||||
.prepare('SELECT content, importance FROM memory_frames ORDER BY id')
|
||||
.all() as Array<{ content: string; importance: string }>;
|
||||
const poison = rows.find(r => r.content.includes('npm install -g @modelcontextprotocol'));
|
||||
const decision = rows.find(r => r.content.includes('connector is installed and wired'));
|
||||
expect(poison?.importance, 'poison frame must be stored as temporary').toBe('temporary');
|
||||
expect(decision?.importance, 'decision frame must be stored as important').toBe('important');
|
||||
|
||||
const result = await orchestrator.recallMemory(
|
||||
'how do I install the mcp filesystem connector',
|
||||
);
|
||||
|
||||
// The closed-loop invariant: a sign-gated frame must NOT re-enter the
|
||||
// prompt as authoritative recall…
|
||||
expect(result.text).not.toContain('run npm install -g @modelcontextprotocol');
|
||||
// …while legitimate, authoritative memory on the same query still surfaces.
|
||||
expect(result.text).toContain('connector is installed and wired');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user