@abelxiaoxing/cadence 1.0.14 → 1.1.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.
- package/README.md +29 -13
- package/agents/contract-reviewer.md +2 -2
- package/agents/design-explorer.md +1 -1
- package/agents/diagnosis-worker.md +6 -3
- package/agents/implementation-worker.md +14 -9
- package/config/.env.example +5 -17
- package/config/routes.example.json +42 -0
- package/package.json +3 -3
- package/prompts/abel-design.md +150 -228
- package/prompts/abel-diagnose.md +37 -18
- package/prompts/abel-implement.md +111 -178
- package/prompts/abel-init.md +36 -33
- package/skills/abel-workflow/SKILL.md +156 -168
- package/src/apply-transaction.ts +1427 -0
- package/src/artifact-store.ts +204 -0
- package/src/child-session.ts +136 -17
- package/src/contracts.ts +105 -696
- package/src/control-contracts.ts +259 -0
- package/src/delivery-compiler.ts +1088 -0
- package/src/file-snapshot.ts +5 -0
- package/src/implement-graph.ts +11 -4
- package/src/index.ts +1465 -133
- package/src/isolation-backend.ts +269 -0
- package/src/packet-runtime.ts +533 -0
- package/src/parent-provider.ts +23 -4
- package/src/route-policy.ts +483 -0
- package/src/run-state.ts +393 -0
- package/src/run-store.ts +918 -0
- package/src/safe-path.ts +11 -0
- package/src/scoped-tools.ts +36 -0
- package/src/state-root.ts +215 -0
- package/src/subagent-activity.ts +526 -27
- package/src/submit-tool.ts +274 -0
- package/src/task-ledger.ts +1380 -0
- package/src/verification-capability.ts +529 -42
- package/src/worker-broker.ts +703 -0
- package/src/workflow-engine.ts +7391 -0
- package/src/workspace-store.ts +544 -0
- package/src/candidate-preflight.ts +0 -1618
- package/src/drain.ts +0 -20
- package/src/patch.ts +0 -505
- package/src/result-store.ts +0 -312
- package/src/runtime.ts +0 -2436
- package/src/scheduler.ts +0 -384
- package/src/subagent-endpoint.ts +0 -158
- package/src/worker.ts +0 -204
package/src/contracts.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
// Strict request/result contracts for the private orchestration kernel.
|
|
2
2
|
// Structural validation only; no runtime dependency on a schema library.
|
|
3
|
+
|
|
4
|
+
export type {
|
|
5
|
+
ControlCommand,
|
|
6
|
+
ControlCommandName,
|
|
7
|
+
ControlCommandValidation,
|
|
8
|
+
ControlStage,
|
|
9
|
+
} from "./control-contracts.ts";
|
|
10
|
+
export {
|
|
11
|
+
assertControlCommand,
|
|
12
|
+
CONTROL_COMMANDS,
|
|
13
|
+
CONTROL_PROTOCOL_VERSION,
|
|
14
|
+
CONTROL_STAGES,
|
|
15
|
+
controlRunKey,
|
|
16
|
+
validateControlCommand,
|
|
17
|
+
} from "./control-contracts.ts";
|
|
18
|
+
|
|
3
19
|
export const STAGES = [
|
|
4
20
|
"abel-design",
|
|
5
21
|
"abel-implement",
|
|
@@ -11,7 +27,6 @@ export const ROLES = [
|
|
|
11
27
|
"implementation-worker",
|
|
12
28
|
"diagnosis-worker",
|
|
13
29
|
] as const;
|
|
14
|
-
export const ACTIONS = ["run", "apply", "discard", "cancel", "finish"] as const;
|
|
15
30
|
export const OUTPUT_KINDS = ["evidence", "diff"] as const;
|
|
16
31
|
export const PHASES = [
|
|
17
32
|
"evidence",
|
|
@@ -40,7 +55,6 @@ export type ImpactSurface = (typeof IMPACT_SURFACES)[number];
|
|
|
40
55
|
|
|
41
56
|
export const LIMITS = {
|
|
42
57
|
maxActiveChildSessions: 4,
|
|
43
|
-
maxRequestsPerBatch: 8,
|
|
44
58
|
maxEnvelopeBytes: 64 * 1024,
|
|
45
59
|
phaseTimeoutMs: 20 * 60 * 1000,
|
|
46
60
|
maxCompleteResultBytes: 64 * 1024,
|
|
@@ -159,7 +173,6 @@ export const FAILURE_STAGES = [
|
|
|
159
173
|
"structural-submit",
|
|
160
174
|
"candidate-retention",
|
|
161
175
|
"candidate-diff",
|
|
162
|
-
"candidate-preflight",
|
|
163
176
|
"parent-review",
|
|
164
177
|
"candidate-apply",
|
|
165
178
|
"agents-checkpoint",
|
|
@@ -205,12 +218,6 @@ export interface SafeFailureDetails {
|
|
|
205
218
|
verificationId?: string;
|
|
206
219
|
}
|
|
207
220
|
|
|
208
|
-
export interface SafeFailureDiagnostic {
|
|
209
|
-
code: ArtifactFailureCode | StaleFailureCode | ChildTransportCode;
|
|
210
|
-
stage: FailureStage;
|
|
211
|
-
details?: SafeFailureDetails;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
221
|
export type EnvironmentFailure = {
|
|
215
222
|
kind: "environment";
|
|
216
223
|
code: EnvironmentFailureCode;
|
|
@@ -241,48 +248,7 @@ export type ChildFailure =
|
|
|
241
248
|
details?: SafeFailureDetails;
|
|
242
249
|
};
|
|
243
250
|
|
|
244
|
-
export type CandidateRejection =
|
|
245
|
-
| {
|
|
246
|
-
kind: "artifact";
|
|
247
|
-
code: "parent-review-rejected";
|
|
248
|
-
stage: "parent-review";
|
|
249
|
-
}
|
|
250
|
-
| { kind: "approval-boundary"; code: ApprovalBoundaryCode };
|
|
251
|
-
|
|
252
|
-
export type TaskFailure =
|
|
253
|
-
| { kind: "approval-boundary"; code: ApprovalBoundaryCode }
|
|
254
|
-
| { kind: "verification-adapter"; code: VerificationAdapterCode }
|
|
255
|
-
| {
|
|
256
|
-
kind: "graph-readiness";
|
|
257
|
-
diagnostic: ImplementGraphReadinessDiagnostic;
|
|
258
|
-
}
|
|
259
|
-
| {
|
|
260
|
-
kind: "attempts-exhausted";
|
|
261
|
-
cause: "artifact" | "stale" | "transport";
|
|
262
|
-
attemptsUsed: 2;
|
|
263
|
-
lastFailure: SafeFailureDiagnostic;
|
|
264
|
-
}
|
|
265
|
-
| {
|
|
266
|
-
kind: "checkpoint-attempts-exhausted";
|
|
267
|
-
cause: "artifact" | "stale";
|
|
268
|
-
attemptsUsed: 2;
|
|
269
|
-
lastFailure: SafeFailureDiagnostic;
|
|
270
|
-
}
|
|
271
|
-
| EnvironmentFailure
|
|
272
|
-
| { kind: "result-limit"; limitBytes: number };
|
|
273
|
-
|
|
274
|
-
export interface CandidateApplyEvidence {
|
|
275
|
-
targets: string[];
|
|
276
|
-
checkExitCode: 0;
|
|
277
|
-
applyExitCode: 0;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
export type ApplyCandidateResult =
|
|
281
|
-
| { ok: true; result: CandidateApplyEvidence }
|
|
282
|
-
| { ok: false; failure: CandidateFailure };
|
|
283
|
-
|
|
284
251
|
const NONCANONICAL = /(^|\/)\.\.(\/|$)|(^|\/)\/|^\//;
|
|
285
|
-
const SNAPSHOT_SHA256 = /^[a-f0-9]{64}$/;
|
|
286
252
|
|
|
287
253
|
export type VerificationClassification =
|
|
288
254
|
| "expected-red"
|
|
@@ -371,6 +337,7 @@ export type VerificationInputBinding =
|
|
|
371
337
|
export interface PhaseBoundary {
|
|
372
338
|
read: string[];
|
|
373
339
|
write: string[];
|
|
340
|
+
delete: string[];
|
|
374
341
|
verification: VerificationContract;
|
|
375
342
|
verificationInputs: VerificationInputBinding[];
|
|
376
343
|
verificationLock?: string;
|
|
@@ -469,32 +436,9 @@ export type ImplementGraphReadinessDiagnostic =
|
|
|
469
436
|
verificationId?: string;
|
|
470
437
|
} & ImplementGraphDiagnosticDetails);
|
|
471
438
|
|
|
472
|
-
export interface
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
requestId: string;
|
|
476
|
-
phase: ImplementationPhase;
|
|
477
|
-
snapshot: unknown;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
export type ImplementRunRequest =
|
|
481
|
-
| {
|
|
482
|
-
stage: "abel-implement";
|
|
483
|
-
kind: "admit-graph";
|
|
484
|
-
graph: ImplementGraphBoundary;
|
|
485
|
-
graphHash: string;
|
|
486
|
-
state: { completedTasks: string[]; blockedTasks: string[] };
|
|
487
|
-
}
|
|
488
|
-
| {
|
|
489
|
-
stage: "abel-implement";
|
|
490
|
-
kind: "task-attempt";
|
|
491
|
-
attempt: PhaseAttempt;
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
export interface RequestEnvelope {
|
|
495
|
-
stage: string;
|
|
496
|
-
role: string;
|
|
497
|
-
taskId?: string;
|
|
439
|
+
export interface PacketEnvelope {
|
|
440
|
+
stage: "abel-design" | "abel-diagnose";
|
|
441
|
+
role: "design-explorer" | "diagnosis-worker";
|
|
498
442
|
id: string;
|
|
499
443
|
phase: string;
|
|
500
444
|
objective: string;
|
|
@@ -507,44 +451,7 @@ export interface RequestEnvelope {
|
|
|
507
451
|
resources: string[];
|
|
508
452
|
verificationLock?: string;
|
|
509
453
|
};
|
|
510
|
-
output:
|
|
511
|
-
agentsImpact?: AgentsImpact;
|
|
512
|
-
agentsTarget?: string;
|
|
513
|
-
agentsManagedOnly?: true;
|
|
514
|
-
approvedDependencies?: string[];
|
|
515
|
-
impactClosure?: ImpactClosureContract;
|
|
516
|
-
verification?: VerificationContract;
|
|
517
|
-
snapshot?: unknown;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
export type RunRequest = RequestEnvelope | ImplementRunRequest;
|
|
521
|
-
|
|
522
|
-
export interface AgentsCheckpointRequest {
|
|
523
|
-
stage: "abel-implement";
|
|
524
|
-
taskId: string;
|
|
525
|
-
agentsImpact: Exclude<AgentsImpact, "none">;
|
|
526
|
-
agentsTarget: string;
|
|
527
|
-
agentsManagedOnly: true;
|
|
528
|
-
stableCheckpoint: true;
|
|
529
|
-
snapshot: unknown;
|
|
530
|
-
diff: string;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
export interface AgentsCheckpointAttempt {
|
|
534
|
-
changeId: string;
|
|
535
|
-
taskId: string;
|
|
536
|
-
requestId: string;
|
|
537
|
-
snapshot: unknown;
|
|
538
|
-
diff: string;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
export interface ImplementApplyOperation {
|
|
542
|
-
resultId: string;
|
|
543
|
-
requestId: string;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
export interface ImplementDiscardOperation extends ImplementApplyOperation {
|
|
547
|
-
rejection: CandidateRejection;
|
|
454
|
+
output: "evidence" | "diff";
|
|
548
455
|
}
|
|
549
456
|
|
|
550
457
|
export function isValidRelativePath(p: unknown): p is string {
|
|
@@ -572,6 +479,7 @@ export function isAgentsPath(p: unknown): p is string {
|
|
|
572
479
|
const UNSAFE_VERIFICATION_TOKEN = /[;&|`$<>\n\r\0]/u;
|
|
573
480
|
const VERIFICATION_NAME = /^[a-z0-9][a-z0-9._:@/-]*$/iu;
|
|
574
481
|
const EXECUTABLE_NAME = /^[a-z0-9][a-z0-9._-]*$/iu;
|
|
482
|
+
const IDENTIFIER = /^[a-z0-9](?:[a-z0-9._:-]{0,126}[a-z0-9])?$/iu;
|
|
575
483
|
|
|
576
484
|
function validVerificationToken(value: unknown): value is string {
|
|
577
485
|
const optionValue =
|
|
@@ -615,9 +523,7 @@ function validScriptCommand(value: unknown): value is string {
|
|
|
615
523
|
|
|
616
524
|
function validVerificationIdentity(contract: Record<string, unknown>): boolean {
|
|
617
525
|
if (
|
|
618
|
-
|
|
619
|
-
contract.id.length === 0 ||
|
|
620
|
-
contract.id.length > 128 ||
|
|
526
|
+
!validIdentifier(contract.id) ||
|
|
621
527
|
!["expected-red", "expected-green", "expected-refactor"].includes(
|
|
622
528
|
String(contract.classification),
|
|
623
529
|
)
|
|
@@ -773,9 +679,7 @@ function validateStructuredVerification(
|
|
|
773
679
|
}
|
|
774
680
|
if (
|
|
775
681
|
!hasExactKeys(contract, ["kind", "id", "classification", "steps"]) ||
|
|
776
|
-
|
|
777
|
-
contract.id.length === 0 ||
|
|
778
|
-
contract.id.length > 128 ||
|
|
682
|
+
!validIdentifier(contract.id) ||
|
|
779
683
|
!["expected-red", "expected-green", "expected-refactor"].includes(
|
|
780
684
|
String(contract.classification),
|
|
781
685
|
) ||
|
|
@@ -852,77 +756,6 @@ export function cloneVerificationContract(
|
|
|
852
756
|
return structuredClone(normalized.value);
|
|
853
757
|
}
|
|
854
758
|
|
|
855
|
-
function validateImplementationSnapshot(
|
|
856
|
-
snapshot: unknown,
|
|
857
|
-
paths: string[],
|
|
858
|
-
): string | null {
|
|
859
|
-
if (
|
|
860
|
-
snapshot === undefined ||
|
|
861
|
-
snapshot === null ||
|
|
862
|
-
typeof snapshot !== "object" ||
|
|
863
|
-
Array.isArray(snapshot)
|
|
864
|
-
) {
|
|
865
|
-
return "invalid snapshot";
|
|
866
|
-
}
|
|
867
|
-
const bounds = snapshot as Record<string, unknown>;
|
|
868
|
-
for (const [path, value] of Object.entries(bounds)) {
|
|
869
|
-
if (
|
|
870
|
-
!isValidRelativePath(path) ||
|
|
871
|
-
value === null ||
|
|
872
|
-
typeof value !== "object" ||
|
|
873
|
-
Array.isArray(value)
|
|
874
|
-
) {
|
|
875
|
-
return "invalid snapshot";
|
|
876
|
-
}
|
|
877
|
-
const entry = value as Record<string, unknown>;
|
|
878
|
-
if (entry.kind === "file") {
|
|
879
|
-
if (
|
|
880
|
-
!hasExactKeys(entry, ["kind", "sha256", "bytes"]) ||
|
|
881
|
-
typeof entry.sha256 !== "string" ||
|
|
882
|
-
!SNAPSHOT_SHA256.test(entry.sha256) ||
|
|
883
|
-
typeof entry.bytes !== "number" ||
|
|
884
|
-
!Number.isSafeInteger(entry.bytes) ||
|
|
885
|
-
entry.bytes < 0
|
|
886
|
-
) {
|
|
887
|
-
return "invalid snapshot";
|
|
888
|
-
}
|
|
889
|
-
} else if (entry.kind === "dir") {
|
|
890
|
-
if (
|
|
891
|
-
!hasExactKeys(entry, ["kind", "manifest"]) ||
|
|
892
|
-
typeof entry.manifest !== "string" ||
|
|
893
|
-
!SNAPSHOT_SHA256.test(entry.manifest)
|
|
894
|
-
) {
|
|
895
|
-
return "invalid snapshot";
|
|
896
|
-
}
|
|
897
|
-
} else if (entry.kind === "absent") {
|
|
898
|
-
if (!hasExactKeys(entry, ["kind", "absent"]) || entry.absent !== true) {
|
|
899
|
-
return "invalid snapshot";
|
|
900
|
-
}
|
|
901
|
-
} else {
|
|
902
|
-
return "invalid snapshot";
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
if ([...new Set(paths)].some((path) => !Object.hasOwn(bounds, path))) {
|
|
906
|
-
return "snapshot does not cover declared paths";
|
|
907
|
-
}
|
|
908
|
-
return null;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
function validateAgentsContract(env: Record<string, unknown>): string | null {
|
|
912
|
-
if (!(AGENTS_IMPACTS as readonly unknown[]).includes(env.agentsImpact)) {
|
|
913
|
-
return "invalid AGENTS impact";
|
|
914
|
-
}
|
|
915
|
-
if (env.agentsManagedOnly !== true) return "AGENTS must be managed-only";
|
|
916
|
-
if (env.agentsImpact === "none") {
|
|
917
|
-
return env.agentsTarget === undefined
|
|
918
|
-
? null
|
|
919
|
-
: "none AGENTS impact cannot declare a target";
|
|
920
|
-
}
|
|
921
|
-
return isAgentsPath(env.agentsTarget)
|
|
922
|
-
? null
|
|
923
|
-
: "AGENTS impact requires an explicit AGENTS target";
|
|
924
|
-
}
|
|
925
|
-
|
|
926
759
|
function validateApprovedDependencies(value: unknown): string | null {
|
|
927
760
|
if (
|
|
928
761
|
!Array.isArray(value) ||
|
|
@@ -1053,140 +886,6 @@ function isTestPath(path: string): boolean {
|
|
|
1053
886
|
return /^(?:test|tests)\//u.test(path);
|
|
1054
887
|
}
|
|
1055
888
|
|
|
1056
|
-
export function validateAgentsCheckpointRequest(
|
|
1057
|
-
value: unknown,
|
|
1058
|
-
):
|
|
1059
|
-
| { ok: true; value: AgentsCheckpointRequest }
|
|
1060
|
-
| { ok: false; reason: string } {
|
|
1061
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1062
|
-
return { ok: false, reason: "missing AGENTS checkpoint request" };
|
|
1063
|
-
}
|
|
1064
|
-
const request = value as Record<string, unknown>;
|
|
1065
|
-
if (
|
|
1066
|
-
request.stage !== "abel-implement" ||
|
|
1067
|
-
typeof request.taskId !== "string" ||
|
|
1068
|
-
request.taskId.length === 0 ||
|
|
1069
|
-
request.taskId.length > 128 ||
|
|
1070
|
-
request.agentsImpact === "none" ||
|
|
1071
|
-
!(AGENTS_IMPACTS as readonly unknown[]).includes(request.agentsImpact) ||
|
|
1072
|
-
!isAgentsPath(request.agentsTarget) ||
|
|
1073
|
-
request.agentsManagedOnly !== true ||
|
|
1074
|
-
request.stableCheckpoint !== true ||
|
|
1075
|
-
typeof request.diff !== "string" ||
|
|
1076
|
-
request.diff.length === 0 ||
|
|
1077
|
-
request.diff.length > LIMITS.maxCompleteResultBytes
|
|
1078
|
-
) {
|
|
1079
|
-
return { ok: false, reason: "invalid AGENTS checkpoint contract" };
|
|
1080
|
-
}
|
|
1081
|
-
const snapshotReason = validateImplementationSnapshot(request.snapshot, [
|
|
1082
|
-
request.agentsTarget,
|
|
1083
|
-
] as string[]);
|
|
1084
|
-
if (snapshotReason !== null) {
|
|
1085
|
-
return { ok: false, reason: snapshotReason };
|
|
1086
|
-
}
|
|
1087
|
-
return {
|
|
1088
|
-
ok: true,
|
|
1089
|
-
value: request as unknown as AgentsCheckpointRequest,
|
|
1090
|
-
};
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
export function validateAgentsCheckpointAttempt(
|
|
1094
|
-
value: unknown,
|
|
1095
|
-
):
|
|
1096
|
-
| { ok: true; value: AgentsCheckpointAttempt }
|
|
1097
|
-
| { ok: false; reason: string } {
|
|
1098
|
-
if (
|
|
1099
|
-
!hasExactKeys(value, [
|
|
1100
|
-
"changeId",
|
|
1101
|
-
"taskId",
|
|
1102
|
-
"requestId",
|
|
1103
|
-
"snapshot",
|
|
1104
|
-
"diff",
|
|
1105
|
-
]) ||
|
|
1106
|
-
!validIdentifier(value.changeId) ||
|
|
1107
|
-
!validIdentifier(value.taskId) ||
|
|
1108
|
-
!validIdentifier(value.requestId) ||
|
|
1109
|
-
typeof value.diff !== "string" ||
|
|
1110
|
-
value.diff.length === 0 ||
|
|
1111
|
-
value.diff.length > LIMITS.maxCompleteResultBytes ||
|
|
1112
|
-
!value.snapshot ||
|
|
1113
|
-
typeof value.snapshot !== "object" ||
|
|
1114
|
-
Array.isArray(value.snapshot) ||
|
|
1115
|
-
Object.keys(value.snapshot).length === 0 ||
|
|
1116
|
-
validateImplementationSnapshot(
|
|
1117
|
-
value.snapshot,
|
|
1118
|
-
Object.keys(value.snapshot as Record<string, unknown>),
|
|
1119
|
-
) !== null
|
|
1120
|
-
) {
|
|
1121
|
-
return { ok: false, reason: "invalid AGENTS checkpoint attempt" };
|
|
1122
|
-
}
|
|
1123
|
-
return {
|
|
1124
|
-
ok: true,
|
|
1125
|
-
value: structuredClone(value) as unknown as AgentsCheckpointAttempt,
|
|
1126
|
-
};
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
export function validateImplementApplyOperation(
|
|
1130
|
-
value: unknown,
|
|
1131
|
-
):
|
|
1132
|
-
| { ok: true; value: ImplementApplyOperation }
|
|
1133
|
-
| { ok: false; reason: string } {
|
|
1134
|
-
if (
|
|
1135
|
-
!hasExactKeys(value, ["resultId", "requestId"]) ||
|
|
1136
|
-
!validIdentifier(value.resultId) ||
|
|
1137
|
-
!validIdentifier(value.requestId)
|
|
1138
|
-
) {
|
|
1139
|
-
return { ok: false, reason: "invalid Implement apply operation" };
|
|
1140
|
-
}
|
|
1141
|
-
return {
|
|
1142
|
-
ok: true,
|
|
1143
|
-
value: { resultId: value.resultId, requestId: value.requestId },
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
function validateCandidateRejection(
|
|
1148
|
-
value: unknown,
|
|
1149
|
-
): value is CandidateRejection {
|
|
1150
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1151
|
-
return false;
|
|
1152
|
-
}
|
|
1153
|
-
const rejection = value as Record<string, unknown>;
|
|
1154
|
-
if (rejection.kind === "approval-boundary") {
|
|
1155
|
-
return (
|
|
1156
|
-
hasExactKeys(rejection, ["kind", "code"]) &&
|
|
1157
|
-
(APPROVAL_BOUNDARY_CODES as readonly unknown[]).includes(rejection.code)
|
|
1158
|
-
);
|
|
1159
|
-
}
|
|
1160
|
-
if (
|
|
1161
|
-
rejection.kind !== "artifact" ||
|
|
1162
|
-
rejection.code !== "parent-review-rejected" ||
|
|
1163
|
-
rejection.stage !== "parent-review" ||
|
|
1164
|
-
!hasExactKeys(rejection, ["kind", "code", "stage"])
|
|
1165
|
-
) {
|
|
1166
|
-
return false;
|
|
1167
|
-
}
|
|
1168
|
-
return true;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
export function validateImplementDiscardOperation(
|
|
1172
|
-
value: unknown,
|
|
1173
|
-
):
|
|
1174
|
-
| { ok: true; value: ImplementDiscardOperation }
|
|
1175
|
-
| { ok: false; reason: string } {
|
|
1176
|
-
if (
|
|
1177
|
-
!hasExactKeys(value, ["resultId", "requestId", "rejection"]) ||
|
|
1178
|
-
!validIdentifier(value.resultId) ||
|
|
1179
|
-
!validIdentifier(value.requestId) ||
|
|
1180
|
-
!validateCandidateRejection(value.rejection)
|
|
1181
|
-
) {
|
|
1182
|
-
return { ok: false, reason: "invalid Implement discard operation" };
|
|
1183
|
-
}
|
|
1184
|
-
return {
|
|
1185
|
-
ok: true,
|
|
1186
|
-
value: structuredClone(value) as unknown as ImplementDiscardOperation,
|
|
1187
|
-
};
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
889
|
function hasExactKeys(
|
|
1191
890
|
value: unknown,
|
|
1192
891
|
required: readonly string[],
|
|
@@ -1202,7 +901,7 @@ function hasExactKeys(
|
|
|
1202
901
|
}
|
|
1203
902
|
|
|
1204
903
|
function validIdentifier(value: unknown): value is string {
|
|
1205
|
-
return typeof value === "string" &&
|
|
904
|
+
return typeof value === "string" && IDENTIFIER.test(value);
|
|
1206
905
|
}
|
|
1207
906
|
|
|
1208
907
|
function validatePathSet(value: unknown): value is string[] {
|
|
@@ -1286,7 +985,7 @@ function validatePhaseBoundary(
|
|
|
1286
985
|
if (
|
|
1287
986
|
!hasExactKeys(
|
|
1288
987
|
value,
|
|
1289
|
-
["read", "write", "verification", "verificationInputs"],
|
|
988
|
+
["read", "write", "delete", "verification", "verificationInputs"],
|
|
1290
989
|
["verificationLock"],
|
|
1291
990
|
)
|
|
1292
991
|
) {
|
|
@@ -1295,8 +994,11 @@ function validatePhaseBoundary(
|
|
|
1295
994
|
if (
|
|
1296
995
|
!validatePathSet(value.read) ||
|
|
1297
996
|
!validatePathSet(value.write) ||
|
|
997
|
+
!validatePathSet(value.delete) ||
|
|
1298
998
|
!validateVerificationInputBindings(value.verificationInputs) ||
|
|
1299
999
|
value.write.some(isAgentsPath) ||
|
|
1000
|
+
value.delete.some(isAgentsPath) ||
|
|
1001
|
+
value.write.some((path) => (value.delete as string[]).includes(path)) ||
|
|
1300
1002
|
(value.verificationLock !== undefined &&
|
|
1301
1003
|
!isValidRelativePath(value.verificationLock))
|
|
1302
1004
|
) {
|
|
@@ -1375,6 +1077,7 @@ function validateTaskBoundary(
|
|
|
1375
1077
|
[
|
|
1376
1078
|
...phase.read,
|
|
1377
1079
|
...phase.write,
|
|
1080
|
+
...phase.delete,
|
|
1378
1081
|
...verificationInputPaths(phase.verification),
|
|
1379
1082
|
].some((path) => !isWithinRoots(path, roots)),
|
|
1380
1083
|
)
|
|
@@ -1394,7 +1097,9 @@ function validateTaskBoundary(
|
|
|
1394
1097
|
}
|
|
1395
1098
|
const declared = {
|
|
1396
1099
|
read: [...new Set(phases.flatMap((entry) => entry.read))],
|
|
1397
|
-
write: [
|
|
1100
|
+
write: [
|
|
1101
|
+
...new Set(phases.flatMap((entry) => [...entry.write, ...entry.delete])),
|
|
1102
|
+
],
|
|
1398
1103
|
};
|
|
1399
1104
|
return (
|
|
1400
1105
|
validateImpactClosure(value.impactClosure, declared, snapshot) === null
|
|
@@ -1518,173 +1223,14 @@ export function validateImplementGraphBoundary(value: unknown):
|
|
|
1518
1223
|
};
|
|
1519
1224
|
}
|
|
1520
1225
|
|
|
1521
|
-
function
|
|
1522
|
-
if (
|
|
1523
|
-
!hasExactKeys(value, [
|
|
1524
|
-
"changeId",
|
|
1525
|
-
"taskId",
|
|
1526
|
-
"requestId",
|
|
1527
|
-
"phase",
|
|
1528
|
-
"snapshot",
|
|
1529
|
-
]) ||
|
|
1530
|
-
!validIdentifier(value.changeId) ||
|
|
1531
|
-
!validIdentifier(value.taskId) ||
|
|
1532
|
-
!validIdentifier(value.requestId) ||
|
|
1533
|
-
!["red", "green", "refactor"].includes(String(value.phase))
|
|
1534
|
-
) {
|
|
1535
|
-
return false;
|
|
1536
|
-
}
|
|
1537
|
-
if (
|
|
1538
|
-
!value.snapshot ||
|
|
1539
|
-
typeof value.snapshot !== "object" ||
|
|
1540
|
-
Array.isArray(value.snapshot)
|
|
1541
|
-
) {
|
|
1542
|
-
return false;
|
|
1543
|
-
}
|
|
1544
|
-
return (
|
|
1545
|
-
validateImplementationSnapshot(
|
|
1546
|
-
value.snapshot,
|
|
1547
|
-
Object.keys(value.snapshot as Record<string, unknown>),
|
|
1548
|
-
) === null
|
|
1549
|
-
);
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
export function validatePhaseAttemptAgainstBoundary(
|
|
1553
|
-
boundary: TaskBoundary,
|
|
1554
|
-
attempt: PhaseAttempt,
|
|
1555
|
-
): string | null {
|
|
1556
|
-
if (
|
|
1557
|
-
attempt.changeId !== boundary.changeId ||
|
|
1558
|
-
attempt.taskId !== boundary.taskId
|
|
1559
|
-
) {
|
|
1560
|
-
return "task attempt identity mismatch";
|
|
1561
|
-
}
|
|
1562
|
-
const phase = boundary.phases[attempt.phase];
|
|
1563
|
-
if (!phase) return "task attempt phase is not declared";
|
|
1564
|
-
const snapshotReason = validateImplementationSnapshot(attempt.snapshot, [
|
|
1565
|
-
...phase.read,
|
|
1566
|
-
...phase.write,
|
|
1567
|
-
]);
|
|
1568
|
-
if (snapshotReason !== null) return snapshotReason;
|
|
1569
|
-
const bounds = attempt.snapshot as Record<string, { kind?: unknown }>;
|
|
1570
|
-
if (
|
|
1571
|
-
phase.write.some(
|
|
1572
|
-
(path) =>
|
|
1573
|
-
bounds[path]?.kind !== "file" && bounds[path]?.kind !== "absent",
|
|
1574
|
-
)
|
|
1575
|
-
) {
|
|
1576
|
-
return "write snapshot must bind a regular file or absent path";
|
|
1577
|
-
}
|
|
1578
|
-
const phases = [
|
|
1579
|
-
boundary.phases.red,
|
|
1580
|
-
boundary.phases.green,
|
|
1581
|
-
...(boundary.phases.refactor ? [boundary.phases.refactor] : []),
|
|
1582
|
-
];
|
|
1583
|
-
return validateImpactClosure(
|
|
1584
|
-
boundary.impactClosure,
|
|
1585
|
-
{
|
|
1586
|
-
read: [...new Set(phases.flatMap((entry) => entry.read))],
|
|
1587
|
-
write: [...new Set(phases.flatMap((entry) => entry.write))],
|
|
1588
|
-
},
|
|
1589
|
-
attempt.snapshot,
|
|
1590
|
-
);
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
function validateImplementRunRequest(
|
|
1594
|
-
value: unknown,
|
|
1595
|
-
): { ok: true; value: ImplementRunRequest } | { ok: false; reason: string } {
|
|
1596
|
-
const serialized = JSON.stringify(value);
|
|
1597
|
-
if (serialized.length > LIMITS.maxEnvelopeBytes) {
|
|
1598
|
-
return {
|
|
1599
|
-
ok: false,
|
|
1600
|
-
reason: `request envelope exceeds ${LIMITS.maxEnvelopeBytes / 1024} KiB`,
|
|
1601
|
-
};
|
|
1602
|
-
}
|
|
1603
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1604
|
-
return { ok: false, reason: "missing request envelope" };
|
|
1605
|
-
}
|
|
1606
|
-
const request = value as Record<string, unknown>;
|
|
1607
|
-
if (request.kind === "admit-graph") {
|
|
1608
|
-
const state = request.state as Record<string, unknown>;
|
|
1609
|
-
if (
|
|
1610
|
-
!hasExactKeys(request, [
|
|
1611
|
-
"stage",
|
|
1612
|
-
"kind",
|
|
1613
|
-
"graph",
|
|
1614
|
-
"graphHash",
|
|
1615
|
-
"state",
|
|
1616
|
-
]) ||
|
|
1617
|
-
typeof request.graphHash !== "string" ||
|
|
1618
|
-
!SNAPSHOT_SHA256.test(request.graphHash) ||
|
|
1619
|
-
!hasExactKeys(state, ["completedTasks", "blockedTasks"]) ||
|
|
1620
|
-
!validateIdentifierSet(state.completedTasks) ||
|
|
1621
|
-
!validateIdentifierSet(state.blockedTasks)
|
|
1622
|
-
) {
|
|
1623
|
-
return { ok: false, reason: "invalid Implement graph admission" };
|
|
1624
|
-
}
|
|
1625
|
-
const completedTasks = state.completedTasks as string[];
|
|
1626
|
-
const blockedTasks = state.blockedTasks as string[];
|
|
1627
|
-
if (completedTasks.some((taskId) => blockedTasks.includes(taskId))) {
|
|
1628
|
-
return { ok: false, reason: "invalid Implement graph admission" };
|
|
1629
|
-
}
|
|
1630
|
-
const graph = validateImplementGraphBoundary(request.graph);
|
|
1631
|
-
if (!graph.ok) return { ok: false, reason: graph.reason };
|
|
1632
|
-
const taskIds = new Set(graph.value.tasks.map((task) => task.taskId));
|
|
1633
|
-
if (
|
|
1634
|
-
[...completedTasks, ...blockedTasks].some(
|
|
1635
|
-
(taskId) => !taskIds.has(taskId),
|
|
1636
|
-
)
|
|
1637
|
-
) {
|
|
1638
|
-
return { ok: false, reason: "unknown Implement graph task state" };
|
|
1639
|
-
}
|
|
1640
|
-
return {
|
|
1641
|
-
ok: true,
|
|
1642
|
-
value: {
|
|
1643
|
-
stage: "abel-implement",
|
|
1644
|
-
kind: "admit-graph",
|
|
1645
|
-
graph: graph.value,
|
|
1646
|
-
graphHash: request.graphHash,
|
|
1647
|
-
state: structuredClone({ completedTasks, blockedTasks }),
|
|
1648
|
-
},
|
|
1649
|
-
};
|
|
1650
|
-
}
|
|
1651
|
-
if (request.kind === "task-attempt") {
|
|
1652
|
-
if (
|
|
1653
|
-
!hasExactKeys(request, ["stage", "kind", "attempt"]) ||
|
|
1654
|
-
!validatePhaseAttemptShape(request.attempt)
|
|
1655
|
-
) {
|
|
1656
|
-
return { ok: false, reason: "invalid task attempt contract" };
|
|
1657
|
-
}
|
|
1658
|
-
return {
|
|
1659
|
-
ok: true,
|
|
1660
|
-
value: structuredClone(request) as ImplementRunRequest,
|
|
1661
|
-
};
|
|
1662
|
-
}
|
|
1663
|
-
return { ok: false, reason: "invalid Implement run kind" };
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
export function validateRequestEnvelope(
|
|
1667
|
-
value: unknown,
|
|
1668
|
-
): { ok: true; value: RunRequest } | { ok: false; reason: string } {
|
|
1669
|
-
if (
|
|
1670
|
-
value &&
|
|
1671
|
-
typeof value === "object" &&
|
|
1672
|
-
!Array.isArray(value) &&
|
|
1673
|
-
(value as Record<string, unknown>).stage === "abel-implement"
|
|
1674
|
-
) {
|
|
1675
|
-
return validateImplementRunRequest(value);
|
|
1676
|
-
}
|
|
1677
|
-
return validateNonImplementRequestEnvelope(value);
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
function validateNonImplementRequestEnvelope(
|
|
1226
|
+
export function validatePacketEnvelope(
|
|
1681
1227
|
value: unknown,
|
|
1682
|
-
): { ok: true; value:
|
|
1683
|
-
if (value === null || typeof value !== "object") {
|
|
1228
|
+
): { ok: true; value: PacketEnvelope } | { ok: false; reason: string } {
|
|
1229
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1684
1230
|
return { ok: false, reason: "missing request envelope" };
|
|
1685
1231
|
}
|
|
1686
|
-
const
|
|
1687
|
-
|
|
1232
|
+
const packet = value as Record<string, unknown>;
|
|
1233
|
+
const required = [
|
|
1688
1234
|
"stage",
|
|
1689
1235
|
"role",
|
|
1690
1236
|
"id",
|
|
@@ -1694,48 +1240,54 @@ function validateNonImplementRequestEnvelope(
|
|
|
1694
1240
|
"context",
|
|
1695
1241
|
"declared",
|
|
1696
1242
|
"output",
|
|
1697
|
-
]
|
|
1698
|
-
|
|
1243
|
+
] as const;
|
|
1244
|
+
for (const field of required) {
|
|
1245
|
+
if (packet[field] === undefined) {
|
|
1699
1246
|
return { ok: false, reason: `missing required field: ${field}` };
|
|
1700
1247
|
}
|
|
1701
1248
|
}
|
|
1702
|
-
if (!(
|
|
1703
|
-
return { ok: false, reason:
|
|
1249
|
+
if (!hasExactKeys(packet, required)) {
|
|
1250
|
+
return { ok: false, reason: "invalid packet fields" };
|
|
1704
1251
|
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1252
|
+
|
|
1253
|
+
if (packet.stage !== "abel-design" && packet.stage !== "abel-diagnose") {
|
|
1254
|
+
return {
|
|
1255
|
+
ok: false,
|
|
1256
|
+
reason: `unsupported packet stage: ${String(packet.stage)}`,
|
|
1257
|
+
};
|
|
1707
1258
|
}
|
|
1708
1259
|
if (
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
env.id.length > 128
|
|
1260
|
+
(packet.stage === "abel-design" && packet.role !== "design-explorer") ||
|
|
1261
|
+
(packet.stage === "abel-diagnose" && packet.role !== "diagnosis-worker")
|
|
1712
1262
|
) {
|
|
1713
|
-
return { ok: false, reason: "
|
|
1263
|
+
return { ok: false, reason: "packet role does not match stage" };
|
|
1714
1264
|
}
|
|
1715
1265
|
if (
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
env.taskId.length > 128)
|
|
1266
|
+
typeof packet.id !== "string" ||
|
|
1267
|
+
packet.id.length === 0 ||
|
|
1268
|
+
packet.id.length > 128
|
|
1720
1269
|
) {
|
|
1721
|
-
return { ok: false, reason: "invalid
|
|
1270
|
+
return { ok: false, reason: "invalid request id" };
|
|
1722
1271
|
}
|
|
1723
|
-
// Implementation phases use a phase-local request id and carry their stable
|
|
1724
|
-
// task identity explicitly. Other packets keep id as task id.
|
|
1725
1272
|
if (
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
env.taskId === undefined
|
|
1273
|
+
typeof packet.phase !== "string" ||
|
|
1274
|
+
!(PHASES as readonly string[]).includes(packet.phase)
|
|
1729
1275
|
) {
|
|
1730
|
-
return { ok: false, reason:
|
|
1276
|
+
return { ok: false, reason: `invalid phase: ${String(packet.phase)}` };
|
|
1731
1277
|
}
|
|
1732
1278
|
if (
|
|
1733
|
-
typeof
|
|
1734
|
-
|
|
1279
|
+
typeof packet.objective !== "string" ||
|
|
1280
|
+
packet.objective.length === 0 ||
|
|
1281
|
+
packet.objective.length > 4096
|
|
1735
1282
|
) {
|
|
1736
|
-
return { ok: false, reason:
|
|
1283
|
+
return { ok: false, reason: "invalid objective" };
|
|
1284
|
+
}
|
|
1285
|
+
let serialized: string;
|
|
1286
|
+
try {
|
|
1287
|
+
serialized = JSON.stringify(packet);
|
|
1288
|
+
} catch {
|
|
1289
|
+
return { ok: false, reason: "request envelope is not serializable" };
|
|
1737
1290
|
}
|
|
1738
|
-
const serialized = JSON.stringify(env);
|
|
1739
1291
|
if (serialized.length > LIMITS.maxEnvelopeBytes) {
|
|
1740
1292
|
return {
|
|
1741
1293
|
ok: false,
|
|
@@ -1743,112 +1295,66 @@ function validateNonImplementRequestEnvelope(
|
|
|
1743
1295
|
};
|
|
1744
1296
|
}
|
|
1745
1297
|
if (
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
) {
|
|
1750
|
-
return { ok: false, reason: "invalid objective" };
|
|
1751
|
-
}
|
|
1752
|
-
if (
|
|
1753
|
-
!Array.isArray(env.roots) ||
|
|
1754
|
-
env.roots.length === 0 ||
|
|
1755
|
-
!env.roots.every(isValidRelativePath)
|
|
1298
|
+
!Array.isArray(packet.roots) ||
|
|
1299
|
+
packet.roots.length === 0 ||
|
|
1300
|
+
!packet.roots.every(isValidRelativePath)
|
|
1756
1301
|
) {
|
|
1757
1302
|
return { ok: false, reason: "invalid path roots" };
|
|
1758
1303
|
}
|
|
1759
|
-
const ctx = env.context as Record<string, unknown>;
|
|
1760
1304
|
if (
|
|
1761
|
-
|
|
1762
|
-
typeof
|
|
1763
|
-
typeof
|
|
1764
|
-
typeof ctx.contract !== "string"
|
|
1305
|
+
!hasExactKeys(packet.context, ["agents", "contract"]) ||
|
|
1306
|
+
typeof packet.context.agents !== "string" ||
|
|
1307
|
+
typeof packet.context.contract !== "string"
|
|
1765
1308
|
) {
|
|
1766
1309
|
return { ok: false, reason: "invalid context" };
|
|
1767
1310
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1311
|
+
if (
|
|
1312
|
+
!hasExactKeys(
|
|
1313
|
+
packet.declared,
|
|
1314
|
+
["read", "write", "conflicts", "resources"],
|
|
1315
|
+
["verificationLock"],
|
|
1316
|
+
)
|
|
1317
|
+
) {
|
|
1770
1318
|
return { ok: false, reason: "invalid declared sets" };
|
|
1771
1319
|
}
|
|
1772
|
-
for (const key of ["read", "write", "conflicts", "resources"]) {
|
|
1320
|
+
for (const key of ["read", "write", "conflicts", "resources"] as const) {
|
|
1321
|
+
const entries = packet.declared[key];
|
|
1773
1322
|
if (
|
|
1774
|
-
!Array.isArray(
|
|
1775
|
-
!
|
|
1323
|
+
!Array.isArray(entries) ||
|
|
1324
|
+
!entries.every(isValidRelativePath) ||
|
|
1325
|
+
new Set(entries).size !== entries.length
|
|
1776
1326
|
) {
|
|
1777
1327
|
return { ok: false, reason: `invalid declared ${key} set` };
|
|
1778
1328
|
}
|
|
1779
1329
|
}
|
|
1780
|
-
if ((
|
|
1330
|
+
if ((packet.declared.write as unknown[]).some(isAgentsPath)) {
|
|
1781
1331
|
return { ok: false, reason: "subagents cannot declare AGENTS writes" };
|
|
1782
1332
|
}
|
|
1783
1333
|
if (
|
|
1784
|
-
|
|
1785
|
-
!isValidRelativePath(
|
|
1334
|
+
packet.declared.verificationLock !== undefined &&
|
|
1335
|
+
!isValidRelativePath(packet.declared.verificationLock)
|
|
1786
1336
|
) {
|
|
1787
1337
|
return { ok: false, reason: "invalid verification lock" };
|
|
1788
1338
|
}
|
|
1789
|
-
if (!(OUTPUT_KINDS as readonly
|
|
1790
|
-
return {
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
if (!verification.ok) {
|
|
1795
|
-
return {
|
|
1796
|
-
ok: false,
|
|
1797
|
-
reason: verification.reason,
|
|
1798
|
-
};
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
if (env.stage === "abel-implement" && env.output === "diff") {
|
|
1802
|
-
const agentsReason = validateAgentsContract(env);
|
|
1803
|
-
if (agentsReason !== null) return { ok: false, reason: agentsReason };
|
|
1804
|
-
const dependenciesReason = validateApprovedDependencies(
|
|
1805
|
-
env.approvedDependencies,
|
|
1806
|
-
);
|
|
1807
|
-
if (dependenciesReason !== null) {
|
|
1808
|
-
return { ok: false, reason: dependenciesReason };
|
|
1809
|
-
}
|
|
1810
|
-
const closureReason = validateImpactClosure(
|
|
1811
|
-
env.impactClosure,
|
|
1812
|
-
decl,
|
|
1813
|
-
env.snapshot,
|
|
1814
|
-
);
|
|
1815
|
-
if (closureReason !== null) return { ok: false, reason: closureReason };
|
|
1816
|
-
if (env.verification === undefined) {
|
|
1817
|
-
return { ok: false, reason: "missing required field: verification" };
|
|
1818
|
-
}
|
|
1819
|
-
const expectedClassification = {
|
|
1820
|
-
red: "expected-red",
|
|
1821
|
-
green: "expected-green",
|
|
1822
|
-
refactor: "expected-refactor",
|
|
1823
|
-
}[String(env.phase)];
|
|
1824
|
-
if (
|
|
1825
|
-
expectedClassification === undefined ||
|
|
1826
|
-
(env.verification as VerificationContract).classification !==
|
|
1827
|
-
expectedClassification
|
|
1828
|
-
) {
|
|
1829
|
-
return {
|
|
1830
|
-
ok: false,
|
|
1831
|
-
reason:
|
|
1832
|
-
"verification classification does not match implementation phase",
|
|
1833
|
-
};
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
if (env.stage === "abel-implement" && env.output === "diff") {
|
|
1837
|
-
const snapshotReason = validateImplementationSnapshot(env.snapshot, [
|
|
1838
|
-
...(decl.read as string[]),
|
|
1839
|
-
...(decl.write as string[]),
|
|
1840
|
-
]);
|
|
1841
|
-
if (snapshotReason !== null) {
|
|
1842
|
-
return { ok: false, reason: snapshotReason };
|
|
1843
|
-
}
|
|
1339
|
+
if (!(OUTPUT_KINDS as readonly unknown[]).includes(packet.output)) {
|
|
1340
|
+
return {
|
|
1341
|
+
ok: false,
|
|
1342
|
+
reason: `invalid output kind: ${String(packet.output)}`,
|
|
1343
|
+
};
|
|
1844
1344
|
}
|
|
1845
1345
|
if (
|
|
1846
|
-
|
|
1847
|
-
(
|
|
1346
|
+
packet.stage === "abel-design" &&
|
|
1347
|
+
(packet.phase !== "evidence" ||
|
|
1348
|
+
packet.output !== "evidence" ||
|
|
1349
|
+
(packet.declared.write as unknown[]).length !== 0)
|
|
1848
1350
|
) {
|
|
1849
|
-
return { ok: false, reason: "invalid
|
|
1351
|
+
return { ok: false, reason: "invalid Design packet contract" };
|
|
1850
1352
|
}
|
|
1851
|
-
|
|
1353
|
+
|
|
1354
|
+
return {
|
|
1355
|
+
ok: true,
|
|
1356
|
+
value: structuredClone(packet) as unknown as PacketEnvelope,
|
|
1357
|
+
};
|
|
1852
1358
|
}
|
|
1853
1359
|
|
|
1854
1360
|
/** Extract proposed write paths from ordinary unified diff header pairs. */
|
|
@@ -2107,103 +1613,6 @@ export interface DiffResult {
|
|
|
2107
1613
|
contractCompliant: boolean;
|
|
2108
1614
|
}
|
|
2109
1615
|
|
|
2110
|
-
export interface ApplyResult {
|
|
2111
|
-
targets: string[];
|
|
2112
|
-
checkExitCode: 0;
|
|
2113
|
-
applyExitCode: 0;
|
|
2114
|
-
sequence?: number;
|
|
2115
|
-
}
|
|
2116
|
-
|
|
2117
|
-
export interface AgentsCheckpointResult {
|
|
2118
|
-
target: string;
|
|
2119
|
-
agentsImpact: Exclude<TaskBoundary["agents"]["impact"], "none">;
|
|
2120
|
-
checkExitCode: 0;
|
|
2121
|
-
applyExitCode: 0;
|
|
2122
|
-
sequence?: number;
|
|
2123
|
-
}
|
|
2124
|
-
|
|
2125
|
-
export type ImplementOutcome =
|
|
2126
|
-
| {
|
|
2127
|
-
kind: "graph-admitted";
|
|
2128
|
-
changeId: string;
|
|
2129
|
-
graphHash: string;
|
|
2130
|
-
readyTasks: string[];
|
|
2131
|
-
blockedTasks: string[];
|
|
2132
|
-
}
|
|
2133
|
-
| {
|
|
2134
|
-
kind: "graph-rejected";
|
|
2135
|
-
changeId: string;
|
|
2136
|
-
graphHash: string;
|
|
2137
|
-
diagnostics: ImplementGraphReadinessDiagnostic[];
|
|
2138
|
-
}
|
|
2139
|
-
| {
|
|
2140
|
-
kind: "dependency-blocked";
|
|
2141
|
-
requestId: string;
|
|
2142
|
-
taskId: string;
|
|
2143
|
-
phase: ImplementationPhase;
|
|
2144
|
-
diagnostics: ImplementGraphReadinessDiagnostic[];
|
|
2145
|
-
}
|
|
2146
|
-
| {
|
|
2147
|
-
kind: "deferred";
|
|
2148
|
-
requestId: string;
|
|
2149
|
-
taskId: string;
|
|
2150
|
-
reason: "task-conflict";
|
|
2151
|
-
}
|
|
2152
|
-
| {
|
|
2153
|
-
kind: "candidate";
|
|
2154
|
-
requestId: string;
|
|
2155
|
-
taskId: string;
|
|
2156
|
-
phase: ImplementationPhase;
|
|
2157
|
-
resultId: string;
|
|
2158
|
-
result: DiffResult;
|
|
2159
|
-
}
|
|
2160
|
-
| {
|
|
2161
|
-
kind: "applied";
|
|
2162
|
-
requestId: string;
|
|
2163
|
-
taskId: string;
|
|
2164
|
-
phase: "red" | "green";
|
|
2165
|
-
readyPhase: "green" | "refactor";
|
|
2166
|
-
result: ApplyResult;
|
|
2167
|
-
}
|
|
2168
|
-
| {
|
|
2169
|
-
kind: "checkpoint-required";
|
|
2170
|
-
requestId: string;
|
|
2171
|
-
taskId: string;
|
|
2172
|
-
finalPhase: "green" | "refactor";
|
|
2173
|
-
result: ApplyResult;
|
|
2174
|
-
}
|
|
2175
|
-
| {
|
|
2176
|
-
kind: "retry";
|
|
2177
|
-
requestId: string;
|
|
2178
|
-
taskId: string;
|
|
2179
|
-
scope: "worker" | "checkpoint";
|
|
2180
|
-
phase: ImplementationPhase;
|
|
2181
|
-
cause: "artifact" | "stale";
|
|
2182
|
-
remainingAttempts: 1;
|
|
2183
|
-
lastFailure: SafeFailureDiagnostic;
|
|
2184
|
-
}
|
|
2185
|
-
| {
|
|
2186
|
-
kind: "completed";
|
|
2187
|
-
requestId: string;
|
|
2188
|
-
taskId: string;
|
|
2189
|
-
finalPhase: "green" | "refactor";
|
|
2190
|
-
result?: ApplyResult | AgentsCheckpointResult;
|
|
2191
|
-
}
|
|
2192
|
-
| {
|
|
2193
|
-
kind: "blocked";
|
|
2194
|
-
requestId: string;
|
|
2195
|
-
taskId: string;
|
|
2196
|
-
phase: ImplementationPhase;
|
|
2197
|
-
failure: TaskFailure;
|
|
2198
|
-
result?: ApplyResult;
|
|
2199
|
-
}
|
|
2200
|
-
| {
|
|
2201
|
-
kind: "cancelled";
|
|
2202
|
-
requestId: string;
|
|
2203
|
-
taskId: string;
|
|
2204
|
-
phase: ImplementationPhase;
|
|
2205
|
-
};
|
|
2206
|
-
|
|
2207
1616
|
export function validateEvidenceResult(value: unknown): {
|
|
2208
1617
|
ok: boolean;
|
|
2209
1618
|
reason?: string;
|