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
|
@@ -496,6 +496,10 @@
|
|
|
496
496
|
"minimum": 1,
|
|
497
497
|
"maximum": 65535,
|
|
498
498
|
"description": "Port the dashboard HTTP server listens on."
|
|
499
|
+
},
|
|
500
|
+
"quick_launch": {
|
|
501
|
+
"type": "boolean",
|
|
502
|
+
"description": "Enables dashboard quick-launch feature creation flow (`POST /api/run`)."
|
|
499
503
|
}
|
|
500
504
|
}
|
|
501
505
|
},
|
|
@@ -42,6 +42,36 @@ type ActivityFrontMatter = FeatureStateFrontMatter & {
|
|
|
42
42
|
|
|
43
43
|
const DEFAULT_IDLE_THRESHOLD_MS = 300_000;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Monitors feature activity and detects stuck agents.
|
|
47
|
+
*
|
|
48
|
+
* ## Responsibilities
|
|
49
|
+
* - Track last activity timestamp per feature
|
|
50
|
+
* - Detect idle/stuck agents based on threshold
|
|
51
|
+
* - Notify when agents are stuck
|
|
52
|
+
* - Persist activity state to feature metadata
|
|
53
|
+
*
|
|
54
|
+
* ## Dependencies
|
|
55
|
+
* - {@link SupervisorToolCaller} - for tool invocation
|
|
56
|
+
* - {@link NotifierService} - for stuck agent notifications
|
|
57
|
+
* - Operation ledger - for activity tracking
|
|
58
|
+
*
|
|
59
|
+
* ## Key Methods
|
|
60
|
+
* - `getActivityState()` - get current activity snapshot
|
|
61
|
+
* - `checkAndNotifyStuck()` - check if stuck and notify
|
|
62
|
+
*
|
|
63
|
+
* ## Error Conditions
|
|
64
|
+
* - Activity persistence failures are logged but don't block
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const monitor = new ActivityMonitorService({ toolCaller, notifier });
|
|
69
|
+
* const state = await monitor.getActivityState('my_feature');
|
|
70
|
+
* if (state.state === 'idle') {
|
|
71
|
+
* await monitor.checkAndNotifyStuck('my_feature');
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
45
75
|
export class ActivityMonitorService {
|
|
46
76
|
private readonly toolCaller: SupervisorToolCaller;
|
|
47
77
|
private readonly operationLedger:
|
|
@@ -152,6 +182,12 @@ export class ActivityMonitorService {
|
|
|
152
182
|
}
|
|
153
183
|
}
|
|
154
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Gets current activity snapshot for a feature.
|
|
187
|
+
*
|
|
188
|
+
* @param featureId - feature identifier
|
|
189
|
+
* @returns Activity snapshot with state and last event timestamp
|
|
190
|
+
*/
|
|
155
191
|
async getActivityState(featureId: string): Promise<ActivitySnapshot> {
|
|
156
192
|
const response = await this.toolCaller.callTool<FeatureStatePayload>(
|
|
157
193
|
'orchestrator',
|
|
@@ -169,6 +205,12 @@ export class ActivityMonitorService {
|
|
|
169
205
|
};
|
|
170
206
|
}
|
|
171
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Checks if agent is stuck and sends notification.
|
|
210
|
+
*
|
|
211
|
+
* @param featureId - feature identifier
|
|
212
|
+
* @returns True if stuck notification was sent
|
|
213
|
+
*/
|
|
172
214
|
async checkAndNotifyStuck(featureId: string): Promise<boolean> {
|
|
173
215
|
const snapshot = await this.getActivityState(featureId);
|
|
174
216
|
await this.persistSnapshot(featureId, snapshot);
|
|
@@ -59,6 +59,37 @@ export interface CollisionQueueServicePort {
|
|
|
59
59
|
): Promise<{ data: { feature_id: string; accepted: boolean; plan_version: number } }>;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Manages blocked feature queue and collision re-drive.
|
|
64
|
+
*
|
|
65
|
+
* ## Responsibilities
|
|
66
|
+
* - Add features to blocked queue when collisions detected
|
|
67
|
+
* - Re-drive queue when locks released or plans updated
|
|
68
|
+
* - Retry plan submission for unblocked features
|
|
69
|
+
* - Remove resolved entries from queue
|
|
70
|
+
*
|
|
71
|
+
* ## Dependencies
|
|
72
|
+
* Depends on {@link CollisionQueueServicePort} for:
|
|
73
|
+
* - Index and state operations
|
|
74
|
+
* - Plan submission
|
|
75
|
+
* - Lock primitives
|
|
76
|
+
*
|
|
77
|
+
* ## Key Methods
|
|
78
|
+
* - `reDriveBlockedQueue()` - retry blocked features
|
|
79
|
+
* - `processSingleQueueEntry()` - process one queue entry
|
|
80
|
+
* - `clearQueueEntry()` - remove entry from queue
|
|
81
|
+
*
|
|
82
|
+
* ## Error Conditions
|
|
83
|
+
* - PLAN_COLLISION - collision still exists
|
|
84
|
+
* - LOCK_NOT_HELD - required locks not available
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const queueService = new CollisionQueueService(port);
|
|
89
|
+
* const result = await queueService.reDriveBlockedQueue();
|
|
90
|
+
* console.log(`Retried: ${result.data.retried}, Still blocked: ${result.data.still_blocked}`);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
62
93
|
export class CollisionQueueService {
|
|
63
94
|
private readonly port: CollisionQueueServicePort;
|
|
64
95
|
|
|
@@ -157,6 +188,17 @@ export class CollisionQueueService {
|
|
|
157
188
|
}
|
|
158
189
|
}
|
|
159
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Re-drives blocked queue to retry unblocked features.
|
|
193
|
+
*
|
|
194
|
+
* @returns Result with processed/retried/still_blocked counts
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const result = await queueService.reDriveBlockedQueue();
|
|
199
|
+
* console.log(`Retried: ${result.data.retried}`);
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
160
202
|
async reDriveBlockedQueue(): Promise<{
|
|
161
203
|
data: { processed: number; retried: number; still_blocked: number };
|
|
162
204
|
}> {
|
|
@@ -219,6 +261,12 @@ export class CollisionQueueService {
|
|
|
219
261
|
});
|
|
220
262
|
}
|
|
221
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Removes entry from blocked queue.
|
|
266
|
+
*
|
|
267
|
+
* @param featureId - feature identifier
|
|
268
|
+
* @returns Success response
|
|
269
|
+
*/
|
|
222
270
|
async clearQueueEntry(featureId: string): Promise<{ data: { removed: boolean } }> {
|
|
223
271
|
return await this.port.withIndexLock(async () => {
|
|
224
272
|
const index = await this.port.readIndex();
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { atomicWriteJson, nowIso, readJson } from '../../core/fs.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Cost tracking and budget enforcement service.
|
|
5
|
+
*/
|
|
6
|
+
|
|
3
7
|
export interface FeatureCost {
|
|
4
8
|
feature_id: string;
|
|
5
9
|
tokens_used: number;
|
|
@@ -25,6 +29,39 @@ export interface CostTrackingServicePort {
|
|
|
25
29
|
getPolicySnapshot(): Record<string, unknown>;
|
|
26
30
|
}
|
|
27
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Manages cost tracking and budget enforcement.
|
|
34
|
+
*
|
|
35
|
+
* ## Responsibilities
|
|
36
|
+
* - Record token usage and API costs per feature
|
|
37
|
+
* - Enforce per_feature_limit_usd budget
|
|
38
|
+
* - Aggregate costs by provider/model
|
|
39
|
+
* - Pause features exceeding budget
|
|
40
|
+
*
|
|
41
|
+
* ## Dependencies
|
|
42
|
+
* Depends on {@link CostTrackingServicePort} for:
|
|
43
|
+
* - Cost file path resolution
|
|
44
|
+
* - Policy budget configuration
|
|
45
|
+
*
|
|
46
|
+
* ## Key Methods
|
|
47
|
+
* - `recordCost()` - record cost entry
|
|
48
|
+
* - `getFeatureCost()` - retrieve accumulated costs
|
|
49
|
+
* - `checkBudget()` - enforce budget threshold
|
|
50
|
+
*
|
|
51
|
+
* ## Error Conditions
|
|
52
|
+
* - BUDGET_EXCEEDED - feature cost exceeds policy limit
|
|
53
|
+
* - FILE_NOT_FOUND - cost file does not exist
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const costService = new CostTrackingService(port);
|
|
58
|
+
* await costService.recordCost('my_feature', { tokens: 1000, cost_usd: 0.02 });
|
|
59
|
+
* const costs = await costService.getFeatureCost('my_feature');
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* ## Cost Format
|
|
63
|
+
* cost.json: { entries: [{ timestamp, tokens, cost_usd, provider, model }], total_usd }
|
|
64
|
+
*/
|
|
28
65
|
export class CostTrackingService {
|
|
29
66
|
private readonly port: CostTrackingServicePort;
|
|
30
67
|
|
|
@@ -32,6 +69,18 @@ export class CostTrackingService {
|
|
|
32
69
|
this.port = port;
|
|
33
70
|
}
|
|
34
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Retrieves accumulated costs for a feature.
|
|
74
|
+
*
|
|
75
|
+
* @param featureId - feature identifier
|
|
76
|
+
* @returns Cost summary with entries and total
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const costs = await costService.getFeatureCost('my_feature');
|
|
81
|
+
* console.log(costs.total_cost_usd);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
35
84
|
async getFeatureCost(featureId: string): Promise<FeatureCost> {
|
|
36
85
|
const existing = await readJson<FeatureCost>(this.port.featureCostPath(featureId), null);
|
|
37
86
|
return (
|
|
@@ -44,6 +93,23 @@ export class CostTrackingService {
|
|
|
44
93
|
);
|
|
45
94
|
}
|
|
46
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Records a cost entry for a feature.
|
|
98
|
+
*
|
|
99
|
+
* @param featureId - feature identifier
|
|
100
|
+
* @param entry - cost entry with tokens, USD amount, provider, model
|
|
101
|
+
* @returns Success response
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* await costService.recordCost('my_feature', {
|
|
106
|
+
* tokens: 1000,
|
|
107
|
+
* cost_usd: 0.02,
|
|
108
|
+
* provider: 'claude',
|
|
109
|
+
* model: 'claude-3-5-sonnet-20241022'
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
47
113
|
async recordCost(
|
|
48
114
|
featureId: string,
|
|
49
115
|
tokensDelta: number,
|
|
@@ -60,6 +126,20 @@ export class CostTrackingService {
|
|
|
60
126
|
return updated;
|
|
61
127
|
}
|
|
62
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Checks if feature is within budget limits.
|
|
131
|
+
*
|
|
132
|
+
* @param featureId - feature identifier
|
|
133
|
+
* @returns Budget check result with over_budget and alert flags
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const check = await costService.checkBudget('my_feature');
|
|
138
|
+
* if (check.over_budget) {
|
|
139
|
+
* console.log('Budget exceeded!');
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
63
143
|
async checkBudget(featureId: string): Promise<BudgetCheckResult> {
|
|
64
144
|
const policy = this.port.getPolicySnapshot();
|
|
65
145
|
const budgetPolicy = (
|
|
@@ -155,6 +155,41 @@ export interface FeatureDeletionServicePort {
|
|
|
155
155
|
locksRelease(resource: string, featureId: string): Promise<unknown>;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Manages feature deletion and cleanup.
|
|
160
|
+
*
|
|
161
|
+
* ## Responsibilities
|
|
162
|
+
* - Delete feature artifacts (state, plan, logs, evidence)
|
|
163
|
+
* - Remove worktree and branch (optional)
|
|
164
|
+
* - Clean up index references
|
|
165
|
+
* - Release held locks
|
|
166
|
+
* - Remove runtime session assignments
|
|
167
|
+
*
|
|
168
|
+
* ## Dependencies
|
|
169
|
+
* Depends on {@link FeatureDeletionServicePort} for:
|
|
170
|
+
* - File operations
|
|
171
|
+
* - Git operations
|
|
172
|
+
* - Index and run-lease updates
|
|
173
|
+
* - Lock release
|
|
174
|
+
*
|
|
175
|
+
* ## Key Methods
|
|
176
|
+
* - `featureDelete()` - delete feature with configurable cleanup
|
|
177
|
+
*
|
|
178
|
+
* ## Error Conditions
|
|
179
|
+
* - FILE_NOT_FOUND - feature does not exist
|
|
180
|
+
* - RUN_ALREADY_ACTIVE - cannot delete during active run
|
|
181
|
+
* - GIT_ERROR - worktree/branch removal failed
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* const deletionService = new FeatureDeletionService(port);
|
|
186
|
+
* await deletionService.featureDelete('my_feature', {
|
|
187
|
+
* dry_run: false,
|
|
188
|
+
* remove_worktree: true,
|
|
189
|
+
* remove_branch: 'safe'
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
158
193
|
export class FeatureDeletionService {
|
|
159
194
|
private readonly port: FeatureDeletionServicePort;
|
|
160
195
|
|
|
@@ -162,6 +197,28 @@ export class FeatureDeletionService {
|
|
|
162
197
|
this.port = port;
|
|
163
198
|
}
|
|
164
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Deletes feature artifacts with configurable cleanup options.
|
|
202
|
+
*
|
|
203
|
+
* @param featureId - feature identifier
|
|
204
|
+
* @param options - deletion options (dry_run, remove_worktree, remove_branch)
|
|
205
|
+
* @returns Deletion result with cleanup summary
|
|
206
|
+
* @throws FILE_NOT_FOUND - feature does not exist
|
|
207
|
+
* @throws RUN_ALREADY_ACTIVE - cannot delete during active run
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* // Preview deletion
|
|
212
|
+
* await deletionService.featureDelete('my_feature', { dry_run: true });
|
|
213
|
+
*
|
|
214
|
+
* // Execute deletion with worktree cleanup
|
|
215
|
+
* await deletionService.featureDelete('my_feature', {
|
|
216
|
+
* dry_run: false,
|
|
217
|
+
* remove_worktree: true,
|
|
218
|
+
* remove_branch: 'safe'
|
|
219
|
+
* });
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
165
222
|
async featureDelete(
|
|
166
223
|
featureId: string | null,
|
|
167
224
|
options: DeleteOptions = {},
|
|
@@ -24,6 +24,10 @@ function normalizeRepoPathForState(repoRoot: string, absolutePath: string): stri
|
|
|
24
24
|
return relative;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* @fileoverview Feature initialization and lifecycle management service.
|
|
29
|
+
*/
|
|
30
|
+
|
|
27
31
|
export interface FeatureLifecycleServicePort {
|
|
28
32
|
getRepoRoot(): string;
|
|
29
33
|
getFeaturesDir(): string;
|
|
@@ -45,6 +49,41 @@ export interface FeatureLifecycleServicePort {
|
|
|
45
49
|
evidenceLatest(featureId: string): Promise<{ data: { latest?: unknown | null } }>;
|
|
46
50
|
}
|
|
47
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Manages feature initialization and lifecycle.
|
|
54
|
+
*
|
|
55
|
+
* ## Responsibilities
|
|
56
|
+
* - Discover canonical feature specs
|
|
57
|
+
* - Initialize feature directories and state.md
|
|
58
|
+
* - Create feature branches and worktrees
|
|
59
|
+
* - Validate feature IDs
|
|
60
|
+
* - Handle spec ingestion (canonical and non-canonical)
|
|
61
|
+
*
|
|
62
|
+
* ## Dependencies
|
|
63
|
+
* Depends on {@link FeatureLifecycleServicePort} for:
|
|
64
|
+
* - File operations and path resolution
|
|
65
|
+
* - Index updates
|
|
66
|
+
* - State/plan/evidence access
|
|
67
|
+
*
|
|
68
|
+
* ## Key Methods
|
|
69
|
+
* - `featureDiscoverSpecs()` - discover canonical spec files
|
|
70
|
+
* - `featureInit()` - initialize feature folder and state
|
|
71
|
+
* - `featureGetContext()` - return spec + state + plan + evidence bundle
|
|
72
|
+
* - `featureLogAppend()` - append timestamped log entry
|
|
73
|
+
*
|
|
74
|
+
* ## Error Conditions
|
|
75
|
+
* - FEATURE_ALREADY_EXISTS - feature already initialized
|
|
76
|
+
* - INVALID_FEATURE_ID - feature ID format invalid
|
|
77
|
+
* - FILE_NOT_FOUND - spec or state file missing
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* const lifecycleService = new FeatureLifecycleService(port);
|
|
82
|
+
* const specs = await lifecycleService.featureDiscoverSpecs();
|
|
83
|
+
* await lifecycleService.featureInit('my_feature');
|
|
84
|
+
* const context = await lifecycleService.featureGetContext('my_feature');
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
48
87
|
export class FeatureLifecycleService {
|
|
49
88
|
private readonly port: FeatureLifecycleServicePort;
|
|
50
89
|
|
|
@@ -52,6 +91,11 @@ export class FeatureLifecycleService {
|
|
|
52
91
|
this.port = port;
|
|
53
92
|
}
|
|
54
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Discovers canonical feature spec files.
|
|
96
|
+
*
|
|
97
|
+
* @returns Array of discovered specs with feature_id and path
|
|
98
|
+
*/
|
|
55
99
|
async featureDiscoverSpecs(): Promise<{
|
|
56
100
|
data: { specs: Array<{ feature_id: string; spec_path: string }> };
|
|
57
101
|
}> {
|
|
@@ -81,6 +125,13 @@ export class FeatureLifecycleService {
|
|
|
81
125
|
return { data: { specs: discovered } };
|
|
82
126
|
}
|
|
83
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Initializes feature folder, state, branch, and worktree.
|
|
130
|
+
*
|
|
131
|
+
* @param featureId - feature identifier
|
|
132
|
+
* @returns Initialization result
|
|
133
|
+
* @throws FEATURE_ALREADY_EXISTS - feature already initialized
|
|
134
|
+
*/
|
|
84
135
|
async featureInit(featureId: string | null): Promise<{
|
|
85
136
|
data: { feature_id: string | null; initialized: boolean; worktree_path: string };
|
|
86
137
|
}> {
|
|
@@ -135,6 +186,12 @@ export class FeatureLifecycleService {
|
|
|
135
186
|
};
|
|
136
187
|
}
|
|
137
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Returns complete feature context bundle.
|
|
191
|
+
*
|
|
192
|
+
* @param featureId - feature identifier
|
|
193
|
+
* @returns Context bundle with spec, state, plan, evidence, QA index
|
|
194
|
+
*/
|
|
138
195
|
async featureGetContext(featureId: string): Promise<{
|
|
139
196
|
data: {
|
|
140
197
|
feature_id: string;
|
|
@@ -163,6 +220,14 @@ export class FeatureLifecycleService {
|
|
|
163
220
|
};
|
|
164
221
|
}
|
|
165
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Appends timestamped entry to feature decision log.
|
|
225
|
+
*
|
|
226
|
+
* @param featureId - feature identifier
|
|
227
|
+
* @param note - log entry content
|
|
228
|
+
* @param context - actor context
|
|
229
|
+
* @returns Success response
|
|
230
|
+
*/
|
|
166
231
|
async featureLogAppend(
|
|
167
232
|
featureId: string,
|
|
168
233
|
note: string,
|
|
@@ -40,12 +40,58 @@ function readVersion(frontMatter: AnyRecord): number {
|
|
|
40
40
|
return 0;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* @fileoverview Feature state persistence and version management service.
|
|
45
|
+
*/
|
|
46
|
+
|
|
43
47
|
export interface FeatureStateServicePort {
|
|
44
48
|
withFeatureLock<T>(featureId: string, operation: () => Promise<T>): Promise<T>;
|
|
45
49
|
readState(featureId: string): Promise<StateReadResult>;
|
|
46
50
|
writeState(featureId: string, frontMatter: AnyRecord, body?: string): Promise<void>;
|
|
47
51
|
}
|
|
48
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Manages feature state persistence with version control.
|
|
55
|
+
*
|
|
56
|
+
* ## Responsibilities
|
|
57
|
+
* - Read/write state.md files (YAML front matter + markdown body)
|
|
58
|
+
* - Parse and validate front matter
|
|
59
|
+
* - Enforce optimistic concurrency via version guards
|
|
60
|
+
* - Atomic state updates with file locking
|
|
61
|
+
*
|
|
62
|
+
* ## Dependencies
|
|
63
|
+
* Depends on {@link FeatureStateServicePort} for:
|
|
64
|
+
* - File operations and state path resolution
|
|
65
|
+
* - Feature-level locking primitives
|
|
66
|
+
*
|
|
67
|
+
* ## Key Methods
|
|
68
|
+
* - `featureStateGet()` - read parsed state
|
|
69
|
+
* - `featureStatePatch()` - update state with version guard
|
|
70
|
+
* - `updateState()` - internal atomic update with custom updater
|
|
71
|
+
*
|
|
72
|
+
* ## Error Conditions
|
|
73
|
+
* - FILE_NOT_FOUND - state file does not exist
|
|
74
|
+
* - VERSION_CONFLICT - concurrent state update detected
|
|
75
|
+
* - INVALID_STATE_TRANSITION - illegal status transition
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const stateService = new FeatureStateService(port);
|
|
80
|
+
* const state = await stateService.featureStateGet('my_feature');
|
|
81
|
+
* await stateService.featureStatePatch('my_feature', { status: 'building' }, 1);
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* ## State Format
|
|
85
|
+
* ```markdown
|
|
86
|
+
* ---
|
|
87
|
+
* feature_id: my_feature
|
|
88
|
+
* version: 5
|
|
89
|
+
* status: building
|
|
90
|
+
* ---
|
|
91
|
+
* # Feature State
|
|
92
|
+
* Body content...
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
49
95
|
export class FeatureStateService {
|
|
50
96
|
private readonly port: FeatureStateServicePort;
|
|
51
97
|
|
|
@@ -53,6 +99,19 @@ export class FeatureStateService {
|
|
|
53
99
|
this.port = port;
|
|
54
100
|
}
|
|
55
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Updates feature state atomically with version guard.
|
|
104
|
+
*
|
|
105
|
+
* Internal method used by public state operations. Acquires feature lock,
|
|
106
|
+
* validates version, applies updater function, and writes merged state.
|
|
107
|
+
*
|
|
108
|
+
* @param featureId - feature identifier
|
|
109
|
+
* @param expectedVersion - expected current version (null = no check)
|
|
110
|
+
* @param updater - function returning state updates
|
|
111
|
+
* @returns Updated state frontmatter
|
|
112
|
+
* @throws VERSION_CONFLICT - version mismatch
|
|
113
|
+
* @throws INVALID_STATE_TRANSITION - illegal status transition
|
|
114
|
+
*/
|
|
56
115
|
async updateState(
|
|
57
116
|
featureId: string,
|
|
58
117
|
expectedVersion: number | null,
|
|
@@ -99,6 +158,19 @@ export class FeatureStateService {
|
|
|
99
158
|
});
|
|
100
159
|
}
|
|
101
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Reads and parses feature state.
|
|
163
|
+
*
|
|
164
|
+
* @param featureId - feature identifier
|
|
165
|
+
* @returns Parsed state with frontmatter and body
|
|
166
|
+
* @throws FILE_NOT_FOUND - state file does not exist
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* const state = await stateService.featureStateGet('my_feature');
|
|
171
|
+
* console.log(state.data.frontMatter.status);
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
102
174
|
async featureStateGet(
|
|
103
175
|
featureId: string,
|
|
104
176
|
): Promise<{ data: { front_matter: AnyRecord; body: string } }> {
|
|
@@ -111,6 +183,21 @@ export class FeatureStateService {
|
|
|
111
183
|
};
|
|
112
184
|
}
|
|
113
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Updates feature state with partial updates and version guard.
|
|
188
|
+
*
|
|
189
|
+
* @param featureId - feature identifier
|
|
190
|
+
* @param updates - partial state updates
|
|
191
|
+
* @param expectedVersion - expected current version
|
|
192
|
+
* @returns Updated state
|
|
193
|
+
* @throws VERSION_CONFLICT - version mismatch
|
|
194
|
+
* @throws INVALID_STATE_TRANSITION - illegal status transition
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* await stateService.featureStatePatch('my_feature', { status: 'building' }, 1);
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
114
201
|
async featureStatePatch(
|
|
115
202
|
featureId: string,
|
|
116
203
|
expectedVersion: number | null,
|
|
@@ -54,6 +54,34 @@ export interface CanonicalizedPlanInput {
|
|
|
54
54
|
findings: GateSelectionFinding[];
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Manages gate profile selection and resolution.
|
|
59
|
+
*
|
|
60
|
+
* ## Responsibilities
|
|
61
|
+
* - Select gate profile based on feature state
|
|
62
|
+
* - Resolve profile/mode configuration
|
|
63
|
+
* - Apply policy defaults and fallbacks
|
|
64
|
+
* - Auto-heal invalid state profiles
|
|
65
|
+
*
|
|
66
|
+
* ## Dependencies
|
|
67
|
+
* Depends on {@link GateSelectionServicePort} for:
|
|
68
|
+
* - Gates configuration
|
|
69
|
+
* - Policy snapshot
|
|
70
|
+
*
|
|
71
|
+
* ## Key Methods
|
|
72
|
+
* - `resolveProfileAndMode()` - resolve profile/mode configuration
|
|
73
|
+
* - `selectGateProfile()` - select profile from state or policy
|
|
74
|
+
* - `isAutoHealInvalidStateProfileEnabled()` - check auto-heal policy
|
|
75
|
+
*
|
|
76
|
+
* ## Error Conditions
|
|
77
|
+
* - INVALID_ARGUMENT - unknown profile or mode
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* const gateSelection = new GateSelectionService(port);
|
|
82
|
+
* const resolved = gateSelection.resolveProfileAndMode('fast', 'fast');
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
57
85
|
export class GateSelectionService {
|
|
58
86
|
private readonly port: GateSelectionServicePort;
|
|
59
87
|
|