Files
ECC/examples/unified-memory/conformance.cjs
T
Affaan MustafaandGitHub 052ddcb988 Validate unified memory evidence across local CLI and MCP handoffs (#3027)
* test(memory): add cross-harness conformance example

* fix: update js-yaml to patched 4.3.2

* feat(memory): verify recalled evidence against scoped source catalog

* fix: preserve control-character validation under lint
2026-09-10 14:11:04 +03:00

247 lines
14 KiB
JavaScript

'use strict';
// Runs existing ECC code against disposable synthetic vaults. No service or SDK installs.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const crypto = require('node:crypto');
const { spawnSync } = require('node:child_process');
const { encodeEvidence, verifyEvidence } = require('./evidence.cjs');
const repo = path.resolve(__dirname, '../..');
const sha256 = bytes => crypto.createHash('sha256').update(bytes).digest('hex');
const cleanEnv = { PATH: process.env.PATH || '/usr/bin:/bin' };
// Use the already installed Ajv; no package manager or network operation occurs.
let dependencyRoot;
try {
dependencyRoot = path.dirname(path.dirname(require.resolve('ajv/package.json')));
} catch {
process.stderr.write('ECC memory example requires the existing Ajv runtime dependency.\n');
process.exit(1);
}
const sourcePaths = [
'scripts/memory.js', 'scripts/memory-mcp.mjs', 'scripts/lib/memory-vault.js',
'scripts/lib/memory-vault-format.js', 'scripts/lib/path-safety.js',
'scripts/lib/missing-dependency.js', 'schemas/memory.schema.json', 'package.json',
'examples/unified-memory/evidence.cjs',
];
function snapshot() {
return Object.fromEntries(sourcePaths.map(file => [file, sha256(fs.readFileSync(path.join(repo, file)))]));
}
function sourceHead() {
const result = spawnSync('git', ['-C', repo, 'rev-parse', 'HEAD'], {
encoding: 'utf8', env: cleanEnv, timeout: 5000, maxBuffer: 1024,
});
return result.status === 0 && /^[a-f0-9]{40}\s*$/.test(result.stdout) ? result.stdout.trim() : null;
}
const before = snapshot();
const headBefore = sourceHead();
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-memory-conformance-'));
const checks = [];
const startedAt = new Date().toISOString();
function envFor(partition = 'alpha', harness = 'codex', allowUser = false) {
const cwd = path.join(root, partition);
fs.mkdirSync(cwd, { recursive: true });
return { cwd, env: { ...cleanEnv,
NODE_PATH: dependencyRoot,
ECC_MEMORY_PROJECT_ROOT: path.join(cwd, 'vault'),
ECC_MEMORY_USER_ROOT: path.join(root, 'synthetic-user'),
...(harness ? { ECC_MEMORY_HARNESS: harness } : {}),
ECC_MEMORY_ALLOW_USER_SCOPE: allowUser ? '1' : '0',
} };
}
function run(script, args, input, options) {
return spawnSync(process.execPath, [path.join(repo, script), ...args], {
...options, input, encoding: 'utf8', timeout: 10000, maxBuffer: 2 * 1024 * 1024,
});
}
function cli(args, input = '', partition = 'alpha') {
const result = run('scripts/memory.js', [...args, '--json'], input, envFor(partition));
assert.equal(result.status, 0, 'Synthetic CLI operation failed; raw output withheld');
return JSON.parse(result.stdout);
}
function mcp(harness, calls, partition = 'alpha', allowUser = false) {
const frames = [
{ jsonrpc: '2.0', id: 1, method: 'initialize', params: {
protocolVersion: '2025-11-25', capabilities: {},
clientInfo: { name: 'ecc-lane-conformance', version: '1.0.0' },
} },
{ jsonrpc: '2.0', method: 'notifications/initialized', params: {} },
...calls.map(([name, args], index) => ({ jsonrpc: '2.0', id: index + 2,
method: 'tools/call', params: { name, arguments: args } })),
];
const result = run('scripts/memory-mcp.mjs', [],
frames.map(frame => JSON.stringify(frame)).join('\n') + '\n', envFor(partition, harness, allowUser));
assert.equal(result.status, 0, 'Synthetic MCP process failed; raw output withheld');
const responses = result.stdout.trim().split('\n').map(line => JSON.parse(line));
assert.equal(responses.length, calls.length + 1, 'Missing or extra MCP response');
assert.equal(responses[0].result.protocolVersion, '2025-11-25');
return calls.map((_, index) => {
const response = responses.find(item => item.id === index + 2);
assert.ok(response, 'Missing correlated MCP response');
return response;
});
}
function payload(response) {
assert.equal(response.error, undefined, 'Unexpected JSON-RPC error');
assert.notEqual(response.result.isError, true, 'Unexpected tool rejection');
return JSON.parse(response.result.content.find(item => item.type === 'text').text);
}
function check(name, fn) { fn(); checks.push({ name, passed: true }); }
function save(title, scope = 'project', target = 'all', partition = 'alpha', body = 'Synthetic orbit evidence.') {
return cli(['save', '--title', title, '--scope', scope, '--source-harness', 'codex',
'--target', target, '--stdin'], body, partition).memory;
}
try {
const sourceText = 'Synthetic fixture only: orbit project uses scoped memory.';
// Kept separately from recalled content; memory cannot supply its own source catalog.
const sources = new Map([['fixture:orbit', Object.freeze({ workspace: 'alpha', scope: 'project', text: sourceText,
observedAt: startedAt, sessionId: 'fixture-session', checkpointId: 'fixture-checkpoint' })]]);
const evidenceContext = { workspace: 'alpha', scope: 'project' };
const body = encodeEvidence('fixture:orbit', sources, evidenceContext);
const shared = save('orbit shared evidence', 'project', 'all', 'alpha', body);
const team = save('orbit team context', 'team');
const targeted = save('orbit codex context', 'project', 'codex');
const user = save('orbit user context', 'user');
const other = save('orbit other project', 'project', 'all', 'beta');
for (const harness of ['codex', 'claude', 'hermes']) {
const result = mcp(harness, [
['memory_search', { query: 'orbit' }],
['memory_read', { id: shared.id }],
['memory_read', { id: targeted.id }],
['memory_search', { query: 'orbit', scopes: ['user'] }],
['memory_save', { title: 'spoof', body: 'Synthetic', sourceHarness: 'other' }],
['memory_search', { query: 'orbit', targetHarness: 'codex' }],
['memory_save', { title: 'trusted', body: 'Synthetic', trust: 'verified' }],
['memory_read', { id: user.id, scope: 'user' }],
['memory_save', { title: 'user write', body: 'Synthetic', scope: 'user' }],
]);
check(`${harness}: CLI/MCP ordered search parity`, () => {
const expected = cli(['search', 'orbit', '--target-harness', harness]);
assert.deepEqual(payload(result[0]).results, expected.results.map(({ memory, score, excerpt }) => ({ memory, score, excerpt })));
const ids = payload(result[0]).results.map(item => item.memory.id);
assert.ok(ids.includes(shared.id) && ids.includes(team.id));
assert.equal(ids.includes(targeted.id), harness === 'codex');
assert.ok(!ids.includes(user.id) && !ids.includes(other.id));
});
check(`${harness}: read preserves provenance and unreviewed trust`, () => {
const read = payload(result[1]).memory;
assert.equal(read.body, body);
for (const field of ['id', 'scope', 'sourceHarness', 'targetHarnesses', 'createdAt', 'updatedAt', 'trust']) {
assert.deepEqual(read[field], shared[field]);
}
assert.equal(read.trust, 'unreviewed');
const cliRead = cli(['read', shared.id]).memory;
assert.deepEqual(verifyEvidence(read.body, sources, { workspace: 'alpha', scope: read.scope }),
verifyEvidence(cliRead.body, sources, { workspace: 'alpha', scope: cliRead.scope }));
});
check(`${harness}: direct target visibility enforced by MCP`, () => {
if (harness === 'codex') assert.equal(payload(result[2]).memory.id, targeted.id);
else assert.equal(result[2].result.isError, true);
});
check(`${harness}: scope elevation, identity spoofing and trust promotion rejected`, () => {
for (const response of result.slice(3)) assert.equal(response.error?.code, -32602);
});
check(`${harness}: query reproducible across process restart`, () => {
assert.deepEqual(payload(mcp(harness, [['memory_search', { query: 'orbit' }]])[0]), payload(result[0]));
});
}
check('MCP write identity and evidence survive CLI handoff read', () => {
sources.set('fixture:handoff', Object.freeze({ workspace: 'alpha', scope: 'project', text: 'Synthetic handoff.',
observedAt: startedAt, sessionId: 'fixture-hermes-session', checkpointId: 'fixture-handoff' }));
const handoffBody = encodeEvidence('fixture:handoff', sources, evidenceContext);
const saved = payload(mcp('hermes', [['memory_save', { title: 'handoff fixture', body: handoffBody,
kind: 'handoff', targetHarnesses: ['codex'], links: [shared.id] }]])[0]).memory;
assert.equal(saved.sourceHarness, 'hermes');
assert.equal(saved.trust, 'unreviewed');
const read = payload(mcp('codex', [['memory_read', { id: saved.id }]])[0]).memory;
assert.deepEqual(read.links, [shared.id]);
const cliRead = cli(['read', saved.id]).memory;
assert.equal(cliRead.body, handoffBody);
assert.equal(cliRead.sourceHarness, 'hermes');
assert.equal(cliRead.trust, 'unreviewed');
assert.deepEqual(verifyEvidence(cliRead.body, sources, { workspace: 'alpha', scope: cliRead.scope }),
verifyEvidence(read.body, sources, { workspace: 'alpha', scope: read.scope }));
});
check('operator opt-in enables only explicit user recall', () => {
const result = mcp('hermes', [['memory_search', { query: 'orbit', scopes: ['user'] }],
['memory_search', { query: 'orbit' }]], 'alpha', true);
assert.deepEqual(payload(result[0]).results.map(item => item.memory.id), [user.id]);
assert.ok(!payload(result[1]).results.some(item => item.memory.id === user.id));
});
check('separate project root excludes alpha records', () => {
const read = mcp('hermes', [['memory_search', { query: 'orbit' }], ['memory_read', { id: shared.id }]], 'beta');
assert.deepEqual(payload(read[0]).results.map(item => item.memory.id), [other.id]);
assert.equal(read[1].result.isError, true);
});
check('CLI direct read is operator access, not target authorization', () => {
assert.equal(cli(['read', targeted.id]).memory.id, targeted.id);
});
check('missing configured identity prevents MCP startup', () => {
const result = run('scripts/memory-mcp.mjs', [], '', envFor('alpha', null));
assert.equal(result.status, 1);
assert.match(result.stderr, /ECC_MEMORY_HARNESS/);
});
check('recalled evidence rejects tamper, unavailable source and foreign context', () => {
const read = payload(mcp('codex', [['memory_read', { id: shared.id }]])[0]).memory;
const altered = JSON.stringify({ ...JSON.parse(read.body), text: 'Synthetic altered evidence.' });
assert.throws(() => verifyEvidence(altered, sources, evidenceContext), { code: 'SOURCE_MISMATCH' });
assert.throws(() => verifyEvidence(read.body, new Map(), evidenceContext), { code: 'SOURCE_UNAVAILABLE' });
assert.throws(() => verifyEvidence(read.body, sources, { ...evidenceContext, workspace: 'beta' }),
{ code: 'CONTEXT_MISMATCH' });
assert.throws(() => verifyEvidence(read.body, sources, { ...evidenceContext, scope: 'user' }),
{ code: 'CONTEXT_MISMATCH' });
});
check('stored altered content and digest fail evidence verification after MCP recall', () => {
for (const change of [{ text: 'Synthetic altered content.' }, { sha256: '0'.repeat(64) }]) {
const altered = JSON.stringify({ ...JSON.parse(body), ...change });
const saved = save('evidence rejection fixture', 'project', 'all', 'alpha', altered);
const read = payload(mcp('hermes', [['memory_read', { id: saved.id }]])[0]).memory;
assert.equal(read.id, saved.id);
assert.equal(read.body, altered);
assert.equal(read.trust, 'unreviewed');
assert.throws(() => verifyEvidence(read.body, sources, { workspace: 'alpha', scope: read.scope }),
{ code: 'SOURCE_MISMATCH' });
}
});
check('synthetic private-key marker rejected without changing recalled dataset', () => {
// Deliberately incomplete synthetic marker; never a real key or private input.
const marker = '-----BEGIN PRIVATE KEY-----\nSynthetic non-key fixture.';
const beforePrivacy = cli(['search', 'orbit', '--target-harness', 'codex']).results;
const cliDenied = run('scripts/memory.js', ['save', '--title', 'orbit rejected fixture', '--stdin', '--json'],
marker, envFor());
assert.equal(cliDenied.status, 1, 'Synthetic sensitive write must be rejected');
assert.equal(cliDenied.error, undefined, 'CLI rejection must not be a subprocess failure');
assert.match(cliDenied.stderr, /suspected secret/i);
const mcpDenied = mcp('codex', [['memory_save', { title: 'orbit rejected fixture', body: marker }]])[0];
assert.equal(mcpDenied.result.isError, true, 'Synthetic sensitive write must be a tool rejection');
const rejection = JSON.parse(mcpDenied.result.content.find(item => item.type === 'text').text);
assert.equal(rejection.error.code, 'MEMORY_WRITE_REJECTED');
assert.equal(rejection.error.message, 'Memory operation rejected a suspected secret.');
assert.deepEqual(cli(['search', 'orbit', '--target-harness', 'codex']).results, beforePrivacy);
assert.deepEqual(payload(mcp('codex', [['memory_search', { query: 'orbit' }]])[0]).results, beforePrivacy);
});
check('source files and HEAD unchanged after execution', () => {
assert.deepEqual(snapshot(), before);
assert.equal(sourceHead(), headBefore);
});
process.stdout.write(JSON.stringify({ schemaVersion: 'ecc.memory.conformance.receipt.v1',
status: 'passed', startedAt, completedAt: new Date().toISOString(), nodeVersion: process.version,
source: { head: headBefore, files: before,
executionMode: 'local source files with existing dependencies; no fetch performed',
identityBoundary: 'File digests identify executed source; HEAD alone does not establish a clean tree.' },
exampleSha256: sha256(fs.readFileSync(__filename)), checks,
evidenceBoundary: 'Synthetic real CLI/stdio execution. No live harness, Graphiti, OAuth, replication or deployment verification.',
}, null, 2) + '\n');
} catch (error) {
// Never print raw process output or assertion values into the receipt.
process.stderr.write(JSON.stringify({ status: 'failed', passedChecks: checks.map(item => item.name),
errorType: error.name, message: 'Conformance failed after the listed checks; inspect the next synthetic operation.' }) + '\n');
process.exitCode = 1;
} finally {
fs.rmSync(root, { recursive: true, force: true });
}