@renseiai/agentfactory 0.8.8 → 0.8.10

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 (186) hide show
  1. package/README.md +2 -2
  2. package/dist/src/config/index.d.ts +1 -1
  3. package/dist/src/config/index.d.ts.map +1 -1
  4. package/dist/src/config/index.js +1 -1
  5. package/dist/src/config/repository-config.d.ts +26 -0
  6. package/dist/src/config/repository-config.d.ts.map +1 -1
  7. package/dist/src/config/repository-config.js +40 -0
  8. package/dist/src/config/repository-config.test.js +140 -1
  9. package/dist/src/governor/decision-engine.d.ts +3 -0
  10. package/dist/src/governor/decision-engine.d.ts.map +1 -1
  11. package/dist/src/governor/decision-engine.js +11 -0
  12. package/dist/src/governor/decision-engine.test.js +33 -0
  13. package/dist/src/governor/governor-types.d.ts +1 -1
  14. package/dist/src/governor/governor-types.d.ts.map +1 -1
  15. package/dist/src/governor/governor.d.ts +17 -1
  16. package/dist/src/governor/governor.d.ts.map +1 -1
  17. package/dist/src/governor/governor.js +112 -1
  18. package/dist/src/governor/governor.test.js +155 -0
  19. package/dist/src/index.d.ts +1 -0
  20. package/dist/src/index.d.ts.map +1 -1
  21. package/dist/src/index.js +1 -0
  22. package/dist/src/orchestrator/index.d.ts +1 -1
  23. package/dist/src/orchestrator/index.d.ts.map +1 -1
  24. package/dist/src/orchestrator/index.js +1 -1
  25. package/dist/src/orchestrator/issue-tracker-client.d.ts +4 -0
  26. package/dist/src/orchestrator/issue-tracker-client.d.ts.map +1 -1
  27. package/dist/src/orchestrator/orchestrator-utils.test.js +31 -1
  28. package/dist/src/orchestrator/orchestrator.d.ts +16 -2
  29. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
  30. package/dist/src/orchestrator/orchestrator.js +70 -15
  31. package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -1
  32. package/dist/src/orchestrator/parse-work-result.js +6 -0
  33. package/dist/src/orchestrator/parse-work-result.test.js +19 -0
  34. package/dist/src/orchestrator/types.d.ts +1 -1
  35. package/dist/src/orchestrator/types.d.ts.map +1 -1
  36. package/dist/src/providers/index.d.ts +64 -1
  37. package/dist/src/providers/index.d.ts.map +1 -1
  38. package/dist/src/providers/index.js +132 -1
  39. package/dist/src/providers/index.test.js +340 -2
  40. package/dist/src/routing/index.d.ts +7 -0
  41. package/dist/src/routing/index.d.ts.map +1 -0
  42. package/dist/src/routing/index.js +6 -0
  43. package/dist/src/routing/observation-recorder.d.ts +19 -0
  44. package/dist/src/routing/observation-recorder.d.ts.map +1 -0
  45. package/dist/src/routing/observation-recorder.js +73 -0
  46. package/dist/src/routing/observation-recorder.test.d.ts +2 -0
  47. package/dist/src/routing/observation-recorder.test.d.ts.map +1 -0
  48. package/dist/src/routing/observation-recorder.test.js +322 -0
  49. package/dist/src/routing/observation-store.d.ts +40 -0
  50. package/dist/src/routing/observation-store.d.ts.map +1 -0
  51. package/dist/src/routing/observation-store.js +1 -0
  52. package/dist/src/routing/observation-store.test.d.ts +2 -0
  53. package/dist/src/routing/observation-store.test.d.ts.map +1 -0
  54. package/dist/src/routing/observation-store.test.js +138 -0
  55. package/dist/src/routing/posterior-store.d.ts +12 -0
  56. package/dist/src/routing/posterior-store.d.ts.map +1 -0
  57. package/dist/src/routing/posterior-store.js +13 -0
  58. package/dist/src/routing/posterior-store.test.d.ts +2 -0
  59. package/dist/src/routing/posterior-store.test.d.ts.map +1 -0
  60. package/dist/src/routing/posterior-store.test.js +37 -0
  61. package/dist/src/routing/reward.d.ts +16 -0
  62. package/dist/src/routing/reward.d.ts.map +1 -0
  63. package/dist/src/routing/reward.js +29 -0
  64. package/dist/src/routing/reward.test.d.ts +2 -0
  65. package/dist/src/routing/reward.test.d.ts.map +1 -0
  66. package/dist/src/routing/reward.test.js +210 -0
  67. package/dist/src/routing/routing-engine.d.ts +20 -0
  68. package/dist/src/routing/routing-engine.d.ts.map +1 -0
  69. package/dist/src/routing/routing-engine.js +113 -0
  70. package/dist/src/routing/routing-engine.test.d.ts +2 -0
  71. package/dist/src/routing/routing-engine.test.d.ts.map +1 -0
  72. package/dist/src/routing/routing-engine.test.js +310 -0
  73. package/dist/src/routing/types.d.ts +157 -0
  74. package/dist/src/routing/types.d.ts.map +1 -0
  75. package/dist/src/routing/types.js +68 -0
  76. package/dist/src/routing/types.test.d.ts +2 -0
  77. package/dist/src/routing/types.test.d.ts.map +1 -0
  78. package/dist/src/routing/types.test.js +184 -0
  79. package/dist/src/templates/types.d.ts +3 -0
  80. package/dist/src/templates/types.d.ts.map +1 -1
  81. package/dist/src/templates/types.js +2 -0
  82. package/dist/src/workflow/agent-cancellation.d.ts +37 -0
  83. package/dist/src/workflow/agent-cancellation.d.ts.map +1 -0
  84. package/dist/src/workflow/agent-cancellation.js +41 -0
  85. package/dist/src/workflow/agent-cancellation.test.d.ts +2 -0
  86. package/dist/src/workflow/agent-cancellation.test.d.ts.map +1 -0
  87. package/dist/src/workflow/agent-cancellation.test.js +86 -0
  88. package/dist/src/workflow/concurrency-semaphore.d.ts +21 -0
  89. package/dist/src/workflow/concurrency-semaphore.d.ts.map +1 -0
  90. package/dist/src/workflow/concurrency-semaphore.js +46 -0
  91. package/dist/src/workflow/concurrency-semaphore.test.d.ts +2 -0
  92. package/dist/src/workflow/concurrency-semaphore.test.d.ts.map +1 -0
  93. package/dist/src/workflow/concurrency-semaphore.test.js +183 -0
  94. package/dist/src/workflow/gate-state.d.ts +115 -0
  95. package/dist/src/workflow/gate-state.d.ts.map +1 -0
  96. package/dist/src/workflow/gate-state.js +185 -0
  97. package/dist/src/workflow/gate-state.test.d.ts +2 -0
  98. package/dist/src/workflow/gate-state.test.d.ts.map +1 -0
  99. package/dist/src/workflow/gate-state.test.js +251 -0
  100. package/dist/src/workflow/gates/gate-evaluator.d.ts +119 -0
  101. package/dist/src/workflow/gates/gate-evaluator.d.ts.map +1 -0
  102. package/dist/src/workflow/gates/gate-evaluator.js +243 -0
  103. package/dist/src/workflow/gates/gate-evaluator.test.d.ts +2 -0
  104. package/dist/src/workflow/gates/gate-evaluator.test.d.ts.map +1 -0
  105. package/dist/src/workflow/gates/gate-evaluator.test.js +240 -0
  106. package/dist/src/workflow/gates/signal-gate.d.ts +114 -0
  107. package/dist/src/workflow/gates/signal-gate.d.ts.map +1 -0
  108. package/dist/src/workflow/gates/signal-gate.js +216 -0
  109. package/dist/src/workflow/gates/signal-gate.test.d.ts +2 -0
  110. package/dist/src/workflow/gates/signal-gate.test.d.ts.map +1 -0
  111. package/dist/src/workflow/gates/signal-gate.test.js +199 -0
  112. package/dist/src/workflow/gates/timeout-engine.d.ts +96 -0
  113. package/dist/src/workflow/gates/timeout-engine.d.ts.map +1 -0
  114. package/dist/src/workflow/gates/timeout-engine.js +162 -0
  115. package/dist/src/workflow/gates/timeout-engine.test.d.ts +2 -0
  116. package/dist/src/workflow/gates/timeout-engine.test.d.ts.map +1 -0
  117. package/dist/src/workflow/gates/timeout-engine.test.js +186 -0
  118. package/dist/src/workflow/gates/timer-gate.d.ts +125 -0
  119. package/dist/src/workflow/gates/timer-gate.d.ts.map +1 -0
  120. package/dist/src/workflow/gates/timer-gate.js +381 -0
  121. package/dist/src/workflow/gates/timer-gate.test.d.ts +2 -0
  122. package/dist/src/workflow/gates/timer-gate.test.d.ts.map +1 -0
  123. package/dist/src/workflow/gates/timer-gate.test.js +211 -0
  124. package/dist/src/workflow/gates/webhook-gate.d.ts +132 -0
  125. package/dist/src/workflow/gates/webhook-gate.d.ts.map +1 -0
  126. package/dist/src/workflow/gates/webhook-gate.js +216 -0
  127. package/dist/src/workflow/gates/webhook-gate.test.d.ts +2 -0
  128. package/dist/src/workflow/gates/webhook-gate.test.d.ts.map +1 -0
  129. package/dist/src/workflow/gates/webhook-gate.test.js +182 -0
  130. package/dist/src/workflow/index.d.ts +23 -2
  131. package/dist/src/workflow/index.d.ts.map +1 -1
  132. package/dist/src/workflow/index.js +15 -1
  133. package/dist/src/workflow/parallelism-executor.d.ts +25 -0
  134. package/dist/src/workflow/parallelism-executor.d.ts.map +1 -0
  135. package/dist/src/workflow/parallelism-executor.js +53 -0
  136. package/dist/src/workflow/parallelism-executor.test.d.ts +2 -0
  137. package/dist/src/workflow/parallelism-executor.test.d.ts.map +1 -0
  138. package/dist/src/workflow/parallelism-executor.test.js +191 -0
  139. package/dist/src/workflow/parallelism-types.d.ts +80 -0
  140. package/dist/src/workflow/parallelism-types.d.ts.map +1 -0
  141. package/dist/src/workflow/parallelism-types.js +8 -0
  142. package/dist/src/workflow/phase-context-injector.d.ts +29 -0
  143. package/dist/src/workflow/phase-context-injector.d.ts.map +1 -0
  144. package/dist/src/workflow/phase-context-injector.js +43 -0
  145. package/dist/src/workflow/phase-context-injector.test.d.ts +2 -0
  146. package/dist/src/workflow/phase-context-injector.test.d.ts.map +1 -0
  147. package/dist/src/workflow/phase-context-injector.test.js +123 -0
  148. package/dist/src/workflow/phase-output-collector.d.ts +39 -0
  149. package/dist/src/workflow/phase-output-collector.d.ts.map +1 -0
  150. package/dist/src/workflow/phase-output-collector.js +141 -0
  151. package/dist/src/workflow/phase-output-collector.test.d.ts +2 -0
  152. package/dist/src/workflow/phase-output-collector.test.d.ts.map +1 -0
  153. package/dist/src/workflow/phase-output-collector.test.js +179 -0
  154. package/dist/src/workflow/strategies/fan-in-strategy.d.ts +21 -0
  155. package/dist/src/workflow/strategies/fan-in-strategy.d.ts.map +1 -0
  156. package/dist/src/workflow/strategies/fan-in-strategy.js +92 -0
  157. package/dist/src/workflow/strategies/fan-in-strategy.test.d.ts +2 -0
  158. package/dist/src/workflow/strategies/fan-in-strategy.test.d.ts.map +1 -0
  159. package/dist/src/workflow/strategies/fan-in-strategy.test.js +182 -0
  160. package/dist/src/workflow/strategies/fan-out-strategy.d.ts +16 -0
  161. package/dist/src/workflow/strategies/fan-out-strategy.d.ts.map +1 -0
  162. package/dist/src/workflow/strategies/fan-out-strategy.js +47 -0
  163. package/dist/src/workflow/strategies/fan-out-strategy.test.d.ts +2 -0
  164. package/dist/src/workflow/strategies/fan-out-strategy.test.d.ts.map +1 -0
  165. package/dist/src/workflow/strategies/fan-out-strategy.test.js +97 -0
  166. package/dist/src/workflow/strategies/index.d.ts +4 -0
  167. package/dist/src/workflow/strategies/index.d.ts.map +1 -0
  168. package/dist/src/workflow/strategies/index.js +3 -0
  169. package/dist/src/workflow/strategies/race-strategy.d.ts +19 -0
  170. package/dist/src/workflow/strategies/race-strategy.d.ts.map +1 -0
  171. package/dist/src/workflow/strategies/race-strategy.js +92 -0
  172. package/dist/src/workflow/strategies/race-strategy.test.d.ts +2 -0
  173. package/dist/src/workflow/strategies/race-strategy.test.d.ts.map +1 -0
  174. package/dist/src/workflow/strategies/race-strategy.test.js +318 -0
  175. package/dist/src/workflow/transition-engine.d.ts.map +1 -1
  176. package/dist/src/workflow/transition-engine.js +12 -0
  177. package/dist/src/workflow/transition-engine.test.js +92 -0
  178. package/dist/src/workflow/workflow-registry.d.ts +5 -1
  179. package/dist/src/workflow/workflow-registry.d.ts.map +1 -1
  180. package/dist/src/workflow/workflow-registry.js +8 -0
  181. package/dist/src/workflow/workflow-registry.test.js +54 -0
  182. package/dist/src/workflow/workflow-types.d.ts +151 -6
  183. package/dist/src/workflow/workflow-types.d.ts.map +1 -1
  184. package/dist/src/workflow/workflow-types.js +71 -1
  185. package/dist/src/workflow/workflow-types.test.js +293 -2
  186. package/package.json +2 -2
@@ -1,5 +1,8 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PhaseDefinitionSchema, TransitionDefinitionSchema, EscalationLadderRungSchema, EscalationConfigSchema, GateDefinitionSchema, ParallelismGroupDefinitionSchema, WorkflowDefinitionSchema, validateWorkflowDefinition, } from './workflow-types.js';
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { describe, it, expect, vi } from 'vitest';
4
+ import { parse as parseYaml } from 'yaml';
5
+ import { PhaseDefinitionSchema, PhaseOutputDeclarationSchema, PhaseInputDeclarationSchema, TransitionDefinitionSchema, EscalationLadderRungSchema, EscalationConfigSchema, GateDefinitionSchema, ParallelismGroupDefinitionSchema, WorkflowDefinitionSchema, validateWorkflowDefinition, } from './workflow-types.js';
3
6
  describe('PhaseDefinitionSchema', () => {
4
7
  it('validates a minimal phase', () => {
5
8
  const result = PhaseDefinitionSchema.parse({
@@ -48,6 +51,112 @@ describe('PhaseDefinitionSchema', () => {
48
51
  template: '',
49
52
  })).toThrow();
50
53
  });
54
+ it('validates phase with outputs', () => {
55
+ const result = PhaseDefinitionSchema.parse({
56
+ name: 'development',
57
+ template: 'development',
58
+ outputs: {
59
+ prUrl: { type: 'url', description: 'Pull request URL', required: true },
60
+ branch: { type: 'string' },
61
+ },
62
+ });
63
+ expect(result.outputs).toEqual({
64
+ prUrl: { type: 'url', description: 'Pull request URL', required: true },
65
+ branch: { type: 'string' },
66
+ });
67
+ });
68
+ it('validates phase with inputs', () => {
69
+ const result = PhaseDefinitionSchema.parse({
70
+ name: 'qa',
71
+ template: 'qa',
72
+ inputs: {
73
+ prUrl: { from: 'development.prUrl', description: 'PR to test' },
74
+ },
75
+ });
76
+ expect(result.inputs).toEqual({
77
+ prUrl: { from: 'development.prUrl', description: 'PR to test' },
78
+ });
79
+ });
80
+ it('validates phase with both outputs and inputs', () => {
81
+ const result = PhaseDefinitionSchema.parse({
82
+ name: 'qa',
83
+ template: 'qa',
84
+ inputs: {
85
+ prUrl: { from: 'development.prUrl' },
86
+ },
87
+ outputs: {
88
+ testsPassed: { type: 'boolean', required: true },
89
+ },
90
+ });
91
+ expect(result.inputs).toBeDefined();
92
+ expect(result.outputs).toBeDefined();
93
+ });
94
+ it('accepts phase without outputs and inputs (backwards compatible)', () => {
95
+ const result = PhaseDefinitionSchema.parse({
96
+ name: 'development',
97
+ template: 'development',
98
+ });
99
+ expect(result.outputs).toBeUndefined();
100
+ expect(result.inputs).toBeUndefined();
101
+ });
102
+ });
103
+ describe('PhaseOutputDeclarationSchema', () => {
104
+ it('validates all output types', () => {
105
+ for (const type of ['string', 'json', 'url', 'boolean']) {
106
+ const result = PhaseOutputDeclarationSchema.parse({ type });
107
+ expect(result.type).toBe(type);
108
+ }
109
+ });
110
+ it('validates with all optional fields', () => {
111
+ const result = PhaseOutputDeclarationSchema.parse({
112
+ type: 'url',
113
+ description: 'The pull request URL',
114
+ required: true,
115
+ });
116
+ expect(result.type).toBe('url');
117
+ expect(result.description).toBe('The pull request URL');
118
+ expect(result.required).toBe(true);
119
+ });
120
+ it('validates minimal declaration', () => {
121
+ const result = PhaseOutputDeclarationSchema.parse({ type: 'string' });
122
+ expect(result.type).toBe('string');
123
+ expect(result.description).toBeUndefined();
124
+ expect(result.required).toBeUndefined();
125
+ });
126
+ it('rejects missing type', () => {
127
+ expect(() => PhaseOutputDeclarationSchema.parse({})).toThrow();
128
+ });
129
+ it('rejects invalid type', () => {
130
+ expect(() => PhaseOutputDeclarationSchema.parse({ type: 'number' })).toThrow();
131
+ });
132
+ it('rejects non-boolean required', () => {
133
+ expect(() => PhaseOutputDeclarationSchema.parse({
134
+ type: 'string',
135
+ required: 'yes',
136
+ })).toThrow();
137
+ });
138
+ });
139
+ describe('PhaseInputDeclarationSchema', () => {
140
+ it('validates a minimal input declaration', () => {
141
+ const result = PhaseInputDeclarationSchema.parse({
142
+ from: 'development.prUrl',
143
+ });
144
+ expect(result.from).toBe('development.prUrl');
145
+ expect(result.description).toBeUndefined();
146
+ });
147
+ it('validates with description', () => {
148
+ const result = PhaseInputDeclarationSchema.parse({
149
+ from: 'development.prUrl',
150
+ description: 'The PR URL from the development phase',
151
+ });
152
+ expect(result.description).toBe('The PR URL from the development phase');
153
+ });
154
+ it('rejects missing from', () => {
155
+ expect(() => PhaseInputDeclarationSchema.parse({})).toThrow();
156
+ });
157
+ it('rejects empty from', () => {
158
+ expect(() => PhaseInputDeclarationSchema.parse({ from: '' })).toThrow();
159
+ });
51
160
  });
52
161
  describe('TransitionDefinitionSchema', () => {
53
162
  it('validates a minimal transition', () => {
@@ -438,3 +547,185 @@ describe('validateWorkflowDefinition', () => {
438
547
  expect(() => validateWorkflowDefinition({ invalid: true })).toThrow();
439
548
  });
440
549
  });
550
+ // ---------------------------------------------------------------------------
551
+ // Cross-validation tests
552
+ // ---------------------------------------------------------------------------
553
+ describe('validateWorkflowDefinition cross-validation', () => {
554
+ /** Helper to build a minimal valid workflow with overrides */
555
+ function makeWorkflow(overrides = {}) {
556
+ return {
557
+ apiVersion: 'v1.1',
558
+ kind: 'WorkflowDefinition',
559
+ metadata: { name: 'test-cross-validation' },
560
+ phases: [
561
+ { name: 'dev', template: 'development' },
562
+ { name: 'qa', template: 'qa' },
563
+ ],
564
+ transitions: [
565
+ { from: 'Backlog', to: 'dev' },
566
+ ],
567
+ ...overrides,
568
+ };
569
+ }
570
+ it('validates example YAML against schema', () => {
571
+ const examplePath = path.join(path.dirname(new URL(import.meta.url).pathname), 'defaults', 'workflow-parallel-example.yaml');
572
+ const content = fs.readFileSync(examplePath, 'utf-8');
573
+ const data = parseYaml(content);
574
+ const result = validateWorkflowDefinition(data, examplePath);
575
+ expect(result.metadata.name).toBe('parallel-development');
576
+ expect(result.parallelism).toHaveLength(1);
577
+ expect(result.parallelism[0].strategy).toBe('fan-in');
578
+ expect(result.phases.find(p => p.name === 'development')?.outputs).toBeDefined();
579
+ expect(result.phases.find(p => p.name === 'qa')?.inputs).toBeDefined();
580
+ });
581
+ it('rejects parallelism group referencing undefined phase', () => {
582
+ expect(() => validateWorkflowDefinition(makeWorkflow({
583
+ parallelism: [{
584
+ name: 'bad-group',
585
+ phases: ['nonexistent'],
586
+ strategy: 'fan-out',
587
+ }],
588
+ }))).toThrow('Parallelism group "bad-group" references undefined phase "nonexistent"');
589
+ });
590
+ it('includes file path in parallelism group error', () => {
591
+ expect(() => validateWorkflowDefinition(makeWorkflow({
592
+ parallelism: [{
593
+ name: 'bad-group',
594
+ phases: ['nonexistent'],
595
+ strategy: 'fan-out',
596
+ }],
597
+ }), '/some/path.yaml')).toThrow('in /some/path.yaml');
598
+ });
599
+ it('rejects phase input with invalid from format', () => {
600
+ expect(() => validateWorkflowDefinition(makeWorkflow({
601
+ phases: [
602
+ { name: 'dev', template: 'development' },
603
+ {
604
+ name: 'qa',
605
+ template: 'qa',
606
+ inputs: { prUrl: { from: 'invalid-no-dot' } },
607
+ },
608
+ ],
609
+ }))).toThrow('expected "phaseName.outputKey" format');
610
+ });
611
+ it('rejects phase input with too many dot segments', () => {
612
+ expect(() => validateWorkflowDefinition(makeWorkflow({
613
+ phases: [
614
+ { name: 'dev', template: 'development' },
615
+ {
616
+ name: 'qa',
617
+ template: 'qa',
618
+ inputs: { prUrl: { from: 'a.b.c' } },
619
+ },
620
+ ],
621
+ }))).toThrow('expected "phaseName.outputKey" format');
622
+ });
623
+ it('rejects phase input referencing undefined phase', () => {
624
+ expect(() => validateWorkflowDefinition(makeWorkflow({
625
+ phases: [
626
+ { name: 'dev', template: 'development' },
627
+ {
628
+ name: 'qa',
629
+ template: 'qa',
630
+ inputs: { prUrl: { from: 'nonexistent.prUrl' } },
631
+ },
632
+ ],
633
+ }))).toThrow('references undefined phase "nonexistent"');
634
+ });
635
+ it('rejects phase input referencing undefined output key', () => {
636
+ expect(() => validateWorkflowDefinition(makeWorkflow({
637
+ phases: [
638
+ {
639
+ name: 'dev',
640
+ template: 'development',
641
+ outputs: {
642
+ branch: { type: 'string' },
643
+ },
644
+ },
645
+ {
646
+ name: 'qa',
647
+ template: 'qa',
648
+ inputs: { prUrl: { from: 'dev.prUrl' } },
649
+ },
650
+ ],
651
+ }))).toThrow('references undefined output "prUrl" on phase "dev"');
652
+ });
653
+ it('allows phase input referencing phase without outputs declared', () => {
654
+ // When the source phase has no outputs declared at all, we allow the
655
+ // reference through — the outputs may be dynamic / not statically declared
656
+ const result = validateWorkflowDefinition(makeWorkflow({
657
+ phases: [
658
+ { name: 'dev', template: 'development' },
659
+ {
660
+ name: 'qa',
661
+ template: 'qa',
662
+ inputs: { prUrl: { from: 'dev.prUrl' } },
663
+ },
664
+ ],
665
+ }));
666
+ expect(result.phases).toHaveLength(2);
667
+ });
668
+ it('warns on high maxConcurrent', () => {
669
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
670
+ try {
671
+ validateWorkflowDefinition(makeWorkflow({
672
+ parallelism: [{
673
+ name: 'big-group',
674
+ phases: ['dev'],
675
+ strategy: 'fan-out',
676
+ maxConcurrent: 20,
677
+ }],
678
+ }));
679
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('maxConcurrent=20 which may be excessive'));
680
+ }
681
+ finally {
682
+ warnSpy.mockRestore();
683
+ }
684
+ });
685
+ it('does not warn when maxConcurrent is at or below threshold', () => {
686
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
687
+ try {
688
+ validateWorkflowDefinition(makeWorkflow({
689
+ parallelism: [{
690
+ name: 'ok-group',
691
+ phases: ['dev'],
692
+ strategy: 'fan-out',
693
+ maxConcurrent: 10,
694
+ }],
695
+ }));
696
+ expect(warnSpy).not.toHaveBeenCalled();
697
+ }
698
+ finally {
699
+ warnSpy.mockRestore();
700
+ }
701
+ });
702
+ it('passes cross-validation for valid workflow with parallelism and inputs/outputs', () => {
703
+ const result = validateWorkflowDefinition(makeWorkflow({
704
+ phases: [
705
+ {
706
+ name: 'dev',
707
+ template: 'development',
708
+ outputs: {
709
+ prUrl: { type: 'url', required: true },
710
+ },
711
+ },
712
+ {
713
+ name: 'qa',
714
+ template: 'qa',
715
+ inputs: {
716
+ prUrls: { from: 'dev.prUrl' },
717
+ },
718
+ },
719
+ ],
720
+ parallelism: [{
721
+ name: 'dev-fan-in',
722
+ phases: ['dev'],
723
+ strategy: 'fan-in',
724
+ maxConcurrent: 5,
725
+ waitForAll: true,
726
+ }],
727
+ }));
728
+ expect(result.parallelism).toHaveLength(1);
729
+ expect(result.phases.find(p => p.name === 'qa')?.inputs).toBeDefined();
730
+ });
731
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@renseiai/agentfactory",
3
- "version": "0.8.8",
3
+ "version": "0.8.10",
4
4
  "type": "module",
5
5
  "description": "Multi-agent fleet management for coding agents — orchestrator, providers, crash recovery",
6
6
  "author": "Rensei AI (https://rensei.ai)",
@@ -55,7 +55,7 @@
55
55
  "@types/node": "^22.5.4",
56
56
  "typescript": "^5.7.3",
57
57
  "vitest": "^3.2.3",
58
- "@renseiai/create-agentfactory-app": "0.8.8"
58
+ "@renseiai/create-agentfactory-app": "0.8.10"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "tsc",