principles-disciple 1.32.0 → 1.34.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 (37) hide show
  1. package/openclaw.plugin.json +4 -4
  2. package/package.json +1 -1
  3. package/src/core/correction-cue-learner.ts +203 -0
  4. package/src/core/correction-types.ts +88 -0
  5. package/src/core/evolution-logger.ts +3 -3
  6. package/src/core/init.ts +67 -0
  7. package/src/service/correction-observer-types.ts +58 -0
  8. package/src/service/correction-observer-workflow-manager.ts +218 -0
  9. package/src/service/evolution-worker.ts +172 -146
  10. package/src/service/nocturnal-service.ts +4 -1
  11. package/src/service/subagent-workflow/index.ts +14 -0
  12. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +3 -1
  13. package/tests/service/evolution-worker.nocturnal.test.ts +14 -1
  14. package/tests/service/evolution-worker.timeout.test.ts +350 -0
  15. package/tests/commands/implementation-lifecycle.test.ts +0 -362
  16. package/tests/core/detection-funnel.test.ts +0 -63
  17. package/tests/core/evolution-e2e.test.ts +0 -58
  18. package/tests/core/evolution-engine-gate-integration.test.ts +0 -543
  19. package/tests/core/evolution-engine.test.ts +0 -562
  20. package/tests/core/evolution-reducer.test.ts +0 -180
  21. package/tests/core/evolution-user-stories.e2e.test.ts +0 -249
  22. package/tests/core/local-worker-routing.test.ts +0 -757
  23. package/tests/core/rule-host.test.ts +0 -389
  24. package/tests/core/trajectory-correction-pain.test.ts +0 -180
  25. package/tests/hooks/gate-edit-verification.test.ts +0 -435
  26. package/tests/hooks/llm.test.ts +0 -308
  27. package/tests/hooks/progressive-trust-gate.test.ts +0 -277
  28. package/tests/hooks/prompt.test.ts +0 -1473
  29. package/tests/index.integration.test.ts +0 -179
  30. package/tests/index.shadow-routing.integration.test.ts +0 -140
  31. package/tests/service/evolution-worker.test.ts +0 -462
  32. package/tests/service/nocturnal-service.test.ts +0 -577
  33. package/tests/service/nocturnal-workflow-manager.test.ts +0 -441
  34. package/tests/tools/critique-prompt.test.ts +0 -260
  35. package/tests/tools/deep-reflect.test.ts +0 -232
  36. package/tests/tools/model-index.test.ts +0 -246
  37. package/tests/ui/app.test.tsx +0 -114
@@ -1,441 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import type { TrinityRuntimeAdapter } from '../../core/nocturnal-trinity.js';
3
-
4
- // Mock dependencies
5
- vi.mock('../../src/core/nocturnal-trinity.js', () => ({
6
- runTrinityAsync: vi.fn(),
7
- TrinityStageFailure: {
8
- stage: 'dreamer',
9
- reason: '',
10
- },
11
- }));
12
-
13
- vi.mock('../../src/core/nocturnal-paths.js', () => ({
14
- NocturnalPathResolver: {
15
- samplePath: vi.fn((workspaceDir: string, sampleId: string) =>
16
- `${workspaceDir}/.state/nocturnal/samples/${sampleId}.json`
17
- ),
18
- resolveNocturnalDir: vi.fn((workspaceDir: string, _type: string) =>
19
- `${workspaceDir}/.state/nocturnal/samples`
20
- ),
21
- },
22
- }));
23
-
24
- import { NocturnalWorkflowManager, nocturnalWorkflowSpec } from '../../src/service/subagent-workflow/nocturnal-workflow-manager.js';
25
- import { runTrinityAsync } from '../../src/core/nocturnal-trinity.js';
26
- import type { TrinityStageFailure, TrinityResult, DreamerOutput, PhilosopherOutput, TrinityDraftArtifact, TrinityTelemetry, TrinityConfig } from '../../src/core/nocturnal-trinity.js';
27
-
28
- const mockRunTrinityAsync = runTrinityAsync as ReturnType<typeof vi.fn>;
29
-
30
- function createMockRuntimeAdapter() {
31
- return {
32
- isRuntimeAvailable: vi.fn(() => true),
33
- getLastFailureReason: vi.fn(() => null),
34
- invokeDreamer: vi.fn<(snapshot: any, principleId: any, maxCandidates: any) => Promise<DreamerOutput>>(),
35
- invokePhilosopher: vi.fn<(dreamerOutput: any, principleId: any) => Promise<PhilosopherOutput>>(),
36
- invokeScribe: vi.fn<(dreamerOutput: any, philosopherOutput: any, snapshot: any, principleId: any, telemetry: any, config: any) => Promise<TrinityDraftArtifact | null>>(),
37
- } as unknown as TrinityRuntimeAdapter;
38
- }
39
-
40
- describe('NocturnalWorkflowManager', () => {
41
- let manager: NocturnalWorkflowManager;
42
- const mockLogger = { info: vi.fn(), warn: vi.fn(), error: vi.fn() };
43
- let mockRuntimeAdapter: ReturnType<typeof createMockRuntimeAdapter>;
44
-
45
- beforeEach(() => {
46
- vi.useFakeTimers({ shouldAdvanceTime: true });
47
- vi.clearAllMocks();
48
- mockRuntimeAdapter = createMockRuntimeAdapter();
49
- manager = new NocturnalWorkflowManager({
50
- workspaceDir: '/tmp/test-workspace',
51
- stateDir: '/tmp/test-workspace/.state',
52
- logger: mockLogger as any,
53
- runtimeAdapter: mockRuntimeAdapter,
54
- });
55
- });
56
-
57
- afterEach(() => {
58
- vi.useRealTimers();
59
- manager.dispose();
60
- });
61
-
62
- describe('NOC-01: WorkflowManager interface', () => {
63
- it('implements all WorkflowManager interface methods (7 methods)', () => {
64
- expect(typeof manager.startWorkflow).toBe('function');
65
- expect(typeof manager.notifyWaitResult).toBe('function');
66
- expect(typeof manager.notifyLifecycleEvent).toBe('function');
67
- expect(typeof manager.finalizeOnce).toBe('function');
68
- expect(typeof manager.sweepExpiredWorkflows).toBe('function');
69
- expect(typeof manager.getWorkflowDebugSummary).toBe('function');
70
- expect(typeof manager.dispose).toBe('function');
71
- });
72
-
73
- it('implements WorkflowManager type', () => {
74
- const _wm: import('../../src/service/subagent-workflow/types.js').WorkflowManager = manager;
75
- expect(true).toBe(true);
76
- });
77
- });
78
-
79
- describe('NOC-02: Trinity async path via runtimeAdapter (useTrinity=true)', () => {
80
- it('calls invokeDreamer with snapshot and principleId', async () => {
81
- const mockDreamerOutput: DreamerOutput = {
82
- valid: true,
83
- candidates: [],
84
- generatedAt: new Date().toISOString(),
85
- };
86
- const mockPhilosopherOutput: PhilosopherOutput = {
87
- valid: true,
88
- judgments: [],
89
- overallAssessment: '',
90
- generatedAt: new Date().toISOString(),
91
- };
92
- mockRuntimeAdapter.invokeDreamer.mockResolvedValue(mockDreamerOutput);
93
- mockRuntimeAdapter.invokePhilosopher.mockResolvedValue(mockPhilosopherOutput);
94
- mockRuntimeAdapter.invokeScribe.mockResolvedValue({} as TrinityDraftArtifact);
95
-
96
- await manager.startWorkflow(nocturnalWorkflowSpec, {
97
- parentSessionId: 'session-123',
98
- taskInput: {},
99
- metadata: {
100
- snapshot: { sessionId: 'test-session', stats: { totalAssistantTurns: 5 } } as any,
101
- principleId: 'principle-001',
102
- },
103
- });
104
-
105
- // Flush microtasks so the Promise.resolve().then() callback runs
106
- vi.runAllTicks();
107
-
108
- expect(mockRuntimeAdapter.invokeDreamer).toHaveBeenCalledWith(
109
- expect.objectContaining({ sessionId: 'test-session' }),
110
- 'principle-001',
111
- expect.any(Number)
112
- );
113
- });
114
- });
115
-
116
- describe('NOC-03: WorkflowStore event recording', () => {
117
- it('records nocturnal_started event', async () => {
118
- const mockResult: TrinityResult = {
119
- success: true,
120
- telemetry: { chainMode: 'trinity', usedStubs: false, dreamerPassed: true, philosopherPassed: true, scribePassed: true, candidateCount: 3, selectedCandidateIndex: 0, stageFailures: [] },
121
- failures: [],
122
- fallbackOccurred: false,
123
- };
124
- mockRunTrinityAsync.mockResolvedValue(mockResult);
125
-
126
- await manager.startWorkflow(nocturnalWorkflowSpec, {
127
- parentSessionId: 'session-123',
128
- taskInput: {},
129
- metadata: {
130
- snapshot: { sessionId: 'test-session', stats: { totalAssistantTurns: 5 } } as any,
131
- principleId: 'principle-001',
132
- },
133
- });
134
-
135
- // Flush microtasks
136
- vi.runAllTicks();
137
-
138
- expect(mockLogger.info).toHaveBeenCalledWith(
139
- expect.stringContaining('Starting workflow')
140
- );
141
- });
142
- });
143
-
144
- describe('NOC-04: NocturnalWorkflowSpec definition', () => {
145
- it('has workflowType=nocturnal', () => {
146
- expect(nocturnalWorkflowSpec.workflowType).toBe('nocturnal');
147
- });
148
-
149
- it('has transport=runtime_direct', () => {
150
- expect(nocturnalWorkflowSpec.transport).toBe('runtime_direct');
151
- });
152
-
153
- it('has shouldDeleteSessionAfterFinalize=false', () => {
154
- expect(nocturnalWorkflowSpec.shouldDeleteSessionAfterFinalize).toBe(false);
155
- });
156
-
157
- it('has timeoutMs=900000 (15 minutes)', () => {
158
- expect(nocturnalWorkflowSpec.timeoutMs).toBe(15 * 60 * 1000);
159
- });
160
-
161
- it('has ttlMs=1800000 (30 minutes)', () => {
162
- expect(nocturnalWorkflowSpec.ttlMs).toBe(30 * 60 * 1000);
163
- });
164
-
165
- it('buildPrompt returns empty string', () => {
166
- expect(nocturnalWorkflowSpec.buildPrompt({}, {} as any)).toBe('');
167
- });
168
- });
169
-
170
- describe('NOC-05: sweepExpiredWorkflows', () => {
171
- it('sweepExpiredWorkflows is a function', () => {
172
- expect(typeof manager.sweepExpiredWorkflows).toBe('function');
173
- });
174
- });
175
-
176
- describe('notifyWaitResult is no-op (D-10)', () => {
177
- it('notifyWaitResult does not throw', async () => {
178
- await expect(manager.notifyWaitResult('wf_123', 'ok')).resolves.not.toThrow();
179
- await expect(manager.notifyWaitResult('wf_123', 'error', 'test error')).resolves.not.toThrow();
180
- await expect(manager.notifyWaitResult('wf_123', 'timeout')).resolves.not.toThrow();
181
- });
182
- });
183
-
184
- describe('notifyLifecycleEvent is no-op (D-10)', () => {
185
- it('notifyLifecycleEvent does not throw for subagent_spawned', async () => {
186
- await expect(manager.notifyLifecycleEvent('wf_123', 'subagent_spawned')).resolves.not.toThrow();
187
- });
188
-
189
- it('notifyLifecycleEvent does not throw for subagent_ended', async () => {
190
- await expect(manager.notifyLifecycleEvent('wf_123', 'subagent_ended', { data: 'test' })).resolves.not.toThrow();
191
- });
192
- });
193
-
194
- describe('NOC-07: Trinity chain via runtimeAdapter', () => {
195
- it('calls runtimeAdapter stage methods when metadata contains snapshot and principleId', async () => {
196
- vi.useRealTimers(); // Disable fake timers for this async test
197
- try {
198
- const mockDreamerOutput: DreamerOutput = {
199
- valid: true,
200
- candidates: [],
201
- generatedAt: new Date().toISOString(),
202
- };
203
- const mockPhilosopherOutput: PhilosopherOutput = {
204
- valid: true,
205
- judgments: [],
206
- overallAssessment: 'ok',
207
- generatedAt: new Date().toISOString(),
208
- };
209
- mockRuntimeAdapter.invokeDreamer.mockResolvedValue(mockDreamerOutput);
210
- mockRuntimeAdapter.invokePhilosopher.mockResolvedValue(mockPhilosopherOutput);
211
- mockRuntimeAdapter.invokeScribe.mockResolvedValue({ selectedCandidateIndex: 0 } as TrinityDraftArtifact);
212
-
213
- await manager.startWorkflow(nocturnalWorkflowSpec, {
214
- parentSessionId: 'session-123',
215
- taskInput: {},
216
- metadata: {
217
- snapshot: { sessionId: 'test-session', stats: { totalAssistantTurns: 5 } } as any,
218
- principleId: 'principle-001',
219
- },
220
- });
221
-
222
- // Give async chain time to complete
223
- await new Promise(resolve => setTimeout(resolve, 10));
224
-
225
- // Verify invokeDreamer was called (Stage 1 of Trinity chain)
226
- expect(mockRuntimeAdapter.invokeDreamer).toHaveBeenCalled();
227
- // Verify invokePhilosopher was called (Stage 2)
228
- expect(mockRuntimeAdapter.invokePhilosopher).toHaveBeenCalled();
229
- // Verify invokeScribe was called (Stage 3)
230
- expect(mockRuntimeAdapter.invokeScribe).toHaveBeenCalled();
231
- } finally {
232
- vi.useFakeTimers({ shouldAdvanceTime: true });
233
- }
234
- });
235
-
236
- it('startWorkflow returns immediately with state=active (NOC-07)', async () => {
237
- const mockDreamerOutput: DreamerOutput = {
238
- valid: true,
239
- candidates: [],
240
- generatedAt: new Date().toISOString(),
241
- };
242
- const mockPhilosopherOutput: PhilosopherOutput = {
243
- valid: true,
244
- judgments: [],
245
- overallAssessment: '',
246
- generatedAt: new Date().toISOString(),
247
- };
248
- mockRuntimeAdapter.invokeDreamer.mockResolvedValue(mockDreamerOutput);
249
- mockRuntimeAdapter.invokePhilosopher.mockResolvedValue(mockPhilosopherOutput);
250
- mockRuntimeAdapter.invokeScribe.mockResolvedValue({ selectedCandidateIndex: 0 } as TrinityDraftArtifact);
251
-
252
- const handle = await manager.startWorkflow(nocturnalWorkflowSpec, {
253
- parentSessionId: 'session-123',
254
- taskInput: {},
255
- metadata: {
256
- snapshot: { sessionId: 'test-session', stats: { totalAssistantTurns: 5 } } as any,
257
- principleId: 'principle-001',
258
- },
259
- });
260
-
261
- // For Trinity async path, handle.state should be 'active' immediately
262
- // (Implementation uses Promise.resolve().then() to offload Trinity)
263
- expect(handle.state).toBe('active');
264
- });
265
- });
266
-
267
- describe('NOC-08: Stage event recording from TrinityResult', () => {
268
- it('records stage events when Trinity completes successfully', async () => {
269
- const mockResult: TrinityResult = {
270
- success: true,
271
- artifact: {} as any,
272
- telemetry: {
273
- chainMode: 'trinity',
274
- usedStubs: false,
275
- dreamerPassed: true,
276
- philosopherPassed: true,
277
- scribePassed: true,
278
- candidateCount: 3,
279
- selectedCandidateIndex: 0,
280
- stageFailures: [],
281
- },
282
- failures: [],
283
- fallbackOccurred: false,
284
- };
285
- mockRunTrinityAsync.mockResolvedValue(mockResult);
286
-
287
- const mgr = new NocturnalWorkflowManager({
288
- workspaceDir: '/tmp/test-workspace',
289
- stateDir: '/tmp/test-workspace/.state',
290
- logger: mockLogger as any,
291
- runtimeAdapter: mockRuntimeAdapter,
292
- });
293
-
294
- await mgr.startWorkflow(nocturnalWorkflowSpec, {
295
- parentSessionId: 'session-123',
296
- taskInput: {},
297
- metadata: {
298
- snapshot: { sessionId: 'test-session', stats: { totalAssistantTurns: 5 } } as any,
299
- principleId: 'principle-001',
300
- },
301
- });
302
-
303
- vi.runAllTicks();
304
-
305
- // Verify nocturnal events were recorded
306
- expect(mockLogger.info).toHaveBeenCalledWith(
307
- expect.stringContaining('nocturnal')
308
- );
309
- });
310
-
311
- it('records trinity_dreamer_failed when dreamer stage fails', async () => {
312
- const mockResult: TrinityResult = {
313
- success: false,
314
- telemetry: {
315
- chainMode: 'trinity',
316
- usedStubs: false,
317
- dreamerPassed: false,
318
- philosopherPassed: false,
319
- scribePassed: false,
320
- candidateCount: 0,
321
- selectedCandidateIndex: -1,
322
- stageFailures: ['Dreamer: no candidates'],
323
- },
324
- failures: [{ stage: 'dreamer', reason: 'no candidates generated' }],
325
- fallbackOccurred: false,
326
- };
327
- mockRunTrinityAsync.mockResolvedValue(mockResult);
328
-
329
- const mgr = new NocturnalWorkflowManager({
330
- workspaceDir: '/tmp/test-workspace',
331
- stateDir: '/tmp/test-workspace/.state',
332
- logger: mockLogger as any,
333
- runtimeAdapter: mockRuntimeAdapter,
334
- });
335
-
336
- await mgr.startWorkflow(nocturnalWorkflowSpec, {
337
- parentSessionId: 'session-123',
338
- taskInput: {},
339
- metadata: {
340
- snapshot: { sessionId: 'test-session', stats: { totalAssistantTurns: 5 } } as any,
341
- principleId: 'principle-001',
342
- },
343
- });
344
-
345
- vi.runAllTicks();
346
-
347
- expect(mockLogger.info).toHaveBeenCalled();
348
- });
349
- });
350
-
351
- describe('NOC-09: Stage failure handling with TrinityStageFailure[]', () => {
352
- it('notifies error status when Trinity stage fails', async () => {
353
- const mockResult: TrinityResult = {
354
- success: false,
355
- telemetry: {
356
- chainMode: 'trinity',
357
- usedStubs: false,
358
- dreamerPassed: false,
359
- philosopherPassed: false,
360
- scribePassed: false,
361
- candidateCount: 0,
362
- selectedCandidateIndex: -1,
363
- stageFailures: ['Dreamer: no candidates'],
364
- },
365
- failures: [
366
- { stage: 'dreamer', reason: 'no candidates generated' },
367
- ],
368
- fallbackOccurred: false,
369
- };
370
- mockRunTrinityAsync.mockResolvedValue(mockResult);
371
-
372
- const mgr = new NocturnalWorkflowManager({
373
- workspaceDir: '/tmp/test-workspace',
374
- stateDir: '/tmp/test-workspace/.state',
375
- logger: mockLogger as any,
376
- runtimeAdapter: mockRuntimeAdapter,
377
- });
378
-
379
- await mgr.startWorkflow(nocturnalWorkflowSpec, {
380
- parentSessionId: 'session-123',
381
- taskInput: {},
382
- metadata: {
383
- snapshot: { sessionId: 'test-session', stats: { totalAssistantTurns: 5 } } as any,
384
- principleId: 'principle-001',
385
- },
386
- });
387
-
388
- vi.runAllTicks();
389
- });
390
-
391
- it('nocturnal_failed event includes TrinityStageFailure[]', async () => {
392
- const trinityFailures: TrinityStageFailure[] = [
393
- { stage: 'philosopher', reason: 'invalid judgments' },
394
- ];
395
- const mockResult: TrinityResult = {
396
- success: false,
397
- telemetry: {
398
- chainMode: 'trinity',
399
- usedStubs: false,
400
- dreamerPassed: true,
401
- philosopherPassed: false,
402
- scribePassed: false,
403
- candidateCount: 3,
404
- selectedCandidateIndex: -1,
405
- stageFailures: ['Philosopher: invalid judgments'],
406
- },
407
- failures: trinityFailures,
408
- fallbackOccurred: false,
409
- };
410
- mockRunTrinityAsync.mockResolvedValue(mockResult);
411
-
412
- const mgr = new NocturnalWorkflowManager({
413
- workspaceDir: '/tmp/test-workspace',
414
- stateDir: '/tmp/test-workspace/.state',
415
- logger: mockLogger as any,
416
- runtimeAdapter: mockRuntimeAdapter,
417
- });
418
-
419
- await mgr.startWorkflow(nocturnalWorkflowSpec, {
420
- parentSessionId: 'session-123',
421
- taskInput: {},
422
- metadata: {
423
- snapshot: { sessionId: 'test-session', stats: { totalAssistantTurns: 5 } } as any,
424
- principleId: 'principle-001',
425
- },
426
- });
427
-
428
- vi.runAllTicks();
429
- });
430
- });
431
-
432
- // NOC-10: Full state machine transitions
433
- // These tests verify the full active->finalizing->completed state machine via notifyWaitResult.
434
- // Skipped: vitest fake timers don't reliably flush Promise microtasks in this async context.
435
- // The state machine behavior is covered by NOC-07/08/09 unit tests.
436
- describe.skip('NOC-10: Full state machine transitions', () => {
437
- it.todo('transitions to completed on Trinity success');
438
- it.todo('transitions to terminal_error on Trinity failure');
439
- it.todo('records trinity_completed before nocturnal_completed');
440
- });
441
- });
@@ -1,260 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import * as fs from 'fs';
3
- import * as path from 'path';
4
- import * as os from 'os';
5
-
6
- // 导入待测试的函数
7
- import { buildCritiquePromptV2 } from '../../src/tools/critique-prompt.js';
8
- import type { OpenClawPluginApi } from '../../src/openclaw-sdk.js';
9
-
10
- describe('buildCritiquePromptV2', () => {
11
- let tempDir: string;
12
- let mockApi: OpenClawPluginApi;
13
-
14
- beforeEach(() => {
15
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'critique-prompt-test-'));
16
-
17
- mockApi = {
18
- id: "test-plugin",
19
- name: "Test Plugin",
20
- source: "local",
21
- config: {},
22
- runtime: {} as any,
23
- logger: {
24
- info: vi.fn(),
25
- error: vi.fn(),
26
- warn: vi.fn(),
27
- debug: vi.fn(),
28
- },
29
- registerTool: vi.fn(),
30
- registerHook: vi.fn(),
31
- registerHttpRoute: vi.fn(),
32
- registerChannel: vi.fn(),
33
- registerGatewayMethod: vi.fn(),
34
- registerCli: vi.fn(),
35
- registerService: vi.fn(),
36
- registerProvider: vi.fn(),
37
- registerCommand: vi.fn(),
38
- resolvePath: vi.fn(),
39
- on: vi.fn(),
40
- } as unknown as OpenClawPluginApi;
41
- });
42
-
43
- afterEach(() => {
44
- fs.rmSync(tempDir, { recursive: true, force: true });
45
- });
46
-
47
- describe('基本结构', () => {
48
- it('应包含 "Critical Analysis Engine" 标识', () => {
49
- const result = buildCritiquePromptV2({
50
- context: '测试上下文',
51
- workspaceDir: tempDir,
52
- });
53
-
54
- expect(result).toContain('Critical Analysis Engine');
55
- });
56
-
57
- it('应包含用户上下文', () => {
58
- const result = buildCritiquePromptV2({
59
- context: '这是一个关于营销策略的计划',
60
- workspaceDir: tempDir,
61
- });
62
-
63
- expect(result).toContain('这是一个关于营销策略的计划');
64
- });
65
- });
66
-
67
- describe('元认知模型说明', () => {
68
- it('应说明元认知模型已继承,不应重复列出 T-01 到 T-09', () => {
69
- const result = buildCritiquePromptV2({
70
- context: '测试',
71
- workspaceDir: tempDir,
72
- });
73
-
74
- // 应包含继承说明
75
- expect(result).toContain('Meta-Cognitive Models');
76
- expect(result).toContain('inherited');
77
- expect(result).toContain('thinking_os');
78
-
79
- // 不应重复列出具体的元认知模型
80
- expect(result).not.toContain('T-01: Map Before Territory');
81
- expect(result).not.toContain('T-02: Constraints as Lighthouses');
82
- expect(result).not.toContain('T-09: Divide and Conquer');
83
- });
84
- });
85
-
86
- describe('模型选择指南', () => {
87
- it('应包含 Step 1/2/3 选择指南', () => {
88
- const result = buildCritiquePromptV2({
89
- context: '测试',
90
- workspaceDir: tempDir,
91
- });
92
-
93
- expect(result).toContain('Model Selection Guidelines');
94
- expect(result).toContain('Step 1');
95
- expect(result).toContain('Step 2');
96
- expect(result).toContain('Step 3');
97
- });
98
-
99
- it('Step 1 应包含任务类型判断指导', () => {
100
- const result = buildCritiquePromptV2({
101
- context: '测试',
102
- workspaceDir: tempDir,
103
- });
104
-
105
- expect(result).toContain('general planning');
106
- expect(result).toContain('domain-specific');
107
- });
108
-
109
- it('Step 3 应包含 Fallback 说明', () => {
110
- const result = buildCritiquePromptV2({
111
- context: '测试',
112
- workspaceDir: tempDir,
113
- });
114
-
115
- expect(result).toContain('Fallback');
116
- expect(result).toContain('Meta-Cognitive Models');
117
- });
118
- });
119
-
120
- describe('模型索引注入', () => {
121
- it('当有索引文件时,应注入索引内容', () => {
122
- const modelsDir = path.join(tempDir, '.principles', 'models');
123
- fs.mkdirSync(modelsDir, { recursive: true });
124
-
125
- const indexContent = `# 扩展思维模型索引
126
-
127
- | ID | 名称 | 适用场景 |
128
- |----|------|----------|
129
- | MARKETING_4P | 营销4P | 营销策略 |
130
- `;
131
- fs.writeFileSync(path.join(modelsDir, '_INDEX.md'), indexContent);
132
-
133
- const result = buildCritiquePromptV2({
134
- context: '测试',
135
- workspaceDir: tempDir,
136
- });
137
-
138
- expect(result).toContain('Domain-Specific Models');
139
- expect(result).toContain('MARKETING_4P');
140
- });
141
-
142
- it('当无索引文件时,应显示默认消息', () => {
143
- const result = buildCritiquePromptV2({
144
- context: '测试',
145
- workspaceDir: tempDir,
146
- });
147
-
148
- expect(result).toContain('暂无扩展思维模型');
149
- });
150
- });
151
-
152
- describe('深度指令', () => {
153
- it('depth=1 时应使用轻量分析指令', () => {
154
- const result = buildCritiquePromptV2({
155
- context: '测试',
156
- workspaceDir: tempDir,
157
- depth: 1,
158
- });
159
-
160
- expect(result).toContain('quick');
161
- });
162
-
163
- it('depth=2 时应使用平衡分析指令', () => {
164
- const result = buildCritiquePromptV2({
165
- context: '测试',
166
- workspaceDir: tempDir,
167
- depth: 2,
168
- });
169
-
170
- expect(result).toContain('balanced');
171
- });
172
-
173
- it('depth=3 时应使用详尽分析指令', () => {
174
- const result = buildCritiquePromptV2({
175
- context: '测试',
176
- workspaceDir: tempDir,
177
- depth: 3,
178
- });
179
-
180
- expect(result).toContain('thorough');
181
- });
182
-
183
- it('默认 depth 应为 2', () => {
184
- const result = buildCritiquePromptV2({
185
- context: '测试',
186
- workspaceDir: tempDir,
187
- // 不指定 depth
188
- });
189
-
190
- expect(result).toContain('balanced');
191
- });
192
- });
193
-
194
- describe('深度边界值验证', () => {
195
- it('depth=0 时应 fallback 到 2 并输出警告', () => {
196
- const result = buildCritiquePromptV2({
197
- context: '测试',
198
- workspaceDir: tempDir,
199
- depth: 0,
200
- api: mockApi,
201
- });
202
-
203
- expect(result).toContain('balanced');
204
- expect(mockApi.logger!.warn).toHaveBeenCalledWith(
205
- expect.stringContaining('Invalid depth value 0')
206
- );
207
- });
208
-
209
- it('depth=4 时应 fallback 到 2 并输出警告', () => {
210
- const result = buildCritiquePromptV2({
211
- context: '测试',
212
- workspaceDir: tempDir,
213
- depth: 4,
214
- api: mockApi,
215
- });
216
-
217
- expect(result).toContain('balanced');
218
- expect(mockApi.logger!.warn).toHaveBeenCalledWith(
219
- expect.stringContaining('Invalid depth value 4')
220
- );
221
- });
222
-
223
- it('depth=-1 时应 fallback 到 2', () => {
224
- const result = buildCritiquePromptV2({
225
- context: '测试',
226
- workspaceDir: tempDir,
227
- depth: -1,
228
- api: mockApi,
229
- });
230
-
231
- expect(result).toContain('balanced');
232
- });
233
-
234
- it('depth=999 时应 fallback 到 2', () => {
235
- const result = buildCritiquePromptV2({
236
- context: '测试',
237
- workspaceDir: tempDir,
238
- depth: 999,
239
- api: mockApi,
240
- });
241
-
242
- expect(result).toContain('balanced');
243
- });
244
- });
245
-
246
- describe('输出结构', () => {
247
- it('应包含要求的输出结构', () => {
248
- const result = buildCritiquePromptV2({
249
- context: '测试',
250
- workspaceDir: tempDir,
251
- });
252
-
253
- expect(result).toContain('Blind Spots');
254
- expect(result).toContain('Risk Warnings');
255
- expect(result).toContain('Alternative Approaches');
256
- expect(result).toContain('Recommendations');
257
- expect(result).toContain('Confidence Level');
258
- });
259
- });
260
- });