owlrunkit 0.19.2 → 0.21.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 (51) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +94 -8
  3. package/docs/architecture/OWLCODA_RUNKIT_0201_RELEASE_TRUTH_AND_PROFILE_NORMALIZATION_PATCH_DECISION_20260813.md +95 -0
  4. package/docs/architecture/OWLCODA_RUNKIT_020_TEAM_DELIVERY_PHASE1_PRODUCT_DECISION_20260813.md +296 -0
  5. package/docs/architecture/OWLCODA_RUNKIT_021_TEAM_DELIVERY_PHASE2_PRODUCT_DECISION_20260813.md +249 -0
  6. package/integrations/codex/skills/owlcoda-runkit/SKILL.md +114 -12
  7. package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
  8. package/package.json +1 -1
  9. package/packages/attest/README.md +7 -1
  10. package/packages/attest/consumer-surface-v3.json +275 -0
  11. package/packages/attest/distribution-manifest-v1.json +19 -0
  12. package/packages/attest/schemas/canonical-candidate-identity-v1.schema.json +112 -0
  13. package/packages/attest/schemas/foreign-workspace-filesystem-identity-v1.schema.json +32 -0
  14. package/packages/attest/schemas/quick-verification-receipt-v4.schema.json +226 -0
  15. package/packages/attest/src/candidate-identity.mjs +454 -0
  16. package/packages/attest/src/index.mjs +11 -0
  17. package/packages/attest/src/quick-receipt-contract.mjs +126 -5
  18. package/packages/attest/src/quick.mjs +48 -8
  19. package/{registry-release-policy-v1.json → registry-release-policy-v2.json} +8 -9
  20. package/sbom.spdx.json +4 -4
  21. package/scripts/runkit-contract/core-contract.mjs +21 -1
  22. package/scripts/runkit-contract/fleet-skill-plan.mjs +223 -0
  23. package/scripts/runkit-contract/formal-workflow.mjs +3 -1
  24. package/scripts/runkit-contract/inspect-presentation.mjs +56 -0
  25. package/scripts/runkit-contract/install-codex-skill.mjs +1 -0
  26. package/scripts/runkit-contract/onboarding-bootstrap.mjs +15 -0
  27. package/scripts/runkit-contract/onboarding-doctor.mjs +201 -7
  28. package/scripts/runkit-contract/profile-onboarding.mjs +6 -0
  29. package/scripts/runkit-contract/project-cli-resolver.mjs +12 -1
  30. package/scripts/runkit-contract/quick-metrics.mjs +9 -5
  31. package/scripts/runkit-contract/quick-verify.mjs +108 -24
  32. package/scripts/runkit-contract/registry-adoption-gate.mjs +12 -3
  33. package/scripts/runkit-contract/registry-adoption.mjs +10 -5
  34. package/scripts/runkit-contract/runkit-cli.mjs +367 -20
  35. package/scripts/runkit-contract/schemas/candidate-transfer-packet-v1.schema.json +98 -0
  36. package/scripts/runkit-contract/schemas/delivery-lifecycle-v1.schema.json +91 -0
  37. package/scripts/runkit-contract/schemas/external-gate-v1.schema.json +68 -0
  38. package/scripts/runkit-contract/schemas/fleet-skill-upgrade-plan-v1.schema.json +140 -0
  39. package/scripts/runkit-contract/schemas/inspect-compact-v1.schema.json +93 -0
  40. package/scripts/runkit-contract/schemas/team-acceptance-packet-v2.schema.json +109 -0
  41. package/scripts/runkit-contract/schemas/team-delivery-status-v1.schema.json +72 -0
  42. package/scripts/runkit-contract/schemas/team-delivery-status-v2.schema.json +219 -0
  43. package/scripts/runkit-contract/schemas/team-packet-validation-v1.schema.json +33 -0
  44. package/scripts/runkit-contract/schemas/team-project-event-v6.schema.json +82 -0
  45. package/scripts/runkit-contract/schemas/team-project-event-v7.schema.json +80 -0
  46. package/scripts/runkit-contract/schemas/team-project-status-v4.schema.json +208 -0
  47. package/scripts/runkit-contract/schemas/team-recommendation-v1.schema.json +121 -0
  48. package/scripts/runkit-contract/schemas/team-task-packet-v2.schema.json +228 -0
  49. package/scripts/runkit-contract/sync-release-identity.mjs +87 -0
  50. package/scripts/runkit-contract/team-delivery.mjs +1031 -0
  51. package/scripts/runkit-contract/team-project.mjs +970 -9
@@ -15,10 +15,21 @@ const MAX_CORE_TOTAL_BYTES = 67_108_864;
15
15
  const PACKAGE_NAME = "owlrunkit";
16
16
  const OFFICIAL_CLI_ENTRYPOINT =
17
17
  "scripts/runkit-contract/runkit-bootstrap.mjs";
18
- const SELF_NORMALIZED_QUICK_CORE_VERSIONS = new Set(["0.19.2"]);
18
+ const SELF_NORMALIZED_QUICK_CORE_VERSIONS = new Set([
19
+ "0.19.2",
20
+ "0.20.0",
21
+ "0.20.1",
22
+ "0.21.0",
23
+ ]);
19
24
  const EXACT_VERSION = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/;
20
25
  const SHA256_REF = /^sha256:[a-f0-9]{64}$/;
21
26
  const TRUSTED_PRIOR_CORE_IDENTITIES = new Map([
27
+ ["0.20.0", new Set([
28
+ "sha256:30098aa9344c17c1ed0b5ed45ef1c590b3214b824402f584029c1ecb38ca7a70",
29
+ ])],
30
+ ["0.19.2", new Set([
31
+ "sha256:c46ae9d8ed0572e5636ab5725f922f8db3ee5bf0f966b200bce7b2287c9267c8",
32
+ ])],
22
33
  ["0.19.1", new Set([
23
34
  "sha256:291ef97c93a513eed24a29f75c400b61cefd6a8c0ab228401cf2c6a73ea9e622",
24
35
  ])],
@@ -22,12 +22,13 @@ const INVALID_ATTESTATION_ISSUES = new Set([
22
22
  "attestation_material_missing",
23
23
  "receipt_material_hash_mismatch",
24
24
  ]);
25
- const V3_MUTATION_ISSUES = new Set([
25
+ const SUCCESSOR_MUTATION_ISSUES = new Set([
26
26
  "source_mutated_during_verification",
27
27
  "bound_ignored_path_mutated_during_verification",
28
28
  "dependency_environment_mutated_during_verification",
29
29
  "consumer_source_mutated_during_verification",
30
30
  "quick_isolation_cleanup_failed",
31
+ "foreign_workspace_mutated_during_verification",
31
32
  ]);
32
33
 
33
34
  function readHistoricalReceipt(receiptPath) {
@@ -38,11 +39,14 @@ function readHistoricalReceipt(receiptPath) {
38
39
  && quickSnapshotFingerprintValid(receipt.workspaceAfter);
39
40
  const sourceChanged = receipt.workspaceBefore.sourceFingerprint
40
41
  !== receipt.workspaceAfter.sourceFingerprint;
41
- const receiptV3 = receipt.schemaVersion === "OwlCodaQuickVerificationReceiptV3";
42
+ const successorReceipt = [
43
+ "OwlCodaQuickVerificationReceiptV3",
44
+ "OwlCodaQuickVerificationReceiptV4",
45
+ ].includes(receipt.schemaVersion);
42
46
  const mutationBindingValid = receipt.mutationDecision === "source_unchanged"
43
- ? !sourceChanged && !receipt.issueCodes.some(issue => V3_MUTATION_ISSUES.has(issue))
44
- : receiptV3
45
- ? receipt.issueCodes.some(issue => V3_MUTATION_ISSUES.has(issue))
47
+ ? !sourceChanged && !receipt.issueCodes.some(issue => SUCCESSOR_MUTATION_ISSUES.has(issue))
48
+ : successorReceipt
49
+ ? receipt.issueCodes.some(issue => SUCCESSOR_MUTATION_ISSUES.has(issue))
46
50
  : sourceChanged && receipt.issueCodes.includes("source_mutated_during_verification");
47
51
  if (!snapshotsValid || !mutationBindingValid) {
48
52
  throw new Error("quick_receipt_source_binding_invalid");
@@ -24,6 +24,10 @@ import {
24
24
  captureWorkspaceSnapshot,
25
25
  workspaceSourceStateFingerprint,
26
26
  } from "./quick-workspace-snapshot.mjs";
27
+ import {
28
+ captureCanonicalCandidateIdentityV1,
29
+ captureForeignWorkspaceFilesystemIdentityV1,
30
+ } from "../../packages/attest/src/candidate-identity.mjs";
27
31
  import {
28
32
  captureRuntimeTransition,
29
33
  runtimeReportPath,
@@ -81,7 +85,13 @@ function inputArtifactResult(stdinArtifact) {
81
85
  : { inputArtifacts: { stdin: stdinArtifact } };
82
86
  }
83
87
 
84
- function mutationTruth({ workspaceBefore, workspaceAfter, isolationResult, cleanupStatus }) {
88
+ function mutationTruth({
89
+ workspaceBefore,
90
+ workspaceAfter,
91
+ isolationResult,
92
+ cleanupStatus,
93
+ foreignWorkspace = null,
94
+ }) {
85
95
  const workspaceSourceChanged = workspaceSourceStateFingerprint(workspaceBefore)
86
96
  !== workspaceSourceStateFingerprint(workspaceAfter);
87
97
  const boundIgnoredChanged = workspaceBefore.ignoredPathsBound === true
@@ -89,12 +99,15 @@ function mutationTruth({ workspaceBefore, workspaceAfter, isolationResult, clean
89
99
  const dependencyChanged = isolationResult?.dependencyChanged === true;
90
100
  const consumerSourceChanged = (isolationResult?.consumerDelta.sourcePaths.length ?? 0) > 0;
91
101
  const cleanupFailed = cleanupStatus === "cleanup_failed";
102
+ const foreignChanged = foreignWorkspace !== null
103
+ && foreignWorkspace.zeroWriteObserved !== true;
92
104
  const issueCodes = [
93
105
  ...(workspaceSourceChanged ? ["source_mutated_during_verification"] : []),
94
106
  ...(boundIgnoredChanged ? ["bound_ignored_path_mutated_during_verification"] : []),
95
107
  ...(dependencyChanged ? ["dependency_environment_mutated_during_verification"] : []),
96
108
  ...(consumerSourceChanged ? ["consumer_source_mutated_during_verification"] : []),
97
109
  ...(cleanupFailed ? ["quick_isolation_cleanup_failed"] : []),
110
+ ...(foreignChanged ? ["foreign_workspace_mutated_during_verification"] : []),
98
111
  ];
99
112
  const decisions = [
100
113
  ...(workspaceSourceChanged ? ["invalidated_by_command_source_mutation"] : []),
@@ -102,6 +115,7 @@ function mutationTruth({ workspaceBefore, workspaceAfter, isolationResult, clean
102
115
  ...(dependencyChanged ? ["invalidated_by_dependency_environment_mutation"] : []),
103
116
  ...(consumerSourceChanged ? ["invalidated_by_consumer_source_mutation"] : []),
104
117
  ...(cleanupFailed ? ["invalidated_by_isolation_cleanup_failure"] : []),
118
+ ...(foreignChanged ? ["invalidated_by_foreign_workspace_mutation"] : []),
105
119
  ];
106
120
  return {
107
121
  mutationDecision: decisions.length === 0
@@ -125,10 +139,26 @@ function mutationTruth({ workspaceBefore, workspaceAfter, isolationResult, clean
125
139
  consumerIgnoredArtifacts: (isolationResult?.consumerDelta.ignoredPaths.length ?? 0) > 0
126
140
  ? "changed_and_discarded"
127
141
  : isolationResult === null ? "not_isolated" : "none",
142
+ ...(foreignWorkspace === null
143
+ ? {}
144
+ : { foreignTarget: foreignChanged ? "changed" : "unchanged" }),
128
145
  },
129
146
  };
130
147
  }
131
148
 
149
+ function rootsOverlap(left, right) {
150
+ const leftToRight = path.relative(left, right);
151
+ const rightToLeft = path.relative(right, left);
152
+ return leftToRight === ""
153
+ || rightToLeft === ""
154
+ || (leftToRight !== ".."
155
+ && !leftToRight.startsWith(`..${path.sep}`)
156
+ && !path.isAbsolute(leftToRight))
157
+ || (rightToLeft !== ".."
158
+ && !rightToLeft.startsWith(`..${path.sep}`)
159
+ && !path.isAbsolute(rightToLeft));
160
+ }
161
+
132
162
  function isolationReceipt(isolation, isolationResult, cleanupStatus) {
133
163
  if (isolation === null) return null;
134
164
  return {
@@ -150,6 +180,7 @@ function isolationReceipt(isolation, isolationResult, cleanupStatus) {
150
180
 
151
181
  export async function runQuickVerification({
152
182
  workspaceRoot,
183
+ foreignWorkspaceRoot,
153
184
  commandArgv,
154
185
  stdinFile,
155
186
  isolate = false,
@@ -165,18 +196,27 @@ export async function runQuickVerification({
165
196
  authorizationGranted: false,
166
197
  };
167
198
  }
168
- let root;
199
+ let controllerRoot;
200
+ let sourceRoot;
169
201
  try {
170
- root = realpathSync(workspaceRoot);
202
+ controllerRoot = realpathSync(workspaceRoot);
203
+ sourceRoot = foreignWorkspaceRoot === undefined
204
+ ? controllerRoot
205
+ : realpathSync(foreignWorkspaceRoot);
206
+ if (foreignWorkspaceRoot !== undefined && rootsOverlap(controllerRoot, sourceRoot)) {
207
+ throw new Error("Controller and source workspace overlap.");
208
+ }
171
209
  } catch {
172
210
  return {
173
211
  status: "quick_verification_input_invalid",
174
212
  exitCode: 3,
175
- issues: ["Workspace does not exist."],
213
+ issues: ["Controller or source workspace does not exist or is not safely separated."],
176
214
  authorizationGranted: false,
177
215
  };
178
216
  }
179
- if (dependencyRoot !== undefined && isolate !== true) {
217
+ const foreignMode = foreignWorkspaceRoot !== undefined;
218
+ const effectiveIsolation = isolate === true || foreignMode;
219
+ if (dependencyRoot !== undefined && effectiveIsolation !== true) {
180
220
  return {
181
221
  status: "quick_verification_input_invalid",
182
222
  exitCode: 3,
@@ -184,6 +224,14 @@ export async function runQuickVerification({
184
224
  authorizationGranted: false,
185
225
  };
186
226
  }
227
+ if (foreignMode && captureRuntime === true) {
228
+ return {
229
+ status: "quick_verification_input_invalid",
230
+ exitCode: 3,
231
+ issues: ["Foreign Quick does not capture external runtime transitions."],
232
+ authorizationGranted: false,
233
+ };
234
+ }
187
235
  if (!Array.isArray(ignoredPaths) || ignoredPaths.some(value => typeof value !== "string")) {
188
236
  return {
189
237
  status: "quick_verification_input_invalid",
@@ -206,7 +254,7 @@ export async function runQuickVerification({
206
254
  try {
207
255
  const selected = path.isAbsolute(stdinFile)
208
256
  ? stdinFile
209
- : path.resolve(root, stdinFile);
257
+ : path.resolve(sourceRoot, stdinFile);
210
258
  stdinBytes = readFileBytesBounded(selected).bytes;
211
259
  } catch (error) {
212
260
  return {
@@ -219,25 +267,32 @@ export async function runQuickVerification({
219
267
  }
220
268
 
221
269
  let workspaceBefore;
270
+ let foreignWorkspaceBefore = null;
222
271
  let store;
223
272
  let stdinArtifact = null;
224
273
  let persistedStdinPath;
225
274
  let isolation = null;
226
275
  try {
227
- workspaceBefore = captureWorkspaceSnapshot(root, { ignoredPaths });
228
- store = createQuickReceiptStore(root);
276
+ workspaceBefore = captureWorkspaceSnapshot(sourceRoot, { ignoredPaths });
277
+ if (foreignMode) {
278
+ foreignWorkspaceBefore = {
279
+ candidate: captureCanonicalCandidateIdentityV1(sourceRoot),
280
+ filesystem: captureForeignWorkspaceFilesystemIdentityV1(sourceRoot),
281
+ };
282
+ }
283
+ store = createQuickReceiptStore(controllerRoot);
229
284
  if (stdinBytes !== undefined) {
230
285
  const persistedInput = persistQuickInputArtifact({
231
- workspaceRoot: root,
286
+ workspaceRoot: controllerRoot,
232
287
  store,
233
288
  bytes: stdinBytes,
234
289
  });
235
290
  stdinArtifact = persistedInput.artifact;
236
291
  persistedStdinPath = persistedInput.inputPath;
237
292
  }
238
- if (isolate === true) {
239
- isolation = prepareQuickIsolation({ workspaceRoot: root, dependencyRoot });
240
- const stableSource = captureWorkspaceSnapshot(root, { ignoredPaths });
293
+ if (effectiveIsolation === true) {
294
+ isolation = prepareQuickIsolation({ workspaceRoot: sourceRoot, dependencyRoot });
295
+ const stableSource = captureWorkspaceSnapshot(sourceRoot, { ignoredPaths });
241
296
  if (stableSource.sourceFingerprint !== workspaceBefore.sourceFingerprint) {
242
297
  throw new Error("Source workspace changed while the temporary consumer was being prepared.");
243
298
  }
@@ -254,7 +309,7 @@ export async function runQuickVerification({
254
309
  }
255
310
 
256
311
  const [executable, ...argv] = commandArgv;
257
- const executionCwd = isolation?.consumerRoot ?? root;
312
+ const executionCwd = isolation?.consumerRoot ?? sourceRoot;
258
313
  const expectedRuntimeReportPath = captureRuntime
259
314
  ? runtimeReportPath(store.receiptRoot)
260
315
  : null;
@@ -279,6 +334,7 @@ export async function runQuickVerification({
279
334
  const finishedAt = new Date().toISOString();
280
335
 
281
336
  let workspaceAfter;
337
+ let foreignWorkspace = null;
282
338
  let isolationResult = null;
283
339
  let cleanupStatus = "not_required";
284
340
  try {
@@ -286,7 +342,21 @@ export async function runQuickVerification({
286
342
  isolationResult = inspectQuickIsolation(isolation);
287
343
  cleanupStatus = removeQuickIsolation(isolation);
288
344
  }
289
- workspaceAfter = captureWorkspaceSnapshot(root, { ignoredPaths });
345
+ workspaceAfter = captureWorkspaceSnapshot(sourceRoot, { ignoredPaths });
346
+ if (foreignMode) {
347
+ const candidateAfter = captureCanonicalCandidateIdentityV1(sourceRoot);
348
+ const filesystemAfter = captureForeignWorkspaceFilesystemIdentityV1(sourceRoot);
349
+ foreignWorkspace = {
350
+ candidateBefore: foreignWorkspaceBefore.candidate,
351
+ candidateAfter,
352
+ filesystemBefore: foreignWorkspaceBefore.filesystem,
353
+ filesystemAfter,
354
+ zeroWriteObserved: foreignWorkspaceBefore.candidate.candidateFingerprint
355
+ === candidateAfter.candidateFingerprint
356
+ && foreignWorkspaceBefore.filesystem.filesystemFingerprint
357
+ === filesystemAfter.filesystemFingerprint,
358
+ };
359
+ }
290
360
  } catch (error) {
291
361
  if (isolation !== null && cleanupStatus === "not_required") {
292
362
  removeQuickIsolation(isolation);
@@ -306,16 +376,20 @@ export async function runQuickVerification({
306
376
  workspaceAfter,
307
377
  isolationResult,
308
378
  cleanupStatus,
379
+ foreignWorkspace,
309
380
  });
310
381
  const sourceMutated = mutation.mutationDecision !== "source_unchanged";
311
- const receiptV3 = isolate === true || ignoredPaths.length > 0;
382
+ const receiptV4 = foreignMode;
383
+ const receiptV3 = !receiptV4 && (effectiveIsolation === true || ignoredPaths.length > 0);
312
384
  let persisted;
313
385
  try {
314
386
  persisted = persistQuickReceipt({
315
- workspaceRoot: root,
387
+ workspaceRoot: controllerRoot,
316
388
  store,
317
389
  receipt: {
318
- schemaVersion: receiptV3
390
+ schemaVersion: receiptV4
391
+ ? "OwlCodaQuickVerificationReceiptV4"
392
+ : receiptV3
319
393
  ? "OwlCodaQuickVerificationReceiptV3"
320
394
  : stdinArtifact === null
321
395
  ? "OwlCodaQuickVerificationReceiptV1"
@@ -334,13 +408,19 @@ export async function runQuickVerification({
334
408
  argv,
335
409
  cwd: executionCwd,
336
410
  },
337
- ...(receiptV3
411
+ ...(receiptV3 || receiptV4
338
412
  ? {
339
- sourceWorkspaceRoot: root,
413
+ sourceWorkspaceRoot: sourceRoot,
340
414
  executionIsolation: isolationReceipt(isolation, isolationResult, cleanupStatus),
341
415
  mutationClasses: mutation.mutationClasses,
342
416
  }
343
417
  : {}),
418
+ ...(receiptV4
419
+ ? {
420
+ controllerWorkspaceRoot: controllerRoot,
421
+ foreignWorkspace,
422
+ }
423
+ : {}),
344
424
  ...(stdinArtifact === null
345
425
  ? {}
346
426
  : { inputArtifacts: { stdin: stdinArtifact } }),
@@ -382,7 +462,7 @@ export async function runQuickVerification({
382
462
  if (expectedRuntimeReportPath !== null) {
383
463
  try {
384
464
  runtimeTransition = captureRuntimeTransition({
385
- workspaceRoot: root,
465
+ workspaceRoot: controllerRoot,
386
466
  quickReceiptPath: persisted.receiptPath,
387
467
  runtimeReportPath: expectedRuntimeReportPath,
388
468
  });
@@ -398,7 +478,7 @@ export async function runQuickVerification({
398
478
  const runtimeFields = runtimeTransition === null ? {} : { runtimeTransition };
399
479
 
400
480
  if (sourceMutated) {
401
- const attestCommand = quickAttestCommand(root, persisted.receiptPath);
481
+ const attestCommand = quickAttestCommand(controllerRoot, persisted.receiptPath);
402
482
  return {
403
483
  status: "quick_verification_source_mutated",
404
484
  exitCode: 2,
@@ -408,6 +488,7 @@ export async function runQuickVerification({
408
488
  commandExitCode: execution.exitCode,
409
489
  mutationDecision: mutation.mutationDecision,
410
490
  issueCodes: mutation.issueCodes,
491
+ ...(foreignMode ? { targetZeroWrite: foreignWorkspace.zeroWriteObserved } : {}),
411
492
  ...inputArtifactResult(stdinArtifact),
412
493
  outputSummary,
413
494
  evidenceStorage,
@@ -419,7 +500,7 @@ export async function runQuickVerification({
419
500
  };
420
501
  }
421
502
  if (runtimeTransition?.status === "runtime_transition_invalid") {
422
- const attestCommand = quickAttestCommand(root, persisted.receiptPath);
503
+ const attestCommand = quickAttestCommand(controllerRoot, persisted.receiptPath);
423
504
  return {
424
505
  status: "quick_verification_runtime_truth_invalid",
425
506
  exitCode: 1,
@@ -432,6 +513,7 @@ export async function runQuickVerification({
432
513
  commandExitCode: execution.exitCode,
433
514
  mutationDecision: "source_unchanged",
434
515
  issueCodes: mutation.issueCodes,
516
+ ...(foreignMode ? { targetZeroWrite: foreignWorkspace.zeroWriteObserved } : {}),
435
517
  ...inputArtifactResult(stdinArtifact),
436
518
  outputSummary,
437
519
  evidenceStorage,
@@ -443,7 +525,7 @@ export async function runQuickVerification({
443
525
  };
444
526
  }
445
527
  if (execution.launchError || execution.exitCode !== 0) {
446
- const attestCommand = quickAttestCommand(root, persisted.receiptPath);
528
+ const attestCommand = quickAttestCommand(controllerRoot, persisted.receiptPath);
447
529
  return {
448
530
  status: "quick_verification_failed",
449
531
  exitCode: 1,
@@ -453,6 +535,7 @@ export async function runQuickVerification({
453
535
  commandExitCode: execution.exitCode,
454
536
  mutationDecision: "source_unchanged",
455
537
  issueCodes: mutation.issueCodes,
538
+ ...(foreignMode ? { targetZeroWrite: foreignWorkspace.zeroWriteObserved } : {}),
456
539
  ...inputArtifactResult(stdinArtifact),
457
540
  outputSummary,
458
541
  evidenceStorage,
@@ -463,7 +546,7 @@ export async function runQuickVerification({
463
546
  authorizationGranted: false,
464
547
  };
465
548
  }
466
- const attestCommand = quickAttestCommand(root, persisted.receiptPath);
549
+ const attestCommand = quickAttestCommand(controllerRoot, persisted.receiptPath);
467
550
  return {
468
551
  status: "quick_verification_passed",
469
552
  exitCode: 0,
@@ -473,6 +556,7 @@ export async function runQuickVerification({
473
556
  commandExitCode: 0,
474
557
  mutationDecision: "source_unchanged",
475
558
  issueCodes: mutation.issueCodes,
559
+ ...(foreignMode ? { targetZeroWrite: foreignWorkspace.zeroWriteObserved } : {}),
476
560
  ...inputArtifactResult(stdinArtifact),
477
561
  outputSummary,
478
562
  evidenceStorage,
@@ -61,12 +61,15 @@ function hasExactFields(value, fields) {
61
61
  }
62
62
 
63
63
  function decision(policy, issueCodes) {
64
+ const version = policy.schemaVersion === "OwlCodaRunKitRegistryReleasePolicyV2"
65
+ ? policy.artifactVersion
66
+ : policy.candidateVersion;
64
67
  return {
65
68
  schemaVersion: "OwlCodaRunKitRegistryAdoptionDecisionV1",
66
69
  decision: issueCodes.length === 0 ? "ELIGIBLE" : "INELIGIBLE",
67
70
  issueCodes,
68
71
  packageName: policy.packageName,
69
- version: policy.candidateVersion,
72
+ version,
70
73
  authorizationGranted: false,
71
74
  };
72
75
  }
@@ -157,6 +160,9 @@ export function validateStoredRegistryAdoptionEvidence({
157
160
  }
158
161
 
159
162
  function validReleaseEvidence(policy, release) {
163
+ const version = policy.schemaVersion === "OwlCodaRunKitRegistryReleasePolicyV2"
164
+ ? policy.artifactVersion
165
+ : policy.candidateVersion;
160
166
  if (
161
167
  policy.registry !== OFFICIAL_NPM_REGISTRY
162
168
  || Object.keys(release).sort().join("\n") !== RELEASE_EVIDENCE_FIELDS.join("\n")
@@ -164,7 +170,7 @@ function validReleaseEvidence(policy, release) {
164
170
  || !isCanonicalSha512Integrity(release.integrity)
165
171
  || release.tarballUrl !== expectedRegistryTarballUrl(
166
172
  policy.packageName,
167
- policy.candidateVersion,
173
+ version,
168
174
  )
169
175
  ) {
170
176
  return false;
@@ -198,13 +204,16 @@ export function evaluateRegistryAdoption({ policy, releaseEvidence, install }) {
198
204
  }
199
205
 
200
206
  const release = releaseEvidence;
207
+ const version = policy.schemaVersion === "OwlCodaRunKitRegistryReleasePolicyV2"
208
+ ? policy.artifactVersion
209
+ : policy.candidateVersion;
201
210
  if (!validReleaseEvidence(policy, release)) {
202
211
  return decision(policy, ["registry_release_evidence_invalid"]);
203
212
  }
204
213
  if (
205
214
  release.registry !== policy.registry
206
215
  || release.packageName !== policy.packageName
207
- || release.version !== policy.candidateVersion
216
+ || release.version !== version
208
217
  ) {
209
218
  return decision(policy, ["registry_release_evidence_mismatch"]);
210
219
  }
@@ -23,10 +23,10 @@ const CANDIDATE_VERSION = CORE_VERSION;
23
23
  const EXACT_SPEC = `${PACKAGE_NAME}@${CANDIDATE_VERSION}`;
24
24
 
25
25
  const POLICY = Object.freeze({
26
- schemaVersion: "OwlCodaRunKitRegistryReleasePolicyV1",
27
- status: "registry_verified",
26
+ schemaVersion: "OwlCodaRunKitRegistryReleasePolicyV2",
27
+ status: "release_artifact_registry_gated",
28
28
  packageName: PACKAGE_NAME,
29
- candidateVersion: CANDIDATE_VERSION,
29
+ artifactVersion: CANDIDATE_VERSION,
30
30
  registry: OFFICIAL_NPM_REGISTRY,
31
31
  registryRequiredBeforeAdoption: true,
32
32
  exactVersionRequired: true,
@@ -45,8 +45,13 @@ const POLICY = Object.freeze({
45
45
  "symlink",
46
46
  "workspace",
47
47
  ],
48
- currentPublishedRelease: null,
49
- registryRelease: null,
48
+ rollbackRelease: {
49
+ packageName: PACKAGE_NAME,
50
+ version: "0.20.0",
51
+ shasum: "b123f5ea33a4f500961a0df3226ed0ca409d111d",
52
+ integrity: "sha512-VcXUh+q8DwR6RoO9JDoUMEzFrpFRBpSvpqgHvIY15Z+Oo+A31Ij2kFO5wCFki80nd6Tl/YT70t7UV2aG0TZ0GQ==",
53
+ tarballUrl: "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.20.0.tgz",
54
+ },
50
55
  registryEvidenceMustBeExternal: true,
51
56
  registryEvidenceSchemaVersion: "OwlCodaRunKitRegistryReleaseEvidenceV1",
52
57
  embeddedAttestComponentVersion: "0.2.0",