@devrik-tools/claude-gates 0.1.2 → 0.2.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 (106) hide show
  1. package/.claude-plugin/marketplace.json +7 -1
  2. package/cli/__tests__/config.test.mjs +113 -101
  3. package/cli/__tests__/install.test.mjs +102 -0
  4. package/cli/__tests__/materialize.test.mjs +95 -0
  5. package/cli/__tests__/registry-gates-consistency.test.mjs +52 -44
  6. package/cli/__tests__/task.test.mjs +124 -0
  7. package/cli/__tests__/version-consistency.test.mjs +44 -0
  8. package/cli/config.mjs +124 -113
  9. package/cli/index.mjs +100 -97
  10. package/cli/init.mjs +259 -256
  11. package/cli/install.mjs +150 -80
  12. package/cli/materialize.mjs +102 -51
  13. package/cli/registry.mjs +136 -127
  14. package/cli/task.mjs +140 -0
  15. package/package.json +2 -1
  16. package/plugins/gates/.claude-plugin/plugin.json +8 -2
  17. package/plugins/gates/hooks/__tests__/ask-adoption.test.mjs +83 -0
  18. package/plugins/gates/hooks/__tests__/doctor.test.mjs +85 -0
  19. package/plugins/gates/hooks/__tests__/wiring-check.test.mjs +65 -0
  20. package/plugins/gates/hooks/ask-adoption.mjs +147 -0
  21. package/plugins/gates/hooks/doctor.mjs +207 -0
  22. package/plugins/gates/hooks/gates/audit-before-build/audit-before-build.edge.test.mjs +83 -0
  23. package/plugins/gates/hooks/gates/audit-before-build/index.mjs +88 -88
  24. package/plugins/gates/hooks/gates/audit-before-build/test.mjs +117 -92
  25. package/plugins/gates/hooks/gates/autonomous-mode/autonomous.edge.test.mjs +104 -0
  26. package/plugins/gates/hooks/gates/autonomous-mode/index.mjs +48 -45
  27. package/plugins/gates/hooks/gates/bash-commands/bash-commands.edge.test.mjs +165 -0
  28. package/plugins/gates/hooks/gates/bash-commands/index.mjs +285 -268
  29. package/plugins/gates/hooks/gates/brief-before-delegate/brief-before-delegate.edge.test.mjs +151 -0
  30. package/plugins/gates/hooks/gates/brief-before-delegate/index.mjs +265 -177
  31. package/plugins/gates/hooks/gates/circuit-breaker/circuit-breaker.edge.test.mjs +207 -0
  32. package/plugins/gates/hooks/gates/circuit-breaker/index.mjs +501 -456
  33. package/plugins/gates/hooks/gates/circuit-breaker/test.mjs +161 -143
  34. package/plugins/gates/hooks/gates/dependency-skills/dependency-skills.edge.test.mjs +69 -0
  35. package/plugins/gates/hooks/gates/dependency-skills/index.mjs +126 -118
  36. package/plugins/gates/hooks/gates/diagnosis-before-patch/diagnosis-before-patch.edge.test.mjs +68 -0
  37. package/plugins/gates/hooks/gates/diagnosis-before-patch/index.mjs +43 -49
  38. package/plugins/gates/hooks/gates/feature-catalog/edge.edge.test.mjs +83 -0
  39. package/plugins/gates/hooks/gates/feature-catalog/index.mjs +83 -100
  40. package/plugins/gates/hooks/gates/force-parallel/index.mjs +119 -0
  41. package/plugins/gates/hooks/gates/force-parallel/test.mjs +88 -0
  42. package/plugins/gates/hooks/gates/forge-flow/forge-flow.edge.test.mjs +155 -0
  43. package/plugins/gates/hooks/gates/forge-flow/index.mjs +134 -112
  44. package/plugins/gates/hooks/gates/implementation-pipeline/edge.edge.test.mjs +66 -0
  45. package/plugins/gates/hooks/gates/implementation-pipeline/index.mjs +187 -186
  46. package/plugins/gates/hooks/gates/intent-flow/index.mjs +260 -238
  47. package/plugins/gates/hooks/gates/intent-flow/intent-flow.edge.test.mjs +96 -0
  48. package/plugins/gates/hooks/gates/intent-flow/test.mjs +165 -136
  49. package/plugins/gates/hooks/gates/lint-commit/index.mjs +149 -0
  50. package/plugins/gates/hooks/gates/lint-commit/test.mjs +85 -0
  51. package/plugins/gates/hooks/gates/mandatory-flow/edge.edge.test.mjs +100 -0
  52. package/plugins/gates/hooks/gates/mandatory-flow/index.mjs +180 -166
  53. package/plugins/gates/hooks/gates/neutral-spanish/index.mjs +79 -85
  54. package/plugins/gates/hooks/gates/neutral-spanish/neutral-spanish.edge.test.mjs +80 -0
  55. package/plugins/gates/hooks/gates/never-assume/index.mjs +58 -55
  56. package/plugins/gates/hooks/gates/never-assume/never-assume.edge.test.mjs +71 -0
  57. package/plugins/gates/hooks/gates/never-assume/test.mjs +95 -78
  58. package/plugins/gates/hooks/gates/no-blocking/index.mjs +148 -142
  59. package/plugins/gates/hooks/gates/no-blocking/no-blocking.edge.test.mjs +109 -0
  60. package/plugins/gates/hooks/gates/no-memory-dependency/index.mjs +123 -120
  61. package/plugins/gates/hooks/gates/no-memory-dependency/no-memory-dependency.edge.test.mjs +83 -0
  62. package/plugins/gates/hooks/gates/no-reconfirm/no-reconfirm.edge.test.mjs +91 -0
  63. package/plugins/gates/hooks/gates/protected-paths/index.mjs +144 -147
  64. package/plugins/gates/hooks/gates/protected-paths/protected-paths.edge.test.mjs +126 -0
  65. package/plugins/gates/hooks/gates/recurrence-lock/index.mjs +89 -64
  66. package/plugins/gates/hooks/gates/recurrence-lock/recurrence-lock.edge.test.mjs +102 -0
  67. package/plugins/gates/hooks/gates/reuse-before-build/edge.edge.test.mjs +94 -0
  68. package/plugins/gates/hooks/gates/reuse-before-build/index.mjs +159 -150
  69. package/plugins/gates/hooks/gates/risk-level/index.mjs +263 -203
  70. package/plugins/gates/hooks/gates/risk-level/risk-level.edge.test.mjs +116 -0
  71. package/plugins/gates/hooks/gates/risk-level/test.mjs +153 -125
  72. package/plugins/gates/hooks/gates/root-cause-first/index.mjs +56 -50
  73. package/plugins/gates/hooks/gates/root-cause-first/root-cause-first.edge.test.mjs +58 -0
  74. package/plugins/gates/hooks/gates/root-whitelist/index.mjs +116 -111
  75. package/plugins/gates/hooks/gates/root-whitelist/root-whitelist.edge.test.mjs +97 -0
  76. package/plugins/gates/hooks/gates/rule-skill-autodiscovery/index.mjs +184 -99
  77. package/plugins/gates/hooks/gates/rule-skill-autodiscovery/rule-skill-autodiscovery.edge.test.mjs +126 -0
  78. package/plugins/gates/hooks/gates/sdd-specs/edge.edge.test.mjs +130 -0
  79. package/plugins/gates/hooks/gates/sdd-specs/index.mjs +256 -251
  80. package/plugins/gates/hooks/gates/stop-pending/index.mjs +159 -0
  81. package/plugins/gates/hooks/gates/stop-pending/test.mjs +114 -0
  82. package/plugins/gates/hooks/gates/test-after-implementation/index.mjs +101 -101
  83. package/plugins/gates/hooks/gates/test-after-implementation/test-after-implementation.edge.test.mjs +72 -0
  84. package/plugins/gates/hooks/gates/test-matrix/edge.edge.test.mjs +63 -0
  85. package/plugins/gates/hooks/gates/test-matrix/index.mjs +187 -181
  86. package/plugins/gates/hooks/gates/test-matrix/test.mjs +114 -87
  87. package/plugins/gates/hooks/gates/tool-map/edge.edge.test.mjs +62 -0
  88. package/plugins/gates/hooks/gates/tool-map/index.mjs +143 -140
  89. package/plugins/gates/hooks/hooks.json +346 -286
  90. package/plugins/gates/hooks/lib/__tests__/config.test.mjs +173 -154
  91. package/plugins/gates/hooks/lib/__tests__/hook-io.test.mjs +210 -154
  92. package/plugins/gates/hooks/lib/__tests__/signals.test.mjs +114 -0
  93. package/plugins/gates/hooks/lib/config.mjs +172 -165
  94. package/plugins/gates/hooks/lib/hook-io-config.edge.test.mjs +189 -0
  95. package/plugins/gates/hooks/lib/hook-io.mjs +318 -208
  96. package/plugins/gates/hooks/lib/signals.mjs +127 -0
  97. package/plugins/gates/hooks/wiring-check.mjs +227 -0
  98. package/plugins/tasks/.claude-plugin/plugin.json +14 -0
  99. package/plugins/tasks/hooks/__tests__/register-requests.test.mjs +100 -0
  100. package/plugins/tasks/hooks/__tests__/session-tasks.test.mjs +95 -0
  101. package/plugins/tasks/hooks/hooks.json +26 -0
  102. package/plugins/tasks/hooks/lib/__tests__/task-store.test.mjs +180 -132
  103. package/plugins/tasks/hooks/lib/task-store.mjs +197 -159
  104. package/plugins/tasks/hooks/register-requests.mjs +141 -108
  105. package/plugins/tasks/hooks/session-tasks.mjs +108 -0
  106. package/registry.json +793 -686
@@ -1,55 +1,58 @@
1
- import { runGate, warn, TOOL_GROUPS } from '../../lib/hook-io.mjs';
2
-
3
- const GATE_ID = 'never-assume';
4
- const CONFIG_KEY = 'requireVerificationBeforeAssuming';
5
-
6
- const DEFAULT_CONJECTURE_PATTERNS = [
7
- 'i assume',
8
- 'assuming that',
9
- 'probably',
10
- 'i guess',
11
- 'should be',
12
- "i'll default to",
13
- ];
14
-
15
- function extractContent(toolName, toolInput) {
16
- if (TOOL_GROUPS.delegation.includes(toolName)) return toolInput?.prompt;
17
- return toolInput?.content ?? toolInput?.new_string;
18
- }
19
-
20
- runGate(
21
- {
22
- id: GATE_ID,
23
- configKey: CONFIG_KEY,
24
- enabledByDefault: false,
25
- defaultParams: {
26
- conjecturePatterns: DEFAULT_CONJECTURE_PATTERNS,
27
- },
28
- },
29
- ({ toolName, toolInput, parameters }) => {
30
- const isWrite = TOOL_GROUPS.write.includes(toolName);
31
- const isDelegation = TOOL_GROUPS.delegation.includes(toolName);
32
- if (!isWrite && !isDelegation) return;
33
-
34
- const content = extractContent(toolName, toolInput);
35
- if (typeof content !== 'string') return;
36
-
37
- const patterns = parameters.conjecturePatterns.map(
38
- (source) => new RegExp(source, 'i'),
39
- );
40
- const hits = [];
41
- for (const pattern of patterns) {
42
- const match = content.match(pattern);
43
- if (match) hits.push(match[0]);
44
- }
45
- if (hits.length === 0) return;
46
-
47
- warn(
48
- GATE_ID,
49
- `Content contains conjecture phrasing without stated verification: ${hits.join(', ')}. Verify before asserting instead of assuming.`,
50
- );
51
- },
52
- );
53
-
54
- // The source guard (guard-never-assume.mjs) is explicit: this never denies,
55
- // only warns — conjecture language is a prompt to verify, not a blocker.
1
+ import {
2
+ runGate,
3
+ warn,
4
+ toolInGroups,
5
+ writtenContentOf,
6
+ delegationPromptOf,
7
+ } from '../../lib/hook-io.mjs';
8
+ import { CONJECTURE_SOURCES } from '../../lib/signals.mjs';
9
+
10
+ const GATE_ID = 'never-assume';
11
+ const CONFIG_KEY = 'requireVerificationBeforeAssuming';
12
+
13
+ // Bilingual (ES+EN) conjecture phrasing, centralized in lib/signals.mjs so every gate
14
+ // that needs to recognize unverified-assumption prose shares the same coverage. See
15
+ // signals.mjs header for the ES/EN-only limitation.
16
+ const DEFAULT_CONJECTURE_PATTERNS = CONJECTURE_SOURCES;
17
+
18
+ function extractContent(toolName, toolInput) {
19
+ if (toolInGroups(toolName, ['delegation'])) return delegationPromptOf(toolInput);
20
+ return writtenContentOf(toolInput);
21
+ }
22
+
23
+ runGate(
24
+ {
25
+ id: GATE_ID,
26
+ configKey: CONFIG_KEY,
27
+ enabledByDefault: false,
28
+ defaultParams: {
29
+ conjecturePatterns: DEFAULT_CONJECTURE_PATTERNS,
30
+ },
31
+ },
32
+ ({ toolName, toolInput, parameters }) => {
33
+ const isWrite = toolInGroups(toolName, ['write']);
34
+ const isDelegation = toolInGroups(toolName, ['delegation']);
35
+ if (!isWrite && !isDelegation) return;
36
+
37
+ const content = extractContent(toolName, toolInput);
38
+ if (!content) return;
39
+
40
+ const patterns = parameters.conjecturePatterns.map(
41
+ (source) => new RegExp(source, 'i'),
42
+ );
43
+ const hits = [];
44
+ for (const pattern of patterns) {
45
+ const match = content.match(pattern);
46
+ if (match) hits.push(match[0]);
47
+ }
48
+ if (hits.length === 0) return;
49
+
50
+ warn(
51
+ GATE_ID,
52
+ `Content contains conjecture phrasing without stated verification: ${hits.join(', ')}. Verify before asserting instead of assuming.`,
53
+ );
54
+ },
55
+ );
56
+
57
+ // The source guard (guard-never-assume.mjs) is explicit: this never denies,
58
+ // only warns — conjecture language is a prompt to verify, not a blocker.
@@ -0,0 +1,71 @@
1
+ import assert from 'node:assert/strict';
2
+ import { execFileSync } from 'node:child_process';
3
+ import { mkdtempSync, mkdirSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { dirname, join } from 'node:path';
6
+ import { test } from 'node:test';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const GATE = join(dirname(fileURLToPath(import.meta.url)), 'index.mjs');
10
+
11
+ function runGate(payload, { config } = {}) {
12
+ const project = mkdtempSync(join(tmpdir(), 'never-assume-edge-'));
13
+ mkdirSync(join(project, '.git'));
14
+ if (config) {
15
+ mkdirSync(join(project, '.ai'));
16
+ writeFileSync(join(project, '.ai', 'config.json'), JSON.stringify(config));
17
+ }
18
+ const out = execFileSync(process.execPath, [GATE], {
19
+ input: JSON.stringify(payload),
20
+ encoding: 'utf8',
21
+ cwd: project,
22
+ env: { ...process.env, HOME: project, USERPROFILE: project },
23
+ });
24
+ return out.trim() ? JSON.parse(out.trim()) : null;
25
+ }
26
+
27
+ function isWarn(result) {
28
+ return result?.hookSpecificOutput?.additionalContext !== undefined;
29
+ }
30
+
31
+ const ENABLE = { config: { gates: { requireVerificationBeforeAssuming: true } } };
32
+
33
+ // FIXED: extractContent() now reads through writtenContentOf() (lib/hook-io.mjs), which
34
+ // covers NotebookEdit's new_source field, closing the tool-coverage gap this test used to
35
+ // document as a bug.
36
+ test('FIXED: conjecture phrasing written via NotebookEdit is detected', () => {
37
+ const payload = {
38
+ tool_name: 'NotebookEdit',
39
+ tool_input: {
40
+ notebook_path: '/repo/nb.ipynb',
41
+ cell_id: 'x',
42
+ new_source: '# i assume the timezone is UTC',
43
+ cell_type: 'code',
44
+ },
45
+ };
46
+ assert.ok(isWarn(runGate(payload, ENABLE)), 'gate now detects conjecture phrasing via NotebookEdit');
47
+ });
48
+
49
+ // EDGE CASE (BUG): 'invoke_subagent' is a declared delegation tool name (TOOL_GROUPS.delegation
50
+ // in lib/hook-io.mjs) and is matched in hooks.json, but extractContent() branches on
51
+ // TOOL_GROUPS.delegation.includes(toolName) which DOES include invoke_subagent — so this one
52
+ // should actually work. Verify it does (a real behavioral check, not a bug) to make sure the
53
+ // delegation branch itself is sound before concluding the write-side gap is the only issue.
54
+ test('OK: conjecture phrasing in an invoke_subagent prompt is detected (delegation branch is sound)', () => {
55
+ const payload = {
56
+ tool_name: 'invoke_subagent',
57
+ tool_input: { prompt: 'i assume the config is already loaded, proceed' },
58
+ };
59
+ assert.ok(isWarn(runGate(payload, ENABLE)));
60
+ });
61
+
62
+ // EDGE CASE: false-positive check — "should be" is a very broad conjecture pattern that can
63
+ // match ordinary correct engineering prose describing an invariant, not an unverified guess,
64
+ // e.g. "the response should be a 200 for valid input" (spec language, not conjecture).
65
+ test('OK/expected-noise: "should be" fires even on spec-style invariant language (broad pattern, not a bug but a known false-positive source)', () => {
66
+ const result = runGate(
67
+ { tool_name: 'Write', tool_input: { file_path: '/repo/x.js', content: '// per the spec, the response should be a 200 for valid input' } },
68
+ ENABLE,
69
+ );
70
+ assert.ok(isWarn(result), 'documents that "should be" over-fires on legitimate spec prose, not just conjecture');
71
+ });
@@ -1,78 +1,95 @@
1
- import assert from 'node:assert/strict';
2
- import { execFileSync } from 'node:child_process';
3
- import { mkdtempSync, mkdirSync, writeFileSync } from 'node:fs';
4
- import { tmpdir } from 'node:os';
5
- import { dirname, join } from 'node:path';
6
- import { test } from 'node:test';
7
- import { fileURLToPath } from 'node:url';
8
-
9
- const GATE = join(dirname(fileURLToPath(import.meta.url)), 'index.mjs');
10
-
11
- function runGate(payload, { config } = {}) {
12
- const project = mkdtempSync(join(tmpdir(), 'never-assume-'));
13
- mkdirSync(join(project, '.git'));
14
- if (config) {
15
- mkdirSync(join(project, '.ai'));
16
- writeFileSync(join(project, '.ai', 'config.json'), JSON.stringify(config));
17
- }
18
- const out = execFileSync(process.execPath, [GATE], {
19
- input: JSON.stringify(payload),
20
- encoding: 'utf8',
21
- cwd: project,
22
- // Isolate from the user's real global config: point homedir() at the temp
23
- // project so the global-config fallback finds nothing (registry default).
24
- env: { ...process.env, HOME: project, USERPROFILE: project },
25
- });
26
- return out.trim() ? JSON.parse(out.trim()) : null;
27
- }
28
-
29
- function write(content) {
30
- return {
31
- tool_name: 'Write',
32
- tool_input: { file_path: '/repo/src/x.js', content },
33
- };
34
- }
35
- function isWarn(result) {
36
- return result?.hookSpecificOutput?.additionalContext !== undefined;
37
- }
38
- function isDeny(result) {
39
- return result?.hookSpecificOutput?.permissionDecision === 'deny';
40
- }
41
-
42
- const ENABLE = {
43
- config: { gates: { requireVerificationBeforeAssuming: true } },
44
- };
45
-
46
- test('warns on conjecture phrasing', () => {
47
- assert.ok(isWarn(runGate(write('// i assume the timezone is UTC'), ENABLE)));
48
- assert.ok(isWarn(runGate(write('// probably fine to skip this'), ENABLE)));
49
- });
50
-
51
- test('never denies, only warns', () => {
52
- const result = runGate(write('// i assume this works'), ENABLE);
53
- assert.ok(isWarn(result));
54
- assert.ok(!isDeny(result));
55
- });
56
-
57
- test('allows content without conjecture phrasing', () => {
58
- assert.equal(runGate(write('const x = 1;'), ENABLE), null);
59
- });
60
-
61
- test('disabled by default (registry default is false)', () => {
62
- assert.equal(runGate(write('// i assume this works')), null);
63
- });
64
-
65
- test('project conjecturePatterns override replaces the built-in list', () => {
66
- const config = {
67
- gates: {
68
- requireVerificationBeforeAssuming: {
69
- enabled: true,
70
- conjecturePatterns: ['totally made up'],
71
- },
72
- },
73
- };
74
- assert.equal(runGate(write('// i assume this works'), { config }), null);
75
- assert.ok(
76
- isWarn(runGate(write('// totally made up value here'), { config })),
77
- );
78
- });
1
+ import assert from 'node:assert/strict';
2
+ import { execFileSync } from 'node:child_process';
3
+ import { mkdtempSync, mkdirSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { dirname, join } from 'node:path';
6
+ import { test } from 'node:test';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const GATE = join(dirname(fileURLToPath(import.meta.url)), 'index.mjs');
10
+
11
+ function runGate(payload, { config } = {}) {
12
+ const project = mkdtempSync(join(tmpdir(), 'never-assume-'));
13
+ mkdirSync(join(project, '.git'));
14
+ if (config) {
15
+ mkdirSync(join(project, '.ai'));
16
+ writeFileSync(join(project, '.ai', 'config.json'), JSON.stringify(config));
17
+ }
18
+ const out = execFileSync(process.execPath, [GATE], {
19
+ input: JSON.stringify(payload),
20
+ encoding: 'utf8',
21
+ cwd: project,
22
+ // Isolate from the user's real global config: point homedir() at the temp
23
+ // project so the global-config fallback finds nothing (registry default).
24
+ env: { ...process.env, HOME: project, USERPROFILE: project },
25
+ });
26
+ return out.trim() ? JSON.parse(out.trim()) : null;
27
+ }
28
+
29
+ function write(content) {
30
+ return {
31
+ tool_name: 'Write',
32
+ tool_input: { file_path: '/repo/src/x.js', content },
33
+ };
34
+ }
35
+ function isWarn(result) {
36
+ return result?.hookSpecificOutput?.additionalContext !== undefined;
37
+ }
38
+ function isDeny(result) {
39
+ return result?.hookSpecificOutput?.permissionDecision === 'deny';
40
+ }
41
+
42
+ const ENABLE = {
43
+ config: { gates: { requireVerificationBeforeAssuming: true } },
44
+ };
45
+
46
+ test('warns on conjecture phrasing', () => {
47
+ assert.ok(isWarn(runGate(write('// i assume the timezone is UTC'), ENABLE)));
48
+ assert.ok(isWarn(runGate(write('// probably fine to skip this'), ENABLE)));
49
+ });
50
+
51
+ test('never denies, only warns', () => {
52
+ const result = runGate(write('// i assume this works'), ENABLE);
53
+ assert.ok(isWarn(result));
54
+ assert.ok(!isDeny(result));
55
+ });
56
+
57
+ test('allows content without conjecture phrasing', () => {
58
+ assert.equal(runGate(write('const x = 1;'), ENABLE), null);
59
+ });
60
+
61
+ test('disabled by default (registry default is false)', () => {
62
+ assert.equal(runGate(write('// i assume this works')), null);
63
+ });
64
+
65
+ test('warns on conjecture phrasing in Spanish', () => {
66
+ assert.ok(isWarn(runGate(write('// supongo que la zona horaria es UTC'), ENABLE)));
67
+ assert.ok(isWarn(runGate(write('// probablemente sea correcto omitir esto'), ENABLE)));
68
+ assert.ok(isWarn(runGate(write('// deberia ser suficiente con este chequeo'), ENABLE)));
69
+ });
70
+
71
+ test('bilingual control: ES and EN equivalent conjecture briefs both warn', () => {
72
+ const es = write('// creo que el usuario ya esta autenticado, no lo verifique');
73
+ const en = write('// i assume the user is already authenticated, did not verify it');
74
+ assert.ok(isWarn(runGate(es, ENABLE)));
75
+ assert.ok(isWarn(runGate(en, ENABLE)));
76
+ });
77
+
78
+ test('allows neutral Spanish content without conjecture phrasing', () => {
79
+ assert.equal(runGate(write('const contrasena = "verificada";'), ENABLE), null);
80
+ });
81
+
82
+ test('project conjecturePatterns override replaces the built-in list', () => {
83
+ const config = {
84
+ gates: {
85
+ requireVerificationBeforeAssuming: {
86
+ enabled: true,
87
+ conjecturePatterns: ['totally made up'],
88
+ },
89
+ },
90
+ };
91
+ assert.equal(runGate(write('// i assume this works'), { config }), null);
92
+ assert.ok(
93
+ isWarn(runGate(write('// totally made up value here'), { config })),
94
+ );
95
+ });