@mmnto/cli 1.105.0 → 1.106.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.
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Prop 309 P2 degraded-mode test pack — entry E4 (induced-failure).
3
+ *
4
+ * Boundary (verbatim scope): ECL/mail poll completeness. Induced faults →
5
+ * required behavior: "basename collision · own-broadcast exclusion · glob
6
+ * failure → surfaced or LOUDLY accounted, never silent narrowing."
7
+ *
8
+ * Pack contract — every induced-failure test carries three assertions:
9
+ * 1. The accounting fires (degraded output announces itself — never output
10
+ * indistinguishable from a clean poll).
11
+ * 2. The backstop throws. `totem mail` is a READ-ONLY path, so per
12
+ * ADR-115 § 2 the requirement is a DEGRADED/UNVERIFIED-style envelope in
13
+ * rendered output — the structured `warnings` channel (rendered as
14
+ * `Warning: …` lines by `formatTextResult`) IS that envelope — not a
15
+ * nonzero exit. Where this pack pins an exit code it is pinning the
16
+ * deliberate exit-0-with-envelope contract, never blessing silence.
17
+ * 3. Recovery/control: a positive control (healthy fixture poll renders
18
+ * everything it should) AND fault-removed-returns-to-green.
19
+ *
20
+ * Framing defects:
21
+ * - mmnto-ai/totem#2462 — "silent mail-suppression class": the own-broadcast
22
+ * exclusion at mail.ts (`if (opts.includeProcessed !== true && toLower ===
23
+ * 'broadcast' && selfLower.has(senderSeat)) continue;`) assumes SELF is one
24
+ * seat; post-dirs-union the default poll resolves ALL resident seats, so a
25
+ * resident sender's broadcast is suppressed from every seat's default
26
+ * render — silently, no accounting line.
27
+ * - mmnto-ai/totem#2509 — the accounting-line gap: the default render gives
28
+ * no account of items dropped by the exclusion; the required fix is a
29
+ * rendered accounting line when suppression drops files mark-absent for
30
+ * ≥1 resident non-sender seat. NOT yet implemented — the required-behavior
31
+ * assertion below is `test.fails` with this issue ref (repo rule:
32
+ * suppressions carry a ticket-ref comment), written against the REQUIRED
33
+ * behavior so it EXECUTES every run and flips the moment the fix lands
34
+ * (vitest 4 dropped `test.fixme`; `test.fails` is the in-repo vitest's
35
+ * expected-fail primitive — green while the defect stands, loudly red
36
+ * once the assertions pass and the marker must be removed).
37
+ *
38
+ * Filesystem-driven, hermetic (fresh tmp workspace per test), same fixture
39
+ * idiom as mail.test.ts. Assertions target the structured `MailPollResult`
40
+ * (the durable hook contract); the text formatter is human-only.
41
+ */
42
+ export {};
43
+ //# sourceMappingURL=mail-degraded-e4.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-degraded-e4.test.d.ts","sourceRoot":"","sources":["../../src/commands/mail-degraded-e4.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG"}
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Prop 309 P2 degraded-mode test pack — entry E4 (induced-failure).
3
+ *
4
+ * Boundary (verbatim scope): ECL/mail poll completeness. Induced faults →
5
+ * required behavior: "basename collision · own-broadcast exclusion · glob
6
+ * failure → surfaced or LOUDLY accounted, never silent narrowing."
7
+ *
8
+ * Pack contract — every induced-failure test carries three assertions:
9
+ * 1. The accounting fires (degraded output announces itself — never output
10
+ * indistinguishable from a clean poll).
11
+ * 2. The backstop throws. `totem mail` is a READ-ONLY path, so per
12
+ * ADR-115 § 2 the requirement is a DEGRADED/UNVERIFIED-style envelope in
13
+ * rendered output — the structured `warnings` channel (rendered as
14
+ * `Warning: …` lines by `formatTextResult`) IS that envelope — not a
15
+ * nonzero exit. Where this pack pins an exit code it is pinning the
16
+ * deliberate exit-0-with-envelope contract, never blessing silence.
17
+ * 3. Recovery/control: a positive control (healthy fixture poll renders
18
+ * everything it should) AND fault-removed-returns-to-green.
19
+ *
20
+ * Framing defects:
21
+ * - mmnto-ai/totem#2462 — "silent mail-suppression class": the own-broadcast
22
+ * exclusion at mail.ts (`if (opts.includeProcessed !== true && toLower ===
23
+ * 'broadcast' && selfLower.has(senderSeat)) continue;`) assumes SELF is one
24
+ * seat; post-dirs-union the default poll resolves ALL resident seats, so a
25
+ * resident sender's broadcast is suppressed from every seat's default
26
+ * render — silently, no accounting line.
27
+ * - mmnto-ai/totem#2509 — the accounting-line gap: the default render gives
28
+ * no account of items dropped by the exclusion; the required fix is a
29
+ * rendered accounting line when suppression drops files mark-absent for
30
+ * ≥1 resident non-sender seat. NOT yet implemented — the required-behavior
31
+ * assertion below is `test.fails` with this issue ref (repo rule:
32
+ * suppressions carry a ticket-ref comment), written against the REQUIRED
33
+ * behavior so it EXECUTES every run and flips the moment the fix lands
34
+ * (vitest 4 dropped `test.fixme`; `test.fails` is the in-repo vitest's
35
+ * expected-fail primitive — green while the defect stands, loudly red
36
+ * once the assertions pass and the marker must be removed).
37
+ *
38
+ * Filesystem-driven, hermetic (fresh tmp workspace per test), same fixture
39
+ * idiom as mail.test.ts. Assertions target the structured `MailPollResult`
40
+ * (the durable hook contract); the text formatter is human-only.
41
+ */
42
+ import * as os from 'node:os';
43
+ import * as path from 'node:path';
44
+ import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest';
45
+ // Mock node:fs so spyOn works in ESM — same pattern as core's session-id.test.ts.
46
+ // Passthrough spread: every call behaves identically to real fs until a test
47
+ // installs a targeted spy (and restores it in `finally`).
48
+ vi.mock('node:fs', async () => {
49
+ const actual = await vi.importActual('node:fs');
50
+ return { ...actual, default: actual };
51
+ });
52
+ import * as fs from 'node:fs';
53
+ import { cleanTmpDir } from '../test-utils.js';
54
+ import { pollMail, resolveMailExitCode } from './mail.js';
55
+ // ─── Fixture helpers ────────────────────────────────────
56
+ let tmpRoot;
57
+ let workspace;
58
+ function mkDir(p) {
59
+ fs.mkdirSync(p, { recursive: true });
60
+ return p;
61
+ }
62
+ /** `<repo>/.totem/orchestration/<senderAgent>/outbox/<file>.md` — same shape as mail.test.ts. */
63
+ function writeOutbox(repo, senderAgent, files) {
64
+ const outbox = path.join(workspace, repo, '.totem', 'orchestration', senderAgent, 'outbox');
65
+ mkDir(outbox);
66
+ for (const f of files) {
67
+ const content = [
68
+ '---',
69
+ `from: ${f.from ?? senderAgent}`,
70
+ `to: ${f.to}`,
71
+ `date: ${f.date ?? '2026-07-20T1700Z'}`,
72
+ `subject: ${f.subject ?? '(no subject)'}`,
73
+ '---',
74
+ '',
75
+ 'Body text.',
76
+ '',
77
+ ].join('\n');
78
+ fs.writeFileSync(path.join(outbox, f.name), content, 'utf-8');
79
+ }
80
+ }
81
+ /**
82
+ * The `totem` basename pre-resolves to [totem-claude, totem-gemini] via the
83
+ * cohort map; materializing the seat dirs puts resolution on the
84
+ * post-dirs-union DEFAULT path (source 'dirs', no env override) — the exact
85
+ * multi-seat condition of mmnto-ai/totem#2462: the default poll resolves ALL
86
+ * resident seats, and the own-broadcast exclusion assumes SELF is one seat.
87
+ */
88
+ function selfRepoRootWithSeats(seats) {
89
+ const root = mkDir(path.join(workspace, 'totem'));
90
+ // The #2312 walk-up needs a `.totem` marker to anchor the repo root on.
91
+ const orchDir = mkDir(path.join(root, '.totem', 'orchestration'));
92
+ for (const seat of seats)
93
+ mkDir(path.join(orchDir, seat));
94
+ return root;
95
+ }
96
+ beforeEach(() => {
97
+ tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-mail-e4-'));
98
+ workspace = mkDir(path.join(tmpRoot, 'workspace'));
99
+ });
100
+ afterEach(() => {
101
+ cleanTmpDir(tmpRoot);
102
+ });
103
+ // ─── Fault 1: basename collision ────────────────────────
104
+ // Two seats converge on one addressed-inbound basename. Required: surface
105
+ // both or loudly account — never silently narrow/merge/drop one.
106
+ describe('E4 fault 1 — basename collision is surfaced AND loudly accounted', () => {
107
+ const NAME = '2026-07-20T1717Z-totem-claude-round-reply.md';
108
+ it('both colliding dispatches surface AND the collision warning names every seat path (regression lock)', () => {
109
+ // Cross-repo variant: distinct outbox-owner seats in distinct repos.
110
+ writeOutbox('totem-strategy', 'strategy-claude', [{ name: NAME, to: 'totem-claude' }]);
111
+ writeOutbox('liquid-city', 'lc-claude', [{ name: NAME, to: 'totem-claude' }]);
112
+ const result = pollMail({ repoRoot: selfRepoRootWithSeats([]), workspace, env: {} });
113
+ // Pack 1 — the accounting fires: the degraded envelope announces itself.
114
+ const collisionWarnings = result.warnings.filter((w) => w.startsWith('cross-sender basename collision'));
115
+ expect(collisionWarnings).toHaveLength(1);
116
+ expect(collisionWarnings[0]).toContain(NAME);
117
+ expect(collisionWarnings[0]).toContain('totem-strategy/strategy-claude');
118
+ expect(collisionWarnings[0]).toContain('liquid-city/lc-claude');
119
+ // Pack 2 — backstop for a read-only path (ADR-115 § 2): no silent
120
+ // narrowing — BOTH dispatches render; the warnings envelope (not an exit
121
+ // code) is the loud announcement. Load-bearing downstream: the same
122
+ // warnings channel reds the `ecl-gc --compact` A2.2 completeness gate
123
+ // (`warnings.length === 0` arm, mmnto-ai/totem#2309).
124
+ expect(result.mail).toHaveLength(2);
125
+ expect(result.mail.map((m) => m.file)).toEqual([NAME, NAME]);
126
+ });
127
+ it('positive control + fault-removed: distinct basenames render clean with zero warnings', () => {
128
+ writeOutbox('totem-strategy', 'strategy-claude', [
129
+ { name: '2026-07-20T1717Z-totem-claude-round-reply.md', to: 'totem-claude' },
130
+ ]);
131
+ writeOutbox('liquid-city', 'lc-claude', [
132
+ { name: '2026-07-20T1718Z-totem-claude-lane-note.md', to: 'totem-claude' },
133
+ ]);
134
+ const result = pollMail({ repoRoot: selfRepoRootWithSeats([]), workspace, env: {} });
135
+ expect(result.mail).toHaveLength(2);
136
+ expect(result.warnings).toEqual([]);
137
+ expect(resolveMailExitCode(result)).toBe(0);
138
+ });
139
+ });
140
+ // ─── Fault 2: own-broadcast exclusion ───────────────────
141
+ // A broadcast from a RESIDENT sender seat, unread for a non-sender resident
142
+ // seat. Required: render it, or print an accounting line naming the
143
+ // suppression (mmnto-ai/totem#2509 — unimplemented today).
144
+ describe('E4 fault 2 — own-broadcast suppression is never silent (mmnto-ai/totem#2462, #2509)', () => {
145
+ const BCAST = '2026-07-20T2326Z-broadcast-cohort-note.md';
146
+ const RESIDENT_SEATS = ['totem-claude', 'totem-gemini'];
147
+ /** Default-resolution poll (no env override) over the two-seat fixture. */
148
+ function pollTwoSeat(opts = {}) {
149
+ return pollMail({
150
+ repoRoot: selfRepoRootWithSeats(RESIDENT_SEATS),
151
+ workspace,
152
+ env: {},
153
+ ...opts,
154
+ });
155
+ }
156
+ // RED ROW — required behavior, unimplemented. `test.fails` carries the
157
+ // ticket ref per the repo rule (suppressions need a ticket-ref comment);
158
+ // the body is written against the REQUIRED behavior so it EXECUTES every
159
+ // run (an expected failure while #2509 stands) and flips the run loudly
160
+ // red the moment the fix lands and the marker must be removed. (vitest 4
161
+ // dropped `test.fixme`; `test.fails` is this repo's vitest's expected-fail
162
+ // primitive.)
163
+ //
164
+ // Today's actual behavior (regression-documented, NOT locked): mail.ts's
165
+ // own-broadcast exclusion hits `continue` with no warning and no accounting
166
+ // line, so the default two-seat poll returns `mail: []`, `warnings: []` —
167
+ // rendered as "No unread mail addressed to totem-claude, totem-gemini or
168
+ // broadcast." That is the #2462 silent-suppression class: the dispatch is
169
+ // mark-absent for the resident NON-sender seat (totem-gemini), yet the
170
+ // render is indistinguishable from a genuinely clean inbox.
171
+ test.fails('REQUIRED (mmnto-ai/totem#2509): a resident-sender broadcast unread for a non-sender seat is rendered OR loudly accounted — never silently dropped', () => {
172
+ writeOutbox('totem', 'totem-claude', [
173
+ { name: BCAST, to: 'broadcast', subject: 'cohort note' },
174
+ ]);
175
+ // No sanity assertion in this body, deliberately: `test.fails` is
176
+ // satisfied by ANY failure, so an extra assertion here would let an
177
+ // unrelated seat-resolution regression keep the marker green without
178
+ // the #2509 assertion ever running (CR round-1 catch). The two-seat
179
+ // resolution is asserted by the positive control below on the same
180
+ // fixture shape, where a regression fails loudly as a real failure.
181
+ const result = pollTwoSeat();
182
+ // Pack 1 + 2 — accounting fires / degraded envelope: the poll must
183
+ // either render the dispatch (to the non-sender seat) or name the
184
+ // suppression in the warnings channel — the read-only path's
185
+ // DEGRADED-style announcement per ADR-115 § 2. The exact accounting
186
+ // wording is #2509's to define; the assertion keys on the suppressed
187
+ // basename so any honest accounting line satisfies it.
188
+ const renderedToNonSender = result.mail.some((m) => m.file === BCAST);
189
+ const accounted = result.warnings.some((w) => w.includes(BCAST));
190
+ expect(renderedToNonSender || accounted).toBe(true);
191
+ });
192
+ it('positive control: a NON-resident sender broadcast surfaces to the same two-seat default poll', () => {
193
+ // Healthy-fixture control: the same poll renders everything it should
194
+ // when the sender is NOT a resident seat (the exclusion arm cannot fire).
195
+ writeOutbox('totem-strategy', 'strategy-claude', [
196
+ { name: BCAST, to: 'broadcast', subject: 'theirs' },
197
+ ]);
198
+ const result = pollTwoSeat();
199
+ expect(result.selfAgents.agents).toEqual(['totem-claude', 'totem-gemini']);
200
+ expect(result.mail).toHaveLength(1);
201
+ expect(result.mail[0].file).toBe(BCAST);
202
+ expect(result.warnings).toEqual([]);
203
+ });
204
+ it('fault-removed control: removing the sender from the resident set surfaces the identical broadcast', () => {
205
+ // Same dispatch, same fixture — but SELF is restricted to the non-sender
206
+ // seat, so the exclusion arm (`selfLower.has(senderSeat)`) cannot fire.
207
+ // Proves the dispatch is deliverable and the suppression is specifically
208
+ // the resident-sender arm, not a fixture artifact.
209
+ writeOutbox('totem', 'totem-claude', [{ name: BCAST, to: 'broadcast' }]);
210
+ const result = pollMail({
211
+ repoRoot: selfRepoRootWithSeats(RESIDENT_SEATS),
212
+ workspace,
213
+ env: { TOTEM_SELF_AGENT: 'totem-gemini' },
214
+ });
215
+ expect(result.mail).toHaveLength(1);
216
+ expect(result.mail[0].file).toBe(BCAST);
217
+ });
218
+ it('over-correction guard: a single-seat poll where SELF IS the sender still excludes its own broadcast (mmnto-ai/totem#2364, intended)', () => {
219
+ // The #2509 fix must not over-correct into surfacing a seat's OWN
220
+ // broadcast back to the sender itself — that exclusion is the shipped
221
+ // #2364 behavior. Lock it so the accounting-line fix stays scoped to
222
+ // non-sender seats.
223
+ writeOutbox('totem', 'totem-claude', [{ name: BCAST, to: 'broadcast' }]);
224
+ const result = pollMail({
225
+ repoRoot: selfRepoRootWithSeats(RESIDENT_SEATS),
226
+ workspace,
227
+ env: { TOTEM_SELF_AGENT: 'totem-claude' },
228
+ });
229
+ expect(result.mail).toEqual([]);
230
+ });
231
+ });
232
+ // ─── Fault 3: glob/scan failure ─────────────────────────
233
+ // The scan resolves zero repos/dirs while dispatches exist on disk.
234
+ // Required: refuse a silently-clean inbox over an incomplete scan — announce
235
+ // the degradation.
236
+ describe('E4 fault 3 — scan failure announces itself, never a silent clean inbox', () => {
237
+ const MAIL_A = '2026-07-20T1800Z-totem-claude-from-strategy.md';
238
+ const MAIL_B = '2026-07-20T1801Z-totem-claude-from-lc.md';
239
+ function writeHealthyFixture() {
240
+ writeOutbox('totem-strategy', 'strategy-claude', [{ name: MAIL_A, to: 'totem-claude' }]);
241
+ writeOutbox('liquid-city', 'lc-claude', [{ name: MAIL_B, to: 'totem-claude' }]);
242
+ }
243
+ it('workspace scan root unresolvable (ENOTDIR) with dispatches on disk → degraded envelope, empty mail, exit 0', () => {
244
+ writeHealthyFixture();
245
+ // Positive control (pack 3): the healthy fixture renders everything.
246
+ const healthy = pollMail({ repoRoot: selfRepoRootWithSeats([]), workspace, env: {} });
247
+ expect(healthy.mail).toHaveLength(2);
248
+ expect(healthy.warnings).toEqual([]);
249
+ // Induce: point the scan root at a FILE — fs.existsSync passes,
250
+ // readdirSync throws ENOTDIR, enumerateOutboxes resolves ZERO repos while
251
+ // the dispatches exist on disk under the real workspace. No mocks: the
252
+ // real errno drives the real catch arm.
253
+ const brokenRoot = path.join(tmpRoot, 'workspace-as-file');
254
+ fs.writeFileSync(brokenRoot, 'not a directory', 'utf-8');
255
+ const degraded = pollMail({
256
+ repoRoot: selfRepoRootWithSeats([]),
257
+ workspace: brokenRoot,
258
+ env: {},
259
+ });
260
+ // Pack 1 — the accounting fires: the scan failure is named in the
261
+ // warnings channel, so the rendered output carries `Warning: workspace
262
+ // scan failed: …` lines and is distinguishable from a clean poll.
263
+ expect(degraded.warnings.some((w) => w.startsWith('workspace scan failed'))).toBe(true);
264
+ // Pack 2 — backstop for a read-only path (ADR-115 § 2): the envelope is
265
+ // the announcement; the exit code stays 0 by the deliberate
266
+ // `resolveMailExitCode` contract (only an UNRESOLVED self is exit 2 — a
267
+ // scan failure still resolved self, and the warnings channel is the
268
+ // DEGRADED envelope). Pinning exit 0 here pins that contract, not the
269
+ // silence. The poll also asserts nothing it cannot back: no mail entries
270
+ // are fabricated over the incomplete scan.
271
+ expect(degraded.mail).toEqual([]);
272
+ expect(degraded.scanned).toBe(0);
273
+ expect(resolveMailExitCode(degraded)).toBe(0);
274
+ // Fault-removed-returns-to-green (pack 3): repoint at the healthy scan
275
+ // root and the same fixture polls clean.
276
+ const recovered = pollMail({ repoRoot: selfRepoRootWithSeats([]), workspace, env: {} });
277
+ expect(recovered.mail).toHaveLength(2);
278
+ expect(recovered.warnings).toEqual([]);
279
+ });
280
+ it('one repo orchestration scan fails (EACCES) → failure named, healthy sibling still surfaces; fault-removed returns to green', () => {
281
+ writeHealthyFixture();
282
+ // Induce: targeted readdirSync failure on ONE repo's orchestration dir —
283
+ // the exact path mail.ts scans (`<workspace>/totem-strategy/.totem/
284
+ // orchestration`). Path-conditional passthrough keeps every other fs call
285
+ // real. (fs spy pattern: core session-id.test.ts.)
286
+ const brokenMarker = path.join('totem-strategy', '.totem', 'orchestration');
287
+ const realReaddirSync = fs.readdirSync;
288
+ const spy = vi.spyOn(fs, 'readdirSync').mockImplementation((target, options) => {
289
+ if (String(target).endsWith(brokenMarker)) {
290
+ const err = new Error(`EACCES: permission denied, scandir '${String(target)}'`);
291
+ err.code = 'EACCES';
292
+ throw err;
293
+ }
294
+ return realReaddirSync(target, options);
295
+ });
296
+ try {
297
+ const degraded = pollMail({ repoRoot: selfRepoRootWithSeats([]), workspace, env: {} });
298
+ // Pack 1 — the accounting fires: the failed repo is NAMED.
299
+ expect(degraded.warnings.some((w) => w.startsWith('orchestration scan failed (totem-strategy)'))).toBe(true);
300
+ // Pack 2 — backstop: skip-don't-abort, never silent narrowing — the
301
+ // healthy sibling repo's dispatch still surfaces, and the failed repo's
302
+ // absence is announced (above) rather than folded into a fake-clean
303
+ // inbox. Exit stays 0 per the read-only degraded-envelope contract.
304
+ expect(degraded.mail.map((m) => m.file)).toEqual([MAIL_B]);
305
+ expect(resolveMailExitCode(degraded)).toBe(0);
306
+ }
307
+ finally {
308
+ spy.mockRestore();
309
+ }
310
+ // Pack 3 — fault-removed-returns-to-green: with the scan healthy again,
311
+ // BOTH dispatches surface with zero warnings.
312
+ const recovered = pollMail({ repoRoot: selfRepoRootWithSeats([]), workspace, env: {} });
313
+ expect(recovered.mail.map((m) => m.file).sort()).toEqual([MAIL_A, MAIL_B].sort());
314
+ expect(recovered.warnings).toEqual([]);
315
+ });
316
+ });
317
+ //# sourceMappingURL=mail-degraded-e4.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-degraded-e4.test.js","sourceRoot":"","sources":["../../src/commands/mail-degraded-e4.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE/E,kFAAkF;AAClF,6EAA6E;AAC7E,0DAA0D;AAC1D,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;IAC5B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,YAAY,CAA2B,SAAS,CAAC,CAAC;IAC1E,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAE1D,2DAA2D;AAE3D,IAAI,OAAe,CAAC;AACpB,IAAI,SAAiB,CAAC;AAEtB,SAAS,KAAK,CAAC,CAAS;IACtB,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,CAAC;AACX,CAAC;AAUD,iGAAiG;AACjG,SAAS,WAAW,CAAC,IAAY,EAAE,WAAmB,EAAE,KAAmB;IACzE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC5F,KAAK,CAAC,MAAM,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG;YACd,KAAK;YACL,SAAS,CAAC,CAAC,IAAI,IAAI,WAAW,EAAE;YAChC,OAAO,CAAC,CAAC,EAAE,EAAE;YACb,SAAS,CAAC,CAAC,IAAI,IAAI,kBAAkB,EAAE;YACvC,YAAY,CAAC,CAAC,OAAO,IAAI,cAAc,EAAE;YACzC,KAAK;YACL,EAAE;YACF,YAAY;YACZ,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,qBAAqB,CAAC,KAAe;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IAClD,wEAAwE;IACxE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;IAClE,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,UAAU,CAAC,GAAG,EAAE;IACd,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACnE,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,GAAG,EAAE;IACb,WAAW,CAAC,OAAO,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,2DAA2D;AAC3D,0EAA0E;AAC1E,iEAAiE;AAEjE,QAAQ,CAAC,kEAAkE,EAAE,GAAG,EAAE;IAChF,MAAM,IAAI,GAAG,8CAA8C,CAAC;IAE5D,EAAE,CAAC,qGAAqG,EAAE,GAAG,EAAE;QAC7G,qEAAqE;QACrE,WAAW,CAAC,gBAAgB,EAAE,iBAAiB,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;QACvF,WAAW,CAAC,aAAa,EAAE,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;QAE9E,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QAErF,yEAAyE;QACzE,MAAM,iBAAiB,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACrD,CAAC,CAAC,UAAU,CAAC,iCAAiC,CAAC,CAChD,CAAC;QACF,MAAM,CAAC,iBAAiB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;QACzE,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QAChE,kEAAkE;QAClE,yEAAyE;QACzE,oEAAoE;QACpE,sEAAsE;QACtE,sDAAsD;QACtD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,GAAG,EAAE;QAC9F,WAAW,CAAC,gBAAgB,EAAE,iBAAiB,EAAE;YAC/C,EAAE,IAAI,EAAE,8CAA8C,EAAE,EAAE,EAAE,cAAc,EAAE;SAC7E,CAAC,CAAC;QACH,WAAW,CAAC,aAAa,EAAE,WAAW,EAAE;YACtC,EAAE,IAAI,EAAE,4CAA4C,EAAE,EAAE,EAAE,cAAc,EAAE;SAC3E,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QAErF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACpC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,2DAA2D;AAC3D,4EAA4E;AAC5E,oEAAoE;AACpE,2DAA2D;AAE3D,QAAQ,CAAC,qFAAqF,EAAE,GAAG,EAAE;IACnG,MAAM,KAAK,GAAG,2CAA2C,CAAC;IAC1D,MAAM,cAAc,GAAG,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;IAExD,2EAA2E;IAC3E,SAAS,WAAW,CAAC,OAAuC,EAAE;QAC5D,OAAO,QAAQ,CAAC;YACd,QAAQ,EAAE,qBAAqB,CAAC,cAAc,CAAC;YAC/C,SAAS;YACT,GAAG,EAAE,EAAE;YACP,GAAG,IAAI;SACR,CAAC,CAAC;IACL,CAAC;IAED,uEAAuE;IACvE,yEAAyE;IACzE,yEAAyE;IACzE,wEAAwE;IACxE,yEAAyE;IACzE,2EAA2E;IAC3E,cAAc;IACd,EAAE;IACF,yEAAyE;IACzE,4EAA4E;IAC5E,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,uEAAuE;IACvE,4DAA4D;IAC5D,IAAI,CAAC,KAAK,CACR,mJAAmJ,EACnJ,GAAG,EAAE;QACH,WAAW,CAAC,OAAO,EAAE,cAAc,EAAE;YACnC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE;SACzD,CAAC,CAAC;QACH,kEAAkE;QAClE,oEAAoE;QACpE,qEAAqE;QACrE,oEAAoE;QACpE,mEAAmE;QACnE,oEAAoE;QACpE,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;QAE7B,mEAAmE;QACnE,kEAAkE;QAClE,6DAA6D;QAC7D,oEAAoE;QACpE,qEAAqE;QACrE,uDAAuD;QACvD,MAAM,mBAAmB,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;QACtE,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACjE,MAAM,CAAC,mBAAmB,IAAI,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC,CACF,CAAC;IAEF,EAAE,CAAC,8FAA8F,EAAE,GAAG,EAAE;QACtG,sEAAsE;QACtE,0EAA0E;QAC1E,WAAW,CAAC,gBAAgB,EAAE,iBAAiB,EAAE;YAC/C,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE;SACpD,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mGAAmG,EAAE,GAAG,EAAE;QAC3G,yEAAyE;QACzE,wEAAwE;QACxE,yEAAyE;QACzE,mDAAmD;QACnD,WAAW,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QACzE,MAAM,MAAM,GAAG,QAAQ,CAAC;YACtB,QAAQ,EAAE,qBAAqB,CAAC,cAAc,CAAC;YAC/C,SAAS;YACT,GAAG,EAAE,EAAE,gBAAgB,EAAE,cAAc,EAAE;SAC1C,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qIAAqI,EAAE,GAAG,EAAE;QAC7I,kEAAkE;QAClE,sEAAsE;QACtE,qEAAqE;QACrE,oBAAoB;QACpB,WAAW,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QACzE,MAAM,MAAM,GAAG,QAAQ,CAAC;YACtB,QAAQ,EAAE,qBAAqB,CAAC,cAAc,CAAC;YAC/C,SAAS;YACT,GAAG,EAAE,EAAE,gBAAgB,EAAE,cAAc,EAAE;SAC1C,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,2DAA2D;AAC3D,oEAAoE;AACpE,6EAA6E;AAC7E,mBAAmB;AAEnB,QAAQ,CAAC,wEAAwE,EAAE,GAAG,EAAE;IACtF,MAAM,MAAM,GAAG,gDAAgD,CAAC;IAChE,MAAM,MAAM,GAAG,0CAA0C,CAAC;IAE1D,SAAS,mBAAmB;QAC1B,WAAW,CAAC,gBAAgB,EAAE,iBAAiB,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;QACzF,WAAW,CAAC,aAAa,EAAE,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;IAClF,CAAC;IAED,EAAE,CAAC,4GAA4G,EAAE,GAAG,EAAE;QACpH,mBAAmB,EAAE,CAAC;QACtB,qEAAqE;QACrE,MAAM,OAAO,GAAG,QAAQ,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QACtF,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAErC,gEAAgE;QAChE,0EAA0E;QAC1E,uEAAuE;QACvE,wCAAwC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QAC3D,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,iBAAiB,EAAE,OAAO,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,QAAQ,CAAC;YACxB,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC;YACnC,SAAS,EAAE,UAAU;YACrB,GAAG,EAAE,EAAE;SACR,CAAC,CAAC;QAEH,kEAAkE;QAClE,uEAAuE;QACvE,kEAAkE;QAClE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxF,wEAAwE;QACxE,4DAA4D;QAC5D,wEAAwE;QACxE,oEAAoE;QACpE,sEAAsE;QACtE,yEAAyE;QACzE,2CAA2C;QAC3C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAClC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE9C,uEAAuE;QACvE,yCAAyC;QACzC,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QACxF,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4HAA4H,EAAE,GAAG,EAAE;QACpI,mBAAmB,EAAE,CAAC;QACtB,yEAAyE;QACzE,oEAAoE;QACpE,0EAA0E;QAC1E,mDAAmD;QACnD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAC5E,MAAM,eAAe,GAAG,EAAE,CAAC,WAAW,CAAC;QACvC,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,kBAAkB,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;YAC7E,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC1C,MAAM,GAAG,GAAG,IAAI,KAAK,CACnB,uCAAuC,MAAM,CAAC,MAAM,CAAC,GAAG,CAChC,CAAC;gBAC3B,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;gBACpB,MAAM,GAAG,CAAC;YACZ,CAAC;YACD,OAAO,eAAe,CAAC,MAAM,EAAE,OAAgB,CAAU,CAAC;QAC5D,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,QAAQ,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;YACvF,2DAA2D;YAC3D,MAAM,CACJ,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,4CAA4C,CAAC,CAAC,CAC1F,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,oEAAoE;YACpE,wEAAwE;YACxE,oEAAoE;YACpE,oEAAoE;YACpE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YAC3D,MAAM,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC;gBAAS,CAAC;YACT,GAAG,CAAC,WAAW,EAAE,CAAC;QACpB,CAAC;QAED,wEAAwE;QACxE,8CAA8C;QAC9C,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QACxF,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAClF,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"run-compiled-rules.d.ts","sourceRoot":"","sources":["../../src/commands/run-compiled-rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAEZ,kBAAkB,EAClB,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACtC,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAC/B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC;;;;OAIG;IACH,gBAAgB,EAAE,sBAAsB,EAAE,CAAC;CAC5C;AA6FD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,sBAAsB,CAAC,CA8nBjC"}
1
+ {"version":3,"file":"run-compiled-rules.d.ts","sourceRoot":"","sources":["../../src/commands/run-compiled-rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAEZ,kBAAkB,EAClB,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACtC,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAC/B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC;;;;OAIG;IACH,gBAAgB,EAAE,sBAAsB,EAAE,CAAC;CAC5C;AA6OD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,sBAAsB,CAAC,CAwoBjC"}
@@ -82,6 +82,130 @@ async function countLessonCorpusFiles(config, projectRoot) {
82
82
  }
83
83
  return matched.size;
84
84
  }
85
+ // ─── Target-mismatch derivation (mmnto-ai/totem-strategy#971, Prop 309 Class 7) ─
86
+ /**
87
+ * Hard-error when a loaded AST rule can never execute because NONE of its
88
+ * declared `fileGlobs` targets an extension the engine has a registered
89
+ * Tree-sitter language for (mmnto-ai/totem-strategy#971, Prop 309 Class 7).
90
+ *
91
+ * The failure this closes: `resolveAstGrepLangs` (ast-grep-query.ts) silently
92
+ * falls back to `Lang.Tsx` when no glob resolves, so a target-mismatched rule
93
+ * looks healthy at load. It then detonates at RUN time — `rule-engine.ts`'s
94
+ * language check throws `TotemParseError` the first time a diff happens to
95
+ * contain a file the rule's globs match, and that throw escapes the per-file
96
+ * loop and aborts the WHOLE lint. The observed specimen was an `ast-grep` rule
97
+ * whose four globs all pointed at `.json` files, which no language registration
98
+ * covers. Diff-dependent detonation is the worst property: the gate is green on
99
+ * every run until the one run where it isn't.
100
+ *
101
+ * The guard converts that into a deterministic load-time failure. It runs on
102
+ * EVERY invocation regardless of the diff, and — unlike the corpus-bearing gate
103
+ * above — it is deliberately NOT gated on `options.config`. That opt-out exists
104
+ * because zero-rules is genuinely ambiguous (empty-corpus repo vs disarmed gate)
105
+ * and the config is the only discriminator. Target-mismatch is unambiguous from
106
+ * the manifest alone: no discriminator is needed, so there is nothing to opt out
107
+ * of and `shield estimate` gets the same protection. The one escape is the
108
+ * pre-existing `--ast-parse-mode lenient` operator flag (see the branch below),
109
+ * which is opt-in and leaves the strict default fail-closed.
110
+ *
111
+ * Scope matches the abort seam exactly. `rule-engine.ts` searches the
112
+ * `allAstRules` union (Tree-sitter `'ast'` ∪ `'ast-grep'`) for a rule whose
113
+ * globs claim the unparseable file, so BOTH engines are validated here.
114
+ *
115
+ * Two shapes stay permitted:
116
+ *
117
+ * - **Globless rules** (`fileGlobs` absent or empty). The documented
118
+ * `Lang.Tsx` fallback is their intended behavior — many pre-1.16 rules ship
119
+ * unscoped — and `rule-engine.ts` only throws for a rule with non-empty
120
+ * `fileGlobs`, so a globless rule cannot arm the landmine.
121
+ * - **Extensionless positive globs** — a recursive `src/**`, a bare-star
122
+ * leaf, or a brace-set leaf like `.{ts,json}` (glob literals are omitted
123
+ * here because a star-slash pair would close this comment).
124
+ * `TRAILING_EXT_RE` extracts nothing from these, but at run time they can
125
+ * and do match `.ts` files and dispatch correctly. Absence of a trailing
126
+ * extension is absence of evidence, not proof of mismatch — treating it as
127
+ * unresolvable would hard-error legitimately broad rules. A rule is only
128
+ * condemned when EVERY positive glob names a concrete extension and NOT ONE
129
+ * of those extensions resolves.
130
+ *
131
+ * Lifecycle status needs no filtering here: `loadCompiledRules` has already
132
+ * dropped `archived` / `untested-against-codebase` / `pending-verification`
133
+ * (compiler.ts), so an archived specimen never reaches this function — which is
134
+ * exactly why archiving is the offered remedy.
135
+ *
136
+ * Pack-contributed languages are honored: CLI commands call `bootstrapEngine`
137
+ * (→ `loadInstalledPacks`) before this point, so a pack that registers `.rs`
138
+ * has already populated the registry when the check reads it.
139
+ */
140
+ async function assertRulesCanTargetTheirGlobs(rules, astParseMode, onWarn) {
141
+ const { extensionToLanguage, registeredExtensions, sanitizeForTerminal, TotemError, TRAILING_EXT_RE, } = await import('@mmnto/totem');
142
+ const offenders = [];
143
+ for (const rule of rules) {
144
+ if (rule.engine !== 'ast-grep' && rule.engine !== 'ast')
145
+ continue;
146
+ const globs = rule.fileGlobs;
147
+ if (!globs || globs.length === 0)
148
+ continue;
149
+ // Negations describe files the rule must NOT match, so they never carry a
150
+ // dispatch target — same skip `resolveAstGrepLangs` performs.
151
+ const positive = globs.filter((g) => typeof g === 'string' && !g.startsWith('!'));
152
+ // An all-negation glob list matches every file (see the zero-match
153
+ // derivation below, where `positive.length === 0` matches unconditionally),
154
+ // so it necessarily reaches resolvable files. Permitted.
155
+ if (positive.length === 0)
156
+ continue;
157
+ const extensions = [];
158
+ let resolvable = false;
159
+ for (const glob of positive) {
160
+ const match = glob.match(TRAILING_EXT_RE);
161
+ if (!match) {
162
+ // Extensionless positive glob — unprovable, therefore permitted.
163
+ resolvable = true;
164
+ break;
165
+ }
166
+ const ext = `.${match[1].toLowerCase()}`;
167
+ if (extensionToLanguage(ext)) {
168
+ resolvable = true;
169
+ break;
170
+ }
171
+ if (!extensions.includes(ext))
172
+ extensions.push(ext);
173
+ }
174
+ if (!resolvable)
175
+ offenders.push({ rule, globs: positive, extensions });
176
+ }
177
+ if (offenders.length === 0)
178
+ return;
179
+ // Deterministic detail line per offender — corpus order, every field the
180
+ // operator needs to act without opening the manifest.
181
+ const detail = offenders
182
+ .map((o) => `'${o.rule.lessonHash}' (${o.rule.lessonHeading}) engine '${o.rule.engine}' fileGlobs [${o.globs.join(', ')}] → unregistered extension(s) ${o.extensions.join(', ')}`)
183
+ .join('; ');
184
+ // Manifest-derived fields (lessonHash, lessonHeading, fileGlobs) are
185
+ // repo-controlled text reaching operator terminals and CI logs — sanitize the
186
+ // whole rendered summary so a corrupted or hostile manifest cannot inject
187
+ // control sequences through the guard's own report. The remedy line is
188
+ // engine-derived only and needs no sanitizing.
189
+ const summary = sanitizeForTerminal(`${offenders.length} active AST rule(s) declare fileGlobs whose extensions have no registered Tree-sitter language, so they can never match and instead abort the entire lint on first contact with a file they claim — ${detail}.`);
190
+ // Three remedies, deliberately un-ranked: the manifest alone cannot tell a
191
+ // permanently-broken rule (`.json`, which no pack will ever register) from an
192
+ // incomplete environment (`.rs`, which a pack provides but which has not been
193
+ // installed/synced here). Leading with "archive it" would misdiagnose the
194
+ // second case, which `rule-engine.ts`'s STALE_MANIFEST path handles well.
195
+ const remedy = `Install or sync the pack that provides the language ('totem sync --packs-only'), archive each rule (set status: 'archived'), or correct its fileGlobs/engine so at least one positive glob targets a registered extension. Registered now: ${registeredExtensions().join(', ')}.`;
196
+ if (astParseMode === 'lenient') {
197
+ // Honor the operator escape (mmnto-ai/totem#1982). `--ast-parse-mode lenient`
198
+ // already means "an AST language the engine cannot parse degrades this run
199
+ // instead of failing it", and a target-mismatched rule is exactly that class
200
+ // — it simply fails at load rather than at dispatch. Without this branch the
201
+ // guard would REMOVE the only escape for a repo whose rules target a pack
202
+ // language it has not installed, leaving the operator no way to lint at all.
203
+ // The rules stay inert either way, so lenient loses no enforcement it had.
204
+ onWarn(`AST rules target-mismatched (--ast-parse-mode lenient): ${summary} ${remedy}`);
205
+ return;
206
+ }
207
+ throw new TotemError('LINT_LESSONS_FAILED', `Enforcement disarmed: ${summary}`, remedy);
208
+ }
85
209
  // ─── Core logic ─────────────────────────────────────
86
210
  /**
87
211
  * Compiled-rules execution engine used by `totem lint`. Loads rules, extracts additions, enriches
@@ -163,6 +287,17 @@ export async function runCompiledRules(options) {
163
287
  log.info(tag, `No compiled rules at ${totemDir}/${COMPILED_RULES_FILE} — skipping (empty-corpus repo). Run 'totem lesson compile' once you have lessons.`);
164
288
  return emptyResult;
165
289
  }
290
+ // mmnto-ai/totem#1982. Resolved here rather than at the AST dispatch site
291
+ // because the rule-load guard below shares the mode (CLI flag > env > strict).
292
+ const effectiveAstParseMode = astParseMode ??
293
+ // totem-context: reading Node's process.env (cleaned by the runtime), not parsing a custom .env file; CRLF/quote-stripping rule doesn't apply.
294
+ (process.env['TOTEM_LINT_AST_PARSE_MODE'] === 'lenient' ? 'lenient' : 'strict');
295
+ // Rule-LOAD validation (mmnto-ai/totem-strategy#971, Prop 309 Class 7). Runs
296
+ // before any diff is touched so a rule whose engine cannot process ANY of its
297
+ // declared globs fails on every run, not on the unlucky run whose diff happens
298
+ // to contain a file it claims. Ordered AFTER the corpus-bearing block so a
299
+ // missing/unloadable manifest still reports its own (more fundamental) cause.
300
+ await assertRulesCanTargetTheirGlobs(rules, effectiveAstParseMode, (msg) => log.warn(tag, msg));
166
301
  log.info(tag, `Running ${rules.length} rules (zero LLM)...`);
167
302
  // Extract additions, exclude compiled rules file, export targets, and binary files
168
303
  const BINARY_EXTENSIONS = new Set([
@@ -297,11 +432,8 @@ export async function runCompiledRules(options) {
297
432
  const astRules = rules.filter((r) => r.engine === 'ast' || r.engine === 'ast-grep');
298
433
  let astViolations = [];
299
434
  const astParseFailures = [];
300
- // mmnto-ai/totem#1982. Resolve effective mode with env override (matches
301
- // the operator escape pattern: CLI flag > env var > default 'strict').
302
- const effectiveAstParseMode = astParseMode ??
303
- // totem-context: reading Node's process.env (cleaned by the runtime), not parsing a custom .env file; CRLF/quote-stripping rule doesn't apply.
304
- (process.env['TOTEM_LINT_AST_PARSE_MODE'] === 'lenient' ? 'lenient' : 'strict');
435
+ // `effectiveAstParseMode` (mmnto-ai/totem#1982) is resolved above the rule-load
436
+ // guard, which shares the same operator escape.
305
437
  if (astRules.length > 0) {
306
438
  log.dim(tag, `Running ${astRules.length} AST rule(s)...`);
307
439
  try {