Files
ECC/tests/lib/agent-proximity-shadow-trail.test.js
T

234 lines
12 KiB
JavaScript

'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const {
createShadowTrail, appendAcknowledgement, appendScopeRevision,
appendReobservation, verifyShadowTrail, summarizeShadowTrail
} = require('../../scripts/lib/agent-proximity/shadow-trail');
const at = '2026-09-15T12:00:00.000Z';
const later = '2026-09-15T12:01:00.000Z';
const latest = '2026-09-15T12:02:00.000Z';
const baseRevision = 'a'.repeat(40);
function observation(agentId, overrides = {}) {
return {
agentId, taskId: `task-${agentId}`, repoId: 'ecc', baseRevision,
scopeRevision: 1, footprint: 'complete', readPaths: [],
writePaths: ['src/shared.js'], observedAt: '2026-09-15T11:59:00.000Z',
expiresAt: '2026-09-15T12:05:00.000Z', ...overrides
};
}
function start(a = observation('a'), b = observation('b'), options = {}) {
return createShadowTrail({ trailId: 'shadow-1', observations: [a, b], at, ...options });
}
function bothAcknowledge(trail) {
const first = appendAcknowledgement(trail, { id: 'ack-a', agentId: 'a', scopeRevision: 1, at: later });
return appendAcknowledgement(first, { id: 'ack-b', agentId: 'b', scopeRevision: 1, at: later });
}
test('records a copy-on-append passive observation → advisory → ACK → scope revision → re-observation chain', () => {
const initial = start();
assert.equal(summarizeShadowTrail(initial).latestAdvisory.state, 'advisory');
assert.equal(summarizeShadowTrail(initial).latestAdvisory.hazard, 'write-write');
const acknowledged = bothAcknowledge(initial);
assert.deepEqual(summarizeShadowTrail(acknowledged).latestAdvisoryAcknowledgedBy, ['a', 'b']);
const revised = appendScopeRevision(acknowledged, {
id: 'scope-b2', agentId: 'b', previousScopeRevision: 1,
newScopeRevision: 2, newReadPaths: [], newWritePaths: ['docs/guide.md'],
authorizationRef: 'declared-contract-42', at: later
});
const complete = appendReobservation(revised, {
id: 'reobserve-1', at: latest,
observations: [observation('a', { writePaths: ['src/shared.js'], observedAt: later }),
observation('b', { scopeRevision: 2, writePaths: ['docs/guide.md'], observedAt: later })]
});
const summary = summarizeShadowTrail(complete);
assert.equal(summary.mode, 'shadow-read-only');
assert.equal(summary.latestAdvisory.state, 'no-advisory-in-scope');
assert.equal(summary.outcome, 'unadjudicated');
assert.equal(summary.scopeChange.authority, 'declared-only');
assert.deepEqual(summary.historicalAcknowledgedBy, ['a', 'b']);
assert.deepEqual(summary.latestAdvisoryAcknowledgedBy, []);
assert.equal(verifyShadowTrail(JSON.parse(JSON.stringify(complete))), true);
assert.equal(initial.receipts.length, 2);
assert.equal(acknowledged.receipts.length, 4);
assert.equal(complete.receipts.length, 7);
for (let i = 1; i < complete.receipts.length; i += 1) {
assert.equal(complete.receipts[i].previousHash, complete.receipts[i - 1].hash);
}
assert.ok(!JSON.stringify(complete).includes('CANARY_SECRET'));
});
test('read/read is quiet, write/read is a direct hazard, and dependency proximity reuses shipped scorer', () => {
const quiet = start(observation('a', { readPaths: ['src/shared.js'], writePaths: [] }),
observation('b', { readPaths: ['src/shared.js'], writePaths: [] }));
assert.equal(summarizeShadowTrail(quiet).latestAdvisory.state, 'no-advisory-in-scope');
const hazard = start(observation('a', { readPaths: ['src/shared.js'], writePaths: [] }),
observation('b', { readPaths: [], writePaths: ['src/shared.js'] }));
assert.equal(summarizeShadowTrail(hazard).latestAdvisory.hazard, 'write-read');
const graph = { adjacency: { 'apps/web/page.js': ['packages/core/util.js'], 'packages/core/util.js': [] } };
const coupled = start(observation('a', { writePaths: ['apps/web/page.js'] }),
observation('b', { writePaths: ['packages/core/util.js'] }), { graph });
assert.equal(summarizeShadowTrail(coupled).latestAdvisory.state, 'advisory');
assert.equal(summarizeShadowTrail(coupled).latestAdvisory.hazard, null);
});
test('known overlap cannot be clear, while incomplete, stale and incomparable critical data remain unknown', () => {
const partialHazard = start(observation('a', { footprint: 'incomplete' }), observation('b'));
assert.equal(summarizeShadowTrail(partialHazard).latestAdvisory.state, 'unknown');
assert.equal(summarizeShadowTrail(partialHazard).latestAdvisory.hazard, 'write-write');
const partialQuiet = start(observation('a', { footprint: 'incomplete', writePaths: [] }),
observation('b', { writePaths: ['docs/guide.md'] }));
assert.equal(summarizeShadowTrail(partialQuiet).latestAdvisory.state, 'unknown');
const stale = start(observation('a', { expiresAt: '2026-09-15T11:59:30.000Z' }), observation('b'));
assert.equal(summarizeShadowTrail(stale).latestAdvisory.state, 'unknown');
const foreignBase = start(observation('a'), observation('b', { baseRevision: 'b'.repeat(40) }));
assert.equal(summarizeShadowTrail(foreignBase).latestAdvisory.state, 'unknown');
});
test('stale or crossed ACK, unacknowledged scope change and mismatched re-observation fail closed', () => {
const initial = start();
assert.throws(() => appendAcknowledgement(initial, { id: 'bad-ack', agentId: 'a', scopeRevision: 2, at: later }), /Invalid shadow/);
assert.throws(() => appendAcknowledgement(initial, { id: 'bad-ack', agentId: 'c', scopeRevision: 1, at: later }), /Invalid shadow/);
assert.throws(() => appendScopeRevision(initial, { id: 'premature', agentId: 'b', previousScopeRevision: 1,
newScopeRevision: 2, newReadPaths: [], newWritePaths: ['docs/guide.md'],
authorizationRef: 'evidence-1', at: later }), /Invalid shadow/);
const ack = bothAcknowledge(initial);
assert.throws(() => appendAcknowledgement(ack, { id: 'third-ack', agentId: 'a', scopeRevision: 1, at: later }), /Invalid shadow/);
const revised = appendScopeRevision(ack, { id: 'scope-b2', agentId: 'b', previousScopeRevision: 1,
newScopeRevision: 2, newReadPaths: [], newWritePaths: ['docs/guide.md'],
authorizationRef: 'evidence-1', at: later });
assert.throws(() => appendReobservation(revised, { id: 'wrong-scope', at: latest,
observations: [observation('a'), observation('b')] }), /Invalid shadow/);
assert.throws(() => appendReobservation(revised, { id: 'foreign-task', at: latest,
observations: [observation('a'), observation('b', { scopeRevision: 2, taskId: 'other-task' })] }), /Invalid shadow/);
assert.throws(() => appendReobservation(revised, { id: 'different-declaration', at: latest,
observations: [observation('a'), observation('b', { scopeRevision: 2,
writePaths: ['src/not-declared.js'], observedAt: later })] }), /Invalid shadow/);
assert.throws(() => appendReobservation(revised, { id: 'changed-other-agent', at: latest,
observations: [observation('a', { writePaths: ['src/changed-without-revision.js'], observedAt: later }),
observation('b', { scopeRevision: 2, writePaths: ['docs/guide.md'], observedAt: later })] }), /Invalid shadow/);
});
test('unknown critical footprints cannot be used as an acknowledged clear-scoped resolution', () => {
const unknown = start(observation('a', { footprint: 'incomplete', writePaths: [] }), observation('b'));
assert.throws(() => appendAcknowledgement(unknown, { id: 'ack-unknown', agentId: 'a',
scopeRevision: 1, at: later }), /Invalid shadow/);
});
test('receipt hashes bind the trail identity and reject cross-trail relabeling', () => {
const relabeled = JSON.parse(JSON.stringify(start()));
relabeled.trailId = 'different-shadow-trail';
assert.throws(() => verifyShadowTrail(relabeled), /Invalid shadow/);
});
test('a persisted receipt with reordered path bytes is noncanonical and rejected', () => {
const trail = start(observation('a', { writePaths: ['src/a.js', 'src/b.js'] }),
observation('b', { writePaths: ['src/b.js'] }));
const reordered = JSON.parse(JSON.stringify(trail));
reordered.receipts[0].payload.observations[0].writePaths.reverse();
assert.throws(() => verifyShadowTrail(reordered), /Invalid shadow/);
});
test('dependency graph insertion order canonicalizes to the same receipt content', () => {
const a = start(observation('a'), observation('b'), { graph: { adjacency: {
'src/z.js': ['src/a.js'], 'src/a.js': []
} } });
const b = start(observation('a'), observation('b'), { graph: { adjacency: {
'src/a.js': [], 'src/z.js': ['src/a.js']
} } });
assert.equal(a.receipts[0].hash, b.receipts[0].hash);
});
test('append results keep independent copies of earlier receipt payloads', () => {
const initial = start();
const appended = appendAcknowledgement(initial, {
id: 'copy-ack', agentId: 'a', scopeRevision: 1, at: later
});
initial.receipts[0].payload.observations[0].writePaths[0] = 'src/changed.js';
assert.throws(() => verifyShadowTrail(initial), /Invalid shadow/);
assert.equal(verifyShadowTrail(appended), true);
});
test('verification rejects nested serialization hooks, sparse and accessor arrays before execution', () => {
const original = start();
let callbackRuns = 0;
const hooked = JSON.parse(JSON.stringify(original));
hooked.receipts[0].payload.observations[0].writePaths.toJSON = () => {
callbackRuns += 1;
return ['src/shared.js'];
};
assert.throws(() => verifyShadowTrail(hooked), /Invalid shadow/);
assert.equal(callbackRuns, 0);
const accessor = JSON.parse(JSON.stringify(original));
Object.defineProperty(accessor.receipts[0].payload.observations, '0', {
enumerable: true, get() { callbackRuns += 1; return observation('a'); }
});
assert.throws(() => verifyShadowTrail(accessor), /Invalid shadow/);
assert.equal(callbackRuns, 0);
const sparse = JSON.parse(JSON.stringify(original));
delete sparse.receipts[0].payload.observations[0].writePaths[0];
assert.throws(() => verifyShadowTrail(sparse), /Invalid shadow/);
});
test('hash fields reject coercible objects without invoking their conversion hooks', () => {
const trail = JSON.parse(JSON.stringify(start()));
let callbackRuns = 0;
trail.receipts[0].hash = { toString() { callbackRuns += 1; return '0'.repeat(64); } };
assert.throws(() => verifyShadowTrail(trail), /Invalid shadow/);
assert.equal(callbackRuns, 0);
});
test('receipt verification never invokes inherited JSON serialization hooks', () => {
const persisted = JSON.parse(JSON.stringify(start()));
let callbackRuns = 0;
Object.defineProperty(Array.prototype, 'toJSON', { configurable: true,
value() { callbackRuns += 1; return []; } });
try {
assert.equal(verifyShadowTrail(persisted), true);
assert.equal(callbackRuns, 0);
} finally {
delete Array.prototype.toJSON;
}
});
test('exotic verification failures are generic and do not echo private text', () => {
const trail = start();
const receipts = new Proxy(trail.receipts, {
getPrototypeOf() { throw new Error('CANARY_SECRET'); }
});
assert.throws(() => verifyShadowTrail({ ...trail, receipts }), error =>
/Invalid shadow/.test(error.message) && !error.message.includes('CANARY_SECRET'));
});
test('scope revision requires both acknowledged observations to remain current', () => {
const initial = start(observation('a'), observation('b', {
expiresAt: '2026-09-15T12:01:30.000Z'
}));
const acknowledged = bothAcknowledge(initial);
assert.throws(() => appendScopeRevision(acknowledged, {
id: 'scope-after-peer-expiry', agentId: 'a', previousScopeRevision: 1,
newScopeRevision: 2, newReadPaths: [], newWritePaths: ['docs/guide.md'],
authorizationRef: 'declared-contract-42', at: latest
}), /Invalid shadow/);
});
test('tampering, duplicate IDs, unknown fields and unbounded input cannot be accepted or echoed', () => {
const initial = start();
const tampered = JSON.parse(JSON.stringify(initial));
tampered.receipts[0].payload.observations[0].writePaths = ['CANARY_SECRET'];
assert.throws(() => verifyShadowTrail(tampered), /Invalid shadow/);
const poisoned = observation('a', { summary: 'CANARY_SECRET' });
assert.throws(() => start(poisoned, observation('b')), error =>
/Invalid shadow/.test(error.message) && !error.message.includes('CANARY_SECRET'));
assert.throws(() => start(observation('a', { writePaths: ['../escape'] }), observation('b')), /Invalid shadow/);
assert.throws(() => start(observation('a', { writePaths: Array(33).fill('src/a.js') }), observation('b')), /Invalid shadow/);
assert.throws(() => start(observation('a'), observation('b'), { graph: { adjacency: { '__proto__': ['src/a.js'] } } }), /Invalid shadow/);
assert.throws(() => appendAcknowledgement(initial, { id: initial.receipts[0].id, agentId: 'a',
scopeRevision: 1, at: later }), /Invalid shadow/);
});