@mmnto/cli 1.88.0 → 1.90.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.
Files changed (104) hide show
  1. package/README.md +34 -0
  2. package/dist/author-sandbox.d.ts +32 -0
  3. package/dist/author-sandbox.d.ts.map +1 -0
  4. package/dist/author-sandbox.js +95 -0
  5. package/dist/author-sandbox.js.map +1 -0
  6. package/dist/authored-rule-intake-prewindow.test.d.ts +2 -0
  7. package/dist/authored-rule-intake-prewindow.test.d.ts.map +1 -0
  8. package/dist/authored-rule-intake-prewindow.test.js +84 -0
  9. package/dist/authored-rule-intake-prewindow.test.js.map +1 -0
  10. package/dist/authored-rule-intake.d.ts +21 -1
  11. package/dist/authored-rule-intake.d.ts.map +1 -1
  12. package/dist/authored-rule-intake.js +56 -1
  13. package/dist/authored-rule-intake.js.map +1 -1
  14. package/dist/commands/authored-whitelist.d.ts.map +1 -1
  15. package/dist/commands/authored-whitelist.js +19 -8
  16. package/dist/commands/authored-whitelist.js.map +1 -1
  17. package/dist/commands/ecl-gc.d.ts +259 -0
  18. package/dist/commands/ecl-gc.d.ts.map +1 -0
  19. package/dist/commands/ecl-gc.js +579 -0
  20. package/dist/commands/ecl-gc.js.map +1 -0
  21. package/dist/commands/ecl-gc.test.d.ts +12 -0
  22. package/dist/commands/ecl-gc.test.d.ts.map +1 -0
  23. package/dist/commands/ecl-gc.test.js +609 -0
  24. package/dist/commands/ecl-gc.test.js.map +1 -0
  25. package/dist/commands/eject.d.ts.map +1 -1
  26. package/dist/commands/eject.js +10 -6
  27. package/dist/commands/eject.js.map +1 -1
  28. package/dist/commands/eject.test.js +5 -1
  29. package/dist/commands/eject.test.js.map +1 -1
  30. package/dist/commands/init-templates.d.ts +4 -0
  31. package/dist/commands/init-templates.d.ts.map +1 -1
  32. package/dist/commands/init-templates.js +30 -2
  33. package/dist/commands/init-templates.js.map +1 -1
  34. package/dist/commands/init.test.js +26 -3
  35. package/dist/commands/init.test.js.map +1 -1
  36. package/dist/commands/mail.d.ts +11 -0
  37. package/dist/commands/mail.d.ts.map +1 -1
  38. package/dist/commands/mail.js +5 -1
  39. package/dist/commands/mail.js.map +1 -1
  40. package/dist/commands/mail.test.js +19 -0
  41. package/dist/commands/mail.test.js.map +1 -1
  42. package/dist/commands/rule-author-command.test.js +29 -2
  43. package/dist/commands/rule-author-command.test.js.map +1 -1
  44. package/dist/commands/rule-author-teardown.test.d.ts +2 -0
  45. package/dist/commands/rule-author-teardown.test.d.ts.map +1 -0
  46. package/dist/commands/rule-author-teardown.test.js +123 -0
  47. package/dist/commands/rule-author-teardown.test.js.map +1 -0
  48. package/dist/commands/rule-author.d.ts +1 -0
  49. package/dist/commands/rule-author.d.ts.map +1 -1
  50. package/dist/commands/rule-author.js +143 -2
  51. package/dist/commands/rule-author.js.map +1 -1
  52. package/dist/commands/spine-authored-cert-corpus.d.ts +15 -0
  53. package/dist/commands/spine-authored-cert-corpus.d.ts.map +1 -1
  54. package/dist/commands/spine-authored-cert-corpus.js +17 -1
  55. package/dist/commands/spine-authored-cert-corpus.js.map +1 -1
  56. package/dist/commands/spine-authored-cert-corpus.test.js +440 -4
  57. package/dist/commands/spine-authored-cert-corpus.test.js.map +1 -1
  58. package/dist/commands/spine-authored-materialize.d.ts +55 -0
  59. package/dist/commands/spine-authored-materialize.d.ts.map +1 -0
  60. package/dist/commands/spine-authored-materialize.js +315 -0
  61. package/dist/commands/spine-authored-materialize.js.map +1 -0
  62. package/dist/commands/spine-cert-materialize.d.ts.map +1 -1
  63. package/dist/commands/spine-cert-materialize.js +17 -0
  64. package/dist/commands/spine-cert-materialize.js.map +1 -1
  65. package/dist/commands/spine-cert-persist.d.ts +17 -1
  66. package/dist/commands/spine-cert-persist.d.ts.map +1 -1
  67. package/dist/commands/spine-cert-persist.js +17 -5
  68. package/dist/commands/spine-cert-persist.js.map +1 -1
  69. package/dist/commands/spine-cert-persist.test.js +60 -0
  70. package/dist/commands/spine-cert-persist.test.js.map +1 -1
  71. package/dist/commands/spine-cert-run-corpus.d.ts +18 -28
  72. package/dist/commands/spine-cert-run-corpus.d.ts.map +1 -1
  73. package/dist/commands/spine-cert-run-corpus.js +162 -21
  74. package/dist/commands/spine-cert-run-corpus.js.map +1 -1
  75. package/dist/commands/spine-derive-labels.d.ts.map +1 -1
  76. package/dist/commands/spine-derive-labels.js +9 -1
  77. package/dist/commands/spine-derive-labels.js.map +1 -1
  78. package/dist/commands/spine-freeze-orchestration.test.d.ts +2 -0
  79. package/dist/commands/spine-freeze-orchestration.test.d.ts.map +1 -0
  80. package/dist/commands/spine-freeze-orchestration.test.js +468 -0
  81. package/dist/commands/spine-freeze-orchestration.test.js.map +1 -0
  82. package/dist/commands/spine-freeze-split.d.ts +10 -0
  83. package/dist/commands/spine-freeze-split.d.ts.map +1 -0
  84. package/dist/commands/spine-freeze-split.js +131 -0
  85. package/dist/commands/spine-freeze-split.js.map +1 -0
  86. package/dist/commands/spine-windtunnel.d.ts +35 -0
  87. package/dist/commands/spine-windtunnel.d.ts.map +1 -1
  88. package/dist/commands/spine-windtunnel.js +47 -7
  89. package/dist/commands/spine-windtunnel.js.map +1 -1
  90. package/dist/index.js +93 -0
  91. package/dist/index.js.map +1 -1
  92. package/dist/spine-fixture-ancestry.d.ts +20 -0
  93. package/dist/spine-fixture-ancestry.d.ts.map +1 -0
  94. package/dist/spine-fixture-ancestry.js +46 -0
  95. package/dist/spine-fixture-ancestry.js.map +1 -0
  96. package/dist/spine-fixture-ancestry.test.d.ts +2 -0
  97. package/dist/spine-fixture-ancestry.test.d.ts.map +1 -0
  98. package/dist/spine-fixture-ancestry.test.js +62 -0
  99. package/dist/spine-fixture-ancestry.test.js.map +1 -0
  100. package/dist/spine-freeze-proof.d.ts +89 -0
  101. package/dist/spine-freeze-proof.d.ts.map +1 -0
  102. package/dist/spine-freeze-proof.js +237 -0
  103. package/dist/spine-freeze-proof.js.map +1 -0
  104. package/package.json +2 -2
@@ -0,0 +1,579 @@
1
+ /**
2
+ * `totem ecl-gc` — ECL outbox retention prune (mmnto-ai/totem#2279; parent
3
+ * mmnto-ai/totem-strategy#700 / doctrine/ecl-discipline.md § 4.4).
4
+ *
5
+ * The binary-guaranteed cohort-wide replacement for the interim
6
+ * `scripts/prune-outbox.mjs`: it deletes an agent's OWN outbox dispatches once
7
+ * they age past the retention window N (default 14 days). Outbox dispatches are
8
+ * TRANSPORT, not archive — the durable record of whatever a dispatch carried
9
+ * lives in its home (rulings → ADRs / issues, work-state → the GH board,
10
+ * session history → `journal/`), so an aged courier file is disposable.
11
+ *
12
+ * SINGLE-WRITER INVARIANT (ADR-106): each agent prunes only its OWN
13
+ * `<repoRoot>/.totem/orchestration/<agent-id>/outbox/` — never a peer's, never
14
+ * the operator's chore. Self-resolution (Tenet-21 reuse of `resolveSelfSender`)
15
+ * makes pruning a peer structurally unreachable: the target path is composed
16
+ * from the resolved single agent-id and nothing else. This command NEVER reads
17
+ * or touches `journal/` (bounded-past record + MCP-indexed) or `processed/`
18
+ * (the handled-state cursor — erasing it makes consumed backlog re-read as
19
+ * unread) or any inbox / other seat. Scope is `outbox/` only.
20
+ *
21
+ * Safe by default: dry-run (list only) unless `--apply` is passed.
22
+ *
23
+ * COMPACTION (mmnto-ai/totem#2307; contract ADR-106 § A2 + ecl-discipline § 4.5,
24
+ * ratified strategy#826). `eclCompact` is the cursor-coupled processed-mark GC
25
+ * sibling of the prune above: it deletes an agent's OWN `processed/` marks that
26
+ * shadow nothing — a mark whose inbound dispatch its sender already swept per
27
+ * § 4.4. The retained cursor is `processed ∩ raw-addressed-inbound` (A2.1: the
28
+ * PRE-dedupe scan, never `pollMail`'s `inbound − processed` list). Deletion is
29
+ * licensed ONLY against a provably-complete poll (A2.2: full expected roster
30
+ * present, zero warnings, not truncated — else zero deletes), binds to exactly
31
+ * one seat (A2.3), and self-verifies via an immediate re-poll (A2.4). Unlike the
32
+ * prune's age window, compaction couples to the outbox lifecycle, not to time.
33
+ */
34
+ import * as fs from 'node:fs';
35
+ import * as path from 'node:path';
36
+ import { cohortRepos, getErrorMessage, isPathSafeAgentId, TotemError } from '@mmnto/totem';
37
+ import { pollMail, resolveSelfSender } from './mail.js';
38
+ // ─── Constants ──────────────────────────────────────────
39
+ const TAG = 'EclGc';
40
+ /**
41
+ * Doctrine-ratified retention window (ecl-discipline.md § 4.4). A grace window,
42
+ * not a correctness boundary — durable content already lives in homes; N only
43
+ * bounds how long the courier lingers. 14d comfortably covers infrequent-
44
+ * session (operator-run vendor) seats so a prune never clips a dispatch a slow
45
+ * peer hasn't read.
46
+ */
47
+ const DEFAULT_RETAIN_DAYS = 14;
48
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
49
+ /**
50
+ * Dual-form stamp acceptance ported verbatim from `scripts/prune-outbox.mjs`:
51
+ * the cohort emits both `YYYY-MM-DDTHHMMZ` (4-digit) and `YYYY-MM-DDTHHMMSSZ`
52
+ * (6-digit, `date -u +%Y-%m-%dT%H%M%SZ`). Matching only one form would silently
53
+ * skip the other (the verify-absence trap).
54
+ */
55
+ const STAMP_RE = /^(\d{4}-\d{2}-\d{2}T\d{4}(?:\d{2})?Z)/;
56
+ // ─── Pure helpers ───────────────────────────────────────
57
+ /**
58
+ * Canonicalize either stamp form to a 14-digit `YYYYMMDDHHMMSS` key (seconds
59
+ * default to `00`) so mixed-length stamps compare correctly. Ported from
60
+ * `scripts/prune-outbox.mjs:toKey`.
61
+ */
62
+ export function toStampKey(stamp) {
63
+ const digits = stamp.replace(/\D/g, '');
64
+ return digits.length === 12 ? `${digits}00` : digits;
65
+ }
66
+ /**
67
+ * Cutoff = `now − retainDays`, as a comparable 14-digit `YYYYMMDDHHMMSS` key.
68
+ * Derives from the injected `now` for determinism (Tenet 15). Ported from
69
+ * `scripts/prune-outbox.mjs:cutoffKey`.
70
+ */
71
+ export function cutoffKey(now, retainDays) {
72
+ const iso = new Date(now.getTime() - retainDays * MS_PER_DAY).toISOString();
73
+ // iso = 2026-06-16T22:13:32.088Z -> 20260616221332
74
+ return (iso.slice(0, 4) +
75
+ iso.slice(5, 7) +
76
+ iso.slice(8, 10) +
77
+ iso.slice(11, 13) +
78
+ iso.slice(14, 16) +
79
+ iso.slice(17, 19));
80
+ }
81
+ /**
82
+ * Classify a single directory entry against the cutoff. Safe-direction bias:
83
+ * anything whose age is not derivable from an eligible filename is KEPT +
84
+ * surfaced, never deleted (auto-deleting an un-ageable file is worse than
85
+ * letting it linger — mmnto-ai/totem-strategy#700 by-design). Non-file entries
86
+ * are checked FIRST so a directory named `*.md` can never reach a delete.
87
+ */
88
+ export function classifyEntry(entry, cutoff) {
89
+ if (!entry.isFile) {
90
+ return { action: 'skip', reason: 'not a regular file' };
91
+ }
92
+ // Extension gate (spec-narrowed vs the port): only `.md` dispatches are
93
+ // prune-eligible; a `.tmp` / `.gitkeep` / other stray is surfaced, never
94
+ // deleted.
95
+ if (!entry.name.endsWith('.md')) {
96
+ return { action: 'skip', reason: 'not a .md dispatch' };
97
+ }
98
+ const m = STAMP_RE.exec(entry.name);
99
+ if (m === null) {
100
+ return { action: 'skip', reason: 'unparseable stamp' };
101
+ }
102
+ // Exact boundary is RETAINED: `< cutoff` prunes, `>= cutoff` keeps.
103
+ return toStampKey(m[1]) < cutoff ? { action: 'prune' } : { action: 'keep' };
104
+ }
105
+ /**
106
+ * Pure prune-plan classification: given a directory listing + a cutoff key,
107
+ * partition entries into prune / keep / skip. Deterministic order (filename
108
+ * sort) so the reported lists are stable across platforms.
109
+ */
110
+ export function planPrune(entries, cutoff) {
111
+ const prune = [];
112
+ const skipped = [];
113
+ let kept = 0;
114
+ const ordered = [...entries].sort((a, b) => a.name.localeCompare(b.name));
115
+ for (const entry of ordered) {
116
+ const verdict = classifyEntry(entry, cutoff);
117
+ if (verdict.action === 'prune') {
118
+ prune.push(entry.name);
119
+ }
120
+ else if (verdict.action === 'keep') {
121
+ kept += 1;
122
+ }
123
+ else {
124
+ skipped.push({ file: entry.name, reason: verdict.reason });
125
+ }
126
+ }
127
+ return { prune, kept, skipped };
128
+ }
129
+ // ─── Core prune ─────────────────────────────────────────
130
+ /**
131
+ * Programmatic entry point. Resolves the single self-agent, validates inputs,
132
+ * scans that agent's OWN outbox, and (under `--apply`) deletes the aged
133
+ * dispatches. Returns a structured `EclGcResult`.
134
+ *
135
+ * Throws ONLY on usage errors (unresolvable/ambiguous self, unsafe agent-id,
136
+ * invalid `--retain-days`), and always BEFORE any directory scan or deletion.
137
+ * Filesystem failures are NEVER thrown — a per-file delete failure is captured
138
+ * into `result.failed` (janitorial sensor, not a gate — Tenet 13).
139
+ */
140
+ export function eclGc(opts = {}) {
141
+ const env = opts.env ?? process.env;
142
+ const repoRoot = path.resolve(opts.repoRoot ?? process.cwd());
143
+ const now = (opts.now ?? (() => new Date()))();
144
+ // Retain-days validation (usage error) — evaluated before any scan.
145
+ const retainDays = opts.retainDays ?? DEFAULT_RETAIN_DAYS;
146
+ if (!Number.isInteger(retainDays) || retainDays < 0) {
147
+ throw new TotemError('CONFIG_INVALID', `--retain-days must be a non-negative integer (got: ${String(retainDays)})`, 'pass --retain-days <n> with a whole number of days ≥ 0.');
148
+ }
149
+ // Self-resolution (Tenet-21 reuse — panel-mandated). `resolveSelfSender`
150
+ // picks the single writer (explicit > unambiguous self > throw). Its native
151
+ // message talks about "outbound dispatch"; rewrap into a prune-shaped usage
152
+ // error. Resolution + validation happen BEFORE any directory scan/deletion.
153
+ let agent;
154
+ try {
155
+ agent = resolveSelfSender(repoRoot, env, opts.agentId);
156
+ }
157
+ catch (err) {
158
+ throw new TotemError('CONFIG_INVALID', `cannot resolve a single agent whose outbox to prune: ${getErrorMessage(err)}`, 'set TOTEM_SELF_AGENT or pass --agent-id <agent-id>.', err);
159
+ }
160
+ // Defense-in-depth on a destructive path: the resolved id is a path segment
161
+ // in `.totem/orchestration/<agent>/outbox`. Reject anything that could escape
162
+ // it (traversal, separators, control/whitespace/win32-reserved chars).
163
+ if (!isPathSafeAgentId(agent)) {
164
+ throw new TotemError('CONFIG_INVALID', `invalid agent-id ${JSON.stringify(agent)} (path traversal, unsafe characters, or empty)`, 'pass a plain agent-id such as "totem-claude" (no path separators, "..", whitespace, or control characters).');
165
+ }
166
+ const dryRun = opts.apply !== true;
167
+ const outbox = path.join(repoRoot, '.totem', 'orchestration', agent, 'outbox');
168
+ const cutoff = cutoffKey(now, retainDays);
169
+ const warnings = [];
170
+ const result = {
171
+ agent,
172
+ retainDays,
173
+ dryRun,
174
+ outbox,
175
+ cutoffKey: cutoff,
176
+ pruned: [],
177
+ failed: [],
178
+ kept: 0,
179
+ skipped: [],
180
+ warnings,
181
+ };
182
+ // Missing outbox is the routine "nothing to prune" signal — clean empty
183
+ // result, never an error (the seat simply hasn't sent mail from this repo).
184
+ if (!fs.existsSync(outbox)) {
185
+ return result;
186
+ }
187
+ let dirents;
188
+ try {
189
+ dirents = fs.readdirSync(outbox, { withFileTypes: true });
190
+ // totem-context: intentional cleanup — an unreadable outbox (EACCES, raced removal) is surfaced as a warning and degrades to an empty prune rather than throwing; the prune is a janitorial sensor, not a gate (Tenet 13).
191
+ }
192
+ catch (err) {
193
+ warnings.push(`outbox read failed (${outbox}): ${getErrorMessage(err)}`);
194
+ return result;
195
+ }
196
+ const plan = planPrune(dirents.map((d) => ({ name: d.name, isFile: d.isFile() })), cutoff);
197
+ result.kept = plan.kept;
198
+ result.skipped = plan.skipped;
199
+ // Dry-run: report the would-prune set, delete nothing.
200
+ if (dryRun) {
201
+ result.pruned = plan.prune;
202
+ return result;
203
+ }
204
+ // Apply: unlink each aged dispatch. A single failed delete (EPERM, raced
205
+ // removal) is captured with per-item accounting and the loop continues —
206
+ // one bad file must never abort the prune (fail-soft, Tenet 4).
207
+ for (const file of plan.prune) {
208
+ try {
209
+ fs.unlinkSync(path.join(outbox, file));
210
+ result.pruned.push(file);
211
+ // totem-context: intentional cleanup — a per-file unlink failure is captured into result.failed and the loop continues; the wrapper turns a non-empty failed[] into exit 1 (partial-prune sensor), never a throw.
212
+ }
213
+ catch (err) {
214
+ result.failed.push({ file, error: getErrorMessage(err) });
215
+ }
216
+ }
217
+ return result;
218
+ }
219
+ // ─── CLI wrapper ────────────────────────────────────────
220
+ /**
221
+ * Render an `EclGcResult`. With `json`, the structured result goes to stdout
222
+ * (hook-friendly clean stream). Otherwise a human summary goes to stderr via
223
+ * the standard CLI logger (agent, mode, pruned/kept counts, skipped-with-
224
+ * reasons, failed count) — mirrors `mail.ts`'s stderr `log` usage.
225
+ */
226
+ export async function eclGcCommand(result, json) {
227
+ if (json) {
228
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
229
+ return result;
230
+ }
231
+ const { log } = await import('../ui.js');
232
+ const mode = result.dryRun ? 'dry-run (list only — re-run with --apply to delete)' : 'apply';
233
+ const verb = result.dryRun ? 'would prune' : 'pruned';
234
+ log.info(TAG, `agent: ${result.agent} · retention ${result.retainDays}d · cutoff < ${result.cutoffKey}`);
235
+ log.info(TAG, `mode: ${mode}`);
236
+ log.info(TAG, `${verb} ${result.pruned.length}; kept ${result.kept}`);
237
+ if (result.skipped.length > 0) {
238
+ log.warn(TAG, `skipped ${result.skipped.length} non-dispatch entr(y/ies) — left untouched:`);
239
+ for (const s of result.skipped) {
240
+ log.warn(TAG, ` - ${s.file}: ${s.reason}`);
241
+ }
242
+ }
243
+ if (result.failed.length > 0) {
244
+ log.error('Totem Error', `FAILED ${result.failed.length} delete(s) — surfaced, non-blocking:`);
245
+ for (const f of result.failed) {
246
+ log.error('Totem Error', ` - ${f.file}: ${f.error}`);
247
+ }
248
+ }
249
+ for (const w of result.warnings) {
250
+ log.warn(TAG, w);
251
+ }
252
+ return result;
253
+ }
254
+ // ─── Compaction (ADR-106 § A2 / ecl-discipline § 4.5) ───
255
+ const COMPACT_TAG = 'EclGc:compact';
256
+ /** Local directory predicate (stat-based) — a repo/orch dir must be a real
257
+ * directory, not merely an existing path (a same-named file must not pass the
258
+ * A2.2 roster-presence check). Mirrors the resolver's private `isDirectory`. */
259
+ function isDir(p) {
260
+ try {
261
+ return fs.statSync(p).isDirectory();
262
+ // totem-context: intentional fall-through — a stat failure (ENOENT, EACCES) is the "repo absent" signal the A2.2 roster gate consumes; rethrowing would force the gate to wrap a routine outcome.
263
+ }
264
+ catch {
265
+ return false;
266
+ }
267
+ }
268
+ /** Read the `.md` mark basenames in a `processed/` (sub)dir. A missing dir is
269
+ * the routine "no marks here" signal (empty). A read FAILURE is a stop
270
+ * condition for the A2.2 gate — pushed to `warnings` so the gate goes red
271
+ * (an un-enumerable own cursor cannot be safely compacted). */
272
+ function readMarkDir(dir, warnings) {
273
+ if (!fs.existsSync(dir))
274
+ return [];
275
+ try {
276
+ return fs.readdirSync(dir).filter((f) => f.endsWith('.md'));
277
+ // totem-context: intentional cleanup — a processed/ read failure (EACCES, raced rename) becomes a gate-reddening warning, never a throw; the compaction degrades to zero deletes (retain), the safe direction (A2.2).
278
+ }
279
+ catch (err) {
280
+ warnings.push(`processed/ scan failed (${dir}): ${getErrorMessage(err)}`);
281
+ return [];
282
+ }
283
+ }
284
+ /**
285
+ * Programmatic entry point for cursor-coupled processed-mark compaction. For a
286
+ * single resolved seat, deletes `processed/` marks whose inbound dispatch is
287
+ * absent from the RAW addressed-inbound set (A2.1) — but ONLY when discovery is
288
+ * provably complete (A2.2), and then self-verifies (A2.4).
289
+ *
290
+ * Throws ONLY on usage errors (unresolvable/ambiguous self, unsafe agent-id),
291
+ * always BEFORE any scan or deletion (exit-2 class, parity with `eclGc`). Every
292
+ * safety failure (incomplete roster, scan warning, truncation) is a STRUCTURED
293
+ * gate-red result with zero deletes, never a throw — the caller maps it to the
294
+ * compaction-abort exit code.
295
+ */
296
+ export function eclCompact(opts = {}) {
297
+ const env = opts.env ?? process.env;
298
+ const repoRoot = path.resolve(opts.repoRoot ?? process.cwd());
299
+ const workspace = path.resolve(opts.workspace ?? env['TOTEM_WORKSPACE'] ?? path.dirname(repoRoot));
300
+ const expectedRepos = [...(opts.expectedRepos ?? cohortRepos())].sort();
301
+ // Single-writer target (A2.3): resolve EXACTLY one seat. Ambiguous/zero self
302
+ // is a usage error (parity with `eclGc`), thrown before any scan or delete.
303
+ let agent;
304
+ try {
305
+ agent = resolveSelfSender(repoRoot, env, opts.agentId);
306
+ }
307
+ catch (err) {
308
+ throw new TotemError('CONFIG_INVALID', `cannot resolve a single agent whose processed cursor to compact: ${getErrorMessage(err)}`, 'set TOTEM_SELF_AGENT or pass --agent-id <agent-id>.', err);
309
+ }
310
+ if (!isPathSafeAgentId(agent)) {
311
+ throw new TotemError('CONFIG_INVALID', `invalid agent-id ${JSON.stringify(agent)} (path traversal, unsafe characters, or empty)`, 'pass a plain agent-id such as "totem-claude" (no path separators, "..", whitespace, or control characters).');
312
+ }
313
+ const dryRun = opts.apply !== true;
314
+ const warnings = [];
315
+ // Own processed marks (single-writer: this seat's OWN cursor only, in this
316
+ // repo). `processed/` (direct) ∪ `processed/_broadcast/` (broadcast). Read
317
+ // first so the undeclared-roster hard-abort below can still report the count.
318
+ const processedBase = path.join(repoRoot, '.totem', 'orchestration', agent, 'processed');
319
+ const broadcastDir = path.join(processedBase, '_broadcast');
320
+ const directMarks = readMarkDir(processedBase, warnings);
321
+ const broadcastMarks = readMarkDir(broadcastDir, warnings);
322
+ const directSet = new Set(directMarks);
323
+ const broadcastSet = new Set(broadcastMarks);
324
+ const allMarks = new Set([...directMarks, ...broadcastMarks]);
325
+ // Safety corollary (codified contract strategy#828 / eb9ff5b — A2.2): an
326
+ // UNDECLARED (empty) roster gives the completeness gate nothing to assert
327
+ // against, so completeness cannot be proven. This is a HARD ABORT (fail-loud,
328
+ // non-zero exit — the caller maps `gateComplete=false` to exit 3), NOT a silent
329
+ // no-op that would let `processed/` accumulate unsignalled (Tenet 4). A genuine
330
+ // single-repo consumer opts into completeness-1 by DECLARING a 1-repo roster;
331
+ // the interim `cohortRepos()` default is non-empty for our cohort, so this
332
+ // guards the config-declared undeclared-consumer case (mmnto-ai/totem#2310).
333
+ // `rosterDeclared=false` is retained only to frame the operator message.
334
+ if (expectedRepos.length === 0) {
335
+ return {
336
+ agent,
337
+ dryRun,
338
+ workspace,
339
+ expectedRepos,
340
+ rosterDeclared: false,
341
+ gateComplete: false,
342
+ gateReasons: [
343
+ 'no cohort roster declared — compaction HARD-ABORTS (declare cohort repos, or a single-repo roster to opt into completeness-1)',
344
+ ],
345
+ rawInbound: 0,
346
+ marks: allMarks.size,
347
+ collectable: [],
348
+ collected: [],
349
+ retained: allMarks.size,
350
+ failed: [],
351
+ resurfaced: [],
352
+ verifyComplete: true,
353
+ warnings,
354
+ };
355
+ }
356
+ // A2.2 gate part 1 — every declared roster repo must be present AND actually
357
+ // SCANNED. A silently-absent cohort repo is the false-unread leak (a live mark
358
+ // in its unscanned outbox looks inert): N < M is a hard block, never a silent
359
+ // skip (Tenet 4). Coverage is checked against `pollMail`'s OWN scan predicate
360
+ // (mail.ts `enumerateOutboxes` visits a workspace child only if it is a
361
+ // directory whose name does not start with '.' and is not `node_modules`) — a
362
+ // roster entry the gate accepts but the scan would skip leaves its outbox
363
+ // unscanned, re-opening the leak (CodeRabbit). `--force-incomplete` waives a
364
+ // genuinely ABSENT repo, but never an unscannable-name declaration (that is a
365
+ // config error, not a known gap). Unreachable with the clean #611 roster
366
+ // today; hardens the config-declared future (mmnto-ai/totem#2310).
367
+ const forced = opts.forceIncomplete === true;
368
+ const missingRepos = expectedRepos.filter((repo) => !repo.startsWith('.') && repo !== 'node_modules' && !isDir(path.join(workspace, repo)));
369
+ const unscannableRepos = expectedRepos.filter((repo) => repo.startsWith('.') || repo === 'node_modules');
370
+ // A2.1 — raw addressed-inbound: the SAME mail scan the reader runs, halted at
371
+ // the pre-dedupe stage (`includeProcessed`), bound to EXACTLY this seat via a
372
+ // forced `TOTEM_SELF_AGENT` so the multi-seat coordinator union never leaks in
373
+ // (A2.3). `pollMail` never throws — fs failures land in `poll.warnings`.
374
+ const poll = pollMail({
375
+ repoRoot,
376
+ workspace,
377
+ maxScan: opts.maxScan,
378
+ includeProcessed: true,
379
+ env: { ...env, TOTEM_SELF_AGENT: agent },
380
+ });
381
+ warnings.push(...poll.warnings);
382
+ const rawBasenames = new Set(poll.mail.map((m) => m.file));
383
+ // A2.2 gate: roster present (or forced) AND every roster name scannable AND
384
+ // zero scan/read warnings AND not truncated. `--force-incomplete` waives the
385
+ // absent-repo arm only — an unscannable-name declaration, scan warnings, and
386
+ // truncation stay hard aborts (a config error / broken read is not a
387
+ // known-absent repo). A forced-past missing repo is still surfaced (loud).
388
+ const gateComplete = (missingRepos.length === 0 || forced) &&
389
+ unscannableRepos.length === 0 &&
390
+ warnings.length === 0 &&
391
+ !poll.truncated;
392
+ const gateReasons = [
393
+ ...missingRepos.map((r) => `expected cohort repo missing from workspace: ${r}${forced ? ' (proceeding — --force-incomplete)' : ''}`),
394
+ ...unscannableRepos.map((r) => `declared cohort repo is unscannable (name filtered by the workspace scan): ${r}`),
395
+ ...warnings,
396
+ ...(poll.truncated ? [`scan truncated at ${poll.scanned} files (MAX_SCAN)`] : []),
397
+ ];
398
+ // Collectable = own marks whose dispatch is ABSENT from raw addressed-inbound.
399
+ // The raw set is the union of to:S ∪ broadcast (this seat forced; broadcast
400
+ // always matches), which mirrors `pollMail`'s recipient-class-blind basename
401
+ // filter — so a mark in EITHER store shadows a live dispatch of that basename;
402
+ // union retention is the sound match (codex panel). Deterministic order.
403
+ const collectable = [...allMarks].filter((m) => !rawBasenames.has(m)).sort();
404
+ const result = {
405
+ agent,
406
+ dryRun,
407
+ workspace,
408
+ expectedRepos,
409
+ rosterDeclared: true,
410
+ gateComplete,
411
+ gateReasons,
412
+ rawInbound: rawBasenames.size,
413
+ marks: allMarks.size,
414
+ collectable,
415
+ collected: [],
416
+ retained: allMarks.size,
417
+ failed: [],
418
+ resurfaced: [],
419
+ verifyComplete: true,
420
+ warnings,
421
+ };
422
+ // Never delete in dry-run OR when the gate is red. A collectable set computed
423
+ // from an INCOMPLETE poll is unreliable by construction (the A2.2 point), so a
424
+ // gate-red run does not even surface it as would-collect (uncertain ⇒ retain).
425
+ if (dryRun || !gateComplete) {
426
+ if (!gateComplete) {
427
+ result.collectable = [];
428
+ }
429
+ else {
430
+ // Dry-run + gate green: `retained` reflects the WOULD-survive count so the
431
+ // JSON payload matches the human display and the apply-path semantics —
432
+ // never reads as "total marks" in dry-run (greptile).
433
+ result.retained = allMarks.size - result.collectable.length;
434
+ }
435
+ return result;
436
+ }
437
+ // Apply + gate green: delete each inert mark from whichever store holds it. A
438
+ // per-file unlink failure leaves a lingering INERT mark (bounded storage,
439
+ // safe) — captured with per-item accounting, non-fatal (janitorial exit-1
440
+ // class), never a throw.
441
+ for (const name of collectable) {
442
+ let anyDeleted = false;
443
+ let anyFailed = false;
444
+ if (directSet.has(name)) {
445
+ try {
446
+ fs.unlinkSync(path.join(processedBase, name));
447
+ anyDeleted = true;
448
+ // totem-context: intentional cleanup — a per-mark unlink failure is captured into result.failed and the loop continues; the wrapper maps a non-empty failed[] to the janitorial exit code, never a throw.
449
+ }
450
+ catch (err) {
451
+ result.failed.push({ file: name, error: getErrorMessage(err) });
452
+ anyFailed = true;
453
+ }
454
+ }
455
+ if (broadcastSet.has(name)) {
456
+ try {
457
+ fs.unlinkSync(path.join(broadcastDir, name));
458
+ anyDeleted = true;
459
+ // totem-context: intentional cleanup — see the direct-store unlink above; dual placement so the rule fires on either catch line.
460
+ }
461
+ catch (err) {
462
+ result.failed.push({ file: `_broadcast/${name}`, error: getErrorMessage(err) });
463
+ anyFailed = true;
464
+ }
465
+ }
466
+ // Count as collected ONLY when fully removed from every store it was in. A
467
+ // dual-store mark whose one arm failed still persists on disk (kept in
468
+ // `failed`, out of `collected`) — so `collected` and `failed` never overlap
469
+ // and `retained` stays an accurate on-disk count (greptile).
470
+ if (anyDeleted && !anyFailed)
471
+ result.collected.push(name);
472
+ }
473
+ result.retained = allMarks.size - result.collected.length;
474
+ // A2.4 falsifier (in-command): immediately re-poll the SAME seat and assert NO
475
+ // previously-handled dispatch re-surfaces as unread. A resurfaced basename ∈
476
+ // the pre-compaction mark set means a LIVE mark was collected — the gate was
477
+ // too weak; the caller maps a non-empty `resurfaced` to the abort exit code.
478
+ // The re-poll inherits `maxScan` so it can never be MORE truncated than the
479
+ // discovery poll that passed the gate (CodeRabbit).
480
+ const verify = pollMail({
481
+ repoRoot,
482
+ workspace,
483
+ maxScan: opts.maxScan,
484
+ env: { ...env, TOTEM_SELF_AGENT: agent },
485
+ });
486
+ result.resurfaced = verify.mail
487
+ .map((m) => m.file)
488
+ .filter((f) => allMarks.has(f))
489
+ .sort();
490
+ // The A2.4 verdict is trustworthy only if the re-poll itself was complete: a
491
+ // truncated or warned verify could miss a resurfaced dispatch beyond its
492
+ // horizon and read as clean. An untrustworthy verify is a hard failure — we
493
+ // deleted marks and cannot confirm no resurface (CodeRabbit).
494
+ result.verifyComplete = !verify.truncated && verify.warnings.length === 0;
495
+ return result;
496
+ }
497
+ /**
498
+ * Render an `EclCompactResult`. `--json` → structured stdout; otherwise a human
499
+ * summary to stderr. A red gate reports the reasons + zero deletes; a tripped
500
+ * A2.4 falsifier and per-mark delete failures are surfaced via the error logger.
501
+ */
502
+ export async function eclCompactCommand(result, json) {
503
+ if (json) {
504
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
505
+ return result;
506
+ }
507
+ const { log } = await import('../ui.js');
508
+ const mode = result.dryRun ? 'dry-run (list only — re-run with --apply to delete)' : 'apply';
509
+ log.info(COMPACT_TAG, `agent: ${result.agent} · roster ${result.expectedRepos.length} repo(s) · mode: ${mode}`);
510
+ if (!result.gateComplete) {
511
+ // HARD ABORT (fail-loud, exit 3), never a silent skip — covers a declared
512
+ // roster that is incomplete AND the no-declared-roster case alike; the
513
+ // specific reason ("no cohort roster declared" vs a missing/unscannable
514
+ // repo, warning, or truncation) is carried in `gateReasons` (strategy#828).
515
+ log.warn(COMPACT_TAG, `A2.2 completeness gate NOT met — retained all ${result.marks} mark(s), deleted 0:`);
516
+ for (const r of result.gateReasons)
517
+ log.warn(COMPACT_TAG, ` - ${r}`);
518
+ return result;
519
+ }
520
+ // Gate green. When forced past a missing repo (the only way gateReasons is
521
+ // non-empty here), surface it LOUDLY — deletes proceeded despite an incomplete
522
+ // workspace at the operator's explicit request.
523
+ if (result.gateReasons.length > 0) {
524
+ log.warn(COMPACT_TAG, `proceeded despite an incomplete workspace (--force-incomplete):`);
525
+ for (const r of result.gateReasons)
526
+ log.warn(COMPACT_TAG, ` - ${r}`);
527
+ }
528
+ const verb = result.dryRun ? 'would collect' : 'collected';
529
+ const collectN = result.dryRun ? result.collectable.length : result.collected.length;
530
+ // `result.retained` is now accurate in every path (dry-run reflects would-
531
+ // survive; apply reflects on-disk after partial-failure accounting).
532
+ log.info(COMPACT_TAG, `${verb} ${collectN} inert mark(s); retained ${result.retained} of ${result.marks}; raw addressed-inbound ${result.rawInbound}`);
533
+ if (result.failed.length > 0) {
534
+ log.error('Totem Error', `FAILED ${result.failed.length} mark delete(s) — surfaced, non-blocking:`);
535
+ for (const f of result.failed)
536
+ log.error('Totem Error', ` - ${f.file}: ${f.error}`);
537
+ }
538
+ if (result.resurfaced.length > 0) {
539
+ log.error('Totem Error', `A2.4 FALSIFIER TRIPPED — ${result.resurfaced.length} handled dispatch(es) re-surfaced as unread (a live mark was collected):`);
540
+ for (const f of result.resurfaced)
541
+ log.error('Totem Error', ` - ${f}`);
542
+ }
543
+ if (!result.verifyComplete) {
544
+ log.error('Totem Error', 'A2.4 self-check UNTRUSTWORTHY — the post-compact re-poll was truncated or warned; a resurfaced dispatch beyond its horizon cannot be ruled out.');
545
+ }
546
+ for (const w of result.warnings)
547
+ log.warn(COMPACT_TAG, w);
548
+ return result;
549
+ }
550
+ /**
551
+ * Combined prune+compact exit-code contract (codex panel, mmnto-ai/totem#2307).
552
+ * Pure so the precedence is unit-testable independent of the CLI wrapper. The
553
+ * usage code `2` is NOT modeled here — it is the thrown-error path the wrapper
554
+ * catches around this call. Precedence: `3` (compaction abort — A2.2 gate red or
555
+ * A2.4 falsifier tripped) outranks `1` (partial janitorial delete failure, from
556
+ * either phase), which outranks `0` (clean). So a prune-partial + compact-abort
557
+ * is `3`, with the prune count still carried in the structured result.
558
+ */
559
+ export function resolveEclGcExitCode(prune, compact) {
560
+ if (compact !== undefined) {
561
+ // A2.4 falsifier tripped — OR an untrustworthy (truncated/warned) re-poll
562
+ // that cannot confirm no resurface — is always a hard failure.
563
+ if (compact.resurfaced.length > 0 || !compact.verifyComplete)
564
+ return 3;
565
+ // Any incomplete gate is a HARD ABORT (non-zero) — a declared roster that is
566
+ // incomplete (missing/unscannable repo, scan warning, truncation) AND the
567
+ // no-declared-roster case alike: absent a completeness expectation there is
568
+ // nothing to prove against, so the contract fails LOUD, never a silent no-op
569
+ // that lets `processed/` accumulate unsignalled (strategy#828 / eb9ff5b —
570
+ // A2.2 no-roster hard-abort corollary; Tenet 4).
571
+ if (!compact.gateComplete)
572
+ return 3;
573
+ }
574
+ if (prune.failed.length > 0 || (compact?.failed.length ?? 0) > 0) {
575
+ return 1;
576
+ }
577
+ return 0;
578
+ }
579
+ //# sourceMappingURL=ecl-gc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ecl-gc.js","sourceRoot":"","sources":["../../src/commands/ecl-gc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Tests for `totem ecl-gc` (mmnto-ai/totem#2279; parent mmnto-ai/totem-strategy#700).
3
+ *
4
+ * Filesystem-driven with an injected clock: every test builds a fresh
5
+ * `<tmp>/.totem/orchestration/<agent>/outbox/` tree, exercises `eclGc`, and
6
+ * asserts on the structured `EclGcResult` AND on the on-disk aftermath (which
7
+ * files survived). The safety rows (peer immunity, non-outbox trees untouched,
8
+ * ambiguity-throws-before-deletion, exact-boundary retention) are written to
9
+ * FAIL if their guard were removed — see the per-test non-vacuity notes.
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=ecl-gc.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ecl-gc.test.d.ts","sourceRoot":"","sources":["../../src/commands/ecl-gc.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}