@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.
- package/README.md +7 -1
- package/agents/diagnosis-worker.md +6 -2
- package/config/routes.example.json +0 -1
- package/package.json +1 -1
- package/prompts/abel-design.md +15 -4
- package/skills/abel-workflow/SKILL.md +5 -3
- package/src/apply-transaction.ts +1 -5
- package/src/child-session.ts +37 -46
- package/src/contracts.ts +278 -32
- package/src/control-contracts.ts +62 -0
- package/src/delivery-compiler.ts +239 -124
- package/src/design-control.ts +112 -44
- package/src/design-journal.ts +22 -9
- package/src/file-snapshot.ts +1 -1
- package/src/index.ts +393 -52
- package/src/packet-runtime.ts +79 -21
- package/src/parent-provider.ts +63 -30
- package/src/route-policy.ts +58 -14
- package/src/run-store.ts +308 -37
- package/src/subagent-activity.ts +82 -6
- package/src/submit-tool.ts +182 -10
- package/src/task-ledger.ts +1 -2
- package/src/worker-broker.ts +72 -13
- package/src/workflow-engine.ts +219 -31
- package/src/workspace-store.ts +2 -10
package/src/workflow-engine.ts
CHANGED
|
@@ -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
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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,
|
|
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,
|
|
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:
|
|
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:
|
|
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
|
-
|
|
6407
|
-
|
|
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)
|
|
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 === "
|
|
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 !== "
|
|
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: "
|
|
8210
|
+
selectorBeforeCutover: "bootstrap",
|
|
8023
8211
|
selectorCasPending: true,
|
|
8024
8212
|
};
|
|
8025
8213
|
const preparedRows = this.#database
|
package/src/workspace-store.ts
CHANGED
|
@@ -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
|
|
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,
|