@abelxiaoxing/cadence 1.3.0 → 1.4.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 +172 -13
- package/agents/design-explorer.md +17 -11
- package/agents/diagnosis-worker.md +9 -1
- package/agents/implementation-worker.md +3 -1
- package/config/plan-draft.example.json +122 -0
- package/config/plan-draft.multiple-tasks.example.json +345 -0
- package/package.json +9 -5
- package/prompts/abel-design.md +141 -38
- package/prompts/abel-diagnose.md +10 -1
- package/prompts/abel-implement.md +118 -20
- package/prompts/abel-init.md +9 -2
- package/src/activity-contracts.ts +82 -0
- package/src/apply-transaction.ts +16 -14
- package/src/artifact-store.ts +17 -0
- package/src/candidate-artifact.ts +143 -0
- package/src/candidate-patch.ts +33 -12
- package/src/candidate-workspace.ts +74 -0
- package/src/canonical.ts +16 -0
- package/src/change-contract-schema.ts +191 -0
- package/src/change-contract.ts +273 -0
- package/src/change-verification.ts +896 -0
- package/src/child-model.ts +129 -0
- package/src/child-session.ts +296 -328
- package/src/contracts.ts +325 -109
- package/src/control-contracts.ts +51 -1
- package/src/delivery-compiler.ts +241 -141
- package/src/delivery-revision.ts +42 -0
- package/src/design-control.ts +182 -15
- package/src/design-diagnostics.ts +150 -0
- package/src/design-journal.ts +216 -72
- package/src/durable-contracts.ts +210 -0
- package/src/durable-workflow.ts +963 -0
- package/src/evidence-draft.ts +122 -0
- package/src/implement-graph.ts +14 -16
- package/src/implement-plan.ts +70 -0
- package/src/index.ts +341 -1405
- package/src/isolation-backend.ts +84 -23
- package/src/model-source.ts +22 -0
- package/src/package-candidate.ts +224 -0
- package/src/package-delivery.ts +486 -0
- package/src/package-verification.ts +509 -0
- package/src/package-workflow.ts +253 -0
- package/src/packet-runtime.ts +27 -48
- package/src/parent-provider.ts +86 -198
- package/src/phase-execution.ts +2248 -0
- package/src/pi-adapter.ts +19 -0
- package/src/plan-draft-summary.ts +146 -0
- package/src/plan-draft.ts +493 -0
- package/src/route-policy.ts +1 -11
- package/src/run-state.ts +4 -16
- package/src/run-store.ts +5 -144
- package/src/scoped-grep-worker.mjs +23 -0
- package/src/scoped-tools.ts +312 -116
- package/src/sqlite-schema.ts +171 -0
- package/src/storage-schema.ts +352 -0
- package/src/subagent-activity.ts +46 -67
- package/src/submit-tool.ts +40 -61
- package/src/task-ledger.ts +14 -55
- package/src/transport-budget.ts +32 -0
- package/src/verification-capability.ts +298 -102
- package/src/verification-environment.ts +213 -0
- package/src/verification-identity.ts +77 -0
- package/src/worker-broker.ts +134 -91
- package/src/workflow-engine.ts +28 -8285
- package/src/workflow-policy.ts +1388 -0
- package/src/workflow-recovery-policy.ts +95 -0
- package/src/workflow-scheduling.ts +105 -0
- package/src/workflow-state-machine.ts +4086 -0
- package/src/workflow-status.ts +384 -0
- package/src/workspace-io-entry.mjs +58 -0
- package/src/workspace-io-worker.mjs +991 -0
- package/src/workspace-io.ts +84 -0
- package/src/workspace-store.ts +125 -19
- package/skills/abel-workflow/SKILL.md +0 -205
- package/src/empty-resource-loader.ts +0 -38
- package/src/parent-payload-bridge.ts +0 -523
|
@@ -9,13 +9,21 @@ import path from "node:path";
|
|
|
9
9
|
import {
|
|
10
10
|
type AtomicVerificationContract,
|
|
11
11
|
type StructuredVerificationContract,
|
|
12
|
+
VERIFICATION_CONFIGURATION_PATHS,
|
|
12
13
|
type VerificationAdapterCode,
|
|
13
14
|
validateVerificationContract,
|
|
15
|
+
verificationBoundInputPaths,
|
|
14
16
|
verificationInputPaths,
|
|
15
17
|
verificationSteps,
|
|
16
18
|
} from "./contracts.ts";
|
|
17
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
type Bound,
|
|
21
|
+
isCurrent,
|
|
22
|
+
snapshotFile,
|
|
23
|
+
snapshotFiles,
|
|
24
|
+
} from "./file-snapshot.ts";
|
|
18
25
|
import { isSafeRegularFile, observeSafePath } from "./safe-path.ts";
|
|
26
|
+
import { verificationEnvironmentDigest } from "./verification-identity.ts";
|
|
19
27
|
|
|
20
28
|
export type VerificationCapabilityDiagnostic =
|
|
21
29
|
| {
|
|
@@ -43,6 +51,7 @@ export interface CurrentVerificationCapability {
|
|
|
43
51
|
verificationId: string;
|
|
44
52
|
runnerBindings: VerificationRunnerBinding[];
|
|
45
53
|
inputSnapshot: Bound;
|
|
54
|
+
runnerSnapshot: { paths: string[]; digest: string };
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
export type CurrentVerificationCapabilityResult =
|
|
@@ -64,14 +73,12 @@ export interface VerificationRunnerEnvironment {
|
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
export interface VerificationCapabilityOptions {
|
|
76
|
+
/** Trusted plan write/delete authority, supplied only for isolated execution. */
|
|
77
|
+
executionWritePaths?: readonly string[];
|
|
67
78
|
dependencyOwner?: string;
|
|
68
79
|
runnerEnvironment?: VerificationRunnerEnvironment;
|
|
69
80
|
}
|
|
70
81
|
|
|
71
|
-
const SHELL_OPERATOR = /[;&|`$<>\n\r\0]/u;
|
|
72
|
-
const SIMPLE_TOKEN =
|
|
73
|
-
/^(?:[a-z0-9][a-z0-9._:@/=-]*|--?[a-z0-9][a-z0-9._:@/=-]*)$/iu;
|
|
74
|
-
const NETWORKING_RUNNERS = new Set(["bunx", "pnpx"]);
|
|
75
82
|
const WINDOWS_EXECUTABLE_EXTENSIONS = new Set([".exe", ".cmd"]);
|
|
76
83
|
const DEFAULT_WINDOWS_PATH_EXT = ".COM;.EXE;.BAT;.CMD";
|
|
77
84
|
const SYSTEM_MOUNT_ROOTS = ["/usr", "/bin", "/lib", "/lib64", "/etc"];
|
|
@@ -633,95 +640,13 @@ function packageManifest(root: string): Record<string, unknown> | null {
|
|
|
633
640
|
}
|
|
634
641
|
}
|
|
635
642
|
|
|
636
|
-
function validateScriptCommand(
|
|
637
|
-
root: string,
|
|
638
|
-
verificationId: string,
|
|
639
|
-
command: string,
|
|
640
|
-
dependencyOwner: string,
|
|
641
|
-
runnerEnvironment: VerificationRunnerEnvironment,
|
|
642
|
-
): VerificationCapabilityResult {
|
|
643
|
-
if (
|
|
644
|
-
command.length === 0 ||
|
|
645
|
-
command.trim() !== command ||
|
|
646
|
-
SHELL_OPERATOR.test(command)
|
|
647
|
-
) {
|
|
648
|
-
return adapterFailure(
|
|
649
|
-
verificationId,
|
|
650
|
-
"script-unsafe",
|
|
651
|
-
"approved package script contains unsupported shell syntax",
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
|
-
const tokens = command.split(/\s+/u);
|
|
655
|
-
if (!SIMPLE_TOKEN.test(tokens[0] ?? "")) {
|
|
656
|
-
return adapterFailure(
|
|
657
|
-
verificationId,
|
|
658
|
-
"script-unsafe",
|
|
659
|
-
"approved package script cannot be represented as safe tokens",
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
const executable = tokens[0] ?? "";
|
|
663
|
-
const subcommands = new Set(tokens.slice(1));
|
|
664
|
-
if (
|
|
665
|
-
NETWORKING_RUNNERS.has(executable) ||
|
|
666
|
-
(executable === "bun" && subcommands.has("x")) ||
|
|
667
|
-
(executable === "npm" &&
|
|
668
|
-
(subcommands.has("exec") || subcommands.has("x"))) ||
|
|
669
|
-
(executable === "pnpm" && subcommands.has("dlx")) ||
|
|
670
|
-
(executable === "yarn" && subcommands.has("dlx"))
|
|
671
|
-
) {
|
|
672
|
-
return adapterFailure(
|
|
673
|
-
verificationId,
|
|
674
|
-
"script-unsafe",
|
|
675
|
-
"package script uses a runner that can download implicitly",
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
if (executable === "npx") {
|
|
679
|
-
const local = tokens[1] === "--no-install" ? tokens[2] : undefined;
|
|
680
|
-
if (
|
|
681
|
-
!local ||
|
|
682
|
-
!localExecutable(root, local, dependencyOwner, runnerEnvironment)
|
|
683
|
-
) {
|
|
684
|
-
return adapterFailure(
|
|
685
|
-
verificationId,
|
|
686
|
-
"local-executable-missing",
|
|
687
|
-
"npx requires --no-install and an installed local executable",
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
const runner = resolveVerificationRunner("npx", runnerEnvironment);
|
|
691
|
-
return runner
|
|
692
|
-
? capabilitySuccess(verificationId, [runner])
|
|
693
|
-
: adapterFailure(
|
|
694
|
-
verificationId,
|
|
695
|
-
"runner-missing",
|
|
696
|
-
"npx is unavailable or cannot be mounted safely",
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
if (["node", "bun"].includes(executable)) {
|
|
700
|
-
const runner = resolveVerificationRunner(executable, runnerEnvironment);
|
|
701
|
-
return runner
|
|
702
|
-
? capabilitySuccess(verificationId, [runner])
|
|
703
|
-
: adapterFailure(
|
|
704
|
-
verificationId,
|
|
705
|
-
"runner-missing",
|
|
706
|
-
`approved runner ${executable} is unavailable or cannot be mounted safely`,
|
|
707
|
-
);
|
|
708
|
-
}
|
|
709
|
-
return localExecutable(root, executable, dependencyOwner, runnerEnvironment)
|
|
710
|
-
? capabilitySuccess(verificationId)
|
|
711
|
-
: adapterFailure(
|
|
712
|
-
verificationId,
|
|
713
|
-
"local-executable-missing",
|
|
714
|
-
`local executable ${executable} is unavailable`,
|
|
715
|
-
);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
643
|
function validatePackageScript(
|
|
719
644
|
root: string,
|
|
720
645
|
verificationId: string,
|
|
721
646
|
packageManager: string,
|
|
722
647
|
script: string,
|
|
723
648
|
approvedCommand: string,
|
|
724
|
-
|
|
649
|
+
_dependencyOwner = root,
|
|
725
650
|
runnerEnvironment: VerificationRunnerEnvironment = {},
|
|
726
651
|
): VerificationCapabilityResult {
|
|
727
652
|
const packageRunner = resolveVerificationRunner(
|
|
@@ -755,19 +680,91 @@ function validatePackageScript(
|
|
|
755
680
|
`package.json script ${script} differs from the Gate B contract`,
|
|
756
681
|
);
|
|
757
682
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
683
|
+
// Approved scripts are opaque shell programs and may invoke nested scripts
|
|
684
|
+
// or hooks. Supply the available supported runtimes without parsing shell
|
|
685
|
+
// syntax or rejecting composition; isolation still owns their execution.
|
|
686
|
+
const runtimes = ["node", "bun", "npm", "npx", "pnpm", "yarn"].flatMap(
|
|
687
|
+
(command) => {
|
|
688
|
+
const binding = resolveVerificationRunner(command, runnerEnvironment);
|
|
689
|
+
return binding ? [binding] : [];
|
|
690
|
+
},
|
|
764
691
|
);
|
|
765
|
-
return
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
692
|
+
return capabilitySuccess(verificationId, [packageRunner, ...runtimes]);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/** Inspect active directives, never comments or arbitrary words in values. */
|
|
696
|
+
function unsupportedExecutionConfiguration(source: string): boolean {
|
|
697
|
+
const restricted = new Set([
|
|
698
|
+
"scriptshell",
|
|
699
|
+
"userconfig",
|
|
700
|
+
"globalconfig",
|
|
701
|
+
"yarnpath",
|
|
702
|
+
"plugins",
|
|
703
|
+
"auth",
|
|
704
|
+
"authtoken",
|
|
705
|
+
"password",
|
|
706
|
+
"token",
|
|
707
|
+
"npmauthtoken",
|
|
708
|
+
"npmauthident",
|
|
709
|
+
]);
|
|
710
|
+
for (const line of source.split(/\r?\n/u)) {
|
|
711
|
+
let quote = "";
|
|
712
|
+
let escaped = false;
|
|
713
|
+
let active = "";
|
|
714
|
+
const parts: string[] = [];
|
|
715
|
+
let flowDepth = 0;
|
|
716
|
+
for (const character of line) {
|
|
717
|
+
if (escaped) {
|
|
718
|
+
active += character;
|
|
719
|
+
escaped = false;
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
if (character === "\\" && quote === '"') {
|
|
723
|
+
active += character;
|
|
724
|
+
escaped = true;
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
if (quote) {
|
|
728
|
+
active += character;
|
|
729
|
+
if (character === quote) quote = "";
|
|
730
|
+
} else if (character === '"' || character === "'") {
|
|
731
|
+
quote = character;
|
|
732
|
+
active += character;
|
|
733
|
+
} else if (
|
|
734
|
+
character === "#" ||
|
|
735
|
+
(character === ";" && active.trim() === "")
|
|
736
|
+
) {
|
|
737
|
+
break;
|
|
738
|
+
} else if (
|
|
739
|
+
character === "{" ||
|
|
740
|
+
character === "}" ||
|
|
741
|
+
(character === "," && flowDepth > 0)
|
|
742
|
+
) {
|
|
743
|
+
if (character === "{") flowDepth++;
|
|
744
|
+
if (character === "}") flowDepth = Math.max(0, flowDepth - 1);
|
|
745
|
+
parts.push(active);
|
|
746
|
+
active = "";
|
|
747
|
+
} else active += character;
|
|
748
|
+
}
|
|
749
|
+
parts.push(active);
|
|
750
|
+
for (const part of parts) {
|
|
751
|
+
if (part.includes("${")) return true;
|
|
752
|
+
const directive = part.match(
|
|
753
|
+
/^\s*(?:"((?:\\.|[^"\\])*)"|'([^']*)'|([^\s=]+))\s*(.*)$/u,
|
|
754
|
+
);
|
|
755
|
+
if (!directive) continue;
|
|
756
|
+
const rawKey = directive[1] ?? directive[2] ?? directive[3];
|
|
757
|
+
if (!rawKey.endsWith(":") && !directive[4]) continue;
|
|
758
|
+
const key = rawKey
|
|
759
|
+
.replace(/:$/u, "")
|
|
760
|
+
.split(":")
|
|
761
|
+
.at(-1)
|
|
762
|
+
?.replace(/[-_]/gu, "")
|
|
763
|
+
.toLowerCase();
|
|
764
|
+
if (key && restricted.has(key)) return true;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return false;
|
|
771
768
|
}
|
|
772
769
|
|
|
773
770
|
function validateAtomicCapability(
|
|
@@ -775,7 +772,73 @@ function validateAtomicCapability(
|
|
|
775
772
|
step: AtomicVerificationContract,
|
|
776
773
|
dependencyOwner: string,
|
|
777
774
|
runnerEnvironment: VerificationRunnerEnvironment,
|
|
775
|
+
executionWritePaths: readonly string[],
|
|
778
776
|
): VerificationCapabilityResult {
|
|
777
|
+
if (
|
|
778
|
+
step.kind === "vitest" &&
|
|
779
|
+
[
|
|
780
|
+
...step.args,
|
|
781
|
+
...(step.runner.kind === "package-script"
|
|
782
|
+
? step.runner.command.split(/\s+/u)
|
|
783
|
+
: []),
|
|
784
|
+
].some((arg) => /^--(?:reporters?|outputFile)(?:[.=]|$)/u.test(arg))
|
|
785
|
+
) {
|
|
786
|
+
return adapterFailure(
|
|
787
|
+
step.id,
|
|
788
|
+
"report-arguments-conflict",
|
|
789
|
+
"Vitest report arguments are owned by the adapter",
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
if (step.executionBindings) {
|
|
793
|
+
for (const [relative, expected] of Object.entries(step.executionBindings)) {
|
|
794
|
+
if (executionWritePaths.includes(relative)) continue;
|
|
795
|
+
const observation = observeSafePath(root, relative);
|
|
796
|
+
if (
|
|
797
|
+
(expected === null && observation.kind !== "absent") ||
|
|
798
|
+
(expected !== null && snapshotFile(root, relative)?.sha256 !== expected)
|
|
799
|
+
) {
|
|
800
|
+
return adapterFailure(
|
|
801
|
+
step.id,
|
|
802
|
+
"verification-config-mismatch",
|
|
803
|
+
"Bound execution inputs changed; recompile the verification contract",
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
for (const relative of verificationBoundInputPaths(step).filter((file) =>
|
|
809
|
+
VERIFICATION_CONFIGURATION_PATHS.includes(file),
|
|
810
|
+
)) {
|
|
811
|
+
const observation = observeSafePath(root, relative);
|
|
812
|
+
if (!["file", "absent"].includes(observation.kind))
|
|
813
|
+
return adapterFailure(
|
|
814
|
+
step.id,
|
|
815
|
+
"verification-config-unsafe",
|
|
816
|
+
"Execution configuration must be a regular file",
|
|
817
|
+
);
|
|
818
|
+
if (
|
|
819
|
+
observation.kind !== "file" ||
|
|
820
|
+
![
|
|
821
|
+
".npmrc",
|
|
822
|
+
".yarnrc",
|
|
823
|
+
".yarnrc.yml",
|
|
824
|
+
"bunfig.toml",
|
|
825
|
+
"pnpm-workspace.yaml",
|
|
826
|
+
].includes(relative)
|
|
827
|
+
)
|
|
828
|
+
continue;
|
|
829
|
+
if (
|
|
830
|
+
lstatSync(path.join(root, relative)).size > 64 * 1024 ||
|
|
831
|
+
unsupportedExecutionConfiguration(
|
|
832
|
+
readFileSync(path.join(root, relative), "utf8"),
|
|
833
|
+
)
|
|
834
|
+
) {
|
|
835
|
+
return adapterFailure(
|
|
836
|
+
step.id,
|
|
837
|
+
"verification-config-unsafe",
|
|
838
|
+
"Execution configuration requires unsupported credentials or host configuration",
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
779
842
|
if (step.kind === "package-script") {
|
|
780
843
|
return validatePackageScript(
|
|
781
844
|
root,
|
|
@@ -862,6 +925,57 @@ function validateAtomicCapability(
|
|
|
862
925
|
return capabilitySuccess(step.id);
|
|
863
926
|
}
|
|
864
927
|
|
|
928
|
+
/** Enrich only a new design draft, never rewrite proof-bound canonical delivery bytes. */
|
|
929
|
+
export function bindDraftVerificationInputs(
|
|
930
|
+
root: string,
|
|
931
|
+
draft: unknown,
|
|
932
|
+
): unknown {
|
|
933
|
+
const visit = (value: unknown): unknown => {
|
|
934
|
+
if (Array.isArray(value)) return value.map(visit);
|
|
935
|
+
if (!value || typeof value !== "object") return value;
|
|
936
|
+
const item = Object.fromEntries(
|
|
937
|
+
Object.entries(value).map(([key, child]) => [
|
|
938
|
+
key,
|
|
939
|
+
// Gate A is immutable approval authority, not an execution contract.
|
|
940
|
+
key === "changeContract" ? structuredClone(child) : visit(child),
|
|
941
|
+
]),
|
|
942
|
+
);
|
|
943
|
+
if (
|
|
944
|
+
item.kind === "package-script" &&
|
|
945
|
+
item.command === undefined &&
|
|
946
|
+
typeof item.script === "string"
|
|
947
|
+
) {
|
|
948
|
+
const scripts = packageManifest(root)?.scripts as
|
|
949
|
+
| Record<string, unknown>
|
|
950
|
+
| undefined;
|
|
951
|
+
item.command = scripts?.[item.script];
|
|
952
|
+
}
|
|
953
|
+
if (
|
|
954
|
+
["vitest", "package-script", "static-check"].includes(
|
|
955
|
+
String(item.kind),
|
|
956
|
+
) &&
|
|
957
|
+
typeof item.id === "string"
|
|
958
|
+
) {
|
|
959
|
+
const validation = validateVerificationContract(item);
|
|
960
|
+
if (
|
|
961
|
+
validation.ok &&
|
|
962
|
+
verificationBoundInputPaths(validation.value).some((file) =>
|
|
963
|
+
VERIFICATION_CONFIGURATION_PATHS.includes(file),
|
|
964
|
+
)
|
|
965
|
+
) {
|
|
966
|
+
item.executionBindings = Object.fromEntries(
|
|
967
|
+
VERIFICATION_CONFIGURATION_PATHS.map((relative) => [
|
|
968
|
+
relative,
|
|
969
|
+
snapshotFile(root, relative)?.sha256 ?? null,
|
|
970
|
+
]),
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
return item;
|
|
975
|
+
};
|
|
976
|
+
return visit(draft);
|
|
977
|
+
}
|
|
978
|
+
|
|
865
979
|
export function validateVerificationCapability(
|
|
866
980
|
root: string,
|
|
867
981
|
value: unknown,
|
|
@@ -882,6 +996,26 @@ export function bindCurrentVerificationCapability(
|
|
|
882
996
|
if (!validation.ok) {
|
|
883
997
|
throw new Error("verification-capability-invariant");
|
|
884
998
|
}
|
|
999
|
+
let paths: string[];
|
|
1000
|
+
let runnerDigest: string;
|
|
1001
|
+
try {
|
|
1002
|
+
paths = verificationRunnerFiles(
|
|
1003
|
+
options.dependencyOwner ?? root,
|
|
1004
|
+
validation.value,
|
|
1005
|
+
capability.runnerBindings,
|
|
1006
|
+
);
|
|
1007
|
+
runnerDigest = verificationEnvironmentDigest(paths);
|
|
1008
|
+
} catch {
|
|
1009
|
+
return {
|
|
1010
|
+
ok: false,
|
|
1011
|
+
diagnostic: {
|
|
1012
|
+
kind: "verification-adapter",
|
|
1013
|
+
verificationId: capability.verificationId,
|
|
1014
|
+
code: "runner-missing",
|
|
1015
|
+
message: "Installed verification runner changed or is unavailable",
|
|
1016
|
+
},
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
885
1019
|
return {
|
|
886
1020
|
ok: true,
|
|
887
1021
|
value: {
|
|
@@ -892,8 +1026,9 @@ export function bindCurrentVerificationCapability(
|
|
|
892
1026
|
})),
|
|
893
1027
|
inputSnapshot: snapshotFiles(
|
|
894
1028
|
root,
|
|
895
|
-
|
|
1029
|
+
verificationBoundInputPaths(validation.value),
|
|
896
1030
|
),
|
|
1031
|
+
runnerSnapshot: { paths, digest: runnerDigest },
|
|
897
1032
|
},
|
|
898
1033
|
};
|
|
899
1034
|
}
|
|
@@ -902,7 +1037,65 @@ export function isVerificationCapabilityCurrent(
|
|
|
902
1037
|
root: string,
|
|
903
1038
|
capability: CurrentVerificationCapability,
|
|
904
1039
|
): boolean {
|
|
905
|
-
|
|
1040
|
+
try {
|
|
1041
|
+
return (
|
|
1042
|
+
isCurrent(root, capability.inputSnapshot) &&
|
|
1043
|
+
verificationEnvironmentDigest(capability.runnerSnapshot.paths) ===
|
|
1044
|
+
capability.runnerSnapshot.digest
|
|
1045
|
+
);
|
|
1046
|
+
} catch {
|
|
1047
|
+
return false;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
export function verificationRunnerFiles(
|
|
1052
|
+
dependencyOwner: string,
|
|
1053
|
+
verification: StructuredVerificationContract,
|
|
1054
|
+
bindings: readonly VerificationRunnerBinding[],
|
|
1055
|
+
): string[] {
|
|
1056
|
+
const files = bindings.flatMap((binding) => [
|
|
1057
|
+
binding.executablePath,
|
|
1058
|
+
...(binding.fixedArgs ?? []).filter((arg) => path.isAbsolute(arg)),
|
|
1059
|
+
]);
|
|
1060
|
+
for (const file of [...new Set(files)]) {
|
|
1061
|
+
// Keep the launcher identity and explicitly include its resolved bytes:
|
|
1062
|
+
// the digest intentionally does not follow arbitrary dependency symlinks.
|
|
1063
|
+
const resolved = realpathSync(file);
|
|
1064
|
+
files.push(resolved);
|
|
1065
|
+
// System runners have no mountSource. Bind their installed package too,
|
|
1066
|
+
// including implementation modules and bundled dependencies beyond bin/.
|
|
1067
|
+
for (
|
|
1068
|
+
let directory = path.dirname(resolved);
|
|
1069
|
+
directory !== path.dirname(directory);
|
|
1070
|
+
directory = path.dirname(directory)
|
|
1071
|
+
) {
|
|
1072
|
+
if (
|
|
1073
|
+
lstatSync(path.join(directory, "package.json"), {
|
|
1074
|
+
throwIfNoEntry: false,
|
|
1075
|
+
})?.isFile()
|
|
1076
|
+
) {
|
|
1077
|
+
files.push(directory);
|
|
1078
|
+
break;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
for (const step of verificationSteps(verification)) {
|
|
1083
|
+
if (
|
|
1084
|
+
!("runner" in step) ||
|
|
1085
|
+
step.runner.kind === "node" ||
|
|
1086
|
+
step.runner.kind === "package-script"
|
|
1087
|
+
)
|
|
1088
|
+
continue;
|
|
1089
|
+
const executable =
|
|
1090
|
+
step.kind === "vitest" ? "vitest" : step.runner.executable;
|
|
1091
|
+
const file = path.join(dependencyOwner, "node_modules/.bin", executable);
|
|
1092
|
+
try {
|
|
1093
|
+
files.push(file, realpathSync(file));
|
|
1094
|
+
} catch {
|
|
1095
|
+
/* Admission owns missing runner diagnostics. */
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
return [...new Set(files)];
|
|
906
1099
|
}
|
|
907
1100
|
|
|
908
1101
|
export function validateVerificationAdapterCapability(
|
|
@@ -945,6 +1138,7 @@ function validateCapability(
|
|
|
945
1138
|
dependencyOwner,
|
|
946
1139
|
options.runnerEnvironment ?? {},
|
|
947
1140
|
requireInputAvailability,
|
|
1141
|
+
options.executionWritePaths ?? [],
|
|
948
1142
|
);
|
|
949
1143
|
}
|
|
950
1144
|
|
|
@@ -954,6 +1148,7 @@ function validateAcceptedVerificationCapability(
|
|
|
954
1148
|
dependencyOwner: string,
|
|
955
1149
|
runnerEnvironment: VerificationRunnerEnvironment,
|
|
956
1150
|
requireInputAvailability: boolean,
|
|
1151
|
+
executionWritePaths: readonly string[],
|
|
957
1152
|
): VerificationCapabilityResult {
|
|
958
1153
|
if (requireInputAvailability) {
|
|
959
1154
|
for (const input of verificationInputPaths(verification)) {
|
|
@@ -973,6 +1168,7 @@ function validateAcceptedVerificationCapability(
|
|
|
973
1168
|
step,
|
|
974
1169
|
dependencyOwner,
|
|
975
1170
|
runnerEnvironment,
|
|
1171
|
+
executionWritePaths,
|
|
976
1172
|
);
|
|
977
1173
|
if (!capability.ok) return capability;
|
|
978
1174
|
runnerBindings.push(...capability.runnerBindings);
|