@abelxiaoxing/cadence 1.2.2 → 1.2.4

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.
@@ -30,7 +30,6 @@ import {
30
30
  import type { GateApprovalProof } from "./delivery-compiler.ts";
31
31
  import {
32
32
  DeliveryValidationError,
33
- IMPLEMENT_PLAN_SCHEMA_VERSION,
34
33
  type ImplementPlan,
35
34
  type PlanTaskDraft,
36
35
  } from "./delivery-compiler.ts";
@@ -76,11 +75,72 @@ const LOCKFILES = new Set([
76
75
  "pnpm-lock.yaml",
77
76
  "yarn.lock",
78
77
  ]);
79
- const IMPLEMENTATION_ROUTE_REQUIREMENTS = Object.freeze({
80
- minContextWindow: 128_000,
81
- minOutputTokens: 64_000,
78
+ const IMPLEMENTATION_ROUTE_LIMITS = Object.freeze({
79
+ preferredContextWindow: 128_000,
80
+ preferredOutputTokens: 64_000,
81
+ minimumContextWindow: 16_000,
82
+ minimumOutputTokens: 8_000,
82
83
  });
83
84
 
85
+ function implementationRouteRequirements(
86
+ input: {
87
+ task?: PlanTaskDraft;
88
+ artifactCorrection?: RedArtifactCorrection;
89
+ repair?: { failureIdentities: string[] };
90
+ } = {},
91
+ ) {
92
+ if (!input.task) {
93
+ return {
94
+ minContextWindow: IMPLEMENTATION_ROUTE_LIMITS.minimumContextWindow,
95
+ minOutputTokens: IMPLEMENTATION_ROUTE_LIMITS.minimumOutputTokens,
96
+ };
97
+ }
98
+ const serializedBytes = Buffer.byteLength(canonicalJson(input.task), "utf8");
99
+ const pathCount = Object.values(input.task.phases).reduce(
100
+ (total, phase) =>
101
+ total + phase.read.length + phase.write.length + phase.delete.length,
102
+ 0,
103
+ );
104
+ const complexity =
105
+ serializedBytes +
106
+ pathCount * 512 +
107
+ (input.artifactCorrection ? 16_384 : 0) +
108
+ (input.repair ? input.repair.failureIdentities.length * 256 + 16_384 : 0);
109
+ return {
110
+ minContextWindow: Math.min(
111
+ IMPLEMENTATION_ROUTE_LIMITS.preferredContextWindow,
112
+ Math.max(
113
+ IMPLEMENTATION_ROUTE_LIMITS.minimumContextWindow,
114
+ 16_384 + complexity * 2,
115
+ ),
116
+ ),
117
+ minOutputTokens: Math.min(
118
+ IMPLEMENTATION_ROUTE_LIMITS.preferredOutputTokens,
119
+ Math.max(
120
+ IMPLEMENTATION_ROUTE_LIMITS.minimumOutputTokens,
121
+ 8_192 + Math.ceil(complexity / 2),
122
+ ),
123
+ ),
124
+ };
125
+ }
126
+
127
+ const SEMANTIC_ROUTE_FAILURE_CODES = new Set([
128
+ "child-no-structural-submit",
129
+ "invalid-structural-result",
130
+ "structural-identity-mismatch",
131
+ "invalid-diff",
132
+ "candidate-diff-invalid",
133
+ ]);
134
+
135
+ function semanticRouteFailure(
136
+ value: DurableCandidateProposal,
137
+ ): string | undefined {
138
+ return (value.kind === "retryable" || value.kind === "paused") &&
139
+ SEMANTIC_ROUTE_FAILURE_CODES.has(value.code)
140
+ ? value.code
141
+ : undefined;
142
+ }
143
+
84
144
  function isCancellationException(error: unknown, signal: AbortSignal): boolean {
85
145
  if (!signal.aborted) return false;
86
146
  if (error === signal.reason) return true;
@@ -137,6 +197,7 @@ const ENGINE_SCHEMA = `
137
197
  phase TEXT NOT NULL,
138
198
  pause_code TEXT,
139
199
  context_request_json TEXT,
200
+ attempt_diagnostic_json TEXT,
140
201
  route_id TEXT,
141
202
  route_fingerprint TEXT,
142
203
  queue_position INTEGER,
@@ -290,7 +351,9 @@ export type WorkflowAttemptOutcome = (
290
351
  | { kind: "operation-cancelled"; code: "cancelled" }
291
352
  ) &
292
353
  WorkflowWorkspaceFacts &
293
- WorkflowRouteFacts;
354
+ WorkflowRouteFacts & {
355
+ attemptDiagnostic?: SafeAttemptDiagnostic;
356
+ };
294
357
 
295
358
  export interface WorkflowWorker {
296
359
  runAttempt(input: {
@@ -310,6 +373,7 @@ export interface WorkflowWorker {
310
373
  failureIdentities: string[];
311
374
  };
312
375
  artifactCorrection?: RedArtifactCorrection;
376
+ contextRequest?: WorkflowContextRequest;
313
377
  signal: AbortSignal;
314
378
  onActivity?: (
315
379
  event: Pick<
@@ -321,6 +385,7 @@ export interface WorkflowWorker {
321
385
  rebind(input: {
322
386
  runId: string;
323
387
  taskId: string;
388
+ task: PlanTaskDraft;
324
389
  role: "implementation-worker";
325
390
  routeId: string;
326
391
  }):
@@ -354,7 +419,6 @@ export interface WorkflowWorker {
354
419
  }
355
420
 
356
421
  export interface WorkflowDelivery {
357
- version: 2;
358
422
  gate: "gate-a" | "gate-b";
359
423
  revision: number;
360
424
  receiptHash: string;
@@ -494,11 +558,22 @@ interface EngineTaskRow {
494
558
  phase: "red" | "green" | "refactor";
495
559
  pause_code: string | null;
496
560
  context_request_json: string | null;
561
+ attempt_diagnostic_json: string | null;
497
562
  route_id: string | null;
498
563
  route_fingerprint: string | null;
499
564
  queue_position: number | null;
500
565
  }
501
566
 
567
+ interface SafeAttemptDiagnostic {
568
+ fingerprint?: string;
569
+ finalCategory?: string;
570
+ submitAttempts?: number;
571
+ schema?: string;
572
+ identityMismatch?: string[];
573
+ sameFailureCount?: number;
574
+ action?: "rebind-or-revise-delivery";
575
+ }
576
+
502
577
  interface EngineOperationRow {
503
578
  command: string;
504
579
  state: string;
@@ -970,7 +1045,6 @@ function parsePlan(value: string): ImplementPlan {
970
1045
  function assertPlan(value: unknown): asserts value is ImplementPlan {
971
1046
  if (
972
1047
  !isRecord(value) ||
973
- value.schemaVersion !== IMPLEMENT_PLAN_SCHEMA_VERSION ||
974
1048
  typeof value.changeId !== "string" ||
975
1049
  !Array.isArray(value.tasks) ||
976
1050
  !Array.isArray(value.outputs) ||
@@ -1016,7 +1090,6 @@ function assertDelivery(
1016
1090
  ): asserts value is WorkflowDelivery {
1017
1091
  if (
1018
1092
  !isRecord(value) ||
1019
- value.version !== 2 ||
1020
1093
  (value.gate !== "gate-a" && value.gate !== "gate-b") ||
1021
1094
  !Number.isSafeInteger(value.revision) ||
1022
1095
  (value.revision as number) < 1 ||
@@ -1217,6 +1290,7 @@ function ensureEngineColumns(database: DatabaseSync): void {
1217
1290
  ensure("workflow_engine_tasks", [
1218
1291
  ["route_fingerprint", "TEXT"],
1219
1292
  ["context_request_json", "TEXT"],
1293
+ ["attempt_diagnostic_json", "TEXT"],
1220
1294
  ]);
1221
1295
  }
1222
1296
 
@@ -1233,6 +1307,7 @@ export type DurableCandidateProposal =
1233
1307
  kind: "paused" | "retryable" | "approval-needed";
1234
1308
  code: string;
1235
1309
  contextRequest?: WorkflowContextRequest;
1310
+ attemptDiagnostic?: SafeAttemptDiagnostic;
1236
1311
  }
1237
1312
  | { kind: "operation-cancelled"; code: "cancelled" };
1238
1313
 
@@ -1286,7 +1361,6 @@ interface DurableVerificationObservation {
1286
1361
  }
1287
1362
 
1288
1363
  interface DurableVerificationBaseline {
1289
- version: 1;
1290
1364
  revisionId: string;
1291
1365
  targetContracts: Array<{
1292
1366
  taskId: string;
@@ -1345,6 +1419,7 @@ export interface DurableWorkflowEngineOptions {
1345
1419
  failureIdentities: string[];
1346
1420
  };
1347
1421
  artifactCorrection?: RedArtifactCorrection;
1422
+ contextRequest?: WorkflowContextRequest;
1348
1423
  signal: AbortSignal;
1349
1424
  onHeaders(): void;
1350
1425
  onProgress(): void;
@@ -1634,7 +1709,7 @@ function verificationIdentities(
1634
1709
  return [...new Set(supplied)].sort();
1635
1710
  }
1636
1711
  if (result.ok) return [];
1637
- return [hash("verification-failure-v1", scope, verification.id, result.code)];
1712
+ return [hash("verification-failure", scope, verification.id, result.code)];
1638
1713
  }
1639
1714
 
1640
1715
  function parseVerificationBaseline(
@@ -1642,7 +1717,6 @@ function parseVerificationBaseline(
1642
1717
  ): DurableVerificationBaseline {
1643
1718
  if (
1644
1719
  !isRecord(value) ||
1645
- value.version !== 1 ||
1646
1720
  typeof value.revisionId !== "string" ||
1647
1721
  !SHA256.test(value.revisionId) ||
1648
1722
  !Array.isArray(value.targetContracts) ||
@@ -2101,7 +2175,6 @@ class DurableWorkflowComposition
2101
2175
  });
2102
2176
  if (!fullSuite.ok) return fullSuite;
2103
2177
  const baseline: DurableVerificationBaseline = {
2104
- version: 1,
2105
2178
  revisionId: input.resources.baselineRevisionId,
2106
2179
  targetContracts: plan.tasks.map((task) => ({
2107
2180
  taskId: task.taskId,
@@ -2932,7 +3005,13 @@ class DurableWorkflowComposition
2932
3005
  ...(request.routeFingerprint
2933
3006
  ? { expectedFingerprint: request.routeFingerprint }
2934
3007
  : {}),
2935
- requirements: IMPLEMENTATION_ROUTE_REQUIREMENTS,
3008
+ requirements: implementationRouteRequirements({
3009
+ task: request.task,
3010
+ ...(request.artifactCorrection
3011
+ ? { artifactCorrection: request.artifactCorrection }
3012
+ : {}),
3013
+ repair: { failureIdentities: input.failureIdentities },
3014
+ }),
2936
3015
  });
2937
3016
  if (!rebound.ok) return { kind: "paused", code: rebound.code };
2938
3017
  }
@@ -2951,9 +3030,16 @@ class DurableWorkflowComposition
2951
3030
  runId: request.runId,
2952
3031
  operationId: `${request.operationId}:${request.taskId}:repair:${input.repairAttempt}`,
2953
3032
  role: "implementation-worker",
2954
- requirements: IMPLEMENTATION_ROUTE_REQUIREMENTS,
3033
+ requirements: implementationRouteRequirements({
3034
+ task: request.task,
3035
+ ...(request.artifactCorrection
3036
+ ? { artifactCorrection: request.artifactCorrection }
3037
+ : {}),
3038
+ repair: { failureIdentities: input.failureIdentities },
3039
+ }),
2955
3040
  signal: request.signal,
2956
3041
  ...(request.onActivity ? { onActivity: request.onActivity } : {}),
3042
+ classifyResult: semanticRouteFailure,
2957
3043
  execute: (attempt) => {
2958
3044
  identity = {
2959
3045
  candidateId: `candidate-${randomUUID()}`,
@@ -2994,6 +3080,9 @@ class DurableWorkflowComposition
2994
3080
  ...(request.artifactCorrection
2995
3081
  ? { artifactCorrection: request.artifactCorrection }
2996
3082
  : {}),
3083
+ ...(request.contextRequest
3084
+ ? { contextRequest: request.contextRequest }
3085
+ : {}),
2997
3086
  signal: attempt.signal,
2998
3087
  onHeaders: attempt.onHeaders,
2999
3088
  onProgress: attempt.onProgress,
@@ -4007,7 +4096,13 @@ class DurableWorkflowComposition
4007
4096
  ...(input.routeFingerprint
4008
4097
  ? { expectedFingerprint: input.routeFingerprint }
4009
4098
  : {}),
4010
- requirements: IMPLEMENTATION_ROUTE_REQUIREMENTS,
4099
+ requirements: implementationRouteRequirements({
4100
+ task: input.task,
4101
+ ...(input.artifactCorrection
4102
+ ? { artifactCorrection: input.artifactCorrection }
4103
+ : {}),
4104
+ ...(input.repair ? { repair: input.repair } : {}),
4105
+ }),
4011
4106
  });
4012
4107
  if (!rebound.ok) {
4013
4108
  return retained({ kind: "paused", code: rebound.code });
@@ -4023,9 +4118,16 @@ class DurableWorkflowComposition
4023
4118
  runId: input.runId,
4024
4119
  operationId: `${input.operationId}:${input.taskId}:${input.phase}`,
4025
4120
  role: "implementation-worker",
4026
- requirements: IMPLEMENTATION_ROUTE_REQUIREMENTS,
4121
+ requirements: implementationRouteRequirements({
4122
+ task: input.task,
4123
+ ...(input.artifactCorrection
4124
+ ? { artifactCorrection: input.artifactCorrection }
4125
+ : {}),
4126
+ ...(input.repair ? { repair: input.repair } : {}),
4127
+ }),
4027
4128
  signal: input.signal,
4028
4129
  ...(input.onActivity ? { onActivity: input.onActivity } : {}),
4130
+ classifyResult: semanticRouteFailure,
4029
4131
  execute: (attempt) => {
4030
4132
  identity = {
4031
4133
  candidateId: `candidate-${randomUUID()}`,
@@ -4062,6 +4164,9 @@ class DurableWorkflowComposition
4062
4164
  deletePaths: phase.delete,
4063
4165
  },
4064
4166
  route: attempt.route,
4167
+ ...(input.contextRequest
4168
+ ? { contextRequest: input.contextRequest }
4169
+ : {}),
4065
4170
  signal: attempt.signal,
4066
4171
  onHeaders: attempt.onHeaders,
4067
4172
  onProgress: attempt.onProgress,
@@ -4641,7 +4746,7 @@ class DurableWorkflowComposition
4641
4746
  runId: input.runId,
4642
4747
  role: input.role,
4643
4748
  routeId: input.routeId,
4644
- requirements: IMPLEMENTATION_ROUTE_REQUIREMENTS,
4749
+ requirements: implementationRouteRequirements({ task: input.task }),
4645
4750
  });
4646
4751
  return rebound.ok
4647
4752
  ? {
@@ -5528,8 +5633,8 @@ export class WorkflowEngine {
5528
5633
  return this.#database
5529
5634
  .prepare(
5530
5635
  `SELECT task_id, task_order, delivery_revision, plan_json, state,
5531
- phase, pause_code, context_request_json, route_id,
5532
- route_fingerprint, queue_position
5636
+ phase, pause_code, context_request_json, attempt_diagnostic_json,
5637
+ route_id, route_fingerprint, queue_position
5533
5638
  FROM workflow_engine_tasks WHERE run_id = ? ORDER BY task_order`,
5534
5639
  )
5535
5640
  .all(runId) as unknown as EngineTaskRow[];
@@ -5826,7 +5931,8 @@ export class WorkflowEngine {
5826
5931
  `UPDATE workflow_engine_tasks
5827
5932
  SET task_order = ?, delivery_revision = ?, plan_json = ?,
5828
5933
  state = 'pending', phase = 'red', pause_code = NULL,
5829
- context_request_json = NULL, queue_position = NULL
5934
+ context_request_json = NULL, attempt_diagnostic_json = NULL,
5935
+ queue_position = NULL
5830
5936
  WHERE run_id = ? AND task_id = ?`,
5831
5937
  )
5832
5938
  .run(taskOrder, delivery.revision, taskJson, runId, task.taskId);
@@ -5883,6 +5989,7 @@ export class WorkflowEngine {
5883
5989
  phase?: "red" | "green" | "refactor";
5884
5990
  pauseCode?: string | null;
5885
5991
  contextRequest?: WorkflowContextRequest | null;
5992
+ attemptDiagnostic?: SafeAttemptDiagnostic | null;
5886
5993
  routeId?: string | null;
5887
5994
  routeFingerprint?: string | null;
5888
5995
  queuePosition?: number | null;
@@ -5913,6 +6020,14 @@ export class WorkflowEngine {
5913
6020
  ),
5914
6021
  );
5915
6022
  }
6023
+ if (values.attemptDiagnostic !== undefined) {
6024
+ assignments.push("attempt_diagnostic_json = ?");
6025
+ parameters.push(
6026
+ values.attemptDiagnostic === null
6027
+ ? null
6028
+ : JSON.stringify(values.attemptDiagnostic),
6029
+ );
6030
+ }
5916
6031
  if (values.routeId !== undefined) {
5917
6032
  assignments.push("route_id = ?");
5918
6033
  parameters.push(values.routeId);
@@ -6133,6 +6248,7 @@ export class WorkflowEngine {
6133
6248
  row.phase === "green" &&
6134
6249
  (row.pause_code === "red-artifact-constraint" ||
6135
6250
  persistedRedArtifactCorrection);
6251
+ let requestedContext = persistedContextRequest;
6136
6252
  let correctionContextRequest = redArtifactCorrection
6137
6253
  ? persistedContextRequest
6138
6254
  : undefined;
@@ -6194,7 +6310,8 @@ export class WorkflowEngine {
6194
6310
  state: "phase-running",
6195
6311
  phase,
6196
6312
  pauseCode: artifactCorrection?.code ?? null,
6197
- contextRequest: correctionContextRequest ?? null,
6313
+ contextRequest: requestedContext ?? null,
6314
+ attemptDiagnostic: null,
6198
6315
  queuePosition: null,
6199
6316
  },
6200
6317
  lease,
@@ -6230,6 +6347,7 @@ export class WorkflowEngine {
6230
6347
  : {}),
6231
6348
  ...(repair ? { repair } : {}),
6232
6349
  ...(artifactCorrection ? { artifactCorrection } : {}),
6350
+ ...(requestedContext ? { contextRequest: requestedContext } : {}),
6233
6351
  signal,
6234
6352
  onActivity: (event) => {
6235
6353
  const projection = this.#runStore.status(runId);
@@ -6268,12 +6386,51 @@ export class WorkflowEngine {
6268
6386
  ) {
6269
6387
  this.#setVerificationStatus(runId, outcome.verification, lease);
6270
6388
  }
6389
+ if (
6390
+ outcome.kind !== "phase-committed" &&
6391
+ outcome.kind !== "operation-cancelled" &&
6392
+ outcome.contextRequest
6393
+ ) {
6394
+ requestedContext = normalizeWorkflowContextRequest(
6395
+ outcome.contextRequest,
6396
+ );
6397
+ }
6398
+ if (
6399
+ outcome.kind !== "phase-committed" &&
6400
+ outcome.kind !== "operation-cancelled" &&
6401
+ outcome.attemptDiagnostic
6402
+ ) {
6403
+ const previous = row.attempt_diagnostic_json
6404
+ ? (JSON.parse(row.attempt_diagnostic_json) as SafeAttemptDiagnostic)
6405
+ : undefined;
6406
+ const fingerprint = hash(
6407
+ outcome.code,
6408
+ outcome.attemptDiagnostic.finalCategory ?? "unknown",
6409
+ outcome.attemptDiagnostic.schema ?? "unknown",
6410
+ JSON.stringify(outcome.attemptDiagnostic.identityMismatch ?? []),
6411
+ ).slice(0, 16);
6412
+ const sameFailure = previous?.fingerprint === fingerprint;
6413
+ const sameFailureCount = sameFailure
6414
+ ? (previous?.sameFailureCount ?? 1) + 1
6415
+ : 1;
6416
+ const attemptDiagnostic = {
6417
+ ...outcome.attemptDiagnostic,
6418
+ fingerprint,
6419
+ sameFailureCount,
6420
+ ...(sameFailureCount > 1
6421
+ ? { action: "rebind-or-revise-delivery" as const }
6422
+ : {}),
6423
+ };
6424
+ this.#setTask(runId, row.task_id, { attemptDiagnostic }, lease);
6425
+ row.attempt_diagnostic_json = JSON.stringify(attemptDiagnostic);
6426
+ }
6271
6427
  if (outcome.kind === "retryable" && outcome.retryPolicy === "artifact") {
6272
6428
  if (outcome.code === "red-artifact-constraint") {
6273
6429
  redArtifactCorrection = true;
6274
6430
  correctionContextRequest = outcome.contextRequest
6275
6431
  ? normalizeWorkflowContextRequest(outcome.contextRequest)
6276
6432
  : correctionContextRequest;
6433
+ requestedContext = correctionContextRequest ?? requestedContext;
6277
6434
  }
6278
6435
  artifactAttempts += 1;
6279
6436
  const maxAttempts = plan.verification.artifactCorrection.maxAttempts;
@@ -6287,7 +6444,7 @@ export class WorkflowEngine {
6287
6444
  pauseCode: redArtifactCorrection
6288
6445
  ? "red-artifact-constraint"
6289
6446
  : outcome.code,
6290
- contextRequest: correctionContextRequest ?? null,
6447
+ contextRequest: requestedContext ?? null,
6291
6448
  queuePosition: null,
6292
6449
  },
6293
6450
  lease,
@@ -6328,6 +6485,7 @@ export class WorkflowEngine {
6328
6485
  phase,
6329
6486
  pauseCode: null,
6330
6487
  contextRequest: null,
6488
+ attemptDiagnostic: null,
6331
6489
  queuePosition: null,
6332
6490
  },
6333
6491
  lease,
@@ -6336,6 +6494,7 @@ export class WorkflowEngine {
6336
6494
  }
6337
6495
  artifactAttempts = 0;
6338
6496
  redArtifactCorrection = false;
6497
+ requestedContext = undefined;
6339
6498
  correctionContextRequest = undefined;
6340
6499
  phaseIndex += 1;
6341
6500
  if (phaseIndex >= orderedPhases.length) {
@@ -6347,6 +6506,7 @@ export class WorkflowEngine {
6347
6506
  phase,
6348
6507
  pauseCode: null,
6349
6508
  contextRequest: null,
6509
+ attemptDiagnostic: null,
6350
6510
  queuePosition: null,
6351
6511
  },
6352
6512
  lease,
@@ -6361,6 +6521,7 @@ export class WorkflowEngine {
6361
6521
  phase: orderedPhases[phaseIndex],
6362
6522
  pauseCode: null,
6363
6523
  contextRequest: null,
6524
+ attemptDiagnostic: null,
6364
6525
  queuePosition: null,
6365
6526
  },
6366
6527
  lease,
@@ -6402,10 +6563,9 @@ export class WorkflowEngine {
6402
6563
  pauseCode: approvalCodeInvalid
6403
6564
  ? "approval-code-invalid"
6404
6565
  : outcome.code,
6405
- contextRequest:
6406
- !approvalCodeInvalid && outcome.contextRequest
6407
- ? normalizeWorkflowContextRequest(outcome.contextRequest)
6408
- : (correctionContextRequest ?? null),
6566
+ contextRequest: approvalCodeInvalid
6567
+ ? null
6568
+ : (requestedContext ?? null),
6409
6569
  queuePosition: null,
6410
6570
  },
6411
6571
  lease,
@@ -7103,6 +7263,21 @@ export class WorkflowEngine {
7103
7263
  return this.#statusByRun(runId);
7104
7264
  }
7105
7265
 
7266
+ #safeAttemptDiagnostic(
7267
+ _runId: string,
7268
+ row: EngineTaskRow | undefined,
7269
+ ): SafeAttemptDiagnostic | undefined {
7270
+ if (!row) return undefined;
7271
+ if (row.attempt_diagnostic_json) {
7272
+ try {
7273
+ return JSON.parse(row.attempt_diagnostic_json) as SafeAttemptDiagnostic;
7274
+ } catch {
7275
+ return undefined;
7276
+ }
7277
+ }
7278
+ return undefined;
7279
+ }
7280
+
7106
7281
  #statusByRun(runId: string): Record<string, unknown> {
7107
7282
  const projection = this.#runStore.status(runId);
7108
7283
  const rows = this.#tasks(runId);
@@ -7135,9 +7310,9 @@ export class WorkflowEngine {
7135
7310
  projection.state === "approval-needed"
7136
7311
  ? approvalRequirement(pauseCode, contextRequest)
7137
7312
  : undefined;
7313
+ const attemptDiagnostic = this.#safeAttemptDiagnostic(runId, firstPaused);
7138
7314
  const currentRevision = projection.deliveryRevision ?? 0;
7139
7315
  return {
7140
- version: 2,
7141
7316
  runId: projection.runId,
7142
7317
  stage: projection.stage,
7143
7318
  ...(projection.change ? { change: projection.change } : {}),
@@ -7154,6 +7329,7 @@ export class WorkflowEngine {
7154
7329
  ? {
7155
7330
  pause: {
7156
7331
  code: pauseCode,
7332
+ ...(attemptDiagnostic ? { diagnostic: attemptDiagnostic } : {}),
7157
7333
  },
7158
7334
  }
7159
7335
  : {}),
@@ -7637,6 +7813,7 @@ export class WorkflowEngine {
7637
7813
  const rebound = this.#worker.rebind({
7638
7814
  runId,
7639
7815
  taskId: task.task_id,
7816
+ task: structuredClone(this.#taskPlan(task)),
7640
7817
  role: "implementation-worker",
7641
7818
  routeId: command.routeId,
7642
7819
  });
@@ -7875,7 +8052,18 @@ export class WorkflowEngine {
7875
8052
  );
7876
8053
  case "status": {
7877
8054
  const runId = this.#lookupRun(command.stage, command.change);
7878
- if (!runId) throw new Error("run-not-found");
8055
+ if (!runId) {
8056
+ return {
8057
+ stage: command.stage,
8058
+ change: command.change,
8059
+ state: "not-started",
8060
+ durable: true,
8061
+ completed: false,
8062
+ legalCommands: ["start"],
8063
+ tasks: [],
8064
+ queue: [],
8065
+ };
8066
+ }
7879
8067
  this.#recoverLegacyContextApproval(runId);
7880
8068
  return this.#withAvailableDelivery(this.#statusByRun(runId));
7881
8069
  }
@@ -7941,7 +8129,7 @@ export class WorkflowEngine {
7941
8129
  outcome.handoffId === existing.handoff_id &&
7942
8130
  outcome.runId === existing.owner_run_id &&
7943
8131
  outcome.receiptHash === existing.receipt_hash &&
7944
- outcome.selectorBeforeCutover === "v1-bootstrap" &&
8132
+ outcome.selectorBeforeCutover === "bootstrap" &&
7945
8133
  existing.state === "prepared" &&
7946
8134
  outcome.state === "prepared" &&
7947
8135
  outcome.selectorCasPending === true;
@@ -7962,7 +8150,7 @@ export class WorkflowEngine {
7962
8150
  if (
7963
8151
  existing.state !== "prepared" ||
7964
8152
  outcome.state !== "prepared" ||
7965
- outcome.selectorBeforeCutover !== "v1-bootstrap" ||
8153
+ outcome.selectorBeforeCutover !== "bootstrap" ||
7966
8154
  outcome.selectorCasPending !== true ||
7967
8155
  acceptanceFacts === undefined ||
7968
8156
  workspaceManifestHash === undefined
@@ -8019,7 +8207,7 @@ export class WorkflowEngine {
8019
8207
  acceptanceHash: input.acceptanceHash,
8020
8208
  ...(acceptanceFacts ? { acceptanceFacts } : {}),
8021
8209
  ...(workspaceManifestHash ? { workspaceManifestHash } : {}),
8022
- selectorBeforeCutover: "v1-bootstrap",
8210
+ selectorBeforeCutover: "bootstrap",
8023
8211
  selectorCasPending: true,
8024
8212
  };
8025
8213
  const preparedRows = this.#database
@@ -20,7 +20,6 @@ import { isValidRelativePath } from "./contracts.ts";
20
20
  import { observeSafePath } from "./safe-path.ts";
21
21
 
22
22
  const SHA256 = /^[a-f0-9]{64}$/u;
23
- const REVISION_VERSION = 2 as const;
24
23
 
25
24
  export interface WorkspaceFileEntry {
26
25
  kind: "file";
@@ -37,7 +36,6 @@ export type WorkspaceEntry = WorkspaceFileEntry | WorkspaceAbsentEntry;
37
36
  export type WorkspaceEntries = Record<string, WorkspaceEntry>;
38
37
 
39
38
  export interface WorkspaceRevision {
40
- version: typeof REVISION_VERSION;
41
39
  revisionId: string;
42
40
  parentRevisionId: string | null;
43
41
  manifestHash: string;
@@ -89,17 +87,13 @@ function stableEntries(entries: WorkspaceEntries): WorkspaceEntries {
89
87
 
90
88
  function manifestHash(entries: WorkspaceEntries): string {
91
89
  return hash(
92
- "cadence-workspace-manifest-v2",
90
+ "cadence-workspace-manifest",
93
91
  JSON.stringify(stableEntries(entries)),
94
92
  );
95
93
  }
96
94
 
97
95
  function revisionId(parentRevisionId: string | null, manifest: string): string {
98
- return hash(
99
- "cadence-workspace-revision-v2",
100
- parentRevisionId ?? "",
101
- manifest,
102
- );
96
+ return hash("cadence-workspace-revision", parentRevisionId ?? "", manifest);
103
97
  }
104
98
 
105
99
  function ensureDirectory(directory: string): void {
@@ -219,7 +213,6 @@ function requireRevisionShape(value: unknown): WorkspaceRevision {
219
213
  }
220
214
  const candidate = value as Partial<WorkspaceRevision>;
221
215
  if (
222
- candidate.version !== REVISION_VERSION ||
223
216
  typeof candidate.revisionId !== "string" ||
224
217
  !SHA256.test(candidate.revisionId) ||
225
218
  (candidate.parentRevisionId !== null &&
@@ -287,7 +280,6 @@ export class WorkspaceStore {
287
280
  const manifest = manifestHash(entries);
288
281
  const id = revisionId(parentRevisionId, manifest);
289
282
  const revision: WorkspaceRevision = {
290
- version: REVISION_VERSION,
291
283
  revisionId: id,
292
284
  parentRevisionId,
293
285
  manifestHash: manifest,