owlrunkit 0.13.0 → 0.14.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.
@@ -0,0 +1,332 @@
1
+ import {
2
+ existsSync,
3
+ } from "node:fs";
4
+ import path from "node:path";
5
+
6
+ import {
7
+ readWorkspaceJsonBounded,
8
+ resolveBoundedWorkspaceRoot,
9
+ } from "./onboarding-doctor.mjs";
10
+ import { resolveProfileImpactDetailed } from "./profile-impact.mjs";
11
+ import {
12
+ safeIdentifier,
13
+ safeRelativePath,
14
+ } from "./provenance-common.mjs";
15
+
16
+ function command(id, script) {
17
+ return {
18
+ id,
19
+ cwd: ".",
20
+ executable: "npm",
21
+ argv: ["run", script],
22
+ };
23
+ }
24
+
25
+ function detectedScripts(manifest) {
26
+ return Object.keys(manifest.scripts ?? {}).sort();
27
+ }
28
+
29
+ function profileCandidates(root, manifest) {
30
+ const scripts = new Set(detectedScripts(manifest));
31
+ const candidates = [];
32
+ if (
33
+ existsSync(path.join(root, "prisma"))
34
+ || [...scripts].some((script) => /^(db:|prisma)/.test(script))
35
+ ) {
36
+ const commands = [...scripts]
37
+ .filter((script) => /^(db:|prisma)/.test(script))
38
+ .map((script) => command(`npm-${script.replaceAll(":", "-")}`, script));
39
+ candidates.push({
40
+ id: "database",
41
+ paths: ["prisma/**"],
42
+ role: "primary",
43
+ primary: false,
44
+ requiresProfileIds: [],
45
+ commands,
46
+ });
47
+ }
48
+ if (
49
+ existsSync(path.join(root, "package.json"))
50
+ && [...scripts].some((script) => ["build", "lint", "test", "typecheck"].includes(script))
51
+ ) {
52
+ const commands = ["build", "lint", "test", "typecheck"]
53
+ .filter((script) => scripts.has(script))
54
+ .map((script) => command(`npm-${script}`, script));
55
+ candidates.push({
56
+ id: "node-quality",
57
+ paths: ["src/**", "tests/**"],
58
+ role: "primary",
59
+ primary: false,
60
+ requiresProfileIds: [],
61
+ commands,
62
+ });
63
+ }
64
+ if (
65
+ existsSync(path.join(root, "playwright.config.ts"))
66
+ || existsSync(path.join(root, "playwright.config.js"))
67
+ || scripts.has("e2e")
68
+ ) {
69
+ const commands = scripts.has("e2e") ? [command("npm-e2e", "e2e")] : [];
70
+ candidates.push({
71
+ id: "web-e2e",
72
+ paths: ["playwright.config.ts", "tests/e2e/**"],
73
+ role: "primary",
74
+ primary: false,
75
+ requiresProfileIds: [],
76
+ commands,
77
+ });
78
+ }
79
+ return candidates.sort((left, right) => left.id.localeCompare(right.id));
80
+ }
81
+
82
+ export function detectProfiles({ workspaceRoot } = {}) {
83
+ const root = resolveBoundedWorkspaceRoot(workspaceRoot);
84
+ const manifest = readWorkspaceJsonBounded(root, "package.json");
85
+ const candidates = profileCandidates(root, manifest);
86
+ const primaryCandidates = candidates.filter((profile) => profile.role === "primary");
87
+ return {
88
+ schemaVersion: "OwlCodaRunKitProfileDetectionV1",
89
+ status: "profiles_detected",
90
+ dryRun: true,
91
+ writesPerformed: 0,
92
+ candidates,
93
+ primaryProfileId: primaryCandidates.length === 1 ? primaryCandidates[0].id : null,
94
+ warnings: primaryCandidates.length > 1
95
+ ? [`ambiguous_primary_profile:${primaryCandidates.map((profile) => profile.id).join(",")}`]
96
+ : [],
97
+ detectedScripts: detectedScripts(manifest),
98
+ authorizationGranted: false,
99
+ };
100
+ }
101
+
102
+ function exactKeys(value, allowed) {
103
+ return value
104
+ && typeof value === "object"
105
+ && !Array.isArray(value)
106
+ && Object.keys(value).every((key) => allowed.includes(key));
107
+ }
108
+
109
+ function representativePath(rule) {
110
+ return rule.endsWith("/**")
111
+ ? `${rule.slice(0, -3)}/__owlrunkit_profile_surface__`
112
+ : rule;
113
+ }
114
+
115
+ function validateProfileDocument(config) {
116
+ const issues = [];
117
+ if (
118
+ !exactKeys(config, ["schemaVersion", "profiles"])
119
+ || config.schemaVersion !== "OwlCodaRunKitProfilesV1"
120
+ || !Array.isArray(config.profiles)
121
+ || config.profiles.length === 0
122
+ ) {
123
+ return ["profiles_document_invalid"];
124
+ }
125
+
126
+ const ids = new Set();
127
+ const commandIds = new Set();
128
+ for (const profile of config.profiles) {
129
+ if (!exactKeys(profile, [
130
+ "commands",
131
+ "id",
132
+ "paths",
133
+ "primary",
134
+ "requiresProfileIds",
135
+ "role",
136
+ ])) {
137
+ issues.push("profile_shape_invalid");
138
+ continue;
139
+ }
140
+ if (typeof profile.id !== "string" || profile.id.length === 0) {
141
+ issues.push("profile_id_invalid");
142
+ } else if (ids.has(profile.id)) {
143
+ issues.push(`duplicate_profile_id:${profile.id}`);
144
+ } else {
145
+ ids.add(profile.id);
146
+ }
147
+ if (!Array.isArray(profile.paths) || profile.paths.length === 0) {
148
+ issues.push(`profile_paths_missing:${profile.id ?? "unknown"}`);
149
+ }
150
+ if (profile.role !== undefined && !["primary", "supporting"].includes(profile.role)) {
151
+ issues.push(`profile_role_invalid:${profile.id ?? "unknown"}`);
152
+ }
153
+ if (profile.primary !== undefined && typeof profile.primary !== "boolean") {
154
+ issues.push(`profile_primary_invalid:${profile.id ?? "unknown"}`);
155
+ }
156
+ if (
157
+ profile.requiresProfileIds !== undefined
158
+ && (
159
+ !Array.isArray(profile.requiresProfileIds)
160
+ || profile.requiresProfileIds.some((id) => typeof id !== "string" || id.length === 0)
161
+ || new Set(profile.requiresProfileIds).size !== profile.requiresProfileIds.length
162
+ )
163
+ ) {
164
+ issues.push(`profile_requirements_invalid:${profile.id ?? "unknown"}`);
165
+ }
166
+ if (profile.commands !== undefined) {
167
+ if (!Array.isArray(profile.commands)) {
168
+ issues.push(`profile_commands_invalid:${profile.id ?? "unknown"}`);
169
+ } else {
170
+ for (const entry of profile.commands) {
171
+ if (
172
+ !exactKeys(entry, ["argv", "cwd", "executable", "id"])
173
+ || typeof entry.id !== "string"
174
+ || entry.id.length === 0
175
+ || typeof entry.cwd !== "string"
176
+ || entry.cwd.length === 0
177
+ || typeof entry.executable !== "string"
178
+ || entry.executable.length === 0
179
+ || !Array.isArray(entry.argv)
180
+ || entry.argv.some((argument) => typeof argument !== "string" || argument.length === 0)
181
+ ) {
182
+ issues.push(`profile_command_invalid:${profile.id ?? "unknown"}`);
183
+ break;
184
+ }
185
+ try {
186
+ safeIdentifier(entry.id, "profile command id");
187
+ } catch {
188
+ issues.push(`profile_command_id_invalid:${profile.id}:${entry.id}`);
189
+ }
190
+ try {
191
+ safeRelativePath(entry.cwd, "profile command cwd", { allowDot: true });
192
+ } catch {
193
+ issues.push(`profile_command_cwd_invalid:${profile.id}:${entry.id}`);
194
+ }
195
+ if (commandIds.has(entry.id)) {
196
+ issues.push(`duplicate_profile_command_id:${entry.id}`);
197
+ } else {
198
+ commandIds.add(entry.id);
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
204
+ if (issues.length > 0) return [...new Set(issues)].sort();
205
+
206
+ try {
207
+ const changedPaths = config.profiles.flatMap((profile) =>
208
+ profile.paths.map(representativePath));
209
+ resolveProfileImpactDetailed({ changedPaths, profiles: config.profiles });
210
+ } catch (error) {
211
+ issues.push(error instanceof Error ? error.message : String(error));
212
+ }
213
+
214
+ for (const profile of config.profiles) {
215
+ for (const requiredId of profile.requiresProfileIds ?? []) {
216
+ if (!ids.has(requiredId)) {
217
+ issues.push(`required_profile_missing:${profile.id}:${requiredId}`);
218
+ }
219
+ }
220
+ }
221
+ const actionable = config.profiles.filter((profile) =>
222
+ (profile.role ?? "primary") === "primary"
223
+ && Array.isArray(profile.commands)
224
+ && profile.commands.length > 0);
225
+ if (actionable.length === 0) issues.push("actionable_primary_profile_missing");
226
+ const explicitPrimaryIds = actionable
227
+ .filter((profile) => profile.primary === true)
228
+ .map((profile) => profile.id)
229
+ .sort();
230
+ if (explicitPrimaryIds.length > 1) {
231
+ issues.push(`ambiguous_primary_profile:${explicitPrimaryIds.join(",")}`);
232
+ } else if (explicitPrimaryIds.length === 0 && actionable.length > 1) {
233
+ issues.push(`ambiguous_primary_profile:${actionable.map((profile) => profile.id).sort().join(",")}`);
234
+ }
235
+ return [...new Set(issues)].sort();
236
+ }
237
+
238
+ export function validateProfiles({ workspaceRoot } = {}) {
239
+ const root = resolveBoundedWorkspaceRoot(workspaceRoot);
240
+ const issues = [];
241
+ let profileIds = [];
242
+ try {
243
+ const config = readWorkspaceJsonBounded(
244
+ root,
245
+ path.join(".owlcoda", "runkit", "profiles.json"),
246
+ );
247
+ profileIds = Array.isArray(config.profiles)
248
+ ? config.profiles
249
+ .map((profile) => profile?.id)
250
+ .filter((id) => typeof id === "string")
251
+ .sort()
252
+ : [];
253
+ issues.push(...validateProfileDocument(config));
254
+ if (issues.length === 0) {
255
+ const detected = detectProfiles({ workspaceRoot: root });
256
+ const detectedPaths = detected.candidates.flatMap((profile) =>
257
+ profile.paths.map(representativePath));
258
+ if (detectedPaths.length > 0) {
259
+ const impact = resolveProfileImpactDetailed({
260
+ changedPaths: detectedPaths,
261
+ profiles: config.profiles,
262
+ });
263
+ issues.push(...impact.uncoveredPaths.map((value) =>
264
+ `uncovered_detected_surface:${value}`));
265
+ issues.push(...impact.warnings
266
+ .filter((warning) => warning.startsWith("supporting_only_match:"))
267
+ .map((warning) => `detected_surface_${warning}`));
268
+ }
269
+ }
270
+ } catch (error) {
271
+ issues.push(error instanceof Error ? error.message : String(error));
272
+ }
273
+ const valid = issues.length === 0;
274
+ return {
275
+ schemaVersion: "OwlCodaRunKitProfilesValidationV1",
276
+ status: valid ? "valid" : "invalid",
277
+ valid,
278
+ exitCode: valid ? 0 : 2,
279
+ profileIds,
280
+ issues: [...new Set(issues)].sort(),
281
+ authorizationGranted: false,
282
+ };
283
+ }
284
+
285
+ export function resolveProfilesImpact({ workspaceRoot, changedPaths } = {}) {
286
+ const root = resolveBoundedWorkspaceRoot(workspaceRoot);
287
+ const config = readWorkspaceJsonBounded(
288
+ root,
289
+ path.join(".owlcoda", "runkit", "profiles.json"),
290
+ );
291
+ const documentIssues = validateProfileDocument(config);
292
+ if (documentIssues.length > 0) {
293
+ return {
294
+ schemaVersion: "OwlCodaRunKitProfileImpactV1",
295
+ status: "profile_impact_blocked",
296
+ valid: false,
297
+ exitCode: 2,
298
+ changedPaths: [...new Set(changedPaths)].sort(),
299
+ decision: "invalid_profiles",
300
+ primaryProfileId: null,
301
+ directProfileIds: [],
302
+ transitiveProfileIds: [],
303
+ supportingProfileIds: [],
304
+ selectedProfileIds: [],
305
+ uncoveredPaths: [],
306
+ warnings: [],
307
+ issues: documentIssues,
308
+ authorizationGranted: false,
309
+ };
310
+ }
311
+ const impact = resolveProfileImpactDetailed({
312
+ changedPaths,
313
+ profiles: config.profiles,
314
+ });
315
+ const issues = [
316
+ ...impact.uncoveredPaths.map((value) => `uncovered_changed_path:${value}`),
317
+ ...impact.warnings.filter((warning) =>
318
+ warning.startsWith("supporting_only_match:")
319
+ || warning.startsWith("ambiguous_primary_profile:")),
320
+ ];
321
+ const valid = impact.decision === "targeted_profiles" && issues.length === 0;
322
+ return {
323
+ schemaVersion: "OwlCodaRunKitProfileImpactV1",
324
+ status: valid ? "profile_impact_resolved" : "profile_impact_blocked",
325
+ valid,
326
+ exitCode: valid ? 0 : 2,
327
+ changedPaths: [...new Set(changedPaths)].sort(),
328
+ ...impact,
329
+ issues: [...new Set(issues)].sort(),
330
+ authorizationGranted: false,
331
+ };
332
+ }
@@ -32,12 +32,12 @@ function decision(policy, issueCodes) {
32
32
  };
33
33
  }
34
34
 
35
- function expectedTarballUrl(policy) {
36
- const tarballName = policy.packageName.split("/").at(-1);
37
- return `${OFFICIAL_NPM_REGISTRY}/${policy.packageName}/-/${tarballName}-${policy.candidateVersion}.tgz`;
35
+ export function expectedRegistryTarballUrl(packageName, version) {
36
+ const tarballName = packageName.split("/").at(-1);
37
+ return `${OFFICIAL_NPM_REGISTRY}/${packageName}/-/${tarballName}-${version}.tgz`;
38
38
  }
39
39
 
40
- function validSha512Integrity(value) {
40
+ export function isCanonicalSha512Integrity(value) {
41
41
  if (typeof value !== "string") return false;
42
42
  const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(value);
43
43
  if (!match) return false;
@@ -49,9 +49,12 @@ function validReleaseEvidence(policy, release) {
49
49
  if (
50
50
  policy.registry !== OFFICIAL_NPM_REGISTRY
51
51
  || Object.keys(release).sort().join("\n") !== RELEASE_EVIDENCE_FIELDS.join("\n")
52
- || !/^[0-9a-f]{40}$/.test(release.shasum)
53
- || !validSha512Integrity(release.integrity)
54
- || release.tarballUrl !== expectedTarballUrl(policy)
52
+ || !/^[0-9a-f]{40}$/i.test(release.shasum)
53
+ || !isCanonicalSha512Integrity(release.integrity)
54
+ || release.tarballUrl !== expectedRegistryTarballUrl(
55
+ policy.packageName,
56
+ policy.candidateVersion,
57
+ )
55
58
  ) {
56
59
  return false;
57
60
  }
@@ -0,0 +1,197 @@
1
+ import {
2
+ existsSync,
3
+ lstatSync,
4
+ mkdirSync,
5
+ realpathSync,
6
+ } from "node:fs";
7
+ import path from "node:path";
8
+
9
+ import {
10
+ OFFICIAL_NPM_REGISTRY,
11
+ evaluateRegistryAdoption,
12
+ } from "./registry-adoption-gate.mjs";
13
+ import {
14
+ readLocalInstallBinding,
15
+ readRegistryExact,
16
+ resolveBoundedWorkspaceRoot,
17
+ } from "./onboarding-doctor.mjs";
18
+ import { writeJsonExclusiveAtomically } from "./provenance-common.mjs";
19
+
20
+ const PACKAGE_NAME = "owlrunkit";
21
+ const CANDIDATE_VERSION = "0.14.0";
22
+ const EXACT_SPEC = `${PACKAGE_NAME}@${CANDIDATE_VERSION}`;
23
+
24
+ const POLICY = Object.freeze({
25
+ schemaVersion: "OwlCodaRunKitRegistryReleasePolicyV1",
26
+ status: "registry_verified",
27
+ packageName: PACKAGE_NAME,
28
+ candidateVersion: CANDIDATE_VERSION,
29
+ registry: OFFICIAL_NPM_REGISTRY,
30
+ registryRequiredBeforeAdoption: true,
31
+ exactVersionRequired: true,
32
+ requiredRegistryBindings: [
33
+ "packageName",
34
+ "version",
35
+ "shasum",
36
+ "integrity",
37
+ "tarballUrl",
38
+ ],
39
+ forbiddenAdoptionSources: [
40
+ "directory",
41
+ "file",
42
+ "git",
43
+ "local_tarball",
44
+ "symlink",
45
+ "workspace",
46
+ ],
47
+ currentPublishedRelease: null,
48
+ registryRelease: null,
49
+ registryEvidenceMustBeExternal: true,
50
+ registryEvidenceSchemaVersion: "OwlCodaRunKitRegistryReleaseEvidenceV1",
51
+ embeddedAttestComponentVersion: "0.2.0",
52
+ standaloneAttestPublicationRequired: false,
53
+ signatureRequired: false,
54
+ githubActionRequired: false,
55
+ foreignProjectWriteAuthorized: false,
56
+ authorizationGranted: false,
57
+ });
58
+
59
+ function ineligible(issueCodes) {
60
+ return {
61
+ schemaVersion: "OwlCodaRunKitRegistryAdoptionResultV1",
62
+ status: "ineligible",
63
+ decision: "INELIGIBLE",
64
+ issueCodes,
65
+ packageName: PACKAGE_NAME,
66
+ version: CANDIDATE_VERSION,
67
+ authorizationGranted: false,
68
+ };
69
+ }
70
+
71
+ function assertRealDirectory(directory, label) {
72
+ const stat = lstatSync(directory);
73
+ if (stat.isSymbolicLink() || !stat.isDirectory() || realpathSync(directory) !== path.resolve(directory)) {
74
+ throw new Error(`${label} must be a real directory without symlink ancestors.`);
75
+ }
76
+ }
77
+
78
+ function adoptionRoot(workspaceRoot) {
79
+ const root = resolveBoundedWorkspaceRoot(workspaceRoot);
80
+ const owlcodaRoot = path.join(root, ".owlcoda");
81
+ const runtimeRoot = path.join(owlcodaRoot, "runkit");
82
+ assertRealDirectory(owlcodaRoot, ".owlcoda");
83
+ assertRealDirectory(runtimeRoot, "RunKit runtime");
84
+ const target = path.join(runtimeRoot, "adoption");
85
+ if (!existsSync(target)) mkdirSync(target);
86
+ assertRealDirectory(target, "RunKit adoption directory");
87
+ return target;
88
+ }
89
+
90
+ export async function runRegistryAdoption({
91
+ workspaceRoot,
92
+ exactSpec,
93
+ registryClient,
94
+ timeoutMs = 1_500,
95
+ } = {}) {
96
+ if (exactSpec !== EXACT_SPEC) return ineligible(["exact_registry_spec_required"]);
97
+ const root = resolveBoundedWorkspaceRoot(workspaceRoot);
98
+ const local = readLocalInstallBinding({ workspaceRoot: root });
99
+ if (local.status !== "bound") {
100
+ return ineligible(local.issueCodes ?? ["registry_install_binding_mismatch"]);
101
+ }
102
+ const observedRelease = registryClient
103
+ ? await registryClient.readExact({
104
+ registry: OFFICIAL_NPM_REGISTRY,
105
+ packageName: PACKAGE_NAME,
106
+ version: CANDIDATE_VERSION,
107
+ })
108
+ : await readRegistryExact({
109
+ registryUrl: OFFICIAL_NPM_REGISTRY,
110
+ packageName: PACKAGE_NAME,
111
+ version: CANDIDATE_VERSION,
112
+ timeoutMs,
113
+ });
114
+ const release = observedRelease.status === "registry_verified"
115
+ ? {
116
+ schemaVersion: "OwlCodaRunKitRegistryReleaseEvidenceV1",
117
+ ...observedRelease,
118
+ }
119
+ : observedRelease;
120
+ if (release.status !== "registry_verified") {
121
+ return ineligible(release.issueCodes ?? ["registry_release_not_verified"]);
122
+ }
123
+ const install = {
124
+ kind: "registry",
125
+ requestedSpec: EXACT_SPEC,
126
+ packageName: local.packageName,
127
+ version: local.version,
128
+ shasum: release.shasum,
129
+ integrity: local.integrity,
130
+ resolved: local.resolved,
131
+ };
132
+ const decision = evaluateRegistryAdoption({
133
+ policy: POLICY,
134
+ releaseEvidence: release,
135
+ install,
136
+ });
137
+ if (decision.decision !== "ELIGIBLE") return ineligible(decision.issueCodes);
138
+
139
+ const relativeEvidencePath = `.owlcoda/runkit/adoption/${PACKAGE_NAME}-${CANDIDATE_VERSION}.json`;
140
+ const evidencePath = path.join(adoptionRoot(root), `${PACKAGE_NAME}-${CANDIDATE_VERSION}.json`);
141
+ const evidence = {
142
+ schemaVersion: "OwlCodaRunKitRegistryAdoptionEvidenceV1",
143
+ status: "adopted",
144
+ decision: "ELIGIBLE",
145
+ packageName: PACKAGE_NAME,
146
+ version: CANDIDATE_VERSION,
147
+ exactSpec: EXACT_SPEC,
148
+ registryRelease: {
149
+ registry: release.registry,
150
+ packageName: release.packageName,
151
+ version: release.version,
152
+ shasum: release.shasum,
153
+ integrity: release.integrity,
154
+ tarballUrl: release.tarballUrl,
155
+ },
156
+ installedBinding: {
157
+ requestedSpec: local.requestedSpec,
158
+ packageName: local.packageName,
159
+ version: local.version,
160
+ resolved: local.resolved,
161
+ integrity: local.integrity,
162
+ },
163
+ authority: {
164
+ git: false,
165
+ release: false,
166
+ foreignProjectWrite: false,
167
+ },
168
+ authorizationGranted: false,
169
+ };
170
+ writeJsonExclusiveAtomically(evidencePath, evidence);
171
+ return {
172
+ schemaVersion: "OwlCodaRunKitRegistryAdoptionResultV1",
173
+ status: "adopted",
174
+ decision: "ELIGIBLE",
175
+ evidencePath: relativeEvidencePath,
176
+ issueCodes: [],
177
+ packageName: PACKAGE_NAME,
178
+ version: CANDIDATE_VERSION,
179
+ authorizationGranted: false,
180
+ };
181
+ }
182
+
183
+ export function formatAdoptionHuman(result) {
184
+ if (result.status === "adopted") {
185
+ return [
186
+ `Adopted ${result.packageName}@${result.version}`,
187
+ `Evidence: ${result.evidencePath}`,
188
+ "Git and release authority remain false.",
189
+ "",
190
+ ].join("\n");
191
+ }
192
+ return [
193
+ `Adoption blocked: ${result.issueCodes.join(", ")}`,
194
+ "No adoption evidence was written.",
195
+ "",
196
+ ].join("\n");
197
+ }
@@ -94,6 +94,11 @@ function assertRegularArtifact(filePath, label) {
94
94
  if (!stat.isFile()) throw new Error(`${label} must be a regular file.`);
95
95
  }
96
96
 
97
+ function readRegularJsonArtifact(filePath, label) {
98
+ assertRegularArtifact(filePath, label);
99
+ return readJson(filePath);
100
+ }
101
+
97
102
  function acquireResumeLock(workspaceRoot) {
98
103
  const runtimeRoot = path.join(workspaceRoot, RUNTIME_ROOT);
99
104
  assertSafeDirectory(workspaceRoot, runtimeRoot, "RunKit runtime root");
@@ -299,7 +304,7 @@ function validatedParentCloseout(workspaceRoot, sourceRoot, sourceRunId) {
299
304
  throw new Error("Parent closeout payload is invalid.");
300
305
  }
301
306
  if (payload.decision === "accepted") validateAcceptedParentEvidence(sourceRoot, sourceRunId, payload.verification);
302
- const pin = readJson(path.join(sourceRoot, "engine-pin.json"));
307
+ const pin = readRegularJsonArtifact(path.join(sourceRoot, "engine-pin.json"), "Parent engine pin");
303
308
  const pinGate = validateExecutionPin({ expected: pin, actual: closeout.artifact.core });
304
309
  if (pinGate.status !== "valid") throw new Error(`Parent closeout does not match its engine pin: ${pinGate.issues.join("; ")}`);
305
310
  return {
@@ -392,7 +397,7 @@ function appendSameExecution({ workspaceRoot, sourceRunId, sourceRoot, request,
392
397
  throw new Error("Unclosed execution resume requires continuationRunId=null.");
393
398
  }
394
399
  assertSoleActiveRun(workspaceRoot, sourceRunId);
395
- const pin = readJson(path.join(sourceRoot, "engine-pin.json"));
400
+ const pin = readRegularJsonArtifact(path.join(sourceRoot, "engine-pin.json"), "Resume engine pin");
396
401
  const pinGate = validateExecutionPin({ expected: pin, actual: currentCoreIdentity() });
397
402
  if (pinGate.status !== "valid") throw new Error(`Resume engine pin is stale: ${pinGate.issues.join("; ")}`);
398
403
  assertNoActiveWriterLease(sourceRoot);
@@ -507,6 +512,7 @@ function createContinuation({ workspaceRoot, sourceRunId, sourceRoot, request, g
507
512
  runId: targetRunId,
508
513
  state: "planned",
509
514
  enginePin,
515
+ goalContractSha256: sha256(goal.bytes),
510
516
  continuation: {
511
517
  parentRunId: sourceRunId,
512
518
  resumeId: request.resumeId,