@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/design-control.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
compileImplementPlan,
|
|
16
16
|
compileReadyReceipt,
|
|
17
17
|
type DeliveryArtifactBinding,
|
|
18
|
+
DesignPlanValidationError,
|
|
18
19
|
type GateApprovalProof,
|
|
19
20
|
parseGateAReceipt,
|
|
20
21
|
parseImplementPlan,
|
|
@@ -88,6 +89,10 @@ export type DesignControlRequest =
|
|
|
88
89
|
operationId: string;
|
|
89
90
|
gate: "gate-b";
|
|
90
91
|
}
|
|
92
|
+
| {
|
|
93
|
+
operation: "validate-plan-draft";
|
|
94
|
+
runId: string;
|
|
95
|
+
}
|
|
91
96
|
| {
|
|
92
97
|
operation: "compile-plan";
|
|
93
98
|
runId: string;
|
|
@@ -198,7 +203,10 @@ export function validateDesignControlRequest(
|
|
|
198
203
|
? { ok: true, value: structuredClone(value) as DesignControlRequest }
|
|
199
204
|
: { ok: false, code: "invalid-design-control-request" };
|
|
200
205
|
}
|
|
201
|
-
if (
|
|
206
|
+
if (
|
|
207
|
+
value.operation === "status" ||
|
|
208
|
+
value.operation === "validate-plan-draft"
|
|
209
|
+
) {
|
|
202
210
|
return exactKeys(value, ["operation", "runId"]) &&
|
|
203
211
|
typeof value.runId === "string" &&
|
|
204
212
|
IDENTIFIER.test(value.runId)
|
|
@@ -448,12 +456,21 @@ export class DesignController {
|
|
|
448
456
|
private constructor(options: DesignControllerOptions) {
|
|
449
457
|
this.consumerRoot = path.resolve(options.consumerRoot);
|
|
450
458
|
this.#runs = RunStore.open(options.stateRoot);
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
459
|
+
try {
|
|
460
|
+
this.#journal = DesignJournal.open(options.stateRoot, {
|
|
461
|
+
...(options.now ? { now: options.now } : {}),
|
|
462
|
+
...(options.finalizationLeaseMs !== undefined
|
|
463
|
+
? { finalizationLeaseMs: options.finalizationLeaseMs }
|
|
464
|
+
: {}),
|
|
465
|
+
});
|
|
466
|
+
} catch (error) {
|
|
467
|
+
try {
|
|
468
|
+
this.#runs.close();
|
|
469
|
+
} catch {
|
|
470
|
+
// The Design journal initialization failure remains authoritative.
|
|
471
|
+
}
|
|
472
|
+
throw error;
|
|
473
|
+
}
|
|
457
474
|
this.#inspectOpenSpec = options.inspectOpenSpec;
|
|
458
475
|
}
|
|
459
476
|
|
|
@@ -508,6 +525,8 @@ export class DesignController {
|
|
|
508
525
|
return this.#journal.recordDecision(request);
|
|
509
526
|
case "approve-gate":
|
|
510
527
|
return this.#journal.approveGate(request);
|
|
528
|
+
case "validate-plan-draft":
|
|
529
|
+
return this.#validatePlanDraft(request);
|
|
511
530
|
case "compile-plan":
|
|
512
531
|
return this.#compilePlan(request);
|
|
513
532
|
case "finalize-delivery":
|
|
@@ -539,7 +558,7 @@ export class DesignController {
|
|
|
539
558
|
const canApproveGate =
|
|
540
559
|
!design.gates.gateA.current ||
|
|
541
560
|
(planReadyForGateB && !design.gates.gateB.current);
|
|
542
|
-
const
|
|
561
|
+
const legalOperations = terminal
|
|
543
562
|
? ["status"]
|
|
544
563
|
: [
|
|
545
564
|
"status",
|
|
@@ -549,7 +568,12 @@ export class DesignController {
|
|
|
549
568
|
? ["bind-change"]
|
|
550
569
|
: []),
|
|
551
570
|
...(design.gates.gateA.current && design.change
|
|
552
|
-
? [
|
|
571
|
+
? [
|
|
572
|
+
"write-artifact",
|
|
573
|
+
"delete-artifact",
|
|
574
|
+
"validate-plan-draft",
|
|
575
|
+
"compile-plan",
|
|
576
|
+
]
|
|
553
577
|
: []),
|
|
554
578
|
...(design.change &&
|
|
555
579
|
design.plan &&
|
|
@@ -557,7 +581,6 @@ export class DesignController {
|
|
|
557
581
|
design.gates.gateB.current
|
|
558
582
|
? ["finalize-delivery"]
|
|
559
583
|
: []),
|
|
560
|
-
"finish",
|
|
561
584
|
];
|
|
562
585
|
return {
|
|
563
586
|
runId: run.runId,
|
|
@@ -566,25 +589,40 @@ export class DesignController {
|
|
|
566
589
|
state: run.state,
|
|
567
590
|
completed: run.state === "completed",
|
|
568
591
|
...(run.pauseCode ? { pause: { code: run.pauseCode } } : {}),
|
|
569
|
-
|
|
592
|
+
legalOperations,
|
|
593
|
+
packetActions: terminal ? [] : ["finish"],
|
|
570
594
|
design,
|
|
571
595
|
};
|
|
572
596
|
}
|
|
573
597
|
|
|
574
598
|
#start(request: Extract<DesignControlRequest, { operation: "start" }>) {
|
|
575
599
|
const fromRequirement = "requirement" in request;
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
600
|
+
let run: ReturnType<RunStore["startRun"]>;
|
|
601
|
+
try {
|
|
602
|
+
run = this.#runs.startRun({
|
|
603
|
+
stage: "abel-design",
|
|
604
|
+
...(fromRequirement
|
|
605
|
+
? {
|
|
606
|
+
provisionalKey: transientHash(
|
|
607
|
+
"abel-design-requirement",
|
|
608
|
+
request.requirement,
|
|
609
|
+
),
|
|
610
|
+
provisionalAliases: [
|
|
611
|
+
transientHash(
|
|
612
|
+
"abel-design-requirement-v1",
|
|
613
|
+
request.requirement,
|
|
614
|
+
),
|
|
615
|
+
],
|
|
616
|
+
}
|
|
617
|
+
: { change: request.change }),
|
|
618
|
+
operationId: request.operationId,
|
|
619
|
+
});
|
|
620
|
+
} catch (error) {
|
|
621
|
+
if (error instanceof Error && error.message === "operation-id-conflict") {
|
|
622
|
+
throw new Error("design-operation-conflict", { cause: error });
|
|
623
|
+
}
|
|
624
|
+
throw error;
|
|
625
|
+
}
|
|
588
626
|
if (run.state === "created") {
|
|
589
627
|
this.#runs.transition({
|
|
590
628
|
runId: run.runId,
|
|
@@ -772,6 +810,51 @@ export class DesignController {
|
|
|
772
810
|
}
|
|
773
811
|
}
|
|
774
812
|
|
|
813
|
+
#readCompiledDraft(runId: string) {
|
|
814
|
+
const status = this.#journal.status(runId);
|
|
815
|
+
if (!status.change) throw new Error("design-change-required");
|
|
816
|
+
if (!status.gates.gateA.current) throw new Error("design-gate-a-required");
|
|
817
|
+
const draftPath = safeRelative(status.change, "plan-draft.json");
|
|
818
|
+
const draftBytes = readSafeFile(this.consumerRoot, draftPath);
|
|
819
|
+
let draft: unknown;
|
|
820
|
+
try {
|
|
821
|
+
draft = JSON.parse(draftBytes.toString("utf8"));
|
|
822
|
+
} catch {
|
|
823
|
+
throw new DesignPlanValidationError("design-plan-draft-invalid", [
|
|
824
|
+
{ code: "design-plan-draft-invalid", field: "plan-draft.json" },
|
|
825
|
+
]);
|
|
826
|
+
}
|
|
827
|
+
const compiled = compileImplementPlan(draft, {
|
|
828
|
+
consumerRoot: this.consumerRoot,
|
|
829
|
+
});
|
|
830
|
+
if (compiled.plan.changeId !== status.change) {
|
|
831
|
+
throw new DesignPlanValidationError("design-plan-change-mismatch", [
|
|
832
|
+
{ code: "design-plan-change-mismatch", field: "changeId" },
|
|
833
|
+
]);
|
|
834
|
+
}
|
|
835
|
+
return { status, compiled };
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
#validatePlanDraft(
|
|
839
|
+
request: Extract<
|
|
840
|
+
DesignControlRequest,
|
|
841
|
+
{ operation: "validate-plan-draft" }
|
|
842
|
+
>,
|
|
843
|
+
) {
|
|
844
|
+
const { compiled } = this.#readCompiledDraft(request.runId);
|
|
845
|
+
return {
|
|
846
|
+
operation: request.operation,
|
|
847
|
+
runId: request.runId,
|
|
848
|
+
valid: true,
|
|
849
|
+
plan: {
|
|
850
|
+
taskCount: compiled.plan.tasks.length,
|
|
851
|
+
outputCount: compiled.plan.outputs.length,
|
|
852
|
+
rawSha256: compiled.rawSha256,
|
|
853
|
+
canonicalHash: compiled.planHash,
|
|
854
|
+
},
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
|
|
775
858
|
#compilePlan(
|
|
776
859
|
request: Extract<DesignControlRequest, { operation: "compile-plan" }>,
|
|
777
860
|
) {
|
|
@@ -787,32 +870,17 @@ export class DesignController {
|
|
|
787
870
|
});
|
|
788
871
|
try {
|
|
789
872
|
this.#journal.assertNoPendingArtifactOperations(request.runId);
|
|
790
|
-
const status = this.#
|
|
791
|
-
|
|
792
|
-
if (!
|
|
793
|
-
|
|
794
|
-
const
|
|
795
|
-
const gateAPath = safeRelative(status.change, "gate-a.yaml");
|
|
873
|
+
const { status, compiled } = this.#readCompiledDraft(request.runId);
|
|
874
|
+
const change = status.change;
|
|
875
|
+
if (!change) throw new Error("design-change-required");
|
|
876
|
+
const readyPath = safeRelative(change, "ready.yaml");
|
|
877
|
+
const gateAPath = safeRelative(change, "gate-a.yaml");
|
|
796
878
|
removeSafeFile(this.consumerRoot, readyPath);
|
|
797
879
|
removeSafeFile(this.consumerRoot, gateAPath);
|
|
798
|
-
const draftPath = safeRelative(status.change, "plan-draft.json");
|
|
799
|
-
const draftBytes = readSafeFile(this.consumerRoot, draftPath);
|
|
800
|
-
let draft: unknown;
|
|
801
|
-
try {
|
|
802
|
-
draft = JSON.parse(draftBytes.toString("utf8"));
|
|
803
|
-
} catch {
|
|
804
|
-
throw new Error("design-plan-draft-invalid");
|
|
805
|
-
}
|
|
806
|
-
const compiled = compileImplementPlan(draft as never, {
|
|
807
|
-
consumerRoot: this.consumerRoot,
|
|
808
|
-
});
|
|
809
|
-
if (compiled.plan.changeId !== status.change) {
|
|
810
|
-
throw new Error("design-plan-change-mismatch");
|
|
811
|
-
}
|
|
812
880
|
this.#journal.assertFinalizationLease(lease);
|
|
813
881
|
atomicWrite(
|
|
814
882
|
this.consumerRoot,
|
|
815
|
-
safeRelative(
|
|
883
|
+
safeRelative(change, "implement-plan.json"),
|
|
816
884
|
compiled.bytes,
|
|
817
885
|
);
|
|
818
886
|
return this.#journal.recordCompiledPlan({
|
package/src/design-journal.ts
CHANGED
|
@@ -317,12 +317,21 @@ export class DesignJournal {
|
|
|
317
317
|
}
|
|
318
318
|
const stateRoot = prepareStateRoot(unresolved);
|
|
319
319
|
const database = new DatabaseSync(stateRoot.databasePath);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
320
|
+
try {
|
|
321
|
+
database.exec("PRAGMA foreign_keys = ON");
|
|
322
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
323
|
+
database.exec("PRAGMA synchronous = FULL");
|
|
324
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
325
|
+
database.exec(SCHEMA);
|
|
326
|
+
return new DesignJournal(stateRoot, database, options);
|
|
327
|
+
} catch (error) {
|
|
328
|
+
try {
|
|
329
|
+
database.close();
|
|
330
|
+
} catch {
|
|
331
|
+
// The Design journal initialization failure remains authoritative.
|
|
332
|
+
}
|
|
333
|
+
throw error;
|
|
334
|
+
}
|
|
326
335
|
}
|
|
327
336
|
|
|
328
337
|
#assertOpen(): void {
|
|
@@ -337,7 +346,11 @@ export class DesignJournal {
|
|
|
337
346
|
this.#database.exec("COMMIT");
|
|
338
347
|
return result;
|
|
339
348
|
} catch (error) {
|
|
340
|
-
|
|
349
|
+
try {
|
|
350
|
+
this.#database.exec("ROLLBACK");
|
|
351
|
+
} catch {
|
|
352
|
+
// The original operation or commit failure remains authoritative.
|
|
353
|
+
}
|
|
341
354
|
throw error;
|
|
342
355
|
}
|
|
343
356
|
}
|
|
@@ -899,7 +912,7 @@ export class DesignJournal {
|
|
|
899
912
|
}): Record<string, unknown> {
|
|
900
913
|
requireIdentifier(input.decisionId, "design-decision-id");
|
|
901
914
|
const decisionContractHash = contractHash(
|
|
902
|
-
"abel-design-decision
|
|
915
|
+
"abel-design-decision",
|
|
903
916
|
input.contract,
|
|
904
917
|
);
|
|
905
918
|
if (input.category !== "behavior" && input.category !== "technical") {
|
|
@@ -1024,7 +1037,7 @@ export class DesignJournal {
|
|
|
1024
1037
|
}
|
|
1025
1038
|
const gateAContractHash =
|
|
1026
1039
|
input.gate === "gate-a"
|
|
1027
|
-
? contractHash("abel-design-gate-a
|
|
1040
|
+
? contractHash("abel-design-gate-a", input.contract)
|
|
1028
1041
|
: undefined;
|
|
1029
1042
|
return this.#transaction(() => {
|
|
1030
1043
|
this.#run(input.runId, true);
|
package/src/file-snapshot.ts
CHANGED
|
@@ -56,7 +56,7 @@ function updateManifest(
|
|
|
56
56
|
|
|
57
57
|
function recursiveDirectoryManifest(abs: string): string {
|
|
58
58
|
const hash = createHash("sha256");
|
|
59
|
-
updateManifest(hash, "cadence-directory-manifest
|
|
59
|
+
updateManifest(hash, "cadence-directory-manifest");
|
|
60
60
|
const visit = (directory: string, prefix: string): void => {
|
|
61
61
|
const names = readdirSync(directory).sort();
|
|
62
62
|
for (const name of names) {
|