@mmnto/cli 1.105.0 → 1.107.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 (43) hide show
  1. package/dist/commands/doctor-qbd.d.ts +40 -0
  2. package/dist/commands/doctor-qbd.d.ts.map +1 -0
  3. package/dist/commands/doctor-qbd.js +156 -0
  4. package/dist/commands/doctor-qbd.js.map +1 -0
  5. package/dist/commands/doctor-qbd.test.d.ts +13 -0
  6. package/dist/commands/doctor-qbd.test.d.ts.map +1 -0
  7. package/dist/commands/doctor-qbd.test.js +548 -0
  8. package/dist/commands/doctor-qbd.test.js.map +1 -0
  9. package/dist/commands/mail-degraded-e4.test.d.ts +43 -0
  10. package/dist/commands/mail-degraded-e4.test.d.ts.map +1 -0
  11. package/dist/commands/mail-degraded-e4.test.js +317 -0
  12. package/dist/commands/mail-degraded-e4.test.js.map +1 -0
  13. package/dist/commands/orient.d.ts.map +1 -1
  14. package/dist/commands/orient.js +23 -0
  15. package/dist/commands/orient.js.map +1 -1
  16. package/dist/commands/qbd-seam.d.ts +77 -0
  17. package/dist/commands/qbd-seam.d.ts.map +1 -0
  18. package/dist/commands/qbd-seam.js +150 -0
  19. package/dist/commands/qbd-seam.js.map +1 -0
  20. package/dist/commands/qbd-seam.test.d.ts +16 -0
  21. package/dist/commands/qbd-seam.test.d.ts.map +1 -0
  22. package/dist/commands/qbd-seam.test.js +339 -0
  23. package/dist/commands/qbd-seam.test.js.map +1 -0
  24. package/dist/commands/review-fan.d.ts.map +1 -1
  25. package/dist/commands/review-fan.js +26 -0
  26. package/dist/commands/review-fan.js.map +1 -1
  27. package/dist/commands/run-compiled-rules.d.ts.map +1 -1
  28. package/dist/commands/run-compiled-rules.js +137 -5
  29. package/dist/commands/run-compiled-rules.js.map +1 -1
  30. package/dist/commands/run-compiled-rules.test.js +359 -1
  31. package/dist/commands/run-compiled-rules.test.js.map +1 -1
  32. package/dist/commands/search.d.ts.map +1 -1
  33. package/dist/commands/search.js +15 -0
  34. package/dist/commands/search.js.map +1 -1
  35. package/dist/commands/shield.d.ts.map +1 -1
  36. package/dist/commands/shield.js +34 -0
  37. package/dist/commands/shield.js.map +1 -1
  38. package/dist/commands/spec.d.ts.map +1 -1
  39. package/dist/commands/spec.js +16 -0
  40. package/dist/commands/spec.js.map +1 -1
  41. package/dist/index.js +8 -1
  42. package/dist/index.js.map +1 -1
  43. package/package.json +2 -2
@@ -0,0 +1,548 @@
1
+ /**
2
+ * Render tests for the `totem doctor --compliance` query-before-derive section
3
+ * (mmnto-ai/totem#2510).
4
+ *
5
+ * The degraded-read block follows the ADR-115 § 2 convention: this is a
6
+ * READ-ONLY path, so the backstop is an explicit `DEGRADED` / `UNVERIFIED`
7
+ * envelope in the rendered output — never a nonzero exit — and the per-item
8
+ * accounting is asserted by the specific failing class being NAMED. The control
9
+ * assertion pins that a clean ledger renders no envelope at all, so the envelope
10
+ * cannot rot into decoration that is always present.
11
+ */
12
+ import * as fs from 'node:fs';
13
+ import * as os from 'node:os';
14
+ import * as path from 'node:path';
15
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
16
+ import { cleanTmpDir } from '../test-utils.js';
17
+ import { doctorQbdComplianceCommand } from './doctor-qbd.js';
18
+ const T0 = Date.parse('2026-07-28T12:00:00.000Z');
19
+ /**
20
+ * Strip ANSI so assertions are colour-independent regardless of whether
21
+ * picocolors decides this environment supports colour. Constructed via
22
+ * `String.fromCharCode(27)` so no raw ESC control byte is authored into source.
23
+ */
24
+ const ANSI = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, 'g');
25
+ /** A minimal VALID config — `targets` requires at least one entry. */
26
+ const TOTEM_YAML = [
27
+ 'targets:',
28
+ ' - glob: "src/**/*.ts"',
29
+ ' type: code',
30
+ ' strategy: file',
31
+ '',
32
+ ].join('\n');
33
+ let cwd;
34
+ /**
35
+ * A temp home with NO global profile. Passed to EVERY invocation so the reader
36
+ * can never fall back to the developer's real `~/.totem/` profile — which on
37
+ * this machine resolves to a live ledger the tests would otherwise read.
38
+ */
39
+ let emptyHome;
40
+ let lines;
41
+ let spy;
42
+ beforeEach(() => {
43
+ cwd = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'totem-qbd-cli-')));
44
+ // A real local config, so resolution stays LOCAL. Without one, this machine's
45
+ // global `~/.totem/` profile wins the lookup and the reader would resolve a
46
+ // ledger outside the fixture entirely.
47
+ fs.writeFileSync(path.join(cwd, 'totem.yaml'), TOTEM_YAML, 'utf-8');
48
+ emptyHome = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'totem-qbd-cli-home-')));
49
+ lines = [];
50
+ spy = vi.spyOn(console, 'error').mockImplementation((msg) => {
51
+ lines.push(String(msg).replace(ANSI, ''));
52
+ });
53
+ });
54
+ afterEach(() => {
55
+ spy.mockRestore();
56
+ cleanTmpDir(cwd);
57
+ cleanTmpDir(emptyHome);
58
+ });
59
+ function output() {
60
+ return lines.join('\n');
61
+ }
62
+ function sid(n) {
63
+ return `00000000-0000-4000-8000-${String(n).padStart(12, '0')}`;
64
+ }
65
+ async function correlatedLedger() {
66
+ const { mintQbdCorrelationId } = await import('@mmnto/totem');
67
+ const id = mintQbdCorrelationId(T0);
68
+ return [
69
+ JSON.stringify({
70
+ timestamp: new Date(T0).toISOString(),
71
+ type: 'corpus_query',
72
+ activity_name: 'totem_search',
73
+ source: 'lint',
74
+ justification: '',
75
+ qbd_correlation_id: id,
76
+ session_id: sid(1),
77
+ }),
78
+ JSON.stringify({
79
+ timestamp: new Date(T0 + 1000).toISOString(),
80
+ type: 'derive_action',
81
+ activity_name: 'spec',
82
+ source: 'lint',
83
+ justification: '',
84
+ qbd_correlation_id: id,
85
+ session_id: sid(1),
86
+ }),
87
+ JSON.stringify({
88
+ timestamp: new Date(T0 + 2000).toISOString(),
89
+ type: 'derive_action',
90
+ activity_name: 'orient',
91
+ source: 'lint',
92
+ justification: '',
93
+ session_id: sid(1),
94
+ }),
95
+ ].join('\n');
96
+ }
97
+ describe('doctorQbdComplianceCommand — clean render', () => {
98
+ it('renders the number with its raw counts', async () => {
99
+ await doctorQbdComplianceCommand({
100
+ cwdForTest: cwd,
101
+ homeDirForTest: emptyHome,
102
+ ledgerContentForTest: await correlatedLedger(),
103
+ });
104
+ // One correlated derive of two → 0.50.
105
+ expect(output()).toContain('compliance: 0.50 (1/2)');
106
+ });
107
+ it('states the pre-registered threshold and window VERBATIM', async () => {
108
+ await doctorQbdComplianceCommand({
109
+ cwdForTest: cwd,
110
+ homeDirForTest: emptyHome,
111
+ ledgerContentForTest: await correlatedLedger(),
112
+ });
113
+ expect(output()).toContain('compliance ≥ 0.50, evaluated over the first 20 instrumented sessions carrying ≥1 derive-class event, regardless of query count');
114
+ });
115
+ it('holds the verdict at PENDING until the window fills', async () => {
116
+ await doctorQbdComplianceCommand({
117
+ cwdForTest: cwd,
118
+ homeDirForTest: emptyHome,
119
+ ledgerContentForTest: await correlatedLedger(),
120
+ });
121
+ expect(output()).toContain('verdict: PENDING');
122
+ expect(output()).toContain('1/20 instrumented sessions');
123
+ });
124
+ it('names the ritual-query limitation instead of claiming it solved', async () => {
125
+ await doctorQbdComplianceCommand({
126
+ cwdForTest: cwd,
127
+ homeDirForTest: emptyHome,
128
+ ledgerContentForTest: await correlatedLedger(),
129
+ });
130
+ expect(output()).toContain('ADJACENCY, not influence');
131
+ expect(output()).toContain('Named, not solved.');
132
+ });
133
+ it('renders NO degraded envelope on a clean ledger (control)', async () => {
134
+ await doctorQbdComplianceCommand({
135
+ cwdForTest: cwd,
136
+ homeDirForTest: emptyHome,
137
+ ledgerContentForTest: await correlatedLedger(),
138
+ });
139
+ expect(output()).not.toContain('DEGRADED');
140
+ expect(output()).not.toContain('UNVERIFIED');
141
+ });
142
+ it('never touches the exit code (Tenet 13 sensor)', async () => {
143
+ const before = process.exitCode;
144
+ await doctorQbdComplianceCommand({
145
+ cwdForTest: cwd,
146
+ homeDirForTest: emptyHome,
147
+ ledgerContentForTest: await correlatedLedger(),
148
+ });
149
+ expect(process.exitCode).toBe(before);
150
+ });
151
+ });
152
+ describe('doctorQbdComplianceCommand — degraded render (ADR-115 § 2)', () => {
153
+ it('announces DEGRADED/UNVERIFIED and names the failing items', async () => {
154
+ const { mintQbdCorrelationId } = await import('@mmnto/totem');
155
+ // A backfilled row (ID minted an hour after the derive) plus a torn line.
156
+ const backfilled = mintQbdCorrelationId(T0 + 3_600_000);
157
+ const content = [
158
+ JSON.stringify({
159
+ timestamp: new Date(T0).toISOString(),
160
+ type: 'derive_action',
161
+ activity_name: 'spec',
162
+ source: 'lint',
163
+ justification: '',
164
+ qbd_correlation_id: backfilled,
165
+ session_id: sid(1),
166
+ }),
167
+ '{ torn line',
168
+ JSON.stringify({
169
+ timestamp: new Date(T0 + 1000).toISOString(),
170
+ type: 'derive_action',
171
+ activity_name: 'orient',
172
+ source: 'lint',
173
+ justification: '',
174
+ session_id: sid(1),
175
+ }),
176
+ ].join('\n');
177
+ await doctorQbdComplianceCommand({
178
+ cwdForTest: cwd,
179
+ homeDirForTest: emptyHome,
180
+ ledgerContentForTest: content,
181
+ });
182
+ const out = output();
183
+ // The envelope itself.
184
+ expect(out).toContain('DEGRADED');
185
+ expect(out).toContain('compliance (UNVERIFIED)');
186
+ // Per-item accounting, by class.
187
+ expect(out).toContain('1 malformed JSON line(s)');
188
+ expect(out).toContain('1 correlation-contract violation(s)');
189
+ });
190
+ it('a broken scan never renders like a clean number', async () => {
191
+ // The defect class ADR-115 § 2 names: degraded state that is
192
+ // indistinguishable from verified state.
193
+ //
194
+ // The fixture MUST contain real derives. An earlier version used two torn
195
+ // lines and nothing else, so the scan had zero derive events and rendered
196
+ // "n/a" — meaning `not.toMatch(/compliance: \d/)` passed no matter what
197
+ // label the code used. The assertion only bites when a number genuinely
198
+ // would have been printed.
199
+ const { mintQbdCorrelationId } = await import('@mmnto/totem');
200
+ const id = mintQbdCorrelationId(T0);
201
+ const content = [
202
+ JSON.stringify({
203
+ timestamp: new Date(T0).toISOString(),
204
+ type: 'corpus_query',
205
+ activity_name: 'totem_search',
206
+ source: 'lint',
207
+ justification: '',
208
+ qbd_correlation_id: id,
209
+ session_id: sid(1),
210
+ }),
211
+ JSON.stringify({
212
+ timestamp: new Date(T0 + 1000).toISOString(),
213
+ type: 'derive_action',
214
+ activity_name: 'spec',
215
+ source: 'lint',
216
+ justification: '',
217
+ qbd_correlation_id: id,
218
+ session_id: sid(1),
219
+ }),
220
+ '{ torn',
221
+ '{ also torn',
222
+ ].join('\n');
223
+ await doctorQbdComplianceCommand({
224
+ cwdForTest: cwd,
225
+ homeDirForTest: emptyHome,
226
+ ledgerContentForTest: content,
227
+ });
228
+ const out = output();
229
+ expect(out).toContain('DEGRADED');
230
+ expect(out).toContain('2 malformed JSON line(s)');
231
+ // A number IS computed here (1/1) — but it must never appear unlabelled.
232
+ expect(out).toContain('compliance (UNVERIFIED): 1.00 (1/1)');
233
+ expect(out).not.toMatch(/(?<!\()compliance: \d/);
234
+ });
235
+ it('DEGRADES on a backdated append', async () => {
236
+ const { mintQbdCorrelationId } = await import('@mmnto/totem');
237
+ const backdated = Date.parse('2019-01-01T00:00:00.000Z');
238
+ const content = [
239
+ JSON.stringify({
240
+ timestamp: new Date(T0).toISOString(),
241
+ type: 'derive_action',
242
+ activity_name: 'spec',
243
+ source: 'lint',
244
+ justification: '',
245
+ session_id: sid(1),
246
+ }),
247
+ JSON.stringify({
248
+ timestamp: new Date(backdated).toISOString(),
249
+ type: 'corpus_query',
250
+ activity_name: 'totem_search',
251
+ source: 'lint',
252
+ justification: '',
253
+ qbd_correlation_id: mintQbdCorrelationId(backdated),
254
+ session_id: sid(2),
255
+ }),
256
+ ].join('\n');
257
+ await doctorQbdComplianceCommand({
258
+ cwdForTest: cwd,
259
+ homeDirForTest: emptyHome,
260
+ ledgerContentForTest: content,
261
+ });
262
+ expect(output()).toContain('backdated append');
263
+ });
264
+ it('names duplicate correlations and offers a remediation path', async () => {
265
+ const { mintQbdCorrelationId } = await import('@mmnto/totem');
266
+ const id = mintQbdCorrelationId(T0);
267
+ const content = [
268
+ JSON.stringify({
269
+ timestamp: new Date(T0).toISOString(),
270
+ type: 'corpus_query',
271
+ activity_name: 'totem_search',
272
+ source: 'lint',
273
+ justification: '',
274
+ qbd_correlation_id: id,
275
+ session_id: sid(1),
276
+ }),
277
+ JSON.stringify({
278
+ timestamp: new Date(T0 + 1000).toISOString(),
279
+ type: 'derive_action',
280
+ activity_name: 'spec',
281
+ source: 'lint',
282
+ justification: '',
283
+ qbd_correlation_id: id,
284
+ session_id: sid(1),
285
+ }),
286
+ JSON.stringify({
287
+ timestamp: new Date(T0 + 2000).toISOString(),
288
+ type: 'derive_action',
289
+ activity_name: 'spec',
290
+ source: 'lint',
291
+ justification: '',
292
+ qbd_correlation_id: id,
293
+ session_id: sid(1),
294
+ }),
295
+ ].join('\n');
296
+ await doctorQbdComplianceCommand({
297
+ cwdForTest: cwd,
298
+ homeDirForTest: emptyHome,
299
+ ledgerContentForTest: content,
300
+ });
301
+ const out = output();
302
+ expect(out).toContain('1 derive(s) re-citing a correlation ID');
303
+ // Degraded is sticky, so the envelope must say what to do about it.
304
+ expect(out).toContain('to clear:');
305
+ expect(out).toContain('events.ndjson');
306
+ });
307
+ it('surfaces a seat-mismatch hint WITHOUT degrading the read', async () => {
308
+ const { mintQbdCorrelationId } = await import('@mmnto/totem');
309
+ const content = [
310
+ JSON.stringify({
311
+ timestamp: new Date(T0).toISOString(),
312
+ type: 'corpus_query',
313
+ activity_name: 'search_knowledge',
314
+ source: 'bot',
315
+ justification: '',
316
+ qbd_correlation_id: mintQbdCorrelationId(T0),
317
+ session_id: sid(1),
318
+ agent_source: 'seat-a',
319
+ }),
320
+ JSON.stringify({
321
+ timestamp: new Date(T0 + 1000).toISOString(),
322
+ type: 'derive_action',
323
+ activity_name: 'spec',
324
+ source: 'lint',
325
+ justification: '',
326
+ session_id: sid(1),
327
+ agent_source: 'seat-b',
328
+ }),
329
+ ].join('\n');
330
+ await doctorQbdComplianceCommand({
331
+ cwdForTest: cwd,
332
+ homeDirForTest: emptyHome,
333
+ ledgerContentForTest: content,
334
+ });
335
+ const out = output();
336
+ expect(out).toContain('one-sided seat plumbing');
337
+ expect(out).toContain('mmnto-ai/totem#2530');
338
+ // A config smell is not tampering.
339
+ expect(out).not.toContain('DEGRADED');
340
+ });
341
+ it('reports an unknown event type as an advisory, NOT as DEGRADED', async () => {
342
+ const content = JSON.stringify({
343
+ timestamp: new Date(T0).toISOString(),
344
+ type: 'some_future_event_type',
345
+ source: 'lint',
346
+ justification: '',
347
+ });
348
+ await doctorQbdComplianceCommand({
349
+ cwdForTest: cwd,
350
+ homeDirForTest: emptyHome,
351
+ ledgerContentForTest: content,
352
+ });
353
+ const out = output();
354
+ expect(out).toContain('version skew');
355
+ expect(out).not.toContain('DEGRADED');
356
+ });
357
+ it('flags an orphan correlation rather than crediting it', async () => {
358
+ const { mintQbdCorrelationId } = await import('@mmnto/totem');
359
+ const orphan = mintQbdCorrelationId(T0);
360
+ const content = JSON.stringify({
361
+ timestamp: new Date(T0 + 1000).toISOString(),
362
+ type: 'derive_action',
363
+ activity_name: 'review',
364
+ source: 'lint',
365
+ justification: '',
366
+ qbd_correlation_id: orphan,
367
+ session_id: sid(1),
368
+ });
369
+ await doctorQbdComplianceCommand({
370
+ cwdForTest: cwd,
371
+ homeDirForTest: emptyHome,
372
+ ledgerContentForTest: content,
373
+ });
374
+ const out = output();
375
+ expect(out).toContain('DEGRADED');
376
+ expect(out).toContain('no preceding query row');
377
+ expect(out).toContain('compliance (UNVERIFIED): 0.00 (0/1)');
378
+ });
379
+ });
380
+ describe('doctorQbdComplianceCommand — real on-disk read', () => {
381
+ /** Build N instrumented sessions on disk, correlated or not. */
382
+ async function writeLedger(sessions, correlated) {
383
+ const { mintQbdCorrelationId } = await import('@mmnto/totem');
384
+ const lines = [];
385
+ for (let i = 0; i < sessions; i++) {
386
+ const ms = T0 + i * 86_400_000;
387
+ if (correlated) {
388
+ const id = mintQbdCorrelationId(ms);
389
+ lines.push(JSON.stringify({
390
+ timestamp: new Date(ms).toISOString(),
391
+ type: 'corpus_query',
392
+ activity_name: 'totem_search',
393
+ source: 'lint',
394
+ justification: '',
395
+ qbd_correlation_id: id,
396
+ session_id: sid(i),
397
+ }), JSON.stringify({
398
+ timestamp: new Date(ms + 1000).toISOString(),
399
+ type: 'derive_action',
400
+ activity_name: 'spec',
401
+ source: 'lint',
402
+ justification: '',
403
+ qbd_correlation_id: id,
404
+ session_id: sid(i),
405
+ }));
406
+ }
407
+ else {
408
+ lines.push(JSON.stringify({
409
+ timestamp: new Date(ms).toISOString(),
410
+ type: 'derive_action',
411
+ activity_name: 'spec',
412
+ source: 'lint',
413
+ justification: '',
414
+ session_id: sid(i),
415
+ }));
416
+ }
417
+ }
418
+ const dir = path.join(cwd, '.totem', 'ledger');
419
+ fs.mkdirSync(dir, { recursive: true });
420
+ fs.writeFileSync(path.join(dir, 'events.ndjson'), lines.join('\n') + '\n', 'utf-8');
421
+ }
422
+ it('reads a real events.ndjson off disk through the production path', async () => {
423
+ // No `ledgerContentForTest` — this exercises the actual file read, which
424
+ // every other test in this file stubs past.
425
+ await writeLedger(2, true);
426
+ await doctorQbdComplianceCommand({ cwdForTest: cwd, homeDirForTest: emptyHome });
427
+ const out = output();
428
+ expect(out).toContain('compliance: 1.00 (2/2)');
429
+ expect(out).not.toContain('SKIP');
430
+ });
431
+ it('renders PASS on a filled window at or above the floor, exit code untouched', async () => {
432
+ await writeLedger(20, true);
433
+ const before = process.exitCode;
434
+ await doctorQbdComplianceCommand({ cwdForTest: cwd, homeDirForTest: emptyHome });
435
+ expect(output()).toContain('verdict: PASS');
436
+ expect(process.exitCode).toBe(before);
437
+ });
438
+ it('qualifies the VERDICT and TREND lines too when the scan is degraded', async () => {
439
+ // The verdict line carries the pre-registered consequence, so rendering a
440
+ // bare `verdict: PASS` beside an UNVERIFIED number reproduces exactly the
441
+ // indistinguishability the envelope exists to prevent. Trend is derived from
442
+ // the same untrusted rows and gets the same treatment.
443
+ await writeLedger(20, true);
444
+ const ledgerFile = path.join(cwd, '.totem', 'ledger', 'events.ndjson');
445
+ fs.appendFileSync(ledgerFile, '{ torn\n', 'utf-8');
446
+ const before = process.exitCode;
447
+ await doctorQbdComplianceCommand({ cwdForTest: cwd, homeDirForTest: emptyHome });
448
+ const out = output();
449
+ expect(out).toContain('DEGRADED');
450
+ expect(out).toContain('verdict (UNVERIFIED): PASS');
451
+ expect(out).toContain('trend (UNVERIFIED):');
452
+ expect(out).toContain('compliance (UNVERIFIED):');
453
+ // No unqualified verdict or trend line may survive alongside the envelope.
454
+ expect(out).not.toMatch(/verdict: (PASS|FAIL|PENDING)/);
455
+ expect(out).not.toMatch(/trend: /);
456
+ // Still a pure sensor.
457
+ expect(process.exitCode).toBe(before);
458
+ });
459
+ it('leaves verdict and trend UNqualified on a clean scan (control)', async () => {
460
+ await writeLedger(20, true);
461
+ await doctorQbdComplianceCommand({ cwdForTest: cwd, homeDirForTest: emptyHome });
462
+ const out = output();
463
+ expect(out).toContain('verdict: PASS');
464
+ expect(out).not.toContain('UNVERIFIED');
465
+ });
466
+ it('renders FAIL below the floor and STILL leaves the exit code alone', async () => {
467
+ // The sensor-not-actuator contract matters most on the failing branch:
468
+ // this is the one a reader would expect to gate, and it must not.
469
+ await writeLedger(20, false);
470
+ const before = process.exitCode;
471
+ await doctorQbdComplianceCommand({ cwdForTest: cwd, homeDirForTest: emptyHome });
472
+ const out = output();
473
+ expect(out).toContain('verdict: FAIL');
474
+ expect(out).toContain('FALSIFIED');
475
+ expect(process.exitCode).toBe(before);
476
+ });
477
+ it('SKIPs on unresolved config even when a stale cwd/.totem ledger exists', async () => {
478
+ // The reader must not fall back to `<cwd>/.totem`. The WRITERS skip when
479
+ // config is unresolved, so a reader that fell back would render a ledger no
480
+ // writer ever writes — reviving the divergence, and presenting a stale
481
+ // directory as if it were this project's data.
482
+ const orphan = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'totem-qbd-noconf-')));
483
+ try {
484
+ // A stale ledger sitting exactly where the removed fallback pointed.
485
+ const staleDir = path.join(orphan, '.totem', 'ledger');
486
+ fs.mkdirSync(staleDir, { recursive: true });
487
+ fs.writeFileSync(path.join(staleDir, 'events.ndjson'), JSON.stringify({
488
+ timestamp: new Date(T0).toISOString(),
489
+ type: 'derive_action',
490
+ activity_name: 'spec',
491
+ source: 'lint',
492
+ justification: '',
493
+ session_id: sid(1),
494
+ }) + '\n', 'utf-8');
495
+ await doctorQbdComplianceCommand({ cwdForTest: orphan, homeDirForTest: emptyHome });
496
+ const out = output();
497
+ expect(out).toContain('SKIP');
498
+ expect(out).toContain('no Totem config found');
499
+ // The stale row must NOT have been read into a rendered number.
500
+ expect(out).not.toMatch(/compliance.*: \d/);
501
+ }
502
+ finally {
503
+ cleanTmpDir(orphan);
504
+ }
505
+ });
506
+ it('SKIPs honestly when the ledger path is unreadable', async () => {
507
+ // Induce a real errno: a DIRECTORY where events.ndjson should be, so the
508
+ // read throws EISDIR rather than ENOENT.
509
+ const dir = path.join(cwd, '.totem', 'ledger', 'events.ndjson');
510
+ fs.mkdirSync(dir, { recursive: true });
511
+ await doctorQbdComplianceCommand({ cwdForTest: cwd, homeDirForTest: emptyHome });
512
+ const out = output();
513
+ expect(out).toContain('SKIP');
514
+ expect(out).toContain('unreadable');
515
+ // The errno is named: EACCES, EISDIR and ENOTDIR are different problems
516
+ // with different fixes, and a bare "unreadable" is unactionable.
517
+ expect(out).toMatch(/unreadable \((EISDIR|EACCES|ENOTDIR|EPERM|unknown)\)/);
518
+ expect(out).not.toContain('0.00');
519
+ });
520
+ });
521
+ describe('doctorQbdComplianceCommand — absent and empty states', () => {
522
+ it('SKIPs on a missing ledger — not a fail, and emphatically not 0%', async () => {
523
+ await doctorQbdComplianceCommand({ cwdForTest: cwd, homeDirForTest: emptyHome });
524
+ const out = output();
525
+ expect(out).toContain('SKIP');
526
+ expect(out).not.toContain('0.00');
527
+ expect(out).not.toContain('DEGRADED');
528
+ });
529
+ it('renders n/a rather than 0% when there are no derive-class events', async () => {
530
+ const { mintQbdCorrelationId } = await import('@mmnto/totem');
531
+ const content = JSON.stringify({
532
+ timestamp: new Date(T0).toISOString(),
533
+ type: 'corpus_query',
534
+ activity_name: 'totem_search',
535
+ source: 'lint',
536
+ justification: '',
537
+ qbd_correlation_id: mintQbdCorrelationId(T0),
538
+ session_id: sid(1),
539
+ });
540
+ await doctorQbdComplianceCommand({
541
+ cwdForTest: cwd,
542
+ homeDirForTest: emptyHome,
543
+ ledgerContentForTest: content,
544
+ });
545
+ expect(output()).toContain('n/a (0 derive-class events)');
546
+ });
547
+ });
548
+ //# sourceMappingURL=doctor-qbd.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-qbd.test.js","sourceRoot":"","sources":["../../src/commands/doctor-qbd.test.ts"],"names":[],"mappings":"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"}
@@ -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"}