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.
- package/agentic/orchestrator/defaults/policy.defaults.yaml +1 -0
- package/agentic/orchestrator/policy.yaml +1 -0
- package/agentic/orchestrator/schemas/policy.schema.json +4 -0
- package/agentic/orchestrator/schemas/policy.user.schema.json +4 -0
- package/apps/control-plane/src/application/services/activity-monitor-service.ts +42 -0
- package/apps/control-plane/src/application/services/collision-queue-service.ts +48 -0
- package/apps/control-plane/src/application/services/cost-tracking-service.ts +80 -0
- package/apps/control-plane/src/application/services/feature-deletion-service.ts +57 -0
- package/apps/control-plane/src/application/services/feature-lifecycle-service.ts +65 -0
- package/apps/control-plane/src/application/services/feature-state-service.ts +87 -0
- package/apps/control-plane/src/application/services/gate-selection-service.ts +28 -0
- package/apps/control-plane/src/application/services/gate-service.ts +148 -0
- package/apps/control-plane/src/application/services/issue-tracker-service.ts +28 -0
- package/apps/control-plane/src/application/services/lock-service.ts +88 -0
- package/apps/control-plane/src/application/services/merge-service.ts +63 -0
- package/apps/control-plane/src/application/services/notifier-service.ts +70 -0
- package/apps/control-plane/src/application/services/patch-service.ts +95 -0
- package/apps/control-plane/src/application/services/performance-analytics-service.ts +90 -0
- package/apps/control-plane/src/application/services/plan-service.ts +184 -0
- package/apps/control-plane/src/application/services/pr-monitor-service.ts +58 -0
- package/apps/control-plane/src/application/services/qa-index-service.ts +68 -0
- package/apps/control-plane/src/application/services/reactions-service.ts +77 -0
- package/apps/control-plane/src/application/services/reporting-service.ts +79 -0
- package/apps/control-plane/src/application/services/run-lease-service.ts +79 -0
- package/apps/control-plane/src/application/tools/tool-router.ts +76 -0
- package/apps/control-plane/src/mcp/tool-runtime.ts +4 -0
- package/apps/control-plane/src/providers/providers.ts +96 -0
- package/apps/control-plane/src/providers/worker-provider-factory.ts +4 -0
- package/apps/control-plane/src/supervisor/build-wave-executor.ts +4 -0
- package/apps/control-plane/src/supervisor/planning-wave-executor.ts +4 -0
- package/apps/control-plane/src/supervisor/qa-wave-executor.ts +4 -0
- package/apps/control-plane/src/supervisor/run-coordinator.ts +4 -0
- package/apps/control-plane/src/supervisor/runtime.ts +4 -0
- package/apps/control-plane/src/supervisor/session-orchestrator.ts +4 -0
- package/apps/control-plane/test/dashboard-api.integration.spec.ts +310 -1
- package/apps/control-plane/test/dashboard-client.spec.ts +136 -1
- package/apps/control-plane/test/dashboard-live-feed.spec.ts +153 -0
- package/apps/control-plane/test/dashboard-ui-utils.spec.ts +132 -0
- package/apps/control-plane/test/mcp.spec.ts +96 -0
- package/apps/control-plane/test/tool-runtime.spec.ts +68 -0
- package/config/agentic/orchestrator/policy.yaml +1 -0
- package/dist/apps/control-plane/application/services/activity-monitor-service.d.ts +42 -0
- package/dist/apps/control-plane/application/services/activity-monitor-service.js +42 -0
- package/dist/apps/control-plane/application/services/activity-monitor-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/collision-queue-service.d.ts +48 -0
- package/dist/apps/control-plane/application/services/collision-queue-service.js +48 -0
- package/dist/apps/control-plane/application/services/collision-queue-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/cost-tracking-service.d.ts +79 -0
- package/dist/apps/control-plane/application/services/cost-tracking-service.js +76 -0
- package/dist/apps/control-plane/application/services/cost-tracking-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/feature-deletion-service.d.ts +57 -0
- package/dist/apps/control-plane/application/services/feature-deletion-service.js +57 -0
- package/dist/apps/control-plane/application/services/feature-deletion-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/feature-lifecycle-service.d.ts +64 -0
- package/dist/apps/control-plane/application/services/feature-lifecycle-service.js +61 -0
- package/dist/apps/control-plane/application/services/feature-lifecycle-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/feature-state-service.d.ts +86 -0
- package/dist/apps/control-plane/application/services/feature-state-service.js +83 -0
- package/dist/apps/control-plane/application/services/feature-state-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/gate-selection-service.d.ts +28 -0
- package/dist/apps/control-plane/application/services/gate-selection-service.js +28 -0
- package/dist/apps/control-plane/application/services/gate-selection-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/gate-service.d.ts +148 -0
- package/dist/apps/control-plane/application/services/gate-service.js +120 -0
- package/dist/apps/control-plane/application/services/gate-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/issue-tracker-service.d.ts +28 -0
- package/dist/apps/control-plane/application/services/issue-tracker-service.js +28 -0
- package/dist/apps/control-plane/application/services/issue-tracker-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/lock-service.d.ts +88 -0
- package/dist/apps/control-plane/application/services/lock-service.js +64 -0
- package/dist/apps/control-plane/application/services/lock-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/merge-service.d.ts +62 -0
- package/dist/apps/control-plane/application/services/merge-service.js +59 -0
- package/dist/apps/control-plane/application/services/merge-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/notifier-service.d.ts +70 -0
- package/dist/apps/control-plane/application/services/notifier-service.js +70 -0
- package/dist/apps/control-plane/application/services/notifier-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/patch-service.d.ts +94 -0
- package/dist/apps/control-plane/application/services/patch-service.js +91 -0
- package/dist/apps/control-plane/application/services/patch-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/performance-analytics-service.d.ts +89 -0
- package/dist/apps/control-plane/application/services/performance-analytics-service.js +86 -0
- package/dist/apps/control-plane/application/services/performance-analytics-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/plan-service.d.ts +287 -0
- package/dist/apps/control-plane/application/services/plan-service.js +207 -0
- package/dist/apps/control-plane/application/services/plan-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/pr-monitor-service.d.ts +58 -0
- package/dist/apps/control-plane/application/services/pr-monitor-service.js +58 -0
- package/dist/apps/control-plane/application/services/pr-monitor-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/qa-index-service.d.ts +67 -0
- package/dist/apps/control-plane/application/services/qa-index-service.js +64 -0
- package/dist/apps/control-plane/application/services/qa-index-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/reactions-service.d.ts +76 -0
- package/dist/apps/control-plane/application/services/reactions-service.js +73 -0
- package/dist/apps/control-plane/application/services/reactions-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/reporting-service.d.ts +78 -0
- package/dist/apps/control-plane/application/services/reporting-service.js +75 -0
- package/dist/apps/control-plane/application/services/reporting-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/run-lease-service.d.ts +79 -0
- package/dist/apps/control-plane/application/services/run-lease-service.js +79 -0
- package/dist/apps/control-plane/application/services/run-lease-service.js.map +1 -1
- package/dist/apps/control-plane/application/tools/tool-router.d.ts +76 -0
- package/dist/apps/control-plane/application/tools/tool-router.js +62 -0
- package/dist/apps/control-plane/application/tools/tool-router.js.map +1 -1
- package/dist/apps/control-plane/mcp/tool-runtime.d.ts +3 -0
- package/dist/apps/control-plane/mcp/tool-runtime.js +3 -0
- package/dist/apps/control-plane/mcp/tool-runtime.js.map +1 -1
- package/dist/apps/control-plane/providers/providers.d.ts +88 -0
- package/dist/apps/control-plane/providers/providers.js.map +1 -1
- package/dist/apps/control-plane/providers/worker-provider-factory.d.ts +3 -0
- package/dist/apps/control-plane/providers/worker-provider-factory.js.map +1 -1
- package/dist/apps/control-plane/supervisor/build-wave-executor.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/build-wave-executor.js +3 -0
- package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/planning-wave-executor.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/planning-wave-executor.js +3 -0
- package/dist/apps/control-plane/supervisor/planning-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/qa-wave-executor.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/qa-wave-executor.js +3 -0
- package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/run-coordinator.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/run-coordinator.js +3 -0
- package/dist/apps/control-plane/supervisor/run-coordinator.js.map +1 -1
- package/dist/apps/control-plane/supervisor/runtime.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/runtime.js +3 -0
- package/dist/apps/control-plane/supervisor/runtime.js.map +1 -1
- package/dist/apps/control-plane/supervisor/session-orchestrator.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/session-orchestrator.js +3 -0
- package/dist/apps/control-plane/supervisor/session-orchestrator.js.map +1 -1
- package/package.json +1 -1
- package/packages/web-dashboard/src/app/analytics/page.tsx +451 -0
- package/packages/web-dashboard/src/app/api/analytics/route.ts +62 -0
- package/packages/web-dashboard/src/app/api/collisions/route.ts +63 -0
- package/packages/web-dashboard/src/app/api/features/[id]/cost/route.ts +29 -0
- package/packages/web-dashboard/src/app/api/features/[id]/review-brief/route.ts +31 -0
- package/packages/web-dashboard/src/app/api/features/[id]/route.ts +5 -8
- package/packages/web-dashboard/src/app/api/features/[id]/test-index/route.ts +31 -0
- package/packages/web-dashboard/src/app/api/flaky/route.ts +20 -0
- package/packages/web-dashboard/src/app/api/policy/budget/route.ts +31 -0
- package/packages/web-dashboard/src/app/api/run/route.ts +162 -0
- package/packages/web-dashboard/src/app/api/status/route.ts +2 -5
- package/packages/web-dashboard/src/app/feature/[id]/page.tsx +112 -0
- package/packages/web-dashboard/src/app/page.tsx +379 -77
- package/packages/web-dashboard/src/components/agent-pipeline-stepper.tsx +100 -0
- package/packages/web-dashboard/src/components/dependency-chains.tsx +58 -0
- package/packages/web-dashboard/src/components/detail-panel.tsx +215 -5
- package/packages/web-dashboard/src/components/feature-card.tsx +5 -0
- package/packages/web-dashboard/src/components/feature-cost-panel.tsx +66 -0
- package/packages/web-dashboard/src/components/feature-list-view.tsx +336 -0
- package/packages/web-dashboard/src/components/gate-step-drilldown.tsx +92 -0
- package/packages/web-dashboard/src/components/kanban-board.tsx +3 -0
- package/packages/web-dashboard/src/components/live-event-feed.tsx +60 -0
- package/packages/web-dashboard/src/components/lock-resource-map.tsx +73 -0
- package/packages/web-dashboard/src/components/plan-revision-timeline.tsx +55 -0
- package/packages/web-dashboard/src/components/plan-risk-annotations.tsx +63 -0
- package/packages/web-dashboard/src/components/plan-scope-tree.tsx +285 -0
- package/packages/web-dashboard/src/components/qa-coverage-map.tsx +174 -0
- package/packages/web-dashboard/src/components/quick-launch-panel.tsx +70 -0
- package/packages/web-dashboard/src/components/review-brief-panel.tsx +75 -0
- package/packages/web-dashboard/src/components/run-health-panel.tsx +85 -0
- package/packages/web-dashboard/src/components/summary-bar.tsx +59 -2
- package/packages/web-dashboard/src/components/verification-signals.tsx +167 -0
- package/packages/web-dashboard/src/lib/analytics-utils.ts +3 -0
- package/packages/web-dashboard/src/lib/aop-client.ts +510 -11
- package/packages/web-dashboard/src/lib/api-envelope.ts +48 -0
- package/packages/web-dashboard/src/lib/authz-adapter.ts +33 -0
- package/packages/web-dashboard/src/lib/dashboard-utils.ts +304 -0
- package/packages/web-dashboard/src/lib/live-feed.ts +218 -0
- package/packages/web-dashboard/src/lib/policy-reader.ts +22 -0
- package/packages/web-dashboard/src/lib/types.ts +114 -0
- package/packages/web-dashboard/src/styles/dashboard.module.css +341 -1
- package/spec-files/{outstanding → completed}/agentic_orchestrator_dashboard_advanced_ux_spec.md +1 -1
- package/spec-files/outstanding/agentic_orchestrator_evidence_integrity_doctor_spec.md +561 -0
- package/spec-files/outstanding/agentic_orchestrator_kernel_simplification_spec.md +834 -0
- package/spec-files/outstanding/agentic_orchestrator_observability_integrity_diagnostics_spec.md +12 -5
- package/spec-files/outstanding/agentic_orchestrator_observability_replay_spec.md +442 -0
- 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;
|