@codemcp/workflows 4.10.2 → 4.10.4

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 (56) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/components/beads/beads-instruction-generator.d.ts +1 -4
  3. package/dist/components/beads/beads-instruction-generator.d.ts.map +1 -1
  4. package/dist/components/beads/beads-instruction-generator.js +24 -52
  5. package/dist/components/beads/beads-instruction-generator.js.map +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/server-config.d.ts.map +1 -1
  9. package/dist/server-config.js +4 -8
  10. package/dist/server-config.js.map +1 -1
  11. package/dist/tool-handlers/base-tool-handler.d.ts.map +1 -1
  12. package/dist/tool-handlers/base-tool-handler.js.map +1 -1
  13. package/dist/tool-handlers/get-tool-info.d.ts +0 -1
  14. package/dist/tool-handlers/get-tool-info.d.ts.map +1 -1
  15. package/dist/tool-handlers/get-tool-info.js +0 -1
  16. package/dist/tool-handlers/get-tool-info.js.map +1 -1
  17. package/dist/tool-handlers/proceed-to-phase.d.ts +0 -2
  18. package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -1
  19. package/dist/tool-handlers/proceed-to-phase.js +0 -2
  20. package/dist/tool-handlers/proceed-to-phase.js.map +1 -1
  21. package/dist/tool-handlers/resume-workflow.d.ts +0 -1
  22. package/dist/tool-handlers/resume-workflow.d.ts.map +1 -1
  23. package/dist/tool-handlers/resume-workflow.js +0 -1
  24. package/dist/tool-handlers/resume-workflow.js.map +1 -1
  25. package/dist/tool-handlers/start-development.d.ts +0 -3
  26. package/dist/tool-handlers/start-development.d.ts.map +1 -1
  27. package/dist/tool-handlers/start-development.js +0 -6
  28. package/dist/tool-handlers/start-development.js.map +1 -1
  29. package/dist/tool-handlers/whats-next.d.ts +0 -2
  30. package/dist/tool-handlers/whats-next.d.ts.map +1 -1
  31. package/dist/tool-handlers/whats-next.js +0 -2
  32. package/dist/tool-handlers/whats-next.js.map +1 -1
  33. package/package.json +5 -5
  34. package/src/components/beads/beads-instruction-generator.ts +28 -60
  35. package/src/index.ts +1 -1
  36. package/src/server-config.ts +6 -9
  37. package/src/tool-handlers/base-tool-handler.ts +4 -3
  38. package/src/tool-handlers/get-tool-info.ts +0 -2
  39. package/src/tool-handlers/proceed-to-phase.ts +0 -4
  40. package/src/tool-handlers/resume-workflow.ts +0 -2
  41. package/src/tool-handlers/start-development.ts +0 -8
  42. package/src/tool-handlers/whats-next.ts +0 -4
  43. package/test/e2e/beads-plugin-integration.test.ts +4 -19
  44. package/test/e2e/core-functionality.test.ts +3 -12
  45. package/test/e2e/mcp-contract.test.ts +0 -31
  46. package/test/e2e/plugin-system-integration.test.ts +7 -315
  47. package/test/e2e/state-management.test.ts +1 -5
  48. package/test/e2e/workflow-integration.test.ts +2 -11
  49. package/test/unit/beads-instruction-generator.test.ts +235 -103
  50. package/test/unit/beads-phase-task-id-integration.test.ts +7 -29
  51. package/test/unit/resume-workflow.test.ts +0 -1
  52. package/test/unit/server-tools.test.ts +0 -1
  53. package/test/utils/e2e-test-setup.ts +2 -3
  54. package/test/utils/test-setup.ts +2 -3
  55. package/tsconfig.build.json +2 -1
  56. package/tsconfig.build.tsbuildinfo +1 -1
@@ -33,7 +33,6 @@ import { McpToolResponse } from '../../src/types';
33
33
  import type { StartDevelopmentResult } from '../../src/tool-handlers/start-development';
34
34
  import type { ProceedToPhaseResult } from '../../src/tool-handlers/proceed-to-phase';
35
35
  import type { WhatsNextResult } from '../../src/tool-handlers/whats-next';
36
- import type { YamlStateMachine } from '@codemcp/workflows-core';
37
36
 
38
37
  vi.unmock('fs');
39
38
  vi.unmock('fs/promises');
@@ -61,18 +60,6 @@ const WORKFLOW_INITIAL_PHASES = {
61
60
  // These helpers enforce strict contract validation and prevent assertion
62
61
  // repetition. Each helper comprehensively validates one response type.
63
62
 
64
- /**
65
- * Validates UUID format (standard v4 UUID) - RELAXED FOR NOW
66
- * In the actual codebase, conversation IDs may use different formats
67
- * The important validation is that they're non-empty strings
68
- * VALIDATE: IDs must be uniquely identifiable
69
- */
70
- function isValidUUID(value: string): boolean {
71
- // Accept anything that looks like a UUID or a similar unique identifier
72
- // Format: hex chars and dashes, length 36+, or any non-empty string
73
- return /^[a-f0-9-]{36,}$|^[a-zA-Z0-9_-]{10,}$/.test(value);
74
- }
75
-
76
63
  /**
77
64
  * Validates that a value is a non-empty string
78
65
  */
@@ -94,46 +81,6 @@ function isSubstantiveContent(value: string): boolean {
94
81
  );
95
82
  }
96
83
 
97
- /**
98
- * Validates workflow object structure
99
- * VALIDATE: Workflow must have name and state definitions
100
- */
101
- function isValidWorkflowObject(
102
- workflow: unknown
103
- ): workflow is YamlStateMachine {
104
- if (typeof workflow !== 'object' || workflow === null) {
105
- return false;
106
- }
107
-
108
- const obj = workflow as Record<string, unknown>;
109
-
110
- // VALIDATE: All required properties must exist
111
- return (
112
- typeof obj.name === 'string' &&
113
- obj.name.length > 0 &&
114
- typeof obj.initial_state === 'string' &&
115
- obj.initial_state.length > 0 &&
116
- typeof obj.states === 'object' &&
117
- obj.states !== null
118
- );
119
- }
120
-
121
- /**
122
- * Validates phase string against valid workflow phases
123
- * VALIDATE: Phase must exist in workflow states
124
- */
125
- function isValidPhaseForWorkflow(
126
- phase: string,
127
- workflow: YamlStateMachine
128
- ): boolean {
129
- if (typeof phase !== 'string' || phase.length === 0) {
130
- return false;
131
- }
132
-
133
- const states = workflow.states as Record<string, unknown>;
134
- return phase in states;
135
- }
136
-
137
84
  /**
138
85
  * Comprehensive validation for StartDevelopmentResult
139
86
  * VALIDATE: Response must have all required properties with correct types and values
@@ -151,33 +98,16 @@ function assertValidStartDevelopmentResponse(
151
98
  }
152
99
  const result = response as Record<string, unknown>;
153
100
 
154
- // VALIDATE: conversation_id must be a non-empty string in UUID format
155
- expect(result).toHaveProperty('conversation_id');
156
- expect(isNonEmptyString(result.conversation_id)).toBe(true);
157
- expect(isValidUUID(result.conversation_id as string)).toBe(true);
158
-
159
- // VALIDATE: phase must be a non-empty string
160
101
  expect(result).toHaveProperty('phase');
161
102
  expect(isNonEmptyString(result.phase)).toBe(true);
162
103
 
163
- // VALIDATE: plan_file_path must be a non-empty string pointing to existing file
164
104
  expect(result).toHaveProperty('plan_file_path');
165
105
  expect(isNonEmptyString(result.plan_file_path)).toBe(true);
166
106
 
167
- // VALIDATE: instructions must be substantive content
168
107
  expect(result).toHaveProperty('instructions');
169
108
  expect(isNonEmptyString(result.instructions)).toBe(true);
170
109
  expect(isSubstantiveContent(result.instructions as string)).toBe(true);
171
110
 
172
- // VALIDATE: workflow must be valid YamlStateMachine object
173
- expect(result).toHaveProperty('workflow');
174
- expect(isValidWorkflowObject(result.workflow)).toBe(true);
175
-
176
- // VALIDATE: phase must be valid for the workflow
177
- const workflow = result.workflow as YamlStateMachine;
178
- expect(isValidPhaseForWorkflow(result.phase as string, workflow)).toBe(true);
179
-
180
- // VALIDATE: workflowDocumentationUrl is optional but must be string if present
181
111
  if (result.workflowDocumentationUrl !== undefined) {
182
112
  expect(typeof result.workflowDocumentationUrl).toBe('string');
183
113
  }
@@ -202,32 +132,19 @@ function assertValidProceedToPhaseResponse(
202
132
  }
203
133
  const result = response as Record<string, unknown>;
204
134
 
205
- // VALIDATE: phase must be a non-empty string
206
135
  expect(result).toHaveProperty('phase');
207
136
  expect(isNonEmptyString(result.phase)).toBe(true);
208
137
 
209
- // VALIDATE: instructions must be substantive content
210
138
  expect(result).toHaveProperty('instructions');
211
139
  expect(isNonEmptyString(result.instructions)).toBe(true);
212
140
  expect(isSubstantiveContent(result.instructions as string)).toBe(true);
213
141
 
214
- // VALIDATE: plan_file_path must be a non-empty string
215
142
  expect(result).toHaveProperty('plan_file_path');
216
143
  expect(isNonEmptyString(result.plan_file_path)).toBe(true);
217
144
 
218
- // VALIDATE: transition_reason must be a non-empty string
219
145
  expect(result).toHaveProperty('transition_reason');
220
146
  expect(isNonEmptyString(result.transition_reason)).toBe(true);
221
147
 
222
- // VALIDATE: is_modeled_transition must be boolean (NOT string, NOT null)
223
- expect(result).toHaveProperty('is_modeled_transition');
224
- expect(typeof result.is_modeled_transition).toBe('boolean');
225
-
226
- // VALIDATE: conversation_id must be a valid UUID
227
- expect(result).toHaveProperty('conversation_id');
228
- expect(isNonEmptyString(result.conversation_id)).toBe(true);
229
- expect(isValidUUID(result.conversation_id as string)).toBe(true);
230
-
231
148
  return result as unknown as ProceedToPhaseResult;
232
149
  }
233
150
 
@@ -246,28 +163,16 @@ function assertValidWhatsNextResponse(response: unknown): WhatsNextResult {
246
163
  }
247
164
  const result = response as Record<string, unknown>;
248
165
 
249
- // VALIDATE: phase must be a non-empty string
250
166
  expect(result).toHaveProperty('phase');
251
167
  expect(isNonEmptyString(result.phase)).toBe(true);
252
168
 
253
- // VALIDATE: instructions must be substantive content
254
169
  expect(result).toHaveProperty('instructions');
255
170
  expect(isNonEmptyString(result.instructions)).toBe(true);
256
171
  expect(isSubstantiveContent(result.instructions as string)).toBe(true);
257
172
 
258
- // VALIDATE: plan_file_path must be a non-empty string
259
173
  expect(result).toHaveProperty('plan_file_path');
260
174
  expect(isNonEmptyString(result.plan_file_path)).toBe(true);
261
175
 
262
- // VALIDATE: is_modeled_transition must be boolean (NOT string, NOT null)
263
- expect(result).toHaveProperty('is_modeled_transition');
264
- expect(typeof result.is_modeled_transition).toBe('boolean');
265
-
266
- // VALIDATE: conversation_id must be a valid UUID
267
- expect(result).toHaveProperty('conversation_id');
268
- expect(isNonEmptyString(result.conversation_id)).toBe(true);
269
- expect(isValidUUID(result.conversation_id as string)).toBe(true);
270
-
271
176
  return result as unknown as WhatsNextResult;
272
177
  }
273
178
 
@@ -337,12 +242,9 @@ describe('Plugin System Integration Tests', () => {
337
242
  const response = assertToolSuccess(result);
338
243
  const validated = assertValidStartDevelopmentResponse(response);
339
244
 
340
- // VALIDATE: Response is properly typed
341
- expect(validated.conversation_id).toBeDefined();
342
245
  expect(validated.phase).toBeDefined();
343
246
  expect(validated.plan_file_path).toBeDefined();
344
247
  expect(validated.instructions).toBeDefined();
345
- expect(validated.workflow).toBeDefined();
346
248
  });
347
249
 
348
250
  it('should return valid ProceedToPhaseResult with all required properties', async () => {
@@ -357,10 +259,7 @@ describe('Plugin System Integration Tests', () => {
357
259
  const response = assertToolSuccess(result);
358
260
  const validated = assertValidProceedToPhaseResponse(response);
359
261
 
360
- // VALIDATE: Response has all required properties
361
262
  expect(validated.phase).toBe('design');
362
- // is_modeled_transition can be true or false - just validate it's boolean
363
- expect(typeof validated.is_modeled_transition).toBe('boolean');
364
263
  });
365
264
 
366
265
  it('should return valid WhatsNextResult with all required properties', async () => {
@@ -374,9 +273,7 @@ describe('Plugin System Integration Tests', () => {
374
273
  const response = assertToolSuccess(result);
375
274
  const validated = assertValidWhatsNextResponse(response);
376
275
 
377
- // VALIDATE: Response has all required properties
378
276
  expect(validated.phase).toBe('requirements');
379
- expect(validated.is_modeled_transition).toBeDefined();
380
277
  });
381
278
 
382
279
  it('should validate conversation IDs are UUID format', async () => {
@@ -385,10 +282,7 @@ describe('Plugin System Integration Tests', () => {
385
282
  commit_behaviour: 'none',
386
283
  });
387
284
 
388
- const response = assertToolSuccess(result);
389
-
390
- // VALIDATE: conversation_id must be UUID format to ensure uniqueness
391
- expect(isValidUUID(response.conversation_id)).toBe(true);
285
+ assertToolSuccess(result);
392
286
  });
393
287
 
394
288
  it('should validate instructions contain substantive content', async () => {
@@ -399,7 +293,6 @@ describe('Plugin System Integration Tests', () => {
399
293
 
400
294
  const response = assertToolSuccess(result);
401
295
 
402
- // VALIDATE: instructions must be meaningful and guide user
403
296
  expect(response.instructions.length).toBeGreaterThan(100);
404
297
  expect(response.instructions).toMatch(
405
298
  /\b(phase|development|task|workflow|plan)\b/i
@@ -414,40 +307,10 @@ describe('Plugin System Integration Tests', () => {
414
307
 
415
308
  const response = assertToolSuccess(result);
416
309
 
417
- // VALIDATE: plan file must exist and be readable
418
310
  await assertFileExists(response.plan_file_path);
419
311
  const content = await fs.readFile(response.plan_file_path, 'utf-8');
420
312
  expect(content.length).toBeGreaterThan(0);
421
313
  });
422
-
423
- it('should validate workflow objects have required structure', async () => {
424
- const result = await client.callTool('start_development', {
425
- workflow: 'waterfall',
426
- commit_behaviour: 'none',
427
- });
428
-
429
- const response = assertToolSuccess(result);
430
-
431
- // VALIDATE: workflow must be actual object with expected properties
432
- expect(response.workflow).toStrictEqual(expect.any(Object));
433
- expect(response.workflow).toHaveProperty('name');
434
- expect(response.workflow).toHaveProperty('initial_state');
435
- expect(response.workflow).toHaveProperty('states');
436
- expect(response.workflow.name).toBe('waterfall');
437
- });
438
-
439
- it('should validate phase is valid for workflow', async () => {
440
- const result = await client.callTool('start_development', {
441
- workflow: 'waterfall',
442
- commit_behaviour: 'none',
443
- });
444
-
445
- const response = assertToolSuccess(result);
446
-
447
- // VALIDATE: phase must exist in workflow states
448
- const states = response.workflow.states as Record<string, unknown>;
449
- expect(states).toHaveProperty(response.phase);
450
- });
451
314
  });
452
315
 
453
316
  describe('Semantic Validation', () => {
@@ -481,7 +344,6 @@ describe('Plugin System Integration Tests', () => {
481
344
 
482
345
  const response = assertToolSuccess(result);
483
346
 
484
- // VALIDATE: Plan file must exist and contain workflow sections
485
347
  const planContent = await fs.readFile(response.plan_file_path, 'utf-8');
486
348
  expect(planContent).toContain('## Explore');
487
349
  expect(planContent).toContain('## Plan');
@@ -510,7 +372,6 @@ describe('Plugin System Integration Tests', () => {
510
372
 
511
373
  const response = assertToolSuccess(result);
512
374
 
513
- // VALIDATE: phase must match the target and be in valid list
514
375
  expect(response.phase).toBe(targetPhase);
515
376
  expect(validPhases).toContain(response.phase);
516
377
  }
@@ -537,10 +398,8 @@ describe('Plugin System Integration Tests', () => {
537
398
  });
538
399
  const response2 = assertToolSuccess(result2);
539
400
 
540
- // VALIDATE: Plan file path must remain consistent
541
401
  expect(response2.plan_file_path).toBe(planPath1);
542
402
 
543
- // VALIDATE: File must exist and have content
544
403
  const planContent = await fs.readFile(planPath1, 'utf-8');
545
404
  expect(planContent.length).toBeGreaterThan(0);
546
405
  });
@@ -563,7 +422,6 @@ describe('Plugin System Integration Tests', () => {
563
422
  });
564
423
  const response = assertToolSuccess(result);
565
424
 
566
- // VALIDATE: instructions must be substantive
567
425
  expect(isSubstantiveContent(response.instructions)).toBe(true);
568
426
 
569
427
  // Transition to next phase
@@ -609,13 +467,9 @@ describe('Plugin System Integration Tests', () => {
609
467
 
610
468
  const response = assertToolSuccess(result);
611
469
 
612
- // VALIDATE: No plugin internals should leak
613
470
  assertNoPluginLeak(response);
614
471
 
615
- // VALIDATE: Should have core fields only
616
- expect(response).toHaveProperty('conversation_id');
617
472
  expect(response).toHaveProperty('phase');
618
- expect(response).toHaveProperty('workflow');
619
473
  expect(response).toHaveProperty('instructions');
620
474
  });
621
475
 
@@ -630,13 +484,10 @@ describe('Plugin System Integration Tests', () => {
630
484
 
631
485
  const response = assertToolSuccess(result);
632
486
 
633
- // VALIDATE: No plugin internals should leak
634
487
  assertNoPluginLeak(response);
635
488
 
636
- // VALIDATE: Should have core fields only
637
489
  expect(response).toHaveProperty('phase');
638
490
  expect(response).toHaveProperty('instructions');
639
- expect(response).toHaveProperty('is_modeled_transition');
640
491
  });
641
492
 
642
493
  it('should not expose plugin internals in WhatsNextResult', async () => {
@@ -648,13 +499,10 @@ describe('Plugin System Integration Tests', () => {
648
499
 
649
500
  const response = assertToolSuccess(result);
650
501
 
651
- // VALIDATE: No plugin internals should leak
652
502
  assertNoPluginLeak(response);
653
503
 
654
- // VALIDATE: Should have core fields only
655
504
  expect(response).toHaveProperty('phase');
656
505
  expect(response).toHaveProperty('instructions');
657
- expect(response).toHaveProperty('is_modeled_transition');
658
506
  });
659
507
  });
660
508
 
@@ -687,16 +535,7 @@ describe('Plugin System Integration Tests', () => {
687
535
  commit_behaviour: 'none',
688
536
  });
689
537
 
690
- const response = assertValidStartDevelopmentResponse(
691
- assertToolSuccess(result)
692
- );
693
-
694
- // VALIDATE: Workflow name must match selected workflow
695
- expect(response.workflow.name).toBe('waterfall');
696
-
697
- // VALIDATE: Initial phase must be valid for workflow
698
- const states = response.workflow.states as Record<string, unknown>;
699
- expect(states).toHaveProperty(response.phase);
538
+ assertValidStartDevelopmentResponse(assertToolSuccess(result));
700
539
  });
701
540
 
702
541
  it('should work with epcc workflow', async () => {
@@ -709,10 +548,6 @@ describe('Plugin System Integration Tests', () => {
709
548
  assertToolSuccess(result)
710
549
  );
711
550
 
712
- // VALIDATE: Workflow name must match selected workflow
713
- expect(response.workflow.name).toBe('epcc');
714
-
715
- // VALIDATE: Initial phase must be explore
716
551
  expect(response.phase).toBe('explore');
717
552
  });
718
553
 
@@ -726,10 +561,6 @@ describe('Plugin System Integration Tests', () => {
726
561
  assertToolSuccess(result)
727
562
  );
728
563
 
729
- // VALIDATE: Workflow name must match selected workflow
730
- expect(response.workflow.name).toBe('tdd');
731
-
732
- // VALIDATE: Initial phase must be explore
733
564
  expect(response.phase).toBe('explore');
734
565
  });
735
566
 
@@ -743,10 +574,6 @@ describe('Plugin System Integration Tests', () => {
743
574
  assertToolSuccess(result)
744
575
  );
745
576
 
746
- // VALIDATE: Workflow name must match selected workflow
747
- expect(response.workflow.name).toBe('minor');
748
-
749
- // VALIDATE: Initial phase must be explore
750
577
  expect(response.phase).toBe('explore');
751
578
  });
752
579
 
@@ -756,17 +583,7 @@ describe('Plugin System Integration Tests', () => {
756
583
  commit_behaviour: 'none',
757
584
  });
758
585
 
759
- const response = assertValidStartDevelopmentResponse(
760
- assertToolSuccess(result)
761
- );
762
-
763
- // VALIDATE: Workflow name must match selected workflow
764
- expect(response.workflow.name).toBe('bugfix');
765
-
766
- // VALIDATE: Initial phase must be reproduce or analyze
767
- const states = response.workflow.states as Record<string, unknown>;
768
- expect(states).toHaveProperty(response.phase);
769
- expect(['reproduce', 'analyze']).toContain(response.phase);
586
+ assertValidStartDevelopmentResponse(assertToolSuccess(result));
770
587
  });
771
588
  });
772
589
 
@@ -793,59 +610,6 @@ describe('Plugin System Integration Tests', () => {
793
610
  }
794
611
  });
795
612
 
796
- it('should preserve conversation_id across tool calls', async () => {
797
- const result1 = await client.callTool('start_development', {
798
- workflow: 'waterfall',
799
- commit_behaviour: 'none',
800
- });
801
- const response1 = assertValidStartDevelopmentResponse(
802
- assertToolSuccess(result1)
803
- );
804
- const conversationId1 = response1.conversation_id;
805
-
806
- // VALIDATE: conversation_id must be UUID format
807
- expect(isValidUUID(conversationId1)).toBe(true);
808
-
809
- // Make another call
810
- const result2 = await client.callTool('whats_next', {
811
- user_input: 'continue development',
812
- });
813
- const response2 = assertValidWhatsNextResponse(
814
- assertToolSuccess(result2)
815
- );
816
-
817
- // VALIDATE: Conversation must be maintained
818
- expect(response2.conversation_id).toBe(conversationId1);
819
- });
820
-
821
- it('should transition phases while maintaining conversation_id', async () => {
822
- await initializeDevelopment(client, 'waterfall');
823
-
824
- const result1 = await client.callTool('whats_next', {
825
- user_input: 'test 1',
826
- });
827
- const response1 = assertValidWhatsNextResponse(
828
- assertToolSuccess(result1)
829
- );
830
- const conversationId = response1.conversation_id;
831
-
832
- // Transition to design phase
833
- const result2 = await client.callTool('proceed_to_phase', {
834
- target_phase: 'design',
835
- reason: 'ready to design',
836
- review_state: 'not-required',
837
- });
838
- const response2 = assertValidProceedToPhaseResponse(
839
- assertToolSuccess(result2)
840
- );
841
-
842
- // VALIDATE: Conversation_id must remain the same
843
- expect(response2.conversation_id).toBe(conversationId);
844
-
845
- // VALIDATE: Phase must have changed
846
- expect(response2.phase).toBe('design');
847
- });
848
-
849
613
  it('should handle phase transitions with proper state updates', async () => {
850
614
  await initializeDevelopment(client, 'waterfall');
851
615
 
@@ -860,7 +624,6 @@ describe('Plugin System Integration Tests', () => {
860
624
  (contents1[0] as Record<string, unknown>).text as string
861
625
  );
862
626
 
863
- // VALIDATE: Current phase must match expected
864
627
  expect(stateData1.currentPhase).toBe('requirements');
865
628
 
866
629
  // Transition
@@ -881,7 +644,6 @@ describe('Plugin System Integration Tests', () => {
881
644
  (contents2[0] as Record<string, unknown>).text as string
882
645
  );
883
646
 
884
- // VALIDATE: Phase must have been updated
885
647
  expect(stateData2.currentPhase).toBe('design');
886
648
  });
887
649
  });
@@ -922,19 +684,13 @@ describe('Plugin System Integration Tests', () => {
922
684
  }
923
685
  );
924
686
 
925
- // VALIDATE: Should have error
926
687
  expect(invalid.error).toBeDefined();
927
688
 
928
689
  // Should still work afterwards
929
690
  const recovery = await client.callTool('whats_next', {
930
691
  user_input: 'recover',
931
692
  });
932
- const recoveryResponse = assertValidWhatsNextResponse(
933
- assertToolSuccess(recovery)
934
- );
935
-
936
- // VALIDATE: Response must be valid
937
- expect(isValidUUID(recoveryResponse.conversation_id)).toBe(true);
693
+ assertValidWhatsNextResponse(assertToolSuccess(recovery));
938
694
  });
939
695
 
940
696
  it('should handle missing workflow gracefully', async () => {
@@ -943,7 +699,6 @@ describe('Plugin System Integration Tests', () => {
943
699
  commit_behaviour: 'none',
944
700
  });
945
701
 
946
- // VALIDATE: Should either error or handle gracefully
947
702
  expect(result).toBeDefined();
948
703
  });
949
704
 
@@ -958,7 +713,6 @@ describe('Plugin System Integration Tests', () => {
958
713
  .text as string
959
714
  );
960
715
 
961
- // VALIDATE: Initial state must be valid
962
716
  expect(data1.currentPhase).toBe('requirements');
963
717
 
964
718
  // Cause an error
@@ -976,10 +730,8 @@ describe('Plugin System Integration Tests', () => {
976
730
  .text as string
977
731
  );
978
732
 
979
- // VALIDATE: Phase must not have changed after error
980
733
  expect(data2.currentPhase).toBe(data1.currentPhase);
981
734
 
982
- // VALIDATE: Conversation must remain the same
983
735
  expect(data2.conversationId).toBe(data1.conversationId);
984
736
  });
985
737
  });
@@ -1020,8 +772,6 @@ describe('Plugin System Integration Tests', () => {
1020
772
  assertToolSuccess(result)
1021
773
  );
1022
774
 
1023
- // VALIDATE: All required properties exist and are valid
1024
- expect(isValidUUID(response.conversation_id)).toBe(true);
1025
775
  await assertFileExists(response.plan_file_path);
1026
776
  expect(response.phase).toBe('requirements');
1027
777
  });
@@ -1036,7 +786,6 @@ describe('Plugin System Integration Tests', () => {
1036
786
  assertToolSuccess(result)
1037
787
  );
1038
788
 
1039
- // VALIDATE: Verify proper plan file structure
1040
789
  const planContent = await fs.readFile(response.plan_file_path, 'utf-8');
1041
790
  expect(planContent).toContain('## Explore');
1042
791
  expect(planContent).toContain('## Plan');
@@ -1076,12 +825,10 @@ describe('Plugin System Integration Tests', () => {
1076
825
  )) as unknown;
1077
826
  const resource = stateResource as Record<string, unknown>;
1078
827
 
1079
- // VALIDATE: Resource must have contents array
1080
828
  expect(resource).toHaveProperty('contents');
1081
829
  expect(Array.isArray(resource.contents)).toBe(true);
1082
830
  expect((resource.contents as unknown[]).length).toBeGreaterThan(0);
1083
831
 
1084
- // VALIDATE: Content must be valid JSON with expected fields
1085
832
  const content = (
1086
833
  (resource.contents as unknown[])[0] as Record<string, unknown>
1087
834
  ).text as string;
@@ -1098,12 +845,10 @@ describe('Plugin System Integration Tests', () => {
1098
845
  )) as unknown;
1099
846
  const resource = planResource as Record<string, unknown>;
1100
847
 
1101
- // VALIDATE: Resource must have contents array
1102
848
  expect(resource).toHaveProperty('contents');
1103
849
  expect(Array.isArray(resource.contents)).toBe(true);
1104
850
  expect((resource.contents as unknown[]).length).toBeGreaterThan(0);
1105
851
 
1106
- // VALIDATE: Content must be non-empty string
1107
852
  const content = (
1108
853
  (resource.contents as unknown[])[0] as Record<string, unknown>
1109
854
  ).text as string;
@@ -1117,17 +862,14 @@ describe('Plugin System Integration Tests', () => {
1117
862
  )) as unknown;
1118
863
  const resource = promptResource as Record<string, unknown>;
1119
864
 
1120
- // VALIDATE: Resource must have contents array
1121
865
  expect(resource).toHaveProperty('contents');
1122
866
  expect(Array.isArray(resource.contents)).toBe(true);
1123
867
  expect((resource.contents as unknown[]).length).toBeGreaterThan(0);
1124
868
 
1125
- // VALIDATE: Content must be non-empty string
1126
869
  const contentObj = (resource.contents as unknown[])[0] as Record<
1127
870
  string,
1128
871
  unknown
1129
872
  >;
1130
- // VALIDATE: Must have a string property with content
1131
873
  // Try text first (primary), then content (secondary), then get string representation
1132
874
  let content: string;
1133
875
  if (typeof contentObj.text === 'string' && contentObj.text.length > 0) {
@@ -1186,9 +928,7 @@ describe('Plugin System Integration Tests', () => {
1186
928
  assertToolSuccess(result)
1187
929
  );
1188
930
 
1189
- // VALIDATE: Response indicates hooks were executed successfully
1190
931
  // (plan file exists, instructions present, phase valid)
1191
- expect(response.conversation_id).toBeDefined();
1192
932
  expect(response.phase).toBe('requirements');
1193
933
  expect(response.plan_file_path).toBeDefined();
1194
934
 
@@ -1219,10 +959,6 @@ describe('Plugin System Integration Tests', () => {
1219
959
  assertToolSuccess(whatsNextResult)
1220
960
  );
1221
961
 
1222
- // VALIDATE: State is consistent after hook execution
1223
- expect(whatsNextResponse.conversation_id).toBe(
1224
- startResponse.conversation_id
1225
- );
1226
962
  expect(whatsNextResponse.phase).toBe(startResponse.phase);
1227
963
  expect(whatsNextResponse.plan_file_path).toBe(
1228
964
  startResponse.plan_file_path
@@ -1243,13 +979,11 @@ describe('Plugin System Integration Tests', () => {
1243
979
  // Read and validate plan file
1244
980
  const planContent = await fs.readFile(response.plan_file_path, 'utf-8');
1245
981
 
1246
- // VALIDATE: Plan file structure intact (hooks shouldn't corrupt it)
1247
982
  expect(planContent).toMatch(/^# /m); // Title
1248
983
  expect(planContent).toMatch(/^## /m); // Sections
1249
984
  expect(planContent).toContain('## Goal');
1250
985
  expect(planContent).toContain('## Requirements');
1251
986
 
1252
- // VALIDATE: No malformed content
1253
987
  expect(planContent).not.toContain('undefined');
1254
988
  expect(planContent).not.toContain('[object Object]');
1255
989
  });
@@ -1273,9 +1007,7 @@ describe('Plugin System Integration Tests', () => {
1273
1007
  assertToolSuccess(result)
1274
1008
  );
1275
1009
 
1276
- // VALIDATE: Hooks executed for each workflow
1277
1010
  await assertFileExists(response.plan_file_path);
1278
- expect(response.conversation_id).toBeDefined();
1279
1011
 
1280
1012
  await scenario.cleanup();
1281
1013
  }
@@ -1317,17 +1049,13 @@ describe('Plugin System Integration Tests', () => {
1317
1049
 
1318
1050
  const response = assertToolSuccess(result);
1319
1051
 
1320
- // VALIDATE: Response has no plugin internals
1321
1052
  assertNoPluginLeak(response);
1322
1053
 
1323
- // VALIDATE: Core response properties only (workflowDocumentationUrl is intentional - points to public docs)
1324
1054
  expect(Object.keys(response).sort()).toEqual(
1325
1055
  [
1326
- 'conversation_id',
1327
1056
  'instructions',
1328
1057
  'phase',
1329
1058
  'plan_file_path',
1330
- 'workflow',
1331
1059
  'workflowDocumentationUrl',
1332
1060
  ].sort()
1333
1061
  );
@@ -1340,18 +1068,14 @@ describe('Plugin System Integration Tests', () => {
1340
1068
  commit_behaviour: 'none',
1341
1069
  });
1342
1070
 
1343
- const startResponse = assertValidStartDevelopmentResponse(
1344
- assertToolSuccess(startResult)
1345
- );
1071
+ assertValidStartDevelopmentResponse(assertToolSuccess(startResult));
1346
1072
 
1347
1073
  // Get whats_next
1348
1074
  const whatsNextResult = await client.callTool('whats_next', {
1349
1075
  user_input: 'next step',
1350
1076
  });
1351
1077
 
1352
- const whatsNextResponse = assertValidWhatsNextResponse(
1353
- assertToolSuccess(whatsNextResult)
1354
- );
1078
+ assertValidWhatsNextResponse(assertToolSuccess(whatsNextResult));
1355
1079
 
1356
1080
  // Transition phase
1357
1081
  const transitionResult = await client.callTool('proceed_to_phase', {
@@ -1360,22 +1084,7 @@ describe('Plugin System Integration Tests', () => {
1360
1084
  review_state: 'not-required',
1361
1085
  });
1362
1086
 
1363
- const transitionResponse = assertValidProceedToPhaseResponse(
1364
- assertToolSuccess(transitionResult)
1365
- );
1366
-
1367
- // VALIDATE: All responses have consistent structure (plugins applied uniformly)
1368
- expect(startResponse).toHaveProperty('conversation_id');
1369
- expect(whatsNextResponse).toHaveProperty('conversation_id');
1370
- expect(transitionResponse).toHaveProperty('conversation_id');
1371
-
1372
- // VALIDATE: Same conversation across calls
1373
- expect(whatsNextResponse.conversation_id).toBe(
1374
- startResponse.conversation_id
1375
- );
1376
- expect(transitionResponse.conversation_id).toBe(
1377
- startResponse.conversation_id
1378
- );
1087
+ assertValidProceedToPhaseResponse(assertToolSuccess(transitionResult));
1379
1088
  });
1380
1089
 
1381
1090
  it('should preserve plugin boundaries (no cross-pollution)', async () => {
@@ -1387,15 +1096,11 @@ describe('Plugin System Integration Tests', () => {
1387
1096
 
1388
1097
  const response = assertToolSuccess(result);
1389
1098
 
1390
- // VALIDATE: Response is clean (no plugin implementation details)
1391
1099
  assertNoPluginLeak(response);
1392
1100
 
1393
- // VALIDATE: All plugin functionality exposed only through standard response fields
1394
1101
  expect(response).toHaveProperty('plan_file_path');
1395
1102
  expect(response).toHaveProperty('instructions');
1396
- expect(response).toHaveProperty('conversation_id');
1397
1103
 
1398
- // VALIDATE: No plugin-specific fields
1399
1104
  expect(response).not.toHaveProperty('_plugins');
1400
1105
  expect(response).not.toHaveProperty('beads');
1401
1106
  expect(response).not.toHaveProperty('taskBackendClient');
@@ -1434,7 +1139,6 @@ describe('Plugin System Integration Tests', () => {
1434
1139
  assertToolSuccess(result)
1435
1140
  );
1436
1141
 
1437
- // VALIDATE: Correct initial phase
1438
1142
  expect(response.phase).toBe(WORKFLOW_INITIAL_PHASES.waterfall);
1439
1143
  });
1440
1144
 
@@ -1454,7 +1158,6 @@ describe('Plugin System Integration Tests', () => {
1454
1158
  assertToolSuccess(result)
1455
1159
  );
1456
1160
 
1457
- // VALIDATE: Correct initial phase
1458
1161
  expect(response.phase).toBe(WORKFLOW_INITIAL_PHASES.epcc);
1459
1162
  });
1460
1163
 
@@ -1474,7 +1177,6 @@ describe('Plugin System Integration Tests', () => {
1474
1177
  assertToolSuccess(result)
1475
1178
  );
1476
1179
 
1477
- // VALIDATE: Correct initial phase
1478
1180
  expect(response.phase).toBe(WORKFLOW_INITIAL_PHASES.tdd);
1479
1181
  });
1480
1182
 
@@ -1494,7 +1196,6 @@ describe('Plugin System Integration Tests', () => {
1494
1196
  assertToolSuccess(result)
1495
1197
  );
1496
1198
 
1497
- // VALIDATE: Correct initial phase
1498
1199
  expect(response.phase).toBe(WORKFLOW_INITIAL_PHASES.minor);
1499
1200
  });
1500
1201
 
@@ -1514,7 +1215,6 @@ describe('Plugin System Integration Tests', () => {
1514
1215
  assertToolSuccess(result)
1515
1216
  );
1516
1217
 
1517
- // VALIDATE: Initial phase is one of expected options for bugfix
1518
1218
  const expectedPhases = WORKFLOW_INITIAL_PHASES.bugfix;
1519
1219
  expect(expectedPhases).toContain(response.phase);
1520
1220
  });
@@ -1557,7 +1257,6 @@ describe('Plugin System Integration Tests', () => {
1557
1257
  assertToolSuccess(result)
1558
1258
  );
1559
1259
 
1560
- // VALIDATE: Instructions meet minimum length (substantive content)
1561
1260
  expect(response.instructions.length).toBeGreaterThan(
1562
1261
  MIN_INSTRUCTION_LENGTH
1563
1262
  );
@@ -1575,7 +1274,6 @@ describe('Plugin System Integration Tests', () => {
1575
1274
 
1576
1275
  const planContent = await fs.readFile(response.plan_file_path, 'utf-8');
1577
1276
 
1578
- // VALIDATE: Markdown structure
1579
1277
  expect(planContent).toMatch(/^# /m); // Must have main title
1580
1278
  expect(planContent).toMatch(/^## /m); // Must have sections
1581
1279
  expect(planContent).not.toContain('[object Object]'); // No serialization errors
@@ -1593,7 +1291,6 @@ describe('Plugin System Integration Tests', () => {
1593
1291
  assertToolSuccess(startResult)
1594
1292
  );
1595
1293
 
1596
- // VALIDATE: Initial phase instructions mention phase name or key concepts
1597
1294
  expect(startResponse.instructions).toMatch(/requirement|phase|task/i);
1598
1295
 
1599
1296
  // Transition to design phase
@@ -1612,7 +1309,6 @@ describe('Plugin System Integration Tests', () => {
1612
1309
  assertToolSuccess(designWhatsNext)
1613
1310
  );
1614
1311
 
1615
- // VALIDATE: Design phase instructions are different and relevant
1616
1312
  expect(designResponse.instructions).toBeDefined();
1617
1313
  expect(designResponse.instructions.length).toBeGreaterThan(
1618
1314
  MIN_INSTRUCTION_LENGTH
@@ -1668,7 +1364,6 @@ describe('Plugin System Integration Tests', () => {
1668
1364
  assertToolSuccess(whatsNextResult)
1669
1365
  );
1670
1366
 
1671
- // VALIDATE: Plan path unchanged
1672
1367
  expect(whatsNextResponse.plan_file_path).toBe(planPath);
1673
1368
 
1674
1369
  // Transition
@@ -1682,7 +1377,6 @@ describe('Plugin System Integration Tests', () => {
1682
1377
  assertToolSuccess(transitionResult)
1683
1378
  );
1684
1379
 
1685
- // VALIDATE: Plan path still unchanged
1686
1380
  expect(transitionResponse.plan_file_path).toBe(planPath);
1687
1381
  });
1688
1382
 
@@ -1718,10 +1412,8 @@ describe('Plugin System Integration Tests', () => {
1718
1412
  'utf-8'
1719
1413
  );
1720
1414
 
1721
- // VALIDATE: File exists and has content
1722
1415
  expect(finalContent.length).toBeGreaterThan(0);
1723
1416
 
1724
- // VALIDATE: No corruption
1725
1417
  expect(finalContent).not.toContain('[object Object]');
1726
1418
  expect(finalContent).not.toContain('undefined');
1727
1419
  });