@memnexus-ai/mx-agent-cli 0.1.166 → 0.1.168
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/claude-sync.test.js +15 -0
- package/dist/__tests__/claude-sync.test.js.map +1 -1
- package/dist/__tests__/config-command.test.d.ts +9 -0
- package/dist/__tests__/config-command.test.d.ts.map +1 -0
- package/dist/__tests__/config-command.test.js +55 -0
- package/dist/__tests__/config-command.test.js.map +1 -0
- package/dist/__tests__/config-generation.test.d.ts +7 -0
- package/dist/__tests__/config-generation.test.d.ts.map +1 -0
- package/dist/__tests__/config-generation.test.js +141 -0
- package/dist/__tests__/config-generation.test.js.map +1 -0
- package/dist/__tests__/config-sync.test.d.ts +10 -0
- package/dist/__tests__/config-sync.test.d.ts.map +1 -0
- package/dist/__tests__/config-sync.test.js +180 -0
- package/dist/__tests__/config-sync.test.js.map +1 -0
- package/dist/__tests__/coordination-client.test.d.ts +9 -0
- package/dist/__tests__/coordination-client.test.d.ts.map +1 -0
- package/dist/__tests__/coordination-client.test.js +72 -0
- package/dist/__tests__/coordination-client.test.js.map +1 -0
- package/dist/__tests__/coordination-command.test.d.ts +9 -0
- package/dist/__tests__/coordination-command.test.d.ts.map +1 -0
- package/dist/__tests__/coordination-command.test.js +108 -0
- package/dist/__tests__/coordination-command.test.js.map +1 -0
- package/dist/__tests__/coordination-fallback.test.d.ts +9 -0
- package/dist/__tests__/coordination-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/coordination-fallback.test.js +158 -0
- package/dist/__tests__/coordination-fallback.test.js.map +1 -0
- package/dist/__tests__/project-config.test.js +1 -0
- package/dist/__tests__/project-config.test.js.map +1 -1
- package/dist/agent-config/.mx-config-generation.json +4 -0
- package/dist/agent-config/CLAUDE.md.template +140 -0
- package/dist/agent-config/OWNERS.md +51 -0
- package/dist/agent-config/agents/bar-raiser.md +205 -0
- package/dist/agent-config/agents/dogfood-auditor.md +243 -0
- package/dist/agent-config/agents/git-expert.md +380 -0
- package/dist/agent-config/agents/implementation-engineer.md +387 -0
- package/dist/agent-config/agents/memory-creator.md +330 -0
- package/dist/agent-config/agents/prd-writer.md +478 -0
- package/dist/agent-config/agents/prfaq-writer.md +344 -0
- package/dist/agent-config/agents/prior-art-researcher.md +264 -0
- package/dist/agent-config/agents/qa-tester.md +406 -0
- package/dist/agent-config/agents/red-team.md +330 -0
- package/dist/agent-config/agents/security-reviewer.md +300 -0
- package/dist/agent-config/agents/status-reporter.md +297 -0
- package/dist/agent-config/claude-md/CLAUDE.core.md +105 -0
- package/dist/agent-config/claude-md/CLAUDE.overlay.memnexus.md +68 -0
- package/dist/agent-config/claude-md/compose.mjs +179 -0
- package/dist/agent-config/claude-md/vars.memnexus.json +4 -0
- package/dist/agent-config/hooks/auto-capture-precompact.sh +176 -0
- package/dist/agent-config/hooks/auto-capture-session-end.sh +178 -0
- package/dist/agent-config/hooks/auto-checkpoint.sh +102 -0
- package/dist/agent-config/hooks/ci-validate-hook.sh +499 -0
- package/dist/agent-config/hooks/ci-validate-routes.conf +16 -0
- package/dist/agent-config/hooks/delegation-audit.sh +61 -0
- package/dist/agent-config/hooks/deploy-verification-reminder.sh +179 -0
- package/dist/agent-config/hooks/git-mutation-guard.sh +219 -0
- package/dist/agent-config/hooks/iteration-log-check.sh +212 -0
- package/dist/agent-config/hooks/memory-checkpoint-reminder.sh +103 -0
- package/dist/agent-config/hooks/mx-guard-hook.sh +77 -0
- package/dist/agent-config/hooks/named-memory-write-guard.sh +237 -0
- package/dist/agent-config/hooks/reload-checkpoint.sh +68 -0
- package/dist/agent-config/hooks/set-terminal-appearance.sh +34 -0
- package/dist/agent-config/hooks/workflow-dispatch-guard.sh +184 -0
- package/dist/agent-config/hooks/worktree-guard.sh +258 -0
- package/dist/agent-config/rules/eval-harness.md +33 -0
- package/dist/agent-config/rules/frontend.md +17 -0
- package/dist/agent-config/rules/mcp-protocol.md +15 -0
- package/dist/agent-config/rules/retrieval.md +17 -0
- package/dist/agent-config/settings.json +196 -0
- package/dist/agent-config/shared/eval-tiered-rigor.md +15 -0
- package/dist/agent-config/shared/team-operating-rules.md +37 -0
- package/dist/agent-config/skills/eval-claim-impact/SKILL.md +20 -0
- package/dist/agent-config/skills/eval-harness-review/SKILL.md +21 -0
- package/dist/agent-config/skills/mx-checkpoint/SKILL.md +111 -0
- package/dist/agent-config/skills/mx-fix-pr/SKILL.md +90 -0
- package/dist/agent-config/skills/mx-gap-assessment/SKILL.md +90 -0
- package/dist/agent-config/skills/mx-iteration-close/SKILL.md +81 -0
- package/dist/agent-config/skills/mx-post-merge-sync/SKILL.md +83 -0
- package/dist/agent-config/skills/mx-pre-pr-check/SKILL.md +85 -0
- package/dist/agent-config/skills/mx-save/SKILL.md +102 -0
- package/dist/agent-config/skills/mx-session-start/SKILL.md +85 -0
- package/dist/agent-config/skills/team-gate-checklist/SKILL.md +19 -0
- package/dist/commands/config-sync.d.ts +39 -0
- package/dist/commands/config-sync.d.ts.map +1 -0
- package/dist/commands/config-sync.js +212 -0
- package/dist/commands/config-sync.js.map +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +5 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/coordination.d.ts +49 -0
- package/dist/commands/coordination.d.ts.map +1 -0
- package/dist/commands/coordination.js +128 -0
- package/dist/commands/coordination.js.map +1 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +3 -0
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -4
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +6 -0
- package/dist/commands/start.js.map +1 -1
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/bundled-config.d.ts +44 -0
- package/dist/lib/bundled-config.d.ts.map +1 -0
- package/dist/lib/bundled-config.js +164 -0
- package/dist/lib/bundled-config.js.map +1 -0
- package/dist/lib/claude.d.ts.map +1 -1
- package/dist/lib/claude.js +9 -3
- package/dist/lib/claude.js.map +1 -1
- package/dist/lib/config-generation.d.ts +36 -0
- package/dist/lib/config-generation.d.ts.map +1 -0
- package/dist/lib/config-generation.js +73 -0
- package/dist/lib/config-generation.js.map +1 -0
- package/dist/lib/coordination-client.d.ts +74 -0
- package/dist/lib/coordination-client.d.ts.map +1 -0
- package/dist/lib/coordination-client.js +83 -0
- package/dist/lib/coordination-client.js.map +1 -0
- package/dist/lib/coordination-fallback.d.ts +75 -0
- package/dist/lib/coordination-fallback.d.ts.map +1 -0
- package/dist/lib/coordination-fallback.js +224 -0
- package/dist/lib/coordination-fallback.js.map +1 -0
- package/dist/lib/project-config.d.ts +7 -0
- package/dist/lib/project-config.d.ts.map +1 -1
- package/dist/lib/project-config.js +4 -0
- package/dist/lib/project-config.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination-client -- HTTP client for the coordination service (FR-7).
|
|
3
|
+
*
|
|
4
|
+
* Uses a stubbed global fetch to assert the client builds correct requests and
|
|
5
|
+
* maps transport failures to ServiceUnreachableError and non-2xx to
|
|
6
|
+
* ServiceHttpError.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
9
|
+
import { HttpCoordinationClient, ServiceUnreachableError, resolveClientConfig, } from '../lib/coordination-client.js';
|
|
10
|
+
const cfg = { url: 'https://coord.example:8443', token: 'tok-abc' };
|
|
11
|
+
function stubFetch(impl) {
|
|
12
|
+
vi.stubGlobal('fetch', vi.fn((url, init) => Promise.resolve(impl(url, init))));
|
|
13
|
+
}
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vi.unstubAllGlobals();
|
|
16
|
+
delete process.env.MX_COORD_SERVICE_URL;
|
|
17
|
+
delete process.env.MX_COORD_SERVICE_TOKEN;
|
|
18
|
+
});
|
|
19
|
+
describe('coordination-client', () => {
|
|
20
|
+
it('enqueue posts to /enqueue with bearer auth and JSON body', async () => {
|
|
21
|
+
let seenUrl = '';
|
|
22
|
+
let seenInit = {};
|
|
23
|
+
stubFetch((url, init) => {
|
|
24
|
+
seenUrl = url;
|
|
25
|
+
seenInit = init;
|
|
26
|
+
return new Response(JSON.stringify({ messageId: 'm1', duplicate: false }), { status: 200 });
|
|
27
|
+
});
|
|
28
|
+
const client = new HttpCoordinationClient(cfg);
|
|
29
|
+
const res = await client.enqueue({ to: 'platform', priority: 'p1', body: 'hi' });
|
|
30
|
+
expect(res.messageId).toBe('m1');
|
|
31
|
+
expect(seenUrl).toBe('https://coord.example:8443/enqueue');
|
|
32
|
+
expect(seenInit.headers.authorization).toBe('Bearer tok-abc');
|
|
33
|
+
expect(JSON.parse(seenInit.body)).toMatchObject({ to: 'platform', priority: 'p1', body: 'hi' });
|
|
34
|
+
});
|
|
35
|
+
it('drain returns messages + batchId', async () => {
|
|
36
|
+
stubFetch(() => new Response(JSON.stringify({ messages: [{ id: 'm1', from: 'mcp', to: 'platform', priority: 'p1', body: 'x', timestamp: 't' }], remaining: 0, batchId: 'batch_1' }), { status: 200 }));
|
|
37
|
+
const client = new HttpCoordinationClient(cfg);
|
|
38
|
+
const res = await client.drain({ limit: 5 });
|
|
39
|
+
expect(res.messages).toHaveLength(1);
|
|
40
|
+
expect(res.batchId).toBe('batch_1');
|
|
41
|
+
});
|
|
42
|
+
it('confirmDrain posts the batchId to /drain/confirm', async () => {
|
|
43
|
+
let seenUrl = '';
|
|
44
|
+
let seenBody;
|
|
45
|
+
stubFetch((url, init) => {
|
|
46
|
+
seenUrl = url;
|
|
47
|
+
seenBody = JSON.parse(init.body);
|
|
48
|
+
return new Response(JSON.stringify({ confirmed: true }), { status: 200 });
|
|
49
|
+
});
|
|
50
|
+
const client = new HttpCoordinationClient(cfg);
|
|
51
|
+
await client.confirmDrain('batch_9');
|
|
52
|
+
expect(seenUrl).toBe('https://coord.example:8443/drain/confirm');
|
|
53
|
+
expect(seenBody).toEqual({ batchId: 'batch_9' });
|
|
54
|
+
});
|
|
55
|
+
it('maps a network failure to ServiceUnreachableError', async () => {
|
|
56
|
+
vi.stubGlobal('fetch', vi.fn(() => Promise.reject(new Error('ECONNREFUSED'))));
|
|
57
|
+
const client = new HttpCoordinationClient(cfg);
|
|
58
|
+
await expect(client.enqueue({ to: 'platform', priority: 'p2', body: 'x' })).rejects.toBeInstanceOf(ServiceUnreachableError);
|
|
59
|
+
});
|
|
60
|
+
it('maps a non-2xx response to ServiceHttpError with the service error message', async () => {
|
|
61
|
+
stubFetch(() => new Response(JSON.stringify({ error: 'rate limit exceeded' }), { status: 429 }));
|
|
62
|
+
const client = new HttpCoordinationClient(cfg);
|
|
63
|
+
await expect(client.enqueue({ to: 'platform', priority: 'p0', body: 'x' })).rejects.toMatchObject({ name: 'ServiceHttpError', status: 429 });
|
|
64
|
+
});
|
|
65
|
+
it('resolveClientConfig reads URL + token from the environment and strips a trailing slash', () => {
|
|
66
|
+
process.env.MX_COORD_SERVICE_URL = 'https://coord.example:8443/';
|
|
67
|
+
process.env.MX_COORD_SERVICE_TOKEN = 'env-tok';
|
|
68
|
+
const resolved = resolveClientConfig();
|
|
69
|
+
expect(resolved).toEqual({ url: 'https://coord.example:8443', token: 'env-tok' });
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=coordination-client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-client.test.js","sourceRoot":"","sources":["../../src/__tests__/coordination-client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination commands -- FR-7 escalate/drain + FR-8 degraded mode.
|
|
3
|
+
*
|
|
4
|
+
* runEscalate writes to the local fallback when the service is unreachable
|
|
5
|
+
* (non-lossy, exit 0), sends normally when reachable, and fails cleanly on a
|
|
6
|
+
* symlinked fallback path. runDrain confirms the batch only after a full render.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=coordination-command.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-command.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/coordination-command.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination commands -- FR-7 escalate/drain + FR-8 degraded mode.
|
|
3
|
+
*
|
|
4
|
+
* runEscalate writes to the local fallback when the service is unreachable
|
|
5
|
+
* (non-lossy, exit 0), sends normally when reachable, and fails cleanly on a
|
|
6
|
+
* symlinked fallback path. runDrain confirms the batch only after a full render.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
9
|
+
import { mkdtempSync, rmSync, symlinkSync, writeFileSync, readFileSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { tmpdir } from 'os';
|
|
12
|
+
import { runEscalate, runDrain } from '../commands/coordination.js';
|
|
13
|
+
import { readFallbackEntries } from '../lib/coordination-fallback.js';
|
|
14
|
+
import { ServiceUnreachableError } from '../lib/coordination-client.js';
|
|
15
|
+
let dir;
|
|
16
|
+
let logs;
|
|
17
|
+
let errs;
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
dir = mkdtempSync(join(tmpdir(), 'coord-cmd-'));
|
|
20
|
+
process.env.CLAUDE_WORKTREE_PATH = dir;
|
|
21
|
+
process.env.CLAUDE_WORKTREE_NAME = 'mcp';
|
|
22
|
+
logs = [];
|
|
23
|
+
errs = [];
|
|
24
|
+
vi.spyOn(console, 'log').mockImplementation((m) => { logs.push(String(m)); });
|
|
25
|
+
vi.spyOn(console, 'error').mockImplementation((m) => { errs.push(String(m)); });
|
|
26
|
+
});
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
vi.restoreAllMocks();
|
|
29
|
+
rmSync(dir, { recursive: true, force: true });
|
|
30
|
+
delete process.env.CLAUDE_WORKTREE_PATH;
|
|
31
|
+
delete process.env.CLAUDE_WORKTREE_NAME;
|
|
32
|
+
});
|
|
33
|
+
/** Build a client stub; override individual methods per test. */
|
|
34
|
+
function stubClient(over = {}) {
|
|
35
|
+
return {
|
|
36
|
+
async enqueue() { return { messageId: 'm1', duplicate: false }; },
|
|
37
|
+
async drain() { return { messages: [], remaining: 0, batchId: '' }; },
|
|
38
|
+
async confirmDrain() { },
|
|
39
|
+
async ack() { },
|
|
40
|
+
...over,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const unreachableClient = stubClient({
|
|
44
|
+
async enqueue() { throw new ServiceUnreachableError(new Error('down')); },
|
|
45
|
+
async drain() { throw new ServiceUnreachableError(new Error('down')); },
|
|
46
|
+
});
|
|
47
|
+
describe('runEscalate (FR-7 + FR-8)', () => {
|
|
48
|
+
it('degrades to the local fallback when the service is unreachable, non-lossy', async () => {
|
|
49
|
+
await runEscalate({ to: 'platform', priority: 'p1', body: 'help' }, { client: unreachableClient });
|
|
50
|
+
expect(logs.join('\n')).toContain('queued locally, will flush on reconnect');
|
|
51
|
+
const entries = readFallbackEntries(join(dir, '.mx-coordination-fallback.jsonl'));
|
|
52
|
+
expect(entries).toHaveLength(1);
|
|
53
|
+
expect(entries[0]).toMatchObject({ team: 'mcp', to: 'platform', priority: 'p1', body: 'help' });
|
|
54
|
+
});
|
|
55
|
+
it('sends normally when the service is reachable', async () => {
|
|
56
|
+
const sent = [];
|
|
57
|
+
const okClient = stubClient({ async enqueue(req) { sent.push(req.body); return { messageId: 'm1', duplicate: false }; } });
|
|
58
|
+
await runEscalate({ to: 'platform', priority: 'p2', body: 'live' }, { client: okClient });
|
|
59
|
+
expect(logs.join('\n')).toContain('escalated: m1');
|
|
60
|
+
expect(sent).toContain('live');
|
|
61
|
+
});
|
|
62
|
+
it('fails CLEANLY (no stack, non-zero exit) when the fallback path is a symlink (MINOR-6)', async () => {
|
|
63
|
+
const fp = join(dir, '.mx-coordination-fallback.jsonl');
|
|
64
|
+
const target = join(dir, 'evil');
|
|
65
|
+
writeFileSync(target, '');
|
|
66
|
+
symlinkSync(target, fp);
|
|
67
|
+
const exit = vi.spyOn(process, 'exit').mockImplementation((() => { throw new Error('exit'); }));
|
|
68
|
+
const errWrites = [];
|
|
69
|
+
vi.spyOn(process.stderr, 'write').mockImplementation(((c) => { errWrites.push(String(c)); return true; }));
|
|
70
|
+
await expect(runEscalate({ to: 'platform', priority: 'p1', body: 'x' }, { client: unreachableClient })).rejects.toThrow('exit');
|
|
71
|
+
expect(exit).toHaveBeenCalledWith(1);
|
|
72
|
+
expect(errWrites.join('')).toMatch(/refusing to write; escalation NOT queued/);
|
|
73
|
+
expect(readFileSync(target, 'utf8')).toBe(''); // symlink target untouched
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('runDrain (FR-7 + confirm)', () => {
|
|
77
|
+
it('prints the empty-inbox line when nothing is pending', async () => {
|
|
78
|
+
await runDrain({}, { client: stubClient() });
|
|
79
|
+
expect(logs.join('\n')).toContain('no cross-team coordination pending');
|
|
80
|
+
});
|
|
81
|
+
it('prints messages, a "more pending" hint, and confirms the batch after render (MAJOR-2)', async () => {
|
|
82
|
+
const confirmed = [];
|
|
83
|
+
const drainRes = { messages: [{ id: 'm1', from: 'retrieval', to: 'mcp', priority: 'p0', body: 'urgent', timestamp: 't' }], remaining: 4, batchId: 'b1' };
|
|
84
|
+
const client = stubClient({ async drain() { return drainRes; }, async confirmDrain(id) { confirmed.push(id); } });
|
|
85
|
+
await runDrain({ limit: '1' }, { client });
|
|
86
|
+
const out = logs.join('\n');
|
|
87
|
+
expect(out).toContain('[p0] m1 from retrieval: urgent');
|
|
88
|
+
expect(out).toContain('4 more pending');
|
|
89
|
+
expect(confirmed).toEqual(['b1']); // confirmed with the returned batchId
|
|
90
|
+
});
|
|
91
|
+
it('does NOT confirm when rendering fails mid-way (batch stays pending — safety net)', async () => {
|
|
92
|
+
const confirmed = [];
|
|
93
|
+
const drainRes = { messages: [{ id: 'm1', from: 'x', to: 'mcp', priority: 'p1', body: 'b', timestamp: 't' }], remaining: 0, batchId: 'b1' };
|
|
94
|
+
const client = stubClient({ async drain() { return drainRes; }, async confirmDrain(id) { confirmed.push(id); } });
|
|
95
|
+
// Make the render throw.
|
|
96
|
+
console.log.mockImplementation(() => { throw new Error('render failed'); });
|
|
97
|
+
await expect(runDrain({}, { client })).rejects.toThrow('render failed');
|
|
98
|
+
expect(confirmed).toEqual([]); // never confirmed
|
|
99
|
+
});
|
|
100
|
+
it('does NOT confirm when --no-confirm is set', async () => {
|
|
101
|
+
const confirmed = [];
|
|
102
|
+
const drainRes = { messages: [{ id: 'm1', from: 'x', to: 'mcp', priority: 'p2', body: 'b', timestamp: 't' }], remaining: 0, batchId: 'b1' };
|
|
103
|
+
const client = stubClient({ async drain() { return drainRes; }, async confirmDrain(id) { confirmed.push(id); } });
|
|
104
|
+
await runDrain({ noConfirm: true }, { client });
|
|
105
|
+
expect(confirmed).toEqual([]);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=coordination-command.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-command.test.js","sourceRoot":"","sources":["../../src/__tests__/coordination-command.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination-fallback -- FR-8 degraded-mode durable fallback.
|
|
3
|
+
*
|
|
4
|
+
* Covers: outage write is durable, a pre-planted symlink is rejected (O_NOFOLLOW),
|
|
5
|
+
* flush is idempotent and clears the file, an interrupted flush (service still
|
|
6
|
+
* down mid-batch) keeps remaining entries, and a stale lease is re-acquired.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=coordination-fallback.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-fallback.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/coordination-fallback.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination-fallback -- FR-8 degraded-mode durable fallback.
|
|
3
|
+
*
|
|
4
|
+
* Covers: outage write is durable, a pre-planted symlink is rejected (O_NOFOLLOW),
|
|
5
|
+
* flush is idempotent and clears the file, an interrupted flush (service still
|
|
6
|
+
* down mid-batch) keeps remaining entries, and a stale lease is re-acquired.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
9
|
+
import { mkdtempSync, rmSync, existsSync, symlinkSync, writeFileSync, readFileSync, utimesSync, statSync, chmodSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { tmpdir } from 'os';
|
|
12
|
+
import { appendFallbackEntry, readFallbackEntries, flushFallback, acquireLease, releaseLease, idempotencyKeyFor, } from '../lib/coordination-fallback.js';
|
|
13
|
+
import { ServiceUnreachableError } from '../lib/coordination-client.js';
|
|
14
|
+
let dir;
|
|
15
|
+
let fp;
|
|
16
|
+
let lp;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
dir = mkdtempSync(join(tmpdir(), 'coord-fb-'));
|
|
19
|
+
fp = join(dir, '.mx-coordination-fallback.jsonl');
|
|
20
|
+
lp = join(dir, '.mx-coordination-fallback.lock');
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
rmSync(dir, { recursive: true, force: true });
|
|
24
|
+
});
|
|
25
|
+
/** A client that records enqueues, optionally failing after N calls (outage). */
|
|
26
|
+
function recordingClient(failAfter = Infinity) {
|
|
27
|
+
const sent = [];
|
|
28
|
+
const client = {
|
|
29
|
+
async enqueue(req) {
|
|
30
|
+
if (sent.length >= failAfter)
|
|
31
|
+
throw new ServiceUnreachableError(new Error('down'));
|
|
32
|
+
sent.push(req);
|
|
33
|
+
return { messageId: `m${sent.length}`, duplicate: false };
|
|
34
|
+
},
|
|
35
|
+
async drain() {
|
|
36
|
+
return { messages: [], remaining: 0, batchId: '' };
|
|
37
|
+
},
|
|
38
|
+
async confirmDrain() { },
|
|
39
|
+
async ack() { },
|
|
40
|
+
};
|
|
41
|
+
return { client, sent };
|
|
42
|
+
}
|
|
43
|
+
describe('coordination-fallback', () => {
|
|
44
|
+
it('writes an outage entry durably with an idempotency key (0600)', () => {
|
|
45
|
+
const entry = appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'help' }, fp);
|
|
46
|
+
expect(existsSync(fp)).toBe(true);
|
|
47
|
+
expect(entry.idempotencyKey).toBe(idempotencyKeyFor('mcp', entry.localTimestamp, 'help'));
|
|
48
|
+
const back = readFallbackEntries(fp);
|
|
49
|
+
expect(back).toHaveLength(1);
|
|
50
|
+
expect(back[0].body).toBe('help');
|
|
51
|
+
});
|
|
52
|
+
it('rejects a pre-planted symlink at the fallback path (O_NOFOLLOW, KI-COORD-SF2)', () => {
|
|
53
|
+
const target = join(dir, 'evil-target');
|
|
54
|
+
writeFileSync(target, '');
|
|
55
|
+
symlinkSync(target, fp);
|
|
56
|
+
expect(() => appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p2', body: 'x' }, fp)).toThrow();
|
|
57
|
+
// The symlink target was not written through.
|
|
58
|
+
expect(readFileSync(target, 'utf8')).toBe('');
|
|
59
|
+
});
|
|
60
|
+
it('flushes all entries idempotently and clears the file', async () => {
|
|
61
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'a' }, fp);
|
|
62
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p2', body: 'b' }, fp);
|
|
63
|
+
const { client, sent } = recordingClient();
|
|
64
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp });
|
|
65
|
+
expect(res).toEqual({ flushed: 2, remaining: 0, skipped: false });
|
|
66
|
+
expect(sent.map((s) => s.idempotencyKey).every(Boolean)).toBe(true);
|
|
67
|
+
expect(existsSync(fp)).toBe(false); // cleared
|
|
68
|
+
// Re-flush is a no-op (file empty) — safe re-run.
|
|
69
|
+
const again = await flushFallback(client, { fallbackPath: fp, leasePath: lp });
|
|
70
|
+
expect(again.flushed).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
it('keeps remaining entries when an interrupted flush hits a still-down service', async () => {
|
|
73
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'a' }, fp);
|
|
74
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p2', body: 'b' }, fp);
|
|
75
|
+
const { client, sent } = recordingClient(1); // first succeeds, second throws unreachable
|
|
76
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp });
|
|
77
|
+
expect(res.flushed).toBe(1);
|
|
78
|
+
expect(res.remaining).toBe(1);
|
|
79
|
+
expect(sent).toHaveLength(1);
|
|
80
|
+
// The unsent entry is still on disk for the next flush.
|
|
81
|
+
const left = readFallbackEntries(fp);
|
|
82
|
+
expect(left).toHaveLength(1);
|
|
83
|
+
expect(left[0].body).toBe('b');
|
|
84
|
+
});
|
|
85
|
+
it('skips flushing when another flusher holds a fresh lease', async () => {
|
|
86
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'a' }, fp);
|
|
87
|
+
expect(acquireLease(lp, 30_000)).toBe(true); // simulate another holder
|
|
88
|
+
const { client, sent } = recordingClient();
|
|
89
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp });
|
|
90
|
+
expect(res.skipped).toBe(true);
|
|
91
|
+
expect(sent).toHaveLength(0);
|
|
92
|
+
releaseLease(lp);
|
|
93
|
+
});
|
|
94
|
+
it('re-acquires (steals) a stale lease', async () => {
|
|
95
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'a' }, fp);
|
|
96
|
+
acquireLease(lp, 30_000);
|
|
97
|
+
// Age the lease well past the stale window.
|
|
98
|
+
const old = new Date(Date.now() - 10 * 60 * 1000);
|
|
99
|
+
utimesSync(lp, old, old);
|
|
100
|
+
const { client, sent } = recordingClient();
|
|
101
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp, staleMs: 1_000 });
|
|
102
|
+
expect(res.skipped).toBe(false);
|
|
103
|
+
expect(res.flushed).toBe(1);
|
|
104
|
+
expect(sent).toHaveLength(1);
|
|
105
|
+
});
|
|
106
|
+
it('tightens a pre-existing 0666 fallback file to 0600 (MINOR-5)', () => {
|
|
107
|
+
writeFileSync(fp, '', { mode: 0o666 });
|
|
108
|
+
chmodSync(fp, 0o666); // ensure group/other bits despite umask
|
|
109
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p2', body: 'x' }, fp);
|
|
110
|
+
expect(statSync(fp).mode & 0o777).toBe(0o600);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('coordination-fallback — corrupt line quarantine (MAJOR-4)', () => {
|
|
114
|
+
let cp;
|
|
115
|
+
beforeEach(() => { cp = join(dir, '.mx-coordination-fallback.corrupt.jsonl'); });
|
|
116
|
+
afterEach(() => vi.restoreAllMocks());
|
|
117
|
+
it('quarantines a corrupt line, keeps parsing the rest, and warns to stderr (0600)', () => {
|
|
118
|
+
const warn = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
119
|
+
const good = appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'a' }, fp);
|
|
120
|
+
// Append a corrupt (non-JSON) line by hand.
|
|
121
|
+
writeFileSync(fp, `${JSON.stringify(good)}\n{ this is not json\n`, { mode: 0o600 });
|
|
122
|
+
const entries = readFallbackEntries(fp, cp);
|
|
123
|
+
expect(entries).toHaveLength(1);
|
|
124
|
+
expect(entries[0].body).toBe('a');
|
|
125
|
+
expect(existsSync(cp)).toBe(true);
|
|
126
|
+
expect(readFileSync(cp, 'utf8')).toContain('not json');
|
|
127
|
+
expect(statSync(cp).mode & 0o777).toBe(0o600);
|
|
128
|
+
expect((warn.mock.calls.map((c) => String(c[0])).join(''))).toMatch(/corrupt fallback entr.* quarantined/);
|
|
129
|
+
});
|
|
130
|
+
it('flushes the valid entries even when a corrupt line is present', async () => {
|
|
131
|
+
vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
132
|
+
const good = appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'keepme' }, fp);
|
|
133
|
+
writeFileSync(fp, `nonsense-line\n${JSON.stringify(good)}\n`, { mode: 0o600 });
|
|
134
|
+
const { client, sent } = recordingClient();
|
|
135
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp, corruptPath: cp });
|
|
136
|
+
expect(res.flushed).toBe(1);
|
|
137
|
+
expect(sent[0].body).toBe('keepme');
|
|
138
|
+
expect(existsSync(cp)).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
it('does not follow a symlink at the quarantine path — warns inline, target untouched, valid entries still flush', async () => {
|
|
141
|
+
const warn = [];
|
|
142
|
+
vi.spyOn(process.stderr, 'write').mockImplementation((c) => { warn.push(String(c)); return true; });
|
|
143
|
+
// Pre-plant a symlink at the quarantine path pointing at a victim file.
|
|
144
|
+
const victim = join(dir, 'victim-target');
|
|
145
|
+
writeFileSync(victim, 'original');
|
|
146
|
+
symlinkSync(victim, cp);
|
|
147
|
+
const good = appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'keepme' }, fp);
|
|
148
|
+
writeFileSync(fp, `not-json-corrupt\n${JSON.stringify(good)}\n`, { mode: 0o600 });
|
|
149
|
+
const { client, sent } = recordingClient();
|
|
150
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp, corruptPath: cp });
|
|
151
|
+
// Valid entry still flushed; corrupt line surfaced inline, symlink not followed.
|
|
152
|
+
expect(res.flushed).toBe(1);
|
|
153
|
+
expect(sent[0].body).toBe('keepme');
|
|
154
|
+
expect(warn.join('')).toMatch(/could not quarantine .* inline: not-json-corrupt/);
|
|
155
|
+
expect(readFileSync(victim, 'utf8')).toBe('original'); // target untouched
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
//# sourceMappingURL=coordination-fallback.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-fallback.test.js","sourceRoot":"","sources":["../../src/__tests__/coordination-fallback.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -109,6 +109,7 @@ describe('resolveProjectConfig', () => {
|
|
|
109
109
|
teamsDir: 'infra/teams',
|
|
110
110
|
roleguidesDir: 'infra/roleguides',
|
|
111
111
|
projectSlug: 'my-project',
|
|
112
|
+
observabilityApiUrl: 'https://obs.example.com/ingest',
|
|
112
113
|
};
|
|
113
114
|
const result = resolveProjectConfig(override);
|
|
114
115
|
expect(result).toEqual(override);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-config.test.js","sourceRoot":"","sources":["../../src/__tests__/project-config.test.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"project-config.test.js","sourceRoot":"","sources":["../../src/__tests__/project-config.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generation": 1,
|
|
3
|
+
"description": "Monotonic agent-config generation. Bump when shipping agent-config changes that external projects should pull via `mx-agent config-sync`. The CLI stamps this into a project's config on init/config-sync and warns on `mx-agent start` when a project's generation is older than the CLI's bundled generation. Platform v2.35 Phase 3 (#4017)."
|
|
4
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# MemNexus Project Instructions
|
|
2
|
+
|
|
3
|
+
Project-specific corrections for the MemNexus codebase. General rules (save triggers, memory retrieval) are in `~/.claude/CLAUDE.md`; the product-owner communication standard is below. Full CLI reference: `mx agent-help`.
|
|
4
|
+
|
|
5
|
+
MemNexus is the product we build here. We are the first users — we dogfood our own memory tool. That changes how we treat errors and how we use the system.
|
|
6
|
+
|
|
7
|
+
## Communicating with the product owner
|
|
8
|
+
|
|
9
|
+
Status updates, iteration reports, PR summaries, and escalation summaries are read by the product owner (PO) — a smart reader who has NOT been following your work. Write for that reader:
|
|
10
|
+
|
|
11
|
+
- Lead with the headline. The single most important fact is the first sentence.
|
|
12
|
+
- Shared vocabulary, not no vocabulary. Terms like 'LongMemEval' or 'MCP' are fine — the PO knows these. But when you introduce a term the PO has not seen before, explain it in plain language once ('the reranker — the model that re-scores search results after the initial retrieval'), then just use the short name going forward. If you are unsure whether the PO knows a term, explain it. Never assume.
|
|
13
|
+
- Numbers, not adjectives. "3 of 5 tests pass, 2 fail on timeout" — not "good progress on tests."
|
|
14
|
+
- Bad news stated plainly: the problem, then the impact, then the next step. Do not bury it.
|
|
15
|
+
- Concise, not verbose. Explaining everything is not the same as padding — cut filler, keep context.
|
|
16
|
+
- Banned filler: "let me…", "honestly" / "to be honest", "great question", "essentially" / "basically", "moving forward".
|
|
17
|
+
|
|
18
|
+
**Give context, not just facts.** Every update should follow this pattern in short paragraphs:
|
|
19
|
+
1. **What we did.** The action taken this iteration, in one or two sentences.
|
|
20
|
+
2. **What we were trying to learn or achieve.** Why we did it — what question were we answering or what problem were we solving.
|
|
21
|
+
3. **What happened.** The result — what we found, what shipped, what broke.
|
|
22
|
+
4. **What it means.** The implication — how this changes the plan, what it unblocks, or what decision it creates.
|
|
23
|
+
This can be four sentences or four short paragraphs. The point is narrative flow — the PO should be able to read top to bottom and understand the full story without asking follow-up questions. Do not dump a list of findings without the connecting thread.
|
|
24
|
+
|
|
25
|
+
**When you need a decision, frame it.** If the update requires PO input, structure it as:
|
|
26
|
+
- **Situation:** What happened, in one sentence.
|
|
27
|
+
- **Options:** 2–3 choices, each in one sentence with the tradeoff.
|
|
28
|
+
- **Recommendation:** Which one you would pick and why.
|
|
29
|
+
- **Cost of waiting:** What happens if this decision is not made this week.
|
|
30
|
+
If no decision is needed, do not use this structure — just tell the story.
|
|
31
|
+
|
|
32
|
+
**Register boundary.** This standard applies to PO-facing prose: leader status updates, iteration reports, PR summaries, escalation summaries — anything the PO reads. It does NOT apply to machine-parsed or agent-to-agent content: iteration-log table rows, technical detail inside cross-team escalations, or memory content written for other agents. There, internal shorthand (P0–P3, team names, metric names) stays — do not expand it. The metaphor, weasel, and announced-honesty bans below also do not apply to an established technical term where that term is the precise word — "attack surface" in a security review, "critical path" in a schedule. The sampler treats those as advisory (WARN), not violations.
|
|
33
|
+
|
|
34
|
+
**Cut these.** Four classes of phrasing dilute a PO update:
|
|
35
|
+
|
|
36
|
+
- **Borrowed metaphors.** Name the literal thing. Write "a caveat that must travel with the claim or the claim misleads" and "how much breaks if this fails" — not the physical-shorthand versions. The same holds for poker, sailing, and construction figures of speech: state the concrete point.
|
|
37
|
+
- **Announced candor and drama.** Delete any clause whose only job is to signal that you are being open, or that a fact is difficult. State the fact; its weight comes from the fact.
|
|
38
|
+
- **Weasel words (from Amazon writing guidance).** Replace a vague quantifier or hedge with the number: "1.8x faster," not an unmeasured booster; "P99 240 ms," not "fast."
|
|
39
|
+
- **Adjectives, adverbs, intensifiers.** Keep only those that carry a specific, verifiable point. Write "3 of 5 tests fail," not "a handful of tests fail." Cut heat words that add no information.
|
|
40
|
+
|
|
41
|
+
## MemNexus errors are P0 — stop work
|
|
42
|
+
|
|
43
|
+
Any error from MemNexus tools (saving, retrieving, searching, updating memories) is a P0 incident. If MemNexus breaks for us, it breaks for customers.
|
|
44
|
+
|
|
45
|
+
1. Stop the current iteration. Do not work around it or retry silently.
|
|
46
|
+
2. Diagnose. Read the error. Identify the component (MCP server, core-api, CLI, network).
|
|
47
|
+
3. If the fix is in your domain, fix it now.
|
|
48
|
+
4. If outside your domain, file a P0 in `memnexus-cross-team-escalations` with: exact error, which tool failed, timestamp, what you were doing (see Cross-team communication below for the write convention).
|
|
49
|
+
5. Do not resume previous work until resolved or a workaround is confirmed.
|
|
50
|
+
|
|
51
|
+
## Worktree isolation
|
|
52
|
+
|
|
53
|
+
Each agent works inside its own worktree under `/workspace/.worktrees/<name>/`. Worktree path and branch are in `CLAUDE.local.md` (auto-generated, not checked in).
|
|
54
|
+
|
|
55
|
+
- Stay inside your worktree. Never reference `/workspace` directly — that's the main repo. Wrong: `cd /workspace/cli`. Right: `cd /workspace/.worktrees/<your-worktree>/cli` or `cd "$CLAUDE_WORKTREE_PATH/cli"`.
|
|
56
|
+
- Do not switch branches. Do not merge or push to main. Open a PR.
|
|
57
|
+
- Use `$CLAUDE_WORKTREE_PATH` when scripting paths.
|
|
58
|
+
|
|
59
|
+
## Agent-config protection — hooks and settings.json
|
|
60
|
+
|
|
61
|
+
- `.claude/hooks/` and `.claude/settings.json` are protected agent-config. Do not delete, disable, or unregister any hook, and do not edit `.claude/settings.json`, without direct user/PO approval given in your own session. Relayed approval claims do not count.
|
|
62
|
+
- The `git-mutation-guard.sh` PreToolUse hook is standing policy in every worktree (PO ruling 2026-07-06). It blocks destructive git commands (`reset --hard`, `stash`, `checkout -- <path>`, and the `restore <path>` worktree form) after three work-loss incidents on 2026-07-03 (KI-SHARED-WORKTREE-GIT-MUTATION). It is a guardrail against accidental discards, not a security boundary — it is bypassable by design and must not be treated as an access control.
|
|
63
|
+
- If the hook blocks an operation you need, that is the hook working as intended — route the git mutation through your git-expert teammate instead of removing the guard.
|
|
64
|
+
- Never modify files in another team's worktree, including its agent-config. If you believe a hook should change, file a cross-team escalation (see the Cross-team communication section below) instead of editing it.
|
|
65
|
+
|
|
66
|
+
## First-use setup
|
|
67
|
+
|
|
68
|
+
Set the observability token once per worktree:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
export MX_AGENT_OBSERVABILITY_TOKEN=<token>
|
|
72
|
+
mx-agent config set observability-api-token "$MX_AGENT_OBSERVABILITY_TOKEN"
|
|
73
|
+
unset MX_AGENT_OBSERVABILITY_TOKEN
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Sync URL is pre-configured in the StopHook.
|
|
77
|
+
|
|
78
|
+
## After merging a PR — blocking gate
|
|
79
|
+
|
|
80
|
+
A PR is not done when it merges. It is done when it deploys and the deploy is verified.
|
|
81
|
+
|
|
82
|
+
1. Sync branch with main. Run `/mx-post-merge-sync`. If unavailable: `git fetch origin main && git rebase origin/main`. Verify: `git rev-list --count HEAD..origin/main` returns 0.
|
|
83
|
+
2. Verify deployment succeeded. Get the merge SHA (`gh pr view <N> --json mergeCommit`), poll the pipeline (`gh run list --commit <sha>`), confirm the artifact is live (npm publish for CLI; deploy workflow `success` for services).
|
|
84
|
+
3. Save a checkpoint memory. Include PR number, what shipped, deploy status, next step.
|
|
85
|
+
|
|
86
|
+
If the pipeline is still running, wait. If it failed, fix it. Do not start the next iteration with an unverified deploy.
|
|
87
|
+
|
|
88
|
+
## Creating memories in this project
|
|
89
|
+
|
|
90
|
+
**MemNexus is the sole memory store (PO directive, 2026-07-06).** All memories — team state, lessons, decisions, preferences, checkpoints — live in MemNexus. Do not keep parallel memory stores (harness-private file memories, scratch notes promoted to durable state, per-agent local ledgers); anything worth remembering goes into MemNexus with proper codeContext, where every team and future session can find it. If a harness feature auto-maintains a private memory file, treat it as a cache/pointer only — MemNexus is authoritative on any conflict. Rationale: single source of truth + we dogfood our own product; split stores hide lessons from other teams and from the product's own retrieval. Directive record: memory 2aa310bb-9b70-40e4-aaa9-33ed406ffc1e.
|
|
91
|
+
|
|
92
|
+
Always pass `codeContext` with at minimum `product`, `service`, and `team` (your roleguide specifies these). Without it, your memories don't scope correctly and other teams' searches will surface them by accident.
|
|
93
|
+
|
|
94
|
+
Use `--content` (non-interactive). `--name` is for living documents (kebab-case keys for status trackers, configs, reference docs). `--topics` is for status (`completed`, `in-progress`, `blocked`) and component tags only — most topics are auto-extracted.
|
|
95
|
+
|
|
96
|
+
Group related memories into a conversation. First memory of a new topic: `conversationId: "NEW"`. Capture the returned `conv_xxx`. Reuse it for follow-ups in the same topic.
|
|
97
|
+
|
|
98
|
+
If a search returns zero results or results from the wrong team/product context, use `mx memories recall --query "..."` or `mx memories build-context --context "..."` before proceeding. Do not act on guessed context.
|
|
99
|
+
|
|
100
|
+
Never run `mx` commands in the background. They complete in under a second.
|
|
101
|
+
|
|
102
|
+
Never put credentials, secrets, tokens, or connection strings in memory content.
|
|
103
|
+
|
|
104
|
+
## Cross-team communication
|
|
105
|
+
|
|
106
|
+
Read `memnexus-cross-team-escalations` at session start — it remains the single authoritative read for cross-team issues.
|
|
107
|
+
|
|
108
|
+
To file or update an escalation, do NOT rewrite `memnexus-cross-team-escalations` directly (concurrent full-content writes clobber other teams' entries). Instead append your entry to YOUR team's outbox, `memnexus-escalations-<team>-outbox`: fetch it, append your entry (source team, target team, severity P0–P3, description, action needed, timestamp), update. By convention you are your outbox's only writer (no API-enforced ACL — the single-writer property is discipline, not a guarantee); write only your own team's outbox. Platform composes outbox entries into `memnexus-cross-team-escalations`. Exception — P0/P1 urgent only: re-fetch this memory's current content IMMEDIATELY before the write and compose from that head; after writing, confirm the reported previous-version id matches the head you fetched. If it does NOT match, a concurrent write was clobbered — recover it: fetch the superseded intermediate version by id (`mx memories get <id>`; note `mx memories history` is currently broken), diff, merge the lost entries back, and write a reconciliation version. Always also append the entry to your team outbox as the durable copy.
|
|
109
|
+
|
|
110
|
+
Entry format (compose one block per entry so platform can parse it mechanically):
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
======================================
|
|
114
|
+
<STATUS> — <SOURCE-TEAM> → <TARGET-TEAM> (P<0-3>, FILED <YYYY-MM-DD>)
|
|
115
|
+
<one-line summary>. <description, evidence, action needed>.
|
|
116
|
+
Timestamp: <ISO8601 or YYYY-MM-DD ~HH:MM UTC>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
If an entry in your outbox has been pending (not FOLDED) for >48h, direct-write a P1 staleness note to the shared memory so it isn't invisible.
|
|
120
|
+
|
|
121
|
+
Check other teams via `memnexus-<team>-leader-state` and `memnexus-<team>-known-issues` named memories.
|
|
122
|
+
|
|
123
|
+
## Continuous operation
|
|
124
|
+
|
|
125
|
+
After completing an iteration, proceed to the next per your roleguide. Do not pause to ask "what next." Pause only when:
|
|
126
|
+
|
|
127
|
+
- A decision exceeds your authority
|
|
128
|
+
- You've run 4+ hours with no human input
|
|
129
|
+
- The next gap is genuinely unclear
|
|
130
|
+
- A deployment failed and you cannot resolve it
|
|
131
|
+
|
|
132
|
+
## Delegation
|
|
133
|
+
|
|
134
|
+
You lead an agent team. Each teammate is a separate Claude Code session. Spawn teammates by referencing agent types in `.claude/agents/`. Don't do the work yourself — your job is to decide, assign, review, coordinate.
|
|
135
|
+
|
|
136
|
+
Security review and adversarial testing always go to designated teammates. Never self-review.
|
|
137
|
+
|
|
138
|
+
## OpenAPI
|
|
139
|
+
|
|
140
|
+
When working on core-api, see `core-api/.ai/api-instructions.md`. Run `npm run validate:openapi:local` before committing.
|