@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.
- package/README.md +34 -0
- package/dist/author-sandbox.d.ts +32 -0
- package/dist/author-sandbox.d.ts.map +1 -0
- package/dist/author-sandbox.js +95 -0
- package/dist/author-sandbox.js.map +1 -0
- package/dist/authored-rule-intake-prewindow.test.d.ts +2 -0
- package/dist/authored-rule-intake-prewindow.test.d.ts.map +1 -0
- package/dist/authored-rule-intake-prewindow.test.js +84 -0
- package/dist/authored-rule-intake-prewindow.test.js.map +1 -0
- package/dist/authored-rule-intake.d.ts +21 -1
- package/dist/authored-rule-intake.d.ts.map +1 -1
- package/dist/authored-rule-intake.js +56 -1
- package/dist/authored-rule-intake.js.map +1 -1
- package/dist/commands/authored-whitelist.d.ts.map +1 -1
- package/dist/commands/authored-whitelist.js +19 -8
- package/dist/commands/authored-whitelist.js.map +1 -1
- package/dist/commands/ecl-gc.d.ts +259 -0
- package/dist/commands/ecl-gc.d.ts.map +1 -0
- package/dist/commands/ecl-gc.js +579 -0
- package/dist/commands/ecl-gc.js.map +1 -0
- package/dist/commands/ecl-gc.test.d.ts +12 -0
- package/dist/commands/ecl-gc.test.d.ts.map +1 -0
- package/dist/commands/ecl-gc.test.js +609 -0
- package/dist/commands/ecl-gc.test.js.map +1 -0
- package/dist/commands/eject.d.ts.map +1 -1
- package/dist/commands/eject.js +10 -6
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/eject.test.js +5 -1
- package/dist/commands/eject.test.js.map +1 -1
- package/dist/commands/init-templates.d.ts +4 -0
- package/dist/commands/init-templates.d.ts.map +1 -1
- package/dist/commands/init-templates.js +30 -2
- package/dist/commands/init-templates.js.map +1 -1
- package/dist/commands/init.test.js +26 -3
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/mail.d.ts +11 -0
- package/dist/commands/mail.d.ts.map +1 -1
- package/dist/commands/mail.js +5 -1
- package/dist/commands/mail.js.map +1 -1
- package/dist/commands/mail.test.js +19 -0
- package/dist/commands/mail.test.js.map +1 -1
- package/dist/commands/rule-author-command.test.js +29 -2
- package/dist/commands/rule-author-command.test.js.map +1 -1
- package/dist/commands/rule-author-teardown.test.d.ts +2 -0
- package/dist/commands/rule-author-teardown.test.d.ts.map +1 -0
- package/dist/commands/rule-author-teardown.test.js +123 -0
- package/dist/commands/rule-author-teardown.test.js.map +1 -0
- package/dist/commands/rule-author.d.ts +1 -0
- package/dist/commands/rule-author.d.ts.map +1 -1
- package/dist/commands/rule-author.js +143 -2
- package/dist/commands/rule-author.js.map +1 -1
- package/dist/commands/spine-authored-cert-corpus.d.ts +15 -0
- package/dist/commands/spine-authored-cert-corpus.d.ts.map +1 -1
- package/dist/commands/spine-authored-cert-corpus.js +17 -1
- package/dist/commands/spine-authored-cert-corpus.js.map +1 -1
- package/dist/commands/spine-authored-cert-corpus.test.js +440 -4
- package/dist/commands/spine-authored-cert-corpus.test.js.map +1 -1
- package/dist/commands/spine-authored-materialize.d.ts +55 -0
- package/dist/commands/spine-authored-materialize.d.ts.map +1 -0
- package/dist/commands/spine-authored-materialize.js +315 -0
- package/dist/commands/spine-authored-materialize.js.map +1 -0
- package/dist/commands/spine-cert-materialize.d.ts.map +1 -1
- package/dist/commands/spine-cert-materialize.js +17 -0
- package/dist/commands/spine-cert-materialize.js.map +1 -1
- package/dist/commands/spine-cert-persist.d.ts +17 -1
- package/dist/commands/spine-cert-persist.d.ts.map +1 -1
- package/dist/commands/spine-cert-persist.js +17 -5
- package/dist/commands/spine-cert-persist.js.map +1 -1
- package/dist/commands/spine-cert-persist.test.js +60 -0
- package/dist/commands/spine-cert-persist.test.js.map +1 -1
- package/dist/commands/spine-cert-run-corpus.d.ts +18 -28
- package/dist/commands/spine-cert-run-corpus.d.ts.map +1 -1
- package/dist/commands/spine-cert-run-corpus.js +162 -21
- package/dist/commands/spine-cert-run-corpus.js.map +1 -1
- package/dist/commands/spine-derive-labels.d.ts.map +1 -1
- package/dist/commands/spine-derive-labels.js +9 -1
- package/dist/commands/spine-derive-labels.js.map +1 -1
- package/dist/commands/spine-freeze-orchestration.test.d.ts +2 -0
- package/dist/commands/spine-freeze-orchestration.test.d.ts.map +1 -0
- package/dist/commands/spine-freeze-orchestration.test.js +468 -0
- package/dist/commands/spine-freeze-orchestration.test.js.map +1 -0
- package/dist/commands/spine-freeze-split.d.ts +10 -0
- package/dist/commands/spine-freeze-split.d.ts.map +1 -0
- package/dist/commands/spine-freeze-split.js +131 -0
- package/dist/commands/spine-freeze-split.js.map +1 -0
- package/dist/commands/spine-windtunnel.d.ts +35 -0
- package/dist/commands/spine-windtunnel.d.ts.map +1 -1
- package/dist/commands/spine-windtunnel.js +47 -7
- package/dist/commands/spine-windtunnel.js.map +1 -1
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -1
- package/dist/spine-fixture-ancestry.d.ts +20 -0
- package/dist/spine-fixture-ancestry.d.ts.map +1 -0
- package/dist/spine-fixture-ancestry.js +46 -0
- package/dist/spine-fixture-ancestry.js.map +1 -0
- package/dist/spine-fixture-ancestry.test.d.ts +2 -0
- package/dist/spine-fixture-ancestry.test.d.ts.map +1 -0
- package/dist/spine-fixture-ancestry.test.js +62 -0
- package/dist/spine-fixture-ancestry.test.js.map +1 -0
- package/dist/spine-freeze-proof.d.ts +89 -0
- package/dist/spine-freeze-proof.d.ts.map +1 -0
- package/dist/spine-freeze-proof.js +237 -0
- package/dist/spine-freeze-proof.js.map +1 -0
- 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"}
|