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,1214 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createHash } from "node:crypto";
4
+ import {
5
+ appendFileSync,
6
+ existsSync,
7
+ lstatSync,
8
+ mkdirSync,
9
+ readFileSync,
10
+ readdirSync,
11
+ realpathSync,
12
+ rmSync,
13
+ writeFileSync,
14
+ } from "node:fs";
15
+ import path from "node:path";
16
+
17
+ import {
18
+ RUNTIME_ROOT,
19
+ acceptedCloseoutVerificationIssues,
20
+ createCoreArtifact,
21
+ currentCoreIdentity,
22
+ initializeProjectRunKit,
23
+ isDirectExecution,
24
+ validateCoreArtifact,
25
+ validateExecutionPin,
26
+ validateLeaseOwnedPaths,
27
+ validateProjectConfigV2,
28
+ } from "./core-contract.mjs";
29
+ import { collapseByteIdenticalDeliveryCandidates } from "./delivery-selection.mjs";
30
+ import { validateVerificationReceiptGate } from "./verification-receipt-gate.mjs";
31
+ import { validateReceiptLineage } from "./receipt-lineage.mjs";
32
+ import { verifyDeliveryPacket } from "./source-fingerprint.mjs";
33
+ import { runSnapshot } from "./snapshot.mjs";
34
+ import { runFinalize } from "./finalize.mjs";
35
+ import { runReadyForCommit } from "./ready-for-commit.mjs";
36
+ import { runVisualSmoke } from "./visual-smoke.mjs";
37
+ import { runVerifyPlan } from "./verification-plan.mjs";
38
+ import { runCoverageAdoption } from "./coverage-adoption.mjs";
39
+ import { runResumeExecution } from "./resume-execution.mjs";
40
+ import {
41
+ runResourcePreflight,
42
+ summarizeResourcePreflight,
43
+ } from "./resource-preflight.mjs";
44
+ import {
45
+ acquireLease,
46
+ acquireLeaseWithinControlTransaction,
47
+ inspectLeases,
48
+ listLeaseArtifacts,
49
+ releaseLease,
50
+ releaseLeaseWithinControlTransaction,
51
+ restoreLeaseWithinControlTransaction,
52
+ withControlTransaction,
53
+ } from "./lease-lifecycle.mjs";
54
+ import { writeJsonExclusiveAtomically } from "./provenance-common.mjs";
55
+ import { createDeliveryFromLease } from "./delivery-create.mjs";
56
+ import {
57
+ activeAcceptedGate,
58
+ runHighLevelVerify,
59
+ } from "./lifecycle-orchestration.mjs";
60
+ import { buildInspectSummary, formatInspectHuman } from "./inspect-presentation.mjs";
61
+ import { attestQuickReceiptDetails } from "./quick-attest.mjs";
62
+ import { readLocalQuickMetrics } from "./quick-metrics.mjs";
63
+ import { runQuickVerification } from "./quick-verify.mjs";
64
+ import { runRepairExecution } from "./repair-execution.mjs";
65
+ import {
66
+ exportOfflineReceipt,
67
+ importOfflineReceipt,
68
+ } from "./offline-store.mjs";
69
+
70
+ function parseOptions(values, { multi = [], boolean = [] } = {}) {
71
+ const options = {};
72
+ const multiNames = new Set(multi);
73
+ const booleanNames = new Set(boolean);
74
+ for (let index = 0; index < values.length;) {
75
+ const key = values[index];
76
+ if (!key?.startsWith("--")) throw new Error("Options must use --name flags.");
77
+ const name = key.slice(2);
78
+ if (booleanNames.has(name)) {
79
+ if (name in options) throw new Error(`Duplicate option: ${key}`);
80
+ options[name] = true;
81
+ index += 1;
82
+ continue;
83
+ }
84
+ const value = values[index + 1];
85
+ if (value === undefined) throw new Error("Options must be --name value pairs.");
86
+ if (multiNames.has(name)) {
87
+ if (!(name in options)) options[name] = [];
88
+ options[name].push(value);
89
+ } else {
90
+ if (name in options) throw new Error(`Duplicate option: ${key}`);
91
+ options[name] = value;
92
+ }
93
+ index += 2;
94
+ }
95
+ return options;
96
+ }
97
+
98
+ function requireOption(options, name) {
99
+ const value = options[name];
100
+ if (typeof value !== "string" || value.length === 0) throw new Error(`--${name} is required.`);
101
+ return value;
102
+ }
103
+
104
+ function safeRunId(value) {
105
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value) || value === "." || value === "..") {
106
+ throw new Error("run-id must use letters, digits, dot, underscore, or hyphen without path separators.");
107
+ }
108
+ return value;
109
+ }
110
+
111
+ function workspace(options) {
112
+ return realpathSync(requireOption(options, "workspace"));
113
+ }
114
+
115
+ function runtimePath(root, ...segments) {
116
+ return path.resolve(root, RUNTIME_ROOT, ...segments);
117
+ }
118
+
119
+ function pathEntryExists(filePath) {
120
+ try {
121
+ lstatSync(filePath);
122
+ return true;
123
+ } catch (error) {
124
+ if (error?.code === "ENOENT") return false;
125
+ throw error;
126
+ }
127
+ }
128
+
129
+ function readJson(filePath) {
130
+ return JSON.parse(readFileSync(filePath, "utf8"));
131
+ }
132
+
133
+ function sha256(value) {
134
+ return createHash("sha256").update(value).digest("hex");
135
+ }
136
+
137
+ function writeJson(filePath, value, flag = "wx") {
138
+ writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, { flag });
139
+ }
140
+
141
+ function appendEvent(executionRoot, event) {
142
+ appendFileSync(path.join(executionRoot, "events.jsonl"), `${JSON.stringify(event)}\n`);
143
+ }
144
+
145
+ function relativePath(root, filePath) {
146
+ return path.relative(root, filePath).split(path.sep).join("/");
147
+ }
148
+
149
+ function compareCodeUnits(left, right) {
150
+ return left < right ? -1 : left > right ? 1 : 0;
151
+ }
152
+
153
+ function withinRoot(root, candidate) {
154
+ const relative = path.relative(root, candidate);
155
+ return relative === ""
156
+ || (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
157
+ }
158
+
159
+ function inspectDirectory(root, directory, label, issues) {
160
+ try {
161
+ const stat = lstatSync(directory);
162
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
163
+ issues.push(`${label} must be a real directory, not a symlink: ${relativePath(root, directory)}`);
164
+ return false;
165
+ }
166
+ const realRoot = realpathSync(root);
167
+ const realDirectory = realpathSync(directory);
168
+ if (realDirectory !== path.resolve(directory) || !withinRoot(realRoot, realDirectory)) {
169
+ issues.push(`${label} must remain inside the project without symlink ancestors: ${relativePath(root, directory)}`);
170
+ return false;
171
+ }
172
+ return true;
173
+ } catch {
174
+ issues.push(`${label} is unreadable: ${relativePath(root, directory)}`);
175
+ return false;
176
+ }
177
+ }
178
+
179
+ function invalidOpenExecution(runId, issues) {
180
+ return {
181
+ runId,
182
+ lifecycle: "unknown",
183
+ historical: false,
184
+ enginePin: { status: "invalid_artifact", exitCode: 2, issues: [...issues] },
185
+ recovery: {
186
+ lease: { status: "invalid", workItemIds: [], activeWorkItemIds: [], releasedWorkItemIds: [], issues: [...issues] },
187
+ delivery: { status: "invalid", issues: [...issues] },
188
+ verification: { status: "invalid", issues: [...issues] },
189
+ evidenceTrustLevel: "invalid",
190
+ nextAllowedAction: "repair_execution_artifacts",
191
+ issues: [...issues],
192
+ },
193
+ };
194
+ }
195
+
196
+ function findNamedFiles(root, searchRoot, name, label, issues) {
197
+ if (!pathEntryExists(searchRoot)) return [];
198
+ if (!inspectDirectory(root, searchRoot, `${label} directory`, issues)) return [];
199
+ const found = [];
200
+ const visit = (directory) => {
201
+ for (const entry of readdirSync(directory, { withFileTypes: true }).sort((left, right) => compareCodeUnits(left.name, right.name))) {
202
+ const entryPath = path.join(directory, entry.name);
203
+ const stat = lstatSync(entryPath);
204
+ if (stat.isSymbolicLink()) {
205
+ issues.push(`${label} artifact must not be a symlink: ${relativePath(root, entryPath)}`);
206
+ } else if (stat.isDirectory()) {
207
+ if (inspectDirectory(root, entryPath, `${label} directory`, issues)) visit(entryPath);
208
+ } else if (stat.isFile() && entry.name === name) {
209
+ found.push(entryPath);
210
+ } else if (!stat.isFile()) {
211
+ issues.push(`${label} artifact must be a regular file: ${relativePath(root, entryPath)}`);
212
+ }
213
+ }
214
+ };
215
+ visit(searchRoot);
216
+ return found;
217
+ }
218
+
219
+ function inspectFlatJsonFiles(root, directory, directoryLabel, artifactLabel) {
220
+ if (!pathEntryExists(directory)) return { files: [], issues: [] };
221
+ const issues = [];
222
+ if (!inspectDirectory(root, directory, directoryLabel, issues)) return { files: [], issues };
223
+ const files = [];
224
+ for (const entry of readdirSync(directory, { withFileTypes: true }).sort((left, right) => compareCodeUnits(left.name, right.name))) {
225
+ const entryPath = path.join(directory, entry.name);
226
+ const stat = lstatSync(entryPath);
227
+ if (stat.isSymbolicLink()) {
228
+ issues.push(`${artifactLabel} must be a regular file, not a symlink: ${relativePath(root, entryPath)}`);
229
+ } else if (stat.isFile() && entry.name.endsWith(".json")) {
230
+ files.push(entryPath);
231
+ } else if (!stat.isFile()) {
232
+ issues.push(`${artifactLabel} must be a regular file: ${relativePath(root, entryPath)}`);
233
+ }
234
+ }
235
+ return { files, issues };
236
+ }
237
+
238
+ function readJsonForInspection(root, filePath, label, issues) {
239
+ try {
240
+ const stat = lstatSync(filePath);
241
+ if (stat.isSymbolicLink() || !stat.isFile()) {
242
+ issues.push(`${label} must be a regular file, not a symlink: ${relativePath(root, filePath)}`);
243
+ return null;
244
+ }
245
+ const realRoot = realpathSync(root);
246
+ const realFile = realpathSync(filePath);
247
+ if (realFile !== path.resolve(filePath) || !withinRoot(realRoot, realFile)) {
248
+ issues.push(`${label} must remain inside the project without symlink ancestors: ${relativePath(root, filePath)}`);
249
+ return null;
250
+ }
251
+ return readJson(realFile);
252
+ } catch {
253
+ issues.push(`${label} must contain valid JSON: ${relativePath(root, filePath)}`);
254
+ return null;
255
+ }
256
+ }
257
+
258
+ function inspectActiveLeases(root, executionRoot) {
259
+ const leasesRoot = path.join(executionRoot, "leases");
260
+ if (!pathEntryExists(leasesRoot)) {
261
+ return { status: "none", workItemIds: [], activeWorkItemIds: [], releasedWorkItemIds: [], issues: [] };
262
+ }
263
+ const issues = [];
264
+ if (!inspectDirectory(root, leasesRoot, "Lease directory", issues)) {
265
+ return { status: "invalid", workItemIds: [], activeWorkItemIds: [], releasedWorkItemIds: [], issues };
266
+ }
267
+ const files = [];
268
+ for (const entry of readdirSync(leasesRoot, { withFileTypes: true }).sort((left, right) => compareCodeUnits(left.name, right.name))) {
269
+ const entryPath = path.join(leasesRoot, entry.name);
270
+ const stat = lstatSync(entryPath);
271
+ if (stat.isSymbolicLink()) {
272
+ issues.push(`Lease artifact must be a regular file, not a symlink: ${relativePath(root, entryPath)}`);
273
+ } else if (stat.isFile() && entry.name.endsWith(".json")) {
274
+ files.push(entryPath);
275
+ } else if (!stat.isFile()) {
276
+ issues.push(`Lease artifact must be a regular file: ${relativePath(root, entryPath)}`);
277
+ }
278
+ }
279
+ if (files.length === 0) {
280
+ return {
281
+ status: issues.length > 0 ? "invalid" : "none",
282
+ workItemIds: [],
283
+ activeWorkItemIds: [],
284
+ releasedWorkItemIds: [],
285
+ issues,
286
+ };
287
+ }
288
+ const leases = files.map((filePath) => readJsonForInspection(root, filePath, "lease", issues)).filter(Boolean);
289
+ for (const lease of leases) {
290
+ if (lease.schemaVersion !== "OwlCodaRunKitWorkerLeaseV1") issues.push("lease schemaVersion is invalid");
291
+ if (typeof lease.workItemId !== "string" || lease.workItemId.length === 0) issues.push("lease workItemId is required");
292
+ if (!Number.isInteger(lease.attempt) || lease.attempt < 1) issues.push("lease attempt must be a positive integer");
293
+ try {
294
+ validateLeaseOwnedPaths(lease.ownedPaths);
295
+ } catch (error) {
296
+ issues.push(`lease ownedPaths are invalid: ${error instanceof Error ? error.message : String(error)}`);
297
+ }
298
+ if (!new Set(["active", "released"]).has(lease.state)) issues.push("lease state must be active or released");
299
+ }
300
+ const workItemIds = leases.map((lease) => lease.workItemId).filter((value) => typeof value === "string").sort();
301
+ if (new Set(workItemIds).size !== workItemIds.length) issues.push("lease workItemId values must be unique");
302
+ const activeWorkItemIds = leases.filter((lease) => lease.state === "active").map((lease) => lease.workItemId).sort();
303
+ const releasedWorkItemIds = leases.filter((lease) => lease.state === "released").map((lease) => lease.workItemId).sort();
304
+ return {
305
+ status: issues.length > 0 ? "invalid" : activeWorkItemIds.length > 0 ? "active" : "released",
306
+ workItemIds,
307
+ activeWorkItemIds,
308
+ releasedWorkItemIds,
309
+ issues,
310
+ };
311
+ }
312
+
313
+ function inspectActiveVerification(root, executionRoot, runId) {
314
+ const receiptsRoot = path.join(executionRoot, "verification-receipts");
315
+ const issues = [];
316
+ const lineagePaths = findNamedFiles(
317
+ root,
318
+ receiptsRoot,
319
+ "receipt-lineage.json",
320
+ "Verification receipt",
321
+ issues,
322
+ );
323
+ if (issues.length > 0) return { status: "invalid", issues };
324
+ if (lineagePaths.length === 0) return { status: "missing", issues: [] };
325
+ if (lineagePaths.length > 1) {
326
+ return { status: "invalid", issues: ["multiple receipt lineage files require explicit repair"] };
327
+ }
328
+ const entries = readJsonForInspection(root, lineagePaths[0], "receipt lineage", issues);
329
+ if (!entries) return { status: "invalid", issues };
330
+ const lineage = validateReceiptLineage(Array.isArray(entries) ? entries : entries.receipts);
331
+ if (!lineage.valid || !lineage.active) {
332
+ return { status: "invalid", issues: lineage.issues?.map((item) => item.message ?? String(item)) ?? ["receipt lineage is invalid"] };
333
+ }
334
+ if (lineage.active.receipt.runId !== runId) {
335
+ return { status: "invalid", issues: ["active receipt runId does not match the execution"] };
336
+ }
337
+ const matchingGates = [];
338
+ const gateIssues = [];
339
+ for (const gatePath of findNamedFiles(
340
+ root,
341
+ receiptsRoot,
342
+ "verification-gate-input.json",
343
+ "Verification receipt",
344
+ gateIssues,
345
+ )) {
346
+ const gateInput = readJsonForInspection(root, gatePath, "verification gate input", gateIssues);
347
+ if (!gateInput) continue;
348
+ const gate = validateVerificationReceiptGate(gateInput);
349
+ if (gate.accepted && gate.activeReceiptSha256 === lineage.active.receiptSha256) {
350
+ matchingGates.push({ gatePath, gate });
351
+ }
352
+ }
353
+ if (matchingGates.length !== 1) {
354
+ return {
355
+ status: "invalid",
356
+ activeReceiptSha256: lineage.active.receiptSha256,
357
+ issues: [
358
+ ...gateIssues,
359
+ matchingGates.length === 0
360
+ ? "active receipt does not have one accepted verification gate"
361
+ : "active receipt has multiple accepted verification gates",
362
+ ],
363
+ };
364
+ }
365
+ const { gatePath, gate } = matchingGates[0];
366
+ return {
367
+ status: "passed",
368
+ decision: gate.decision,
369
+ activeReceiptSha256: gate.activeReceiptSha256,
370
+ sourceFingerprint: gate.sourceFingerprint,
371
+ receiptId: lineage.active.receipt.receiptId,
372
+ gateInputPath: relativePath(root, gatePath),
373
+ issues: [],
374
+ };
375
+ }
376
+
377
+ function inspectActiveDelivery(root, executionRoot, runId, preferredFingerprint) {
378
+ const packetsRoot = path.join(executionRoot, "delivery-packets");
379
+ const listing = inspectFlatJsonFiles(root, packetsRoot, "Delivery packet directory", "Delivery packet artifact");
380
+ const packetPaths = listing.files;
381
+ if (listing.issues.length > 0) {
382
+ return { status: "invalid", packetPaths: [], stalePacketPaths: [], issues: listing.issues };
383
+ }
384
+ if (packetPaths.length === 0) return { status: "missing", packetPaths: [], stalePacketPaths: [], issues: [] };
385
+ const issues = [];
386
+ const fresh = [];
387
+ const stalePacketPaths = [];
388
+ for (const packetPath of packetPaths) {
389
+ const packet = readJsonForInspection(root, packetPath, "delivery packet", issues);
390
+ if (!packet) continue;
391
+ if (packet.runId !== runId) {
392
+ issues.push(`delivery packet runId does not match the execution: ${relativePath(root, packetPath)}`);
393
+ continue;
394
+ }
395
+ const gate = verifyDeliveryPacket({ workspaceRoot: root, packet });
396
+ if (gate.status === "valid") {
397
+ fresh.push({ path: packetPath, sourceFingerprint: gate.recomputedFingerprint });
398
+ } else if (gate.status === "invalidated_by_concurrent_write") {
399
+ stalePacketPaths.push(relativePath(root, packetPath));
400
+ } else {
401
+ issues.push(...(gate.issues ?? []).map((item) => `${relativePath(root, packetPath)}: ${item.message ?? item}`));
402
+ }
403
+ }
404
+ const matching = collapseByteIdenticalDeliveryCandidates(preferredFingerprint
405
+ ? fresh.filter((item) => item.sourceFingerprint === preferredFingerprint)
406
+ : fresh);
407
+ if (matching.length === 1) {
408
+ return {
409
+ status: "fresh",
410
+ packetPaths: packetPaths.map((item) => relativePath(root, item)),
411
+ selectedPacketPath: relativePath(root, matching[0].path),
412
+ sourceFingerprint: matching[0].sourceFingerprint,
413
+ stalePacketPaths,
414
+ issues,
415
+ };
416
+ }
417
+ if (fresh.length === 0 && stalePacketPaths.length > 0 && issues.length === 0) {
418
+ return { status: "stale", packetPaths: packetPaths.map((item) => relativePath(root, item)), stalePacketPaths, issues: [] };
419
+ }
420
+ return {
421
+ status: "invalid",
422
+ packetPaths: packetPaths.map((item) => relativePath(root, item)),
423
+ stalePacketPaths,
424
+ issues: [
425
+ ...issues,
426
+ preferredFingerprint && matching.length === 0
427
+ ? "no fresh delivery packet matches the active verification receipt"
428
+ : "multiple fresh delivery packets require explicit selection",
429
+ ],
430
+ };
431
+ }
432
+
433
+ function inspectResourcePreflights(root, executionRoot, runId) {
434
+ const resourceRoot = path.join(executionRoot, "resource-preflights");
435
+ const listing = inspectFlatJsonFiles(
436
+ root,
437
+ resourceRoot,
438
+ "Resource preflight directory",
439
+ "Resource preflight artifact",
440
+ );
441
+ if (listing.issues.length > 0) return { status: "invalid", selected: null, issues: listing.issues };
442
+ if (listing.files.length === 0) return { status: "none", selected: null, issues: [] };
443
+ const summaries = [];
444
+ const issues = [];
445
+ for (const artifactPath of listing.files) {
446
+ const artifact = readJsonForInspection(root, artifactPath, "resource preflight", issues);
447
+ if (!artifact) continue;
448
+ try {
449
+ const summary = summarizeResourcePreflight(artifact, runId);
450
+ const bindingIssues = inspectResourceVerificationPlanBinding(root, executionRoot, runId, artifact);
451
+ if (bindingIssues.length > 0) {
452
+ issues.push(...bindingIssues.map(issue => `${relativePath(root, artifactPath)}: ${issue}`));
453
+ continue;
454
+ }
455
+ summaries.push({
456
+ ...summary,
457
+ artifactPath: relativePath(root, artifactPath),
458
+ });
459
+ } catch (error) {
460
+ issues.push(`${relativePath(root, artifactPath)}: ${error instanceof Error ? error.message : String(error)}`);
461
+ }
462
+ }
463
+ const sequences = summaries.map(summary => summary.sequence);
464
+ if (new Set(sequences).size !== sequences.length) issues.push("resource preflight sequences must be unique");
465
+ if (issues.length > 0) return { status: "invalid", selected: null, issues };
466
+ const selected = [...summaries].sort((left, right) => right.sequence - left.sequence)[0];
467
+ const expired = selected.validUntil !== null && Date.now() > new Date(selected.validUntil).getTime();
468
+ return {
469
+ status: expired ? "expired" : "current",
470
+ selected,
471
+ artifactPaths: summaries.sort((left, right) => left.sequence - right.sequence).map(summary => summary.artifactPath),
472
+ issues: [],
473
+ };
474
+ }
475
+
476
+ function inspectResourceVerificationPlanBinding(root, executionRoot, runId, artifact) {
477
+ const planPath = path.resolve(root, artifact.verificationPlan.path);
478
+ const expectedPlanRoot = path.join(executionRoot, "verification-plans");
479
+ if (!withinRoot(expectedPlanRoot, planPath)) {
480
+ return ["resource preflight verification plan must remain inside this execution"];
481
+ }
482
+ const issues = [];
483
+ const plan = readJsonForInspection(root, planPath, "resource preflight verification plan", issues);
484
+ if (!plan) return issues;
485
+ if (sha256(readFileSync(planPath)) !== artifact.verificationPlan.sha256) {
486
+ issues.push("resource preflight verification plan hash does not match the bound SHA-256");
487
+ }
488
+ if (plan.schemaVersion !== "OwlCodaRunKitVerificationPlanV1"
489
+ || plan.runId !== runId
490
+ || plan.planId !== artifact.verificationPlan.planId
491
+ || plan.authorizationGranted !== false
492
+ || !plan.evidence
493
+ || !Array.isArray(plan.evidence.reusableReceiptIds)) {
494
+ issues.push("resource preflight verification plan identity or authority boundary is invalid");
495
+ return issues;
496
+ }
497
+ const planReceipts = [...plan.evidence.reusableReceiptIds].sort(compareCodeUnits);
498
+ if (planReceipts.some(receiptId => typeof receiptId !== "string")
499
+ || new Set(planReceipts).size !== planReceipts.length
500
+ || JSON.stringify(planReceipts) !== JSON.stringify(artifact.receiptReuse.reusableReceiptIds)) {
501
+ issues.push("resource preflight reusable receipts do not match the bound verification plan");
502
+ }
503
+ return issues;
504
+ }
505
+
506
+ function activeRecovery(root, executionRoot, runId, enginePin) {
507
+ const lease = inspectActiveLeases(root, executionRoot);
508
+ const verification = inspectActiveVerification(root, executionRoot, runId);
509
+ const delivery = inspectActiveDelivery(root, executionRoot, runId, verification.sourceFingerprint);
510
+ const resourcePreflight = inspectResourcePreflights(root, executionRoot, runId);
511
+ const issues = [
512
+ ...lease.issues,
513
+ ...delivery.issues,
514
+ ...verification.issues,
515
+ ...(resourcePreflight.status === "invalid" ? resourcePreflight.issues : []),
516
+ ];
517
+ if (enginePin.status !== "valid") issues.push(...enginePin.issues);
518
+ if (verification.status === "passed" && lease.status === "none") {
519
+ issues.push("passed verification requires a recorded writer lease before closeout");
520
+ }
521
+ let evidenceTrustLevel = "planned";
522
+ let nextAllowedAction = "acquire_writer_lease";
523
+ if (issues.length > 0 || new Set(["invalid", "stale"]).has(delivery.status) || verification.status === "invalid") {
524
+ evidenceTrustLevel = "invalid";
525
+ nextAllowedAction = delivery.status === "stale" ? "replace_delivery_packet" : "repair_execution_artifacts";
526
+ } else if (verification.status === "passed") {
527
+ evidenceTrustLevel = "verification_passed";
528
+ nextAllowedAction = lease.status === "active" ? "release_writer_lease" : "close_execution";
529
+ } else if (delivery.status === "fresh") {
530
+ evidenceTrustLevel = "delivery_fresh";
531
+ nextAllowedAction = "run_stage_verification";
532
+ } else if (resourcePreflight.status === "expired") {
533
+ evidenceTrustLevel = "work_in_progress";
534
+ nextAllowedAction = "run_resource_preflight";
535
+ } else if (resourcePreflight.status === "current" && resourcePreflight.selected) {
536
+ evidenceTrustLevel = "work_in_progress";
537
+ nextAllowedAction = resourcePreflight.selected.nextAllowedAction;
538
+ } else if (lease.status === "active") {
539
+ evidenceTrustLevel = "work_in_progress";
540
+ nextAllowedAction = "continue_feature_work";
541
+ } else if (lease.status === "released") {
542
+ evidenceTrustLevel = "work_in_progress";
543
+ nextAllowedAction = "prepare_delivery_packet";
544
+ }
545
+ return { lease, delivery, verification, resourcePreflight, evidenceTrustLevel, nextAllowedAction, issues };
546
+ }
547
+
548
+ function closedRecovery(receipt, closeout) {
549
+ if (closeout.status !== "valid") {
550
+ return {
551
+ lease: { status: "invalid", workItemIds: [], activeWorkItemIds: [], releasedWorkItemIds: [], issues: closeout.issues },
552
+ delivery: { status: "invalid", issues: closeout.issues },
553
+ verification: { status: "invalid", issues: closeout.issues },
554
+ evidenceTrustLevel: "invalid",
555
+ nextAllowedAction: "repair_execution_artifacts",
556
+ issues: closeout.issues,
557
+ };
558
+ }
559
+ const payload = receipt.artifact.payload;
560
+ const verification = payload.verification;
561
+ return {
562
+ lease: verification
563
+ ? { status: "released", workItemIds: [...verification.releasedLeaseIds], activeWorkItemIds: [], releasedWorkItemIds: [...verification.releasedLeaseIds], issues: [] }
564
+ : { status: "not_required", workItemIds: [], activeWorkItemIds: [], releasedWorkItemIds: [], issues: [] },
565
+ delivery: verification
566
+ ? { status: "historical", sourceFingerprint: verification.sourceFingerprint, issues: [] }
567
+ : { status: "not_applicable", issues: [] },
568
+ verification: verification
569
+ ? {
570
+ status: "passed",
571
+ decision: verification.gateDecision,
572
+ activeReceiptSha256: verification.activeReceiptSha256,
573
+ sourceFingerprint: verification.sourceFingerprint,
574
+ issues: [],
575
+ }
576
+ : { status: "not_applicable", issues: [] },
577
+ evidenceTrustLevel: payload.decision === "accepted" ? "closed_accepted" : "closed_nonaccepted",
578
+ nextAllowedAction: "plan_new_execution",
579
+ issues: [],
580
+ };
581
+ }
582
+
583
+ function closeoutGateRejected(runId, decision, issues) {
584
+ return {
585
+ status: "closeout_gate_rejected",
586
+ exitCode: 2,
587
+ runId,
588
+ decision,
589
+ authorizationGranted: false,
590
+ issues,
591
+ };
592
+ }
593
+
594
+ async function initialize(options) {
595
+ return initializeProjectRunKit({ workspaceRoot: workspace(options) });
596
+ }
597
+
598
+ function plan(options) {
599
+ const root = workspace(options);
600
+ const runId = safeRunId(requireOption(options, "run-id"));
601
+ const goalPath = realpathSync(requireOption(options, "goal"));
602
+ const config = readJson(runtimePath(root, "config.json"));
603
+ const pinGate = validateExecutionPin({ expected: config.core, actual: currentCoreIdentity() });
604
+ if (pinGate.status !== "valid") return pinGate;
605
+ const executionRoot = runtimePath(root, "executions", runId);
606
+ if (existsSync(executionRoot)) throw new Error(`run id already exists: ${runId}`);
607
+ mkdirSync(path.join(executionRoot, "leases"), { recursive: true });
608
+ mkdirSync(path.join(executionRoot, "delivery-packets"));
609
+ mkdirSync(path.join(executionRoot, "verification-receipts"));
610
+ const enginePin = currentCoreIdentity();
611
+ const goal = readJson(goalPath);
612
+ const goalContractPath = path.join(executionRoot, "goal-contract.json");
613
+ writeJson(path.join(executionRoot, "engine-pin.json"), enginePin);
614
+ writeJson(goalContractPath, goal);
615
+ writeJson(path.join(executionRoot, "execution-plan.json"), {
616
+ schemaVersion: "OwlCodaRunKitExecutionPlanV1",
617
+ runId,
618
+ state: "planned",
619
+ enginePin,
620
+ goalContractSha256: sha256(readFileSync(goalContractPath)),
621
+ authorizationGranted: false,
622
+ });
623
+ appendEvent(executionRoot, { sequence: 1, type: "execution_planned", runId, authorizationGranted: false });
624
+ return { status: "planned", exitCode: 0, runId, authorizationGranted: false, enginePin };
625
+ }
626
+
627
+ function startExecution(options) {
628
+ const root = workspace(options);
629
+ const runId = safeRunId(requireOption(options, "run-id"));
630
+ const workItemId = requireOption(options, "work-item");
631
+ const ownedPaths = options["owned-path"] ?? [];
632
+ const executionRoot = runtimePath(root, "executions", runId);
633
+ return withControlTransaction(root, () => {
634
+ if (existsSync(executionRoot)) throw new Error(`run id already exists: ${runId}`);
635
+ try {
636
+ const planned = plan(options);
637
+ if (planned.status !== "planned") return planned;
638
+ const lease = acquireLeaseWithinControlTransaction({
639
+ workspaceRoot: root,
640
+ runId,
641
+ workItemId,
642
+ ownedPaths,
643
+ });
644
+ if (lease.status !== "lease_acquired") {
645
+ rmSync(executionRoot, { recursive: true, force: true });
646
+ return lease;
647
+ }
648
+ return {
649
+ status: "started",
650
+ exitCode: 0,
651
+ runId,
652
+ leasePath: lease.leasePath,
653
+ enginePin: planned.enginePin,
654
+ authorizationGranted: false,
655
+ };
656
+ } catch (error) {
657
+ if (existsSync(executionRoot)) rmSync(executionRoot, { recursive: true, force: true });
658
+ throw error;
659
+ }
660
+ });
661
+ }
662
+
663
+ function inspect(options) {
664
+ const root = workspace(options);
665
+ const requestedRunId = options["run-id"] === undefined
666
+ ? null
667
+ : safeRunId(requireOption(options, "run-id"));
668
+ if (options.history && requestedRunId) throw new Error("inspect --history cannot be combined with --run-id.");
669
+ if (options.verbose && !requestedRunId) throw new Error("inspect --verbose requires --run-id.");
670
+ if (options.history && options.verbose) throw new Error("inspect --history cannot be combined with --verbose.");
671
+ const controlIssues = [];
672
+ const configPath = runtimePath(root, "config.json");
673
+ const candidateConfig = readJsonForInspection(root, configPath, "Project config", controlIssues);
674
+ const configGate = candidateConfig
675
+ ? validateProjectConfigV2(candidateConfig)
676
+ : { valid: false, issues: [] };
677
+ if (!configGate.valid) controlIssues.push(...configGate.issues);
678
+ const config = controlIssues.length === 0 ? candidateConfig : null;
679
+ const current = currentCoreIdentity();
680
+ const configCore = config
681
+ ? validateExecutionPin({ expected: config.core, actual: current })
682
+ : { status: "invalid_config", exitCode: 2, issues: [...controlIssues] };
683
+ const executionsRoot = runtimePath(root, "executions");
684
+ const executionEntries = pathEntryExists(executionsRoot) && inspectDirectory(root, executionsRoot, "Executions directory", controlIssues)
685
+ ? readdirSync(executionsRoot, { withFileTypes: true }).sort((left, right) => compareCodeUnits(left.name, right.name))
686
+ : [];
687
+ const runIds = executionEntries.map((entry) => entry.name);
688
+ const executions = executionEntries.map((entry) => {
689
+ const runId = entry.name;
690
+ const executionRoot = runtimePath(root, "executions", runId);
691
+ const entryIssues = [];
692
+ if (entry.isSymbolicLink() || !entry.isDirectory()) {
693
+ entryIssues.push(`Execution must be a real directory, not a symlink: ${relativePath(root, executionRoot)}`);
694
+ return invalidOpenExecution(runId, entryIssues);
695
+ }
696
+ if (!inspectDirectory(root, executionRoot, "Execution directory", entryIssues)) {
697
+ return invalidOpenExecution(runId, entryIssues);
698
+ }
699
+ const inspectionIssues = [];
700
+ const pin = readJsonForInspection(root, path.join(executionRoot, "engine-pin.json"), "engine pin", inspectionIssues);
701
+ if (!pin) {
702
+ const enginePin = { status: "invalid_artifact", exitCode: 2, issues: inspectionIssues };
703
+ return {
704
+ runId,
705
+ lifecycle: "unknown",
706
+ historical: false,
707
+ enginePin,
708
+ recovery: activeRecovery(root, executionRoot, runId, enginePin),
709
+ };
710
+ }
711
+ const currentPinGate = validateExecutionPin({ expected: pin, actual: current });
712
+ const closeoutPath = path.join(executionRoot, "closeout-receipt.json");
713
+ if (!pathEntryExists(closeoutPath)) {
714
+ return {
715
+ runId,
716
+ lifecycle: "active",
717
+ historical: currentPinGate.status !== "valid",
718
+ enginePin: currentPinGate,
719
+ recovery: activeRecovery(root, executionRoot, runId, currentPinGate),
720
+ };
721
+ }
722
+
723
+ const receipt = readJsonForInspection(root, closeoutPath, "closeout receipt", inspectionIssues);
724
+ if (!receipt) {
725
+ const closeout = { status: "invalid", issues: inspectionIssues };
726
+ return {
727
+ runId,
728
+ lifecycle: "closed",
729
+ historical: currentPinGate.status !== "valid",
730
+ enginePin: { status: "invalid_closeout", exitCode: 2, issues: inspectionIssues },
731
+ closeout,
732
+ recovery: closedRecovery({ artifact: { payload: {} } }, closeout),
733
+ };
734
+ }
735
+ const artifactGate = validateCoreArtifact(receipt.artifact);
736
+ const issues = [...artifactGate.issues];
737
+ if (artifactGate.valid && receipt.acceptanceSha256 !== artifactGate.acceptanceSha256) {
738
+ issues.push("closeout acceptanceSha256 does not match the artifact");
739
+ }
740
+ if (artifactGate.valid && receipt.artifactSha256 !== artifactGate.artifactSha256) {
741
+ issues.push("closeout artifactSha256 does not match the artifact");
742
+ }
743
+ if (artifactGate.valid && receipt.artifact.payload.runId !== runId) {
744
+ issues.push("closeout runId does not match the execution directory");
745
+ }
746
+ if (artifactGate.valid && !new Set(["accepted", "rejected", "blocked"]).has(receipt.artifact.payload.decision)) {
747
+ issues.push("closeout decision is invalid");
748
+ }
749
+ if (artifactGate.valid && receipt.artifact.payload.authorizationGranted !== false) {
750
+ issues.push("closeout authorizationGranted must be false");
751
+ }
752
+ if (artifactGate.valid) issues.push(...acceptedCloseoutVerificationIssues(receipt.artifact));
753
+ const enginePin = artifactGate.valid
754
+ ? validateExecutionPin({ expected: pin, actual: receipt.artifact.core })
755
+ : { status: "invalid_closeout", exitCode: 2, issues: [...artifactGate.issues] };
756
+ if (enginePin.status !== "valid") issues.push(...enginePin.issues);
757
+ const closeout = issues.length === 0
758
+ ? {
759
+ status: "valid",
760
+ decision: receipt.artifact.payload.decision,
761
+ authorizationGranted: false,
762
+ }
763
+ : { status: "invalid", issues };
764
+ return {
765
+ runId,
766
+ lifecycle: "closed",
767
+ historical: currentPinGate.status !== "valid",
768
+ enginePin,
769
+ closeout,
770
+ recovery: {
771
+ ...closedRecovery(receipt, closeout),
772
+ resourcePreflight: inspectResourcePreflights(root, executionRoot, runId),
773
+ },
774
+ };
775
+ });
776
+ const invalidClosedIssues = executions
777
+ .filter((execution) => execution.lifecycle === "closed" && execution.closeout?.status === "invalid")
778
+ .flatMap((execution) => execution.closeout.issues ?? []);
779
+ const recoveryControlIssues = [...controlIssues, ...invalidClosedIssues];
780
+ const activeRunIds = executions.filter((execution) => execution.lifecycle !== "closed").map((execution) => execution.runId);
781
+ const selectedRun = activeRunIds.length === 1
782
+ ? executions.find((execution) => execution.runId === activeRunIds[0])
783
+ : null;
784
+ let recovery = activeRunIds.length === 0 && recoveryControlIssues.length > 0
785
+ ? {
786
+ state: "invalid_control_truth",
787
+ activeRunIds,
788
+ selectedRunId: null,
789
+ nextAllowedAction: "repair_execution_artifacts",
790
+ authorizationGranted: false,
791
+ }
792
+ : activeRunIds.length === 0
793
+ ? {
794
+ state: "no_active_execution",
795
+ activeRunIds,
796
+ selectedRunId: null,
797
+ nextAllowedAction: "plan_new_execution",
798
+ authorizationGranted: false,
799
+ }
800
+ : activeRunIds.length === 1
801
+ ? {
802
+ state: "single_active_execution",
803
+ activeRunIds,
804
+ selectedRunId: activeRunIds[0],
805
+ nextAllowedAction: selectedRun.recovery.nextAllowedAction,
806
+ authorizationGranted: false,
807
+ }
808
+ : {
809
+ state: "multiple_active_executions",
810
+ activeRunIds,
811
+ selectedRunId: null,
812
+ nextAllowedAction: "select_active_execution",
813
+ authorizationGranted: false,
814
+ };
815
+ if (recoveryControlIssues.length > 0 && activeRunIds.length > 0) {
816
+ recovery = { ...recovery, nextAllowedAction: "repair_execution_artifacts" };
817
+ }
818
+ const result = {
819
+ status: "inspected",
820
+ exitCode: configCore.status !== "valid" || recoveryControlIssues.length > 0 || executions.some(
821
+ (execution) => execution.enginePin.status !== "valid"
822
+ || execution.closeout?.status === "invalid"
823
+ || execution.recovery?.evidenceTrustLevel === "invalid",
824
+ ) || activeRunIds.length > 1 ? 2 : 0,
825
+ runtimeRoot: RUNTIME_ROOT,
826
+ config,
827
+ configCore,
828
+ controlIssues: recoveryControlIssues,
829
+ runIds,
830
+ executions,
831
+ recovery,
832
+ };
833
+ if (requestedRunId && !executions.some(execution => execution.runId === requestedRunId)) {
834
+ throw new Error(`Execution was not found: ${requestedRunId}`);
835
+ }
836
+ const summary = buildInspectSummary(result);
837
+ const view = options.history
838
+ ? { mode: "history" }
839
+ : requestedRunId
840
+ ? {
841
+ mode: "execution",
842
+ runId: requestedRunId,
843
+ verbose: options.verbose === true,
844
+ execution: executions.find(execution => execution.runId === requestedRunId),
845
+ }
846
+ : { mode: "summary" };
847
+ return { ...result, summary, view };
848
+ }
849
+
850
+ function closeout(options) {
851
+ const root = workspace(options);
852
+ const runId = safeRunId(requireOption(options, "run-id"));
853
+ const decision = requireOption(options, "decision");
854
+ if (!new Set(["accepted", "rejected", "blocked"]).has(decision)) throw new Error("decision must be accepted, rejected, or blocked.");
855
+ if (decision === "accepted" && !options["gate-input"]) {
856
+ return closeoutGateRejected(runId, decision, [
857
+ "A verification gate input is required for accepted closeout.",
858
+ ]);
859
+ }
860
+ const executionRoot = runtimePath(root, "executions", runId);
861
+ const pin = readJson(path.join(executionRoot, "engine-pin.json"));
862
+ const pinGate = validateExecutionPin({ expected: pin, actual: currentCoreIdentity() });
863
+ if (pinGate.status !== "valid") return pinGate;
864
+ let acceptedVerification = null;
865
+ if (decision === "accepted") {
866
+ const gateInputPath = realpathSync(options["gate-input"]);
867
+ const gateInputBytes = readFileSync(gateInputPath);
868
+ const gateInput = JSON.parse(gateInputBytes.toString("utf8"));
869
+ const gate = validateVerificationReceiptGate(gateInput);
870
+ if (!gate.accepted) {
871
+ return closeoutGateRejected(
872
+ runId,
873
+ decision,
874
+ gate.issues.length > 0
875
+ ? gate.issues.map((item) => item.message)
876
+ : ["The verification gate input was not accepted."],
877
+ );
878
+ }
879
+ if (gate.contractVersion !== "0.2") {
880
+ return closeoutGateRejected(runId, decision, [
881
+ "Accepted closeout requires a Contract v0.2 gate input.",
882
+ ]);
883
+ }
884
+ if (gate.lineage.active.receipt.runId !== runId) {
885
+ return closeoutGateRejected(runId, decision, [
886
+ "The active receipt runId must match the current execution.",
887
+ ]);
888
+ }
889
+ const leases = listLeaseArtifacts({ workspaceRoot: root, executionRoot });
890
+ if (leases.length === 0) {
891
+ return closeoutGateRejected(runId, decision, [
892
+ "At least one released lease is required for accepted closeout.",
893
+ ]);
894
+ }
895
+ if (leases.some((lease) => lease.state !== "released")) {
896
+ return closeoutGateRejected(runId, decision, [
897
+ "Accepted closeout is not allowed while an active lease remains.",
898
+ ]);
899
+ }
900
+ const releasedLeaseIds = leases.map((lease) => lease.workItemId);
901
+ if (releasedLeaseIds.some((workItemId) => typeof workItemId !== "string" || workItemId.length === 0)
902
+ || new Set(releasedLeaseIds).size !== releasedLeaseIds.length) {
903
+ return closeoutGateRejected(runId, decision, [
904
+ "Released leases must have unique non-empty workItemId values.",
905
+ ]);
906
+ }
907
+ releasedLeaseIds.sort();
908
+ acceptedVerification = {
909
+ contractVersion: "0.2",
910
+ gateDecision: gate.decision,
911
+ gateInputSha256: sha256(gateInputBytes),
912
+ activeReceiptSha256: gate.activeReceiptSha256,
913
+ sourceFingerprint: gate.sourceFingerprint,
914
+ verificationContextFingerprint: gate.verificationContextFingerprint,
915
+ selectedProfileIds: [...gate.selectedProfileIds],
916
+ leaseState: "released",
917
+ releasedLeaseIds,
918
+ };
919
+ }
920
+ const created = createCoreArtifact({
921
+ core: pin,
922
+ producer: { adapterKind: "codex", adapterVersion: "0.1.0" },
923
+ payload: {
924
+ runId,
925
+ decision,
926
+ authorizationGranted: false,
927
+ ...(acceptedVerification ? { verification: acceptedVerification } : {}),
928
+ },
929
+ extensions: { "dev.owlcoda.adapter.codex": {} },
930
+ });
931
+ const closeoutPath = path.join(executionRoot, "closeout-receipt.json");
932
+ writeJsonExclusiveAtomically(closeoutPath, created);
933
+ const eventsPath = path.join(executionRoot, "events.jsonl");
934
+ const sequence = readFileSync(eventsPath, "utf8").trim().split("\n").filter(Boolean).length + 1;
935
+ try {
936
+ appendEvent(executionRoot, { sequence, type: "execution_closed", runId, decision, artifactSha256: created.artifactSha256 });
937
+ } catch (error) {
938
+ rmSync(closeoutPath, { force: true });
939
+ throw error;
940
+ }
941
+ return { status: "closed", exitCode: 0, runId, decision, authorizationGranted: false, ...created };
942
+ }
943
+
944
+ function finishExecution(options) {
945
+ const root = workspace(options);
946
+ const runId = safeRunId(requireOption(options, "run-id"));
947
+ const decision = requireOption(options, "decision");
948
+ if (!new Set(["accepted", "rejected", "blocked"]).has(decision)) {
949
+ throw new Error("decision must be accepted, rejected, or blocked.");
950
+ }
951
+ return withControlTransaction(root, () => {
952
+ const executionRoot = runtimePath(root, "executions", runId);
953
+ const closeoutPath = path.join(executionRoot, "closeout-receipt.json");
954
+ if (pathEntryExists(closeoutPath)) {
955
+ if (lstatSync(closeoutPath).isSymbolicLink()) {
956
+ throw new Error("Closeout receipt must not be a symlink.");
957
+ }
958
+ throw new Error(`Execution is already closed: ${runId}`);
959
+ }
960
+ const gate = decision === "accepted" ? activeAcceptedGate({ workspaceRoot: root, runId }) : null;
961
+ if (gate && gate.status !== "valid") return gate;
962
+ const activeLeaseIds = listLeaseArtifacts({ workspaceRoot: root, executionRoot })
963
+ .filter(lease => lease.state === "active")
964
+ .map(lease => lease.workItemId)
965
+ .sort();
966
+ const releasedLeaseIds = [];
967
+ const rollbackReleasedLeases = () => {
968
+ for (const workItemId of [...releasedLeaseIds].reverse()) {
969
+ const restored = restoreLeaseWithinControlTransaction({
970
+ workspaceRoot: root,
971
+ runId,
972
+ workItemId,
973
+ });
974
+ if (restored.status !== "lease_restored") {
975
+ throw new Error(`Could not restore lease after failed finish: ${workItemId}`);
976
+ }
977
+ }
978
+ };
979
+ let closed;
980
+ try {
981
+ for (const workItemId of activeLeaseIds) {
982
+ const released = releaseLeaseWithinControlTransaction({
983
+ workspaceRoot: root,
984
+ runId,
985
+ workItemId,
986
+ });
987
+ if (released.status !== "lease_released") {
988
+ rollbackReleasedLeases();
989
+ return released;
990
+ }
991
+ releasedLeaseIds.push(workItemId);
992
+ }
993
+ closed = closeout({
994
+ ...options,
995
+ ...(gate ? { "gate-input": gate.gateInputPath } : {}),
996
+ });
997
+ if (closed.status !== "closed") {
998
+ rollbackReleasedLeases();
999
+ return closed;
1000
+ }
1001
+ } catch (error) {
1002
+ if (!pathEntryExists(closeoutPath)) rollbackReleasedLeases();
1003
+ throw error;
1004
+ }
1005
+ return {
1006
+ status: "finished",
1007
+ exitCode: 0,
1008
+ runId,
1009
+ decision,
1010
+ releasedLeaseIds,
1011
+ activeReceiptSha256: gate?.activeReceiptSha256 ?? null,
1012
+ closeoutArtifactSha256: closed.artifactSha256,
1013
+ authorizationGranted: false,
1014
+ };
1015
+ });
1016
+ }
1017
+
1018
+ function requestCommand(options, handler) {
1019
+ const root = workspace(options);
1020
+ const runId = safeRunId(requireOption(options, "run-id"));
1021
+ const requestPath = realpathSync(requireOption(options, "request"));
1022
+ return handler({ workspaceRoot: root, runId, request: readJson(requestPath) });
1023
+ }
1024
+
1025
+ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
1026
+ try {
1027
+ const [command, ...rest] = argv;
1028
+ const separatorIndex = rest.indexOf("--");
1029
+ if (separatorIndex >= 0 && command !== "verify" && command !== "quick-verify") {
1030
+ throw new Error("Only runkit verify accepts an exact command after --.");
1031
+ }
1032
+ const optionValues = separatorIndex >= 0 ? rest.slice(0, separatorIndex) : rest;
1033
+ const commandArgv = separatorIndex >= 0 ? rest.slice(separatorIndex + 1) : [];
1034
+ const nested = new Set(["lease", "delivery"]).has(command);
1035
+ const [action, ...nestedOptions] = nested ? optionValues : [null, ...optionValues];
1036
+ const options = parseOptions(nested ? nestedOptions : optionValues, {
1037
+ multi: (command === "lease" && action === "acquire") || command === "start" ? ["owned-path"] : [],
1038
+ boolean: command === "inspect"
1039
+ ? ["json", "verbose", "history"]
1040
+ : new Set(["quick-verify", "quick-attest", "quick-metrics"]).has(command)
1041
+ ? ["json", "local"]
1042
+ : [],
1043
+ });
1044
+ if (command === "init") return await initialize(options);
1045
+ if (command === "start") return startExecution(options);
1046
+ if (command === "plan") {
1047
+ const root = workspace(options);
1048
+ return withControlTransaction(root, () => plan(options));
1049
+ }
1050
+ if (command === "inspect") return inspect(options);
1051
+ if (command === "lease") {
1052
+ const root = workspace(options);
1053
+ const runId = safeRunId(requireOption(options, "run-id"));
1054
+ if (action === "acquire") {
1055
+ return acquireLease({
1056
+ workspaceRoot: root,
1057
+ runId,
1058
+ workItemId: requireOption(options, "work-item"),
1059
+ ownedPaths: options["owned-path"] ?? [],
1060
+ });
1061
+ }
1062
+ if (action === "inspect") return inspectLeases({ workspaceRoot: root, runId });
1063
+ if (action === "release") {
1064
+ return releaseLease({
1065
+ workspaceRoot: root,
1066
+ runId,
1067
+ workItemId: requireOption(options, "work-item"),
1068
+ });
1069
+ }
1070
+ throw new Error("Usage: runkit-cli.mjs lease <acquire|inspect|release> [options]");
1071
+ }
1072
+ if (command === "delivery") {
1073
+ if (action !== "create") throw new Error("Usage: runkit-cli.mjs delivery create [options]");
1074
+ const root = workspace(options);
1075
+ return createDeliveryFromLease({
1076
+ workspaceRoot: root,
1077
+ runId: safeRunId(requireOption(options, "run-id")),
1078
+ workItemId: requireOption(options, "from-lease"),
1079
+ packetId: requireOption(options, "packet-id"),
1080
+ });
1081
+ }
1082
+ if (command === "verify") {
1083
+ return runHighLevelVerify({
1084
+ workspaceRoot: workspace(options),
1085
+ runId: safeRunId(requireOption(options, "run-id")),
1086
+ workItemId: requireOption(options, "from-lease"),
1087
+ verificationId: requireOption(options, "verification-id"),
1088
+ cwd: options.cwd ?? ".",
1089
+ commandArgv,
1090
+ });
1091
+ }
1092
+ if (command === "quick-verify") {
1093
+ if (commandArgv.length === 0) {
1094
+ throw new Error("Usage: runkit-cli.mjs quick-verify --workspace <path> -- <executable> [args...]");
1095
+ }
1096
+ return runQuickVerification({
1097
+ workspaceRoot: workspace(options),
1098
+ commandArgv,
1099
+ });
1100
+ }
1101
+ if (command === "quick-attest") {
1102
+ const details = attestQuickReceiptDetails({
1103
+ receiptPath: requireOption(options, "receipt"),
1104
+ workspaceRoot: workspace(options),
1105
+ });
1106
+ const attestation = details.attestation;
1107
+ return {
1108
+ status: "quick_attestation",
1109
+ exitCode: details.exitCode,
1110
+ receiptPath: realpathSync(requireOption(options, "receipt")),
1111
+ receiptSha256: attestation.subjectRef.receiptSha256,
1112
+ sourceFingerprint: details.sourceFingerprint,
1113
+ attestation,
1114
+ nextAllowedAction: attestation.decision === "GO"
1115
+ ? "consume_attestation"
1116
+ : "inspect_attestation_issues",
1117
+ authorizationGranted: false,
1118
+ };
1119
+ }
1120
+ if (command === "quick-metrics") {
1121
+ if (options.local !== true) {
1122
+ throw new Error("Quick metrics requires --local; network telemetry is not available.");
1123
+ }
1124
+ const local = readLocalQuickMetrics(workspace(options));
1125
+ return {
1126
+ ...local,
1127
+ metrics: local.quick,
1128
+ networkRequests: 0,
1129
+ };
1130
+ }
1131
+ if (command === "repair") {
1132
+ return runRepairExecution({
1133
+ workspaceRoot: workspace(options),
1134
+ runId: safeRunId(requireOption(options, "run-id")),
1135
+ onPlan: runtimeHooks.onRepairPlan,
1136
+ });
1137
+ }
1138
+ if (command === "offline-export") {
1139
+ return exportOfflineReceipt({
1140
+ workspaceRoot: workspace(options),
1141
+ receiptPath: requireOption(options, "receipt"),
1142
+ outputPath: requireOption(options, "output"),
1143
+ });
1144
+ }
1145
+ if (command === "offline-import") {
1146
+ return importOfflineReceipt({
1147
+ workspaceRoot: workspace(options),
1148
+ bundlePath: requireOption(options, "bundle"),
1149
+ storeRoot: requireOption(options, "store"),
1150
+ });
1151
+ }
1152
+ if (command === "coverage-adopt") return requestCommand(options, runCoverageAdoption);
1153
+ if (command === "resume") {
1154
+ const root = workspace(options);
1155
+ const sourceRunId = safeRunId(requireOption(options, "run-id"));
1156
+ const requestPath = realpathSync(requireOption(options, "request"));
1157
+ return runResumeExecution({ workspaceRoot: root, sourceRunId, request: readJson(requestPath) });
1158
+ }
1159
+ if (command === "verify-plan") return requestCommand(options, runVerifyPlan);
1160
+ if (command === "resource-preflight") {
1161
+ const root = workspace(options);
1162
+ const runId = safeRunId(requireOption(options, "run-id"));
1163
+ const requestPath = realpathSync(requireOption(options, "request"));
1164
+ const requestBytes = readFileSync(requestPath);
1165
+ return withControlTransaction(root, () => runResourcePreflight({
1166
+ workspaceRoot: root,
1167
+ runId,
1168
+ request: JSON.parse(requestBytes.toString("utf8")),
1169
+ requestSha256: sha256(requestBytes),
1170
+ }));
1171
+ }
1172
+ if (command === "snapshot") {
1173
+ const root = workspace(options);
1174
+ return withControlTransaction(root, () => requestCommand(options, runSnapshot));
1175
+ }
1176
+ if (command === "visual-smoke") return requestCommand(options, runVisualSmoke);
1177
+ if (command === "finalize") {
1178
+ const root = workspace(options);
1179
+ return withControlTransaction(root, () => requestCommand(options, runFinalize));
1180
+ }
1181
+ if (command === "ready-for-commit") return requestCommand(options, runReadyForCommit);
1182
+ if (command === "closeout") {
1183
+ const root = workspace(options);
1184
+ return withControlTransaction(root, () => closeout(options));
1185
+ }
1186
+ if (command === "finish") return finishExecution(options);
1187
+ throw new Error("Usage: runkit-cli.mjs <init|start|verify|repair|offline-export|offline-import|quick-verify|quick-attest|quick-metrics|finish|plan|inspect|lease|delivery|coverage-adopt|resume|verify-plan|resource-preflight|snapshot|visual-smoke|finalize|ready-for-commit|closeout> [options]");
1188
+ } catch (error) {
1189
+ return {
1190
+ status: "invalid_input",
1191
+ exitCode: 3,
1192
+ issues: [error instanceof Error ? error.message : String(error)],
1193
+ authorizationGranted: false,
1194
+ };
1195
+ }
1196
+ }
1197
+
1198
+ if (isDirectExecution(import.meta.url)) {
1199
+ const argv = process.argv.slice(2);
1200
+ const result = await runCli(argv, {
1201
+ onRepairPlan({ plan, planPath, planSha256 }) {
1202
+ writeFileSync(
1203
+ 2,
1204
+ `RunKit RepairPlan persisted before replay: ${planPath} sha256:${planSha256}\n${JSON.stringify(plan, null, 2)}\n`,
1205
+ );
1206
+ },
1207
+ });
1208
+ const inspectOptions = argv[0] === "inspect" && result.status === "inspected"
1209
+ ? parseOptions(argv.slice(1), { boolean: ["json", "verbose", "history"] })
1210
+ : null;
1211
+ const humanInspect = inspectOptions !== null && inspectOptions.json !== true;
1212
+ process.stdout.write(humanInspect ? formatInspectHuman(result) : `${JSON.stringify(result)}\n`);
1213
+ process.exitCode = result.exitCode;
1214
+ }