@mmnto/cli 1.43.6 → 1.45.0
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/commands/compile.d.ts.map +1 -1
- package/dist/commands/compile.js +80 -2
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/mail.d.ts +77 -0
- package/dist/commands/mail.d.ts.map +1 -0
- package/dist/commands/mail.js +339 -0
- package/dist/commands/mail.js.map +1 -0
- package/dist/commands/mail.test.d.ts +11 -0
- package/dist/commands/mail.test.d.ts.map +1 -0
- package/dist/commands/mail.test.js +470 -0
- package/dist/commands/mail.test.js.map +1 -0
- package/dist/hook/schema.d.ts +2 -2
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `totem mail` (mmnto-ai/totem#1970, ADR-106 § 3 / ADR-107).
|
|
3
|
+
*
|
|
4
|
+
* Filesystem-driven: every test builds a fresh `<tmp>/workspace/<repo>/.totem/orchestration/`
|
|
5
|
+
* tree, exercises the poll, and asserts on the structured `MailPollResult`.
|
|
6
|
+
* Skips the human-text formatter path except where the formatter's behavior
|
|
7
|
+
* itself is under test — JSON output is the durable contract for hook
|
|
8
|
+
* consumers, the text output is human-only.
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from 'node:fs';
|
|
11
|
+
import * as os from 'node:os';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
14
|
+
import { cleanTmpDir } from '../test-utils.js';
|
|
15
|
+
import { pollMail } from './mail.js';
|
|
16
|
+
// ─── Fixture helpers ────────────────────────────────────
|
|
17
|
+
let tmpRoot;
|
|
18
|
+
let workspace;
|
|
19
|
+
function mkDir(p) {
|
|
20
|
+
fs.mkdirSync(p, { recursive: true });
|
|
21
|
+
return p;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build a sibling repo with `<repo>/.totem/orchestration/<senderAgent>/outbox/<file>.md`
|
|
25
|
+
* entries. `senderAgent` is the agent-id directory name; recipient lives in
|
|
26
|
+
* the frontmatter `to:` field.
|
|
27
|
+
*/
|
|
28
|
+
function writeOutbox(repo, senderAgent, files) {
|
|
29
|
+
const outbox = path.join(workspace, repo, '.totem', 'orchestration', senderAgent, 'outbox');
|
|
30
|
+
mkDir(outbox);
|
|
31
|
+
for (const f of files) {
|
|
32
|
+
const content = f.raw ??
|
|
33
|
+
[
|
|
34
|
+
'---',
|
|
35
|
+
`from: ${f.from ?? senderAgent}`,
|
|
36
|
+
`to: ${f.to}`,
|
|
37
|
+
`date: ${f.date ?? '2026-05-18T1700Z'}`,
|
|
38
|
+
`subject: ${f.subject ?? '(no subject)'}`,
|
|
39
|
+
'---',
|
|
40
|
+
'',
|
|
41
|
+
'Body text.',
|
|
42
|
+
'',
|
|
43
|
+
].join('\n');
|
|
44
|
+
fs.writeFileSync(path.join(outbox, f.name), content, 'utf-8');
|
|
45
|
+
}
|
|
46
|
+
return outbox;
|
|
47
|
+
}
|
|
48
|
+
function writeProcessed(repo, recipientAgent, names) {
|
|
49
|
+
const dir = path.join(workspace, repo, '.totem', 'orchestration', recipientAgent, 'processed');
|
|
50
|
+
mkDir(dir);
|
|
51
|
+
for (const n of names)
|
|
52
|
+
fs.writeFileSync(path.join(dir, n), '---\nto: x\n---\n', 'utf-8');
|
|
53
|
+
}
|
|
54
|
+
function writeBroadcastProcessed(repo, recipientAgent, names) {
|
|
55
|
+
const dir = path.join(workspace, repo, '.totem', 'orchestration', recipientAgent, 'processed', '_broadcast');
|
|
56
|
+
mkDir(dir);
|
|
57
|
+
for (const n of names)
|
|
58
|
+
fs.writeFileSync(path.join(dir, n), '---\nto: broadcast\n---\n', 'utf-8');
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* `totem` is the only cohort name resolveSelfAgents pre-knows; using it as
|
|
62
|
+
* the "self" repo keeps the SELF_AGENT resolution working off the basename
|
|
63
|
+
* map without needing a config.json or env override for every test.
|
|
64
|
+
*/
|
|
65
|
+
function selfRepoRoot() {
|
|
66
|
+
return mkDir(path.join(workspace, 'totem'));
|
|
67
|
+
}
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-mail-'));
|
|
70
|
+
workspace = mkDir(path.join(tmpRoot, 'workspace'));
|
|
71
|
+
});
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
cleanTmpDir(tmpRoot);
|
|
74
|
+
});
|
|
75
|
+
function poll(opts = {}) {
|
|
76
|
+
return pollMail({ repoRoot: selfRepoRoot(), workspace, env: {}, ...opts });
|
|
77
|
+
}
|
|
78
|
+
// ─── Basic filter behavior ──────────────────────────────
|
|
79
|
+
describe('pollMail — basic filter behavior', () => {
|
|
80
|
+
it('returns no mail when workspace is empty', () => {
|
|
81
|
+
const result = poll();
|
|
82
|
+
expect(result.mail).toEqual([]);
|
|
83
|
+
expect(result.scanned).toBe(0);
|
|
84
|
+
expect(result.truncated).toBe(false);
|
|
85
|
+
expect(result.workspace).toBe(path.resolve(workspace));
|
|
86
|
+
});
|
|
87
|
+
it('returns mail addressed to a SELF_AGENT', () => {
|
|
88
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
89
|
+
{ name: '2026-05-18T1734Z-strategy-claude.md', to: 'totem-claude', subject: 'lane' },
|
|
90
|
+
]);
|
|
91
|
+
const result = poll();
|
|
92
|
+
expect(result.mail).toHaveLength(1);
|
|
93
|
+
expect(result.mail[0].to).toBe('totem-claude');
|
|
94
|
+
expect(result.mail[0].from).toBe('strategy-claude');
|
|
95
|
+
expect(result.mail[0].repo).toBe('totem-strategy');
|
|
96
|
+
expect(result.mail[0].subject).toBe('lane');
|
|
97
|
+
});
|
|
98
|
+
it('returns mail addressed to broadcast', () => {
|
|
99
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
100
|
+
{ name: '2026-05-18T1734Z.md', to: 'broadcast', subject: 'cohort announcement' },
|
|
101
|
+
]);
|
|
102
|
+
const result = poll();
|
|
103
|
+
expect(result.mail).toHaveLength(1);
|
|
104
|
+
expect(result.mail[0].to).toBe('broadcast');
|
|
105
|
+
});
|
|
106
|
+
it('skips mail addressed to a different agent', () => {
|
|
107
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
108
|
+
{ name: '2026-05-18T1918Z.md', to: 'lc-claude', subject: 'not-mine' },
|
|
109
|
+
]);
|
|
110
|
+
const result = poll();
|
|
111
|
+
expect(result.mail).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
it('matches `to:` case-insensitively (back-compat with arhgap11-Gemini)', () => {
|
|
114
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
115
|
+
{ name: '2026-05-18T1734Z.md', to: 'Totem-Claude', subject: 'case test' },
|
|
116
|
+
]);
|
|
117
|
+
const result = poll();
|
|
118
|
+
expect(result.mail).toHaveLength(1);
|
|
119
|
+
expect(result.mail[0].to).toBe('Totem-Claude');
|
|
120
|
+
});
|
|
121
|
+
it('returns mail from multiple sibling repos in one scan', () => {
|
|
122
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
123
|
+
{ name: '2026-05-18T1700Z.md', to: 'totem-claude', subject: 'strategy reply' },
|
|
124
|
+
]);
|
|
125
|
+
writeOutbox('liquid-city', 'lc-claude', [
|
|
126
|
+
{ name: '2026-05-18T1800Z.md', to: 'totem-claude', subject: 'lc heads-up' },
|
|
127
|
+
]);
|
|
128
|
+
const result = poll();
|
|
129
|
+
expect(result.mail).toHaveLength(2);
|
|
130
|
+
const repos = result.mail.map((m) => m.repo).sort();
|
|
131
|
+
expect(repos).toEqual(['liquid-city', 'totem-strategy']);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
// ─── Processed/ exclusion ───────────────────────────────
|
|
135
|
+
describe('pollMail — processed/ exclusion', () => {
|
|
136
|
+
it('excludes mail already in processed/', () => {
|
|
137
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
138
|
+
{ name: '2026-05-18T1734Z.md', to: 'totem-claude', subject: 'already actioned' },
|
|
139
|
+
{ name: '2026-05-18T1918Z.md', to: 'totem-claude', subject: 'still unread' },
|
|
140
|
+
]);
|
|
141
|
+
writeProcessed('totem', 'totem-claude', ['2026-05-18T1734Z.md']);
|
|
142
|
+
const result = poll();
|
|
143
|
+
expect(result.mail).toHaveLength(1);
|
|
144
|
+
expect(result.mail[0].file).toBe('2026-05-18T1918Z.md');
|
|
145
|
+
});
|
|
146
|
+
it('excludes mail already in processed/_broadcast/', () => {
|
|
147
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
148
|
+
{ name: '2026-05-17T0103Z.md', to: 'broadcast', subject: 'cohort freeze' },
|
|
149
|
+
]);
|
|
150
|
+
writeBroadcastProcessed('totem', 'totem-claude', ['2026-05-17T0103Z.md']);
|
|
151
|
+
const result = poll();
|
|
152
|
+
expect(result.mail).toEqual([]);
|
|
153
|
+
});
|
|
154
|
+
it('uses processed/ from every SELF_AGENT (multi-agent repos)', () => {
|
|
155
|
+
// Pre-populate processed for totem-gemini; mail addressed to totem-gemini
|
|
156
|
+
// should be excluded even though totem-claude is also a SELF_AGENT.
|
|
157
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
158
|
+
{ name: '2026-05-18T1700Z.md', to: 'totem-gemini', subject: 'already done' },
|
|
159
|
+
]);
|
|
160
|
+
writeProcessed('totem', 'totem-gemini', ['2026-05-18T1700Z.md']);
|
|
161
|
+
const result = poll();
|
|
162
|
+
expect(result.mail).toEqual([]);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
// ─── Sort + metadata ────────────────────────────────────
|
|
166
|
+
describe('pollMail — sort + metadata', () => {
|
|
167
|
+
it('sorts newest-first by frontmatter date', () => {
|
|
168
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
169
|
+
{ name: 'a.md', to: 'totem-claude', date: '2026-05-18T1000Z', subject: 'older' },
|
|
170
|
+
{ name: 'b.md', to: 'totem-claude', date: '2026-05-18T2000Z', subject: 'newer' },
|
|
171
|
+
{ name: 'c.md', to: 'totem-claude', date: '2026-05-18T1500Z', subject: 'middle' },
|
|
172
|
+
]);
|
|
173
|
+
const result = poll();
|
|
174
|
+
expect(result.mail.map((m) => m.subject)).toEqual(['newer', 'middle', 'older']);
|
|
175
|
+
});
|
|
176
|
+
it('falls back to filename sort when date is absent', () => {
|
|
177
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
178
|
+
{ name: '2026-05-18T1000Z.md', to: 'totem-claude', raw: '---\nto: totem-claude\n---\n' },
|
|
179
|
+
{ name: '2026-05-18T2000Z.md', to: 'totem-claude', raw: '---\nto: totem-claude\n---\n' },
|
|
180
|
+
]);
|
|
181
|
+
const result = poll();
|
|
182
|
+
expect(result.mail[0].file).toBe('2026-05-18T2000Z.md');
|
|
183
|
+
expect(result.mail[1].file).toBe('2026-05-18T1000Z.md');
|
|
184
|
+
});
|
|
185
|
+
it('preserves the `to:` field verbatim (mixed case) in result metadata', () => {
|
|
186
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
187
|
+
{ name: 'x.md', to: 'Totem-Claude', subject: 'mixed case to' },
|
|
188
|
+
]);
|
|
189
|
+
const result = poll();
|
|
190
|
+
expect(result.mail[0].to).toBe('Totem-Claude');
|
|
191
|
+
});
|
|
192
|
+
it('captures `from:` from frontmatter when present', () => {
|
|
193
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
194
|
+
{
|
|
195
|
+
name: 'x.md',
|
|
196
|
+
to: 'totem-claude',
|
|
197
|
+
from: 'override-sender',
|
|
198
|
+
subject: 'overridden from field',
|
|
199
|
+
},
|
|
200
|
+
]);
|
|
201
|
+
const result = poll();
|
|
202
|
+
expect(result.mail[0].from).toBe('override-sender');
|
|
203
|
+
});
|
|
204
|
+
it('falls back to outbox-dir name when from: is absent', () => {
|
|
205
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
206
|
+
{
|
|
207
|
+
name: 'x.md',
|
|
208
|
+
to: 'totem-claude',
|
|
209
|
+
raw: '---\nto: totem-claude\nsubject: no from field\n---\n',
|
|
210
|
+
},
|
|
211
|
+
]);
|
|
212
|
+
const result = poll();
|
|
213
|
+
expect(result.mail[0].from).toBe('strategy-claude');
|
|
214
|
+
});
|
|
215
|
+
it('reports `(no subject)` when subject is absent', () => {
|
|
216
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
217
|
+
{ name: 'x.md', to: 'totem-claude', raw: '---\nto: totem-claude\n---\n' },
|
|
218
|
+
]);
|
|
219
|
+
const result = poll();
|
|
220
|
+
expect(result.mail[0].subject).toBe('(no subject)');
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
// ─── Frontmatter parsing robustness ─────────────────────
|
|
224
|
+
describe('pollMail — frontmatter parsing', () => {
|
|
225
|
+
it('skips files without `to:` in the frontmatter', () => {
|
|
226
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
227
|
+
{ name: 'noto.md', to: 'totem-claude', raw: '---\nfrom: x\n---\n' },
|
|
228
|
+
]);
|
|
229
|
+
const result = poll();
|
|
230
|
+
expect(result.mail).toEqual([]);
|
|
231
|
+
});
|
|
232
|
+
it('only reads frontmatter from the header block (body `to:` cannot fabricate a match)', () => {
|
|
233
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
234
|
+
{
|
|
235
|
+
name: 'body-to.md',
|
|
236
|
+
to: 'unused',
|
|
237
|
+
raw: '---\nfrom: x\n---\n\nto: totem-claude\nsubject: body forge\n',
|
|
238
|
+
},
|
|
239
|
+
]);
|
|
240
|
+
const result = poll();
|
|
241
|
+
expect(result.mail).toEqual([]);
|
|
242
|
+
});
|
|
243
|
+
it('handles CRLF line endings', () => {
|
|
244
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
245
|
+
{
|
|
246
|
+
name: 'crlf.md',
|
|
247
|
+
to: 'totem-claude',
|
|
248
|
+
raw: '---\r\nfrom: strategy-claude\r\nto: totem-claude\r\nsubject: crlf\r\n---\r\n\r\nBody.\r\n',
|
|
249
|
+
},
|
|
250
|
+
]);
|
|
251
|
+
const result = poll();
|
|
252
|
+
expect(result.mail).toHaveLength(1);
|
|
253
|
+
expect(result.mail[0].subject).toBe('crlf');
|
|
254
|
+
});
|
|
255
|
+
it('non-.md files are ignored', () => {
|
|
256
|
+
const outbox = writeOutbox('totem-strategy', 'strategy-claude', []);
|
|
257
|
+
fs.writeFileSync(path.join(outbox, 'note.txt'), '---\nto: totem-claude\n---\nshould be skipped', 'utf-8');
|
|
258
|
+
const result = poll();
|
|
259
|
+
expect(result.mail).toEqual([]);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
// ─── SELF_AGENT precedence + warnings ───────────────────
|
|
263
|
+
describe('pollMail — SELF_AGENT resolution', () => {
|
|
264
|
+
it('records resolution source = map for a known repo', () => {
|
|
265
|
+
const result = poll();
|
|
266
|
+
expect(result.selfAgents.source).toBe('map');
|
|
267
|
+
expect(result.selfAgents.agents).toEqual(['totem-claude', 'totem-gemini']);
|
|
268
|
+
});
|
|
269
|
+
it('respects TOTEM_SELF_AGENT env override', () => {
|
|
270
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
271
|
+
{ name: 'x.md', to: 'custom-id', subject: 'custom' },
|
|
272
|
+
]);
|
|
273
|
+
const result = poll({ env: { TOTEM_SELF_AGENT: 'custom-id' } });
|
|
274
|
+
expect(result.selfAgents.source).toBe('env');
|
|
275
|
+
expect(result.mail).toHaveLength(1);
|
|
276
|
+
});
|
|
277
|
+
it('warns when SELF_AGENT cannot be resolved', () => {
|
|
278
|
+
const unknownRoot = mkDir(path.join(tmpRoot, 'workspace', 'unknown-repo'));
|
|
279
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
280
|
+
{ name: 'x.md', to: 'something', subject: 'orphan' },
|
|
281
|
+
]);
|
|
282
|
+
const result = pollMail({ repoRoot: unknownRoot, workspace, env: {} });
|
|
283
|
+
expect(result.selfAgents.source).toBe('none');
|
|
284
|
+
expect(result.selfAgents.agents).toEqual([]);
|
|
285
|
+
expect(result.warnings.some((w) => w.includes('no SELF_AGENT resolved'))).toBe(true);
|
|
286
|
+
// With no SELF, only broadcast survives the filter.
|
|
287
|
+
expect(result.mail).toEqual([]);
|
|
288
|
+
});
|
|
289
|
+
it('still surfaces broadcast mail when SELF_AGENT cannot be resolved', () => {
|
|
290
|
+
const unknownRoot = mkDir(path.join(tmpRoot, 'workspace', 'unknown-repo'));
|
|
291
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
292
|
+
{ name: 'b.md', to: 'broadcast', subject: 'cohort' },
|
|
293
|
+
]);
|
|
294
|
+
const result = pollMail({ repoRoot: unknownRoot, workspace, env: {} });
|
|
295
|
+
expect(result.mail).toHaveLength(1);
|
|
296
|
+
expect(result.mail[0].to).toBe('broadcast');
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
// ─── Workspace + recursive ──────────────────────────────
|
|
300
|
+
describe('pollMail — workspace', () => {
|
|
301
|
+
it('honors the --workspace option', () => {
|
|
302
|
+
const alt = mkDir(path.join(tmpRoot, 'alt-workspace'));
|
|
303
|
+
fs.mkdirSync(path.join(alt, 'totem-strategy', '.totem', 'orchestration', 'strategy-claude', 'outbox'), {
|
|
304
|
+
recursive: true,
|
|
305
|
+
});
|
|
306
|
+
fs.writeFileSync(path.join(alt, 'totem-strategy', '.totem', 'orchestration', 'strategy-claude', 'outbox', 'x.md'), '---\nto: totem-claude\nsubject: alt-workspace\n---\n', 'utf-8');
|
|
307
|
+
const result = poll({ workspace: alt });
|
|
308
|
+
expect(result.mail).toHaveLength(1);
|
|
309
|
+
expect(result.workspace).toBe(path.resolve(alt));
|
|
310
|
+
});
|
|
311
|
+
it('honors TOTEM_WORKSPACE env var', () => {
|
|
312
|
+
const alt = mkDir(path.join(tmpRoot, 'env-workspace'));
|
|
313
|
+
fs.mkdirSync(path.join(alt, 'totem-strategy', '.totem', 'orchestration', 'strategy-claude', 'outbox'), {
|
|
314
|
+
recursive: true,
|
|
315
|
+
});
|
|
316
|
+
fs.writeFileSync(path.join(alt, 'totem-strategy', '.totem', 'orchestration', 'strategy-claude', 'outbox', 'x.md'), '---\nto: totem-claude\n---\n', 'utf-8');
|
|
317
|
+
const result = poll({ workspace: undefined, env: { TOTEM_WORKSPACE: alt } });
|
|
318
|
+
expect(result.workspace).toBe(path.resolve(alt));
|
|
319
|
+
expect(result.mail).toHaveLength(1);
|
|
320
|
+
});
|
|
321
|
+
it('warns and returns empty when workspace does not exist', () => {
|
|
322
|
+
const missing = path.join(tmpRoot, 'does-not-exist');
|
|
323
|
+
const result = poll({ workspace: missing });
|
|
324
|
+
expect(result.warnings.some((w) => w.includes('workspace does not exist'))).toBe(true);
|
|
325
|
+
expect(result.mail).toEqual([]);
|
|
326
|
+
});
|
|
327
|
+
it('--recursive descends into nested layouts', () => {
|
|
328
|
+
// Sibling repo nested inside a wrapper dir; default scan misses it,
|
|
329
|
+
// recursive scan finds it.
|
|
330
|
+
const wrapper = mkDir(path.join(workspace, 'wrapper-dir'));
|
|
331
|
+
fs.mkdirSync(path.join(wrapper, 'nested-strategy', '.totem', 'orchestration', 'strategy-claude', 'outbox'), { recursive: true });
|
|
332
|
+
fs.writeFileSync(path.join(wrapper, 'nested-strategy', '.totem', 'orchestration', 'strategy-claude', 'outbox', 'x.md'), '---\nto: totem-claude\nsubject: nested\n---\n', 'utf-8');
|
|
333
|
+
const flat = poll({ recursive: false });
|
|
334
|
+
expect(flat.mail).toEqual([]);
|
|
335
|
+
const recursive = poll({ recursive: true });
|
|
336
|
+
expect(recursive.mail).toHaveLength(1);
|
|
337
|
+
expect(recursive.mail[0].subject).toBe('nested');
|
|
338
|
+
// Label uses the immediate parent dir (where `.totem/orchestration/`
|
|
339
|
+
// lives), not the top-level wrapper — readers expect "the repo" to be
|
|
340
|
+
// the leaf, not the container.
|
|
341
|
+
expect(recursive.mail[0].repo).toBe('nested-strategy');
|
|
342
|
+
});
|
|
343
|
+
it('skips dot-directories and node_modules during recursive scan', () => {
|
|
344
|
+
const node = mkDir(path.join(workspace, 'node_modules', 'fake-pkg'));
|
|
345
|
+
fs.mkdirSync(path.join(node, '.totem', 'orchestration', 'strategy-claude', 'outbox'), {
|
|
346
|
+
recursive: true,
|
|
347
|
+
});
|
|
348
|
+
fs.writeFileSync(path.join(node, '.totem', 'orchestration', 'strategy-claude', 'outbox', 'x.md'), '---\nto: totem-claude\nsubject: spam\n---\n', 'utf-8');
|
|
349
|
+
const result = poll({ recursive: true });
|
|
350
|
+
expect(result.mail).toEqual([]);
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
// ─── MAX_SCAN truncation ────────────────────────────────
|
|
354
|
+
describe('pollMail — MAX_SCAN truncation', () => {
|
|
355
|
+
it('marks truncated and stops scanning at the cap (scanned <= MAX_SCAN)', () => {
|
|
356
|
+
// Generate > MAX_SCAN files (501) so the cap is exercised. Filenames
|
|
357
|
+
// chosen ascending so DESC sort lists the highest-numbered first;
|
|
358
|
+
// truncation drops the *oldest* tail, preserving the newest mail.
|
|
359
|
+
const files = [];
|
|
360
|
+
for (let i = 0; i < 510; i++) {
|
|
361
|
+
const num = String(i).padStart(5, '0');
|
|
362
|
+
files.push({ name: `${num}.md`, to: 'totem-claude', subject: `n${i}` });
|
|
363
|
+
}
|
|
364
|
+
writeOutbox('totem-strategy', 'strategy-claude', files);
|
|
365
|
+
const result = poll();
|
|
366
|
+
expect(result.truncated).toBe(true);
|
|
367
|
+
// Contract: scanned never exceeds MAX_SCAN. Documents the pre-increment
|
|
368
|
+
// off-by-one fix from CR R1 (#1971).
|
|
369
|
+
expect(result.scanned).toBeLessThanOrEqual(500);
|
|
370
|
+
expect(result.scanned).toBe(500);
|
|
371
|
+
// Newest file (highest number) must be in the result; the cap drops the tail.
|
|
372
|
+
expect(result.mail.some((m) => m.file === '00509.md')).toBe(true);
|
|
373
|
+
});
|
|
374
|
+
it('preserves global newest-first ordering under truncation across repos (GCA R2 #1971)', () => {
|
|
375
|
+
// Without global ordering, alphabet-early repos (e.g. `apple-repo`) could
|
|
376
|
+
// hog MAX_SCAN. Confirm that the newest files across BOTH repos survive,
|
|
377
|
+
// even when alphabet-first repo has enough files to exhaust the cap alone.
|
|
378
|
+
const oldFiles = [];
|
|
379
|
+
for (let i = 0; i < 600; i++) {
|
|
380
|
+
// Year 2024 — these are "old" in the global ordering.
|
|
381
|
+
oldFiles.push({
|
|
382
|
+
name: `2024-${String(i).padStart(5, '0')}.md`,
|
|
383
|
+
to: 'totem-claude',
|
|
384
|
+
subject: `old-${i}`,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
writeOutbox('apple-repo', 'apple-sender', oldFiles);
|
|
388
|
+
const newFiles = [];
|
|
389
|
+
for (let i = 0; i < 5; i++) {
|
|
390
|
+
// Year 2027 — newer than every apple-repo file. Global sort must
|
|
391
|
+
// surface these even though apple-repo would alphabetically come first.
|
|
392
|
+
newFiles.push({
|
|
393
|
+
name: `2027-${String(i).padStart(5, '0')}.md`,
|
|
394
|
+
to: 'totem-claude',
|
|
395
|
+
subject: `fresh-${i}`,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
writeOutbox('zebra-repo', 'zebra-sender', newFiles);
|
|
399
|
+
const result = poll();
|
|
400
|
+
expect(result.truncated).toBe(true);
|
|
401
|
+
expect(result.scanned).toBe(500);
|
|
402
|
+
// All 5 fresh entries must be in the result. Pre-fix, they would have
|
|
403
|
+
// been dropped because apple-repo's 500 stale files hit the cap first.
|
|
404
|
+
const freshSubjects = result.mail.map((m) => m.subject).filter((s) => s.startsWith('fresh-'));
|
|
405
|
+
expect(freshSubjects).toHaveLength(5);
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
// ─── Frontmatter forge defense (GCA R2 security finding on #1971) ───────
|
|
409
|
+
describe('pollMail — frontmatter forge defense', () => {
|
|
410
|
+
it('rejects files that do not start with the YAML `---` delimiter', () => {
|
|
411
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
412
|
+
{
|
|
413
|
+
name: 'no-delimiter.md',
|
|
414
|
+
to: 'unused',
|
|
415
|
+
raw: 'to: totem-claude\nsubject: forged\nbody text\n',
|
|
416
|
+
},
|
|
417
|
+
]);
|
|
418
|
+
const result = poll();
|
|
419
|
+
expect(result.mail).toEqual([]);
|
|
420
|
+
});
|
|
421
|
+
it('rejects large files lacking a blank-line frontmatter separator', () => {
|
|
422
|
+
// Real frontmatter is tens of bytes; a file > 2 KiB without the
|
|
423
|
+
// blank-line separator is structurally malformed. Capping the header
|
|
424
|
+
// scan in that case prevents body lines from fabricating `to:` matches.
|
|
425
|
+
const filler = 'x'.repeat(2500);
|
|
426
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
427
|
+
{
|
|
428
|
+
name: 'huge-no-separator.md',
|
|
429
|
+
to: 'unused',
|
|
430
|
+
raw: `---\nfrom: strategy-claude\n${filler}\nto: totem-claude\nsubject: forged\n`,
|
|
431
|
+
},
|
|
432
|
+
]);
|
|
433
|
+
const result = poll();
|
|
434
|
+
expect(result.mail).toEqual([]);
|
|
435
|
+
});
|
|
436
|
+
it('accepts a small file without a blank-line separator (header fits in MAX_HEADER_BYTES)', () => {
|
|
437
|
+
// Below the 2 KiB cap, the whole file is treated as header — that's the
|
|
438
|
+
// existing lenient behavior, preserved for valid handoffs that omit the
|
|
439
|
+
// trailing blank line.
|
|
440
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
441
|
+
{
|
|
442
|
+
name: 'small-no-separator.md',
|
|
443
|
+
to: 'totem-claude',
|
|
444
|
+
raw: '---\nfrom: strategy-claude\nto: totem-claude\nsubject: tight\n---\n',
|
|
445
|
+
},
|
|
446
|
+
]);
|
|
447
|
+
const result = poll();
|
|
448
|
+
expect(result.mail).toHaveLength(1);
|
|
449
|
+
expect(result.mail[0].subject).toBe('tight');
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
// ─── Structured warnings on FS failures ─────────────────
|
|
453
|
+
describe('pollMail — structured warnings on FS failures', () => {
|
|
454
|
+
it('emits a warning when processed/ exists but is unreadable', () => {
|
|
455
|
+
writeOutbox('totem-strategy', 'strategy-claude', [
|
|
456
|
+
{ name: '2026-05-18T1734Z.md', to: 'totem-claude', subject: 'live' },
|
|
457
|
+
]);
|
|
458
|
+
// Create processed/ as a FILE (not a dir) so readdirSync throws ENOTDIR.
|
|
459
|
+
// existsSync is true (it's a file), readdir then fails — exercises the
|
|
460
|
+
// catch-and-warn path for processed/ specifically.
|
|
461
|
+
const selfProcessed = path.join(selfRepoRoot(), '.totem', 'orchestration', 'totem-claude');
|
|
462
|
+
mkDir(selfProcessed);
|
|
463
|
+
fs.writeFileSync(path.join(selfProcessed, 'processed'), 'not a directory');
|
|
464
|
+
const result = poll();
|
|
465
|
+
// Mail still surfaces (degraded — no exclusion filter), warning recorded.
|
|
466
|
+
expect(result.mail).toHaveLength(1);
|
|
467
|
+
expect(result.warnings.some((w) => w.startsWith('processed/ scan failed'))).toBe(true);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
//# sourceMappingURL=mail.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail.test.js","sourceRoot":"","sources":["../../src/commands/mail.test.ts"],"names":[],"mappings":"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"}
|
package/dist/hook/schema.d.ts
CHANGED
|
@@ -357,8 +357,8 @@ export declare const CompiledHooksManifestSchema: z.ZodObject<{
|
|
|
357
357
|
recoveryHint?: string | undefined;
|
|
358
358
|
verification_shadow?: unknown;
|
|
359
359
|
}[];
|
|
360
|
-
schemaVersion: 1;
|
|
361
360
|
compiledAt: string;
|
|
361
|
+
schemaVersion: 1;
|
|
362
362
|
sourcePackVersions: Record<string, string>;
|
|
363
363
|
}, {
|
|
364
364
|
hooks: {
|
|
@@ -376,8 +376,8 @@ export declare const CompiledHooksManifestSchema: z.ZodObject<{
|
|
|
376
376
|
recoveryHint?: string | undefined;
|
|
377
377
|
verification_shadow?: unknown;
|
|
378
378
|
}[];
|
|
379
|
-
schemaVersion: 1;
|
|
380
379
|
compiledAt: string;
|
|
380
|
+
schemaVersion: 1;
|
|
381
381
|
sourcePackVersions: Record<string, string>;
|
|
382
382
|
}>;
|
|
383
383
|
export type CompiledHooksManifest = z.infer<typeof CompiledHooksManifestSchema>;
|
package/dist/index.js
CHANGED
|
@@ -443,6 +443,25 @@ program
|
|
|
443
443
|
handleError(err);
|
|
444
444
|
}
|
|
445
445
|
});
|
|
446
|
+
// Canonical cross-repo outbox poll (mmnto-ai/totem#1970; ADR-106 § 3 / ADR-107).
|
|
447
|
+
// Replaces ad-hoc per-hook implementations with one cohort-portable surface.
|
|
448
|
+
program
|
|
449
|
+
.command('mail')
|
|
450
|
+
.description('Show unread cross-repo mail addressed to this repo’s agent(s) (ADR-106 § 3)')
|
|
451
|
+
.option('--json', 'Emit JSON to stdout instead of human-readable text to stderr')
|
|
452
|
+
.option('--recursive', 'Walk the workspace recursively for nested layouts (default: single-level siblings)')
|
|
453
|
+
.option('--workspace <path>', 'Workspace dir to scan (default: $TOTEM_WORKSPACE, else parent of cwd)')
|
|
454
|
+
.action(async (opts) => {
|
|
455
|
+
try {
|
|
456
|
+
const { mailCommand } = await import('./commands/mail.js');
|
|
457
|
+
await mailCommand(opts);
|
|
458
|
+
}
|
|
459
|
+
catch (err) {
|
|
460
|
+
handleError(err);
|
|
461
|
+
// totem-context: handleError returns `never` (process.exit), so the throw is unreachable but required to satisfy the Tenet 4 fail-loud rule that bans bare-catch silent-degrade. Mirrors the verify-badges pattern above.
|
|
462
|
+
throw err;
|
|
463
|
+
}
|
|
464
|
+
});
|
|
446
465
|
program
|
|
447
466
|
.command('remove-secret <index>')
|
|
448
467
|
.description('Remove a custom secret from .totem/secrets.json by index (from list-secrets)')
|