@abelxiaoxing/cadence 1.5.3 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +61 -1
  2. package/config/plan-draft.example.json +3 -0
  3. package/config/plan-draft.multiple-tasks.example.json +30 -0
  4. package/package.json +1 -1
  5. package/prompts/abel-design.md +33 -3
  6. package/prompts/abel-implement.md +36 -4
  7. package/src/apply-transaction.ts +1 -9
  8. package/src/artifact-store.ts +1 -9
  9. package/src/change-verification.ts +758 -65
  10. package/src/contracts.ts +8 -0
  11. package/src/delivery-compiler.ts +452 -9
  12. package/src/design-control.ts +2 -2
  13. package/src/design-diagnostics.ts +6 -4
  14. package/src/directory-sync.ts +36 -0
  15. package/src/durable-contracts.ts +19 -3
  16. package/src/durable-workflow.ts +47 -38
  17. package/src/implement-continuation.ts +559 -0
  18. package/src/implement-graph.ts +114 -0
  19. package/src/implement-plan.ts +2 -0
  20. package/src/index.ts +179 -1
  21. package/src/operator-cli.mjs +12 -7
  22. package/src/package-state.ts +309 -0
  23. package/src/package-verification.ts +17 -1
  24. package/src/package-workflow.ts +219 -171
  25. package/src/phase-execution.ts +7 -1
  26. package/src/plan-draft-summary.ts +385 -7
  27. package/src/plan-draft.ts +9 -0
  28. package/src/run-store.ts +39 -15
  29. package/src/storage-schema.ts +14 -6
  30. package/src/subagent-activity.ts +3 -1
  31. package/src/verification-capability.ts +17 -5
  32. package/src/verification-environment.ts +20 -2
  33. package/src/workflow-policy.ts +296 -5
  34. package/src/workflow-recovery-policy.ts +2 -0
  35. package/src/workflow-state-machine.ts +400 -131
  36. package/src/workflow-status.ts +251 -59
  37. package/src/workspace-io-worker.mjs +48 -42
  38. package/src/workspace-store.ts +1 -9
@@ -14,6 +14,7 @@ import type { PackageContext } from "./model-source.ts";
14
14
  import { inspectOpenSpecDelivery } from "./openspec-cli.ts";
15
15
  import { proposePackageCandidate } from "./package-candidate.ts";
16
16
  import { packageDeliverySource } from "./package-delivery.ts";
17
+ import { acquirePackageState } from "./package-state.ts";
17
18
  import {
18
19
  changeVerificationResult,
19
20
  executePackageVerification,
@@ -79,6 +80,42 @@ function affectedVerification(
79
80
 
80
81
  export function openPackageWorkflowService(
81
82
  initialContext: PackageContext,
83
+ ): PackageWorkflowService {
84
+ const state = acquirePackageState(
85
+ resolveStateRoot({
86
+ consumerRoot: path.resolve(initialContext.cwd),
87
+ xdgStateHome: process.env.XDG_STATE_HOME,
88
+ }),
89
+ );
90
+ try {
91
+ const service = openPreparedPackageWorkflowService(initialContext);
92
+ const notice = (result: Record<string, unknown>) =>
93
+ state.notice ? { ...result, packageStateReset: state.notice } : result;
94
+ return {
95
+ ...service,
96
+ async execute(...args) {
97
+ return notice(await service.execute(...args));
98
+ },
99
+ async executeDesign(...args) {
100
+ return notice(await service.executeDesign(...args));
101
+ },
102
+ async executeAmendment(...args) {
103
+ return notice(await service.executeAmendment(...args));
104
+ },
105
+ async close() {
106
+ // Never release ownership while an executor or storage connection may survive.
107
+ await service.close();
108
+ state.close();
109
+ },
110
+ };
111
+ } catch (error) {
112
+ state.close();
113
+ throw error;
114
+ }
115
+ }
116
+
117
+ function openPreparedPackageWorkflowService(
118
+ initialContext: PackageContext,
82
119
  ): PackageWorkflowService {
83
120
  const consumerRoot = path.resolve(initialContext.cwd);
84
121
  const routeResolution = loadRoutePolicy({
@@ -102,180 +139,191 @@ export function openPackageWorkflowService(
102
139
  stateRoot,
103
140
  inspectOpenSpec: inspectOpenSpecDelivery,
104
141
  });
105
- const implementationAgent = loadAgentDefinitions().find(
106
- (agent) => agent.role === "implementation-worker",
107
- );
108
- if (!implementationAgent) {
109
- throw new Error("implementation-worker-agent-unavailable");
110
- }
111
- const hostLimit = (name: string, fallback: number): number => {
112
- const value =
113
- process.env[name] === undefined ? fallback : Number(process.env[name]);
114
- if (!Number.isSafeInteger(value) || value < 1)
115
- throw new Error("verification-host-limit-invalid");
116
- return value;
117
- };
118
- const maxReportBytes = hostLimit(
119
- "ABEL_VERIFICATION_MAX_REPORT_BYTES",
120
- 64 * 1024 * 1024,
121
- );
122
- const workHardLimit = hostLimit("ABEL_WORK_MAX_UNITS", 512);
123
- const engine = openDurableWorkflowEngine({
124
- workHardLimit,
125
- verificationPolicy: `report-file-v5:${JSON.stringify(executionProfile())}`,
126
- verificationEnvironment: coalesceVerificationScans(
127
- (plan: import("./implement-plan.ts").ImplementPlan, signal) =>
128
- captureVerificationEnvironmentIdentity(
129
- consumerRoot,
130
- [
131
- ...plan.tasks.flatMap((task) => [
132
- ...Object.values(task.phases).map((phase) => phase.verification),
133
- task.affectedVerification,
134
- task.repairVerification,
135
- ]),
136
- plan.verification.baseline.fullSuite,
137
- plan.verification.change.fullSuite,
138
- plan.verification.change.postApply,
139
- ...(plan.verification.agentsCheckpoint.verification
140
- ? [plan.verification.agentsCheckpoint.verification]
141
- : []),
142
- ],
143
- signal,
144
- ),
145
- ),
146
- consumerRoot,
147
- stateRoot,
148
- deliverySource: packageDeliverySource(consumerRoot, {
149
- verifyGateProof: (input) => design.verifyGateProof(input),
150
- verifyFinalizedDelivery: (input) => design.verifyFinalizedDelivery(input),
151
- }),
152
- routePolicy: routeResolution.ok
153
- ? routeResolution.policy
154
- : unavailableRoutePolicy(),
155
- proposeCandidate: (input) =>
156
- proposePackageCandidate(
157
- input,
158
- contexts.getStore(),
159
- implementationAgent,
160
- feedback.getStore()?.current(),
161
- ),
162
- verifyPhase: async (input) =>
163
- phaseVerificationResult(
164
- await verify({
165
- maxReportBytes,
166
- executionWritePaths: input.executionWritePaths,
167
- root: input.root,
168
- dependencyOwner: consumerRoot,
169
- verification: input.verification,
170
- signal: input.signal,
171
- }),
172
- ),
173
- verifyChange: async (input) => {
174
- const verifications = input.verification
175
- ? [input.verification]
176
- : input.plan.tasks.map((task) => affectedVerification(task));
177
- let failure: ReturnType<typeof changeVerificationResult> | undefined;
178
- for (const verification of verifications) {
179
- const result = await verify({
180
- maxReportBytes,
181
- executionWritePaths: input.plan.tasks.flatMap((task) =>
182
- Object.values(task.phases).flatMap((phase) => [
183
- ...phase.write,
184
- ...phase.delete,
185
- ]),
142
+ try {
143
+ const implementationAgent = loadAgentDefinitions().find(
144
+ (agent) => agent.role === "implementation-worker",
145
+ );
146
+ if (!implementationAgent) {
147
+ throw new Error("implementation-worker-agent-unavailable");
148
+ }
149
+ const hostLimit = (name: string, fallback: number): number => {
150
+ const value =
151
+ process.env[name] === undefined ? fallback : Number(process.env[name]);
152
+ if (!Number.isSafeInteger(value) || value < 1)
153
+ throw new Error("verification-host-limit-invalid");
154
+ return value;
155
+ };
156
+ const maxReportBytes = hostLimit(
157
+ "ABEL_VERIFICATION_MAX_REPORT_BYTES",
158
+ 64 * 1024 * 1024,
159
+ );
160
+ const workHardLimit = hostLimit("ABEL_WORK_MAX_UNITS", 512);
161
+ const engine = openDurableWorkflowEngine({
162
+ workHardLimit,
163
+ verificationPolicy: `report-file-v5:${JSON.stringify(executionProfile())}`,
164
+ verificationEnvironment: coalesceVerificationScans(
165
+ (plan: import("./implement-plan.ts").ImplementPlan, signal) =>
166
+ captureVerificationEnvironmentIdentity(
167
+ consumerRoot,
168
+ [
169
+ ...plan.tasks.flatMap((task) => [
170
+ ...Object.values(task.phases).map(
171
+ (phase) => phase.verification,
172
+ ),
173
+ task.affectedVerification,
174
+ ...(task.baselineVerification
175
+ ? [task.baselineVerification]
176
+ : []),
177
+ task.repairVerification,
178
+ ]),
179
+ plan.verification.baseline.fullSuite,
180
+ plan.verification.change.fullSuite,
181
+ plan.verification.change.postApply,
182
+ ...(plan.verification.agentsCheckpoint.verification
183
+ ? [plan.verification.agentsCheckpoint.verification]
184
+ : []),
185
+ ],
186
+ signal,
186
187
  ),
187
- root: input.root,
188
- dependencyOwner: consumerRoot,
189
- verification,
190
- signal: input.signal,
191
- });
192
- const observed = changeVerificationResult(result);
193
- if (!observed.ok) {
194
- if (observed.kind !== "verification") return observed;
195
- if (failure && !failure.ok && failure.kind === "verification") {
196
- failure.failureIdentities = [
197
- ...new Set([
198
- ...(failure.failureIdentities ?? []),
199
- ...(observed.failureIdentities ?? []),
188
+ ),
189
+ consumerRoot,
190
+ stateRoot,
191
+ deliverySource: packageDeliverySource(consumerRoot, {
192
+ verifyGateProof: (input) => design.verifyGateProof(input),
193
+ verifyFinalizedDelivery: (input) =>
194
+ design.verifyFinalizedDelivery(input),
195
+ }),
196
+ routePolicy: routeResolution.ok
197
+ ? routeResolution.policy
198
+ : unavailableRoutePolicy(),
199
+ proposeCandidate: (input) =>
200
+ proposePackageCandidate(
201
+ input,
202
+ contexts.getStore(),
203
+ implementationAgent,
204
+ feedback.getStore()?.current(),
205
+ ),
206
+ verifyPhase: async (input) =>
207
+ phaseVerificationResult(
208
+ await verify({
209
+ maxReportBytes,
210
+ executionWritePaths: input.executionWritePaths,
211
+ root: input.root,
212
+ dependencyOwner: consumerRoot,
213
+ verification: input.verification,
214
+ signal: input.signal,
215
+ }),
216
+ ),
217
+ verifyChange: async (input) => {
218
+ const verifications = input.verification
219
+ ? [input.verification]
220
+ : input.plan.tasks.map((task) => affectedVerification(task));
221
+ let failure: ReturnType<typeof changeVerificationResult> | undefined;
222
+ for (const verification of verifications) {
223
+ const result = await verify({
224
+ maxReportBytes,
225
+ executionWritePaths: input.plan.tasks.flatMap((task) =>
226
+ Object.values(task.phases).flatMap((phase) => [
227
+ ...phase.write,
228
+ ...phase.delete,
200
229
  ]),
201
- ];
202
- failure.attributionReliable =
203
- failure.attributionReliable !== false &&
204
- observed.attributionReliable !== false;
205
- } else failure = observed;
230
+ ),
231
+ root: input.root,
232
+ dependencyOwner: consumerRoot,
233
+ verification,
234
+ signal: input.signal,
235
+ });
236
+ const observed = changeVerificationResult(result);
237
+ if (!observed.ok) {
238
+ if (observed.kind !== "verification") return observed;
239
+ if (failure && !failure.ok && failure.kind === "verification") {
240
+ failure.failureIdentities = [
241
+ ...new Set([
242
+ ...(failure.failureIdentities ?? []),
243
+ ...(observed.failureIdentities ?? []),
244
+ ]),
245
+ ];
246
+ failure.attributionReliable =
247
+ failure.attributionReliable !== false &&
248
+ observed.attributionReliable !== false;
249
+ } else failure = observed;
250
+ }
206
251
  }
207
- }
208
- if (failure) return failure;
209
- return {
210
- ok: true,
211
- exitCode: 0,
212
- classification: "expected-green",
213
- };
214
- },
215
- });
216
- return {
217
- async execute(
218
- command: unknown,
219
- context = initialContext,
220
- signal,
221
- onActivity,
222
- ) {
223
- const operationRouteResolution = loadRoutePolicy({
224
- cwd: consumerRoot,
225
- home: homedir(),
226
- ...(context.model ? { parentModel: context.model } : {}),
227
- });
228
- engine.updateRoutePolicy(
229
- operationRouteResolution.ok
230
- ? operationRouteResolution.policy
231
- : unavailableRoutePolicy(),
232
- );
233
- const validation = validateControlCommand(command);
234
- if (!validation.ok) {
235
- const error = new Error(validation.code);
236
- error.name = "ControlCommandError";
237
- throw error;
238
- }
239
- return feedback.run(new VerificationFeedback(), () =>
240
- contexts.run(context, async () => {
241
- const outcome = await engine.execute(
242
- validation.value,
243
- signal,
244
- onActivity,
245
- );
246
- const routePolicy = visibleRoutePolicyStatus(
247
- operationRouteResolution,
248
- engine.routePolicyStatus(),
249
- );
250
- return {
251
- ...outcome,
252
- routePolicy,
253
- verificationDiagnostics: feedback.getStore()?.current() ?? [],
254
- };
255
- }),
256
- );
257
- },
258
- executeDesign(request: unknown) {
259
- return design.execute(request);
260
- },
261
- executeAmendment(change: string, batchId: string, request: unknown) {
262
- return engine.amend(change, batchId, request, (assertAuthority) =>
263
- design.executeWithAuthority(request, assertAuthority),
264
- );
265
- },
266
- assertDesignRun(runId: string) {
267
- design.assertDesignRun(runId);
268
- },
269
- recordDesignEvidence(input) {
270
- return design.recordEvidence(input);
271
- },
272
- async close() {
273
- contexts.disable();
274
- await engine.close();
275
- feedback.disable();
276
- design.close();
277
- },
278
- };
252
+ if (failure) return failure;
253
+ return {
254
+ ok: true,
255
+ exitCode: 0,
256
+ classification: "expected-green",
257
+ };
258
+ },
259
+ });
260
+ return {
261
+ async execute(
262
+ command: unknown,
263
+ context = initialContext,
264
+ signal,
265
+ onActivity,
266
+ ) {
267
+ const operationRouteResolution = loadRoutePolicy({
268
+ cwd: consumerRoot,
269
+ home: homedir(),
270
+ ...(context.model ? { parentModel: context.model } : {}),
271
+ });
272
+ engine.updateRoutePolicy(
273
+ operationRouteResolution.ok
274
+ ? operationRouteResolution.policy
275
+ : unavailableRoutePolicy(),
276
+ );
277
+ const validation = validateControlCommand(command);
278
+ if (!validation.ok) {
279
+ const error = new Error(validation.code);
280
+ error.name = "ControlCommandError";
281
+ throw error;
282
+ }
283
+ return feedback.run(new VerificationFeedback(), () =>
284
+ contexts.run(context, async () => {
285
+ const outcome = await engine.execute(
286
+ validation.value,
287
+ signal,
288
+ onActivity,
289
+ );
290
+ const routePolicy = visibleRoutePolicyStatus(
291
+ operationRouteResolution,
292
+ engine.routePolicyStatus(),
293
+ );
294
+ return {
295
+ ...outcome,
296
+ routePolicy,
297
+ verificationDiagnostics: feedback.getStore()?.current() ?? [],
298
+ };
299
+ }),
300
+ );
301
+ },
302
+ executeDesign(request: unknown) {
303
+ return design.execute(request);
304
+ },
305
+ executeAmendment(change: string, batchId: string, request: unknown) {
306
+ return engine.amend(change, batchId, request, (assertAuthority) =>
307
+ design.executeWithAuthority(request, assertAuthority),
308
+ );
309
+ },
310
+ assertDesignRun(runId: string) {
311
+ design.assertDesignRun(runId);
312
+ },
313
+ recordDesignEvidence(input) {
314
+ return design.recordEvidence(input);
315
+ },
316
+ async close() {
317
+ contexts.disable();
318
+ await engine.close();
319
+ feedback.disable();
320
+ design.close();
321
+ },
322
+ };
323
+ } catch (error) {
324
+ design.close();
325
+ throw error;
326
+ }
279
327
  }
280
328
 
281
329
  function visibleRoutePolicyStatus(
@@ -43,6 +43,7 @@ import {
43
43
  LOCKFILES,
44
44
  SHA256,
45
45
  semanticRouteFailure,
46
+ taskExecutionContract,
46
47
  type WorkflowAttemptOutcome,
47
48
  type WorkflowRouteFacts,
48
49
  type WorkflowWorker,
@@ -60,6 +61,7 @@ export interface PhaseExecutionServices {
60
61
  resources: PhaseResources;
61
62
  ledger: TaskLedger;
62
63
  signal: AbortSignal;
64
+ taskId?: string;
63
65
  }): Promise<DurableBaselineResult>;
64
66
  verifyTaskAffected(input: {
65
67
  resources: PhaseResources;
@@ -197,7 +199,10 @@ export class PhaseExecution {
197
199
  runId: input.runId,
198
200
  deliveryRevision: input.deliveryRevision,
199
201
  taskId: input.taskId,
200
- boundaryHash: hash("durable-task-boundary", JSON.stringify(input.task)),
202
+ boundaryHash: hash(
203
+ "durable-task-boundary",
204
+ JSON.stringify(taskExecutionContract(input.task)),
205
+ ),
201
206
  objective: input.task.objective,
202
207
  contextRefs,
203
208
  initialPhase:
@@ -1562,6 +1567,7 @@ export class PhaseExecution {
1562
1567
  resources,
1563
1568
  ledger,
1564
1569
  signal: input.signal,
1570
+ taskId: input.taskId,
1565
1571
  });
1566
1572
  if (!captured.ok) {
1567
1573
  const outcome = captured.outcome;