agentic-orchestrator 0.1.25 → 0.1.26

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 (177) hide show
  1. package/agentic/orchestrator/defaults/policy.defaults.yaml +1 -0
  2. package/agentic/orchestrator/policy.yaml +1 -0
  3. package/agentic/orchestrator/schemas/policy.schema.json +4 -0
  4. package/agentic/orchestrator/schemas/policy.user.schema.json +4 -0
  5. package/apps/control-plane/src/application/services/activity-monitor-service.ts +42 -0
  6. package/apps/control-plane/src/application/services/collision-queue-service.ts +48 -0
  7. package/apps/control-plane/src/application/services/cost-tracking-service.ts +80 -0
  8. package/apps/control-plane/src/application/services/feature-deletion-service.ts +57 -0
  9. package/apps/control-plane/src/application/services/feature-lifecycle-service.ts +65 -0
  10. package/apps/control-plane/src/application/services/feature-state-service.ts +87 -0
  11. package/apps/control-plane/src/application/services/gate-selection-service.ts +28 -0
  12. package/apps/control-plane/src/application/services/gate-service.ts +148 -0
  13. package/apps/control-plane/src/application/services/issue-tracker-service.ts +28 -0
  14. package/apps/control-plane/src/application/services/lock-service.ts +88 -0
  15. package/apps/control-plane/src/application/services/merge-service.ts +63 -0
  16. package/apps/control-plane/src/application/services/notifier-service.ts +70 -0
  17. package/apps/control-plane/src/application/services/patch-service.ts +95 -0
  18. package/apps/control-plane/src/application/services/performance-analytics-service.ts +90 -0
  19. package/apps/control-plane/src/application/services/plan-service.ts +184 -0
  20. package/apps/control-plane/src/application/services/pr-monitor-service.ts +58 -0
  21. package/apps/control-plane/src/application/services/qa-index-service.ts +68 -0
  22. package/apps/control-plane/src/application/services/reactions-service.ts +77 -0
  23. package/apps/control-plane/src/application/services/reporting-service.ts +79 -0
  24. package/apps/control-plane/src/application/services/run-lease-service.ts +79 -0
  25. package/apps/control-plane/src/application/tools/tool-router.ts +76 -0
  26. package/apps/control-plane/src/mcp/tool-runtime.ts +4 -0
  27. package/apps/control-plane/src/providers/providers.ts +96 -0
  28. package/apps/control-plane/src/providers/worker-provider-factory.ts +4 -0
  29. package/apps/control-plane/src/supervisor/build-wave-executor.ts +4 -0
  30. package/apps/control-plane/src/supervisor/planning-wave-executor.ts +4 -0
  31. package/apps/control-plane/src/supervisor/qa-wave-executor.ts +4 -0
  32. package/apps/control-plane/src/supervisor/run-coordinator.ts +4 -0
  33. package/apps/control-plane/src/supervisor/runtime.ts +4 -0
  34. package/apps/control-plane/src/supervisor/session-orchestrator.ts +4 -0
  35. package/apps/control-plane/test/dashboard-api.integration.spec.ts +310 -1
  36. package/apps/control-plane/test/dashboard-client.spec.ts +136 -1
  37. package/apps/control-plane/test/dashboard-live-feed.spec.ts +153 -0
  38. package/apps/control-plane/test/dashboard-ui-utils.spec.ts +132 -0
  39. package/apps/control-plane/test/mcp.spec.ts +96 -0
  40. package/apps/control-plane/test/tool-runtime.spec.ts +68 -0
  41. package/config/agentic/orchestrator/policy.yaml +1 -0
  42. package/dist/apps/control-plane/application/services/activity-monitor-service.d.ts +42 -0
  43. package/dist/apps/control-plane/application/services/activity-monitor-service.js +42 -0
  44. package/dist/apps/control-plane/application/services/activity-monitor-service.js.map +1 -1
  45. package/dist/apps/control-plane/application/services/collision-queue-service.d.ts +48 -0
  46. package/dist/apps/control-plane/application/services/collision-queue-service.js +48 -0
  47. package/dist/apps/control-plane/application/services/collision-queue-service.js.map +1 -1
  48. package/dist/apps/control-plane/application/services/cost-tracking-service.d.ts +79 -0
  49. package/dist/apps/control-plane/application/services/cost-tracking-service.js +76 -0
  50. package/dist/apps/control-plane/application/services/cost-tracking-service.js.map +1 -1
  51. package/dist/apps/control-plane/application/services/feature-deletion-service.d.ts +57 -0
  52. package/dist/apps/control-plane/application/services/feature-deletion-service.js +57 -0
  53. package/dist/apps/control-plane/application/services/feature-deletion-service.js.map +1 -1
  54. package/dist/apps/control-plane/application/services/feature-lifecycle-service.d.ts +64 -0
  55. package/dist/apps/control-plane/application/services/feature-lifecycle-service.js +61 -0
  56. package/dist/apps/control-plane/application/services/feature-lifecycle-service.js.map +1 -1
  57. package/dist/apps/control-plane/application/services/feature-state-service.d.ts +86 -0
  58. package/dist/apps/control-plane/application/services/feature-state-service.js +83 -0
  59. package/dist/apps/control-plane/application/services/feature-state-service.js.map +1 -1
  60. package/dist/apps/control-plane/application/services/gate-selection-service.d.ts +28 -0
  61. package/dist/apps/control-plane/application/services/gate-selection-service.js +28 -0
  62. package/dist/apps/control-plane/application/services/gate-selection-service.js.map +1 -1
  63. package/dist/apps/control-plane/application/services/gate-service.d.ts +148 -0
  64. package/dist/apps/control-plane/application/services/gate-service.js +120 -0
  65. package/dist/apps/control-plane/application/services/gate-service.js.map +1 -1
  66. package/dist/apps/control-plane/application/services/issue-tracker-service.d.ts +28 -0
  67. package/dist/apps/control-plane/application/services/issue-tracker-service.js +28 -0
  68. package/dist/apps/control-plane/application/services/issue-tracker-service.js.map +1 -1
  69. package/dist/apps/control-plane/application/services/lock-service.d.ts +88 -0
  70. package/dist/apps/control-plane/application/services/lock-service.js +64 -0
  71. package/dist/apps/control-plane/application/services/lock-service.js.map +1 -1
  72. package/dist/apps/control-plane/application/services/merge-service.d.ts +62 -0
  73. package/dist/apps/control-plane/application/services/merge-service.js +59 -0
  74. package/dist/apps/control-plane/application/services/merge-service.js.map +1 -1
  75. package/dist/apps/control-plane/application/services/notifier-service.d.ts +70 -0
  76. package/dist/apps/control-plane/application/services/notifier-service.js +70 -0
  77. package/dist/apps/control-plane/application/services/notifier-service.js.map +1 -1
  78. package/dist/apps/control-plane/application/services/patch-service.d.ts +94 -0
  79. package/dist/apps/control-plane/application/services/patch-service.js +91 -0
  80. package/dist/apps/control-plane/application/services/patch-service.js.map +1 -1
  81. package/dist/apps/control-plane/application/services/performance-analytics-service.d.ts +89 -0
  82. package/dist/apps/control-plane/application/services/performance-analytics-service.js +86 -0
  83. package/dist/apps/control-plane/application/services/performance-analytics-service.js.map +1 -1
  84. package/dist/apps/control-plane/application/services/plan-service.d.ts +287 -0
  85. package/dist/apps/control-plane/application/services/plan-service.js +207 -0
  86. package/dist/apps/control-plane/application/services/plan-service.js.map +1 -1
  87. package/dist/apps/control-plane/application/services/pr-monitor-service.d.ts +58 -0
  88. package/dist/apps/control-plane/application/services/pr-monitor-service.js +58 -0
  89. package/dist/apps/control-plane/application/services/pr-monitor-service.js.map +1 -1
  90. package/dist/apps/control-plane/application/services/qa-index-service.d.ts +67 -0
  91. package/dist/apps/control-plane/application/services/qa-index-service.js +64 -0
  92. package/dist/apps/control-plane/application/services/qa-index-service.js.map +1 -1
  93. package/dist/apps/control-plane/application/services/reactions-service.d.ts +76 -0
  94. package/dist/apps/control-plane/application/services/reactions-service.js +73 -0
  95. package/dist/apps/control-plane/application/services/reactions-service.js.map +1 -1
  96. package/dist/apps/control-plane/application/services/reporting-service.d.ts +78 -0
  97. package/dist/apps/control-plane/application/services/reporting-service.js +75 -0
  98. package/dist/apps/control-plane/application/services/reporting-service.js.map +1 -1
  99. package/dist/apps/control-plane/application/services/run-lease-service.d.ts +79 -0
  100. package/dist/apps/control-plane/application/services/run-lease-service.js +79 -0
  101. package/dist/apps/control-plane/application/services/run-lease-service.js.map +1 -1
  102. package/dist/apps/control-plane/application/tools/tool-router.d.ts +76 -0
  103. package/dist/apps/control-plane/application/tools/tool-router.js +62 -0
  104. package/dist/apps/control-plane/application/tools/tool-router.js.map +1 -1
  105. package/dist/apps/control-plane/mcp/tool-runtime.d.ts +3 -0
  106. package/dist/apps/control-plane/mcp/tool-runtime.js +3 -0
  107. package/dist/apps/control-plane/mcp/tool-runtime.js.map +1 -1
  108. package/dist/apps/control-plane/providers/providers.d.ts +88 -0
  109. package/dist/apps/control-plane/providers/providers.js.map +1 -1
  110. package/dist/apps/control-plane/providers/worker-provider-factory.d.ts +3 -0
  111. package/dist/apps/control-plane/providers/worker-provider-factory.js.map +1 -1
  112. package/dist/apps/control-plane/supervisor/build-wave-executor.d.ts +3 -0
  113. package/dist/apps/control-plane/supervisor/build-wave-executor.js +3 -0
  114. package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -1
  115. package/dist/apps/control-plane/supervisor/planning-wave-executor.d.ts +3 -0
  116. package/dist/apps/control-plane/supervisor/planning-wave-executor.js +3 -0
  117. package/dist/apps/control-plane/supervisor/planning-wave-executor.js.map +1 -1
  118. package/dist/apps/control-plane/supervisor/qa-wave-executor.d.ts +3 -0
  119. package/dist/apps/control-plane/supervisor/qa-wave-executor.js +3 -0
  120. package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -1
  121. package/dist/apps/control-plane/supervisor/run-coordinator.d.ts +3 -0
  122. package/dist/apps/control-plane/supervisor/run-coordinator.js +3 -0
  123. package/dist/apps/control-plane/supervisor/run-coordinator.js.map +1 -1
  124. package/dist/apps/control-plane/supervisor/runtime.d.ts +3 -0
  125. package/dist/apps/control-plane/supervisor/runtime.js +3 -0
  126. package/dist/apps/control-plane/supervisor/runtime.js.map +1 -1
  127. package/dist/apps/control-plane/supervisor/session-orchestrator.d.ts +3 -0
  128. package/dist/apps/control-plane/supervisor/session-orchestrator.js +3 -0
  129. package/dist/apps/control-plane/supervisor/session-orchestrator.js.map +1 -1
  130. package/package.json +1 -1
  131. package/packages/web-dashboard/src/app/analytics/page.tsx +451 -0
  132. package/packages/web-dashboard/src/app/api/analytics/route.ts +62 -0
  133. package/packages/web-dashboard/src/app/api/collisions/route.ts +63 -0
  134. package/packages/web-dashboard/src/app/api/features/[id]/cost/route.ts +29 -0
  135. package/packages/web-dashboard/src/app/api/features/[id]/review-brief/route.ts +31 -0
  136. package/packages/web-dashboard/src/app/api/features/[id]/route.ts +5 -8
  137. package/packages/web-dashboard/src/app/api/features/[id]/test-index/route.ts +31 -0
  138. package/packages/web-dashboard/src/app/api/flaky/route.ts +20 -0
  139. package/packages/web-dashboard/src/app/api/policy/budget/route.ts +31 -0
  140. package/packages/web-dashboard/src/app/api/run/route.ts +162 -0
  141. package/packages/web-dashboard/src/app/api/status/route.ts +2 -5
  142. package/packages/web-dashboard/src/app/feature/[id]/page.tsx +112 -0
  143. package/packages/web-dashboard/src/app/page.tsx +379 -77
  144. package/packages/web-dashboard/src/components/agent-pipeline-stepper.tsx +100 -0
  145. package/packages/web-dashboard/src/components/dependency-chains.tsx +58 -0
  146. package/packages/web-dashboard/src/components/detail-panel.tsx +215 -5
  147. package/packages/web-dashboard/src/components/feature-card.tsx +5 -0
  148. package/packages/web-dashboard/src/components/feature-cost-panel.tsx +66 -0
  149. package/packages/web-dashboard/src/components/feature-list-view.tsx +336 -0
  150. package/packages/web-dashboard/src/components/gate-step-drilldown.tsx +92 -0
  151. package/packages/web-dashboard/src/components/kanban-board.tsx +3 -0
  152. package/packages/web-dashboard/src/components/live-event-feed.tsx +60 -0
  153. package/packages/web-dashboard/src/components/lock-resource-map.tsx +73 -0
  154. package/packages/web-dashboard/src/components/plan-revision-timeline.tsx +55 -0
  155. package/packages/web-dashboard/src/components/plan-risk-annotations.tsx +63 -0
  156. package/packages/web-dashboard/src/components/plan-scope-tree.tsx +285 -0
  157. package/packages/web-dashboard/src/components/qa-coverage-map.tsx +174 -0
  158. package/packages/web-dashboard/src/components/quick-launch-panel.tsx +70 -0
  159. package/packages/web-dashboard/src/components/review-brief-panel.tsx +75 -0
  160. package/packages/web-dashboard/src/components/run-health-panel.tsx +85 -0
  161. package/packages/web-dashboard/src/components/summary-bar.tsx +59 -2
  162. package/packages/web-dashboard/src/components/verification-signals.tsx +167 -0
  163. package/packages/web-dashboard/src/lib/analytics-utils.ts +3 -0
  164. package/packages/web-dashboard/src/lib/aop-client.ts +510 -11
  165. package/packages/web-dashboard/src/lib/api-envelope.ts +48 -0
  166. package/packages/web-dashboard/src/lib/authz-adapter.ts +33 -0
  167. package/packages/web-dashboard/src/lib/dashboard-utils.ts +304 -0
  168. package/packages/web-dashboard/src/lib/live-feed.ts +218 -0
  169. package/packages/web-dashboard/src/lib/policy-reader.ts +22 -0
  170. package/packages/web-dashboard/src/lib/types.ts +114 -0
  171. package/packages/web-dashboard/src/styles/dashboard.module.css +341 -1
  172. package/spec-files/{outstanding → completed}/agentic_orchestrator_dashboard_advanced_ux_spec.md +1 -1
  173. package/spec-files/outstanding/agentic_orchestrator_evidence_integrity_doctor_spec.md +561 -0
  174. package/spec-files/outstanding/agentic_orchestrator_kernel_simplification_spec.md +834 -0
  175. package/spec-files/outstanding/agentic_orchestrator_observability_integrity_diagnostics_spec.md +12 -5
  176. package/spec-files/outstanding/agentic_orchestrator_observability_replay_spec.md +442 -0
  177. package/spec-files/progress.md +174 -1
@@ -1,6 +1,10 @@
1
1
  import { atomicWriteJson, ensureDir, nowIso, readJson } from '../../core/fs.js';
2
2
  import path from 'node:path';
3
3
 
4
+ /**
5
+ * @fileoverview Performance analytics and outcome tracking service.
6
+ */
7
+
4
8
  export interface FeatureOutcome {
5
9
  feature_id: string;
6
10
  provider: string;
@@ -73,6 +77,49 @@ function computeAggregates(outcomes: FeatureOutcome[]): ProviderModelStats[] {
73
77
  return aggregates;
74
78
  }
75
79
 
80
+ /**
81
+ * Manages performance analytics and outcome tracking.
82
+ *
83
+ * ## Responsibilities
84
+ * - Record feature outcomes (status, retries, duration, cost)
85
+ * - Aggregate metrics by provider/model
86
+ * - Calculate success rates and averages
87
+ * - Provide analytics for dashboard
88
+ *
89
+ * ## Dependencies
90
+ * Depends on {@link PerformanceAnalyticsServicePort} for:
91
+ * - Analytics file path resolution
92
+ * - Timestamp utilities
93
+ *
94
+ * ## Key Methods
95
+ * - `recordOutcome()` - record feature outcome
96
+ * - `getAnalytics()` - retrieve aggregated metrics
97
+ * - `getProviderStats()` - get metrics by provider/model
98
+ *
99
+ * ## Error Conditions
100
+ * - FILE_NOT_FOUND - analytics file does not exist
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * const perfService = new PerformanceAnalyticsService(port);
105
+ * await perfService.recordOutcome({
106
+ * feature_id: 'my_feature',
107
+ * status: 'merged',
108
+ * provider: 'claude',
109
+ * model: 'claude-3-5-sonnet-20241022',
110
+ * retries: 2,
111
+ * duration_ms: 300000,
112
+ * cost_usd: 1.50
113
+ * });
114
+ * const analytics = await perfService.getAnalytics();
115
+ * ```
116
+ *
117
+ * ## Metrics Tracked
118
+ * - Success rate by provider/model
119
+ * - Average cost per feature
120
+ * - Average duration per phase
121
+ * - Retry counts and reasons
122
+ */
76
123
  export class PerformanceAnalyticsService {
77
124
  private readonly port: PerformanceAnalyticsServicePort;
78
125
 
@@ -80,6 +127,25 @@ export class PerformanceAnalyticsService {
80
127
  this.port = port;
81
128
  }
82
129
 
130
+ /**
131
+ * Records feature outcome for analytics.
132
+ *
133
+ * @param outcome - outcome data (status, retries, duration, cost, provider, model)
134
+ * @returns Updated analytics snapshot
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * await perfService.recordOutcome({
139
+ * feature_id: 'my_feature',
140
+ * status: 'merged',
141
+ * provider: 'claude',
142
+ * model: 'claude-3-5-sonnet-20241022',
143
+ * retries: 2,
144
+ * duration_ms: 300000,
145
+ * cost_usd: 1.50
146
+ * });
147
+ * ```
148
+ */
83
149
  async recordOutcome(outcome: FeatureOutcome): Promise<AnalyticsSnapshot> {
84
150
  const filePath = this.port.analyticsPath();
85
151
  await ensureDir(path.dirname(filePath));
@@ -93,10 +159,34 @@ export class PerformanceAnalyticsService {
93
159
  return current;
94
160
  }
95
161
 
162
+ /**
163
+ * Retrieves aggregated performance analytics.
164
+ *
165
+ * @returns Analytics snapshot with provider/model metrics
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * const analytics = await perfService.getAnalytics();
170
+ * console.log(analytics.by_provider);
171
+ * ```
172
+ */
96
173
  async getAnalytics(): Promise<AnalyticsSnapshot> {
97
174
  return await this.readSnapshot();
98
175
  }
99
176
 
177
+ /**
178
+ * Retrieves provider/model statistics with optional filtering.
179
+ *
180
+ * @param provider - optional provider filter
181
+ * @param model - optional model filter
182
+ * @returns Array of provider/model stats
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * const stats = await perfService.getProviderStats('claude');
187
+ * console.log(stats[0].success_rate);
188
+ * ```
189
+ */
100
190
  async getProviderStats(provider?: string, model?: string): Promise<ProviderModelStats[]> {
101
191
  const snapshot = await this.readSnapshot();
102
192
  let stats = snapshot.aggregates;
@@ -165,10 +165,38 @@ export interface PlanServicePort {
165
165
  * - State and index operations
166
166
  * - File locking primitives
167
167
  *
168
+ * ## Key Methods
169
+ * - `planSubmit()` - submit initial plan with full validation
170
+ * - `planUpdate()` - submit versioned plan revision
171
+ * - `planGet()` - retrieve accepted plan
172
+ * - `checkPlanCollision()` - detect collisions with other features
173
+ * - `assertPlanLocksHeld()` - verify required locks are held
174
+ *
175
+ * ## Error Conditions
176
+ * - SCHEMA_VALIDATION_FAILED - plan fails JSON schema validation
177
+ * - PLAN_COLLISION - file/area/contract conflicts with another feature
178
+ * - LOCK_NOT_HELD - required resource locks not acquired
179
+ * - VERSION_CONFLICT - plan version mismatch (concurrent update)
180
+ * - DEPENDENCY_CYCLE - circular feature dependencies detected
181
+ *
168
182
  * ## Thread Safety
169
183
  * All mutations are protected by feature-level or index-level locks.
170
184
  * Concurrent reads are safe. Concurrent writes are serialized.
171
185
  *
186
+ * @example
187
+ * ```typescript
188
+ * const planService = new PlanService(port);
189
+ *
190
+ * // Submit initial plan
191
+ * await planService.planSubmit('my_feature', planData, null);
192
+ *
193
+ * // Update plan with version guard
194
+ * await planService.planUpdate('my_feature', updatedPlan, 1);
195
+ *
196
+ * // Check for collisions
197
+ * const result = await planService.checkPlanCollision('my_feature', planData);
198
+ * ```
199
+ *
172
200
  * @see CollisionDetector - for collision detection algorithm
173
201
  * @see GateSelectionService - for gate profile selection
174
202
  */
@@ -181,6 +209,19 @@ export class PlanService {
181
209
  this.gateSelectionService = new GateSelectionService(port);
182
210
  }
183
211
 
212
+ /**
213
+ * Retrieves the accepted plan for a feature.
214
+ *
215
+ * @param featureId - feature identifier
216
+ * @returns Plan data wrapped in success response
217
+ * @throws FILE_NOT_FOUND - plan file does not exist
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * const result = await planService.planGet('my_feature');
222
+ * console.log(result.data.plan);
223
+ * ```
224
+ */
184
225
  async planGet(featureId: string): Promise<{ data: { feature_id: string; plan: unknown } }> {
185
226
  const plan = await readJson(this.port.planPath(featureId), null);
186
227
  return {
@@ -191,6 +232,20 @@ export class PlanService {
191
232
  };
192
233
  }
193
234
 
235
+ /**
236
+ * Collects all accepted plans from discovered features.
237
+ *
238
+ * Used for collision detection and dependency analysis.
239
+ *
240
+ * @param excludeFeatureId - optional feature to exclude from collection
241
+ * @returns Array of plan objects
242
+ *
243
+ * @example
244
+ * ```typescript
245
+ * // Get all plans except current feature
246
+ * const plans = await planService.collectAcceptedPlans('my_feature');
247
+ * ```
248
+ */
194
249
  async collectAcceptedPlans(excludeFeatureId: string | null = null): Promise<AnyRecord[]> {
195
250
  const specs = await this.port.featureDiscoverSpecs();
196
251
  const plans: AnyRecord[] = [];
@@ -206,6 +261,20 @@ export class PlanService {
206
261
  return plans;
207
262
  }
208
263
 
264
+ /**
265
+ * Determines required resource locks based on plan contracts.
266
+ *
267
+ * Maps contract modifications to resource identifiers per policy.
268
+ *
269
+ * @param plan - plan object with contracts section
270
+ * @returns Sorted array of required resource identifiers
271
+ *
272
+ * @example
273
+ * ```typescript
274
+ * const resources = planService.requiredResourcesForPlan(plan);
275
+ * // ['openapi', 'db_migrations']
276
+ * ```
277
+ */
209
278
  requiredResourcesForPlan(plan: AnyRecord): string[] {
210
279
  const resources: string[] = [];
211
280
  const policy = this.port.getPolicySnapshot();
@@ -221,6 +290,18 @@ export class PlanService {
221
290
  return sortResourcesDeterministically(resources);
222
291
  }
223
292
 
293
+ /**
294
+ * Verifies that all required resource locks are held by the feature.
295
+ *
296
+ * @param featureId - feature identifier
297
+ * @param plan - plan object requiring locks
298
+ * @throws LOCK_NOT_HELD - required locks are not held
299
+ *
300
+ * @example
301
+ * ```typescript
302
+ * await planService.assertPlanLocksHeld('my_feature', plan);
303
+ * ```
304
+ */
224
305
  async assertPlanLocksHeld(featureId: string, plan: AnyRecord): Promise<void> {
225
306
  const state = await this.port.readState(featureId);
226
307
  const held = new Set(readHeldLocks(state.frontMatter));
@@ -243,6 +324,20 @@ export class PlanService {
243
324
  }
244
325
  }
245
326
 
327
+ /**
328
+ * Validates plan revision rules for versioning and optimistic concurrency.
329
+ *
330
+ * Enforces:
331
+ * - Initial plans must be version 1 without revision_of
332
+ * - Updates must increment version and reference previous version
333
+ * - Version guard must match current plan version
334
+ *
335
+ * @param existingPlan - current plan or null for initial submission
336
+ * @param plan - new plan to validate
337
+ * @param expectedPlanVersion - expected current version for concurrency guard
338
+ * @throws PLAN_VALIDATION_FAILED - revision rules violated
339
+ * @throws VERSION_CONFLICT - version mismatch (concurrent update)
340
+ */
246
341
  validatePlanRevisionRules(
247
342
  existingPlan: AnyRecord | null,
248
343
  plan: AnyRecord,
@@ -289,6 +384,17 @@ export class PlanService {
289
384
  }
290
385
  }
291
386
 
387
+ /**
388
+ * Validates plan against JSON schema.
389
+ *
390
+ * @param plan - plan object to validate
391
+ * @throws SCHEMA_VALIDATION_FAILED - plan does not conform to schema
392
+ *
393
+ * @example
394
+ * ```typescript
395
+ * await planService.validatePlanSchema(planData);
396
+ * ```
397
+ */
292
398
  async validatePlanSchema(plan: AnyRecord): Promise<void> {
293
399
  const validation = await this.port.validateSchema('plan.schema.json', plan);
294
400
  if (!validation.valid) {
@@ -306,6 +412,24 @@ export class PlanService {
306
412
  }
307
413
  }
308
414
 
415
+ /**
416
+ * Detects file, area, and contract collisions with other features.
417
+ *
418
+ * Compares plan against all accepted plans to find conflicts.
419
+ *
420
+ * @param featureId - feature identifier
421
+ * @param plan - plan to check for collisions
422
+ * @returns Collision matrix with conflicts by feature
423
+ * @throws PLAN_COLLISION - collisions detected (per policy)
424
+ *
425
+ * @example
426
+ * ```typescript
427
+ * const result = await planService.checkPlanCollision('my_feature', plan);
428
+ * if (result.data.has_collisions) {
429
+ * console.log(result.data.collisions);
430
+ * }
431
+ * ```
432
+ */
309
433
  async checkPlanCollision(featureId: string, plan: AnyRecord): Promise<unknown> {
310
434
  const policy = this.port.getPolicySnapshot();
311
435
  const acceptedPlans = await this.collectAcceptedPlans(featureId);
@@ -382,6 +506,17 @@ export class PlanService {
382
506
  };
383
507
  }
384
508
 
509
+ /**
510
+ * Validates feature dependencies and detects circular references.
511
+ *
512
+ * @param featureId - feature to check (null checks all features)
513
+ * @throws DEPENDENCY_CYCLE - circular dependency detected
514
+ *
515
+ * @example
516
+ * ```typescript
517
+ * await planService.checkDependencies('my_feature');
518
+ * ```
519
+ */
385
520
  async checkDependencies(featureId: string | null): Promise<void> {
386
521
  if (!featureId) {
387
522
  return;
@@ -443,6 +578,33 @@ export class PlanService {
443
578
  }
444
579
  }
445
580
 
581
+ /**
582
+ * Submits initial plan with full validation pipeline.
583
+ *
584
+ * Validation steps:
585
+ * 1. Schema validation
586
+ * 2. Revision rules check
587
+ * 3. Lock requirement verification
588
+ * 4. Collision detection
589
+ * 5. Dependency cycle check
590
+ * 6. Gate profile selection
591
+ *
592
+ * @param featureId - feature identifier
593
+ * @param plan - plan object to submit
594
+ * @param expectedVersion - expected state version for concurrency guard
595
+ * @returns Acceptance result with plan version
596
+ * @throws INVALID_ARGUMENT - missing or invalid plan
597
+ * @throws SCHEMA_VALIDATION_FAILED - plan schema invalid
598
+ * @throws LOCK_NOT_HELD - required locks not held
599
+ * @throws PLAN_COLLISION - collisions detected
600
+ * @throws DEPENDENCY_CYCLE - circular dependencies
601
+ *
602
+ * @example
603
+ * ```typescript
604
+ * const result = await planService.planSubmit('my_feature', planData, null);
605
+ * console.log(`Plan accepted: version ${result.data.plan_version}`);
606
+ * ```
607
+ */
446
608
  async planSubmit(
447
609
  featureId: string | null,
448
610
  plan: AnyRecord | null,
@@ -532,6 +694,28 @@ export class PlanService {
532
694
  };
533
695
  }
534
696
 
697
+ /**
698
+ * Submits versioned plan revision with full revalidation.
699
+ *
700
+ * Requires plan_version to be current_version + 1 and revision_of
701
+ * to reference current version. Runs full validation pipeline.
702
+ *
703
+ * @param featureId - feature identifier
704
+ * @param plan - updated plan object
705
+ * @param expectedPlanVersion - expected current plan version
706
+ * @returns Acceptance result with new plan version
707
+ * @throws VERSION_CONFLICT - plan version mismatch
708
+ * @throws PLAN_VALIDATION_FAILED - revision rules violated
709
+ * @throws SCHEMA_VALIDATION_FAILED - plan schema invalid
710
+ * @throws LOCK_NOT_HELD - required locks not held
711
+ * @throws PLAN_COLLISION - collisions detected
712
+ *
713
+ * @example
714
+ * ```typescript
715
+ * // Update plan from version 1 to version 2
716
+ * const result = await planService.planUpdate('my_feature', updatedPlan, 1);
717
+ * ```
718
+ */
535
719
  async planUpdate(
536
720
  featureId: string | null,
537
721
  expectedPlanVersion: number | null,
@@ -102,6 +102,38 @@ function mapReviewDecision(decision: string | null | undefined): ReviewDecision
102
102
  }
103
103
  }
104
104
 
105
+ /**
106
+ * Monitors pull request status and handles review feedback.
107
+ *
108
+ * ## Responsibilities
109
+ * - Detect PRs for feature branches
110
+ * - Monitor PR status and review decisions
111
+ * - Handle changes_requested events
112
+ * - Store PR metadata in feature state
113
+ *
114
+ * ## Dependencies
115
+ * - {@link SupervisorToolCaller} - for tool invocation
116
+ * - {@link NotifierService} - for PR notifications
117
+ * - GitHub CLI - for PR queries
118
+ *
119
+ * ## Key Methods
120
+ * - `detectPr()` - detect PR for feature branch
121
+ * - `checkAndUpdate()` - check PR status and update state
122
+ * - `handleChangesRequested()` - handle review feedback
123
+ * - `storePrState()` - persist PR metadata
124
+ *
125
+ * ## Error Conditions
126
+ * - PR detection failures are logged but don't block
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * const prMonitor = new PrMonitorService({ toolCaller, notifier });
131
+ * const pr = await prMonitor.detectPr('my_feature', 'my_feature');
132
+ * if (pr) {
133
+ * await prMonitor.checkAndUpdate('my_feature', 'my_feature');
134
+ * }
135
+ * ```
136
+ */
105
137
  export class PrMonitorService {
106
138
  private readonly toolCaller: SupervisorToolCaller;
107
139
  private readonly notifier: NotifierService | undefined;
@@ -115,6 +147,13 @@ export class PrMonitorService {
115
147
  this.ghRunner = dependencies.ghRunner ?? createGhRunner();
116
148
  }
117
149
 
150
+ /**
151
+ * Detects PR for feature branch.
152
+ *
153
+ * @param featureId - feature identifier
154
+ * @param branch - branch name
155
+ * @returns PR info or null if not found
156
+ */
118
157
  async detectPr(featureId: string, branch: string): Promise<PrInfo | null> {
119
158
  const candidates = [
120
159
  ...new Set(
@@ -171,6 +210,12 @@ export class PrMonitorService {
171
210
  return { ...partial, merge_score };
172
211
  }
173
212
 
213
+ /**
214
+ * Stores PR metadata in feature state.
215
+ *
216
+ * @param featureId - feature identifier
217
+ * @param prInfo - PR information
218
+ */
174
219
  async storePrState(featureId: string, prInfo: PrInfo): Promise<void> {
175
220
  const current = await this.toolCaller.callTool<FeatureStatePayload>(
176
221
  'orchestrator',
@@ -215,6 +260,12 @@ export class PrMonitorService {
215
260
  return score;
216
261
  }
217
262
 
263
+ /**
264
+ * Handles changes_requested review decision.
265
+ *
266
+ * @param featureId - feature identifier
267
+ * @param prInfo - PR information with review decision
268
+ */
218
269
  async handleChangesRequested(featureId: string, prInfo: PrInfo): Promise<void> {
219
270
  const reaction = this.reactionsPolicy?.changes_requested;
220
271
  if (!reaction?.enabled) {
@@ -264,6 +315,13 @@ export class PrMonitorService {
264
315
  }
265
316
  }
266
317
 
318
+ /**
319
+ * Checks PR status and updates feature state.
320
+ *
321
+ * @param featureId - feature identifier
322
+ * @param branch - branch name
323
+ * @returns Updated PR info or null
324
+ */
267
325
  async checkAndUpdate(featureId: string, branch: string): Promise<PrInfo | null> {
268
326
  const prInfo = await this.detectPr(featureId, branch);
269
327
  if (!prInfo) {
@@ -6,11 +6,48 @@ import { fail } from '../../core/response.js';
6
6
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
7
  type AnyRecord = Record<string, any>;
8
8
 
9
+ /**
10
+ * @fileoverview QA test obligation tracking service.
11
+ */
12
+
9
13
  export interface QaIndexServicePort {
10
14
  qaIndexPath(featureId: string): string;
11
15
  validateSchema(schemaName: string, value: unknown): Promise<{ valid: boolean; errors?: unknown }>;
12
16
  }
13
17
 
18
+ /**
19
+ * Manages QA test obligation tracking.
20
+ *
21
+ * ## Responsibilities
22
+ * - Create qa_test_index.json from diff hunks
23
+ * - Track test status per hunk (pending/covered/skipped)
24
+ * - Link evidence to test obligations
25
+ * - Validate all obligations are resolved
26
+ *
27
+ * ## Dependencies
28
+ * Depends on {@link QaIndexServicePort} for:
29
+ * - QA index file path resolution
30
+ * - Schema validation
31
+ *
32
+ * ## Key Methods
33
+ * - `qaTestIndexGet()` - retrieve test index
34
+ * - `qaTestIndexUpdate()` - update hunk statuses with evidence
35
+ *
36
+ * ## Error Conditions
37
+ * - FILE_NOT_FOUND - index file does not exist
38
+ * - VERSION_CONFLICT - concurrent index update
39
+ * - SCHEMA_VALIDATION_FAILED - invalid index structure
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const qaService = new QaIndexService(port);
44
+ * const index = await qaService.qaTestIndexGet('my_feature');
45
+ * await qaService.qaTestIndexUpdate('my_feature', updates, 1);
46
+ * ```
47
+ *
48
+ * ## Index Format
49
+ * Each hunk has: path, line range, required tests, status, evidence refs
50
+ */
14
51
  export class QaIndexService {
15
52
  private readonly port: QaIndexServicePort;
16
53
 
@@ -18,6 +55,20 @@ export class QaIndexService {
18
55
  this.port = port;
19
56
  }
20
57
 
58
+ /**
59
+ * Retrieves QA test index for a feature.
60
+ *
61
+ * @param featureId - feature identifier
62
+ * @returns Test index with hunk obligations and pending summary
63
+ * @throws FILE_NOT_FOUND - index does not exist
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * const index = await qaService.qaTestIndexGet('my_feature');
68
+ * console.log(index.data.items);
69
+ * console.log(index.data.pending_obligations);
70
+ * ```
71
+ */
21
72
  async qaTestIndexGet(featureId: string): Promise<{
22
73
  data: {
23
74
  index: AnyRecord;
@@ -53,6 +104,23 @@ export class QaIndexService {
53
104
  };
54
105
  }
55
106
 
107
+ /**
108
+ * Updates hunk statuses with evidence references and version guard.
109
+ *
110
+ * @param featureId - feature identifier
111
+ * @param updates - hunk status updates with evidence refs
112
+ * @param expectedVersion - expected index version
113
+ * @returns Updated index
114
+ * @throws VERSION_CONFLICT - version mismatch
115
+ * @throws SCHEMA_VALIDATION_FAILED - invalid updates
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * await qaService.qaTestIndexUpdate('my_feature', {
120
+ * items: [{ path: 'file.ts', status: 'covered', evidence_refs: ['gate_123'] }]
121
+ * }, 1);
122
+ * ```
123
+ */
56
124
  async qaTestIndexUpdate(
57
125
  featureId: string,
58
126
  expectedVersion: number | null,
@@ -5,6 +5,10 @@ import type {
5
5
  } from '../../core/tool-caller.js';
6
6
  import type { NotifierService } from './notifier-service.js';
7
7
 
8
+ /**
9
+ * @fileoverview Event-driven reactions and retry policy service.
10
+ */
11
+
8
12
  export interface GateFailedReaction {
9
13
  enabled: boolean;
10
14
  max_retries: number;
@@ -58,6 +62,47 @@ const DEFAULT_GATE_FAILED_REACTION: GateFailedReaction = {
58
62
  retry_delay_ms: 30000,
59
63
  };
60
64
 
65
+ /**
66
+ * Manages event-driven reactions and retry policies.
67
+ *
68
+ * ## Responsibilities
69
+ * - Execute reactions for events (gate_failed, agent_stuck, collision_detected, etc.)
70
+ * - Enforce retry limits and backoff
71
+ * - Trigger escalation when limits exceeded
72
+ * - Record reaction outcomes
73
+ *
74
+ * ## Dependencies
75
+ * Depends on:
76
+ * - {@link SupervisorToolCaller} - for tool invocation
77
+ * - {@link NotifierService} - for escalation notifications
78
+ * - Policy reactions configuration
79
+ *
80
+ * ## Key Methods
81
+ * - `shouldRetry()` - determine if retry is allowed
82
+ * - `waitBeforeRetry()` - apply retry delay
83
+ * - `recordRetry()` - log retry attempt
84
+ * - `notifyEscalation()` - escalate to human
85
+ *
86
+ * ## Error Conditions
87
+ * - RETRY_LIMIT_EXCEEDED - max retries reached
88
+ * - REACTION_NOT_CONFIGURED - no reaction defined for event
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * const reactionsService = new ReactionsService({ toolCaller, notifier, policy });
93
+ * if (reactionsService.shouldRetry('my_feature', 1)) {
94
+ * await reactionsService.waitBeforeRetry();
95
+ * await reactionsService.recordRetry('my_feature', 2);
96
+ * }
97
+ * ```
98
+ *
99
+ * ## Supported Events
100
+ * - gate_failed: retry with feedback
101
+ * - agent_stuck: restart or escalate
102
+ * - collision_detected: re-drive queue
103
+ * - ready_to_merge: notify reviewers
104
+ * - changes_requested: restart QA phase
105
+ */
61
106
  export class ReactionsService {
62
107
  private readonly toolCaller: SupervisorToolCaller;
63
108
  private readonly notifier: NotifierService | undefined;
@@ -104,6 +149,16 @@ export class ReactionsService {
104
149
  return DEFAULT_GATE_FAILED_REACTION.max_retries;
105
150
  }
106
151
 
152
+ /**
153
+ * Waits for retry delay before next attempt.
154
+ *
155
+ * @returns Promise that resolves after delay
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * await reactionsService.waitBeforeRetry();
160
+ * ```
161
+ */
107
162
  async waitBeforeRetry(): Promise<void> {
108
163
  const delayMs = this.retryDelayMs();
109
164
  if (delayMs <= 0) {
@@ -145,6 +200,17 @@ export class ReactionsService {
145
200
  ].join('\n');
146
201
  }
147
202
 
203
+ /**
204
+ * Records retry attempt in feature state.
205
+ *
206
+ * @param featureId - feature identifier
207
+ * @param retryCount - current retry count
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * await reactionsService.recordRetry('my_feature', 2);
212
+ * ```
213
+ */
148
214
  async recordRetry(featureId: string, retryCount: number): Promise<void> {
149
215
  const current = await this.toolCaller.callTool<FeatureStatePayload>(
150
216
  'orchestrator',
@@ -168,6 +234,17 @@ export class ReactionsService {
168
234
  return retryCount >= reaction.escalate_after;
169
235
  }
170
236
 
237
+ /**
238
+ * Notifies escalation when retry limit exceeded.
239
+ *
240
+ * @param featureId - feature identifier
241
+ * @param ctx - gate repair context with failure details
242
+ *
243
+ * @example
244
+ * ```typescript
245
+ * await reactionsService.notifyEscalation('my_feature', context);
246
+ * ```
247
+ */
171
248
  async notifyEscalation(featureId: string, ctx: GateRepairContext): Promise<void> {
172
249
  if (!this.notifier) {
173
250
  return;