owlrunkit 0.15.0 → 0.16.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 (76) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +274 -66
  3. package/integrations/codex/skills/owlcoda-runkit/SKILL.md +223 -117
  4. package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
  5. package/integrations/codex/skills/owlcoda-runkit/assets/templates/ready-for-commit-request.json +2 -2
  6. package/package.json +2 -2
  7. package/packages/attest/distribution-manifest-v1.json +10 -0
  8. package/packages/attest/src/quick.mjs +10 -0
  9. package/registry-release-policy-v1.json +8 -2
  10. package/sbom.spdx.json +5 -5
  11. package/scripts/runkit-contract/assurance-router.mjs +119 -0
  12. package/scripts/runkit-contract/core-contract.mjs +250 -115
  13. package/scripts/runkit-contract/core-successor.mjs +2043 -0
  14. package/scripts/runkit-contract/deployment-workflow.mjs +2040 -0
  15. package/scripts/runkit-contract/finalize.mjs +242 -21
  16. package/scripts/runkit-contract/fleet-discovery.mjs +456 -0
  17. package/scripts/runkit-contract/formal-workflow.mjs +850 -0
  18. package/scripts/runkit-contract/human-status.mjs +553 -0
  19. package/scripts/runkit-contract/inspect-presentation.mjs +25 -8
  20. package/scripts/runkit-contract/install-codex-skill.mjs +73 -30
  21. package/scripts/runkit-contract/lease-lifecycle.mjs +8 -2
  22. package/scripts/runkit-contract/lifecycle-orchestration.mjs +135 -12
  23. package/scripts/runkit-contract/onboarding-doctor.mjs +43 -90
  24. package/scripts/runkit-contract/owner-authority-trust.mjs +230 -0
  25. package/scripts/runkit-contract/owner-deployment-decision.mjs +385 -0
  26. package/scripts/runkit-contract/profile-onboarding.mjs +841 -13
  27. package/scripts/runkit-contract/project-cli-resolver.mjs +701 -0
  28. package/scripts/runkit-contract/project-control-parser.mjs +830 -0
  29. package/scripts/runkit-contract/project-control-state.mjs +28 -0
  30. package/scripts/runkit-contract/provenance-common.mjs +19 -2
  31. package/scripts/runkit-contract/ready-for-commit.mjs +330 -18
  32. package/scripts/runkit-contract/remote-deployment.mjs +1596 -0
  33. package/scripts/runkit-contract/remote-process-adapter.mjs +177 -0
  34. package/scripts/runkit-contract/runkit-bootstrap.mjs +131 -0
  35. package/scripts/runkit-contract/runkit-cli.mjs +1097 -21
  36. package/scripts/runkit-contract/schemas/assurance-route-v1.schema.json +26 -0
  37. package/scripts/runkit-contract/schemas/core-successor-batch-receipt-v1.schema.json +126 -0
  38. package/scripts/runkit-contract/schemas/core-successor-plan-v1.schema.json +155 -0
  39. package/scripts/runkit-contract/schemas/core-successor-project-success-receipt-v1.schema.json +72 -0
  40. package/scripts/runkit-contract/schemas/deployment-execute-request-v1.schema.json +63 -0
  41. package/scripts/runkit-contract/schemas/deployment-lineage-v1.schema.json +292 -0
  42. package/scripts/runkit-contract/schemas/deployment-prepare-receipt-v1.schema.json +140 -0
  43. package/scripts/runkit-contract/schemas/fleet-discovery-v1.schema.json +31 -0
  44. package/scripts/runkit-contract/schemas/fleet-manifest-v1.schema.json +20 -0
  45. package/scripts/runkit-contract/schemas/fleet-root-registry-v1.schema.json +34 -0
  46. package/scripts/runkit-contract/schemas/human-status-v1.schema.json +102 -0
  47. package/scripts/runkit-contract/schemas/owner-authority-trust-v1.schema.json +66 -0
  48. package/scripts/runkit-contract/schemas/owner-deployment-authority-v1.schema.json +46 -0
  49. package/scripts/runkit-contract/schemas/owner-deployment-authority-v2.schema.json +94 -0
  50. package/scripts/runkit-contract/schemas/owner-deployment-decision-v1.schema.json +102 -0
  51. package/scripts/runkit-contract/schemas/owner-migration-authority-v1.schema.json +33 -0
  52. package/scripts/runkit-contract/schemas/owner-migration-authority-v2.schema.json +67 -0
  53. package/scripts/runkit-contract/schemas/profile-apply-receipt-v1.schema.json +32 -0
  54. package/scripts/runkit-contract/schemas/profile-detection-v2.schema.json +145 -0
  55. package/scripts/runkit-contract/schemas/ready-for-commit-v2.schema.json +37 -0
  56. package/scripts/runkit-contract/schemas/remote-deployment-manifest-v1.schema.json +379 -0
  57. package/scripts/runkit-contract/schemas/remote-deployment-result-v1.schema.json +153 -0
  58. package/scripts/runkit-contract/schemas/remote-deployment-stage-journal-v1.schema.json +183 -0
  59. package/scripts/runkit-contract/schemas/remote-target-v1.schema.json +32 -0
  60. package/scripts/runkit-contract/schemas/source-candidate-v2.schema.json +152 -0
  61. package/scripts/runkit-contract/schemas/verification-receipt-v2.schema.json +13 -0
  62. package/scripts/runkit-contract/snapshot.mjs +1 -1
  63. package/scripts/runkit-contract/source-candidate.mjs +1382 -0
  64. package/scripts/runkit-contract/ssh-remote-adapter.mjs +1184 -0
  65. package/scripts/runkit-contract/sync-core016-release-identity.mjs +161 -0
  66. package/scripts/runkit-contract/templates/assurance-request.json +13 -0
  67. package/scripts/runkit-contract/templates/core-successor-owner-authority.json +15 -0
  68. package/scripts/runkit-contract/templates/deployment-execute-request.json +39 -0
  69. package/scripts/runkit-contract/templates/deployment-owner-authority.json +23 -0
  70. package/scripts/runkit-contract/templates/deployment-owner-decision.json +26 -0
  71. package/scripts/runkit-contract/templates/fleet-manifest.json +8 -0
  72. package/scripts/runkit-contract/templates/fleet-root-registry.json +8 -0
  73. package/scripts/runkit-contract/templates/owner-authority-trust.json +16 -0
  74. package/scripts/runkit-contract/templates/ready-for-commit-request.json +2 -2
  75. package/scripts/runkit-contract/templates/remote-deployment-manifest.json +108 -0
  76. package/scripts/runkit-contract/verification-receipt-gate.mjs +28 -0
@@ -0,0 +1,1382 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import {
3
+ chmodSync,
4
+ cpSync,
5
+ existsSync,
6
+ lstatSync,
7
+ mkdirSync,
8
+ readFileSync,
9
+ realpathSync,
10
+ renameSync,
11
+ rmSync,
12
+ unlinkSync,
13
+ writeFileSync,
14
+ } from "node:fs";
15
+ import path from "node:path";
16
+
17
+ import { isReservedRuntimePath } from "./core-contract.mjs";
18
+ import {
19
+ createDeliveryFromLeaseWithinControlTransaction,
20
+ } from "./delivery-create.mjs";
21
+ import {
22
+ listLeaseArtifacts,
23
+ withControlTransaction,
24
+ } from "./lease-lifecycle.mjs";
25
+ import {
26
+ loadActiveExecution,
27
+ readJson,
28
+ relativeToWorkspace,
29
+ resolveExistingArtifact,
30
+ safeIdentifier,
31
+ sha256,
32
+ writeJsonAtomically,
33
+ writeJsonExclusiveAtomically,
34
+ } from "./provenance-common.mjs";
35
+ import { verifyDeliveryPacket } from "./source-fingerprint.mjs";
36
+
37
+ function canonicalJson(value) {
38
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
39
+ if (value && typeof value === "object") {
40
+ return `{${Object.keys(value)
41
+ .sort()
42
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
43
+ .join(",")}}`;
44
+ }
45
+ const encoded = JSON.stringify(value);
46
+ if (encoded === undefined) throw new Error("source candidate contains a non-canonical value");
47
+ return encoded;
48
+ }
49
+
50
+ function candidateHash(value) {
51
+ return `sha256:${sha256(canonicalJson(value))}`;
52
+ }
53
+
54
+ function repositoryActionsFalse() {
55
+ return {
56
+ worktreeCreated: false,
57
+ branchCreated: false,
58
+ staged: false,
59
+ committed: false,
60
+ pushed: false,
61
+ tagged: false,
62
+ published: false,
63
+ deployed: false,
64
+ };
65
+ }
66
+
67
+ function compareUnicodeCodeUnits(left, right) {
68
+ if (left < right) return -1;
69
+ if (left > right) return 1;
70
+ return 0;
71
+ }
72
+
73
+ function git(workspaceRoot, args, label, { trim = true } = {}) {
74
+ const completed = spawnSync("git", ["-C", workspaceRoot, ...args], {
75
+ encoding: "utf8",
76
+ maxBuffer: 64 * 1024 * 1024,
77
+ });
78
+ if (completed.error) throw completed.error;
79
+ if (completed.status !== 0) {
80
+ throw new Error(`${label} failed: ${completed.stderr.trim()}`);
81
+ }
82
+ return trim ? completed.stdout.trim() : completed.stdout;
83
+ }
84
+
85
+ function parseStatus(workspaceRoot) {
86
+ const raw = git(workspaceRoot, [
87
+ "status",
88
+ "--porcelain=v1",
89
+ "-z",
90
+ "--untracked-files=all",
91
+ "--",
92
+ ".",
93
+ ":(exclude).owlcoda/runkit/**",
94
+ ], "Git status", { trim: false });
95
+ if (raw.length === 0) return [];
96
+ const entries = raw.split("\0");
97
+ const records = [];
98
+ for (let index = 0; index < entries.length; index += 1) {
99
+ const entry = entries[index];
100
+ if (!entry) continue;
101
+ if (entry.length < 4 || entry[2] !== " ") {
102
+ throw new Error("Git status emitted a malformed entry.");
103
+ }
104
+ const status = entry.slice(0, 2);
105
+ const currentPath = entry.slice(3);
106
+ const paths = [currentPath];
107
+ if (status.includes("R") || status.includes("C")) {
108
+ const previousPath = entries[index + 1];
109
+ if (!previousPath) throw new Error("Git status rename entry is incomplete.");
110
+ paths.push(previousPath);
111
+ index += 1;
112
+ }
113
+ records.push({ status, currentPath, paths });
114
+ }
115
+ return records;
116
+ }
117
+
118
+ function safeManifestPath(filePath) {
119
+ if (
120
+ typeof filePath !== "string"
121
+ || !filePath
122
+ || path.isAbsolute(filePath)
123
+ || path.win32.parse(filePath).root
124
+ || filePath.includes("\\")
125
+ || filePath.includes("\0")
126
+ || isReservedRuntimePath(filePath)
127
+ ) {
128
+ return false;
129
+ }
130
+ const segments = filePath.split("/");
131
+ return segments.every((segment) => segment && segment !== "." && segment !== "..");
132
+ }
133
+
134
+ function pathMatches(filePath, ownedPath) {
135
+ if (ownedPath.endsWith("/**")) {
136
+ const prefix = ownedPath.slice(0, -3);
137
+ return filePath === prefix || filePath.startsWith(`${prefix}/`);
138
+ }
139
+ return filePath === ownedPath;
140
+ }
141
+
142
+ function isOwned(filePath, ownedPaths) {
143
+ return ownedPaths.some((ownedPath) => pathMatches(filePath, ownedPath));
144
+ }
145
+
146
+ function validOwnedPath(ownedPath) {
147
+ if (typeof ownedPath !== "string" || !ownedPath) return false;
148
+ if (!ownedPath.endsWith("/**")) return safeManifestPath(ownedPath);
149
+ return safeManifestPath(ownedPath.slice(0, -3));
150
+ }
151
+
152
+ function within(root, candidate) {
153
+ const relative = path.relative(root, candidate);
154
+ return relative === ""
155
+ || (
156
+ relative !== ".."
157
+ && !relative.startsWith(`..${path.sep}`)
158
+ && !path.isAbsolute(relative)
159
+ );
160
+ }
161
+
162
+ function inspectManifestPath(workspaceRoot, filePath, { mustExist }) {
163
+ if (!safeManifestPath(filePath)) {
164
+ throw new Error(`Source candidate path is unsafe: ${filePath}`);
165
+ }
166
+ let candidate = workspaceRoot;
167
+ const segments = filePath.split("/");
168
+ for (let index = 0; index < segments.length; index += 1) {
169
+ candidate = path.resolve(candidate, segments[index]);
170
+ if (!within(workspaceRoot, candidate)) {
171
+ throw new Error(`Source candidate path escapes workspace: ${filePath}`);
172
+ }
173
+ let stat;
174
+ try {
175
+ stat = lstatSync(candidate);
176
+ } catch (error) {
177
+ if (error?.code === "ENOENT" && !mustExist) return null;
178
+ throw error;
179
+ }
180
+ if (stat.isSymbolicLink()) {
181
+ throw new Error(`Source candidate path is a symlink: ${filePath}`);
182
+ }
183
+ const leaf = index === segments.length - 1;
184
+ if (!leaf && !stat.isDirectory()) {
185
+ throw new Error(`Source candidate path has a non-directory parent: ${filePath}`);
186
+ }
187
+ if (leaf && mustExist && !stat.isFile()) {
188
+ throw new Error(`Source candidate path is not a regular file: ${filePath}`);
189
+ }
190
+ if (leaf && !mustExist) {
191
+ throw new Error(`Source candidate path expected to be absent: ${filePath}`);
192
+ }
193
+ }
194
+ const realCandidate = realpathSync(candidate);
195
+ if (!within(workspaceRoot, realCandidate)) {
196
+ throw new Error(`Source candidate path resolves outside workspace: ${filePath}`);
197
+ }
198
+ return realCandidate;
199
+ }
200
+
201
+ function hashManifestFile(workspaceRoot, filePath) {
202
+ return sha256(readFileSync(inspectManifestPath(
203
+ workspaceRoot,
204
+ filePath,
205
+ { mustExist: true },
206
+ )));
207
+ }
208
+
209
+ function manifestOperation(status) {
210
+ if (status.includes("R")) return "renamed";
211
+ if (status.includes("C") || status.includes("U")) {
212
+ throw new Error(`Source candidate does not support Git status ${status}.`);
213
+ }
214
+ if (status.includes("D")) return "deleted";
215
+ if (status === "??" || status.includes("A")) return "added";
216
+ if (status.includes("M") || status.includes("T")) return "modified";
217
+ throw new Error(`Source candidate does not support Git status ${status}.`);
218
+ }
219
+
220
+ function sourceManifestV2({ workspaceRoot, ownedPaths }) {
221
+ const records = parseStatus(workspaceRoot);
222
+ const entries = [];
223
+ for (const record of records) {
224
+ const ownedRecordPaths = record.paths.filter((filePath) => isOwned(filePath, ownedPaths));
225
+ if (ownedRecordPaths.length === 0) continue;
226
+ const operation = manifestOperation(record.status);
227
+ if (
228
+ (operation === "renamed" && ownedRecordPaths.length !== 2)
229
+ || (operation !== "renamed" && !isOwned(record.currentPath, ownedPaths))
230
+ ) {
231
+ throw new Error("Source candidate rename crosses its active lease.");
232
+ }
233
+ for (const filePath of record.paths) {
234
+ if (!safeManifestPath(filePath)) {
235
+ throw new Error(`Source candidate path is unsafe: ${filePath}`);
236
+ }
237
+ }
238
+ if (operation === "deleted") {
239
+ inspectManifestPath(workspaceRoot, record.currentPath, { mustExist: false });
240
+ entries.push({
241
+ operation,
242
+ status: record.status,
243
+ path: record.currentPath,
244
+ });
245
+ continue;
246
+ }
247
+ if (operation === "renamed") {
248
+ inspectManifestPath(workspaceRoot, record.paths[1], { mustExist: false });
249
+ entries.push({
250
+ operation,
251
+ status: record.status,
252
+ path: record.currentPath,
253
+ previousPath: record.paths[1],
254
+ sha256: hashManifestFile(workspaceRoot, record.currentPath),
255
+ });
256
+ continue;
257
+ }
258
+ entries.push({
259
+ operation,
260
+ status: record.status,
261
+ path: record.currentPath,
262
+ sha256: hashManifestFile(workspaceRoot, record.currentPath),
263
+ });
264
+ }
265
+ entries.sort((left, right) => (
266
+ compareUnicodeCodeUnits(left.path, right.path)
267
+ || compareUnicodeCodeUnits(left.previousPath ?? "", right.previousPath ?? "")
268
+ ));
269
+ if (entries.length === 0) {
270
+ throw new Error("No dirty source paths are owned by the active lease.");
271
+ }
272
+ const body = {
273
+ schemaVersion: "OwlCodaRunKitDirtySourceManifestV2",
274
+ statusMode: "porcelain-v1-z-untracked-all-runkit-excluded",
275
+ ownedPaths: [...ownedPaths],
276
+ entries,
277
+ };
278
+ return {
279
+ ...body,
280
+ sha256: candidateHash(body),
281
+ };
282
+ }
283
+
284
+ function freezePayloadV1({
285
+ workspaceRoot,
286
+ candidateRoot,
287
+ candidateId,
288
+ manifest,
289
+ }) {
290
+ const payloadRoot = path.join(candidateRoot, `${candidateId}.payload`);
291
+ mkdirSync(payloadRoot);
292
+ mkdirSync(path.join(payloadRoot, "sha256"));
293
+ const payloadRootRelative = relativeToWorkspace(workspaceRoot, payloadRoot);
294
+ const entries = [];
295
+ const blobs = new Map();
296
+ for (const manifestEntry of manifest.entries) {
297
+ if (manifestEntry.operation === "deleted") continue;
298
+ const sourcePath = inspectManifestPath(
299
+ workspaceRoot,
300
+ manifestEntry.path,
301
+ { mustExist: true },
302
+ );
303
+ const stat = lstatSync(sourcePath);
304
+ const bytes = readFileSync(sourcePath);
305
+ const digest = sha256(bytes);
306
+ if (digest !== manifestEntry.sha256) {
307
+ throw new Error(`Source changed while freezing payload: ${manifestEntry.path}`);
308
+ }
309
+ const payloadPath = path.join(payloadRoot, "sha256", digest);
310
+ if (!blobs.has(digest)) {
311
+ writeFileSync(payloadPath, bytes, { flag: "wx", mode: 0o600 });
312
+ blobs.set(digest, bytes.length);
313
+ } else if (
314
+ lstatSync(payloadPath).isSymbolicLink()
315
+ || sha256(readFileSync(payloadPath)) !== digest
316
+ ) {
317
+ throw new Error(`Source payload blob drifted during freeze: ${digest}`);
318
+ }
319
+ entries.push({
320
+ path: manifestEntry.path,
321
+ payloadPath: relativeToWorkspace(workspaceRoot, payloadPath),
322
+ sha256: digest,
323
+ sizeBytes: bytes.length,
324
+ mode: stat.mode & 0o777,
325
+ });
326
+ }
327
+ const body = {
328
+ schemaVersion: "OwlCodaRunKitSourcePayloadV1",
329
+ storage: "create_only_content_addressed_sha256",
330
+ root: payloadRootRelative,
331
+ blobCount: blobs.size,
332
+ totalBytes: [...blobs.values()].reduce((total, size) => total + size, 0),
333
+ entries,
334
+ };
335
+ return {
336
+ ...body,
337
+ sha256: candidateHash(body),
338
+ };
339
+ }
340
+
341
+ function payloadContractIssues({
342
+ workspaceRoot,
343
+ candidatePath,
344
+ manifest,
345
+ payload,
346
+ }) {
347
+ const issues = [];
348
+ const candidateBase = path.basename(candidatePath, ".json");
349
+ const expectedRoot = relativeToWorkspace(
350
+ workspaceRoot,
351
+ path.join(path.dirname(candidatePath), `${candidateBase}.payload`),
352
+ );
353
+ const presentEntries = manifest.entries.filter(
354
+ (entry) => entry.operation !== "deleted",
355
+ );
356
+ if (
357
+ payload?.schemaVersion !== "OwlCodaRunKitSourcePayloadV1"
358
+ || payload.storage !== "create_only_content_addressed_sha256"
359
+ || payload.root !== expectedRoot
360
+ || !Number.isInteger(payload.blobCount)
361
+ || payload.blobCount < 0
362
+ || !Number.isInteger(payload.totalBytes)
363
+ || payload.totalBytes < 0
364
+ || !Array.isArray(payload.entries)
365
+ || payload.entries.length !== presentEntries.length
366
+ ) {
367
+ return ["source_candidate_payload_contract_invalid"];
368
+ }
369
+ const { sha256: payloadSha256, ...body } = payload;
370
+ if (payloadSha256 !== candidateHash(body)) {
371
+ issues.push("source_candidate_payload_contract_invalid");
372
+ }
373
+ const expectedByPath = new Map(presentEntries.map((entry) => [entry.path, entry]));
374
+ const seenPaths = new Set();
375
+ const seenBlobs = new Map();
376
+ for (const entry of payload.entries) {
377
+ const manifestEntry = expectedByPath.get(entry?.path);
378
+ if (
379
+ !manifestEntry
380
+ || seenPaths.has(entry.path)
381
+ || entry.sha256 !== manifestEntry.sha256
382
+ || !Number.isInteger(entry.sizeBytes)
383
+ || entry.sizeBytes < 0
384
+ || !Number.isInteger(entry.mode)
385
+ || entry.mode < 0
386
+ || entry.mode > 0o777
387
+ || entry.payloadPath
388
+ !== `${payload.root}/sha256/${entry.sha256}`
389
+ ) {
390
+ issues.push("source_candidate_payload_contract_invalid");
391
+ continue;
392
+ }
393
+ seenPaths.add(entry.path);
394
+ try {
395
+ const payloadPath = resolveExistingArtifact(
396
+ workspaceRoot,
397
+ entry.payloadPath,
398
+ "payloadPath",
399
+ );
400
+ const stat = lstatSync(payloadPath);
401
+ const bytes = readFileSync(payloadPath);
402
+ if (
403
+ stat.isSymbolicLink()
404
+ || !stat.isFile()
405
+ || bytes.length !== entry.sizeBytes
406
+ || sha256(bytes) !== entry.sha256
407
+ ) {
408
+ issues.push("source_candidate_payload_drift");
409
+ continue;
410
+ }
411
+ seenBlobs.set(entry.sha256, entry.sizeBytes);
412
+ } catch {
413
+ issues.push("source_candidate_payload_unreadable");
414
+ }
415
+ }
416
+ const totalBytes = [...seenBlobs.values()].reduce(
417
+ (total, size) => total + size,
418
+ 0,
419
+ );
420
+ if (
421
+ seenPaths.size !== presentEntries.length
422
+ || seenBlobs.size !== payload.blobCount
423
+ || totalBytes !== payload.totalBytes
424
+ ) {
425
+ issues.push("source_candidate_payload_contract_invalid");
426
+ }
427
+ return [...new Set(issues)];
428
+ }
429
+
430
+ function activeLease({ workspaceRoot, executionRoot, workItemId }) {
431
+ const matches = listLeaseArtifacts({ workspaceRoot, executionRoot })
432
+ .filter((lease) => lease.workItemId === workItemId);
433
+ if (matches.length !== 1 || matches[0].state !== "active") {
434
+ throw new Error(`Source candidate requires one active lease: ${workItemId}`);
435
+ }
436
+ return matches[0];
437
+ }
438
+
439
+ function manifestContractValid(manifest) {
440
+ if (
441
+ manifest?.schemaVersion !== "OwlCodaRunKitDirtySourceManifestV2"
442
+ || manifest.statusMode !== "porcelain-v1-z-untracked-all-runkit-excluded"
443
+ || !Array.isArray(manifest.ownedPaths)
444
+ || manifest.ownedPaths.length === 0
445
+ || new Set(manifest.ownedPaths).size !== manifest.ownedPaths.length
446
+ || manifest.ownedPaths.some((ownedPath) => !validOwnedPath(ownedPath))
447
+ || !Array.isArray(manifest.entries)
448
+ || manifest.entries.length === 0
449
+ ) {
450
+ return false;
451
+ }
452
+ for (const entry of manifest.entries) {
453
+ if (
454
+ !entry
455
+ || !["added", "deleted", "modified", "renamed"].includes(entry.operation)
456
+ || typeof entry.status !== "string"
457
+ || entry.status.length !== 2
458
+ || !safeManifestPath(entry.path)
459
+ || !isOwned(entry.path, manifest.ownedPaths)
460
+ ) {
461
+ return false;
462
+ }
463
+ try {
464
+ if (manifestOperation(entry.status) !== entry.operation) return false;
465
+ } catch {
466
+ return false;
467
+ }
468
+ if (entry.operation === "renamed") {
469
+ if (
470
+ !safeManifestPath(entry.previousPath)
471
+ || !isOwned(entry.previousPath, manifest.ownedPaths)
472
+ || !/^[a-f0-9]{64}$/u.test(entry.sha256)
473
+ ) {
474
+ return false;
475
+ }
476
+ } else if (
477
+ entry.previousPath !== undefined
478
+ || (
479
+ entry.operation === "deleted"
480
+ ? entry.sha256 !== undefined
481
+ : !/^[a-f0-9]{64}$/u.test(entry.sha256)
482
+ )
483
+ ) {
484
+ return false;
485
+ }
486
+ }
487
+ const sortedEntries = [...manifest.entries].sort((left, right) => (
488
+ compareUnicodeCodeUnits(left.path, right.path)
489
+ || compareUnicodeCodeUnits(left.previousPath ?? "", right.previousPath ?? "")
490
+ ));
491
+ if (
492
+ canonicalJson(sortedEntries) !== canonicalJson(manifest.entries)
493
+ || new Set(manifest.entries.flatMap((entry) => (
494
+ entry.previousPath ? [entry.path, entry.previousPath] : [entry.path]
495
+ ))).size !== manifest.entries.reduce(
496
+ (count, entry) => count + (entry.previousPath ? 2 : 1),
497
+ 0,
498
+ )
499
+ ) {
500
+ return false;
501
+ }
502
+ const { sha256: manifestSha256, ...body } = manifest;
503
+ return manifestSha256 === candidateHash(body);
504
+ }
505
+
506
+ function repositoryActionsAreFalse(actions) {
507
+ const expected = repositoryActionsFalse();
508
+ return actions
509
+ && Object.keys(expected).every((key) => actions[key] === false)
510
+ && Object.keys(actions).length === Object.keys(expected).length;
511
+ }
512
+
513
+ function candidateV2ContractIssues(candidate) {
514
+ const { candidateSha256, ...body } = candidate ?? {};
515
+ if (
516
+ candidate?.schemaVersion !== "OwlCodaRunKitSourceCandidateV2"
517
+ || candidate.status !== "frozen"
518
+ || candidate.sourceMode !== "dirty_worktree_exact_manifest_v2"
519
+ || candidate.authorizationGranted !== false
520
+ || typeof candidateSha256 !== "string"
521
+ || candidateSha256 !== candidateHash(body)
522
+ || typeof candidate.runId !== "string"
523
+ || typeof candidate.candidateId !== "string"
524
+ || typeof candidate.baseline?.branch !== "string"
525
+ || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u.test(candidate.baseline?.head)
526
+ || typeof candidate.discovery?.fromLease !== "string"
527
+ || typeof candidate.discovery?.leasePath !== "string"
528
+ || !repositoryActionsAreFalse(candidate.repositoryActions)
529
+ || !manifestContractValid(candidate.sourceManifest)
530
+ || candidate.sourceFingerprint?.sha256
531
+ !== candidate.sourceManifest?.sha256?.slice("sha256:".length)
532
+ || candidate.payload?.schemaVersion !== "OwlCodaRunKitSourcePayloadV1"
533
+ ) {
534
+ return ["source_candidate_contract_invalid"];
535
+ }
536
+ return [];
537
+ }
538
+
539
+ function currentBaseline(workspaceRoot) {
540
+ return {
541
+ branch: git(workspaceRoot, ["rev-parse", "--abbrev-ref", "HEAD"], "Git branch"),
542
+ head: git(workspaceRoot, ["rev-parse", "HEAD"], "Git HEAD"),
543
+ };
544
+ }
545
+
546
+ function candidateBody({ runId, candidateId, packetPath, packet, packetBytes }) {
547
+ return {
548
+ schemaVersion: "OwlCodaRunKitSourceCandidateV1",
549
+ runId,
550
+ candidateId,
551
+ status: "frozen",
552
+ sourceMode: "dirty_worktree_exact_manifest",
553
+ baseline: structuredClone(packet.baseline),
554
+ changedFiles: structuredClone(packet.changedFiles),
555
+ sourceFingerprint: structuredClone(packet.sourceFingerprint),
556
+ deliveryPacketPath: packetPath,
557
+ deliveryPacketSha256: `sha256:${sha256(packetBytes)}`,
558
+ repositoryActions: structuredClone(packet.repositoryActions),
559
+ authorizationGranted: false,
560
+ };
561
+ }
562
+
563
+ export function freezeSourceCandidateWithinControlTransactionV1({
564
+ workspaceRoot,
565
+ runId,
566
+ workItemId,
567
+ candidateId,
568
+ }) {
569
+ safeIdentifier(candidateId, "candidateId");
570
+ const root = realpathSync(workspaceRoot);
571
+ const { executionRoot, pinGate } = loadActiveExecution(root, runId);
572
+ if (pinGate.status !== "valid") return { ...pinGate, authorizationGranted: false };
573
+ const candidatePath = path.join(
574
+ executionRoot,
575
+ "source-candidates",
576
+ `${candidateId}.json`,
577
+ );
578
+ if (existsSync(candidatePath)) {
579
+ throw new Error(`source candidate already exists: ${candidateId}`);
580
+ }
581
+ const delivery = createDeliveryFromLeaseWithinControlTransaction({
582
+ workspaceRoot: root,
583
+ runId,
584
+ workItemId,
585
+ packetId: `${candidateId}-delivery`,
586
+ });
587
+ if (delivery.status !== "delivery_packet_created") return delivery;
588
+ const absolutePacketPath = path.join(root, delivery.deliveryPacketPath);
589
+ const packetStat = lstatSync(absolutePacketPath);
590
+ if (packetStat.isSymbolicLink() || !packetStat.isFile()) {
591
+ throw new Error("source candidate delivery packet must be a regular file");
592
+ }
593
+ const packetBytes = readFileSync(absolutePacketPath);
594
+ const packet = JSON.parse(packetBytes.toString("utf8"));
595
+ const sourceGate = verifyDeliveryPacket({ workspaceRoot: root, packet });
596
+ if (sourceGate.status !== "valid") {
597
+ throw new Error("source candidate delivery changed before freeze");
598
+ }
599
+ const body = candidateBody({
600
+ runId,
601
+ candidateId,
602
+ packetPath: delivery.deliveryPacketPath,
603
+ packet,
604
+ packetBytes,
605
+ });
606
+ const candidate = {
607
+ ...body,
608
+ candidateSha256: candidateHash(body),
609
+ };
610
+ writeJsonExclusiveAtomically(candidatePath, candidate);
611
+ return {
612
+ status: "source_candidate_frozen",
613
+ exitCode: 0,
614
+ runId,
615
+ candidateId,
616
+ candidatePath: relativeToWorkspace(root, candidatePath),
617
+ candidateSha256: candidate.candidateSha256,
618
+ sourceFingerprint: packet.sourceFingerprint.sha256,
619
+ deliveryPacketPath: delivery.deliveryPacketPath,
620
+ authorizationGranted: false,
621
+ };
622
+ }
623
+
624
+ export function freezeSourceCandidateV1(options) {
625
+ const workspaceRoot = realpathSync(options.workspaceRoot);
626
+ return withControlTransaction(
627
+ workspaceRoot,
628
+ () => freezeSourceCandidateWithinControlTransactionV1({
629
+ ...options,
630
+ workspaceRoot,
631
+ }),
632
+ );
633
+ }
634
+
635
+ export function freezeSourceCandidateWithinControlTransactionV2({
636
+ workspaceRoot,
637
+ runId,
638
+ workItemId,
639
+ candidateId,
640
+ }) {
641
+ safeIdentifier(candidateId, "candidateId");
642
+ const root = realpathSync(workspaceRoot);
643
+ const { executionRoot, pinGate } = loadActiveExecution(root, runId);
644
+ if (pinGate.status !== "valid") return { ...pinGate, authorizationGranted: false };
645
+ const candidateRoot = path.join(executionRoot, "source-candidates");
646
+ if (!existsSync(candidateRoot)) mkdirSync(candidateRoot);
647
+ const candidateRootStat = lstatSync(candidateRoot);
648
+ if (candidateRootStat.isSymbolicLink() || !candidateRootStat.isDirectory()) {
649
+ throw new Error("Source candidate directory must be a regular directory.");
650
+ }
651
+ if (!within(realpathSync(executionRoot), realpathSync(candidateRoot))) {
652
+ throw new Error("Source candidate directory escapes its execution root.");
653
+ }
654
+ const candidatePath = path.join(candidateRoot, `${candidateId}.json`);
655
+ if (existsSync(candidatePath)) {
656
+ throw new Error(`source candidate already exists: ${candidateId}`);
657
+ }
658
+ const lease = activeLease({
659
+ workspaceRoot: root,
660
+ executionRoot,
661
+ workItemId,
662
+ });
663
+ const baselineBefore = currentBaseline(root);
664
+ const statusBefore = sourceManifestV2({
665
+ workspaceRoot: root,
666
+ ownedPaths: lease.ownedPaths,
667
+ });
668
+ const payload = freezePayloadV1({
669
+ workspaceRoot: root,
670
+ candidateRoot,
671
+ candidateId,
672
+ manifest: statusBefore,
673
+ });
674
+ const statusAfter = sourceManifestV2({
675
+ workspaceRoot: root,
676
+ ownedPaths: lease.ownedPaths,
677
+ });
678
+ const baselineAfter = currentBaseline(root);
679
+ if (
680
+ statusBefore.sha256 !== statusAfter.sha256
681
+ || canonicalJson(baselineBefore) !== canonicalJson(baselineAfter)
682
+ ) {
683
+ throw new Error("Owned workspace source changed during candidate freeze.");
684
+ }
685
+ const body = {
686
+ schemaVersion: "OwlCodaRunKitSourceCandidateV2",
687
+ runId,
688
+ candidateId,
689
+ status: "frozen",
690
+ sourceMode: "dirty_worktree_exact_manifest_v2",
691
+ baseline: baselineAfter,
692
+ sourceManifest: statusAfter,
693
+ sourceFingerprint: {
694
+ sha256: statusAfter.sha256.slice("sha256:".length),
695
+ },
696
+ payload,
697
+ discovery: {
698
+ fromLease: workItemId,
699
+ leasePath: lease.leaseRelativePath,
700
+ },
701
+ repositoryActions: repositoryActionsFalse(),
702
+ authorizationGranted: false,
703
+ };
704
+ const candidate = {
705
+ ...body,
706
+ candidateSha256: candidateHash(body),
707
+ };
708
+ writeJsonExclusiveAtomically(candidatePath, candidate);
709
+ return {
710
+ status: "source_candidate_frozen",
711
+ exitCode: 0,
712
+ runId,
713
+ candidateId,
714
+ candidatePath: relativeToWorkspace(root, candidatePath),
715
+ candidateSha256: candidate.candidateSha256,
716
+ sourceFingerprint: candidate.sourceFingerprint.sha256,
717
+ manifestEntryCount: candidate.sourceManifest.entries.length,
718
+ payloadSha256: candidate.payload.sha256,
719
+ ownedPaths: [...candidate.sourceManifest.ownedPaths],
720
+ authorizationGranted: false,
721
+ };
722
+ }
723
+
724
+ export function freezeSourceCandidateV2(options) {
725
+ const workspaceRoot = realpathSync(options.workspaceRoot);
726
+ return withControlTransaction(
727
+ workspaceRoot,
728
+ () => freezeSourceCandidateWithinControlTransactionV2({
729
+ ...options,
730
+ workspaceRoot,
731
+ }),
732
+ );
733
+ }
734
+
735
+ export function verifySourceCandidateV2({
736
+ workspaceRoot,
737
+ candidatePath,
738
+ }) {
739
+ const root = realpathSync(workspaceRoot);
740
+ const issueCodes = [];
741
+ let absoluteCandidatePath;
742
+ let candidate;
743
+ try {
744
+ absoluteCandidatePath = resolveExistingArtifact(
745
+ root,
746
+ candidatePath,
747
+ "candidatePath",
748
+ );
749
+ const stat = lstatSync(absoluteCandidatePath);
750
+ if (stat.isSymbolicLink() || !stat.isFile()) {
751
+ throw new Error("candidate is not a regular file");
752
+ }
753
+ candidate = readJson(absoluteCandidatePath);
754
+ } catch {
755
+ return {
756
+ status: "invalid",
757
+ exitCode: 2,
758
+ issueCodes: ["source_candidate_unreadable"],
759
+ authorizationGranted: false,
760
+ };
761
+ }
762
+ const candidateSha256 = candidate.candidateSha256;
763
+ issueCodes.push(...candidateV2ContractIssues(candidate));
764
+ if (issueCodes.length === 0) {
765
+ issueCodes.push(...payloadContractIssues({
766
+ workspaceRoot: root,
767
+ candidatePath: absoluteCandidatePath,
768
+ manifest: candidate.sourceManifest,
769
+ payload: candidate.payload,
770
+ }));
771
+ }
772
+ if (issueCodes.length === 0) {
773
+ try {
774
+ const leasePath = resolveExistingArtifact(
775
+ root,
776
+ candidate.discovery?.leasePath,
777
+ "leasePath",
778
+ );
779
+ const leaseStat = lstatSync(leasePath);
780
+ if (leaseStat.isSymbolicLink() || !leaseStat.isFile()) {
781
+ throw new Error("lease is not a regular file");
782
+ }
783
+ const lease = readJson(leasePath);
784
+ if (
785
+ lease.workItemId !== candidate.discovery?.fromLease
786
+ || JSON.stringify(lease.ownedPaths)
787
+ !== JSON.stringify(candidate.sourceManifest.ownedPaths)
788
+ ) {
789
+ issueCodes.push("source_candidate_lease_mismatch");
790
+ }
791
+ } catch {
792
+ issueCodes.push("source_candidate_lease_unreadable");
793
+ }
794
+ }
795
+ if (issueCodes.length === 0) {
796
+ try {
797
+ const currentManifest = sourceManifestV2({
798
+ workspaceRoot: root,
799
+ ownedPaths: candidate.sourceManifest.ownedPaths,
800
+ });
801
+ if (currentManifest.sha256 !== candidate.sourceManifest.sha256) {
802
+ issueCodes.push("source_candidate_manifest_drift");
803
+ }
804
+ } catch {
805
+ issueCodes.push("source_candidate_manifest_drift");
806
+ }
807
+ try {
808
+ if (canonicalJson(candidate.baseline) !== canonicalJson(currentBaseline(root))) {
809
+ issueCodes.push("source_candidate_baseline_drift");
810
+ }
811
+ } catch {
812
+ issueCodes.push("source_candidate_baseline_drift");
813
+ }
814
+ }
815
+ return {
816
+ status: issueCodes.length === 0 ? "valid" : "invalid",
817
+ exitCode: issueCodes.length === 0 ? 0 : 2,
818
+ candidatePath: relativeToWorkspace(root, absoluteCandidatePath),
819
+ candidateSha256,
820
+ sourceFingerprint: candidate.sourceFingerprint?.sha256,
821
+ issueCodes: [...new Set(issueCodes)].sort(),
822
+ authorizationGranted: false,
823
+ };
824
+ }
825
+
826
+ class SimulatedMaterializationInterruption extends Error {
827
+ constructor(phase) {
828
+ super(`simulated materialization interruption: ${phase}`);
829
+ this.name = "SimulatedMaterializationInterruption";
830
+ }
831
+ }
832
+
833
+ function materializationTransactionPaths(targetWorkspaceRoot) {
834
+ const requestedTarget = path.resolve(targetWorkspaceRoot);
835
+ const parent = realpathSync(path.dirname(requestedTarget));
836
+ const target = path.join(parent, path.basename(requestedTarget));
837
+ const transactionId = sha256(Buffer.from(target)).slice(0, 32);
838
+ const prefix = path.join(
839
+ parent,
840
+ `.owlrunkit-source-materialize-${transactionId}`,
841
+ );
842
+ return {
843
+ target,
844
+ staging: `${prefix}.staging`,
845
+ backup: `${prefix}.backup`,
846
+ journal: `${prefix}.journal.json`,
847
+ lock: `${prefix}.lock`,
848
+ };
849
+ }
850
+
851
+ function processIsRunning(pid) {
852
+ if (!Number.isInteger(pid) || pid <= 0) return false;
853
+ try {
854
+ process.kill(pid, 0);
855
+ return true;
856
+ } catch (error) {
857
+ return error?.code !== "ESRCH";
858
+ }
859
+ }
860
+
861
+ function acquireMaterializationLock(paths) {
862
+ const owner = {
863
+ schemaVersion: "OwlCodaRunKitSourceMaterializationLockV1",
864
+ pid: process.pid,
865
+ targetWorkspaceRoot: paths.target,
866
+ };
867
+ for (let attempt = 0; attempt < 4; attempt += 1) {
868
+ try {
869
+ writeJsonExclusiveAtomically(paths.lock, owner);
870
+ return () => rmSync(paths.lock, { force: true });
871
+ } catch (error) {
872
+ if (error?.code !== "EEXIST") throw error;
873
+ let active = true;
874
+ let staleIdentity;
875
+ try {
876
+ const stat = lstatSync(paths.lock);
877
+ const currentOwner = JSON.parse(readFileSync(paths.lock, "utf8"));
878
+ staleIdentity = { dev: stat.dev, ino: stat.ino };
879
+ active = (
880
+ stat.isFile()
881
+ && !stat.isSymbolicLink()
882
+ && currentOwner.schemaVersion
883
+ === "OwlCodaRunKitSourceMaterializationLockV1"
884
+ && currentOwner.targetWorkspaceRoot === paths.target
885
+ && processIsRunning(currentOwner.pid)
886
+ );
887
+ } catch {
888
+ active = true;
889
+ }
890
+ if (active) {
891
+ throw new Error("Source candidate materialization is already active.");
892
+ }
893
+ const latest = lstatSync(paths.lock);
894
+ if (
895
+ latest.dev !== staleIdentity.dev
896
+ || latest.ino !== staleIdentity.ino
897
+ ) {
898
+ continue;
899
+ }
900
+ rmSync(paths.lock, { force: true });
901
+ }
902
+ }
903
+ throw new Error("Source candidate materialization lock could not be acquired.");
904
+ }
905
+
906
+ function readMaterializationJournal(paths) {
907
+ const stat = lstatSync(paths.journal);
908
+ if (stat.isSymbolicLink() || !stat.isFile()) {
909
+ throw new Error("Source candidate materialization journal is unsafe.");
910
+ }
911
+ const journal = readJson(paths.journal);
912
+ if (
913
+ journal?.schemaVersion !== "OwlCodaRunKitSourceMaterializationJournalV1"
914
+ || journal.targetWorkspaceRoot !== paths.target
915
+ || journal.stagingPath !== paths.staging
916
+ || journal.backupPath !== paths.backup
917
+ || ![
918
+ "initializing",
919
+ "staged",
920
+ "prepared",
921
+ "target_backed_up",
922
+ "switched",
923
+ "committed",
924
+ ].includes(journal.phase)
925
+ || typeof journal.candidateSha256 !== "string"
926
+ ) {
927
+ throw new Error("Source candidate materialization journal is invalid.");
928
+ }
929
+ return journal;
930
+ }
931
+
932
+ function writeMaterializationPhase(paths, journal, phase) {
933
+ const next = {
934
+ ...journal,
935
+ phase,
936
+ };
937
+ writeJsonAtomically(paths.journal, next);
938
+ return next;
939
+ }
940
+
941
+ function validateMaterializedCandidate(target, candidate) {
942
+ if (
943
+ git(target, ["rev-parse", "HEAD"], "Materialized Git HEAD")
944
+ !== candidate.baseline.head
945
+ ) {
946
+ throw new Error("Materialized source candidate changed the baseline.");
947
+ }
948
+ const expectedDirtyPaths = [...new Set(
949
+ candidate.sourceManifest.entries.flatMap((entry) => (
950
+ entry.previousPath ? [entry.path, entry.previousPath] : [entry.path]
951
+ )),
952
+ )].sort(compareUnicodeCodeUnits);
953
+ const actualDirtyPaths = [...new Set(
954
+ parseStatus(target).flatMap((record) => record.paths),
955
+ )].sort(compareUnicodeCodeUnits);
956
+ if (canonicalJson(actualDirtyPaths) !== canonicalJson(expectedDirtyPaths)) {
957
+ throw new Error(
958
+ "Materialized source candidate contains unexpected Git changes.",
959
+ );
960
+ }
961
+ const payloadEntries = new Map(
962
+ candidate.payload.entries.map((entry) => [entry.path, entry]),
963
+ );
964
+ for (const entry of candidate.sourceManifest.entries) {
965
+ if (entry.operation === "deleted") {
966
+ inspectManifestPath(target, entry.path, { mustExist: false });
967
+ continue;
968
+ }
969
+ if (entry.operation === "renamed") {
970
+ inspectManifestPath(target, entry.previousPath, { mustExist: false });
971
+ }
972
+ const payloadEntry = payloadEntries.get(entry.path);
973
+ const materializedPath = inspectManifestPath(
974
+ target,
975
+ entry.path,
976
+ { mustExist: true },
977
+ );
978
+ const stat = lstatSync(materializedPath);
979
+ if (
980
+ !payloadEntry
981
+ || hashManifestFile(target, entry.path) !== payloadEntry.sha256
982
+ || (stat.mode & 0o777) !== payloadEntry.mode
983
+ ) {
984
+ throw new Error(`Materialized source candidate drifted: ${entry.path}`);
985
+ }
986
+ }
987
+ }
988
+
989
+ function rollbackMaterialization(paths) {
990
+ const targetExists = existsSync(paths.target);
991
+ const stagingExists = existsSync(paths.staging);
992
+ const backupExists = existsSync(paths.backup);
993
+ if (backupExists) {
994
+ if (targetExists && stagingExists) {
995
+ throw new Error("Source candidate materialization state is ambiguous.");
996
+ }
997
+ if (targetExists) renameSync(paths.target, paths.staging);
998
+ renameSync(paths.backup, paths.target);
999
+ } else if (!targetExists) {
1000
+ throw new Error("Source candidate materialization baseline cannot be restored.");
1001
+ }
1002
+ rmSync(paths.staging, { recursive: true, force: true });
1003
+ rmSync(paths.journal, { force: true });
1004
+ }
1005
+
1006
+ function recoverMaterialization({
1007
+ paths,
1008
+ candidate,
1009
+ }) {
1010
+ if (!existsSync(paths.journal)) return { recovered: false, completed: false };
1011
+ const journal = readMaterializationJournal(paths);
1012
+ if (journal.phase === "committed") {
1013
+ let exact = false;
1014
+ if (
1015
+ journal.candidateSha256 === candidate.candidateSha256
1016
+ && existsSync(paths.target)
1017
+ ) {
1018
+ try {
1019
+ validateMaterializedCandidate(paths.target, candidate);
1020
+ exact = true;
1021
+ } catch {
1022
+ exact = false;
1023
+ }
1024
+ }
1025
+ if (exact) {
1026
+ rmSync(paths.backup, { recursive: true, force: true });
1027
+ rmSync(paths.staging, { recursive: true, force: true });
1028
+ rmSync(paths.journal, { force: true });
1029
+ return { recovered: true, completed: true };
1030
+ }
1031
+ }
1032
+ rollbackMaterialization(paths);
1033
+ return { recovered: true, completed: false };
1034
+ }
1035
+
1036
+ function materializationResult(candidate, {
1037
+ resumed = false,
1038
+ recovered = false,
1039
+ } = {}) {
1040
+ return {
1041
+ status: "source_candidate_materialized",
1042
+ exitCode: 0,
1043
+ candidateSha256: candidate.candidateSha256,
1044
+ payloadSha256: candidate.payload.sha256,
1045
+ sourceFingerprint: candidate.sourceFingerprint.sha256,
1046
+ baselineHead: candidate.baseline.head,
1047
+ materializedEntryCount: candidate.sourceManifest.entries.length,
1048
+ resumed,
1049
+ recovered,
1050
+ repositoryActions: repositoryActionsFalse(),
1051
+ authorizationGranted: false,
1052
+ };
1053
+ }
1054
+
1055
+ function applyCandidateToStaging({
1056
+ root,
1057
+ staging,
1058
+ candidate,
1059
+ hooks,
1060
+ }) {
1061
+ for (const entry of candidate.sourceManifest.entries) {
1062
+ if (entry.operation === "deleted") {
1063
+ const deletedPath = inspectManifestPath(staging, entry.path, {
1064
+ mustExist: true,
1065
+ });
1066
+ unlinkSync(deletedPath);
1067
+ } else if (entry.operation === "renamed") {
1068
+ const previousPath = inspectManifestPath(
1069
+ staging,
1070
+ entry.previousPath,
1071
+ { mustExist: true },
1072
+ );
1073
+ unlinkSync(previousPath);
1074
+ }
1075
+ }
1076
+
1077
+ const payloadEntries = new Map(
1078
+ candidate.payload.entries.map((entry) => [entry.path, entry]),
1079
+ );
1080
+ for (const entry of candidate.sourceManifest.entries) {
1081
+ if (entry.operation === "deleted") continue;
1082
+ const payloadEntry = payloadEntries.get(entry.path);
1083
+ if (!payloadEntry) {
1084
+ throw new Error(`Source candidate payload is missing: ${entry.path}`);
1085
+ }
1086
+ const targetPath = path.join(staging, entry.path);
1087
+ if (entry.operation === "modified") {
1088
+ const current = inspectManifestPath(staging, entry.path, {
1089
+ mustExist: true,
1090
+ });
1091
+ unlinkSync(current);
1092
+ } else {
1093
+ inspectManifestPath(staging, entry.path, { mustExist: false });
1094
+ }
1095
+ mkdirSync(path.dirname(targetPath), { recursive: true });
1096
+ const payloadPath = resolveExistingArtifact(
1097
+ root,
1098
+ payloadEntry.payloadPath,
1099
+ "payloadPath",
1100
+ );
1101
+ const bytes = readFileSync(payloadPath);
1102
+ if (
1103
+ bytes.length !== payloadEntry.sizeBytes
1104
+ || sha256(bytes) !== payloadEntry.sha256
1105
+ ) {
1106
+ throw new Error(`Source candidate payload drifted: ${entry.path}`);
1107
+ }
1108
+ hooks?.beforePayloadWrite?.({ path: entry.path });
1109
+ writeFileSync(targetPath, bytes, {
1110
+ flag: "wx",
1111
+ mode: payloadEntry.mode,
1112
+ });
1113
+ hooks?.beforePayloadChmod?.({ path: entry.path });
1114
+ chmodSync(targetPath, payloadEntry.mode);
1115
+ }
1116
+ validateMaterializedCandidate(staging, candidate);
1117
+ }
1118
+
1119
+ export function materializeSourceCandidateV2({
1120
+ workspaceRoot,
1121
+ candidatePath,
1122
+ targetWorkspaceRoot,
1123
+ hooks,
1124
+ }) {
1125
+ const root = realpathSync(workspaceRoot);
1126
+ const absoluteCandidatePath = resolveExistingArtifact(
1127
+ root,
1128
+ candidatePath,
1129
+ "candidatePath",
1130
+ );
1131
+ const candidateStat = lstatSync(absoluteCandidatePath);
1132
+ if (candidateStat.isSymbolicLink() || !candidateStat.isFile()) {
1133
+ throw new Error("Source candidate must be a regular file.");
1134
+ }
1135
+ const candidate = readJson(absoluteCandidatePath);
1136
+ const issues = [
1137
+ ...candidateV2ContractIssues(candidate),
1138
+ ...payloadContractIssues({
1139
+ workspaceRoot: root,
1140
+ candidatePath: absoluteCandidatePath,
1141
+ manifest: candidate.sourceManifest,
1142
+ payload: candidate.payload,
1143
+ }),
1144
+ ];
1145
+ if (issues.length > 0) {
1146
+ throw new Error(`Source candidate payload is invalid: ${[...new Set(issues)].join(",")}`);
1147
+ }
1148
+
1149
+ const paths = materializationTransactionPaths(targetWorkspaceRoot);
1150
+ const releaseLock = acquireMaterializationLock(paths);
1151
+ try {
1152
+ const recovered = recoverMaterialization({ paths, candidate });
1153
+ if (recovered.completed) {
1154
+ return materializationResult(candidate, {
1155
+ resumed: true,
1156
+ recovered: true,
1157
+ });
1158
+ }
1159
+ if (recovered.recovered) hooks?.afterRecovery?.();
1160
+
1161
+ const targetStat = lstatSync(paths.target);
1162
+ if (targetStat.isSymbolicLink() || !targetStat.isDirectory()) {
1163
+ throw new Error("Materialization target must be a regular directory.");
1164
+ }
1165
+ if (realpathSync(paths.target) !== paths.target) {
1166
+ throw new Error("Materialization target must not resolve through a symlink.");
1167
+ }
1168
+ if (
1169
+ git(paths.target, ["rev-parse", "HEAD"], "Target Git HEAD")
1170
+ !== candidate.baseline.head
1171
+ ) {
1172
+ throw new Error("Materialization target does not match the candidate baseline.");
1173
+ }
1174
+ if (parseStatus(paths.target).length !== 0) {
1175
+ throw new Error("Materialization target must be a clean baseline checkout.");
1176
+ }
1177
+ if (
1178
+ existsSync(paths.staging)
1179
+ || existsSync(paths.backup)
1180
+ || existsSync(paths.journal)
1181
+ ) {
1182
+ throw new Error("Source candidate materialization transaction paths are occupied.");
1183
+ }
1184
+
1185
+ let journal = {
1186
+ schemaVersion: "OwlCodaRunKitSourceMaterializationJournalV1",
1187
+ targetWorkspaceRoot: paths.target,
1188
+ stagingPath: paths.staging,
1189
+ backupPath: paths.backup,
1190
+ candidateSha256: candidate.candidateSha256,
1191
+ phase: "initializing",
1192
+ };
1193
+ writeJsonExclusiveAtomically(paths.journal, journal);
1194
+ try {
1195
+ cpSync(paths.target, paths.staging, {
1196
+ recursive: true,
1197
+ errorOnExist: true,
1198
+ force: false,
1199
+ preserveTimestamps: true,
1200
+ verbatimSymlinks: true,
1201
+ });
1202
+ journal = writeMaterializationPhase(paths, journal, "staged");
1203
+ applyCandidateToStaging({
1204
+ root,
1205
+ staging: paths.staging,
1206
+ candidate,
1207
+ hooks,
1208
+ });
1209
+ journal = writeMaterializationPhase(paths, journal, "prepared");
1210
+ renameSync(paths.target, paths.backup);
1211
+ journal = writeMaterializationPhase(paths, journal, "target_backed_up");
1212
+ hooks?.afterTargetBackup?.();
1213
+ if (hooks?.simulateInterruptionAt === "after_target_backup") {
1214
+ throw new SimulatedMaterializationInterruption("after_target_backup");
1215
+ }
1216
+ renameSync(paths.staging, paths.target);
1217
+ journal = writeMaterializationPhase(paths, journal, "switched");
1218
+ hooks?.afterTargetSwitch?.();
1219
+ if (hooks?.simulateInterruptionAt === "after_target_switch") {
1220
+ throw new SimulatedMaterializationInterruption("after_target_switch");
1221
+ }
1222
+ validateMaterializedCandidate(paths.target, candidate);
1223
+ journal = writeMaterializationPhase(paths, journal, "committed");
1224
+ if (hooks?.simulateInterruptionAt === "after_commit") {
1225
+ throw new SimulatedMaterializationInterruption("after_commit");
1226
+ }
1227
+ rmSync(paths.backup, { recursive: true, force: true });
1228
+ rmSync(paths.journal, { force: true });
1229
+ } catch (error) {
1230
+ if (error instanceof SimulatedMaterializationInterruption) throw error;
1231
+ const recovery = recoverMaterialization({ paths, candidate });
1232
+ if (recovery.completed) {
1233
+ return materializationResult(candidate, {
1234
+ resumed: true,
1235
+ recovered: true,
1236
+ });
1237
+ }
1238
+ throw error;
1239
+ }
1240
+ return materializationResult(candidate, {
1241
+ recovered: recovered.recovered,
1242
+ });
1243
+ } finally {
1244
+ releaseLock();
1245
+ }
1246
+ }
1247
+
1248
+ export function verifySourceCandidatePathClosureV2({
1249
+ workspaceRoot,
1250
+ candidatePath,
1251
+ includedPaths,
1252
+ }) {
1253
+ const root = realpathSync(workspaceRoot);
1254
+ if (
1255
+ !Array.isArray(includedPaths)
1256
+ || includedPaths.length === 0
1257
+ || new Set(includedPaths).size !== includedPaths.length
1258
+ || includedPaths.some((includedPath) => !validOwnedPath(includedPath))
1259
+ ) {
1260
+ throw new Error("Source candidate closure paths are invalid.");
1261
+ }
1262
+ const absoluteCandidatePath = resolveExistingArtifact(
1263
+ root,
1264
+ candidatePath,
1265
+ "candidatePath",
1266
+ );
1267
+ const candidate = readJson(absoluteCandidatePath);
1268
+ const candidateGate = verifySourceCandidateV2({
1269
+ workspaceRoot: root,
1270
+ candidatePath,
1271
+ });
1272
+ const candidateCoveredPaths = [...new Set(
1273
+ candidate.sourceManifest.entries.flatMap((entry) => (
1274
+ entry.previousPath ? [entry.path, entry.previousPath] : [entry.path]
1275
+ )),
1276
+ )].sort(compareUnicodeCodeUnits);
1277
+ const includedDirtyPaths = [...new Set(
1278
+ parseStatus(root).flatMap((record) => record.paths)
1279
+ .filter((filePath) => isOwned(filePath, includedPaths)),
1280
+ )].sort(compareUnicodeCodeUnits);
1281
+ const covered = new Set(candidateCoveredPaths);
1282
+ const uncoveredDirtyPaths = includedDirtyPaths.filter(
1283
+ (filePath) => !covered.has(filePath),
1284
+ );
1285
+ const issueCodes = [
1286
+ ...(candidateGate.status === "valid" ? [] : ["source_candidate_invalid"]),
1287
+ ...(uncoveredDirtyPaths.length === 0
1288
+ ? []
1289
+ : ["source_candidate_path_closure_incomplete"]),
1290
+ ];
1291
+ const closureBody = {
1292
+ schemaVersion: "OwlCodaRunKitSourceCandidatePathClosureV1",
1293
+ candidateSha256: candidate.candidateSha256,
1294
+ payloadSha256: candidate.payload?.sha256 ?? null,
1295
+ includedPaths: [...includedPaths],
1296
+ includedDirtyPaths,
1297
+ candidateCoveredPaths,
1298
+ uncoveredDirtyPaths,
1299
+ };
1300
+ return {
1301
+ ...closureBody,
1302
+ status: issueCodes.length === 0 ? "valid" : "invalid",
1303
+ exitCode: issueCodes.length === 0 ? 0 : 2,
1304
+ issueCodes,
1305
+ closureSha256: candidateHash(closureBody),
1306
+ authorizationGranted: false,
1307
+ };
1308
+ }
1309
+
1310
+ export function verifySourceCandidateV1({
1311
+ workspaceRoot,
1312
+ candidatePath,
1313
+ }) {
1314
+ const root = realpathSync(workspaceRoot);
1315
+ const issueCodes = [];
1316
+ let absoluteCandidatePath;
1317
+ let candidate;
1318
+ try {
1319
+ absoluteCandidatePath = resolveExistingArtifact(
1320
+ root,
1321
+ candidatePath,
1322
+ "candidatePath",
1323
+ );
1324
+ const stat = lstatSync(absoluteCandidatePath);
1325
+ if (stat.isSymbolicLink() || !stat.isFile()) {
1326
+ throw new Error("candidate is not a regular file");
1327
+ }
1328
+ candidate = readJson(absoluteCandidatePath);
1329
+ } catch {
1330
+ return {
1331
+ status: "invalid",
1332
+ exitCode: 2,
1333
+ issueCodes: ["source_candidate_unreadable"],
1334
+ authorizationGranted: false,
1335
+ };
1336
+ }
1337
+ const { candidateSha256, ...body } = candidate;
1338
+ if (
1339
+ candidate.schemaVersion !== "OwlCodaRunKitSourceCandidateV1"
1340
+ || candidate.status !== "frozen"
1341
+ || candidate.sourceMode !== "dirty_worktree_exact_manifest"
1342
+ || candidate.authorizationGranted !== false
1343
+ || typeof candidateSha256 !== "string"
1344
+ || candidateSha256 !== candidateHash(body)
1345
+ ) {
1346
+ issueCodes.push("source_candidate_contract_invalid");
1347
+ }
1348
+ try {
1349
+ const packetPath = resolveExistingArtifact(
1350
+ root,
1351
+ candidate.deliveryPacketPath,
1352
+ "deliveryPacketPath",
1353
+ );
1354
+ const packetStat = lstatSync(packetPath);
1355
+ if (packetStat.isSymbolicLink() || !packetStat.isFile()) {
1356
+ throw new Error("delivery packet is not a regular file");
1357
+ }
1358
+ const packetBytes = readFileSync(packetPath);
1359
+ if (`sha256:${sha256(packetBytes)}` !== candidate.deliveryPacketSha256) {
1360
+ issueCodes.push("source_candidate_delivery_hash_mismatch");
1361
+ }
1362
+ const packet = JSON.parse(packetBytes.toString("utf8"));
1363
+ const gate = verifyDeliveryPacket({ workspaceRoot: root, packet });
1364
+ if (
1365
+ gate.status !== "valid"
1366
+ || packet.runId !== candidate.runId
1367
+ || packet.sourceFingerprint?.sha256 !== candidate.sourceFingerprint?.sha256
1368
+ ) {
1369
+ issueCodes.push("source_candidate_delivery_drift");
1370
+ }
1371
+ } catch {
1372
+ issueCodes.push("source_candidate_delivery_unreadable");
1373
+ }
1374
+ return {
1375
+ status: issueCodes.length === 0 ? "valid" : "invalid",
1376
+ exitCode: issueCodes.length === 0 ? 0 : 2,
1377
+ candidatePath: relativeToWorkspace(root, absoluteCandidatePath),
1378
+ candidateSha256,
1379
+ issueCodes: [...new Set(issueCodes)].sort(),
1380
+ authorizationGranted: false,
1381
+ };
1382
+ }