@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.
Files changed (46) hide show
  1. package/README.md +29 -13
  2. package/agents/contract-reviewer.md +2 -2
  3. package/agents/design-explorer.md +1 -1
  4. package/agents/diagnosis-worker.md +6 -3
  5. package/agents/implementation-worker.md +14 -9
  6. package/config/.env.example +5 -17
  7. package/config/routes.example.json +42 -0
  8. package/package.json +3 -3
  9. package/prompts/abel-design.md +150 -228
  10. package/prompts/abel-diagnose.md +37 -18
  11. package/prompts/abel-implement.md +111 -178
  12. package/prompts/abel-init.md +36 -33
  13. package/skills/abel-workflow/SKILL.md +156 -168
  14. package/src/apply-transaction.ts +1427 -0
  15. package/src/artifact-store.ts +204 -0
  16. package/src/child-session.ts +136 -17
  17. package/src/contracts.ts +105 -696
  18. package/src/control-contracts.ts +259 -0
  19. package/src/delivery-compiler.ts +1088 -0
  20. package/src/file-snapshot.ts +5 -0
  21. package/src/implement-graph.ts +11 -4
  22. package/src/index.ts +1465 -133
  23. package/src/isolation-backend.ts +269 -0
  24. package/src/packet-runtime.ts +533 -0
  25. package/src/parent-provider.ts +23 -4
  26. package/src/route-policy.ts +483 -0
  27. package/src/run-state.ts +393 -0
  28. package/src/run-store.ts +918 -0
  29. package/src/safe-path.ts +11 -0
  30. package/src/scoped-tools.ts +36 -0
  31. package/src/state-root.ts +215 -0
  32. package/src/subagent-activity.ts +526 -27
  33. package/src/submit-tool.ts +274 -0
  34. package/src/task-ledger.ts +1380 -0
  35. package/src/verification-capability.ts +529 -42
  36. package/src/worker-broker.ts +703 -0
  37. package/src/workflow-engine.ts +7391 -0
  38. package/src/workspace-store.ts +544 -0
  39. package/src/candidate-preflight.ts +0 -1618
  40. package/src/drain.ts +0 -20
  41. package/src/patch.ts +0 -505
  42. package/src/result-store.ts +0 -312
  43. package/src/runtime.ts +0 -2436
  44. package/src/scheduler.ts +0 -384
  45. package/src/subagent-endpoint.ts +0 -158
  46. package/src/worker.ts +0 -204
@@ -1,1618 +0,0 @@
1
- import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
2
- import { createHash } from "node:crypto";
3
- import {
4
- chmodSync,
5
- copyFileSync,
6
- existsSync,
7
- lstatSync,
8
- mkdirSync,
9
- mkdtempSync,
10
- readdirSync,
11
- readFileSync,
12
- realpathSync,
13
- rmSync,
14
- writeFileSync,
15
- } from "node:fs";
16
- import path from "node:path";
17
- import {
18
- type AtomicVerificationContract,
19
- type CandidateFailure,
20
- diffWritePaths,
21
- isValidRelativePath,
22
- type StructuredVerificationContract,
23
- type VerificationContract,
24
- validateVerificationContract,
25
- verificationInputPaths,
26
- verificationSteps,
27
- } from "./contracts.ts";
28
- import {
29
- type Bound,
30
- type DirBound,
31
- type FileBound,
32
- isCurrent,
33
- snapshotDirManifest,
34
- } from "./file-snapshot.ts";
35
- import {
36
- type VerificationRunnerBinding,
37
- validateVerificationCapability,
38
- } from "./verification-capability.ts";
39
-
40
- export type { VerificationContract } from "./contracts.ts";
41
-
42
- export interface BaselineEntry {
43
- path: string;
44
- kind: "file" | "deleted";
45
- sha256?: string;
46
- bytes?: number;
47
- executable?: boolean;
48
- }
49
-
50
- export interface CandidatePreflightInput {
51
- root: string;
52
- diff: Buffer;
53
- writeSet: string[];
54
- approvedDependencies: string[];
55
- snapshot: Bound;
56
- baseline: BaselineEntry[];
57
- verification: VerificationContract;
58
- packageManifest: FileBound;
59
- lockfile: FileBound;
60
- dependencyTarget: FileBound | DirBound;
61
- candidateOutputsAvailable?: (checkoutRoot: string) => boolean;
62
- signal?: AbortSignal;
63
- }
64
-
65
- type PreflightCandidateFailure = Exclude<
66
- CandidateFailure,
67
- { kind: "result-limit" }
68
- >;
69
- type PreflightFailureKind = PreflightCandidateFailure["kind"];
70
- type PreflightFailureFor<Kind extends PreflightFailureKind> = Extract<
71
- PreflightCandidateFailure,
72
- { kind: Kind }
73
- >;
74
-
75
- export type CandidatePreflightResult =
76
- | {
77
- ok: true;
78
- classification: VerificationContract["classification"];
79
- targets: string[];
80
- commandId: string;
81
- exitCode: number;
82
- testCount: number;
83
- identityMatch: boolean;
84
- checkoutRemoved: true;
85
- }
86
- | (PreflightCandidateFailure & {
87
- ok: false;
88
- commandId?: string;
89
- exitCode?: number;
90
- testCount?: number;
91
- identityMatch?: boolean;
92
- checkoutRemoved: boolean;
93
- excerpt?: string;
94
- });
95
-
96
- export interface CandidatePreflightDependencies {
97
- mkdtemp: typeof mkdtempSync;
98
- remove: typeof rmSync;
99
- spawn: (
100
- command: string,
101
- args: string[],
102
- options: CommandOptions,
103
- ) => CommandResult | Promise<CommandResult>;
104
- realpath: typeof realpathSync;
105
- bwrapPath: string;
106
- }
107
-
108
- interface CommandResult {
109
- status: number | null;
110
- stdout: string;
111
- stderr: string;
112
- error?: Error;
113
- }
114
-
115
- interface CommandOptions {
116
- cwd: string;
117
- shell: false;
118
- encoding: "utf8";
119
- maxBuffer: number;
120
- timeout: number;
121
- input?: Buffer;
122
- signal?: AbortSignal;
123
- }
124
-
125
- interface FailureDetails {
126
- commandId?: string;
127
- exitCode?: number;
128
- testCount?: number;
129
- identityMatch?: boolean;
130
- excerpt?: string;
131
- }
132
-
133
- class PreflightFailure extends Error {
134
- constructor(
135
- readonly failure: PreflightCandidateFailure,
136
- readonly details: FailureDetails = {},
137
- ) {
138
- super(failure.code);
139
- }
140
- }
141
-
142
- const SHA256 = /^[0-9a-f]{64}$/;
143
- const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/iu;
144
- const LOCKFILES = [
145
- "bun.lock",
146
- "bun.lockb",
147
- "package-lock.json",
148
- "pnpm-lock.yaml",
149
- "yarn.lock",
150
- ] as const;
151
- const LOAD_FAILURE =
152
- /(?:syntaxerror|parse error|failed to load|cannot find (?:module|package)|no test files|tests?\s+no tests|error ts\d+)/iu;
153
- const COMMAND_TIMEOUT_MS = 10 * 60 * 1000;
154
- const SANDBOX_BUN_TARGET = "/usr/bin/bun";
155
- const SANDBOX_REPORT_TARGET = "/tmp/cadence-verification-report.json";
156
- const MAX_REPORT_BYTES = 2 * 1024 * 1024;
157
-
158
- const defaultDependencies: CandidatePreflightDependencies = {
159
- mkdtemp: mkdtempSync,
160
- remove: rmSync,
161
- spawn: spawnCommand,
162
- realpath: realpathSync,
163
- bwrapPath: "/usr/bin/bwrap",
164
- };
165
-
166
- function spawnCommand(
167
- command: string,
168
- args: string[],
169
- options: CommandOptions,
170
- ): Promise<CommandResult> {
171
- return new Promise((resolve) => {
172
- const stdout: Buffer[] = [];
173
- const stderr: Buffer[] = [];
174
- let buffered = 0;
175
- let commandError: Error | undefined;
176
- let settled = false;
177
- let child: ChildProcessWithoutNullStreams;
178
- let timeout: ReturnType<typeof setTimeout> | undefined;
179
- const finish = (status: number | null) => {
180
- if (settled) return;
181
- settled = true;
182
- if (timeout) clearTimeout(timeout);
183
- resolve({
184
- status,
185
- stdout: Buffer.concat(stdout).toString("utf8"),
186
- stderr: Buffer.concat(stderr).toString("utf8"),
187
- ...(commandError ? { error: commandError } : {}),
188
- });
189
- };
190
- const collect = (target: Buffer[], chunk: Buffer) => {
191
- buffered += chunk.length;
192
- if (buffered > options.maxBuffer) {
193
- commandError ??= new Error("preflight command output exceeded limit");
194
- child.kill("SIGKILL");
195
- return;
196
- }
197
- target.push(chunk);
198
- };
199
- try {
200
- child = spawn(command, args, {
201
- cwd: options.cwd,
202
- shell: false,
203
- stdio: ["pipe", "pipe", "pipe"],
204
- ...(options.signal ? { signal: options.signal } : {}),
205
- }) as ChildProcessWithoutNullStreams;
206
- } catch (error) {
207
- commandError = error as Error;
208
- resolve({ status: null, stdout: "", stderr: "", error: commandError });
209
- return;
210
- }
211
- timeout = setTimeout(() => {
212
- commandError ??= new Error("preflight command timed out");
213
- child.kill("SIGKILL");
214
- }, options.timeout);
215
- timeout.unref();
216
- child.stdout.on("data", (chunk: Buffer) => collect(stdout, chunk));
217
- child.stderr.on("data", (chunk: Buffer) => collect(stderr, chunk));
218
- child.on("error", (error) => {
219
- commandError ??= error;
220
- });
221
- child.on("close", (status) => finish(status));
222
- child.stdin.on("error", () => undefined);
223
- child.stdin.end(options.input);
224
- });
225
- }
226
-
227
- function typedFailure<Kind extends PreflightFailureKind>(
228
- kind: Kind,
229
- code: PreflightFailureFor<Kind>["code"],
230
- ): PreflightFailureFor<Kind> {
231
- return { kind, code } as PreflightFailureFor<Kind>;
232
- }
233
-
234
- function reject<Kind extends PreflightFailureKind>(
235
- kind: Kind,
236
- code: PreflightFailureFor<Kind>["code"],
237
- details: FailureDetails = {},
238
- ): never {
239
- throw new PreflightFailure(typedFailure(kind, code), details);
240
- }
241
-
242
- function failed<Kind extends PreflightFailureKind>(
243
- kind: Kind,
244
- code: PreflightFailureFor<Kind>["code"],
245
- details: FailureDetails = {},
246
- checkoutRemoved = true,
247
- ): CandidatePreflightResult {
248
- return {
249
- ok: false,
250
- ...typedFailure(kind, code),
251
- ...details,
252
- checkoutRemoved,
253
- } as CandidatePreflightResult;
254
- }
255
-
256
- function removeCheckout(
257
- temp: string,
258
- dependencies: CandidatePreflightDependencies,
259
- ): { failed: boolean; removed: boolean } {
260
- let cleanupFailed = false;
261
- for (let attempt = 0; attempt < 2; attempt++) {
262
- try {
263
- dependencies.remove(temp, { recursive: true, force: true });
264
- } catch {
265
- cleanupFailed = true;
266
- }
267
- let removed = false;
268
- try {
269
- removed = lstatSync(temp, { throwIfNoEntry: false }) === undefined;
270
- } catch {
271
- cleanupFailed = true;
272
- }
273
- if (removed) return { failed: cleanupFailed, removed: true };
274
- cleanupFailed = true;
275
- }
276
- return { failed: true, removed: false };
277
- }
278
-
279
- function sha256(bytes: Buffer): string {
280
- return createHash("sha256").update(bytes).digest("hex");
281
- }
282
-
283
- function safePath(root: string, relative: string): string {
284
- if (
285
- !isValidRelativePath(relative) ||
286
- relative.trim() !== relative ||
287
- /[\r\n\t]/u.test(relative)
288
- ) {
289
- reject("artifact", "noncanonical-path");
290
- }
291
- const absolute = path.resolve(root, relative);
292
- if (absolute === root || !absolute.startsWith(`${root}${path.sep}`)) {
293
- reject("artifact", "escaping-path");
294
- }
295
- return absolute;
296
- }
297
-
298
- function sameDirBound(actual: DirBound | null, expected: DirBound): boolean {
299
- return (
300
- actual !== null &&
301
- actual.kind === "dir" &&
302
- actual.manifest === expected.manifest
303
- );
304
- }
305
-
306
- function validSnapshot(snapshot: Bound): boolean {
307
- if (
308
- snapshot === null ||
309
- typeof snapshot !== "object" ||
310
- Array.isArray(snapshot)
311
- ) {
312
- return false;
313
- }
314
- for (const [relative, value] of Object.entries(snapshot)) {
315
- if (!isValidRelativePath(relative) || !value || typeof value !== "object")
316
- return false;
317
- if (value.kind === "file") {
318
- if (
319
- !SHA256.test(value.sha256) ||
320
- !Number.isSafeInteger(value.bytes) ||
321
- value.bytes < 0
322
- )
323
- return false;
324
- } else if (value.kind === "dir") {
325
- if (!SHA256.test(value.manifest)) return false;
326
- } else if (value.kind === "absent") {
327
- if (value.absent !== true) return false;
328
- } else {
329
- return false;
330
- }
331
- }
332
- return true;
333
- }
334
-
335
- async function run(
336
- dependencies: CandidatePreflightDependencies,
337
- cwd: string,
338
- command: string,
339
- args: string[],
340
- input?: Buffer,
341
- signal?: AbortSignal,
342
- ): Promise<CommandResult> {
343
- const options: CommandOptions = {
344
- cwd,
345
- shell: false,
346
- encoding: "utf8",
347
- maxBuffer: 2 * 1024 * 1024,
348
- timeout: COMMAND_TIMEOUT_MS,
349
- ...(input === undefined ? {} : { input }),
350
- ...(signal === undefined ? {} : { signal }),
351
- };
352
- const result = await dependencies.spawn(command, args, options);
353
- return {
354
- status: result.status,
355
- stdout: result.stdout ?? "",
356
- stderr: result.stderr ?? "",
357
- ...(result.error === undefined ? {} : { error: result.error }),
358
- };
359
- }
360
-
361
- function commandExcerpt(
362
- result: Pick<CommandResult, "stdout" | "stderr">,
363
- secrets: string[],
364
- ): string {
365
- let text = `${result.stdout}\n${result.stderr}`;
366
- for (const secret of secrets) {
367
- if (secret) text = text.replaceAll(secret, "<path>");
368
- }
369
- return [...text]
370
- .map((character) =>
371
- character === "\n" ||
372
- ((character.codePointAt(0) ?? 0) >= 0x20 &&
373
- (character.codePointAt(0) ?? 0) <= 0x7e)
374
- ? character
375
- : "?",
376
- )
377
- .join("")
378
- .slice(-1024);
379
- }
380
-
381
- function requireCurrentHost(input: CandidatePreflightInput): void {
382
- if (!isCurrent(input.root, input.snapshot)) reject("stale", "stale-snapshot");
383
- }
384
-
385
- function validateInput(input: CandidatePreflightInput): {
386
- targets: string[];
387
- lockPath: string;
388
- dependencyPath: string;
389
- verification: StructuredVerificationContract;
390
- } {
391
- if (!Buffer.isBuffer(input.diff) || input.diff.length === 0)
392
- reject("artifact", "invalid-diff-bytes");
393
- if (!validSnapshot(input.snapshot)) reject("artifact", "invalid-snapshot");
394
- const verification = validateVerificationContract(input.verification);
395
- if (!verification.ok) reject("artifact", "invalid-verification-contract");
396
- if (
397
- !Array.isArray(input.approvedDependencies) ||
398
- input.approvedDependencies.some(
399
- (dependency) => typeof dependency !== "string" || dependency.length === 0,
400
- ) ||
401
- new Set(input.approvedDependencies).size !==
402
- input.approvedDependencies.length
403
- ) {
404
- reject("artifact", "invalid-approved-dependencies");
405
- }
406
-
407
- let targets: string[];
408
- try {
409
- targets = diffWritePaths(input.diff.toString("utf8")).paths;
410
- } catch (error) {
411
- reject("artifact", "invalid-diff", {
412
- excerpt: String((error as Error).message).slice(0, 256),
413
- });
414
- }
415
-
416
- const writeSet = [...new Set(input.writeSet)];
417
- if (
418
- writeSet.length !== input.writeSet.length ||
419
- writeSet.some((relative) => !isValidRelativePath(relative)) ||
420
- targets.some((target) => !writeSet.includes(target))
421
- ) {
422
- reject("artifact", "write-set-mismatch");
423
- }
424
-
425
- const packageBound = input.snapshot["package.json"];
426
- if (
427
- packageBound?.kind !== "file" ||
428
- packageBound.sha256 !== input.packageManifest.sha256 ||
429
- packageBound.bytes !== input.packageManifest.bytes
430
- ) {
431
- reject("artifact", "package-bound-mismatch");
432
- }
433
- const lockPath = LOCKFILES.find((candidate) => {
434
- const bound = input.snapshot[candidate];
435
- return (
436
- bound?.kind === "file" &&
437
- bound.sha256 === input.lockfile.sha256 &&
438
- bound.bytes === input.lockfile.bytes
439
- );
440
- });
441
- if (!lockPath) reject("artifact", "lockfile-bound-mismatch");
442
-
443
- const dependencyPath = "node_modules";
444
- const dependencyBound = input.snapshot[dependencyPath];
445
- if (
446
- !dependencyBound ||
447
- dependencyBound.kind !== input.dependencyTarget.kind ||
448
- (dependencyBound.kind === "file" &&
449
- input.dependencyTarget.kind === "file" &&
450
- (dependencyBound.sha256 !== input.dependencyTarget.sha256 ||
451
- dependencyBound.bytes !== input.dependencyTarget.bytes)) ||
452
- (dependencyBound.kind === "dir" &&
453
- input.dependencyTarget.kind === "dir" &&
454
- dependencyBound.manifest !== input.dependencyTarget.manifest)
455
- ) {
456
- reject("artifact", "dependency-bound-mismatch");
457
- }
458
-
459
- const entries = new Map<string, BaselineEntry>();
460
- for (const entry of input.baseline) {
461
- if (!entry || !isValidRelativePath(entry.path) || entries.has(entry.path)) {
462
- reject("artifact", "baseline-shape");
463
- }
464
- if (entry.kind === "file") {
465
- if (
466
- !SHA256.test(entry.sha256 ?? "") ||
467
- !Number.isSafeInteger(entry.bytes) ||
468
- (entry.bytes ?? -1) < 0 ||
469
- typeof entry.executable !== "boolean"
470
- ) {
471
- reject("artifact", "baseline-shape");
472
- }
473
- const bound = input.snapshot[entry.path];
474
- if (
475
- bound?.kind !== "file" ||
476
- bound.sha256 !== entry.sha256 ||
477
- bound.bytes !== entry.bytes
478
- ) {
479
- reject("artifact", "baseline-bound-mismatch");
480
- }
481
- } else if (
482
- entry.kind !== "deleted" ||
483
- entry.sha256 !== undefined ||
484
- entry.bytes !== undefined ||
485
- entry.executable !== undefined ||
486
- input.snapshot[entry.path]?.kind !== "absent"
487
- ) {
488
- reject("artifact", "baseline-shape");
489
- }
490
- entries.set(entry.path, entry);
491
- }
492
- for (const [relative, bound] of Object.entries(input.snapshot)) {
493
- if (
494
- (bound.kind === "file" || bound.kind === "absent") &&
495
- !entries.has(relative)
496
- ) {
497
- reject("artifact", "incomplete-baseline");
498
- }
499
- }
500
- if (targets.some((target) => !entries.has(target)))
501
- reject("artifact", "target-baseline-missing");
502
- for (const verificationPath of verificationInputPaths(verification.value)) {
503
- if (!entries.has(verificationPath))
504
- reject("artifact", "verification-baseline-missing");
505
- }
506
- return {
507
- targets,
508
- lockPath,
509
- dependencyPath,
510
- verification: verification.value,
511
- };
512
- }
513
-
514
- function verifyBaselineAt(root: string, baseline: BaselineEntry[]): void {
515
- for (const entry of baseline) {
516
- const absolute = safePath(root, entry.path);
517
- const stat = lstatSync(absolute, { throwIfNoEntry: false });
518
- if (entry.kind === "deleted") {
519
- if (stat) reject("stale", "baseline-deletion-drift");
520
- continue;
521
- }
522
- if (!stat?.isFile() || stat.isSymbolicLink())
523
- reject("artifact", "baseline-file-kind");
524
- const bytes = readFileSync(absolute);
525
- if (
526
- bytes.length !== entry.bytes ||
527
- sha256(bytes) !== entry.sha256 ||
528
- Boolean(stat.mode & 0o111) !== entry.executable
529
- ) {
530
- reject("stale", "baseline-file-drift");
531
- }
532
- }
533
- }
534
-
535
- function scanCheckout(checkout: string): void {
536
- const visit = (directory: string): void => {
537
- for (const entry of readdirSync(directory, { withFileTypes: true })) {
538
- if (directory === checkout && entry.name === ".git") continue;
539
- const absolute = path.join(directory, entry.name);
540
- const stat = lstatSync(absolute);
541
- if (stat.isSymbolicLink()) reject("artifact", "checkout-symlink");
542
- if (stat.isDirectory()) visit(absolute);
543
- else if (!stat.isFile()) reject("artifact", "checkout-special-file");
544
- }
545
- };
546
- visit(checkout);
547
- }
548
-
549
- function resolveBunExecutable(
550
- dependencies: CandidatePreflightDependencies,
551
- ): string {
552
- const candidates = [
553
- ...(process.versions.bun ? [process.execPath] : []),
554
- ...(process.env.PATH ?? "")
555
- .split(path.delimiter)
556
- .filter(Boolean)
557
- .map((entry) => path.join(entry, "bun")),
558
- ];
559
- for (const candidate of candidates) {
560
- try {
561
- const executable = dependencies.realpath(candidate);
562
- const stat = lstatSync(executable);
563
- if (stat.isFile() && (stat.mode & 0o111) !== 0) return executable;
564
- } catch {}
565
- }
566
- reject("environment", "bun-executable-unavailable");
567
- }
568
-
569
- async function requireRegularGitModes(
570
- dependencies: CandidatePreflightDependencies,
571
- checkout: string,
572
- signal?: AbortSignal,
573
- ): Promise<void> {
574
- const result = await run(
575
- dependencies,
576
- checkout,
577
- "git",
578
- ["ls-files", "--stage", "-z"],
579
- undefined,
580
- signal,
581
- );
582
- if (signal?.aborted) reject("cancelled", "cancelled");
583
- if (result.error || result.status !== 0)
584
- reject("environment", "git-index-unavailable");
585
- for (const record of result.stdout.split("\0").filter(Boolean)) {
586
- const mode = record.slice(0, 6);
587
- if (mode !== "100644" && mode !== "100755")
588
- reject("artifact", mode === "160000" ? "submodule" : "nonregular-mode");
589
- }
590
- }
591
-
592
- function reconstructBaseline(
593
- input: CandidatePreflightInput,
594
- checkout: string,
595
- ): void {
596
- for (const entry of input.baseline) {
597
- const source = safePath(input.root, entry.path);
598
- const target = safePath(checkout, entry.path);
599
- if (entry.kind === "deleted") {
600
- const stat = lstatSync(target, { throwIfNoEntry: false });
601
- if (stat?.isDirectory()) reject("artifact", "baseline-deletion-kind");
602
- rmSync(target, { force: true });
603
- continue;
604
- }
605
- mkdirSync(path.dirname(target), { recursive: true });
606
- copyFileSync(source, target);
607
- const stat = lstatSync(target);
608
- const mode = entry.executable ? 0o755 : 0o644;
609
- if ((stat.mode & 0o777) !== mode) {
610
- chmodSync(target, mode);
611
- }
612
- }
613
- verifyBaselineAt(checkout, input.baseline);
614
- }
615
-
616
- function copySafeDirectory(source: string, target: string): void {
617
- const directory = lstatSync(source, { throwIfNoEntry: false });
618
- if (!directory?.isDirectory() || directory.isSymbolicLink())
619
- reject("artifact", "baseline-directory-kind");
620
- mkdirSync(target, { recursive: true });
621
- chmodSync(target, directory.mode & 0o777);
622
- for (const name of readdirSync(source).sort()) {
623
- const sourceEntry = path.join(source, name);
624
- const targetEntry = path.join(target, name);
625
- const stat = lstatSync(sourceEntry);
626
- if (stat.isSymbolicLink()) reject("artifact", "baseline-directory-symlink");
627
- if (stat.isDirectory()) {
628
- copySafeDirectory(sourceEntry, targetEntry);
629
- continue;
630
- }
631
- if (!stat.isFile()) reject("artifact", "baseline-directory-special-file");
632
- copyFileSync(sourceEntry, targetEntry);
633
- chmodSync(targetEntry, stat.mode & 0o777);
634
- }
635
- }
636
-
637
- function reconstructBoundDirectories(
638
- input: CandidatePreflightInput,
639
- checkout: string,
640
- dependencyPath: string,
641
- ): void {
642
- const directories = Object.entries(input.snapshot)
643
- .filter(
644
- (entry): entry is [string, DirBound] =>
645
- entry[1].kind === "dir" && entry[0] !== dependencyPath,
646
- )
647
- .sort(([left], [right]) => left.localeCompare(right));
648
- const roots = directories.filter(
649
- ([relative], index) =>
650
- !directories.some(
651
- ([parent], parentIndex) =>
652
- parentIndex !== index && relative.startsWith(`${parent}/`),
653
- ),
654
- );
655
- for (const [relative, expected] of roots) {
656
- const source = safePath(input.root, relative);
657
- const target = safePath(checkout, relative);
658
- rmSync(target, { recursive: true, force: true });
659
- copySafeDirectory(source, target);
660
- if (!sameDirBound(snapshotDirManifest(checkout, relative), expected)) {
661
- if (!sameDirBound(snapshotDirManifest(input.root, relative), expected))
662
- reject("stale", "baseline-directory-drift");
663
- reject("artifact", "baseline-directory-mismatch");
664
- }
665
- }
666
- }
667
-
668
- function sandboxArgs(
669
- checkout: string,
670
- dependencySource: string,
671
- dependencyIsDirectory: boolean,
672
- bunExecutable: string | undefined,
673
- runnerPlan: SandboxRunnerPlan,
674
- invocation: string[],
675
- reportSource?: string,
676
- ): string[] {
677
- const candidate = "/candidate";
678
- const sandboxPath = [
679
- ...runnerPlan.pathEntries,
680
- "/usr/bin",
681
- "/usr/local/bin",
682
- "/bin",
683
- ].join(":");
684
- const args = [
685
- "--unshare-net",
686
- "--unshare-pid",
687
- "--die-with-parent",
688
- "--new-session",
689
- "--proc",
690
- "/proc",
691
- "--dev",
692
- "/dev",
693
- "--tmpfs",
694
- "/tmp",
695
- "--clearenv",
696
- "--setenv",
697
- "PATH",
698
- sandboxPath,
699
- "--setenv",
700
- "HOME",
701
- "/nonexistent",
702
- "--setenv",
703
- "CI",
704
- "1",
705
- ];
706
- for (const systemPath of ["/usr", "/bin", "/lib", "/lib64", "/etc"]) {
707
- if (existsSync(systemPath)) args.push("--ro-bind", systemPath, systemPath);
708
- }
709
- if (bunExecutable) {
710
- args.push("--ro-bind", bunExecutable, SANDBOX_BUN_TARGET);
711
- }
712
- if (runnerPlan.mounts.length > 0) {
713
- args.push("--dir", "/cadence", "--dir", "/cadence/runners");
714
- for (const mount of runnerPlan.mounts) {
715
- args.push("--dir", mount.target, "--ro-bind", mount.source, mount.target);
716
- }
717
- }
718
- const dependencyDestination = path.posix.join(candidate, "node_modules");
719
- const localDependency = path.join(checkout, "node_modules");
720
- rmSync(localDependency, { recursive: true, force: true });
721
- if (dependencyIsDirectory) {
722
- mkdirSync(localDependency, { recursive: true });
723
- } else {
724
- writeFileSync(localDependency, "");
725
- }
726
- args.push(
727
- "--dir",
728
- candidate,
729
- "--bind",
730
- checkout,
731
- candidate,
732
- "--ro-bind",
733
- dependencySource,
734
- dependencyDestination,
735
- ...(reportSource ? ["--bind", reportSource, SANDBOX_REPORT_TARGET] : []),
736
- "--chdir",
737
- candidate,
738
- "--",
739
- ...invocation,
740
- );
741
- return args;
742
- }
743
-
744
- interface SandboxRunnerPlan {
745
- mounts: Array<{ source: string; target: string }>;
746
- pathEntries: string[];
747
- }
748
-
749
- function pathWithin(root: string, candidate: string): boolean {
750
- return candidate === root || candidate.startsWith(`${root}${path.sep}`);
751
- }
752
-
753
- function sandboxRunnerPlan(
754
- bindings: readonly VerificationRunnerBinding[],
755
- ): SandboxRunnerPlan {
756
- const relevant = bindings.filter((binding) => binding.command !== "bun");
757
- const sources = [
758
- ...new Set(
759
- relevant.flatMap((binding) =>
760
- binding.mountSource ? [binding.mountSource] : [],
761
- ),
762
- ),
763
- ]
764
- .sort(
765
- (left, right) => left.length - right.length || left.localeCompare(right),
766
- )
767
- .filter(
768
- (source, index, all) =>
769
- !all.some(
770
- (candidate, candidateIndex) =>
771
- candidateIndex !== index &&
772
- candidate.length < source.length &&
773
- pathWithin(candidate, source),
774
- ),
775
- );
776
- const mounts = sources.map((source, index) => ({
777
- source,
778
- target: `/cadence/runners/${index}`,
779
- }));
780
- const pathEntries = relevant.map((binding) => {
781
- if (!binding.mountSource) return path.dirname(binding.executablePath);
782
- const mount = mounts.find((candidate) =>
783
- pathWithin(candidate.source, binding.executablePath),
784
- );
785
- if (!mount) throw new Error("verification runner mount plan is incomplete");
786
- const relative = path.relative(
787
- mount.source,
788
- path.dirname(binding.executablePath),
789
- );
790
- return relative
791
- ? path.posix.join(mount.target, ...relative.split(path.sep))
792
- : mount.target;
793
- });
794
- return { mounts, pathEntries: [...new Set(pathEntries)] };
795
- }
796
-
797
- interface StructuredTestReport {
798
- total: number;
799
- failed: number;
800
- success: boolean;
801
- failedAssertions: string[];
802
- fileErrors: string[];
803
- }
804
-
805
- function structuredTestReport(reportPath: string): StructuredTestReport {
806
- const stat = lstatSync(reportPath, { throwIfNoEntry: false });
807
- if (
808
- !stat?.isFile() ||
809
- stat.isSymbolicLink() ||
810
- stat.size === 0 ||
811
- stat.size > MAX_REPORT_BYTES
812
- ) {
813
- reject("artifact", "verification-report-missing");
814
- }
815
- let value: unknown;
816
- try {
817
- value = JSON.parse(readFileSync(reportPath, "utf8"));
818
- } catch {
819
- reject("artifact", "verification-report-invalid");
820
- }
821
- if (!value || typeof value !== "object" || Array.isArray(value))
822
- reject("artifact", "verification-report-invalid");
823
- const report = value as Record<string, unknown>;
824
- const total = report.numTotalTests;
825
- const failed = report.numFailedTests;
826
- if (
827
- !Number.isSafeInteger(total) ||
828
- (total as number) < 0 ||
829
- !Number.isSafeInteger(failed) ||
830
- (failed as number) < 0 ||
831
- (failed as number) > (total as number) ||
832
- typeof report.success !== "boolean" ||
833
- !Array.isArray(report.testResults)
834
- ) {
835
- reject("artifact", "verification-report-invalid");
836
- }
837
- const failedAssertions: string[] = [];
838
- const fileErrors: string[] = [];
839
- let failedRecords = 0;
840
- for (const file of report.testResults) {
841
- if (!file || typeof file !== "object" || Array.isArray(file))
842
- reject("artifact", "verification-report-invalid");
843
- const fileRecord = file as Record<string, unknown>;
844
- const assertions = fileRecord.assertionResults;
845
- if (!Array.isArray(assertions) || typeof fileRecord.message !== "string")
846
- reject("artifact", "verification-report-invalid");
847
- if (fileRecord.message) fileErrors.push(fileRecord.message);
848
- for (const assertion of assertions) {
849
- if (
850
- !assertion ||
851
- typeof assertion !== "object" ||
852
- Array.isArray(assertion)
853
- )
854
- reject("artifact", "verification-report-invalid");
855
- const record = assertion as Record<string, unknown>;
856
- if (record.status !== "failed") continue;
857
- if (
858
- typeof record.fullName !== "string" ||
859
- typeof record.title !== "string" ||
860
- !Array.isArray(record.failureMessages) ||
861
- record.failureMessages.some((message) => typeof message !== "string")
862
- ) {
863
- reject("artifact", "verification-report-invalid");
864
- }
865
- failedRecords++;
866
- failedAssertions.push(
867
- record.fullName,
868
- record.title,
869
- ...(record.failureMessages as string[]),
870
- );
871
- }
872
- }
873
- if (failedRecords !== failed)
874
- reject("artifact", "verification-report-invalid");
875
- return {
876
- total: total as number,
877
- failed: failed as number,
878
- success: report.success,
879
- failedAssertions,
880
- fileErrors,
881
- };
882
- }
883
-
884
- interface DependencyContractEntry {
885
- value: string;
886
- names: string[];
887
- }
888
-
889
- function canonicalJson(value: unknown): string {
890
- if (Array.isArray(value)) {
891
- return `[${value.map(canonicalJson).join(",")}]`;
892
- }
893
- if (value && typeof value === "object") {
894
- return `{${Object.entries(value as Record<string, unknown>)
895
- .sort(([left], [right]) => left.localeCompare(right))
896
- .map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`)
897
- .join(",")}}`;
898
- }
899
- return JSON.stringify(value);
900
- }
901
-
902
- function dependencyName(selector: string): string | null {
903
- const scoped = /(@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*)/iu.exec(
904
- selector,
905
- )?.[1];
906
- if (scoped) return scoped;
907
- const segment = selector.split("/").at(-1) ?? "";
908
- const name = segment.replace(/@.*$/u, "");
909
- return /^[a-z0-9][a-z0-9._-]*$/iu.test(name) ? name : null;
910
- }
911
-
912
- function addResolutionEntries(
913
- result: Map<string, DependencyContractEntry>,
914
- section: string,
915
- value: unknown,
916
- path: string[] = [],
917
- names: string[] = [],
918
- ): void {
919
- if (value && typeof value === "object" && !Array.isArray(value)) {
920
- for (const [key, entry] of Object.entries(
921
- value as Record<string, unknown>,
922
- )) {
923
- const name = dependencyName(key);
924
- addResolutionEntries(
925
- result,
926
- section,
927
- entry,
928
- [...path, key],
929
- name && key !== "." ? [...new Set([...names, name])] : names,
930
- );
931
- }
932
- return;
933
- }
934
- result.set(`${section}:${path.join(":")}`, {
935
- value: canonicalJson(value),
936
- names,
937
- });
938
- }
939
-
940
- function addPackageListPolicy(
941
- result: Map<string, DependencyContractEntry>,
942
- section: string,
943
- value: unknown,
944
- ): void {
945
- if (value === undefined) return;
946
- if (
947
- !Array.isArray(value) ||
948
- value.some(
949
- (name) => typeof name !== "string" || !PACKAGE_NAME.test(name),
950
- ) ||
951
- new Set(value).size !== value.length
952
- ) {
953
- reject("artifact", "package-manifest-invalid");
954
- }
955
- result.set(section, {
956
- value: canonicalJson([...value].sort()),
957
- names: [],
958
- });
959
- }
960
-
961
- function addPatchedDependencies(
962
- result: Map<string, DependencyContractEntry>,
963
- section: string,
964
- value: unknown,
965
- ): void {
966
- if (value === undefined) return;
967
- if (!value || typeof value !== "object" || Array.isArray(value)) {
968
- reject("artifact", "package-manifest-invalid");
969
- }
970
- for (const [selector, patchPath] of Object.entries(value)) {
971
- if (
972
- !dependencyName(selector) ||
973
- typeof patchPath !== "string" ||
974
- !isValidRelativePath(patchPath)
975
- ) {
976
- reject("artifact", "package-manifest-invalid");
977
- }
978
- }
979
- result.set(section, { value: canonicalJson(value), names: [] });
980
- }
981
-
982
- function dependencyContract(
983
- manifestPath: string,
984
- ): Map<string, DependencyContractEntry> {
985
- let manifest: unknown;
986
- try {
987
- manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
988
- } catch {
989
- reject("artifact", "package-manifest-invalid");
990
- }
991
- if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
992
- reject("artifact", "package-manifest-invalid");
993
- }
994
- const record = manifest as Record<string, unknown>;
995
- const result = new Map<string, DependencyContractEntry>();
996
- for (const section of [
997
- "dependencies",
998
- "devDependencies",
999
- "peerDependencies",
1000
- "optionalDependencies",
1001
- ]) {
1002
- const dependencies = record[section];
1003
- if (dependencies === undefined) continue;
1004
- if (
1005
- !dependencies ||
1006
- typeof dependencies !== "object" ||
1007
- Array.isArray(dependencies)
1008
- ) {
1009
- reject("artifact", "package-manifest-invalid");
1010
- }
1011
- for (const [name, version] of Object.entries(dependencies)) {
1012
- if (typeof version !== "string")
1013
- reject("artifact", "package-manifest-invalid");
1014
- result.set(`${section}:${name}`, { value: version, names: [name] });
1015
- }
1016
- }
1017
- for (const section of [
1018
- "overrides",
1019
- "resolutions",
1020
- "dependenciesMeta",
1021
- "peerDependenciesMeta",
1022
- ]) {
1023
- if (record[section] !== undefined) {
1024
- addResolutionEntries(result, section, record[section]);
1025
- }
1026
- }
1027
- for (const section of ["workspaces", "catalog", "catalogs"] as const) {
1028
- if (record[section] !== undefined) {
1029
- result.set(section, {
1030
- value: canonicalJson(record[section]),
1031
- names: [],
1032
- });
1033
- }
1034
- }
1035
- addPackageListPolicy(
1036
- result,
1037
- "trustedDependencies",
1038
- record.trustedDependencies,
1039
- );
1040
- addPackageListPolicy(
1041
- result,
1042
- "blockedDependencies",
1043
- record.blockedDependencies,
1044
- );
1045
- addPackageListPolicy(
1046
- result,
1047
- "bundledDependencies",
1048
- record.bundledDependencies,
1049
- );
1050
- addPackageListPolicy(result, "bundleDependencies", record.bundleDependencies);
1051
- addPatchedDependencies(
1052
- result,
1053
- "patchedDependencies",
1054
- record.patchedDependencies,
1055
- );
1056
- const pnpm = record.pnpm;
1057
- if (pnpm !== undefined) {
1058
- if (!pnpm || typeof pnpm !== "object" || Array.isArray(pnpm)) {
1059
- reject("artifact", "package-manifest-invalid");
1060
- }
1061
- const pnpmRecord = pnpm as Record<string, unknown>;
1062
- const resolutionSections = [
1063
- "overrides",
1064
- "packageExtensions",
1065
- "peerDependencyRules",
1066
- "allowedDeprecatedVersions",
1067
- ] as const;
1068
- for (const section of resolutionSections) {
1069
- const value = pnpmRecord[section];
1070
- if (value !== undefined) {
1071
- addResolutionEntries(result, `pnpm.${section}`, value);
1072
- }
1073
- }
1074
- addPatchedDependencies(
1075
- result,
1076
- "pnpm.patchedDependencies",
1077
- pnpmRecord.patchedDependencies,
1078
- );
1079
- const tracked = new Set<string>([
1080
- ...resolutionSections,
1081
- "patchedDependencies",
1082
- ]);
1083
- const policy = Object.fromEntries(
1084
- Object.entries(pnpmRecord).filter(([key]) => !tracked.has(key)),
1085
- );
1086
- if (Object.keys(policy).length > 0) {
1087
- result.set("pnpm.policy", { value: canonicalJson(policy), names: [] });
1088
- }
1089
- }
1090
- return result;
1091
- }
1092
-
1093
- function rejectUnapprovedDependencies(
1094
- before: Map<string, DependencyContractEntry>,
1095
- after: Map<string, DependencyContractEntry>,
1096
- approved: string[],
1097
- lockfileChanged: boolean,
1098
- ): boolean {
1099
- const approvedNames = new Set(approved);
1100
- const keys = new Set([...before.keys(), ...after.keys()]);
1101
- let manifestChanged = false;
1102
- for (const key of keys) {
1103
- const previous = before.get(key);
1104
- const next = after.get(key);
1105
- if (previous?.value === next?.value) continue;
1106
- manifestChanged = true;
1107
- const names = new Set([...(previous?.names ?? []), ...(next?.names ?? [])]);
1108
- if (
1109
- names.size === 0 ||
1110
- [...names].some((name) => !approvedNames.has(name))
1111
- ) {
1112
- reject("approval-boundary", "unapproved-dependency-change");
1113
- }
1114
- }
1115
- if (lockfileChanged) {
1116
- reject("approval-boundary", "unapproved-dependency-change");
1117
- }
1118
- return manifestChanged;
1119
- }
1120
-
1121
- function normalizeTarget(
1122
- verification: AtomicVerificationContract,
1123
- result: CommandResult,
1124
- secrets: string[],
1125
- report?: StructuredTestReport,
1126
- ): CandidatePreflightResult {
1127
- const vitest = verification.kind === "vitest";
1128
- const output = `${result.stdout}\n${result.stderr}`;
1129
- const count = vitest ? (report?.total ?? 0) : 0;
1130
- const identityMatch =
1131
- verification.classification !== "expected-red" ||
1132
- (vitest
1133
- ? (report?.failedAssertions.some((failure) =>
1134
- failure.includes(verification.expectedFailure ?? ""),
1135
- ) ?? false)
1136
- : output.includes(verification.expectedFailure ?? ""));
1137
- const loadable =
1138
- !vitest ||
1139
- ((report?.fileErrors.length ?? 0) === 0 && !LOAD_FAILURE.test(output));
1140
- const enoughTests = !vitest || count >= verification.minTests;
1141
- const accepted =
1142
- verification.classification === "expected-red"
1143
- ? result.status !== 0 &&
1144
- (!vitest || (report?.failed ?? 0) > 0) &&
1145
- enoughTests &&
1146
- identityMatch &&
1147
- loadable
1148
- : result.status === 0 &&
1149
- (!vitest || report?.success === true) &&
1150
- enoughTests &&
1151
- loadable;
1152
- const details = {
1153
- commandId: verification.id,
1154
- exitCode: result.status ?? 1,
1155
- testCount: count,
1156
- identityMatch,
1157
- };
1158
- if (
1159
- verification.classification === "expected-red" &&
1160
- result.status === 0 &&
1161
- enoughTests &&
1162
- loadable
1163
- ) {
1164
- return failed("artifact", "red-not-witnessed", {
1165
- ...details,
1166
- excerpt: commandExcerpt(result, secrets),
1167
- });
1168
- }
1169
- if (!accepted) {
1170
- return failed("artifact", "verification-rejected", {
1171
- ...details,
1172
- excerpt: commandExcerpt(result, secrets),
1173
- });
1174
- }
1175
- return {
1176
- ok: true,
1177
- classification: verification.classification,
1178
- targets: [],
1179
- ...details,
1180
- checkoutRemoved: true,
1181
- };
1182
- }
1183
-
1184
- function packageScriptInvocation(
1185
- packageManager: string,
1186
- script: string,
1187
- args: string[],
1188
- ): string[] {
1189
- switch (packageManager) {
1190
- case "npm":
1191
- case "pnpm":
1192
- return [packageManager, "run", script, "--", ...args];
1193
- case "yarn":
1194
- return [packageManager, "run", script, ...args];
1195
- default:
1196
- return ["bun", "run", script, ...args];
1197
- }
1198
- }
1199
-
1200
- function executableInvocation(
1201
- runner: Exclude<
1202
- AtomicVerificationContract,
1203
- { kind: "package-script" }
1204
- >["runner"],
1205
- args: string[],
1206
- ): string[] {
1207
- switch (runner.kind) {
1208
- case "package-script":
1209
- return packageScriptInvocation(
1210
- runner.packageManager,
1211
- runner.script,
1212
- args,
1213
- );
1214
- case "local-binary":
1215
- return [
1216
- path.posix.join("/candidate/node_modules/.bin", runner.executable),
1217
- ...args,
1218
- ];
1219
- case "npx":
1220
- return ["npx", "--no-install", runner.executable, ...args];
1221
- case "node":
1222
- return ["node", runner.script, ...args];
1223
- }
1224
- }
1225
-
1226
- function verificationInvocation(
1227
- verification: AtomicVerificationContract,
1228
- report: boolean,
1229
- ): string[] {
1230
- const reporter = report
1231
- ? ["--reporter=json", `--outputFile=${SANDBOX_REPORT_TARGET}`]
1232
- : [];
1233
- if (verification.kind === "package-script") {
1234
- return packageScriptInvocation(
1235
- verification.packageManager,
1236
- verification.script,
1237
- verification.args,
1238
- );
1239
- }
1240
- const args =
1241
- verification.kind === "vitest"
1242
- ? [...verification.testFiles, ...verification.args, ...reporter]
1243
- : verification.args;
1244
- return executableInvocation(verification.runner, args);
1245
- }
1246
-
1247
- function resolveDependencySource(
1248
- dependencies: CandidatePreflightDependencies,
1249
- root: string,
1250
- dependencyPath: string,
1251
- ): string {
1252
- const unresolved = safePath(root, dependencyPath);
1253
- let stat: ReturnType<typeof lstatSync> | undefined;
1254
- try {
1255
- stat = lstatSync(unresolved, { throwIfNoEntry: false });
1256
- } catch {
1257
- reject("environment", "dependency-path-unsafe");
1258
- }
1259
- if (!stat || stat.isSymbolicLink())
1260
- reject("environment", "dependency-path-unsafe");
1261
- let canonical: string;
1262
- try {
1263
- canonical = dependencies.realpath(unresolved);
1264
- } catch {
1265
- reject("environment", "dependency-path-unsafe");
1266
- }
1267
- if (canonical === root || !canonical.startsWith(`${root}${path.sep}`))
1268
- reject("environment", "dependency-path-unsafe");
1269
- return canonical;
1270
- }
1271
-
1272
- export async function preflightCandidate(
1273
- input: CandidatePreflightInput,
1274
- dependencies: CandidatePreflightDependencies = defaultDependencies,
1275
- ): Promise<CandidatePreflightResult> {
1276
- let temp: string | undefined;
1277
- let checkout: string | undefined;
1278
- let targets: string[] = [];
1279
- let outcome: CandidatePreflightResult | undefined;
1280
- let unknownError: unknown;
1281
- let hasUnknownError = false;
1282
- try {
1283
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1284
- if (!validSnapshot(input.snapshot)) reject("artifact", "invalid-snapshot");
1285
- let root: string;
1286
- try {
1287
- root = dependencies.realpath(input.root);
1288
- } catch {
1289
- reject("environment", "root-unavailable");
1290
- }
1291
- const dependencySource = resolveDependencySource(
1292
- dependencies,
1293
- root,
1294
- "node_modules",
1295
- );
1296
- requireCurrentHost(input);
1297
- const validated = validateInput(input);
1298
- targets = validated.targets;
1299
- verifyBaselineAt(input.root, input.baseline);
1300
- const prefix = path.join(
1301
- path.dirname(root),
1302
- `.${path.basename(root)}-candidate-`,
1303
- );
1304
- temp = dependencies.mkdtemp(prefix);
1305
- checkout = path.join(temp, "candidate");
1306
-
1307
- const bundle = path.join(temp, "source.bundle");
1308
- const transportLockdown = [
1309
- "-c",
1310
- "protocol.allow=never",
1311
- "-c",
1312
- "protocol.file.allow=never",
1313
- "-c",
1314
- "protocol.ext.allow=never",
1315
- ];
1316
- let result = await run(
1317
- dependencies,
1318
- root,
1319
- "git",
1320
- [...transportLockdown, "bundle", "create", bundle, "HEAD"],
1321
- undefined,
1322
- input.signal,
1323
- );
1324
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1325
- if (result.error || result.status !== 0)
1326
- reject("environment", "clone-failed");
1327
- result = await run(
1328
- dependencies,
1329
- temp,
1330
- "git",
1331
- ["init", "-q", checkout],
1332
- undefined,
1333
- input.signal,
1334
- );
1335
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1336
- if (result.error || result.status !== 0)
1337
- reject("environment", "clone-failed");
1338
- result = await run(
1339
- dependencies,
1340
- checkout,
1341
- "git",
1342
- [
1343
- ...transportLockdown,
1344
- "-c",
1345
- "protocol.file.allow=always",
1346
- "fetch",
1347
- "-q",
1348
- "--no-tags",
1349
- "--no-recurse-submodules",
1350
- bundle,
1351
- "HEAD",
1352
- ],
1353
- undefined,
1354
- input.signal,
1355
- );
1356
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1357
- if (result.error || result.status !== 0)
1358
- reject("environment", "clone-failed");
1359
- result = await run(
1360
- dependencies,
1361
- checkout,
1362
- "git",
1363
- ["checkout", "-q", "--detach", "FETCH_HEAD"],
1364
- undefined,
1365
- input.signal,
1366
- );
1367
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1368
- if (result.error || result.status !== 0)
1369
- reject("environment", "checkout-failed");
1370
- if (
1371
- existsSync(path.join(checkout, ".git", "objects", "info", "alternates"))
1372
- ) {
1373
- reject("environment", "clone-alternates");
1374
- }
1375
-
1376
- await requireRegularGitModes(dependencies, checkout, input.signal);
1377
- scanCheckout(checkout);
1378
- for (const entry of input.baseline) {
1379
- const ignored = await run(
1380
- dependencies,
1381
- root,
1382
- "git",
1383
- ["check-ignore", "-q", "--", entry.path],
1384
- undefined,
1385
- input.signal,
1386
- );
1387
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1388
- if (ignored.error || ignored.status === null || ignored.status > 1)
1389
- reject("environment", "git-ignore-check-failed");
1390
- if (ignored.status === 0) reject("artifact", "ignored-baseline-input");
1391
- }
1392
- reconstructBoundDirectories(input, checkout, validated.dependencyPath);
1393
- reconstructBaseline(input, checkout);
1394
- scanCheckout(checkout);
1395
- const dependenciesBefore = dependencyContract(
1396
- path.join(checkout, "package.json"),
1397
- );
1398
-
1399
- result = await run(
1400
- dependencies,
1401
- checkout,
1402
- "git",
1403
- ["apply", "--check", "--recount", "--whitespace=nowarn", "-"],
1404
- input.diff,
1405
- input.signal,
1406
- );
1407
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1408
- if (result.error) reject("environment", "git-apply-check-unavailable");
1409
- if (result.status !== 0)
1410
- reject("artifact", "git-apply-check-failed", {
1411
- excerpt: commandExcerpt(result, [root, checkout]),
1412
- });
1413
- result = await run(
1414
- dependencies,
1415
- checkout,
1416
- "git",
1417
- ["apply", "--recount", "--whitespace=nowarn", "-"],
1418
- input.diff,
1419
- input.signal,
1420
- );
1421
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1422
- if (result.error) reject("environment", "git-apply-unavailable");
1423
- if (result.status !== 0) reject("artifact", "git-apply-failed");
1424
- const dependencyManifestChanged = rejectUnapprovedDependencies(
1425
- dependenciesBefore,
1426
- dependencyContract(path.join(checkout, "package.json")),
1427
- input.approvedDependencies,
1428
- validated.targets.some((target) =>
1429
- (LOCKFILES as readonly string[]).includes(target),
1430
- ),
1431
- );
1432
- await requireRegularGitModes(dependencies, checkout, input.signal);
1433
- scanCheckout(checkout);
1434
- if (input.candidateOutputsAvailable?.(checkout) === false) {
1435
- reject("artifact", "producer-output-unavailable");
1436
- }
1437
-
1438
- for (const verificationPath of verificationInputPaths(
1439
- validated.verification,
1440
- )) {
1441
- const stat = lstatSync(safePath(checkout, verificationPath), {
1442
- throwIfNoEntry: false,
1443
- });
1444
- if (!stat?.isFile() || stat.isSymbolicLink())
1445
- reject("artifact", "verification-input-unavailable");
1446
- }
1447
-
1448
- const capability = validateVerificationCapability(
1449
- checkout,
1450
- validated.verification,
1451
- { dependencyOwner: root },
1452
- );
1453
- if (!capability.ok) {
1454
- if (capability.diagnostic.kind === "verification-adapter") {
1455
- reject("verification-adapter", capability.diagnostic.code);
1456
- }
1457
- reject("artifact", "invalid-verification-contract");
1458
- }
1459
-
1460
- let bwrap: string;
1461
- try {
1462
- bwrap = dependencies.realpath(dependencies.bwrapPath);
1463
- } catch {
1464
- reject("environment", "bubblewrap-or-dependency-unavailable");
1465
- }
1466
- const runnerPlan = sandboxRunnerPlan(capability.runnerBindings);
1467
- const sandboxSecrets = [
1468
- root,
1469
- checkout,
1470
- ...runnerPlan.mounts.map((mount) => mount.source),
1471
- ];
1472
- let bunExecutable =
1473
- capability.runnerBindings.some((binding) => binding.command === "bun") ||
1474
- dependencyManifestChanged
1475
- ? resolveBunExecutable(dependencies)
1476
- : undefined;
1477
-
1478
- const probeArgs = sandboxArgs(
1479
- checkout,
1480
- dependencySource,
1481
- input.dependencyTarget.kind === "dir",
1482
- bunExecutable,
1483
- runnerPlan,
1484
- ["/bin/true"],
1485
- );
1486
- result = await run(
1487
- dependencies,
1488
- checkout,
1489
- bwrap,
1490
- probeArgs,
1491
- undefined,
1492
- input.signal,
1493
- );
1494
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1495
- if (result.error || result.status !== 0)
1496
- reject("environment", "sandbox-runtime-unavailable", {
1497
- exitCode: result.status ?? 1,
1498
- excerpt: commandExcerpt(result, sandboxSecrets),
1499
- });
1500
-
1501
- if (dependencyManifestChanged) {
1502
- bunExecutable ??= resolveBunExecutable(dependencies);
1503
- const dependencyArgs = sandboxArgs(
1504
- checkout,
1505
- dependencySource,
1506
- input.dependencyTarget.kind === "dir",
1507
- bunExecutable,
1508
- runnerPlan,
1509
- [
1510
- "bun",
1511
- "install",
1512
- "--frozen-lockfile",
1513
- "--dry-run",
1514
- "--ignore-scripts",
1515
- "--no-progress",
1516
- ],
1517
- );
1518
- result = await run(
1519
- dependencies,
1520
- checkout,
1521
- bwrap,
1522
- dependencyArgs,
1523
- undefined,
1524
- input.signal,
1525
- );
1526
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1527
- if (result.error || result.status === null)
1528
- reject("environment", "bubblewrap-launch-failed", {
1529
- excerpt: commandExcerpt(result, sandboxSecrets),
1530
- });
1531
- if (result.status !== 0) {
1532
- reject("approval-boundary", "unapproved-dependency-change");
1533
- }
1534
- }
1535
-
1536
- const steps = verificationSteps(validated.verification);
1537
- for (const [index, step] of steps.entries()) {
1538
- const structuredReportPath =
1539
- step.kind === "vitest"
1540
- ? path.join(temp, `verification-report-${index}.json`)
1541
- : undefined;
1542
- if (structuredReportPath) {
1543
- writeFileSync(structuredReportPath, "", { mode: 0o600 });
1544
- }
1545
- const targetArgs = sandboxArgs(
1546
- checkout,
1547
- dependencySource,
1548
- input.dependencyTarget.kind === "dir",
1549
- bunExecutable,
1550
- runnerPlan,
1551
- verificationInvocation(step, structuredReportPath !== undefined),
1552
- structuredReportPath,
1553
- );
1554
- result = await run(
1555
- dependencies,
1556
- checkout,
1557
- bwrap,
1558
- targetArgs,
1559
- undefined,
1560
- input.signal,
1561
- );
1562
- if (input.signal?.aborted) reject("cancelled", "cancelled");
1563
- requireCurrentHost(input);
1564
- if (result.error || result.status === null) {
1565
- reject("environment", "bubblewrap-launch-failed", {
1566
- excerpt: commandExcerpt(result, sandboxSecrets),
1567
- });
1568
- }
1569
- const report = structuredReportPath
1570
- ? structuredTestReport(structuredReportPath)
1571
- : undefined;
1572
- const normalized = normalizeTarget(step, result, sandboxSecrets, report);
1573
- if (!normalized.ok) {
1574
- outcome =
1575
- index < steps.length - 1
1576
- ? failed("artifact", "candidate-check-failed", {
1577
- commandId: step.id,
1578
- exitCode: normalized.exitCode,
1579
- testCount: normalized.testCount,
1580
- identityMatch: normalized.identityMatch,
1581
- excerpt: normalized.excerpt,
1582
- })
1583
- : normalized;
1584
- break;
1585
- }
1586
- if (index === steps.length - 1) {
1587
- outcome = { ...normalized, targets };
1588
- }
1589
- }
1590
- } catch (error) {
1591
- if (input.signal?.aborted) outcome = failed("cancelled", "cancelled");
1592
- else if (error instanceof PreflightFailure)
1593
- outcome = {
1594
- ok: false,
1595
- ...error.failure,
1596
- ...error.details,
1597
- checkoutRemoved: true,
1598
- } as CandidatePreflightResult;
1599
- else {
1600
- unknownError = error;
1601
- hasUnknownError = true;
1602
- }
1603
- }
1604
- if (temp !== undefined) {
1605
- const cleanup = removeCheckout(temp, dependencies);
1606
- if (cleanup.failed && !hasUnknownError)
1607
- return failed(
1608
- "environment",
1609
- "checkout-cleanup-failed",
1610
- {},
1611
- cleanup.removed,
1612
- );
1613
- }
1614
- if (hasUnknownError) throw unknownError;
1615
- if (outcome === undefined)
1616
- throw new Error("candidate preflight outcome unavailable");
1617
- return outcome;
1618
- }