sinapse-ai 1.9.0 → 1.10.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 (94) hide show
  1. package/.claude/rules/mandatory-delegation.md +1 -1
  2. package/.codex/delegation-matrix.json +4 -3
  3. package/.codex/delegation-parity.json +4 -3
  4. package/.codex/instructions.md +2 -2
  5. package/.sinapse-ai/constitution.md +2 -2
  6. package/.sinapse-ai/core/doctor/checks/git-hooks.js +76 -10
  7. package/.sinapse-ai/core/execution/subagent-dispatcher.js +1 -1
  8. package/.sinapse-ai/core/synapse/engine.js +15 -0
  9. package/.sinapse-ai/data/entity-registry.yaml +13 -13
  10. package/.sinapse-ai/development/agents/snps-orqx.md +4 -4
  11. package/.sinapse-ai/git-hooks/lib/secret-scanner-core.js +76 -4
  12. package/.sinapse-ai/git-hooks/pre-push +7 -1
  13. package/.sinapse-ai/install-manifest.yaml +9 -9
  14. package/AGENTS.md +4 -4
  15. package/CHANGELOG.md +1257 -0
  16. package/README.en.md +4 -3
  17. package/README.md +5 -6
  18. package/bin/commands/install.js +6 -3
  19. package/bin/commands/uninstall.js +2 -2
  20. package/bin/sinapse.js +20 -0
  21. package/bin/utils/secret-scanner-core.js +76 -4
  22. package/docs/agent-reference-guide.md +1 -1
  23. package/docs/framework/architecture-overview.md +4 -4
  24. package/docs/framework/guiding-principles.md +9 -9
  25. package/docs/getting-started.md +1 -1
  26. package/docs/guides/agent-reference.md +1 -1
  27. package/docs/guides/codex-config.md +4 -5
  28. package/docs/pt/architecture/sub-orqx-pattern.md +20 -18
  29. package/package.json +8 -2
  30. package/packages/installer/src/installer/git-hooks-installer.js +3 -1
  31. package/packages/installer/src/installer/sinapse-ai-installer.js +32 -7
  32. package/packages/installer/src/wizard/ide-config-generator.js +9 -1
  33. package/packages/installer/src/wizard/index.js +3 -4
  34. package/scripts/regenerate-orqx-stubs.ps1 +0 -1
  35. package/scripts/sync-counts.js +10 -2
  36. package/scripts/sync-squad-yaml-components.js +108 -6
  37. package/scripts/validate-article-vii.js +68 -7
  38. package/scripts/validate-squad-orqx.js +19 -9
  39. package/sinapse/agents/sinapse-orqx.md +4 -4
  40. package/sinapse/agents/snps-orqx.md +4 -4
  41. package/sinapse/knowledge-base/routing-catalog.md +1 -1
  42. package/sinapse/tasks/diagnose-and-route.md +1 -1
  43. package/sinapse/tasks/squad-status-report.md +1 -1
  44. package/squads/claude-code-mastery/agents/claude-mastery-chief.md +1 -1
  45. package/squads/claude-code-mastery/agents/hooks-architect.md +60 -68
  46. package/squads/claude-code-mastery/knowledge-base/swarm-orchestration-patterns.md +1 -1
  47. package/squads/claude-code-mastery/tasks/audit-setup.md +1 -1
  48. package/squads/claude-code-mastery/workflows/optimization-cycle.yaml +4 -4
  49. package/squads/claude-code-mastery/workflows/project-setup-cycle.yaml +4 -4
  50. package/squads/squad-animations/README.md +1 -1
  51. package/squads/squad-cloning/README.md +1 -1
  52. package/squads/squad-commercial/README.md +1 -1
  53. package/squads/squad-content/README.md +1 -1
  54. package/squads/squad-copy/README.md +1 -1
  55. package/squads/squad-council/README.md +1 -1
  56. package/squads/squad-courses/README.md +1 -1
  57. package/squads/squad-cybersecurity/README.md +1 -1
  58. package/squads/squad-design/README.md +1 -1
  59. package/squads/squad-finance/README.md +1 -1
  60. package/squads/squad-growth/README.md +1 -1
  61. package/squads/squad-paidmedia/README.md +1 -1
  62. package/squads/squad-product/README.md +1 -1
  63. package/squads/squad-research/README.md +1 -1
  64. package/squads/squad-storytelling/README.md +1 -1
  65. package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +0 -265
  66. package/.sinapse-ai/core/permissions/__tests__/permission-mode.test.js +0 -293
  67. package/.sinapse-ai/infrastructure/tests/project-status-loader.test.js +0 -569
  68. package/.sinapse-ai/infrastructure/tests/regression-suite-v2.md +0 -622
  69. package/.sinapse-ai/infrastructure/tests/validate-module.js +0 -98
  70. package/.sinapse-ai/infrastructure/tests/worktree-manager.test.js +0 -620
  71. package/.sinapse-ai/workflow-intelligence/__tests__/confidence-scorer.test.js +0 -335
  72. package/.sinapse-ai/workflow-intelligence/__tests__/integration.test.js +0 -340
  73. package/.sinapse-ai/workflow-intelligence/__tests__/suggestion-engine.test.js +0 -438
  74. package/.sinapse-ai/workflow-intelligence/__tests__/wave-analyzer.test.js +0 -448
  75. package/.sinapse-ai/workflow-intelligence/__tests__/workflow-registry.test.js +0 -303
  76. package/packages/installer/src/__tests__/performance-benchmark.js +0 -383
  77. package/packages/installer/tests/integration/environment-configuration.test.js +0 -332
  78. package/packages/installer/tests/integration/wizard-detection.test.js +0 -352
  79. package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +0 -402
  80. package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +0 -193
  81. package/packages/installer/tests/unit/config-validator.test.js +0 -315
  82. package/packages/installer/tests/unit/detection/detect-project-type.test.js +0 -539
  83. package/packages/installer/tests/unit/doctor/doctor-checks.test.js +0 -675
  84. package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +0 -192
  85. package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +0 -192
  86. package/packages/installer/tests/unit/env-template.test.js +0 -187
  87. package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +0 -310
  88. package/packages/installer/tests/unit/git-hooks-installer.test.js +0 -262
  89. package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +0 -231
  90. package/packages/installer/tests/unit/merger/env-merger.test.js +0 -191
  91. package/packages/installer/tests/unit/merger/markdown-merger.test.js +0 -262
  92. package/packages/installer/tests/unit/merger/strategies.test.js +0 -154
  93. package/packages/installer/tests/unit/merger/yaml-merger.test.js +0 -328
  94. package/packages/sinapse-install/tests/unit/chrome-brain.smoke.test.js +0 -66
@@ -1,448 +0,0 @@
1
- /**
2
- * @fileoverview Unit tests for WaveAnalyzer
3
- * @story WIS-4 - Wave Analysis Engine
4
- */
5
-
6
- 'use strict';
7
-
8
- const {
9
- WaveAnalyzer,
10
- CircularDependencyError,
11
- createWaveAnalyzer,
12
- analyzeWaves,
13
- DEFAULT_TASK_DURATIONS,
14
- } = require('../engine/wave-analyzer');
15
-
16
- describe('WaveAnalyzer', () => {
17
- let analyzer;
18
-
19
- beforeEach(() => {
20
- analyzer = new WaveAnalyzer();
21
- });
22
-
23
- describe('constructor', () => {
24
- it('should create instance with default options', () => {
25
- const instance = new WaveAnalyzer();
26
- expect(instance.taskDurations).toEqual(DEFAULT_TASK_DURATIONS);
27
- });
28
-
29
- it('should accept custom task durations', () => {
30
- const customDurations = { 'custom-task': 25 };
31
- const instance = new WaveAnalyzer({ taskDurations: customDurations });
32
- expect(instance.taskDurations['custom-task']).toBe(25);
33
- expect(instance.taskDurations['default']).toBe(DEFAULT_TASK_DURATIONS.default);
34
- });
35
-
36
- it('should accept custom registry', () => {
37
- const mockRegistry = { getWorkflow: jest.fn() };
38
- const instance = new WaveAnalyzer({ registry: mockRegistry });
39
- expect(instance.registry).toBe(mockRegistry);
40
- });
41
- });
42
-
43
- describe('buildDependencyGraph', () => {
44
- it('should build graph from tasks with dependencies', () => {
45
- const tasks = [
46
- { id: 'task-a', dependsOn: [] },
47
- { id: 'task-b', dependsOn: [] },
48
- { id: 'task-c', dependsOn: ['task-a', 'task-b'] },
49
- { id: 'task-d', dependsOn: ['task-c'] },
50
- ];
51
-
52
- const graph = analyzer.buildDependencyGraph(tasks);
53
-
54
- expect(graph.nodes.size).toBe(4);
55
- expect(graph.nodes.has('task-a')).toBe(true);
56
- expect(graph.nodes.has('task-b')).toBe(true);
57
- expect(graph.nodes.has('task-c')).toBe(true);
58
- expect(graph.nodes.has('task-d')).toBe(true);
59
-
60
- // Check edges (a -> c, b -> c, c -> d)
61
- expect(graph.edges.get('task-a').has('task-c')).toBe(true);
62
- expect(graph.edges.get('task-b').has('task-c')).toBe(true);
63
- expect(graph.edges.get('task-c').has('task-d')).toBe(true);
64
-
65
- // Check in-edges
66
- expect(graph.inEdges.get('task-a').size).toBe(0);
67
- expect(graph.inEdges.get('task-b').size).toBe(0);
68
- expect(graph.inEdges.get('task-c').size).toBe(2);
69
- expect(graph.inEdges.get('task-d').size).toBe(1);
70
- });
71
-
72
- it('should handle tasks without dependencies', () => {
73
- const tasks = [{ id: 'task-a' }, { id: 'task-b' }, { id: 'task-c' }];
74
-
75
- const graph = analyzer.buildDependencyGraph(tasks);
76
-
77
- expect(graph.nodes.size).toBe(3);
78
- expect(graph.edges.get('task-a').size).toBe(0);
79
- expect(graph.edges.get('task-b').size).toBe(0);
80
- expect(graph.edges.get('task-c').size).toBe(0);
81
- });
82
-
83
- it('should use task name if id is missing', () => {
84
- const tasks = [{ name: 'task-a' }, { name: 'task-b', dependsOn: ['task-a'] }];
85
-
86
- const graph = analyzer.buildDependencyGraph(tasks);
87
-
88
- expect(graph.nodes.has('task-a')).toBe(true);
89
- expect(graph.nodes.has('task-b')).toBe(true);
90
- expect(graph.edges.get('task-a').has('task-b')).toBe(true);
91
- });
92
-
93
- it('should ignore dependencies to non-existent nodes', () => {
94
- const tasks = [{ id: 'task-a', dependsOn: ['non-existent'] }];
95
-
96
- const graph = analyzer.buildDependencyGraph(tasks);
97
-
98
- expect(graph.nodes.size).toBe(1);
99
- expect(graph.inEdges.get('task-a').size).toBe(0);
100
- });
101
- });
102
-
103
- describe('findCycle', () => {
104
- it('should return null for acyclic graph', () => {
105
- const tasks = [
106
- { id: 'a', dependsOn: [] },
107
- { id: 'b', dependsOn: ['a'] },
108
- { id: 'c', dependsOn: ['b'] },
109
- ];
110
-
111
- const graph = analyzer.buildDependencyGraph(tasks);
112
- const cycle = analyzer.findCycle(graph);
113
-
114
- expect(cycle).toBeNull();
115
- });
116
-
117
- it('should detect simple cycle', () => {
118
- const tasks = [
119
- { id: 'a', dependsOn: ['c'] },
120
- { id: 'b', dependsOn: ['a'] },
121
- { id: 'c', dependsOn: ['b'] },
122
- ];
123
-
124
- const graph = analyzer.buildDependencyGraph(tasks);
125
- const cycle = analyzer.findCycle(graph);
126
-
127
- expect(cycle).not.toBeNull();
128
- expect(cycle.length).toBeGreaterThan(1);
129
- });
130
-
131
- it('should detect self-loop', () => {
132
- const tasks = [{ id: 'a', dependsOn: ['a'] }];
133
-
134
- const graph = analyzer.buildDependencyGraph(tasks);
135
- const cycle = analyzer.findCycle(graph);
136
-
137
- expect(cycle).not.toBeNull();
138
- expect(cycle).toContain('a');
139
- });
140
-
141
- it('should detect cycle in diamond pattern with back-edge', () => {
142
- const tasks = [
143
- { id: 'a', dependsOn: ['d'] }, // Creates cycle: a -> b -> d -> a
144
- { id: 'b', dependsOn: ['a'] },
145
- { id: 'c', dependsOn: ['a'] },
146
- { id: 'd', dependsOn: ['b', 'c'] },
147
- ];
148
-
149
- const graph = analyzer.buildDependencyGraph(tasks);
150
- const cycle = analyzer.findCycle(graph);
151
-
152
- expect(cycle).not.toBeNull();
153
- });
154
- });
155
-
156
- describe('analyzeWaves with customTasks', () => {
157
- it('should group independent tasks into same wave', () => {
158
- const tasks = [
159
- { id: 'a', dependsOn: [], duration: 5 },
160
- { id: 'b', dependsOn: [], duration: 3 },
161
- { id: 'c', dependsOn: [], duration: 4 },
162
- ];
163
-
164
- const result = analyzer.analyzeWaves('test-workflow', { customTasks: tasks });
165
-
166
- expect(result.waves.length).toBe(1);
167
- expect(result.waves[0].tasks).toEqual(expect.arrayContaining(['a', 'b', 'c']));
168
- expect(result.waves[0].parallel).toBe(true);
169
- });
170
-
171
- it('should create sequential waves for linear dependencies', () => {
172
- const tasks = [
173
- { id: 'a', dependsOn: [], duration: 5 },
174
- { id: 'b', dependsOn: ['a'], duration: 10 },
175
- { id: 'c', dependsOn: ['b'], duration: 3 },
176
- { id: 'd', dependsOn: ['c'], duration: 7 },
177
- ];
178
-
179
- const result = analyzer.analyzeWaves('test-workflow', { customTasks: tasks });
180
-
181
- expect(result.waves.length).toBe(4);
182
- expect(result.waves[0].tasks).toEqual(['a']);
183
- expect(result.waves[1].tasks).toEqual(['b']);
184
- expect(result.waves[2].tasks).toEqual(['c']);
185
- expect(result.waves[3].tasks).toEqual(['d']);
186
- });
187
-
188
- it('should handle diamond dependency pattern', () => {
189
- const tasks = [
190
- { id: 'a', dependsOn: [], duration: 5 },
191
- { id: 'b', dependsOn: ['a'], duration: 10 },
192
- { id: 'c', dependsOn: ['a'], duration: 8 },
193
- { id: 'd', dependsOn: ['b', 'c'], duration: 5 },
194
- ];
195
-
196
- const result = analyzer.analyzeWaves('test-workflow', { customTasks: tasks });
197
-
198
- expect(result.waves.length).toBe(3);
199
- expect(result.waves[0].tasks).toEqual(['a']);
200
- expect(result.waves[1].tasks).toEqual(expect.arrayContaining(['b', 'c']));
201
- expect(result.waves[1].parallel).toBe(true);
202
- expect(result.waves[2].tasks).toEqual(['d']);
203
- });
204
-
205
- it('should handle mixed parallel and sequential', () => {
206
- const tasks = [
207
- { id: 'a', dependsOn: [], duration: 5 },
208
- { id: 'b', dependsOn: [], duration: 3 },
209
- { id: 'c', dependsOn: ['a'], duration: 10 },
210
- { id: 'd', dependsOn: ['b'], duration: 8 },
211
- { id: 'e', dependsOn: ['c', 'd'], duration: 5 },
212
- ];
213
-
214
- const result = analyzer.analyzeWaves('test-workflow', { customTasks: tasks });
215
-
216
- expect(result.waves.length).toBe(3);
217
- expect(result.waves[0].tasks).toEqual(expect.arrayContaining(['a', 'b']));
218
- expect(result.waves[1].tasks).toEqual(expect.arrayContaining(['c', 'd']));
219
- expect(result.waves[2].tasks).toEqual(['e']);
220
- });
221
-
222
- it('should calculate optimization gain', () => {
223
- const tasks = [
224
- { id: 'a', dependsOn: [], duration: 10 },
225
- { id: 'b', dependsOn: [], duration: 10 },
226
- { id: 'c', dependsOn: ['a', 'b'], duration: 10 },
227
- ];
228
-
229
- const result = analyzer.analyzeWaves('test-workflow', { customTasks: tasks });
230
-
231
- // Sequential: 10 + 10 + 10 = 30min
232
- // Parallel: 10 (wave 1: a,b parallel) + 10 (wave 2: c) = 20min
233
- // Gain: (30 - 20) / 30 = 33%
234
- expect(result.optimizationGain).toBe('33%');
235
- });
236
-
237
- it('should return empty result for empty tasks', () => {
238
- const result = analyzer.analyzeWaves('test-workflow', { customTasks: [] });
239
-
240
- expect(result.totalTasks).toBe(0);
241
- expect(result.waves).toEqual([]);
242
- expect(result.criticalPath).toEqual([]);
243
- });
244
-
245
- it('should handle single task workflow', () => {
246
- const tasks = [{ id: 'a', dependsOn: [], duration: 5 }];
247
-
248
- const result = analyzer.analyzeWaves('test-workflow', { customTasks: tasks });
249
-
250
- expect(result.totalTasks).toBe(1);
251
- expect(result.waves.length).toBe(1);
252
- expect(result.waves[0].parallel).toBe(false);
253
- });
254
-
255
- it('should throw CircularDependencyError for cycles', () => {
256
- const tasks = [
257
- { id: 'a', dependsOn: ['c'], duration: 5 },
258
- { id: 'b', dependsOn: ['a'], duration: 5 },
259
- { id: 'c', dependsOn: ['b'], duration: 5 },
260
- ];
261
-
262
- expect(() => {
263
- analyzer.analyzeWaves('test-workflow', { customTasks: tasks });
264
- }).toThrow(CircularDependencyError);
265
- });
266
- });
267
-
268
- describe('CircularDependencyError', () => {
269
- it('should contain cycle information', () => {
270
- const cycle = ['a', 'b', 'c', 'a'];
271
- const error = new CircularDependencyError(cycle);
272
-
273
- expect(error.name).toBe('CircularDependencyError');
274
- expect(error.cycle).toEqual(cycle);
275
- expect(error.message).toContain('a → b → c → a');
276
- });
277
-
278
- it('should provide resolution suggestion', () => {
279
- const cycle = ['a', 'b', 'c', 'a'];
280
- const error = new CircularDependencyError(cycle);
281
-
282
- const suggestion = error.getSuggestion();
283
- expect(suggestion).toBeTruthy();
284
- expect(typeof suggestion).toBe('string');
285
- });
286
- });
287
-
288
- describe('critical path calculation', () => {
289
- it('should find longest path in graph', () => {
290
- const tasks = [
291
- { id: 'a', dependsOn: [], duration: 5 },
292
- { id: 'b', dependsOn: ['a'], duration: 20 },
293
- { id: 'c', dependsOn: ['a'], duration: 3 },
294
- { id: 'd', dependsOn: ['b', 'c'], duration: 5 },
295
- ];
296
-
297
- const result = analyzer.analyzeWaves('test-workflow', { customTasks: tasks });
298
-
299
- // Critical path should be a -> b -> d (5 + 20 + 5 = 30)
300
- // Not a -> c -> d (5 + 3 + 5 = 13)
301
- expect(result.criticalPath).toEqual(expect.arrayContaining(['a', 'b', 'd']));
302
- });
303
-
304
- it('should return empty for empty workflow', () => {
305
- const result = analyzer.analyzeWaves('test-workflow', { customTasks: [] });
306
- expect(result.criticalPath).toEqual([]);
307
- });
308
- });
309
-
310
- describe('getCurrentWave', () => {
311
- it('should return current wave context for task', () => {
312
- // Mock the analyzeWaves to return predictable result
313
- const tasks = [
314
- { id: 'a', dependsOn: [], duration: 5 },
315
- { id: 'b', dependsOn: ['a'], duration: 10 },
316
- { id: 'c', dependsOn: ['b'], duration: 5 },
317
- ];
318
-
319
- const context = analyzer.getCurrentWave('test-workflow', 'b');
320
-
321
- // Since we can't easily mock, this tests the error handling path
322
- expect(context.workflowId).toBe('test-workflow');
323
- expect(context.currentTask).toBe('b');
324
- });
325
- });
326
-
327
- describe('formatOutput', () => {
328
- const mockAnalysis = {
329
- workflowId: 'test',
330
- totalTasks: 3,
331
- waves: [
332
- { waveNumber: 1, tasks: ['a', 'b'], parallel: true, estimatedDuration: '5min' },
333
- { waveNumber: 2, tasks: ['c'], parallel: false, estimatedDuration: '10min' },
334
- ],
335
- optimizationGain: '25%',
336
- criticalPath: ['a', 'c'],
337
- metrics: {
338
- sequentialTime: '20min',
339
- parallelTime: '15min',
340
- },
341
- };
342
-
343
- it('should format as JSON when json option is true', () => {
344
- const output = analyzer.formatOutput(mockAnalysis, { json: true });
345
-
346
- expect(() => JSON.parse(output)).not.toThrow();
347
- const parsed = JSON.parse(output);
348
- expect(parsed.workflowId).toBe('test');
349
- });
350
-
351
- it('should include visual representation when visual option is true', () => {
352
- const output = analyzer.formatOutput(mockAnalysis, { visual: true });
353
-
354
- expect(output).toContain('Wave 1');
355
- expect(output).toContain('──');
356
- });
357
-
358
- it('should include optimization metrics', () => {
359
- const output = analyzer.formatOutput(mockAnalysis);
360
-
361
- expect(output).toContain('25%');
362
- expect(output).toContain('Critical Path');
363
- });
364
- });
365
-
366
- describe('performance', () => {
367
- it('should analyze small workflow in <10ms', () => {
368
- const tasks = Array.from({ length: 5 }, (_, i) => ({
369
- id: `task-${i}`,
370
- dependsOn: i > 0 ? [`task-${i - 1}`] : [],
371
- duration: 5,
372
- }));
373
-
374
- const start = Date.now();
375
- const result = analyzer.analyzeWaves('perf-test', { customTasks: tasks });
376
- const elapsed = Date.now() - start;
377
-
378
- expect(elapsed).toBeLessThan(10);
379
- expect(result.metrics.analysisTime).toBeLessThan(10);
380
- });
381
-
382
- it('should analyze medium workflow in <30ms', () => {
383
- const tasks = Array.from({ length: 20 }, (_, i) => ({
384
- id: `task-${i}`,
385
- dependsOn: i > 0 ? [`task-${Math.floor(i / 2)}`] : [],
386
- duration: 5,
387
- }));
388
-
389
- const start = Date.now();
390
- const result = analyzer.analyzeWaves('perf-test', { customTasks: tasks });
391
- const elapsed = Date.now() - start;
392
-
393
- expect(elapsed).toBeLessThan(30);
394
- });
395
-
396
- it('should analyze large workflow in <50ms', () => {
397
- const tasks = Array.from({ length: 50 }, (_, i) => ({
398
- id: `task-${i}`,
399
- dependsOn: i > 0 ? [`task-${Math.max(0, i - 3)}`] : [],
400
- duration: 5,
401
- }));
402
-
403
- const start = Date.now();
404
- const result = analyzer.analyzeWaves('perf-test', { customTasks: tasks });
405
- const elapsed = Date.now() - start;
406
-
407
- expect(elapsed).toBeLessThan(50);
408
- });
409
- });
410
- });
411
-
412
- describe('Factory functions', () => {
413
- describe('createWaveAnalyzer', () => {
414
- it('should create WaveAnalyzer instance', () => {
415
- const instance = createWaveAnalyzer();
416
- expect(instance).toBeInstanceOf(WaveAnalyzer);
417
- });
418
-
419
- it('should pass options to constructor', () => {
420
- const customDurations = { custom: 15 };
421
- const instance = createWaveAnalyzer({ taskDurations: customDurations });
422
- expect(instance.taskDurations.custom).toBe(15);
423
- });
424
- });
425
-
426
- describe('analyzeWaves convenience function', () => {
427
- it('should analyze waves without creating instance manually', () => {
428
- const tasks = [
429
- { id: 'a', dependsOn: [], duration: 5 },
430
- { id: 'b', dependsOn: ['a'], duration: 10 },
431
- ];
432
-
433
- const result = analyzeWaves('test', { customTasks: tasks });
434
-
435
- expect(result.workflowId).toBe('test');
436
- expect(result.waves.length).toBe(2);
437
- });
438
- });
439
- });
440
-
441
- describe('DEFAULT_TASK_DURATIONS', () => {
442
- it('should have expected default durations', () => {
443
- expect(DEFAULT_TASK_DURATIONS.implement).toBe(30);
444
- expect(DEFAULT_TASK_DURATIONS['write-tests']).toBe(10);
445
- expect(DEFAULT_TASK_DURATIONS.default).toBe(10);
446
- });
447
- });
448
-