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
|
@@ -54,21 +54,98 @@ function deriveStatusFromGateResult(
|
|
|
54
54
|
return currentStatus;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Port interface for gate operations.
|
|
59
|
+
*
|
|
60
|
+
* ## Contract
|
|
61
|
+
* Implementations must ensure:
|
|
62
|
+
* - Access to gates configuration and policy
|
|
63
|
+
* - State read/update with version guards
|
|
64
|
+
* - Path resolution for worktrees, logs, and evidence
|
|
65
|
+
*
|
|
66
|
+
* ## Error Handling
|
|
67
|
+
* All methods throw normalized errors with stable error codes.
|
|
68
|
+
*/
|
|
57
69
|
export interface GateServicePort {
|
|
70
|
+
/** Returns repository root path. */
|
|
58
71
|
getRepoRoot(): string;
|
|
72
|
+
/** Returns gates configuration. */
|
|
59
73
|
getGatesConfig(): AnyRecord;
|
|
74
|
+
/** Returns policy snapshot. */
|
|
60
75
|
getPolicySnapshot(): AnyRecord;
|
|
76
|
+
/**
|
|
77
|
+
* Reads feature state.
|
|
78
|
+
* @throws FEATURE_NOT_FOUND - if state doesn't exist
|
|
79
|
+
*/
|
|
61
80
|
readState(featureId: string): Promise<{ frontMatter: AnyRecord }>;
|
|
81
|
+
/**
|
|
82
|
+
* Updates feature state with version guard.
|
|
83
|
+
* @param featureId - feature identifier
|
|
84
|
+
* @param expectedVersion - expected version (null = no check)
|
|
85
|
+
* @param updater - function returning updated state
|
|
86
|
+
*/
|
|
62
87
|
updateState(
|
|
63
88
|
featureId: string,
|
|
64
89
|
expectedVersion: number | null,
|
|
65
90
|
updater: StateUpdater,
|
|
66
91
|
): Promise<AnyRecord>;
|
|
92
|
+
/** Returns absolute path to feature worktree. */
|
|
67
93
|
worktreePath(featureId: string): string;
|
|
94
|
+
/** Returns absolute path to feature logs directory. */
|
|
68
95
|
logsPath(featureId: string): string;
|
|
96
|
+
/** Returns absolute path to feature evidence directory. */
|
|
69
97
|
evidencePath(featureId: string): string;
|
|
70
98
|
}
|
|
71
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Manages gate execution, evidence collection, and threshold enforcement.
|
|
102
|
+
*
|
|
103
|
+
* ## Responsibilities
|
|
104
|
+
* - Execute gate profiles (fast/full/merge) with command interpolation
|
|
105
|
+
* - Collect logs and evidence artifacts
|
|
106
|
+
* - Parse coverage reports and enforce thresholds
|
|
107
|
+
* - Update feature state with gate results
|
|
108
|
+
* - Transition status based on gate outcomes
|
|
109
|
+
*
|
|
110
|
+
* ## Dependencies
|
|
111
|
+
* Depends on {@link GateServicePort} for:
|
|
112
|
+
* - Gates configuration and policy access
|
|
113
|
+
* - State updates and file operations
|
|
114
|
+
* - Worktree and evidence path resolution
|
|
115
|
+
*
|
|
116
|
+
* ## Key Methods
|
|
117
|
+
* - `gatesRun()` - execute gate profile with evidence collection
|
|
118
|
+
* - `gatesList()` - list available gate profiles
|
|
119
|
+
* - `evidenceLatest()` - retrieve latest evidence summary
|
|
120
|
+
* - `gateProfileAndMode()` - resolve profile/mode configuration
|
|
121
|
+
*
|
|
122
|
+
* ## Error Conditions
|
|
123
|
+
* - GATE_FAILED - gate execution failed or thresholds not met
|
|
124
|
+
* - INVALID_ARGUMENT - unknown profile or mode
|
|
125
|
+
* - FILE_NOT_FOUND - missing evidence or coverage files
|
|
126
|
+
*
|
|
127
|
+
* ## Gate Lifecycle
|
|
128
|
+
* 1. Select profile and mode based on feature state
|
|
129
|
+
* 2. Interpolate commands with feature context
|
|
130
|
+
* 3. Execute steps (sequential or parallel)
|
|
131
|
+
* 4. Collect evidence and parse coverage
|
|
132
|
+
* 5. Enforce thresholds (minimums and targets)
|
|
133
|
+
* 6. Update state and transition status
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const gateService = new GateService(port);
|
|
138
|
+
*
|
|
139
|
+
* // Execute fast gate profile
|
|
140
|
+
* await gateService.gatesRun('my_feature', 'fast', 'fast', null);
|
|
141
|
+
*
|
|
142
|
+
* // Get latest evidence
|
|
143
|
+
* const evidence = await gateService.evidenceLatest('my_feature');
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* ## Thread Safety
|
|
147
|
+
* State updates are atomic via version guards.
|
|
148
|
+
*/
|
|
72
149
|
export class GateService {
|
|
73
150
|
private readonly port: GateServicePort;
|
|
74
151
|
private readonly gateSelectionService: GateSelectionService;
|
|
@@ -78,6 +155,21 @@ export class GateService {
|
|
|
78
155
|
this.gateSelectionService = new GateSelectionService(port);
|
|
79
156
|
}
|
|
80
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Lists available gate profiles or returns specific profile configuration.
|
|
160
|
+
*
|
|
161
|
+
* @param profileName - optional profile name to retrieve
|
|
162
|
+
* @returns Profile configuration or all profiles
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* // List all profiles
|
|
167
|
+
* const all = gateService.gatesList();
|
|
168
|
+
*
|
|
169
|
+
* // Get specific profile
|
|
170
|
+
* const fast = gateService.gatesList('fast');
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
81
173
|
gatesList(profileName: string | null = null): { data: AnyRecord } {
|
|
82
174
|
const gatesConfig = this.port.getGatesConfig();
|
|
83
175
|
if (profileName) {
|
|
@@ -96,10 +188,50 @@ export class GateService {
|
|
|
96
188
|
};
|
|
97
189
|
}
|
|
98
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Resolves gate profile and mode configuration.
|
|
193
|
+
*
|
|
194
|
+
* @param profileName - profile identifier
|
|
195
|
+
* @param mode - mode identifier
|
|
196
|
+
* @returns Resolved profile/mode configuration
|
|
197
|
+
* @throws INVALID_ARGUMENT - unknown profile or mode
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* const config = gateService.gateProfileAndMode('fast', 'fast');
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
99
204
|
gateProfileAndMode(profileName: string, mode: string): AnyRecord {
|
|
100
205
|
return this.gateSelectionService.resolveProfileAndMode(profileName, mode);
|
|
101
206
|
}
|
|
102
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Executes gate profile with evidence collection and threshold enforcement.
|
|
210
|
+
*
|
|
211
|
+
* Workflow:
|
|
212
|
+
* 1. Resolve profile/mode from state or explicit parameters
|
|
213
|
+
* 2. Interpolate commands with feature context
|
|
214
|
+
* 3. Execute steps (parallel groups or sequential)
|
|
215
|
+
* 4. Collect logs and evidence artifacts
|
|
216
|
+
* 5. Parse coverage and enforce thresholds
|
|
217
|
+
* 6. Update state with results and transition status
|
|
218
|
+
*
|
|
219
|
+
* @param featureId - feature identifier
|
|
220
|
+
* @param profileName - gate profile (null = auto-select from state)
|
|
221
|
+
* @param mode - gate mode (fast/full/merge)
|
|
222
|
+
* @returns Gate execution results with coverage and evidence
|
|
223
|
+
* @throws GATE_FAILED - execution failed or thresholds not met
|
|
224
|
+
* @throws INVALID_ARGUMENT - unknown profile or mode
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* // Auto-select profile from state
|
|
229
|
+
* await gateService.gatesRun('my_feature', null, 'fast');
|
|
230
|
+
*
|
|
231
|
+
* // Explicit profile
|
|
232
|
+
* await gateService.gatesRun('my_feature', 'fast', 'fast');
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
103
235
|
async gatesRun(
|
|
104
236
|
featureId: string,
|
|
105
237
|
profileName: string | null,
|
|
@@ -236,6 +368,22 @@ export class GateService {
|
|
|
236
368
|
};
|
|
237
369
|
}
|
|
238
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Retrieves latest evidence summary for a feature.
|
|
373
|
+
*
|
|
374
|
+
* Returns most recent gate execution evidence including logs,
|
|
375
|
+
* coverage reports, and artifact paths.
|
|
376
|
+
*
|
|
377
|
+
* @param featureId - feature identifier
|
|
378
|
+
* @returns Evidence summary with paths and metadata
|
|
379
|
+
* @throws FILE_NOT_FOUND - no evidence exists
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```typescript
|
|
383
|
+
* const evidence = await gateService.evidenceLatest('my_feature');
|
|
384
|
+
* console.log(evidence.data.coverage);
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
239
387
|
async evidenceLatest(
|
|
240
388
|
featureId: string,
|
|
241
389
|
): Promise<{ data: { feature_id: string; latest: AnyRecord | null; path?: string } }> {
|
|
@@ -83,6 +83,16 @@ function tryParseJson(value: string): Record<string, unknown> | null {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* GitHub issue tracker implementation.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const tracker = new GitHubIssueTracker({ repo: 'owner/repo' });
|
|
92
|
+
* const issue = await tracker.getIssue('123');
|
|
93
|
+
* await tracker.updateIssueStatus('123', 'closed');
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
86
96
|
export class GitHubIssueTracker implements IssueTracker {
|
|
87
97
|
private readonly repo: string | undefined;
|
|
88
98
|
private readonly ghRunner: GhRunner;
|
|
@@ -194,6 +204,15 @@ function mapDefaultJiraTransition(status: string): string {
|
|
|
194
204
|
}
|
|
195
205
|
|
|
196
206
|
// --- Linear adapter (GraphQL HTTP) ---
|
|
207
|
+
/**
|
|
208
|
+
* Linear issue tracker implementation.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* const tracker = new LinearIssueTracker({ api_key: 'lin_...' });
|
|
213
|
+
* const issue = await tracker.getIssue('PROJ-123');
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
197
216
|
export class LinearIssueTracker implements IssueTracker {
|
|
198
217
|
private readonly token: string;
|
|
199
218
|
private readonly baseUrl: string;
|
|
@@ -343,6 +362,15 @@ function toJiraDescription(value: unknown): string {
|
|
|
343
362
|
}
|
|
344
363
|
|
|
345
364
|
// --- Jira adapter (REST HTTP) ---
|
|
365
|
+
/**
|
|
366
|
+
* Jira issue tracker implementation.
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* ```typescript
|
|
370
|
+
* const tracker = new JiraIssueTracker({ base_url: 'https://company.atlassian.net', api_token: '...' });
|
|
371
|
+
* const issue = await tracker.getIssue('PROJ-123');
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
346
374
|
export class JiraIssueTracker implements IssueTracker {
|
|
347
375
|
private readonly baseUrl: string;
|
|
348
376
|
private readonly token: string;
|
|
@@ -18,17 +18,41 @@ interface CollisionQueueDrivePort {
|
|
|
18
18
|
}>;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Port interface for lock operations.
|
|
23
|
+
*
|
|
24
|
+
* ## Contract
|
|
25
|
+
* Implementations must ensure:
|
|
26
|
+
* - Atomic lock acquisition and release via index lock
|
|
27
|
+
* - Lease TTL enforcement with stale lock reclamation
|
|
28
|
+
* - State updates reflect lock ownership changes
|
|
29
|
+
*
|
|
30
|
+
* ## Error Handling
|
|
31
|
+
* All methods throw normalized errors with stable error codes.
|
|
32
|
+
*/
|
|
21
33
|
export interface LockServicePort {
|
|
34
|
+
/** Executes an operation with index-level lock held. */
|
|
22
35
|
withIndexLock<T>(operation: () => Promise<T>): Promise<T>;
|
|
36
|
+
/** Reads the global feature index. */
|
|
23
37
|
readIndex(): Promise<AnyRecord>;
|
|
38
|
+
/** Writes the global feature index atomically. */
|
|
24
39
|
writeIndex(index: AnyRecord): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Updates feature state with version guard.
|
|
42
|
+
* @param featureId - feature identifier
|
|
43
|
+
* @param expectedVersion - expected version (null = no check)
|
|
44
|
+
* @param updater - function returning updated state
|
|
45
|
+
*/
|
|
25
46
|
updateState(
|
|
26
47
|
featureId: string,
|
|
27
48
|
expectedVersion: number | null,
|
|
28
49
|
updater: StateUpdater,
|
|
29
50
|
): Promise<AnyRecord>;
|
|
51
|
+
/** Returns absolute path to feature state file. */
|
|
30
52
|
statePath(featureId: string): string;
|
|
53
|
+
/** Returns current policy snapshot. */
|
|
31
54
|
getPolicySnapshot(): AnyRecord;
|
|
55
|
+
/** Optional collision queue service for re-drive after lock release. */
|
|
32
56
|
collisionQueueService?: CollisionQueueDrivePort;
|
|
33
57
|
}
|
|
34
58
|
|
|
@@ -40,6 +64,42 @@ function sleep(ms: number): Promise<void> {
|
|
|
40
64
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
41
65
|
}
|
|
42
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Manages resource lock acquisition, renewal, and release.
|
|
69
|
+
*
|
|
70
|
+
* ## Responsibilities
|
|
71
|
+
* - Acquire resource locks with wait/backoff behavior
|
|
72
|
+
* - Enforce lease TTL and reclaim stale locks
|
|
73
|
+
* - Update feature state to reflect lock ownership
|
|
74
|
+
* - Trigger collision queue re-drive on lock release
|
|
75
|
+
*
|
|
76
|
+
* ## Dependencies
|
|
77
|
+
* - {@link LockServicePort} - for index/state operations and policy access
|
|
78
|
+
*
|
|
79
|
+
* ## Key Methods
|
|
80
|
+
* - `locksAcquire()` - acquire resource lock with wait/backoff
|
|
81
|
+
* - `locksRelease()` - release resource lock and trigger re-drive
|
|
82
|
+
*
|
|
83
|
+
* ## Error Conditions
|
|
84
|
+
* - INVALID_ARGUMENT - unknown resource or missing featureId
|
|
85
|
+
* - LOCK_CONFLICT - lock held by another feature (fail mode)
|
|
86
|
+
* - LOCK_TIMEOUT - wait timeout exceeded
|
|
87
|
+
*
|
|
88
|
+
* ## Thread Safety
|
|
89
|
+
* All lock operations are atomic via index-level lock.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* // Acquire lock with default wait behavior
|
|
94
|
+
* await lockService.locksAcquire('openapi', 'my_feature', null);
|
|
95
|
+
*
|
|
96
|
+
* // Acquire with custom timeout
|
|
97
|
+
* await lockService.locksAcquire('db_migrations', 'my_feature', 60);
|
|
98
|
+
*
|
|
99
|
+
* // Release lock
|
|
100
|
+
* await lockService.locksRelease('openapi', 'my_feature');
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
43
103
|
export class LockService {
|
|
44
104
|
private readonly port: LockServicePort;
|
|
45
105
|
|
|
@@ -47,6 +107,17 @@ export class LockService {
|
|
|
47
107
|
this.port = port;
|
|
48
108
|
}
|
|
49
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Acquires a resource lock for a feature with optional wait behavior.
|
|
112
|
+
*
|
|
113
|
+
* @param resource - resource identifier (must be in policy.locks.resources)
|
|
114
|
+
* @param featureId - feature requesting the lock
|
|
115
|
+
* @param waitTimeoutSeconds - max wait time (null = use policy default)
|
|
116
|
+
* @returns Success response with lease metadata
|
|
117
|
+
* @throws INVALID_ARGUMENT - unknown resource or missing featureId
|
|
118
|
+
* @throws LOCK_TIMEOUT - wait timeout exceeded
|
|
119
|
+
* @throws LOCK_HELD - lock held by another feature (fail behavior only)
|
|
120
|
+
*/
|
|
50
121
|
async locksAcquire(
|
|
51
122
|
resource: string | null,
|
|
52
123
|
featureId: string | null,
|
|
@@ -176,6 +247,23 @@ export class LockService {
|
|
|
176
247
|
}
|
|
177
248
|
}
|
|
178
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Releases a resource lock held by a feature.
|
|
252
|
+
*
|
|
253
|
+
* Removes lock from index, updates feature state, and triggers
|
|
254
|
+
* collision queue re-drive to unblock waiting features.
|
|
255
|
+
*
|
|
256
|
+
* @param resource - resource identifier
|
|
257
|
+
* @param featureId - feature releasing the lock
|
|
258
|
+
* @returns Success response
|
|
259
|
+
* @throws INVALID_ARGUMENT - missing resource or featureId
|
|
260
|
+
* @throws LOCK_CONFLICT - lock held by another feature
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```typescript
|
|
264
|
+
* await lockService.locksRelease('openapi', 'my_feature');
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
179
267
|
async locksRelease(resource: string | null, featureId: string | null) {
|
|
180
268
|
if (!resource || !featureId) {
|
|
181
269
|
throw {
|
|
@@ -33,6 +33,10 @@ function asArray<T = unknown>(value: unknown): T[] {
|
|
|
33
33
|
return Array.isArray(value) ? (value as T[]) : [];
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @fileoverview Merge promotion and branch management service.
|
|
38
|
+
*/
|
|
39
|
+
|
|
36
40
|
export interface MergeServicePort {
|
|
37
41
|
getRepoRoot(): string;
|
|
38
42
|
getPolicySnapshot(): AnyRecord;
|
|
@@ -50,6 +54,41 @@ export interface MergeServicePort {
|
|
|
50
54
|
writeIndex(index: AnyRecord): Promise<void>;
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Manages merge promotion and branch lifecycle.
|
|
59
|
+
*
|
|
60
|
+
* ## Responsibilities
|
|
61
|
+
* - Validate merge readiness (gates passed, approval obtained)
|
|
62
|
+
* - Commit worktree changes
|
|
63
|
+
* - Merge feature branch to base
|
|
64
|
+
* - Clean up worktree and branch
|
|
65
|
+
* - Update index to reflect merge
|
|
66
|
+
*
|
|
67
|
+
* ## Dependencies
|
|
68
|
+
* Depends on {@link MergeServicePort} for:
|
|
69
|
+
* - State and index operations
|
|
70
|
+
* - Policy and worktree access
|
|
71
|
+
* - Lock release coordination
|
|
72
|
+
*
|
|
73
|
+
* ## Key Methods
|
|
74
|
+
* - `featureReadyToMerge()` - validate, commit, merge, and cleanup
|
|
75
|
+
*
|
|
76
|
+
* ## Error Conditions
|
|
77
|
+
* - MERGE_REQUIREMENTS_NOT_MET - gates not passed or approval missing
|
|
78
|
+
* - MERGE_CONFLICT - git merge failed
|
|
79
|
+
* - UNCOMMITTED_CHANGES - worktree has uncommitted changes
|
|
80
|
+
*
|
|
81
|
+
* ## Merge Requirements
|
|
82
|
+
* - All required gates must pass
|
|
83
|
+
* - Approval token must be valid (if required by policy)
|
|
84
|
+
* - No uncommitted changes in worktree
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const mergeService = new MergeService(port);
|
|
89
|
+
* await mergeService.featureReadyToMerge('my_feature', 'approve_abc123');
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
53
92
|
export class MergeService {
|
|
54
93
|
private readonly port: MergeServicePort;
|
|
55
94
|
|
|
@@ -99,6 +138,30 @@ export class MergeService {
|
|
|
99
138
|
}
|
|
100
139
|
}
|
|
101
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Validates merge readiness, commits changes, merges to base, and cleans up.
|
|
143
|
+
*
|
|
144
|
+
* Full workflow:
|
|
145
|
+
* 1. Validate required gates passed
|
|
146
|
+
* 2. Verify approval token (if policy requires)
|
|
147
|
+
* 3. Commit worktree changes
|
|
148
|
+
* 4. Merge feature branch to base branch
|
|
149
|
+
* 5. Release held locks
|
|
150
|
+
* 6. Update index (move to merged, remove from active)
|
|
151
|
+
* 7. Schedule cleanup (worktree/branch removal per policy)
|
|
152
|
+
*
|
|
153
|
+
* @param featureId - feature identifier
|
|
154
|
+
* @param approvalToken - approval token (required if policy enforces)
|
|
155
|
+
* @returns Merge result with commit SHA and cleanup status
|
|
156
|
+
* @throws MERGE_REQUIREMENTS_NOT_MET - gates not passed or approval invalid
|
|
157
|
+
* @throws MERGE_CONFLICT - git merge failed
|
|
158
|
+
* @throws UNCOMMITTED_CHANGES - worktree has uncommitted changes
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* await mergeService.featureReadyToMerge('my_feature', 'approve_abc123');
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
102
165
|
async featureReadyToMerge(
|
|
103
166
|
featureId: string,
|
|
104
167
|
commitMessage: string,
|
|
@@ -72,6 +72,15 @@ const EVENT_PRIORITY: Record<NotificationEvent, CanonicalPriority> = {
|
|
|
72
72
|
|
|
73
73
|
const THROTTLE_WINDOW_MS = 5 * 60 * 1000; // 5 minutes
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Desktop notification channel using notify-send.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const channel = new DesktopNotifierChannel();
|
|
81
|
+
* await channel.send(payload);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
75
84
|
export class DesktopNotifierChannel implements NotifierChannel {
|
|
76
85
|
readonly name = 'desktop';
|
|
77
86
|
|
|
@@ -96,6 +105,15 @@ export class DesktopNotifierChannel implements NotifierChannel {
|
|
|
96
105
|
}
|
|
97
106
|
}
|
|
98
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Slack notification channel using webhook.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const channel = new SlackNotifierChannel(webhookUrl, '#alerts');
|
|
114
|
+
* await channel.send(payload);
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
99
117
|
export class SlackNotifierChannel implements NotifierChannel {
|
|
100
118
|
readonly name = 'slack';
|
|
101
119
|
private readonly webhookUrl: string;
|
|
@@ -139,6 +157,15 @@ export class SlackNotifierChannel implements NotifierChannel {
|
|
|
139
157
|
}
|
|
140
158
|
}
|
|
141
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Generic webhook notification channel.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* const channel = new WebhookNotifierChannel('https://example.com/webhook');
|
|
166
|
+
* await channel.send(payload);
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
142
169
|
export class WebhookNotifierChannel implements NotifierChannel {
|
|
143
170
|
readonly name = 'webhook';
|
|
144
171
|
private readonly url: string;
|
|
@@ -159,6 +186,36 @@ export class WebhookNotifierChannel implements NotifierChannel {
|
|
|
159
186
|
}
|
|
160
187
|
}
|
|
161
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Manages multi-channel notifications with priority routing.
|
|
191
|
+
*
|
|
192
|
+
* ## Responsibilities
|
|
193
|
+
* - Route notifications by priority (critical/warning/info)
|
|
194
|
+
* - Send to multiple channels (desktop/Slack/webhook)
|
|
195
|
+
* - Throttle duplicate notifications
|
|
196
|
+
* - Map events to priorities
|
|
197
|
+
*
|
|
198
|
+
* ## Dependencies
|
|
199
|
+
* - Notification channel implementations
|
|
200
|
+
* - Policy notification configuration
|
|
201
|
+
*
|
|
202
|
+
* ## Key Methods
|
|
203
|
+
* - `notify()` - send notification with priority routing
|
|
204
|
+
*
|
|
205
|
+
* ## Error Conditions
|
|
206
|
+
* - Channel send failures are logged but don't block
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```typescript
|
|
210
|
+
* const notifier = new NotifierService(config, dashboardUrl);
|
|
211
|
+
* await notifier.notify('gate_failed', 'my_feature', 'Gate fast failed', { gate: 'fast' });
|
|
212
|
+
* ```
|
|
213
|
+
*
|
|
214
|
+
* ## Supported Channels
|
|
215
|
+
* - desktop: notify-send
|
|
216
|
+
* - slack: webhook
|
|
217
|
+
* - webhook: generic HTTP POST
|
|
218
|
+
*/
|
|
162
219
|
export class NotifierService {
|
|
163
220
|
private readonly config: NotificationConfig;
|
|
164
221
|
private readonly channels: Map<string, NotifierChannel>;
|
|
@@ -222,6 +279,19 @@ export class NotifierService {
|
|
|
222
279
|
return false;
|
|
223
280
|
}
|
|
224
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Sends notification with priority-based routing.
|
|
284
|
+
*
|
|
285
|
+
* @param event - notification event type
|
|
286
|
+
* @param featureId - optional feature identifier
|
|
287
|
+
* @param message - notification message
|
|
288
|
+
* @param details - optional additional details
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```typescript
|
|
292
|
+
* await notifier.notify('gate_failed', 'my_feature', 'Gate fast failed', { gate: 'fast' });
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
225
295
|
async notify(
|
|
226
296
|
event: NotificationEvent,
|
|
227
297
|
context: { feature_id?: string; message: string; details?: Record<string, unknown> },
|
|
@@ -47,6 +47,10 @@ function normalizeFromWorktree(
|
|
|
47
47
|
return path.relative(repoRoot, absolute).replaceAll('\\\\', '/');
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* @fileoverview Patch validation and application service.
|
|
52
|
+
*/
|
|
53
|
+
|
|
50
54
|
export interface PatchServicePort {
|
|
51
55
|
getRepoRoot(): string;
|
|
52
56
|
getPolicySnapshot(): AnyRecord;
|
|
@@ -63,6 +67,41 @@ export interface PatchServicePort {
|
|
|
63
67
|
): Promise<AnyRecord>;
|
|
64
68
|
}
|
|
65
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Manages patch validation and atomic application.
|
|
72
|
+
*
|
|
73
|
+
* ## Responsibilities
|
|
74
|
+
* - Validate unified diff format
|
|
75
|
+
* - Enforce plan area restrictions
|
|
76
|
+
* - Apply policy path rules
|
|
77
|
+
* - Verify lock requirements
|
|
78
|
+
* - Apply patches atomically to worktree
|
|
79
|
+
*
|
|
80
|
+
* ## Dependencies
|
|
81
|
+
* Depends on {@link PatchServicePort} for:
|
|
82
|
+
* - Plan and policy access
|
|
83
|
+
* - State operations
|
|
84
|
+
* - Worktree path resolution
|
|
85
|
+
*
|
|
86
|
+
* ## Key Methods
|
|
87
|
+
* - `repoApplyPatch()` - validate and apply unified diff
|
|
88
|
+
* - `validatePatchPaths()` - enforce plan/policy path rules
|
|
89
|
+
* - `assertPlanLocksHeld()` - verify required locks
|
|
90
|
+
*
|
|
91
|
+
* ## Error Conditions
|
|
92
|
+
* - PATCH_VALIDATION_FAILED - invalid diff format or path violations
|
|
93
|
+
* - LOCK_NOT_HELD - required locks not acquired
|
|
94
|
+
* - PLAN_NOT_FOUND - no accepted plan exists
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const patchService = new PatchService(port);
|
|
99
|
+
* await patchService.repoApplyPatch('my_feature', unifiedDiff);
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* ## Thread Safety
|
|
103
|
+
* Patch application is atomic within feature worktree.
|
|
104
|
+
*/
|
|
66
105
|
export class PatchService {
|
|
67
106
|
private readonly port: PatchServicePort;
|
|
68
107
|
private readonly gateSelectionService: GateSelectionService;
|
|
@@ -72,6 +111,12 @@ export class PatchService {
|
|
|
72
111
|
this.gateSelectionService = new GateSelectionService(port);
|
|
73
112
|
}
|
|
74
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Loads accepted plan for a feature.
|
|
116
|
+
*
|
|
117
|
+
* @param featureId - feature identifier
|
|
118
|
+
* @returns Plan object or null if not found
|
|
119
|
+
*/
|
|
75
120
|
async loadAcceptedPlan(featureId: string): Promise<AnyRecord | null> {
|
|
76
121
|
const plan = await readJson(this.port.planPath(featureId), null);
|
|
77
122
|
if (!plan && this.port.getPolicySnapshot().patch_policy.enforce_plan) {
|
|
@@ -102,6 +147,13 @@ export class PatchService {
|
|
|
102
147
|
return sortResourcesDeterministically(resources);
|
|
103
148
|
}
|
|
104
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Verifies required resource locks are held for plan.
|
|
152
|
+
*
|
|
153
|
+
* @param featureId - feature identifier
|
|
154
|
+
* @param plan - plan requiring locks
|
|
155
|
+
* @throws LOCK_NOT_HELD - required locks not held
|
|
156
|
+
*/
|
|
105
157
|
async assertPlanLocksHeld(featureId: string, plan: AnyRecord): Promise<void> {
|
|
106
158
|
const state = await this.port.readState(featureId);
|
|
107
159
|
const held = new Set(readHeldLocks(state.frontMatter));
|
|
@@ -124,6 +176,24 @@ export class PatchService {
|
|
|
124
176
|
}
|
|
125
177
|
}
|
|
126
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Validates patch paths against plan areas and policy rules.
|
|
181
|
+
*
|
|
182
|
+
* Enforces:
|
|
183
|
+
* - Paths must be in plan allowed_areas
|
|
184
|
+
* - Paths must not be in plan forbidden_areas
|
|
185
|
+
* - Paths must not violate policy path rules
|
|
186
|
+
*
|
|
187
|
+
* @param featureId - feature identifier
|
|
188
|
+
* @param patch - unified diff content
|
|
189
|
+
* @param plan - accepted plan with area restrictions
|
|
190
|
+
* @throws PATCH_VALIDATION_FAILED - path violations detected
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```typescript
|
|
194
|
+
* await patchService.validatePatchPaths('my_feature', diff, plan);
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
127
197
|
async validatePatchPaths(
|
|
128
198
|
featureId: string,
|
|
129
199
|
parsedDiff: ReturnType<typeof parseUnifiedDiff>,
|
|
@@ -199,6 +269,31 @@ export class PatchService {
|
|
|
199
269
|
return normalizedTouched;
|
|
200
270
|
}
|
|
201
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Validates and applies unified diff to feature worktree.
|
|
274
|
+
*
|
|
275
|
+
* Full validation pipeline:
|
|
276
|
+
* 1. Load accepted plan
|
|
277
|
+
* 2. Verify required locks held
|
|
278
|
+
* 3. Validate patch format
|
|
279
|
+
* 4. Enforce path restrictions
|
|
280
|
+
* 5. Apply patch atomically
|
|
281
|
+
* 6. Update state with patch metadata
|
|
282
|
+
*
|
|
283
|
+
* @param featureId - feature identifier
|
|
284
|
+
* @param patch - unified diff content
|
|
285
|
+
* @returns Success response with applied patch metadata
|
|
286
|
+
* @throws PLAN_NOT_FOUND - no accepted plan
|
|
287
|
+
* @throws LOCK_NOT_HELD - required locks not held
|
|
288
|
+
* @throws PATCH_VALIDATION_FAILED - invalid format or path violations
|
|
289
|
+
* @throws PATCH_APPLICATION_FAILED - git apply failed
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```typescript
|
|
293
|
+
* const diff = `--- a/file.ts\n+++ b/file.ts\n...`;
|
|
294
|
+
* await patchService.repoApplyPatch('my_feature', diff);
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
202
297
|
async repoApplyPatch(
|
|
203
298
|
featureId: string,
|
|
204
299
|
unifiedDiff: string,
|