@mmnto/cli 1.41.0 → 1.43.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,341 @@
1
+ import { createRequire } from 'node:module';
2
+ // ─── Constants ──────────────────────────────────────────
3
+ const TAG = 'ClaimDiscipline';
4
+ const BYPASS_ENV_VAR = 'TOTEM_GATE_BYPASS_JUSTIFICATION';
5
+ /**
6
+ * Discovery convention: WWND rules are recognized by their lesson heading
7
+ * starting with this prefix. Authoring discipline keeps the prefix in sync
8
+ * with the proposal scope (`.totem/lessons/wwnd-rule-*.md`).
9
+ *
10
+ * Filename-based discovery (glob `.totem/lessons/wwnd-*.md`) was considered
11
+ * but would require a lesson-on-disk → compiled-rule-hash mapping pass at
12
+ * runtime; heading-prefix lookup is one-pass against the already-loaded rule
13
+ * set.
14
+ */
15
+ const WWND_HEADING_PREFIX = 'WWND Rule ';
16
+ /**
17
+ * In-scope public claim surfaces per Proposal 279 § Scope. The gate fires
18
+ * only when these files exist in the repo; the rules themselves carry
19
+ * narrower `fileGlobs` for the actual match dispatch.
20
+ */
21
+ const WWND_LITERAL_SURFACES = ['README.md', 'AGENTS.md', 'design-tenets.md'];
22
+ const WWND_GLOB_SURFACES = ['docs/wiki/**'];
23
+ /**
24
+ * Filter the loaded rule set to WWND rules, validate the regex, and
25
+ * project to a tighter shape used by the scan loop. Silently skip rules
26
+ * with invalid regex (logged via the warnings collector at the caller).
27
+ */
28
+ function discoverWwndRules(rules, warnings) {
29
+ const out = [];
30
+ for (const rule of rules) {
31
+ // Runtime type guards — `loadCompiledRules` validates schema, but cast-at-boundary
32
+ // means malformed disk data could still slip through. Defense-in-depth guards
33
+ // degrade gracefully instead of throwing during pre-push gating.
34
+ if (typeof rule.lessonHeading !== 'string')
35
+ continue;
36
+ if (typeof rule.lessonHash !== 'string')
37
+ continue;
38
+ if (typeof rule.pattern !== 'string')
39
+ continue;
40
+ if (!rule.lessonHeading.startsWith(WWND_HEADING_PREFIX))
41
+ continue;
42
+ if (rule.engine !== undefined && rule.engine !== 'regex') {
43
+ // Non-regex WWND rules (ast / ast-grep) need engine-specific dispatch
44
+ // not yet implemented in PR α; flag explicitly so PR β knows to wire it.
45
+ warnings.push(`WWND rule '${rule.lessonHeading}' uses engine '${String(rule.engine)}' which is not yet supported by the claim-discipline scanner. Rule skipped.`);
46
+ continue;
47
+ }
48
+ if (rule.status !== undefined && rule.status !== 'active')
49
+ continue;
50
+ let regex;
51
+ try {
52
+ regex = new RegExp(rule.pattern, 'g');
53
+ // totem-context: invalid-regex is recorded as a warning + skipped, not silently swallowed; this is the sensor pattern (record + continue with the rest of the rule set)
54
+ }
55
+ catch (err) {
56
+ const msg = err instanceof Error ? err.message : String(err);
57
+ warnings.push(`WWND rule '${rule.lessonHeading}' has invalid regex '${rule.pattern}': ${msg}. Rule skipped.`);
58
+ continue;
59
+ }
60
+ // Filter fileGlobs to known-string entries; absent or non-array → undefined.
61
+ const safeFileGlobs = Array.isArray(rule.fileGlobs)
62
+ ? rule.fileGlobs.filter((g) => typeof g === 'string' && g.length > 0)
63
+ : undefined;
64
+ out.push({
65
+ lessonHash: rule.lessonHash,
66
+ lessonHeading: rule.lessonHeading,
67
+ pattern: regex,
68
+ severity: rule.severity ?? 'warning',
69
+ fileGlobs: safeFileGlobs,
70
+ });
71
+ }
72
+ return out;
73
+ }
74
+ // ─── Scan loop ──────────────────────────────────────────
75
+ function scanFile(filePath, relPath, rules, matchesGlob, readFile, sanitizeForTerminal) {
76
+ const findings = [];
77
+ let content;
78
+ try {
79
+ content = readFile(filePath);
80
+ // totem-context: unreadable files (permission, transient FS error) are not a finding class — skip silently and continue scanning the rest of the surface set
81
+ }
82
+ catch (err) {
83
+ void err;
84
+ return findings;
85
+ }
86
+ const lines = content.split('\n');
87
+ for (const rule of rules) {
88
+ // Honor per-rule fileGlobs when present; absent globs mean "applies to
89
+ // any in-scope surface" (the scanner's outer surface-filter is the gate).
90
+ if (rule.fileGlobs && rule.fileGlobs.length > 0) {
91
+ const include = rule.fileGlobs.some((g) => matchesGlob(relPath, g));
92
+ if (!include)
93
+ continue;
94
+ }
95
+ for (let i = 0; i < lines.length; i++) {
96
+ const line = lines[i] ?? '';
97
+ // matchAll iterates without the manual lastIndex bookkeeping that
98
+ // RegExp.exec requires; clean per-line scan with no zero-width-loop
99
+ // hazard.
100
+ for (const m of line.matchAll(rule.pattern)) {
101
+ findings.push({
102
+ ruleId: rule.lessonHash,
103
+ ruleHeading: rule.lessonHeading,
104
+ file: relPath,
105
+ line: i + 1,
106
+ // Sanitize-at-source: README/AGENTS prose is untrusted (could carry terminal
107
+ // control sequences). Sanitize once here so downstream consumers (ledger writer,
108
+ // CLI logger) all get safe text. Same hardening pattern verify-badges uses.
109
+ match: sanitizeForTerminal(m[0]),
110
+ severity: rule.severity,
111
+ });
112
+ }
113
+ }
114
+ }
115
+ return findings;
116
+ }
117
+ // ─── Filesystem walk (zero-dep alternative to glob package) ─
118
+ function walkMarkdown(absDir, relPrefix, out, fs, path) {
119
+ const entries = fs.readdirSync(absDir, { withFileTypes: true });
120
+ for (const entry of entries) {
121
+ const absChild = path.join(absDir, entry.name);
122
+ const relChild = path.posix.join(relPrefix, entry.name);
123
+ if (entry.isDirectory()) {
124
+ walkMarkdown(absChild, relChild, out, fs, path);
125
+ }
126
+ else if (entry.isFile() && entry.name.endsWith('.md')) {
127
+ out.push(relChild);
128
+ }
129
+ }
130
+ }
131
+ // ─── Telemetry ──────────────────────────────────────────
132
+ async function emitTelemetry(repoRoot, findings, bypassed, bypassJustification) {
133
+ if (findings.length === 0)
134
+ return;
135
+ try {
136
+ const { appendLedgerEvent } = await import('@mmnto/totem');
137
+ const path = await import('node:path');
138
+ const totemDir = path.join(repoRoot, '.totem');
139
+ // Read the CLI's own version once per invocation — same pattern as
140
+ // `packages/cli/src/index.ts` and `run-compiled-rules.ts`.
141
+ let cliVersion;
142
+ try {
143
+ const req = createRequire(import.meta.url);
144
+ const pkg = req('../../package.json');
145
+ cliVersion = pkg.version;
146
+ // totem-context: cli_version is a best-effort enrichment of the ledger event; failure to resolve package.json must not block the gate
147
+ }
148
+ catch (err) {
149
+ void err;
150
+ }
151
+ // `justification` is intentionally an empty string for the non-bypass
152
+ // path. The LedgerEventSchema declares `justification: z.string().default('')`
153
+ // (no `.min(1)`), so empty strings are valid input. Audit consumers that
154
+ // need to find bypassed events filter on `.justification != ""` rather
155
+ // than treating absence as the signal — keeps the on-disk shape consistent
156
+ // across event types where empty justification is the norm
157
+ // (`suppress`, `override`, `exemption` all write `''` on the no-context path).
158
+ const justification = bypassed && bypassJustification !== undefined ? bypassJustification : '';
159
+ for (const f of findings) {
160
+ appendLedgerEvent(totemDir, {
161
+ timestamp: new Date().toISOString(),
162
+ type: 'claim_discipline_finding',
163
+ ruleId: f.ruleId,
164
+ file: f.file,
165
+ line: f.line,
166
+ activity_name: f.file,
167
+ justification,
168
+ source: 'lint',
169
+ ...(cliVersion !== undefined ? { cli_version: cliVersion } : {}),
170
+ });
171
+ }
172
+ // totem-context: fire-and-forget telemetry; ledger-write failure must not block the gate per the A.3.a writer-contract pattern
173
+ }
174
+ catch (err) {
175
+ void err;
176
+ }
177
+ }
178
+ // ─── Main command ───────────────────────────────────────
179
+ /**
180
+ * Programmatic surface — returns the result without exiting or throwing.
181
+ * The CLI action layer wraps this and throws a `TotemError` when
182
+ * `result.valid === false` so the top-level `handleError` produces the exit
183
+ * code (avoids direct `process.exit()` calls per AGENTS.md doctrine).
184
+ */
185
+ export async function doctorClaimDisciplineCommand(options = {}) {
186
+ const { loadCompiledRules, matchesGlob, resolveGitRoot, sanitizeForTerminal } = await import('@mmnto/totem');
187
+ const path = await import('node:path');
188
+ const fs = await import('node:fs');
189
+ const env = options.envForTest ?? process.env;
190
+ const repoRoot = options.repoRootForTest ?? resolveGitRoot(process.cwd());
191
+ const findings = [];
192
+ const warnings = [];
193
+ if (!repoRoot) {
194
+ return {
195
+ valid: true,
196
+ findings,
197
+ warnings: ['Not inside a git repo — skipping claim-discipline check.'],
198
+ bypassed: false,
199
+ };
200
+ }
201
+ // ─── Discover in-scope files ─────────────────────────
202
+ let files;
203
+ if (options.filesForTest) {
204
+ files = options.filesForTest;
205
+ }
206
+ else {
207
+ files = [];
208
+ for (const literal of WWND_LITERAL_SURFACES) {
209
+ const abs = path.join(repoRoot, literal);
210
+ if (fs.existsSync(abs))
211
+ files.push(literal);
212
+ }
213
+ // Glob expansion for `docs/wiki/**` is deferred to PR β when the surface
214
+ // becomes load-bearing. For now: recursive walk over docs/wiki/ if it
215
+ // exists, collecting *.md files. Keeps PR α's dep footprint at zero
216
+ // glob libs.
217
+ for (const globRoot of WWND_GLOB_SURFACES) {
218
+ // Strip trailing /** to get the directory prefix
219
+ const dirPart = globRoot.replace(/\/\*\*$/, '');
220
+ const absDir = path.join(repoRoot, dirPart);
221
+ if (!fs.existsSync(absDir))
222
+ continue;
223
+ try {
224
+ const collected = [];
225
+ walkMarkdown(absDir, dirPart, collected, fs, path);
226
+ files.push(...collected);
227
+ // totem-context: walk failure is recorded as a warning, not silently swallowed — sensor pattern (record + continue scanning the literal surfaces)
228
+ }
229
+ catch (err) {
230
+ const msg = err instanceof Error ? err.message : String(err);
231
+ warnings.push(`Failed to walk '${globRoot}': ${msg}`);
232
+ }
233
+ }
234
+ }
235
+ if (files.length === 0) {
236
+ // No in-scope surfaces present → nothing to check. Not a failure.
237
+ return { valid: true, findings, warnings, bypassed: false };
238
+ }
239
+ // ─── Load + filter WWND rules ─────────────────────────
240
+ const rulesPath = path.join(repoRoot, '.totem', 'compiled-rules.json');
241
+ if (!fs.existsSync(rulesPath)) {
242
+ warnings.push('.totem/compiled-rules.json not found — skipping claim-discipline check.');
243
+ return { valid: true, findings, warnings, bypassed: false };
244
+ }
245
+ // Graceful recovery on corrupt/malformed compiled-rules.json — pre-push gating must
246
+ // not crash on file corruption or partial writes. Empty rule set + warning is the
247
+ // sensor-pattern equivalent of the "no WWND rules found" path below.
248
+ let rules;
249
+ try {
250
+ rules = loadCompiledRules(rulesPath);
251
+ // totem-context: load failure is recorded as a warning + recovered-to-empty, not silently swallowed; matches the file-corruption-recovery pattern used elsewhere for config artifacts
252
+ }
253
+ catch (err) {
254
+ const msg = err instanceof Error ? err.message : String(err);
255
+ warnings.push(`Failed to load .totem/compiled-rules.json (${msg}) — claim-discipline check skipped. Run 'totem lesson compile' to regenerate.`);
256
+ return { valid: true, findings, warnings, bypassed: false };
257
+ }
258
+ const wwndRules = discoverWwndRules(rules, warnings);
259
+ if (wwndRules.length === 0) {
260
+ warnings.push('No WWND rules found in compiled-rules.json — claim-discipline check is inert until at least one wwnd-* lesson is compiled.');
261
+ return { valid: true, findings, warnings, bypassed: false };
262
+ }
263
+ // ─── Scan ─────────────────────────────────────────────
264
+ for (const relPath of files) {
265
+ const abs = path.join(repoRoot, relPath);
266
+ findings.push(
267
+ // totem-context: gate operates on working-tree content (what the operator is about to push), not staged index — `git show :path` would skip unstaged edits that the gate should still surface
268
+ ...scanFile(abs, relPath, wwndRules, matchesGlob, (p) => fs.readFileSync(p, 'utf-8'), sanitizeForTerminal));
269
+ }
270
+ // ─── Bypass handling ──────────────────────────────────
271
+ // Sanitize the raw env-var value before downstream use — operators can set arbitrary
272
+ // bytes; the same terminal-control-sequence hardening applies as for `match`.
273
+ const rawJustification = env[BYPASS_ENV_VAR]?.trim();
274
+ const bypassJustification = rawJustification !== undefined ? sanitizeForTerminal(rawJustification) : undefined;
275
+ const bypassed = bypassJustification !== undefined && bypassJustification.length > 0;
276
+ // ─── Emit telemetry ───────────────────────────────────
277
+ await emitTelemetry(repoRoot, findings, bypassed, bypassJustification);
278
+ // ─── Determine validity ───────────────────────────────
279
+ const errorFindings = findings.filter((f) => f.severity === 'error');
280
+ const valid = bypassed || errorFindings.length === 0;
281
+ return {
282
+ valid,
283
+ findings,
284
+ warnings,
285
+ bypassed,
286
+ ...(bypassJustification !== undefined ? { bypassJustification } : {}),
287
+ };
288
+ }
289
+ /**
290
+ * CLI entry — wraps `doctorClaimDisciplineCommand` and throws on hard
291
+ * failure so the top-level `handleError` produces the non-zero exit code
292
+ * without a direct `process.exit` call. Warning-severity findings are
293
+ * reported to the user; they fail the gate only under `--strict`.
294
+ */
295
+ export async function doctorClaimDisciplineCliCommand(options = {}) {
296
+ const { TotemError } = await import('@mmnto/totem');
297
+ const { bold, errorColor, log, success: successColor, warn: warnColor, } = await import('../ui.js');
298
+ // Strip `strict` (CLI-only) before passing the rest through to the programmatic
299
+ // command, which doesn't accept it. The remaining fields (envForTest / filesForTest /
300
+ // repoRootForTest) are inherited from ClaimDisciplineOptions and form the integration
301
+ // test surface that GCA's R1 finding asked us to wire.
302
+ const { strict, ...programmaticOptions } = options;
303
+ const result = await doctorClaimDisciplineCommand(programmaticOptions);
304
+ for (const w of result.warnings) {
305
+ log.warn(TAG, w);
306
+ }
307
+ if (result.findings.length === 0) {
308
+ log.success(TAG, `${successColor(bold('PASS'))} — no claim-discipline findings.`);
309
+ return;
310
+ }
311
+ // Report findings to the user. Warnings go through the warn channel;
312
+ // errors through the error channel; both surface the file:line:match.
313
+ for (const f of result.findings) {
314
+ const tag = f.severity === 'error' ? errorColor(bold('ERROR')) : warnColor(bold('WARN'));
315
+ const msg = `${tag} ${f.file}:${f.line} — [${f.ruleHeading}] matched "${f.match}"`;
316
+ if (f.severity === 'error')
317
+ log.error(TAG, msg);
318
+ else
319
+ log.warn(TAG, msg);
320
+ }
321
+ if (result.bypassed) {
322
+ log.warn(TAG, `${warnColor(bold('BYPASSED'))} — TOTEM_GATE_BYPASS_JUSTIFICATION set; ${result.findings.length} finding(s) recorded but gate passed.`);
323
+ return;
324
+ }
325
+ const errorCount = result.findings.filter((f) => f.severity === 'error').length;
326
+ const warningCount = result.findings.filter((f) => f.severity === 'warning').length;
327
+ if (!result.valid) {
328
+ throw new TotemError('CLAIM_DISCIPLINE_FAILED', `${errorCount} error-severity claim-discipline finding(s).`, 'Fix each error finding above by adjusting the prose (name the structural backing, soften to present-tense intent, or add the required field). To bypass once with audit trail: set TOTEM_GATE_BYPASS_JUSTIFICATION="<reason>" before pushing.');
329
+ }
330
+ // Strict mode promotes warnings to gate failures (Proposal 279 § Implementation Notes Q3
331
+ // — the pre-push hook invokes `--claim-discipline --strict` and expects warnings to fail
332
+ // the push when present).
333
+ if (strict && warningCount > 0) {
334
+ throw new TotemError('CLAIM_DISCIPLINE_FAILED', `${warningCount} warning-severity claim-discipline finding(s) under --strict.`, 'Fix each warning finding above (name the structural backing inline, or soften to present-tense intent). To bypass once with audit trail: set TOTEM_GATE_BYPASS_JUSTIFICATION="<reason>" before pushing.');
335
+ }
336
+ // Warning-only findings outside strict mode — print summary but don't throw.
337
+ if (warningCount > 0) {
338
+ log.warn(TAG, `${warnColor(bold('WARN'))} — ${warningCount} warning-severity finding(s) recorded; pass --strict (or set TOTEM_GATE_BYPASS_JUSTIFICATION) to gate on warnings.`);
339
+ }
340
+ }
341
+ //# sourceMappingURL=doctor-claim-discipline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-claim-discipline.js","sourceRoot":"","sources":["../../src/commands/doctor-claim-discipline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=doctor-claim-discipline.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-claim-discipline.test.d.ts","sourceRoot":"","sources":["../../src/commands/doctor-claim-discipline.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,210 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import { doctorClaimDisciplineCommand } from './doctor-claim-discipline.js';
6
+ function buildRulesFile(rules) {
7
+ return JSON.stringify({
8
+ version: 1,
9
+ rules: rules.map((r) => ({
10
+ compiledAt: '2026-05-16T00:00:00.000Z',
11
+ message: r.lessonHeading,
12
+ engine: 'regex',
13
+ severity: 'warning',
14
+ ...r,
15
+ })),
16
+ }, null, 2);
17
+ }
18
+ const ABSOLUTE_PROMISE_PATTERN = "\\b(?:[Ww]ill\\s+(?:stay|remain|always\\s+be|never\\s+(?:change|move))|[Ww]on['']t\\s+(?:change|ever)|[Gg]uarantees|[Pp]romises\\s+to)\\b";
19
+ let tmpDir;
20
+ beforeEach(() => {
21
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-claim-disc-'));
22
+ fs.mkdirSync(path.join(tmpDir, '.totem'), { recursive: true });
23
+ });
24
+ afterEach(() => {
25
+ fs.rmSync(tmpDir, { recursive: true, force: true });
26
+ });
27
+ // ─── doctorClaimDisciplineCommand ───────────────────────
28
+ describe('doctorClaimDisciplineCommand', () => {
29
+ it('returns valid+no-findings when no in-scope surfaces exist', async () => {
30
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
31
+ {
32
+ lessonHash: 'aaa1111111111111',
33
+ lessonHeading: 'WWND Rule 1: Absolute-promise detection on public surfaces',
34
+ pattern: ABSOLUTE_PROMISE_PATTERN,
35
+ },
36
+ ]));
37
+ const result = await doctorClaimDisciplineCommand({ repoRootForTest: tmpDir });
38
+ expect(result.valid).toBe(true);
39
+ expect(result.findings).toHaveLength(0);
40
+ expect(result.bypassed).toBe(false);
41
+ });
42
+ it('returns valid+inert-warning when in-scope surfaces exist but no WWND rules compiled', async () => {
43
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), '# Project\n\nClean prose.\n');
44
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
45
+ {
46
+ lessonHash: 'bbb1111111111111',
47
+ lessonHeading: 'Some unrelated rule',
48
+ pattern: 'XYZ',
49
+ },
50
+ ]));
51
+ const result = await doctorClaimDisciplineCommand({ repoRootForTest: tmpDir });
52
+ expect(result.valid).toBe(true);
53
+ expect(result.findings).toHaveLength(0);
54
+ expect(result.warnings.some((w) => w.includes('No WWND rules found'))).toBe(true);
55
+ });
56
+ it('fires Rule 1 on absolute-promise prose in README.md', async () => {
57
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), '# Project\n\nThe core will always be free. We guarantees this.\n');
58
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
59
+ {
60
+ lessonHash: 'aaa1111111111111',
61
+ lessonHeading: 'WWND Rule 1: Absolute-promise detection on public surfaces',
62
+ pattern: ABSOLUTE_PROMISE_PATTERN,
63
+ },
64
+ ]));
65
+ const result = await doctorClaimDisciplineCommand({ repoRootForTest: tmpDir });
66
+ expect(result.findings.length).toBeGreaterThanOrEqual(1);
67
+ // "will always be" should match
68
+ expect(result.findings.some((f) => /will\s+always\s+be/i.test(f.match))).toBe(true);
69
+ // All findings should be warning severity (Rule 1 default)
70
+ expect(result.findings.every((f) => f.severity === 'warning')).toBe(true);
71
+ // Validity: warning-severity findings don't fail the gate by default
72
+ expect(result.valid).toBe(true);
73
+ expect(result.bypassed).toBe(false);
74
+ });
75
+ it('does NOT fire Rule 1 on softened/backed prose', async () => {
76
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), '# Project\n\nThe core stays free under the MIT LICENSE. We aim to keep it stable.\n');
77
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
78
+ {
79
+ lessonHash: 'aaa1111111111111',
80
+ lessonHeading: 'WWND Rule 1: Absolute-promise detection on public surfaces',
81
+ pattern: ABSOLUTE_PROMISE_PATTERN,
82
+ },
83
+ ]));
84
+ const result = await doctorClaimDisciplineCommand({ repoRootForTest: tmpDir });
85
+ expect(result.findings).toHaveLength(0);
86
+ expect(result.valid).toBe(true);
87
+ });
88
+ it('error-severity findings fail the gate when no bypass is set', async () => {
89
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), 'We guarantees everything.\n');
90
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
91
+ {
92
+ lessonHash: 'aaa1111111111111',
93
+ lessonHeading: 'WWND Rule 1: Absolute-promise detection on public surfaces',
94
+ pattern: ABSOLUTE_PROMISE_PATTERN,
95
+ severity: 'error',
96
+ },
97
+ ]));
98
+ const result = await doctorClaimDisciplineCommand({ repoRootForTest: tmpDir });
99
+ expect(result.findings.length).toBeGreaterThanOrEqual(1);
100
+ expect(result.findings.every((f) => f.severity === 'error')).toBe(true);
101
+ expect(result.valid).toBe(false);
102
+ });
103
+ it('TOTEM_GATE_BYPASS_JUSTIFICATION passes the gate and records justification', async () => {
104
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), 'We guarantees everything.\n');
105
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
106
+ {
107
+ lessonHash: 'aaa1111111111111',
108
+ lessonHeading: 'WWND Rule 1: Absolute-promise detection on public surfaces',
109
+ pattern: ABSOLUTE_PROMISE_PATTERN,
110
+ severity: 'error',
111
+ },
112
+ ]));
113
+ const result = await doctorClaimDisciplineCommand({
114
+ repoRootForTest: tmpDir,
115
+ envForTest: {
116
+ TOTEM_GATE_BYPASS_JUSTIFICATION: 'one-off marketing release; will address in follow-up PR',
117
+ },
118
+ });
119
+ expect(result.findings.length).toBeGreaterThanOrEqual(1);
120
+ expect(result.bypassed).toBe(true);
121
+ expect(result.bypassJustification).toBe('one-off marketing release; will address in follow-up PR');
122
+ expect(result.valid).toBe(true);
123
+ });
124
+ it('empty/whitespace TOTEM_GATE_BYPASS_JUSTIFICATION does NOT bypass', async () => {
125
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), 'We guarantees everything.\n');
126
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
127
+ {
128
+ lessonHash: 'aaa1111111111111',
129
+ lessonHeading: 'WWND Rule 1: Absolute-promise detection on public surfaces',
130
+ pattern: ABSOLUTE_PROMISE_PATTERN,
131
+ severity: 'error',
132
+ },
133
+ ]));
134
+ const result = await doctorClaimDisciplineCommand({
135
+ repoRootForTest: tmpDir,
136
+ envForTest: { TOTEM_GATE_BYPASS_JUSTIFICATION: ' ' },
137
+ });
138
+ expect(result.bypassed).toBe(false);
139
+ expect(result.valid).toBe(false);
140
+ });
141
+ // Note: ast/ast-grep engine skip-with-warning path is structurally testable
142
+ // but loadCompiledRules schema-validates the full rule shape before
143
+ // discoverWwndRules sees them, so a schema-incomplete ast-grep fixture
144
+ // fails at load. PR β (which actually adds ast/ast-grep WWND rules) will
145
+ // exercise the skip-with-warning path with proper fixtures.
146
+ it('skips WWND rules with invalid regex (warning, not crash)', async () => {
147
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), 'Some text.\n');
148
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
149
+ {
150
+ lessonHash: 'ddd1111111111111',
151
+ lessonHeading: 'WWND Rule Y: Bad regex',
152
+ pattern: '[unterminated character class',
153
+ },
154
+ ]));
155
+ const result = await doctorClaimDisciplineCommand({ repoRootForTest: tmpDir });
156
+ expect(result.findings).toHaveLength(0);
157
+ expect(result.warnings.some((w) => w.includes('invalid regex'))).toBe(true);
158
+ });
159
+ it('skips non-active rules (status: archived)', async () => {
160
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), 'We guarantees everything.\n');
161
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
162
+ {
163
+ lessonHash: 'eee1111111111111',
164
+ lessonHeading: 'WWND Rule Z: Archived rule',
165
+ pattern: ABSOLUTE_PROMISE_PATTERN,
166
+ status: 'archived',
167
+ },
168
+ ]));
169
+ const result = await doctorClaimDisciplineCommand({ repoRootForTest: tmpDir });
170
+ expect(result.findings).toHaveLength(0);
171
+ });
172
+ it('handles missing compiled-rules.json gracefully (warning, valid)', async () => {
173
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), 'Some text.\n');
174
+ // No compiled-rules.json written
175
+ const result = await doctorClaimDisciplineCommand({ repoRootForTest: tmpDir });
176
+ expect(result.valid).toBe(true);
177
+ expect(result.findings).toHaveLength(0);
178
+ expect(result.warnings.some((w) => w.includes('compiled-rules.json not found'))).toBe(true);
179
+ });
180
+ it('scans AGENTS.md as in-scope surface', async () => {
181
+ fs.writeFileSync(path.join(tmpDir, 'AGENTS.md'), 'We promises to deliver.\n');
182
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
183
+ {
184
+ lessonHash: 'aaa1111111111111',
185
+ lessonHeading: 'WWND Rule 1: Absolute-promise detection on public surfaces',
186
+ pattern: ABSOLUTE_PROMISE_PATTERN,
187
+ },
188
+ ]));
189
+ const result = await doctorClaimDisciplineCommand({ repoRootForTest: tmpDir });
190
+ expect(result.findings.length).toBeGreaterThanOrEqual(1);
191
+ expect(result.findings.some((f) => f.file === 'AGENTS.md')).toBe(true);
192
+ });
193
+ it('recursively walks docs/wiki/ for .md files', async () => {
194
+ fs.mkdirSync(path.join(tmpDir, 'docs', 'wiki', 'nested'), { recursive: true });
195
+ fs.writeFileSync(path.join(tmpDir, 'docs', 'wiki', 'top.md'), 'Top-level wiki page guarantees everything.\n');
196
+ fs.writeFileSync(path.join(tmpDir, 'docs', 'wiki', 'nested', 'inner.md'), 'Nested wiki page promises to be perfect.\n');
197
+ fs.writeFileSync(path.join(tmpDir, '.totem', 'compiled-rules.json'), buildRulesFile([
198
+ {
199
+ lessonHash: 'aaa1111111111111',
200
+ lessonHeading: 'WWND Rule 1: Absolute-promise detection on public surfaces',
201
+ pattern: ABSOLUTE_PROMISE_PATTERN,
202
+ },
203
+ ]));
204
+ const result = await doctorClaimDisciplineCommand({ repoRootForTest: tmpDir });
205
+ const files = result.findings.map((f) => f.file);
206
+ expect(files).toContain('docs/wiki/top.md');
207
+ expect(files).toContain('docs/wiki/nested/inner.md');
208
+ });
209
+ });
210
+ //# sourceMappingURL=doctor-claim-discipline.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-claim-discipline.test.js","sourceRoot":"","sources":["../../src/commands/doctor-claim-discipline.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"install-hooks.d.ts","sourceRoot":"","sources":["../../src/commands/install-hooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"install-hooks.d.ts","sourceRoot":"","sources":["../../src/commands/install-hooks.ts"],"names":[],"mappings":"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"}
@@ -310,6 +310,17 @@ if [ -n "$TOTEM_CMD" ]; then
310
310
  exit 1
311
311
  fi
312
312
  fi
313
+
314
+ # WWND claim-discipline gate (Proposal 279 § Implementation Notes Q3 —
315
+ # slot after verify-badges; gates on public-surface absolute promises,
316
+ # missing-Goal-prefix, covenant-without-backing). Fires only when at
317
+ # least one in-scope surface exists. Bypass with mandatory justification:
318
+ # TOTEM_GATE_BYPASS_JUSTIFICATION="<reason>" git push
319
+ if [ -f ".totem/compiled-rules.json" ] && { [ -f "README.md" ] || [ -f "AGENTS.md" ] || [ -f "design-tenets.md" ] || [ -d "docs/wiki" ]; }; then
320
+ if ! $TOTEM_CMD doctor --claim-discipline --strict; then
321
+ exit 1
322
+ fi
323
+ fi
313
324
  ${shieldBlock}
314
325
  fi
315
326