mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-26 18:11:24 +08:00
feat(layer4): live messages-table wiring for proximity triggers
Finishes the steer/transmit loop — advisories now reach the agents' sessions. - message-sink.js: createEccMessageSink() delivers via the canonical writer 'ecc-tui messages send' (maps steer/hold -> conflict kind, transmit -> query), resolving the binary from override/env/built target/PATH. Injectable runner; best-effort (a missing binary/failed send is counted skipped, never blocks). - proximity.js: createProximityDispatcher() adds per-trigger cooldown so a persistent collision fires once then stays quiet (agents get steered, not spammed); runProximityTick() builds the snapshot and dispatches. - scripts/proximity-tick.js: thin CLI — one-shot, --dry-run, --watch <sec>. Messages are internal ECC agent-to-agent coordination, not any external channel. - 14 new tests (sink argv/kind mapping, cooldown dedup, tick dispatch/dry-run, CLI parse). Full suite 2891/2891; lint green.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
'use strict';
|
||||
/**
|
||||
* Tests for the proximity message sink (ecc-tui messages send) and the
|
||||
* deduping dispatcher.
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const { KIND_BY_TYPE, buildSendArgs, createEccMessageSink, resolveEccBin } = require('../../scripts/lib/control-pane/message-sink');
|
||||
const { createProximityDispatcher } = require('../../scripts/lib/control-pane/proximity');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` PASS ${name}`);
|
||||
passed += 1;
|
||||
} catch (e) {
|
||||
console.log(` FAIL ${name}`);
|
||||
console.log(` ${e.message}`);
|
||||
failed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n=== Testing proximity message sink ===\n');
|
||||
|
||||
test('buildSendArgs: maps trigger type to message kind and shapes the CLI argv', () => {
|
||||
const args = buildSendArgs({ fromSession: 'lead', toSession: 'worker', content: 'steer away', msgType: 'proximity_steer' });
|
||||
assert.deepStrictEqual(args, ['messages', 'send', '--from', 'lead', '--to', 'worker', '--kind', 'conflict', '--text', 'steer away']);
|
||||
assert.strictEqual(KIND_BY_TYPE.proximity_transmit, 'query');
|
||||
});
|
||||
|
||||
test('createEccMessageSink: delivers via the injected runner with the resolved binary', () => {
|
||||
const calls = [];
|
||||
const send = createEccMessageSink({
|
||||
binPath: '/fake/ecc-tui',
|
||||
runCommand: (bin, args) => calls.push({ bin, args })
|
||||
});
|
||||
send({ fromSession: 'a', toSession: 'b', content: 'hello', msgType: 'proximity_transmit' });
|
||||
assert.strictEqual(calls.length, 1);
|
||||
assert.strictEqual(calls[0].bin, '/fake/ecc-tui');
|
||||
assert.deepStrictEqual(calls[0].args.slice(0, 2), ['messages', 'send']);
|
||||
assert.ok(calls[0].args.includes('query'), 'transmit maps to query kind');
|
||||
});
|
||||
|
||||
test('createEccMessageSink: a failing command propagates (dispatcher will count it skipped)', () => {
|
||||
const send = createEccMessageSink({
|
||||
binPath: 'ecc-tui',
|
||||
runCommand: () => {
|
||||
throw new Error('ENOENT');
|
||||
}
|
||||
});
|
||||
assert.throws(() => send({ fromSession: 'a', toSession: 'b', content: 'x', msgType: 'proximity_steer' }));
|
||||
});
|
||||
|
||||
test('resolveEccBin: honors explicit override', () => {
|
||||
assert.strictEqual(resolveEccBin({ binPath: '/x/ecc-tui' }), '/x/ecc-tui');
|
||||
});
|
||||
|
||||
test('dispatcher: fires once then suppresses the same trigger within cooldown', () => {
|
||||
let clock = 1000;
|
||||
const sent = [];
|
||||
const dispatcher = createProximityDispatcher({
|
||||
sendMessage: m => sent.push(m),
|
||||
cooldownMs: 1000,
|
||||
now: () => clock
|
||||
});
|
||||
const triggers = [{ to: 'worker', from: 'lead', type: 'proximity_steer', content: 'steer' }];
|
||||
|
||||
const r1 = dispatcher.dispatch(triggers);
|
||||
assert.strictEqual(r1.dispatched, 1);
|
||||
|
||||
clock = 1500; // within cooldown
|
||||
const r2 = dispatcher.dispatch(triggers);
|
||||
assert.strictEqual(r2.dispatched, 0);
|
||||
assert.strictEqual(r2.suppressed, 1);
|
||||
|
||||
clock = 2600; // past cooldown
|
||||
const r3 = dispatcher.dispatch(triggers);
|
||||
assert.strictEqual(r3.dispatched, 1);
|
||||
|
||||
assert.strictEqual(sent.length, 2, 'sent twice total, the middle one suppressed');
|
||||
});
|
||||
|
||||
test('dispatcher: distinct triggers are not cross-suppressed', () => {
|
||||
const sent = [];
|
||||
const dispatcher = createProximityDispatcher({ sendMessage: m => sent.push(m), now: () => 0 });
|
||||
const r = dispatcher.dispatch([
|
||||
{ to: 'worker', from: 'lead', type: 'proximity_steer', content: 'a' },
|
||||
{ to: 'lead', from: 'worker', type: 'proximity_hold', content: 'b' }
|
||||
]);
|
||||
assert.strictEqual(r.dispatched, 2);
|
||||
});
|
||||
|
||||
test('dispatcher: no sink ⇒ skipped, never throws', () => {
|
||||
const dispatcher = createProximityDispatcher({});
|
||||
const r = dispatcher.dispatch([{ to: 'a', from: 'b', type: 'x', content: 'c' }]);
|
||||
assert.strictEqual(r.skipped, 1);
|
||||
assert.strictEqual(r.dispatched, 0);
|
||||
});
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
if (failed > 0) process.exit(1);
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const { buildProximitySnapshot, sessionsToAgents, parseDiffRanges, dispatchProximityTriggers } = require('../../scripts/lib/control-pane/proximity');
|
||||
const { buildProximitySnapshot, sessionsToAgents, parseDiffRanges, dispatchProximityTriggers, createProximityDispatcher, runProximityTick } = require('../../scripts/lib/control-pane/proximity');
|
||||
const { parseArgs: parseTickArgs } = require('../../scripts/proximity-tick');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
@@ -146,5 +147,41 @@ test('dispatchProximityTriggers: no sink ⇒ nothing thrown, all skipped', () =>
|
||||
assert.strictEqual(r.skipped, 1);
|
||||
});
|
||||
|
||||
test('runProximityTick: dispatches the snapshot triggers via the dispatcher', async () => {
|
||||
const snapshot = {
|
||||
proximity: {
|
||||
counts: { agents: 2, advisories: 1, resolutions: 1 },
|
||||
advisories: [{ a: 'lead', b: 'worker', level: 'resolution', risk: 0.9, steer: 'worker', hold: 'lead' }],
|
||||
triggers: [
|
||||
{ to: 'worker', from: 'lead', type: 'proximity_steer', content: 'steer' },
|
||||
{ to: 'lead', from: 'worker', type: 'proximity_hold', content: 'hold' }
|
||||
]
|
||||
}
|
||||
};
|
||||
const sent = [];
|
||||
const dispatcher = createProximityDispatcher({ sendMessage: m => sent.push(m), now: () => 0 });
|
||||
const tick = await runProximityTick({ buildSnapshot: async () => snapshot, dispatcher });
|
||||
assert.strictEqual(tick.result.dispatched, 2);
|
||||
assert.strictEqual(sent.length, 2);
|
||||
});
|
||||
|
||||
test('runProximityTick: dry-run sends nothing', async () => {
|
||||
const snapshot = { proximity: { counts: {}, advisories: [], triggers: [{ to: 'a', from: 'b', type: 'x', content: 'c' }] } };
|
||||
const sent = [];
|
||||
const dispatcher = createProximityDispatcher({ sendMessage: m => sent.push(m) });
|
||||
const tick = await runProximityTick({ buildSnapshot: async () => snapshot, dispatcher, dryRun: true });
|
||||
assert.strictEqual(tick.result.dispatched, 0);
|
||||
assert.strictEqual(tick.result.dryRun, true);
|
||||
assert.strictEqual(sent.length, 0);
|
||||
});
|
||||
|
||||
test('proximity-tick parseArgs: parses flags', () => {
|
||||
const a = parseTickArgs(['node', 'proximity-tick.js', '--watch', '30', '--dry-run', '--db', '/x']);
|
||||
assert.strictEqual(a.watchSec, 30);
|
||||
assert.strictEqual(a.dryRun, true);
|
||||
assert.strictEqual(a.dbPath, '/x');
|
||||
assert.throws(() => parseTickArgs(['node', 'p', '--watch', 'nope']), /positive seconds/);
|
||||
});
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
if (failed > 0) process.exit(1);
|
||||
|
||||
Reference in New Issue
Block a user