owlrunkit 0.13.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 (128) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +674 -0
  3. package/NOTICE.md +137 -0
  4. package/README.md +44 -0
  5. package/SOURCE.md +10 -0
  6. package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_1.md +160 -0
  7. package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_2.md +459 -0
  8. package/integrations/codex/skills/owlcoda-runkit/SKILL.md +408 -0
  9. package/integrations/codex/skills/owlcoda-runkit/agents/openai.yaml +4 -0
  10. package/integrations/codex/skills/owlcoda-runkit/assets/templates/agents-snippet.md +7 -0
  11. package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +10 -0
  12. package/integrations/codex/skills/owlcoda-runkit/assets/templates/coverage-adopt-request.json +26 -0
  13. package/integrations/codex/skills/owlcoda-runkit/assets/templates/execution-delivery-packet.json +13 -0
  14. package/integrations/codex/skills/owlcoda-runkit/assets/templates/finalize-request.json +18 -0
  15. package/integrations/codex/skills/owlcoda-runkit/assets/templates/goal-contract.json +11 -0
  16. package/integrations/codex/skills/owlcoda-runkit/assets/templates/profiles.json +9 -0
  17. package/integrations/codex/skills/owlcoda-runkit/assets/templates/ready-for-commit-request.json +11 -0
  18. package/integrations/codex/skills/owlcoda-runkit/assets/templates/resource-preflight-request.json +56 -0
  19. package/integrations/codex/skills/owlcoda-runkit/assets/templates/resume-request.json +10 -0
  20. package/integrations/codex/skills/owlcoda-runkit/assets/templates/snapshot-request.json +23 -0
  21. package/integrations/codex/skills/owlcoda-runkit/assets/templates/verification-context.json +15 -0
  22. package/integrations/codex/skills/owlcoda-runkit/assets/templates/verification-receipt.json +11 -0
  23. package/integrations/codex/skills/owlcoda-runkit/assets/templates/verify-plan-request.json +31 -0
  24. package/integrations/codex/skills/owlcoda-runkit/assets/templates/visual-smoke-request.json +32 -0
  25. package/integrations/codex/skills/owlcoda-runkit/assets/templates/worker-lease.json +7 -0
  26. package/package.json +24 -0
  27. package/packages/attest/LICENSE +674 -0
  28. package/packages/attest/README.md +63 -0
  29. package/packages/attest/cli/owlcoda-attest.mjs +155 -0
  30. package/packages/attest/consumer-surface-v1.json +255 -0
  31. package/packages/attest/distribution-manifest-v1.json +104 -0
  32. package/packages/attest/fixtures/bad/accepted-authority-leak.json +7 -0
  33. package/packages/attest/fixtures/bad/accepted-failed-receipt.json +7 -0
  34. package/packages/attest/fixtures/bad/active-context-binding-mismatch.json +7 -0
  35. package/packages/attest/fixtures/bad/active-source-binding-mismatch.json +7 -0
  36. package/packages/attest/fixtures/bad/context-fingerprint-mismatch.json +7 -0
  37. package/packages/attest/fixtures/bad/duplicate-object-key.json +8 -0
  38. package/packages/attest/fixtures/bad/multiple-active-leaves.json +10 -0
  39. package/packages/attest/fixtures/bad/orphan-replacement.json +7 -0
  40. package/packages/attest/fixtures/bad/root-shape-extra-property.json +8 -0
  41. package/packages/attest/fixtures/bad/source-fingerprint-mismatch.json +7 -0
  42. package/packages/attest/fixtures/good/accepted-replacement-chain.json +59 -0
  43. package/packages/attest/fixtures/good/blocked-single-leaf.json +9 -0
  44. package/packages/attest/fixtures/index.json +90 -0
  45. package/packages/attest/sbom.spdx.json +33 -0
  46. package/packages/attest/schemas/attestation-ref-v1.schema.json +44 -0
  47. package/packages/attest/schemas/attestation-result-v1.schema.json +163 -0
  48. package/packages/attest/schemas/offline-attestation-bundle-v1.schema.json +30 -0
  49. package/packages/attest/schemas/public-verification-bundle-v1.schema.json +74 -0
  50. package/packages/attest/schemas/quick-verification-receipt-v1.schema.json +219 -0
  51. package/packages/attest/schemas/workspace-snapshot-v1.schema.json +135 -0
  52. package/packages/attest/src/canonical.mjs +12 -0
  53. package/packages/attest/src/formal.mjs +762 -0
  54. package/packages/attest/src/index.mjs +113 -0
  55. package/packages/attest/src/offline-bundle.mjs +108 -0
  56. package/packages/attest/src/quick-receipt-contract.mjs +156 -0
  57. package/packages/attest/src/quick.mjs +274 -0
  58. package/packages/attest/src/reference-contract.mjs +64 -0
  59. package/packages/attest/src/reference.mjs +131 -0
  60. package/packages/attest/src/workspace-snapshot.mjs +171 -0
  61. package/registry-release-policy-v1.json +34 -0
  62. package/sbom.spdx.json +39 -0
  63. package/scripts/runkit-contract/acceptance-evidence.mjs +103 -0
  64. package/scripts/runkit-contract/core-contract.mjs +392 -0
  65. package/scripts/runkit-contract/coverage-adoption.mjs +414 -0
  66. package/scripts/runkit-contract/delivery-create.mjs +252 -0
  67. package/scripts/runkit-contract/delivery-selection.mjs +31 -0
  68. package/scripts/runkit-contract/finalize.mjs +790 -0
  69. package/scripts/runkit-contract/inspect-presentation.mjs +183 -0
  70. package/scripts/runkit-contract/install-codex-skill.mjs +239 -0
  71. package/scripts/runkit-contract/lease-lifecycle.mjs +324 -0
  72. package/scripts/runkit-contract/lifecycle-orchestration.mjs +415 -0
  73. package/scripts/runkit-contract/offline-store.mjs +248 -0
  74. package/scripts/runkit-contract/profile-impact.mjs +223 -0
  75. package/scripts/runkit-contract/provenance-common.mjs +153 -0
  76. package/scripts/runkit-contract/quick-attest.mjs +4 -0
  77. package/scripts/runkit-contract/quick-canonical.mjs +41 -0
  78. package/scripts/runkit-contract/quick-metrics.mjs +86 -0
  79. package/scripts/runkit-contract/quick-receipt.mjs +139 -0
  80. package/scripts/runkit-contract/quick-verify.mjs +201 -0
  81. package/scripts/runkit-contract/quick-workspace-snapshot.mjs +3 -0
  82. package/scripts/runkit-contract/ready-for-commit.mjs +188 -0
  83. package/scripts/runkit-contract/receipt-lineage.mjs +332 -0
  84. package/scripts/runkit-contract/registry-adoption-gate.mjs +117 -0
  85. package/scripts/runkit-contract/repair-execution.mjs +997 -0
  86. package/scripts/runkit-contract/resource-preflight.mjs +829 -0
  87. package/scripts/runkit-contract/resume-execution.mjs +569 -0
  88. package/scripts/runkit-contract/runkit-cli.mjs +1214 -0
  89. package/scripts/runkit-contract/schemas/closeout-receipt-v2.schema.json +42 -0
  90. package/scripts/runkit-contract/schemas/core-artifact-v2.schema.json +23 -0
  91. package/scripts/runkit-contract/schemas/core-artifact.schema.json +23 -0
  92. package/scripts/runkit-contract/schemas/coverage-adopt-request-v1.schema.json +66 -0
  93. package/scripts/runkit-contract/schemas/engine-pin-v2.schema.json +14 -0
  94. package/scripts/runkit-contract/schemas/engine-pin.schema.json +14 -0
  95. package/scripts/runkit-contract/schemas/evidence-coverage-index-v1.schema.json +131 -0
  96. package/scripts/runkit-contract/schemas/finalize-request-v1.schema.json +131 -0
  97. package/scripts/runkit-contract/schemas/profiles.schema.json +47 -0
  98. package/scripts/runkit-contract/schemas/project-config-v2.schema.json +13 -0
  99. package/scripts/runkit-contract/schemas/project-config.schema.json +13 -0
  100. package/scripts/runkit-contract/schemas/ready-for-commit-v1.schema.json +26 -0
  101. package/scripts/runkit-contract/schemas/replayable-evidence-v1.schema.json +82 -0
  102. package/scripts/runkit-contract/schemas/resource-preflight-request-v1.schema.json +178 -0
  103. package/scripts/runkit-contract/schemas/resource-preflight-v1.schema.json +153 -0
  104. package/scripts/runkit-contract/schemas/resume-attempt-v1.schema.json +80 -0
  105. package/scripts/runkit-contract/schemas/resume-request-v1.schema.json +77 -0
  106. package/scripts/runkit-contract/schemas/snapshot-v1.schema.json +71 -0
  107. package/scripts/runkit-contract/schemas/verification-context-v1.schema.json +92 -0
  108. package/scripts/runkit-contract/schemas/verification-plan-v1.schema.json +150 -0
  109. package/scripts/runkit-contract/schemas/verification-receipt-v2.schema.json +118 -0
  110. package/scripts/runkit-contract/schemas/verify-plan-request-v1.schema.json +56 -0
  111. package/scripts/runkit-contract/schemas/visual-smoke-request-v1.schema.json +64 -0
  112. package/scripts/runkit-contract/schemas/visual-smoke-result-v1.schema.json +68 -0
  113. package/scripts/runkit-contract/snapshot.mjs +340 -0
  114. package/scripts/runkit-contract/source-fingerprint.mjs +404 -0
  115. package/scripts/runkit-contract/templates/coverage-adopt-request.json +26 -0
  116. package/scripts/runkit-contract/templates/finalize-request.json +18 -0
  117. package/scripts/runkit-contract/templates/goal-contract.json +11 -0
  118. package/scripts/runkit-contract/templates/ready-for-commit-request.json +11 -0
  119. package/scripts/runkit-contract/templates/resource-preflight-request.json +56 -0
  120. package/scripts/runkit-contract/templates/resume-request.json +10 -0
  121. package/scripts/runkit-contract/templates/snapshot-request.json +23 -0
  122. package/scripts/runkit-contract/templates/verify-plan-request.json +31 -0
  123. package/scripts/runkit-contract/templates/visual-smoke-request.json +32 -0
  124. package/scripts/runkit-contract/templates/worker-lease.json +7 -0
  125. package/scripts/runkit-contract/verification-context.mjs +141 -0
  126. package/scripts/runkit-contract/verification-plan.mjs +608 -0
  127. package/scripts/runkit-contract/verification-receipt-gate.mjs +566 -0
  128. package/scripts/runkit-contract/visual-smoke.mjs +410 -0
@@ -0,0 +1,201 @@
1
+ import { spawn } from "node:child_process";
2
+ import { realpathSync } from "node:fs";
3
+
4
+ import { currentCoreIdentity } from "./core-contract.mjs";
5
+ import {
6
+ closeQuickOutputFiles,
7
+ createQuickReceiptStore,
8
+ persistQuickReceipt,
9
+ } from "./quick-receipt.mjs";
10
+ import { captureWorkspaceSnapshot } from "./quick-workspace-snapshot.mjs";
11
+
12
+ function runExactCommand({ executable, argv, cwd, stdoutFd, stderrFd }) {
13
+ return new Promise((resolve) => {
14
+ let settled = false;
15
+ const child = spawn(executable, argv, {
16
+ cwd,
17
+ shell: false,
18
+ stdio: ["ignore", stdoutFd, stderrFd],
19
+ env: process.env,
20
+ });
21
+ child.once("error", (error) => {
22
+ if (settled) return;
23
+ settled = true;
24
+ resolve({ exitCode: null, signal: null, launchError: error.message });
25
+ });
26
+ child.once("close", (exitCode, signal) => {
27
+ if (settled) return;
28
+ settled = true;
29
+ resolve({ exitCode, signal, launchError: null });
30
+ });
31
+ });
32
+ }
33
+
34
+ export async function runQuickVerification({ workspaceRoot, commandArgv }) {
35
+ if (!Array.isArray(commandArgv) || commandArgv.length === 0 || !commandArgv.every((value) => typeof value === "string")) {
36
+ return {
37
+ status: "quick_verification_input_invalid",
38
+ exitCode: 3,
39
+ issues: ["An exact command is required after --."],
40
+ authorizationGranted: false,
41
+ };
42
+ }
43
+ let root;
44
+ try {
45
+ root = realpathSync(workspaceRoot);
46
+ } catch {
47
+ return {
48
+ status: "quick_verification_input_invalid",
49
+ exitCode: 3,
50
+ issues: ["Workspace does not exist."],
51
+ authorizationGranted: false,
52
+ };
53
+ }
54
+
55
+ let workspaceBefore;
56
+ let store;
57
+ try {
58
+ workspaceBefore = captureWorkspaceSnapshot(root);
59
+ store = createQuickReceiptStore(root);
60
+ } catch (error) {
61
+ return {
62
+ status: "quick_verification_input_invalid",
63
+ exitCode: 3,
64
+ issues: [error instanceof Error ? error.message : String(error)],
65
+ authorizationGranted: false,
66
+ };
67
+ }
68
+
69
+ const [executable, ...argv] = commandArgv;
70
+ const startedAt = new Date().toISOString();
71
+ const execution = await runExactCommand({
72
+ executable,
73
+ argv,
74
+ cwd: root,
75
+ stdoutFd: store.stdoutFd,
76
+ stderrFd: store.stderrFd,
77
+ });
78
+ closeQuickOutputFiles(store);
79
+ const finishedAt = new Date().toISOString();
80
+
81
+ let workspaceAfter;
82
+ try {
83
+ workspaceAfter = captureWorkspaceSnapshot(root);
84
+ } catch (error) {
85
+ return {
86
+ status: "quick_verification_source_mutated",
87
+ exitCode: 2,
88
+ receiptPath: null,
89
+ receiptSha256: null,
90
+ issues: [error instanceof Error ? error.message : String(error)],
91
+ authorizationGranted: false,
92
+ };
93
+ }
94
+
95
+ const sourceMutated = workspaceBefore.sourceFingerprint !== workspaceAfter.sourceFingerprint;
96
+ const issueCodes = ["quick_ignored_artifact_unbound"];
97
+ if (sourceMutated) issueCodes.push("source_mutated_during_verification");
98
+ let persisted;
99
+ try {
100
+ persisted = persistQuickReceipt({
101
+ workspaceRoot: root,
102
+ store,
103
+ receipt: {
104
+ schemaVersion: "OwlCodaQuickVerificationReceiptV1",
105
+ receiptId: store.receiptId,
106
+ assurance: "captured_verification",
107
+ authorizationGranted: false,
108
+ coreIdentity: {
109
+ contractVersion: currentCoreIdentity().contractVersion,
110
+ coreVersion: currentCoreIdentity().coreVersion,
111
+ coreManifestSha256: currentCoreIdentity().coreManifestSha256,
112
+ },
113
+ workspaceBefore,
114
+ exactCommand: {
115
+ executable,
116
+ argv,
117
+ cwd: root,
118
+ },
119
+ verificationContext: {
120
+ platform: process.platform,
121
+ architecture: process.arch,
122
+ runtime: process.version,
123
+ },
124
+ startedAt,
125
+ finishedAt,
126
+ exitResult: {
127
+ exitCode: execution.exitCode,
128
+ signal: execution.signal,
129
+ },
130
+ workspaceAfter,
131
+ mutationDecision: sourceMutated
132
+ ? "invalidated_by_command_source_mutation"
133
+ : "source_unchanged",
134
+ issueCodes,
135
+ },
136
+ });
137
+ } catch (error) {
138
+ return {
139
+ status: "quick_verification_control_invalid",
140
+ exitCode: 1,
141
+ receiptPath: null,
142
+ receiptSha256: null,
143
+ sourceFingerprint: workspaceAfter.sourceFingerprint,
144
+ commandExitCode: execution.exitCode,
145
+ mutationDecision: sourceMutated
146
+ ? "invalidated_by_command_source_mutation"
147
+ : "source_unchanged",
148
+ issueCodes: [...issueCodes, "quick_receipt_store_invalid"],
149
+ issues: [error instanceof Error ? error.message : String(error)],
150
+ nextAllowedAction: "repair_quick_receipt_store_and_rerun",
151
+ authorizationGranted: false,
152
+ };
153
+ }
154
+
155
+ if (sourceMutated) {
156
+ return {
157
+ status: "quick_verification_source_mutated",
158
+ exitCode: 2,
159
+ receiptPath: persisted.receiptPath,
160
+ receiptSha256: persisted.receiptSha256,
161
+ sourceFingerprint: workspaceAfter.sourceFingerprint,
162
+ commandExitCode: execution.exitCode,
163
+ mutationDecision: "invalidated_by_command_source_mutation",
164
+ issueCodes,
165
+ attestCommand: `owlcoda attest ${persisted.receiptPath}`,
166
+ issues: [],
167
+ nextAllowedAction: "restore_source_or_run_a_new_quick_verification",
168
+ authorizationGranted: false,
169
+ };
170
+ }
171
+ if (execution.launchError || execution.exitCode !== 0) {
172
+ return {
173
+ status: "quick_verification_failed",
174
+ exitCode: 1,
175
+ receiptPath: persisted.receiptPath,
176
+ receiptSha256: persisted.receiptSha256,
177
+ sourceFingerprint: workspaceAfter.sourceFingerprint,
178
+ commandExitCode: execution.exitCode,
179
+ mutationDecision: "source_unchanged",
180
+ issueCodes,
181
+ attestCommand: `owlcoda attest ${persisted.receiptPath}`,
182
+ issues: execution.launchError ? [execution.launchError] : [],
183
+ nextAllowedAction: "inspect_output_and_rerun",
184
+ authorizationGranted: false,
185
+ };
186
+ }
187
+ return {
188
+ status: "quick_verification_passed",
189
+ exitCode: 0,
190
+ receiptPath: persisted.receiptPath,
191
+ receiptSha256: persisted.receiptSha256,
192
+ sourceFingerprint: workspaceAfter.sourceFingerprint,
193
+ commandExitCode: 0,
194
+ mutationDecision: "source_unchanged",
195
+ issueCodes,
196
+ attestCommand: `owlcoda attest ${persisted.receiptPath}`,
197
+ issues: [],
198
+ nextAllowedAction: `owlcoda attest ${persisted.receiptPath}`,
199
+ authorizationGranted: false,
200
+ };
201
+ }
@@ -0,0 +1,3 @@
1
+ export {
2
+ captureWorkspaceSnapshot,
3
+ } from "../../packages/attest/src/workspace-snapshot.mjs";
@@ -0,0 +1,188 @@
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import { validateCoreArtifact } from "./core-contract.mjs";
5
+ import {
6
+ assertAllowedKeys,
7
+ isRecord,
8
+ loadActiveExecution,
9
+ readJson,
10
+ relativeToWorkspace,
11
+ repositoryActionsFalse,
12
+ resolveExistingArtifact,
13
+ sha256,
14
+ writeJsonExclusive,
15
+ } from "./provenance-common.mjs";
16
+ import { resolveProfileImpactProjection } from "./profile-impact.mjs";
17
+ import { verifySnapshotEvidence, verifySnapshotFreshness } from "./snapshot.mjs";
18
+ import { verifyDeliveryPacket } from "./source-fingerprint.mjs";
19
+ import { validateVerificationReceiptGate } from "./verification-receipt-gate.mjs";
20
+
21
+ const REQUEST_KEYS = [
22
+ "schemaVersion",
23
+ "deliveryPacketPath",
24
+ "verificationGateInputPath",
25
+ "roots",
26
+ ];
27
+
28
+ function changedPaths(packet) {
29
+ const files = packet?.changedFiles?.files ?? packet?.changedFiles?.wholeFileSha256;
30
+ if (!isRecord(files) || Object.keys(files).length === 0) {
31
+ throw new Error("Delivery packet does not declare changed files.");
32
+ }
33
+ return Object.keys(files).sort();
34
+ }
35
+
36
+ function validateRequest(request) {
37
+ assertAllowedKeys(request, "Ready-for-commit request", REQUEST_KEYS);
38
+ if (request.schemaVersion !== "OwlCodaRunKitReadyForCommitRequestV1") {
39
+ throw new Error("Unsupported ready-for-commit request schemaVersion.");
40
+ }
41
+ if (typeof request.deliveryPacketPath !== "string" || request.deliveryPacketPath.length === 0) {
42
+ throw new Error("Ready-for-commit request requires deliveryPacketPath.");
43
+ }
44
+ if (typeof request.verificationGateInputPath !== "string" || request.verificationGateInputPath.length === 0) {
45
+ throw new Error("Ready-for-commit request requires verificationGateInputPath.");
46
+ }
47
+ if (!Array.isArray(request.roots) || request.roots.length === 0) {
48
+ throw new Error("Ready-for-commit request requires roots.");
49
+ }
50
+ const roles = new Set();
51
+ for (const root of request.roots) {
52
+ if (!isRecord(root)
53
+ || typeof root.role !== "string"
54
+ || root.role.length === 0
55
+ || typeof root.snapshotPath !== "string"
56
+ || root.snapshotPath.length === 0) {
57
+ throw new Error("Each ready root requires role and snapshotPath.");
58
+ }
59
+ if (roles.has(root.role)) throw new Error(`Duplicate ready root role: ${root.role}`);
60
+ roles.add(root.role);
61
+ }
62
+ if (!roles.has("candidate")) throw new Error("Ready-for-commit requires a candidate root snapshot.");
63
+ }
64
+
65
+ function verifyReleasedLeases(executionRoot) {
66
+ const leasesRoot = path.join(executionRoot, "leases");
67
+ const leases = readdirSync(leasesRoot, { withFileTypes: true })
68
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
69
+ .map((entry) => readJson(path.join(leasesRoot, entry.name)));
70
+ if (leases.length === 0) throw new Error("Ready-for-commit requires at least one released lease.");
71
+ if (leases.some((lease) => lease.state !== "released")) {
72
+ throw new Error("Ready-for-commit is not allowed while an active lease remains.");
73
+ }
74
+ return leases.map((lease) => lease.workItemId).sort();
75
+ }
76
+
77
+ function verifyCloseout({ executionRoot, runId, gateInputBytes, gate }) {
78
+ const closeoutPath = path.join(executionRoot, "closeout-receipt.json");
79
+ if (!existsSync(closeoutPath)) throw new Error("Accepted closeout receipt is required.");
80
+ const closeout = readJson(closeoutPath);
81
+ const artifactGate = validateCoreArtifact(closeout.artifact);
82
+ if (!artifactGate.valid) throw new Error(artifactGate.issues.join("; "));
83
+ if (closeout.acceptanceSha256 !== artifactGate.acceptanceSha256
84
+ || closeout.artifactSha256 !== artifactGate.artifactSha256) {
85
+ throw new Error("Closeout artifact hashes are invalid.");
86
+ }
87
+ const payload = closeout.artifact.payload;
88
+ if (payload.runId !== runId || payload.decision !== "accepted") {
89
+ throw new Error("Closeout does not accept the current execution.");
90
+ }
91
+ if (payload.authorizationGranted !== false) throw new Error("Closeout authorization must remain false.");
92
+ if (payload.verification?.gateInputSha256 !== sha256(gateInputBytes)) {
93
+ throw new Error("Closeout gate input hash does not match the requested gate input.");
94
+ }
95
+ if (payload.verification?.activeReceiptSha256 !== gate.activeReceiptSha256) {
96
+ throw new Error("Closeout active receipt does not match the current verification gate.");
97
+ }
98
+ return { closeoutPath, closeout };
99
+ }
100
+
101
+ export function runReadyForCommit({ workspaceRoot, runId, request }) {
102
+ validateRequest(request);
103
+ const { executionRoot, pinGate } = loadActiveExecution(workspaceRoot, runId);
104
+ if (pinGate.status !== "valid") return pinGate;
105
+ const packetPath = resolveExistingArtifact(workspaceRoot, request.deliveryPacketPath, "deliveryPacketPath");
106
+ const packet = readJson(packetPath);
107
+ if (packet.runId !== runId) throw new Error("Delivery packet runId does not match the execution.");
108
+ const sourceGate = verifyDeliveryPacket({ workspaceRoot, packet });
109
+ if (sourceGate.status !== "valid") {
110
+ return {
111
+ status: sourceGate.status,
112
+ exitCode: sourceGate.exitCode,
113
+ runId,
114
+ issues: sourceGate.issues,
115
+ authorizationGranted: false,
116
+ };
117
+ }
118
+ const gateInputPath = resolveExistingArtifact(
119
+ workspaceRoot,
120
+ request.verificationGateInputPath,
121
+ "verificationGateInputPath",
122
+ );
123
+ const gateInputBytes = readFileSync(gateInputPath);
124
+ const gateInput = JSON.parse(gateInputBytes.toString("utf8"));
125
+ const gate = validateVerificationReceiptGate(gateInput);
126
+ if (!gate.accepted || gate.lineage.active.receipt.runId !== runId) {
127
+ throw new Error("Verification gate does not accept the current execution.");
128
+ }
129
+ if (gate.sourceFingerprint !== sourceGate.recomputedFingerprint) {
130
+ throw new Error("Verification gate source fingerprint is stale.");
131
+ }
132
+ const profiles = readJson(path.join(workspaceRoot, ".owlcoda/runkit/profiles.json")).profiles;
133
+ const detailedProfileImpact = isRecord(gateInput.profileImpact)
134
+ && ("directProfileIds" in gateInput.profileImpact
135
+ || "transitiveProfileIds" in gateInput.profileImpact);
136
+ const currentProfileImpact = resolveProfileImpactProjection({
137
+ changedPaths: changedPaths(packet),
138
+ profiles,
139
+ detailed: detailedProfileImpact,
140
+ });
141
+ if (JSON.stringify(currentProfileImpact) !== JSON.stringify(gateInput.profileImpact)) {
142
+ throw new Error("Verification profile impact is stale.");
143
+ }
144
+ const releasedLeaseIds = verifyReleasedLeases(executionRoot);
145
+ const { closeoutPath } = verifyCloseout({ executionRoot, runId, gateInputBytes, gate });
146
+ const roots = request.roots.map((root) => {
147
+ const snapshotPath = resolveExistingArtifact(workspaceRoot, root.snapshotPath, "root snapshotPath");
148
+ const snapshot = readJson(snapshotPath);
149
+ const evidence = verifySnapshotEvidence({ workspaceRoot, runId, snapshot });
150
+ if (!evidence.valid) throw new Error(evidence.issues.join("; "));
151
+ const freshness = verifySnapshotFreshness(snapshot);
152
+ if (!freshness.valid) throw new Error(freshness.issues.join("; "));
153
+ return {
154
+ role: root.role,
155
+ snapshotPath: root.snapshotPath,
156
+ snapshotSha256: sha256(readFileSync(snapshotPath)),
157
+ targetRoot: snapshot.targetRoot,
158
+ head: freshness.current.head,
159
+ manifestFingerprint: freshness.current.manifestFingerprint,
160
+ };
161
+ });
162
+ const readyPath = path.join(executionRoot, "READY_FOR_COMMIT_RECEIPT.json");
163
+ const receipt = {
164
+ schemaVersion: "OwlCodaRunKitReadyForCommitReceiptV1",
165
+ runId,
166
+ status: "ready_for_commit",
167
+ sourceFingerprint: sourceGate.recomputedFingerprint,
168
+ activeReceiptSha256: gate.activeReceiptSha256,
169
+ verificationGateInputPath: request.verificationGateInputPath,
170
+ verificationGateInputSha256: sha256(gateInputBytes),
171
+ deliveryPacketPath: request.deliveryPacketPath,
172
+ deliveryPacketSha256: sha256(readFileSync(packetPath)),
173
+ closeoutReceiptPath: relativeToWorkspace(workspaceRoot, closeoutPath),
174
+ closeoutReceiptSha256: sha256(readFileSync(closeoutPath)),
175
+ releasedLeaseIds,
176
+ roots,
177
+ repositoryActions: repositoryActionsFalse(),
178
+ authorizationGranted: false,
179
+ };
180
+ writeJsonExclusive(readyPath, receipt);
181
+ return {
182
+ status: "ready_for_commit",
183
+ exitCode: 0,
184
+ runId,
185
+ readyReceiptPath: relativeToWorkspace(workspaceRoot, readyPath),
186
+ authorizationGranted: false,
187
+ };
188
+ }