@abelxiaoxing/cadence 1.0.14 → 1.1.1
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 +39 -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 +206 -228
- package/prompts/abel-diagnose.md +39 -18
- package/prompts/abel-implement.md +122 -178
- package/prompts/abel-init.md +36 -33
- package/skills/abel-workflow/SKILL.md +190 -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 +121 -696
- package/src/control-contracts.ts +246 -0
- package/src/delivery-compiler.ts +1118 -0
- package/src/design-control.ts +846 -0
- package/src/design-journal.ts +1569 -0
- package/src/file-snapshot.ts +5 -0
- package/src/implement-graph.ts +11 -4
- package/src/index.ts +1833 -118
- 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 +430 -0
- package/src/run-store.ts +990 -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 +560 -45
- package/src/worker-broker.ts +703 -0
- package/src/workflow-engine.ts +7706 -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,20 @@
|
|
|
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_STAGES,
|
|
14
|
+
controlRunKey,
|
|
15
|
+
validateControlCommand,
|
|
16
|
+
} from "./control-contracts.ts";
|
|
17
|
+
|
|
3
18
|
export const STAGES = [
|
|
4
19
|
"abel-design",
|
|
5
20
|
"abel-implement",
|
|
@@ -11,7 +26,6 @@ export const ROLES = [
|
|
|
11
26
|
"implementation-worker",
|
|
12
27
|
"diagnosis-worker",
|
|
13
28
|
] as const;
|
|
14
|
-
export const ACTIONS = ["run", "apply", "discard", "cancel", "finish"] as const;
|
|
15
29
|
export const OUTPUT_KINDS = ["evidence", "diff"] as const;
|
|
16
30
|
export const PHASES = [
|
|
17
31
|
"evidence",
|
|
@@ -40,7 +54,6 @@ export type ImpactSurface = (typeof IMPACT_SURFACES)[number];
|
|
|
40
54
|
|
|
41
55
|
export const LIMITS = {
|
|
42
56
|
maxActiveChildSessions: 4,
|
|
43
|
-
maxRequestsPerBatch: 8,
|
|
44
57
|
maxEnvelopeBytes: 64 * 1024,
|
|
45
58
|
phaseTimeoutMs: 20 * 60 * 1000,
|
|
46
59
|
maxCompleteResultBytes: 64 * 1024,
|
|
@@ -132,8 +145,10 @@ export const APPROVAL_BOUNDARY_CODES = [
|
|
|
132
145
|
"behavior-contract-insufficient",
|
|
133
146
|
"architecture-contract-insufficient",
|
|
134
147
|
"task-scope-insufficient",
|
|
148
|
+
"conflict-resource-authority-insufficient",
|
|
135
149
|
"verification-contract-insufficient",
|
|
136
150
|
"agents-contract-insufficient",
|
|
151
|
+
"irreversible-scope-insufficient",
|
|
137
152
|
] as const;
|
|
138
153
|
export const VERIFICATION_ADAPTER_CODES = [
|
|
139
154
|
"input-missing",
|
|
@@ -159,7 +174,6 @@ export const FAILURE_STAGES = [
|
|
|
159
174
|
"structural-submit",
|
|
160
175
|
"candidate-retention",
|
|
161
176
|
"candidate-diff",
|
|
162
|
-
"candidate-preflight",
|
|
163
177
|
"parent-review",
|
|
164
178
|
"candidate-apply",
|
|
165
179
|
"agents-checkpoint",
|
|
@@ -205,12 +219,6 @@ export interface SafeFailureDetails {
|
|
|
205
219
|
verificationId?: string;
|
|
206
220
|
}
|
|
207
221
|
|
|
208
|
-
export interface SafeFailureDiagnostic {
|
|
209
|
-
code: ArtifactFailureCode | StaleFailureCode | ChildTransportCode;
|
|
210
|
-
stage: FailureStage;
|
|
211
|
-
details?: SafeFailureDetails;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
222
|
export type EnvironmentFailure = {
|
|
215
223
|
kind: "environment";
|
|
216
224
|
code: EnvironmentFailureCode;
|
|
@@ -241,48 +249,7 @@ export type ChildFailure =
|
|
|
241
249
|
details?: SafeFailureDetails;
|
|
242
250
|
};
|
|
243
251
|
|
|
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
252
|
const NONCANONICAL = /(^|\/)\.\.(\/|$)|(^|\/)\/|^\//;
|
|
285
|
-
const SNAPSHOT_SHA256 = /^[a-f0-9]{64}$/;
|
|
286
253
|
|
|
287
254
|
export type VerificationClassification =
|
|
288
255
|
| "expected-red"
|
|
@@ -371,6 +338,7 @@ export type VerificationInputBinding =
|
|
|
371
338
|
export interface PhaseBoundary {
|
|
372
339
|
read: string[];
|
|
373
340
|
write: string[];
|
|
341
|
+
delete: string[];
|
|
374
342
|
verification: VerificationContract;
|
|
375
343
|
verificationInputs: VerificationInputBinding[];
|
|
376
344
|
verificationLock?: string;
|
|
@@ -469,32 +437,10 @@ export type ImplementGraphReadinessDiagnostic =
|
|
|
469
437
|
verificationId?: string;
|
|
470
438
|
} & ImplementGraphDiagnosticDetails);
|
|
471
439
|
|
|
472
|
-
export interface
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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;
|
|
440
|
+
export interface PacketEnvelope {
|
|
441
|
+
stage: "abel-design" | "abel-diagnose";
|
|
442
|
+
role: "design-explorer" | "diagnosis-worker";
|
|
443
|
+
runId?: string;
|
|
498
444
|
id: string;
|
|
499
445
|
phase: string;
|
|
500
446
|
objective: string;
|
|
@@ -507,44 +453,7 @@ export interface RequestEnvelope {
|
|
|
507
453
|
resources: string[];
|
|
508
454
|
verificationLock?: string;
|
|
509
455
|
};
|
|
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;
|
|
456
|
+
output: "evidence" | "diff";
|
|
548
457
|
}
|
|
549
458
|
|
|
550
459
|
export function isValidRelativePath(p: unknown): p is string {
|
|
@@ -572,6 +481,7 @@ export function isAgentsPath(p: unknown): p is string {
|
|
|
572
481
|
const UNSAFE_VERIFICATION_TOKEN = /[;&|`$<>\n\r\0]/u;
|
|
573
482
|
const VERIFICATION_NAME = /^[a-z0-9][a-z0-9._:@/-]*$/iu;
|
|
574
483
|
const EXECUTABLE_NAME = /^[a-z0-9][a-z0-9._-]*$/iu;
|
|
484
|
+
const IDENTIFIER = /^[a-z0-9](?:[a-z0-9._:-]{0,126}[a-z0-9])?$/iu;
|
|
575
485
|
|
|
576
486
|
function validVerificationToken(value: unknown): value is string {
|
|
577
487
|
const optionValue =
|
|
@@ -615,9 +525,7 @@ function validScriptCommand(value: unknown): value is string {
|
|
|
615
525
|
|
|
616
526
|
function validVerificationIdentity(contract: Record<string, unknown>): boolean {
|
|
617
527
|
if (
|
|
618
|
-
|
|
619
|
-
contract.id.length === 0 ||
|
|
620
|
-
contract.id.length > 128 ||
|
|
528
|
+
!validIdentifier(contract.id) ||
|
|
621
529
|
!["expected-red", "expected-green", "expected-refactor"].includes(
|
|
622
530
|
String(contract.classification),
|
|
623
531
|
)
|
|
@@ -773,9 +681,7 @@ function validateStructuredVerification(
|
|
|
773
681
|
}
|
|
774
682
|
if (
|
|
775
683
|
!hasExactKeys(contract, ["kind", "id", "classification", "steps"]) ||
|
|
776
|
-
|
|
777
|
-
contract.id.length === 0 ||
|
|
778
|
-
contract.id.length > 128 ||
|
|
684
|
+
!validIdentifier(contract.id) ||
|
|
779
685
|
!["expected-red", "expected-green", "expected-refactor"].includes(
|
|
780
686
|
String(contract.classification),
|
|
781
687
|
) ||
|
|
@@ -852,77 +758,6 @@ export function cloneVerificationContract(
|
|
|
852
758
|
return structuredClone(normalized.value);
|
|
853
759
|
}
|
|
854
760
|
|
|
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
761
|
function validateApprovedDependencies(value: unknown): string | null {
|
|
927
762
|
if (
|
|
928
763
|
!Array.isArray(value) ||
|
|
@@ -1053,140 +888,6 @@ function isTestPath(path: string): boolean {
|
|
|
1053
888
|
return /^(?:test|tests)\//u.test(path);
|
|
1054
889
|
}
|
|
1055
890
|
|
|
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
891
|
function hasExactKeys(
|
|
1191
892
|
value: unknown,
|
|
1192
893
|
required: readonly string[],
|
|
@@ -1202,7 +903,7 @@ function hasExactKeys(
|
|
|
1202
903
|
}
|
|
1203
904
|
|
|
1204
905
|
function validIdentifier(value: unknown): value is string {
|
|
1205
|
-
return typeof value === "string" &&
|
|
906
|
+
return typeof value === "string" && IDENTIFIER.test(value);
|
|
1206
907
|
}
|
|
1207
908
|
|
|
1208
909
|
function validatePathSet(value: unknown): value is string[] {
|
|
@@ -1286,7 +987,7 @@ function validatePhaseBoundary(
|
|
|
1286
987
|
if (
|
|
1287
988
|
!hasExactKeys(
|
|
1288
989
|
value,
|
|
1289
|
-
["read", "write", "verification", "verificationInputs"],
|
|
990
|
+
["read", "write", "delete", "verification", "verificationInputs"],
|
|
1290
991
|
["verificationLock"],
|
|
1291
992
|
)
|
|
1292
993
|
) {
|
|
@@ -1295,8 +996,11 @@ function validatePhaseBoundary(
|
|
|
1295
996
|
if (
|
|
1296
997
|
!validatePathSet(value.read) ||
|
|
1297
998
|
!validatePathSet(value.write) ||
|
|
999
|
+
!validatePathSet(value.delete) ||
|
|
1298
1000
|
!validateVerificationInputBindings(value.verificationInputs) ||
|
|
1299
1001
|
value.write.some(isAgentsPath) ||
|
|
1002
|
+
value.delete.some(isAgentsPath) ||
|
|
1003
|
+
value.write.some((path) => (value.delete as string[]).includes(path)) ||
|
|
1300
1004
|
(value.verificationLock !== undefined &&
|
|
1301
1005
|
!isValidRelativePath(value.verificationLock))
|
|
1302
1006
|
) {
|
|
@@ -1375,6 +1079,7 @@ function validateTaskBoundary(
|
|
|
1375
1079
|
[
|
|
1376
1080
|
...phase.read,
|
|
1377
1081
|
...phase.write,
|
|
1082
|
+
...phase.delete,
|
|
1378
1083
|
...verificationInputPaths(phase.verification),
|
|
1379
1084
|
].some((path) => !isWithinRoots(path, roots)),
|
|
1380
1085
|
)
|
|
@@ -1394,7 +1099,9 @@ function validateTaskBoundary(
|
|
|
1394
1099
|
}
|
|
1395
1100
|
const declared = {
|
|
1396
1101
|
read: [...new Set(phases.flatMap((entry) => entry.read))],
|
|
1397
|
-
write: [
|
|
1102
|
+
write: [
|
|
1103
|
+
...new Set(phases.flatMap((entry) => [...entry.write, ...entry.delete])),
|
|
1104
|
+
],
|
|
1398
1105
|
};
|
|
1399
1106
|
return (
|
|
1400
1107
|
validateImpactClosure(value.impactClosure, declared, snapshot) === null
|
|
@@ -1518,173 +1225,14 @@ export function validateImplementGraphBoundary(value: unknown):
|
|
|
1518
1225
|
};
|
|
1519
1226
|
}
|
|
1520
1227
|
|
|
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(
|
|
1228
|
+
export function validatePacketEnvelope(
|
|
1594
1229
|
value: unknown,
|
|
1595
|
-
): { ok: true; value:
|
|
1596
|
-
|
|
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)) {
|
|
1230
|
+
): { ok: true; value: PacketEnvelope } | { ok: false; reason: string } {
|
|
1231
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1604
1232
|
return { ok: false, reason: "missing request envelope" };
|
|
1605
1233
|
}
|
|
1606
|
-
const
|
|
1607
|
-
|
|
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(
|
|
1681
|
-
value: unknown,
|
|
1682
|
-
): { ok: true; value: RequestEnvelope } | { ok: false; reason: string } {
|
|
1683
|
-
if (value === null || typeof value !== "object") {
|
|
1684
|
-
return { ok: false, reason: "missing request envelope" };
|
|
1685
|
-
}
|
|
1686
|
-
const env = value as Record<string, unknown>;
|
|
1687
|
-
for (const field of [
|
|
1234
|
+
const packet = value as Record<string, unknown>;
|
|
1235
|
+
const baseRequired = [
|
|
1688
1236
|
"stage",
|
|
1689
1237
|
"role",
|
|
1690
1238
|
"id",
|
|
@@ -1694,48 +1242,68 @@ function validateNonImplementRequestEnvelope(
|
|
|
1694
1242
|
"context",
|
|
1695
1243
|
"declared",
|
|
1696
1244
|
"output",
|
|
1697
|
-
]
|
|
1698
|
-
|
|
1245
|
+
] as const;
|
|
1246
|
+
const required =
|
|
1247
|
+
packet.stage === "abel-design"
|
|
1248
|
+
? ([
|
|
1249
|
+
...baseRequired.slice(0, 2),
|
|
1250
|
+
"runId",
|
|
1251
|
+
...baseRequired.slice(2),
|
|
1252
|
+
] as const)
|
|
1253
|
+
: baseRequired;
|
|
1254
|
+
for (const field of required) {
|
|
1255
|
+
if (packet[field] === undefined) {
|
|
1699
1256
|
return { ok: false, reason: `missing required field: ${field}` };
|
|
1700
1257
|
}
|
|
1701
1258
|
}
|
|
1702
|
-
if (!(
|
|
1703
|
-
return { ok: false, reason:
|
|
1259
|
+
if (!hasExactKeys(packet, required)) {
|
|
1260
|
+
return { ok: false, reason: "invalid packet fields" };
|
|
1704
1261
|
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1262
|
+
|
|
1263
|
+
if (packet.stage !== "abel-design" && packet.stage !== "abel-diagnose") {
|
|
1264
|
+
return {
|
|
1265
|
+
ok: false,
|
|
1266
|
+
reason: `unsupported packet stage: ${String(packet.stage)}`,
|
|
1267
|
+
};
|
|
1707
1268
|
}
|
|
1708
1269
|
if (
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
env.id.length > 128
|
|
1270
|
+
(packet.stage === "abel-design" && packet.role !== "design-explorer") ||
|
|
1271
|
+
(packet.stage === "abel-diagnose" && packet.role !== "diagnosis-worker")
|
|
1712
1272
|
) {
|
|
1713
|
-
return { ok: false, reason: "
|
|
1273
|
+
return { ok: false, reason: "packet role does not match stage" };
|
|
1274
|
+
}
|
|
1275
|
+
if (
|
|
1276
|
+
(packet.stage === "abel-design" && !validIdentifier(packet.runId)) ||
|
|
1277
|
+
(packet.stage === "abel-diagnose" && packet.runId !== undefined)
|
|
1278
|
+
) {
|
|
1279
|
+
return { ok: false, reason: "invalid Design run identity" };
|
|
1714
1280
|
}
|
|
1715
1281
|
if (
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
env.taskId.length > 128)
|
|
1282
|
+
typeof packet.id !== "string" ||
|
|
1283
|
+
packet.id.length === 0 ||
|
|
1284
|
+
packet.id.length > 128
|
|
1720
1285
|
) {
|
|
1721
|
-
return { ok: false, reason: "invalid
|
|
1286
|
+
return { ok: false, reason: "invalid request id" };
|
|
1722
1287
|
}
|
|
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
1288
|
if (
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
env.taskId === undefined
|
|
1289
|
+
typeof packet.phase !== "string" ||
|
|
1290
|
+
!(PHASES as readonly string[]).includes(packet.phase)
|
|
1729
1291
|
) {
|
|
1730
|
-
return { ok: false, reason:
|
|
1292
|
+
return { ok: false, reason: `invalid phase: ${String(packet.phase)}` };
|
|
1731
1293
|
}
|
|
1732
1294
|
if (
|
|
1733
|
-
typeof
|
|
1734
|
-
|
|
1295
|
+
typeof packet.objective !== "string" ||
|
|
1296
|
+
packet.objective.length === 0 ||
|
|
1297
|
+
packet.objective.length > 4096
|
|
1735
1298
|
) {
|
|
1736
|
-
return { ok: false, reason:
|
|
1299
|
+
return { ok: false, reason: "invalid objective" };
|
|
1300
|
+
}
|
|
1301
|
+
let serialized: string;
|
|
1302
|
+
try {
|
|
1303
|
+
serialized = JSON.stringify(packet);
|
|
1304
|
+
} catch {
|
|
1305
|
+
return { ok: false, reason: "request envelope is not serializable" };
|
|
1737
1306
|
}
|
|
1738
|
-
const serialized = JSON.stringify(env);
|
|
1739
1307
|
if (serialized.length > LIMITS.maxEnvelopeBytes) {
|
|
1740
1308
|
return {
|
|
1741
1309
|
ok: false,
|
|
@@ -1743,112 +1311,66 @@ function validateNonImplementRequestEnvelope(
|
|
|
1743
1311
|
};
|
|
1744
1312
|
}
|
|
1745
1313
|
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)
|
|
1314
|
+
!Array.isArray(packet.roots) ||
|
|
1315
|
+
packet.roots.length === 0 ||
|
|
1316
|
+
!packet.roots.every(isValidRelativePath)
|
|
1756
1317
|
) {
|
|
1757
1318
|
return { ok: false, reason: "invalid path roots" };
|
|
1758
1319
|
}
|
|
1759
|
-
const ctx = env.context as Record<string, unknown>;
|
|
1760
1320
|
if (
|
|
1761
|
-
|
|
1762
|
-
typeof
|
|
1763
|
-
typeof
|
|
1764
|
-
typeof ctx.contract !== "string"
|
|
1321
|
+
!hasExactKeys(packet.context, ["agents", "contract"]) ||
|
|
1322
|
+
typeof packet.context.agents !== "string" ||
|
|
1323
|
+
typeof packet.context.contract !== "string"
|
|
1765
1324
|
) {
|
|
1766
1325
|
return { ok: false, reason: "invalid context" };
|
|
1767
1326
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1327
|
+
if (
|
|
1328
|
+
!hasExactKeys(
|
|
1329
|
+
packet.declared,
|
|
1330
|
+
["read", "write", "conflicts", "resources"],
|
|
1331
|
+
["verificationLock"],
|
|
1332
|
+
)
|
|
1333
|
+
) {
|
|
1770
1334
|
return { ok: false, reason: "invalid declared sets" };
|
|
1771
1335
|
}
|
|
1772
|
-
for (const key of ["read", "write", "conflicts", "resources"]) {
|
|
1336
|
+
for (const key of ["read", "write", "conflicts", "resources"] as const) {
|
|
1337
|
+
const entries = packet.declared[key];
|
|
1773
1338
|
if (
|
|
1774
|
-
!Array.isArray(
|
|
1775
|
-
!
|
|
1339
|
+
!Array.isArray(entries) ||
|
|
1340
|
+
!entries.every(isValidRelativePath) ||
|
|
1341
|
+
new Set(entries).size !== entries.length
|
|
1776
1342
|
) {
|
|
1777
1343
|
return { ok: false, reason: `invalid declared ${key} set` };
|
|
1778
1344
|
}
|
|
1779
1345
|
}
|
|
1780
|
-
if ((
|
|
1346
|
+
if ((packet.declared.write as unknown[]).some(isAgentsPath)) {
|
|
1781
1347
|
return { ok: false, reason: "subagents cannot declare AGENTS writes" };
|
|
1782
1348
|
}
|
|
1783
1349
|
if (
|
|
1784
|
-
|
|
1785
|
-
!isValidRelativePath(
|
|
1350
|
+
packet.declared.verificationLock !== undefined &&
|
|
1351
|
+
!isValidRelativePath(packet.declared.verificationLock)
|
|
1786
1352
|
) {
|
|
1787
1353
|
return { ok: false, reason: "invalid verification lock" };
|
|
1788
1354
|
}
|
|
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
|
-
}
|
|
1355
|
+
if (!(OUTPUT_KINDS as readonly unknown[]).includes(packet.output)) {
|
|
1356
|
+
return {
|
|
1357
|
+
ok: false,
|
|
1358
|
+
reason: `invalid output kind: ${String(packet.output)}`,
|
|
1359
|
+
};
|
|
1844
1360
|
}
|
|
1845
1361
|
if (
|
|
1846
|
-
|
|
1847
|
-
(
|
|
1362
|
+
packet.stage === "abel-design" &&
|
|
1363
|
+
(packet.phase !== "evidence" ||
|
|
1364
|
+
packet.output !== "evidence" ||
|
|
1365
|
+
(packet.declared.write as unknown[]).length !== 0)
|
|
1848
1366
|
) {
|
|
1849
|
-
return { ok: false, reason: "invalid
|
|
1367
|
+
return { ok: false, reason: "invalid Design packet contract" };
|
|
1850
1368
|
}
|
|
1851
|
-
|
|
1369
|
+
|
|
1370
|
+
return {
|
|
1371
|
+
ok: true,
|
|
1372
|
+
value: structuredClone(packet) as unknown as PacketEnvelope,
|
|
1373
|
+
};
|
|
1852
1374
|
}
|
|
1853
1375
|
|
|
1854
1376
|
/** Extract proposed write paths from ordinary unified diff header pairs. */
|
|
@@ -2107,103 +1629,6 @@ export interface DiffResult {
|
|
|
2107
1629
|
contractCompliant: boolean;
|
|
2108
1630
|
}
|
|
2109
1631
|
|
|
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
1632
|
export function validateEvidenceResult(value: unknown): {
|
|
2208
1633
|
ok: boolean;
|
|
2209
1634
|
reason?: string;
|