@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
package/README.md CHANGED
@@ -17,7 +17,7 @@ import { createOrchestrator } from '@renseiai/agentfactory'
17
17
 
18
18
  const orchestrator = createOrchestrator({
19
19
  maxConcurrent: 3,
20
- worktreePath: '.worktrees',
20
+ // Default: '../{repoName}.wt/' (sibling directory)
21
21
  })
22
22
 
23
23
  // Process a single issue
@@ -67,7 +67,7 @@ const orchestrator = createOrchestrator({
67
67
  provider: myProvider, // Agent provider instance
68
68
  maxConcurrent: 3, // Max concurrent agents
69
69
  project: 'MyProject', // Filter by project
70
- worktreePath: '.worktrees', // Git worktree base path
70
+ // worktreePath defaults to '../{repoName}.wt/' (sibling directory)
71
71
  inactivityTimeoutMs: 300_000, // 5 min idle timeout
72
72
  maxSessionTimeoutMs: 7_200_000, // 2 hour hard cap
73
73
  workTypeTimeouts: {
@@ -1,3 +1,3 @@
1
- export { loadRepositoryConfig, RepositoryConfigSchema, ProjectConfigSchema, ProvidersConfigSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig } from './repository-config.js';
1
+ export { loadRepositoryConfig, RepositoryConfigSchema, ProjectConfigSchema, ProvidersConfigSchema, RoutingConfigSectionSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig, getRoutingConfig } from './repository-config.js';
2
2
  export type { RepositoryConfig, ProjectConfig } from './repository-config.js';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACpN,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,cAAc,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAClQ,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA"}
@@ -1 +1 @@
1
- export { loadRepositoryConfig, RepositoryConfigSchema, ProjectConfigSchema, ProvidersConfigSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig } from './repository-config.js';
1
+ export { loadRepositoryConfig, RepositoryConfigSchema, ProjectConfigSchema, ProvidersConfigSchema, RoutingConfigSectionSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig, getRoutingConfig } from './repository-config.js';
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { z } from 'zod';
10
10
  import type { ProvidersConfig } from '../providers/index.js';
11
+ import type { RoutingConfig } from '../routing/types.js';
11
12
  /** Per-project configuration (object form of projectPaths values) */
12
13
  export declare const ProjectConfigSchema: z.ZodObject<{
13
14
  path: z.ZodString;
@@ -47,6 +48,15 @@ export declare const ProvidersConfigSchema: z.ZodObject<{
47
48
  a2a: "a2a";
48
49
  }>>>;
49
50
  }, z.core.$strip>;
51
+ /** Routing configuration for MAB-based provider selection */
52
+ export declare const RoutingConfigSectionSchema: z.ZodObject<{
53
+ enabled: z.ZodDefault<z.ZodBoolean>;
54
+ explorationRate: z.ZodDefault<z.ZodNumber>;
55
+ windowSize: z.ZodDefault<z.ZodNumber>;
56
+ discountFactor: z.ZodDefault<z.ZodNumber>;
57
+ minObservationsForExploit: z.ZodDefault<z.ZodNumber>;
58
+ changeDetectionThreshold: z.ZodDefault<z.ZodNumber>;
59
+ }, z.core.$strip>;
50
60
  export declare const RepositoryConfigSchema: z.ZodObject<{
51
61
  apiVersion: z.ZodString;
52
62
  kind: z.ZodLiteral<"RepositoryConfig">;
@@ -100,6 +110,14 @@ export declare const RepositoryConfigSchema: z.ZodObject<{
100
110
  a2a: "a2a";
101
111
  }>>>;
102
112
  }, z.core.$strip>>;
113
+ routing: z.ZodOptional<z.ZodObject<{
114
+ enabled: z.ZodDefault<z.ZodBoolean>;
115
+ explorationRate: z.ZodDefault<z.ZodNumber>;
116
+ windowSize: z.ZodDefault<z.ZodNumber>;
117
+ discountFactor: z.ZodDefault<z.ZodNumber>;
118
+ minObservationsForExploit: z.ZodDefault<z.ZodNumber>;
119
+ changeDetectionThreshold: z.ZodDefault<z.ZodNumber>;
120
+ }, z.core.$strip>>;
103
121
  mergeQueue: z.ZodOptional<z.ZodObject<{
104
122
  provider: z.ZodDefault<z.ZodEnum<{
105
123
  "github-native": "github-native";
@@ -110,6 +128,9 @@ export declare const RepositoryConfigSchema: z.ZodObject<{
110
128
  autoMerge: z.ZodDefault<z.ZodBoolean>;
111
129
  requiredChecks: z.ZodOptional<z.ZodArray<z.ZodString>>;
112
130
  }, z.core.$strip>>;
131
+ worktree: z.ZodOptional<z.ZodObject<{
132
+ directory: z.ZodDefault<z.ZodString>;
133
+ }, z.core.$strip>>;
113
134
  mergeDriver: z.ZodOptional<z.ZodEnum<{
114
135
  default: "default";
115
136
  mergiraf: "mergiraf";
@@ -138,6 +159,11 @@ export declare function getProjectPath(config: RepositoryConfig, projectName: st
138
159
  * Convenience helper for passing to ProviderResolutionContext.configProviders.
139
160
  */
140
161
  export declare function getProvidersConfig(config: RepositoryConfig): ProvidersConfig | undefined;
162
+ /**
163
+ * Returns the routing config from a RepositoryConfig, if present.
164
+ * Convenience helper for passing to async provider resolution.
165
+ */
166
+ export declare function getRoutingConfig(config: RepositoryConfig): RoutingConfig | undefined;
141
167
  /**
142
168
  * Load and validate .agentfactory/config.yaml from the given git root.
143
169
  *
@@ -1 +1 @@
1
- {"version":3,"file":"repository-config.d.ts","sourceRoot":"","sources":["../../../src/config/repository-config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAM5D,qEAAqE;AACrE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAW9B,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAW/D,uCAAuC;AACvC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;iBAOhC,CAAA;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuElC,CAAA;AAMD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAMrE;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,GAAG,SAAS,CAK1F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAepG;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKhG;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,eAAe,GAAG,SAAS,CAExF;AAMD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAQ7E"}
1
+ {"version":3,"file":"repository-config.d.ts","sourceRoot":"","sources":["../../../src/config/repository-config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAMxD,qEAAqE;AACrE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAW9B,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAW/D,uCAAuC;AACvC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;iBAOhC,CAAA;AAEF,6DAA6D;AAC7D,eAAO,MAAM,0BAA0B;;;;;;;iBAarC,CAAA;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyFlC,CAAA;AAMD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAMrE;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,GAAG,SAAS,CAK1F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAepG;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKhG;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,eAAe,GAAG,SAAS,CAExF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,GAAG,SAAS,CAEpF;AAMD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAQ7E"}
@@ -42,6 +42,21 @@ export const ProvidersConfigSchema = z.object({
42
42
  /** Provider overrides by project name (e.g., { Social: 'codex' }) */
43
43
  byProject: z.record(z.string(), AgentProviderNameSchema).optional(),
44
44
  });
45
+ /** Routing configuration for MAB-based provider selection */
46
+ export const RoutingConfigSectionSchema = z.object({
47
+ /** Enable MAB-based intelligent routing (default: false) */
48
+ enabled: z.boolean().default(false),
49
+ /** Exploration rate for Thompson Sampling (0-1, default: 0.1) */
50
+ explorationRate: z.number().min(0).max(1).default(0.1),
51
+ /** Observation window size (default: 100) */
52
+ windowSize: z.number().int().positive().default(100),
53
+ /** Discount factor for older observations (default: 0.99) */
54
+ discountFactor: z.number().min(0).max(1).default(0.99),
55
+ /** Minimum observations before exploiting (default: 5) */
56
+ minObservationsForExploit: z.number().int().min(0).default(5),
57
+ /** Change detection threshold (default: 0.2) */
58
+ changeDetectionThreshold: z.number().min(0).default(0.2),
59
+ });
45
60
  export const RepositoryConfigSchema = z.object({
46
61
  apiVersion: z.string(),
47
62
  kind: z.literal('RepositoryConfig'),
@@ -90,6 +105,11 @@ export const RepositoryConfigSchema = z.object({
90
105
  * Allows routing agents to different providers by work type or project.
91
106
  */
92
107
  providers: ProvidersConfigSchema.optional(),
108
+ /**
109
+ * Routing configuration for MAB-based intelligent provider selection.
110
+ * When enabled, Thompson Sampling is used to learn optimal provider routing.
111
+ */
112
+ routing: RoutingConfigSectionSchema.optional(),
93
113
  /**
94
114
  * Merge queue configuration.
95
115
  * Controls which merge queue provider agents use for automated merging.
@@ -104,6 +124,19 @@ export const RepositoryConfigSchema = z.object({
104
124
  /** Required CI checks that must pass before merge (provider-specific) */
105
125
  requiredChecks: z.array(z.string()).optional(),
106
126
  }).optional(),
127
+ /**
128
+ * Worktree configuration.
129
+ * Controls where git worktrees are created and how paths are resolved.
130
+ */
131
+ worktree: z.object({
132
+ /**
133
+ * Base directory template for git worktrees.
134
+ * Supports template variables: {repoName} (repo directory basename), {branch} (worktree branch name).
135
+ * Default: '../{repoName}.wt' (sibling directory, outside repo to avoid VSCode file watcher crashes).
136
+ * Legacy: '.worktrees' (inside repo, causes VSCode crashes with many worktrees).
137
+ */
138
+ directory: z.string().default('../{repoName}.wt'),
139
+ }).optional(),
107
140
  /**
108
141
  * Git merge driver to use in agent worktrees.
109
142
  * 'mergiraf' enables syntax-aware merging for supported file types.
@@ -165,6 +198,13 @@ export function getProjectPath(config, projectName) {
165
198
  export function getProvidersConfig(config) {
166
199
  return config.providers;
167
200
  }
201
+ /**
202
+ * Returns the routing config from a RepositoryConfig, if present.
203
+ * Convenience helper for passing to async provider resolution.
204
+ */
205
+ export function getRoutingConfig(config) {
206
+ return config.routing;
207
+ }
168
208
  // ---------------------------------------------------------------------------
169
209
  // Loader
170
210
  // ---------------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
2
  import { existsSync, readFileSync } from 'fs';
3
- import { loadRepositoryConfig, RepositoryConfigSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig, ProvidersConfigSchema } from './repository-config.js';
3
+ import { loadRepositoryConfig, RepositoryConfigSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig, getRoutingConfig, ProvidersConfigSchema, RoutingConfigSectionSchema } from './repository-config.js';
4
4
  vi.mock('fs', () => ({
5
5
  existsSync: vi.fn(),
6
6
  readFileSync: vi.fn(),
@@ -548,3 +548,142 @@ describe('getProvidersConfig', () => {
548
548
  expect(getProvidersConfig(config)).toBeUndefined();
549
549
  });
550
550
  });
551
+ describe('RoutingConfigSectionSchema', () => {
552
+ it('validates a complete routing config', () => {
553
+ const result = RoutingConfigSectionSchema.parse({
554
+ enabled: true,
555
+ explorationRate: 0.2,
556
+ windowSize: 200,
557
+ discountFactor: 0.95,
558
+ minObservationsForExploit: 10,
559
+ changeDetectionThreshold: 0.3,
560
+ });
561
+ expect(result.enabled).toBe(true);
562
+ expect(result.explorationRate).toBe(0.2);
563
+ expect(result.windowSize).toBe(200);
564
+ expect(result.discountFactor).toBe(0.95);
565
+ expect(result.minObservationsForExploit).toBe(10);
566
+ expect(result.changeDetectionThreshold).toBe(0.3);
567
+ });
568
+ it('applies defaults when only enabled is provided', () => {
569
+ const result = RoutingConfigSectionSchema.parse({ enabled: true });
570
+ expect(result.enabled).toBe(true);
571
+ expect(result.explorationRate).toBe(0.1);
572
+ expect(result.windowSize).toBe(100);
573
+ expect(result.discountFactor).toBe(0.99);
574
+ expect(result.minObservationsForExploit).toBe(5);
575
+ expect(result.changeDetectionThreshold).toBe(0.2);
576
+ });
577
+ it('applies defaults for an empty object (enabled defaults to false)', () => {
578
+ const result = RoutingConfigSectionSchema.parse({});
579
+ expect(result.enabled).toBe(false);
580
+ expect(result.explorationRate).toBe(0.1);
581
+ });
582
+ it('rejects explorationRate outside 0-1 range', () => {
583
+ expect(() => RoutingConfigSectionSchema.parse({ enabled: true, explorationRate: 1.5 })).toThrow();
584
+ expect(() => RoutingConfigSectionSchema.parse({ enabled: true, explorationRate: -0.1 })).toThrow();
585
+ });
586
+ it('rejects non-positive windowSize', () => {
587
+ expect(() => RoutingConfigSectionSchema.parse({ enabled: true, windowSize: 0 })).toThrow();
588
+ expect(() => RoutingConfigSectionSchema.parse({ enabled: true, windowSize: -1 })).toThrow();
589
+ });
590
+ it('rejects discountFactor outside 0-1 range', () => {
591
+ expect(() => RoutingConfigSectionSchema.parse({ enabled: true, discountFactor: 1.1 })).toThrow();
592
+ });
593
+ it('rejects negative minObservationsForExploit', () => {
594
+ expect(() => RoutingConfigSectionSchema.parse({ enabled: true, minObservationsForExploit: -1 })).toThrow();
595
+ });
596
+ it('rejects negative changeDetectionThreshold', () => {
597
+ expect(() => RoutingConfigSectionSchema.parse({ enabled: true, changeDetectionThreshold: -0.1 })).toThrow();
598
+ });
599
+ });
600
+ describe('RepositoryConfigSchema with routing', () => {
601
+ it('validates config with routing field', () => {
602
+ const result = RepositoryConfigSchema.parse({
603
+ apiVersion: 'v1',
604
+ kind: 'RepositoryConfig',
605
+ routing: {
606
+ enabled: true,
607
+ explorationRate: 0.05,
608
+ },
609
+ });
610
+ expect(result.routing).toBeDefined();
611
+ expect(result.routing.enabled).toBe(true);
612
+ expect(result.routing.explorationRate).toBe(0.05);
613
+ // Defaults should be applied
614
+ expect(result.routing.windowSize).toBe(100);
615
+ });
616
+ it('validates config without routing field', () => {
617
+ const result = RepositoryConfigSchema.parse({
618
+ apiVersion: 'v1',
619
+ kind: 'RepositoryConfig',
620
+ });
621
+ expect(result.routing).toBeUndefined();
622
+ });
623
+ it('validates config with both providers and routing', () => {
624
+ const result = RepositoryConfigSchema.parse({
625
+ apiVersion: 'v1',
626
+ kind: 'RepositoryConfig',
627
+ providers: { default: 'codex' },
628
+ routing: { enabled: true },
629
+ });
630
+ expect(result.providers).toEqual({ default: 'codex' });
631
+ expect(result.routing.enabled).toBe(true);
632
+ });
633
+ });
634
+ describe('loadRepositoryConfig with routing', () => {
635
+ beforeEach(() => {
636
+ vi.clearAllMocks();
637
+ });
638
+ afterEach(() => {
639
+ vi.restoreAllMocks();
640
+ });
641
+ it('parses config with routing section', () => {
642
+ mockExistsSync.mockReturnValue(true);
643
+ mockReadFileSync.mockReturnValue(`apiVersion: v1
644
+ kind: RepositoryConfig
645
+ routing:
646
+ enabled: true
647
+ explorationRate: 0.05
648
+ windowSize: 50
649
+ `);
650
+ const result = loadRepositoryConfig('/some/repo');
651
+ expect(result?.routing).toBeDefined();
652
+ expect(result?.routing?.enabled).toBe(true);
653
+ expect(result?.routing?.explorationRate).toBe(0.05);
654
+ expect(result?.routing?.windowSize).toBe(50);
655
+ // Defaults applied for unspecified fields
656
+ expect(result?.routing?.discountFactor).toBe(0.99);
657
+ expect(result?.routing?.minObservationsForExploit).toBe(5);
658
+ expect(result?.routing?.changeDetectionThreshold).toBe(0.2);
659
+ });
660
+ it('parses config with routing disabled (default)', () => {
661
+ mockExistsSync.mockReturnValue(true);
662
+ mockReadFileSync.mockReturnValue(`apiVersion: v1
663
+ kind: RepositoryConfig
664
+ routing:
665
+ enabled: false
666
+ `);
667
+ const result = loadRepositoryConfig('/some/repo');
668
+ expect(result?.routing?.enabled).toBe(false);
669
+ });
670
+ });
671
+ describe('getRoutingConfig', () => {
672
+ it('returns routing config when present', () => {
673
+ const config = RepositoryConfigSchema.parse({
674
+ apiVersion: 'v1',
675
+ kind: 'RepositoryConfig',
676
+ routing: { enabled: true },
677
+ });
678
+ const routing = getRoutingConfig(config);
679
+ expect(routing).toBeDefined();
680
+ expect(routing.enabled).toBe(true);
681
+ });
682
+ it('returns undefined when routing not set', () => {
683
+ const config = RepositoryConfigSchema.parse({
684
+ apiVersion: 'v1',
685
+ kind: 'RepositoryConfig',
686
+ });
687
+ expect(getRoutingConfig(config)).toBeUndefined();
688
+ });
689
+ });
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import type { GovernorAction, GovernorConfig, GovernorIssue } from './governor-types.js';
11
11
  import type { WorkflowRegistry } from '../workflow/workflow-registry.js';
12
+ import type { GateEvaluationResult } from '../workflow/gates/gate-evaluator.js';
12
13
  /**
13
14
  * All external state the Governor gathers before asking the decision engine
14
15
  * what to do. Callers are responsible for populating this context; the
@@ -32,6 +33,8 @@ export interface DecisionContext {
32
33
  * switch statement. Falls back to the switch statement when absent.
33
34
  */
34
35
  workflowRegistry?: WorkflowRegistry;
36
+ /** Gate evaluation result from the gate system (Phase 4) */
37
+ gateEvaluation?: GateEvaluationResult;
35
38
  }
36
39
  /** Max agent sessions before the circuit breaker trips and the issue is held */
37
40
  export declare const MAX_SESSION_ATTEMPTS = 3;
@@ -1 +1 @@
1
- {"version":3,"file":"decision-engine.d.ts","sourceRoot":"","sources":["../../../src/governor/decision-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAMxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAOxE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAA;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,gBAAgB,EAAE,OAAO,CAAA;IACzB,MAAM,EAAE,OAAO,CAAA;IACf,gBAAgB,EAAE,OAAO,CAAA;IACzB,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,wBAAwB,EAAE,OAAO,CAAA;IACjC,8EAA8E;IAC9E,qBAAqB,EAAE,MAAM,CAAA;IAC7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;CACpC;AAED,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAMrC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;CACf;AAaD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,cAAc,CAsGjE"}
1
+ {"version":3,"file":"decision-engine.d.ts","sourceRoot":"","sources":["../../../src/governor/decision-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAMxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAExE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAA;AAM/E;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAA;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,gBAAgB,EAAE,OAAO,CAAA;IACzB,MAAM,EAAE,OAAO,CAAA;IACf,gBAAgB,EAAE,OAAO,CAAA;IACzB,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,wBAAwB,EAAE,OAAO,CAAA;IACjC,8EAA8E;IAC9E,qBAAqB,EAAE,MAAM,CAAA;IAC7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,4DAA4D;IAC5D,cAAc,CAAC,EAAE,oBAAoB,CAAA;CACtC;AAED,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAMrC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;CACf;AAaD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,cAAc,CAkHjE"}
@@ -47,6 +47,17 @@ export function decideAction(ctx) {
47
47
  if (ctx.isHeld) {
48
48
  return { action: 'none', reason: `Issue ${issue.identifier} is held (HOLD override active)` };
49
49
  }
50
+ // --- Gate check (Phase 4) ---
51
+ // When the governor has evaluated gates for the current phase and any are
52
+ // unsatisfied, block the transition. The governor populates gateEvaluation
53
+ // via the gate evaluator before calling decideAction().
54
+ if (ctx.gateEvaluation && !ctx.gateEvaluation.allSatisfied) {
55
+ const activeNames = ctx.gateEvaluation.activeGates.map(g => g.gateName).join(', ');
56
+ return {
57
+ action: 'none',
58
+ reason: `Issue ${issue.identifier} has unsatisfied gates: ${activeNames}`,
59
+ };
60
+ }
50
61
  // --- Circuit breaker ---
51
62
  // Prevent issues from cycling through agents indefinitely.
52
63
  // If an issue has had too many sessions without reaching a terminal status,
@@ -627,3 +627,36 @@ describe('decideAction — escalation strategy effects', () => {
627
627
  expect(decideAction(ctx).action).toBe('trigger-acceptance');
628
628
  });
629
629
  });
630
+ // ---------------------------------------------------------------------------
631
+ // Gate check
632
+ // ---------------------------------------------------------------------------
633
+ describe('decideAction — gate check', () => {
634
+ it('returns none when gates are unsatisfied', () => {
635
+ const ctx = makeContext({
636
+ gateEvaluation: {
637
+ allSatisfied: false,
638
+ activeGates: [{ issueId: 'issue-1', gateName: 'human-review', gateType: 'signal', status: 'active', activatedAt: Date.now() }],
639
+ newlySatisfied: [],
640
+ timeoutResolutions: [],
641
+ reason: 'Unsatisfied gates: human-review',
642
+ },
643
+ });
644
+ const result = decideAction(ctx);
645
+ expect(result.action).toBe('none');
646
+ expect(result.reason).toContain('unsatisfied gates');
647
+ });
648
+ it('proceeds when all gates are satisfied', () => {
649
+ const ctx = makeContext({
650
+ issue: makeIssue({ status: 'Backlog' }),
651
+ gateEvaluation: {
652
+ allSatisfied: true,
653
+ activeGates: [],
654
+ newlySatisfied: [],
655
+ timeoutResolutions: [],
656
+ reason: 'All gates satisfied',
657
+ },
658
+ });
659
+ const result = decideAction(ctx);
660
+ expect(result.action).toBe('trigger-development');
661
+ });
662
+ });
@@ -20,7 +20,7 @@ import type { TopOfFunnelConfig } from './top-of-funnel.js';
20
20
  * - escalate-human: Create a human escalation touchpoint
21
21
  * - none: No action needed
22
22
  */
23
- export type GovernorAction = 'trigger-research' | 'trigger-backlog-creation' | 'trigger-development' | 'trigger-qa' | 'trigger-acceptance' | 'trigger-refinement' | 'decompose' | 'escalate-human' | 'none';
23
+ export type GovernorAction = 'trigger-research' | 'trigger-backlog-creation' | 'trigger-development' | 'trigger-qa' | 'trigger-acceptance' | 'trigger-refinement' | 'trigger-parallel-group' | 'decompose' | 'escalate-human' | 'none';
24
24
  /**
25
25
  * Configuration for the Workflow Governor scan loop.
26
26
  */
@@ -1 +1 @@
1
- {"version":3,"file":"governor-types.d.ts","sourceRoot":"","sources":["../../../src/governor/governor-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAM3D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,0BAA0B,GAC1B,qBAAqB,GACrB,YAAY,GACZ,oBAAoB,GACpB,oBAAoB,GACpB,WAAW,GACX,gBAAgB,GAChB,MAAM,CAAA;AAMV;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,qDAAqD;IACrD,cAAc,EAAE,MAAM,CAAA;IACtB,0DAA0D;IAC1D,uBAAuB,EAAE,MAAM,CAAA;IAC/B,uDAAuD;IACvD,kBAAkB,EAAE,OAAO,CAAA;IAC3B,+DAA+D;IAC/D,yBAAyB,EAAE,OAAO,CAAA;IAClC,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAA;IAC9B,mDAAmD;IACnD,YAAY,EAAE,OAAO,CAAA;IACrB,4DAA4D;IAC5D,oBAAoB,EAAE,OAAO,CAAA;IAC7B,gEAAgE;IAChE,sBAAsB,EAAE,MAAM,CAAA;IAC9B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;CACzC;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAUrC,CAAA;AAMD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAClD;AAMD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB"}
1
+ {"version":3,"file":"governor-types.d.ts","sourceRoot":"","sources":["../../../src/governor/governor-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAM3D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,0BAA0B,GAC1B,qBAAqB,GACrB,YAAY,GACZ,oBAAoB,GACpB,oBAAoB,GACpB,wBAAwB,GACxB,WAAW,GACX,gBAAgB,GAChB,MAAM,CAAA;AAMV;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,qDAAqD;IACrD,cAAc,EAAE,MAAM,CAAA;IACtB,0DAA0D;IAC1D,uBAAuB,EAAE,MAAM,CAAA;IAC/B,uDAAuD;IACvD,kBAAkB,EAAE,OAAO,CAAA;IAC3B,+DAA+D;IAC/D,yBAAyB,EAAE,OAAO,CAAA;IAClC,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAA;IAC9B,mDAAmD;IACnD,YAAY,EAAE,OAAO,CAAA;IACrB,4DAA4D;IAC5D,oBAAoB,EAAE,OAAO,CAAA;IAC7B,gEAAgE;IAChE,sBAAsB,EAAE,MAAM,CAAA;IAC9B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;CACzC;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAUrC,CAAA;AAMD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAClD;AAMD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB"}
@@ -11,6 +11,7 @@
11
11
  import type { GovernorAction, GovernorConfig, GovernorIssue, ScanResult } from './governor-types.js';
12
12
  import type { OverridePriority } from './override-parser.js';
13
13
  import { type WorkflowRegistryConfig } from '../workflow/workflow-registry.js';
14
+ import { ParallelismExecutor } from '../workflow/parallelism-executor.js';
14
15
  /**
15
16
  * Abstract dependencies that the Governor needs to interact with
16
17
  * external systems. Callers inject these at construction time.
@@ -41,6 +42,8 @@ export interface GovernorDependencies {
41
42
  getCompletedSessionCount: (issueId: string) => Promise<number>;
42
43
  /** Dispatch work for an issue with a specific action */
43
44
  dispatchWork: (issue: GovernorIssue, action: GovernorAction) => Promise<void>;
45
+ /** Get sub-issues for a parent issue (for parallel dispatch) */
46
+ getSubIssues?: (parentId: string) => Promise<GovernorIssue[]>;
44
47
  }
45
48
  /**
46
49
  * The Workflow Governor scans projects on a configurable interval,
@@ -55,12 +58,13 @@ export declare class WorkflowGovernor {
55
58
  private readonly deps;
56
59
  private readonly callbacks;
57
60
  private readonly workflowRegistry;
61
+ private readonly parallelismExecutor;
58
62
  private intervalHandle;
59
63
  private running;
60
64
  private scanning;
61
65
  constructor(config: Partial<GovernorConfig> & {
62
66
  workflow?: WorkflowRegistryConfig;
63
- }, deps: GovernorDependencies, callbacks?: WorkflowGovernorCallbacks);
67
+ }, deps: GovernorDependencies, callbacks?: WorkflowGovernorCallbacks, parallelismExecutor?: ParallelismExecutor);
64
68
  /**
65
69
  * Start the scan loop. Runs `scanOnce()` immediately, then repeats
66
70
  * on the configured interval.
@@ -100,5 +104,17 @@ export declare class WorkflowGovernor {
100
104
  * Gather context for a single issue and run it through the decision engine.
101
105
  */
102
106
  private evaluateIssue;
107
+ /**
108
+ * Handle dispatching work for a parallel group.
109
+ *
110
+ * Fetches sub-issues for the parent, finds the applicable parallelism group,
111
+ * and executes the group using the ParallelismExecutor.
112
+ */
113
+ private handleParallelGroupDispatch;
114
+ /**
115
+ * Handle post-parallel-group completion: if all tasks succeeded,
116
+ * evaluate transitions to advance the parent issue to the next phase.
117
+ */
118
+ private handleParallelGroupCompletion;
103
119
  }
104
120
  //# sourceMappingURL=governor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"governor.d.ts","sourceRoot":"","sources":["../../../src/governor/governor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,UAAU,EACX,MAAM,qBAAqB,CAAA;AAG5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAoB,KAAK,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;AAsChG;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;IACzD,oDAAoD;IACpD,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,kEAAkE;IAClE,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,qDAAqD;IACrD,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACpD,mDAAmD;IACnD,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7C,mEAAmE;IACnE,mBAAmB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;IAC1E,wDAAwD;IACxD,mBAAmB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACrE,kEAAkE;IAClE,mBAAmB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1D,0EAA0E;IAC1E,0BAA0B,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACjE,wEAAwE;IACxE,wBAAwB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9D,wDAAwD;IACxD,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9E;AAMD;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACjE;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2B;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAkB;IACnD,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAQ;gBAGtB,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG;QAAE,QAAQ,CAAC,EAAE,sBAAsB,CAAA;KAAE,EACvE,IAAI,EAAE,oBAAoB,EAC1B,SAAS,CAAC,EAAE,yBAAyB;IAavC;;;OAGG;IACH,KAAK,IAAI,IAAI;IAuBb;;;OAGG;IACH,IAAI,IAAI,IAAI;IAgBZ;;OAEG;IACH,SAAS,IAAI,OAAO;IAQpB;;;;;;;;;;OAUG;IACG,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IA4BvC;;;;;;;OAOG;YACW,WAAW;IA2GzB;;OAEG;YACW,aAAa;CAsC5B"}
1
+ {"version":3,"file":"governor.d.ts","sourceRoot":"","sources":["../../../src/governor/governor.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,9 @@
11
11
  import { DEFAULT_GOVERNOR_CONFIG } from './governor-types.js';
12
12
  import { decideAction } from './decision-engine.js';
13
13
  import { WorkflowRegistry } from '../workflow/workflow-registry.js';
14
+ import { ParallelismExecutor } from '../workflow/parallelism-executor.js';
15
+ import { FanOutStrategy, FanInStrategy, RaceStrategy } from '../workflow/strategies/index.js';
16
+ import { evaluateTransitions } from '../workflow/transition-engine.js';
14
17
  // ---------------------------------------------------------------------------
15
18
  // Logging
16
19
  // ---------------------------------------------------------------------------
@@ -41,15 +44,26 @@ export class WorkflowGovernor {
41
44
  deps;
42
45
  callbacks;
43
46
  workflowRegistry;
47
+ parallelismExecutor;
44
48
  intervalHandle = null;
45
49
  running = false;
46
50
  scanning = false;
47
- constructor(config, deps, callbacks) {
51
+ constructor(config, deps, callbacks, parallelismExecutor) {
48
52
  const { workflow: workflowConfig, ...governorConfig } = config;
49
53
  this.config = { ...DEFAULT_GOVERNOR_CONFIG, ...governorConfig };
50
54
  this.deps = deps;
51
55
  this.callbacks = callbacks ?? {};
52
56
  this.workflowRegistry = WorkflowRegistry.create(workflowConfig);
57
+ // Use the provided executor or create a default one with all strategies registered
58
+ if (parallelismExecutor) {
59
+ this.parallelismExecutor = parallelismExecutor;
60
+ }
61
+ else {
62
+ this.parallelismExecutor = new ParallelismExecutor();
63
+ this.parallelismExecutor.registerStrategy('fan-out', new FanOutStrategy());
64
+ this.parallelismExecutor.registerStrategy('fan-in', new FanInStrategy());
65
+ this.parallelismExecutor.registerStrategy('race', new RaceStrategy());
66
+ }
53
67
  }
54
68
  // -------------------------------------------------------------------------
55
69
  // Lifecycle
@@ -207,6 +221,18 @@ export class WorkflowGovernor {
207
221
  break;
208
222
  }
209
223
  try {
224
+ // Handle parallel group dispatch separately
225
+ if (item.action === 'trigger-parallel-group') {
226
+ await this.handleParallelGroupDispatch(item.issue);
227
+ result.actionsDispatched++;
228
+ log.info('Dispatched parallel group action', {
229
+ issueIdentifier: item.issue.identifier,
230
+ action: item.action,
231
+ reason: item.reason,
232
+ priority: item.priority ?? 'none',
233
+ });
234
+ continue;
235
+ }
210
236
  await this.deps.dispatchWork(item.issue, item.action);
211
237
  result.actionsDispatched++;
212
238
  log.info('Dispatched action', {
@@ -264,4 +290,89 @@ export class WorkflowGovernor {
264
290
  };
265
291
  return decideAction(ctx);
266
292
  }
293
+ /**
294
+ * Handle dispatching work for a parallel group.
295
+ *
296
+ * Fetches sub-issues for the parent, finds the applicable parallelism group,
297
+ * and executes the group using the ParallelismExecutor.
298
+ */
299
+ async handleParallelGroupDispatch(issue) {
300
+ if (!this.deps.getSubIssues) {
301
+ log.warn('getSubIssues dependency not provided, falling back to normal dispatch');
302
+ return;
303
+ }
304
+ const workflow = this.workflowRegistry.getWorkflow();
305
+ if (!workflow?.parallelism)
306
+ return;
307
+ const subIssues = await this.deps.getSubIssues(issue.id);
308
+ // Find the applicable parallelism group.
309
+ // Use the first group whose phases include a phase that can be
310
+ // resolved from the current workflow transitions.
311
+ const group = workflow.parallelism.find(g => g.phases.length > 0);
312
+ if (!group)
313
+ return;
314
+ const tasks = subIssues.map(sub => ({
315
+ id: sub.id,
316
+ issueId: sub.identifier,
317
+ phaseName: group.phases[0],
318
+ }));
319
+ const result = await this.parallelismExecutor.execute(group, tasks, async (task) => {
320
+ await this.deps.dispatchWork(subIssues.find(s => s.id === task.id), `trigger-${task.phaseName}`);
321
+ return {
322
+ id: task.id,
323
+ issueId: task.issueId,
324
+ success: true,
325
+ };
326
+ });
327
+ log.info('Parallel group dispatched', {
328
+ group: group.name,
329
+ strategy: group.strategy,
330
+ completed: result.completed.length,
331
+ failed: result.failed.length,
332
+ cancelled: result.cancelled.length,
333
+ });
334
+ // Propagate result to advance parent issue if group completed successfully
335
+ await this.handleParallelGroupCompletion(issue, result, group);
336
+ }
337
+ /**
338
+ * Handle post-parallel-group completion: if all tasks succeeded,
339
+ * evaluate transitions to advance the parent issue to the next phase.
340
+ */
341
+ async handleParallelGroupCompletion(parentIssue, result, group) {
342
+ // Only advance if the group completed with no failures
343
+ const allSucceeded = result.failed.length === 0 && result.completed.length > 0
344
+ && result.completed.every(t => t.success);
345
+ if (!allSucceeded) {
346
+ log.info('Parallel group has failures, not advancing parent', {
347
+ group: group.name,
348
+ failedCount: result.failed.length,
349
+ });
350
+ return;
351
+ }
352
+ // Evaluate the next transition for the parent issue.
353
+ // After a parallel development group completes, the parent
354
+ // should transition based on its next status mapping.
355
+ const nextPhaseResult = evaluateTransitions({
356
+ issue: { ...parentIssue, status: 'Finished' },
357
+ registry: this.workflowRegistry,
358
+ isParentIssue: true,
359
+ });
360
+ if (nextPhaseResult.action !== 'none') {
361
+ log.info('Advancing parent after parallel group completion', {
362
+ issueIdentifier: parentIssue.identifier,
363
+ nextAction: nextPhaseResult.action,
364
+ reason: nextPhaseResult.reason,
365
+ });
366
+ try {
367
+ await this.deps.dispatchWork(parentIssue, nextPhaseResult.action);
368
+ }
369
+ catch (err) {
370
+ const errorMsg = err instanceof Error ? err.message : String(err);
371
+ log.error('Failed to advance parent after parallel group', {
372
+ issueIdentifier: parentIssue.identifier,
373
+ error: errorMsg,
374
+ });
375
+ }
376
+ }
377
+ }
267
378
  }