owlrunkit 0.19.1 → 0.19.2

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 (34) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +71 -0
  3. package/docs/architecture/OWLCODA_RUNKIT_0191_PROFILE_RECONCILE_PATCH_PRODUCT_DECISION_20260813.md +11 -5
  4. package/docs/architecture/OWLCODA_RUNKIT_0192_FORMAL_USABILITY_PRODUCT_DECISION_20260813.md +141 -0
  5. package/integrations/codex/skills/owlcoda-runkit/SKILL.md +50 -7
  6. package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
  7. package/package.json +1 -1
  8. package/packages/attest/distribution-manifest-v1.json +5 -0
  9. package/packages/attest/src/quick.mjs +37 -4
  10. package/registry-release-policy-v1.json +5 -5
  11. package/sbom.spdx.json +5 -5
  12. package/scripts/runkit-contract/core-contract.mjs +35 -2
  13. package/scripts/runkit-contract/efficiency-metrics.mjs +9 -7
  14. package/scripts/runkit-contract/formal-preflight.mjs +352 -0
  15. package/scripts/runkit-contract/formal-supplemental-evidence.mjs +713 -0
  16. package/scripts/runkit-contract/formal-workflow.mjs +529 -59
  17. package/scripts/runkit-contract/onboarding-doctor.mjs +32 -13
  18. package/scripts/runkit-contract/project-cli-resolver.mjs +30 -3
  19. package/scripts/runkit-contract/project-external-delivery.mjs +16 -9
  20. package/scripts/runkit-contract/quick-attest.mjs +24 -0
  21. package/scripts/runkit-contract/runkit-cli.mjs +365 -49
  22. package/scripts/runkit-contract/schemas/formal-check-v2.schema.json +231 -0
  23. package/scripts/runkit-contract/schemas/formal-envelope-preflight-v1.schema.json +212 -0
  24. package/scripts/runkit-contract/schemas/formal-evidence-summary-v1.schema.json +114 -0
  25. package/scripts/runkit-contract/schemas/formal-supplemental-evidence-v1.schema.json +103 -0
  26. package/scripts/runkit-contract/schemas/team-project-event-v5.schema.json +89 -0
  27. package/scripts/runkit-contract/schemas/team-project-status-v3.schema.json +173 -0
  28. package/scripts/runkit-contract/snapshot.mjs +125 -5
  29. package/scripts/runkit-contract/source-candidate.mjs +271 -69
  30. package/scripts/runkit-contract/sync-release-identity.mjs +39 -4
  31. package/scripts/runkit-contract/team-project-successor.mjs +7 -4
  32. package/scripts/runkit-contract/team-project.mjs +440 -28
  33. package/scripts/runkit-contract/usage-mode.mjs +9 -3
  34. package/scripts/runkit-contract/verification-envelope.mjs +425 -37
@@ -28,10 +28,15 @@ import {
28
28
  relativeToWorkspace,
29
29
  resolveExistingArtifact,
30
30
  safeIdentifier,
31
+ safeRelativePath,
31
32
  sha256,
32
33
  writeJsonAtomically,
33
34
  writeJsonExclusiveAtomically,
34
35
  } from "./provenance-common.mjs";
36
+
37
+ export const SOURCE_CANDIDATE_V2_MAX_BYTES = 64 * 1024 * 1024;
38
+ export const SOURCE_CANDIDATE_V2_MAX_NODES = 2_000_000;
39
+ import { readWorkspaceJsonBounded } from "./onboarding-doctor.mjs";
35
40
  import { verifyDeliveryPacket } from "./source-fingerprint.mjs";
36
41
 
37
42
  function canonicalJson(value) {
@@ -51,6 +56,18 @@ function candidateHash(value) {
51
56
  return `sha256:${sha256(canonicalJson(value))}`;
52
57
  }
53
58
 
59
+ function hasExactKeys(value, keys) {
60
+ return Boolean(value)
61
+ && typeof value === "object"
62
+ && !Array.isArray(value)
63
+ && canonicalJson(Object.keys(value).sort()) === canonicalJson([...keys].sort());
64
+ }
65
+
66
+ function validIdentifier(value) {
67
+ return typeof value === "string"
68
+ && /^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(value);
69
+ }
70
+
54
71
  function repositoryActionsFalse() {
55
72
  return {
56
73
  worktreeCreated: false,
@@ -281,15 +298,56 @@ function sourceManifestV2({ workspaceRoot, ownedPaths }) {
281
298
  };
282
299
  }
283
300
 
284
- function freezePayloadV1({
301
+ function sourceCandidateNodeCount(value) {
302
+ let count = 0;
303
+ const pending = [value];
304
+ while (pending.length > 0) {
305
+ const current = pending.pop();
306
+ count += 1;
307
+ if (Array.isArray(current)) {
308
+ pending.push(...current);
309
+ } else if (current !== null && typeof current === "object") {
310
+ pending.push(...Object.values(current));
311
+ }
312
+ }
313
+ return count;
314
+ }
315
+
316
+ export function validateSourceCandidateDocumentBoundsV2(value, {
317
+ maxBytes = SOURCE_CANDIDATE_V2_MAX_BYTES,
318
+ maxNodes = SOURCE_CANDIDATE_V2_MAX_NODES,
319
+ } = {}) {
320
+ const bytes = Buffer.byteLength(`${JSON.stringify(value, null, 2)}\n`, "utf8");
321
+ const nodes = sourceCandidateNodeCount(value);
322
+ return {
323
+ valid: bytes <= maxBytes && nodes <= maxNodes,
324
+ bytes,
325
+ nodes,
326
+ maxBytes,
327
+ maxNodes,
328
+ };
329
+ }
330
+
331
+ function assertSourceCandidateDocumentBoundsV2(value) {
332
+ const result = validateSourceCandidateDocumentBoundsV2(value);
333
+ if (!result.valid) {
334
+ throw new Error(
335
+ "SourceCandidateV2 exceeds its supported serialized size or JSON node limit.",
336
+ );
337
+ }
338
+ return result;
339
+ }
340
+
341
+ function planPayloadV1({
285
342
  workspaceRoot,
286
343
  candidateRoot,
287
344
  candidateId,
288
345
  manifest,
289
346
  }) {
290
347
  const payloadRoot = path.join(candidateRoot, `${candidateId}.payload`);
291
- mkdirSync(payloadRoot);
292
- mkdirSync(path.join(payloadRoot, "sha256"));
348
+ if (existsSync(payloadRoot)) {
349
+ throw new Error(`source candidate payload already exists: ${candidateId}`);
350
+ }
293
351
  const payloadRootRelative = relativeToWorkspace(workspaceRoot, payloadRoot);
294
352
  const entries = [];
295
353
  const blobs = new Map();
@@ -308,13 +366,10 @@ function freezePayloadV1({
308
366
  }
309
367
  const payloadPath = path.join(payloadRoot, "sha256", digest);
310
368
  if (!blobs.has(digest)) {
311
- writeFileSync(payloadPath, bytes, { flag: "wx", mode: 0o600 });
312
- blobs.set(digest, bytes.length);
313
- } else if (
314
- lstatSync(payloadPath).isSymbolicLink()
315
- || sha256(readFileSync(payloadPath)) !== digest
316
- ) {
317
- throw new Error(`Source payload blob drifted during freeze: ${digest}`);
369
+ blobs.set(digest, {
370
+ sourcePath,
371
+ sizeBytes: bytes.length,
372
+ });
318
373
  }
319
374
  entries.push({
320
375
  path: manifestEntry.path,
@@ -329,15 +384,46 @@ function freezePayloadV1({
329
384
  storage: "create_only_content_addressed_sha256",
330
385
  root: payloadRootRelative,
331
386
  blobCount: blobs.size,
332
- totalBytes: [...blobs.values()].reduce((total, size) => total + size, 0),
387
+ totalBytes: [...blobs.values()]
388
+ .reduce((total, row) => total + row.sizeBytes, 0),
333
389
  entries,
334
390
  };
335
391
  return {
336
- ...body,
337
- sha256: candidateHash(body),
392
+ payload: {
393
+ ...body,
394
+ sha256: candidateHash(body),
395
+ },
396
+ blobs,
397
+ payloadRoot,
338
398
  };
339
399
  }
340
400
 
401
+ function persistPayloadPlanV1({ candidateRoot, plan }) {
402
+ if (!existsSync(candidateRoot)) mkdirSync(candidateRoot);
403
+ const candidateRootStat = lstatSync(candidateRoot);
404
+ if (
405
+ candidateRootStat.isSymbolicLink()
406
+ || !candidateRootStat.isDirectory()
407
+ || realpathSync(candidateRoot) !== path.resolve(candidateRoot)
408
+ ) {
409
+ throw new Error("Source candidate directory must be a regular directory.");
410
+ }
411
+ mkdirSync(plan.payloadRoot);
412
+ const blobRoot = path.join(plan.payloadRoot, "sha256");
413
+ mkdirSync(blobRoot);
414
+ for (const [digest, row] of plan.blobs) {
415
+ const bytes = readFileSync(row.sourcePath);
416
+ if (bytes.length !== row.sizeBytes || sha256(bytes) !== digest) {
417
+ throw new Error(`Source changed while persisting payload: ${digest}`);
418
+ }
419
+ writeFileSync(path.join(blobRoot, digest), bytes, {
420
+ flag: "wx",
421
+ mode: 0o600,
422
+ });
423
+ }
424
+ return plan.payload;
425
+ }
426
+
341
427
  function payloadContractIssues({
342
428
  workspaceRoot,
343
429
  candidatePath,
@@ -354,7 +440,16 @@ function payloadContractIssues({
354
440
  (entry) => entry.operation !== "deleted",
355
441
  );
356
442
  if (
357
- payload?.schemaVersion !== "OwlCodaRunKitSourcePayloadV1"
443
+ !hasExactKeys(payload, [
444
+ "schemaVersion",
445
+ "storage",
446
+ "root",
447
+ "blobCount",
448
+ "totalBytes",
449
+ "entries",
450
+ "sha256",
451
+ ])
452
+ || payload?.schemaVersion !== "OwlCodaRunKitSourcePayloadV1"
358
453
  || payload.storage !== "create_only_content_addressed_sha256"
359
454
  || payload.root !== expectedRoot
360
455
  || !Number.isInteger(payload.blobCount)
@@ -376,7 +471,14 @@ function payloadContractIssues({
376
471
  for (const entry of payload.entries) {
377
472
  const manifestEntry = expectedByPath.get(entry?.path);
378
473
  if (
379
- !manifestEntry
474
+ !hasExactKeys(entry, [
475
+ "path",
476
+ "payloadPath",
477
+ "sha256",
478
+ "sizeBytes",
479
+ "mode",
480
+ ])
481
+ || !manifestEntry
380
482
  || seenPaths.has(entry.path)
381
483
  || entry.sha256 !== manifestEntry.sha256
382
484
  || !Number.isInteger(entry.sizeBytes)
@@ -438,7 +540,14 @@ function activeLease({ workspaceRoot, executionRoot, workItemId }) {
438
540
 
439
541
  function manifestContractValid(manifest) {
440
542
  if (
441
- manifest?.schemaVersion !== "OwlCodaRunKitDirtySourceManifestV2"
543
+ !hasExactKeys(manifest, [
544
+ "schemaVersion",
545
+ "statusMode",
546
+ "ownedPaths",
547
+ "entries",
548
+ "sha256",
549
+ ])
550
+ || manifest?.schemaVersion !== "OwlCodaRunKitDirtySourceManifestV2"
442
551
  || manifest.statusMode !== "porcelain-v1-z-untracked-all-runkit-excluded"
443
552
  || !Array.isArray(manifest.ownedPaths)
444
553
  || manifest.ownedPaths.length === 0
@@ -450,8 +559,14 @@ function manifestContractValid(manifest) {
450
559
  return false;
451
560
  }
452
561
  for (const entry of manifest.entries) {
562
+ const expectedEntryKeys = entry?.operation === "renamed"
563
+ ? ["operation", "status", "path", "previousPath", "sha256"]
564
+ : entry?.operation === "deleted"
565
+ ? ["operation", "status", "path"]
566
+ : ["operation", "status", "path", "sha256"];
453
567
  if (
454
568
  !entry
569
+ || !hasExactKeys(entry, expectedEntryKeys)
455
570
  || !["added", "deleted", "modified", "renamed"].includes(entry.operation)
456
571
  || typeof entry.status !== "string"
457
572
  || entry.status.length !== 2
@@ -513,18 +628,39 @@ function repositoryActionsAreFalse(actions) {
513
628
  function candidateV2ContractIssues(candidate) {
514
629
  const { candidateSha256, ...body } = candidate ?? {};
515
630
  if (
516
- candidate?.schemaVersion !== "OwlCodaRunKitSourceCandidateV2"
631
+ !hasExactKeys(candidate, [
632
+ "schemaVersion",
633
+ "runId",
634
+ "candidateId",
635
+ "status",
636
+ "sourceMode",
637
+ "baseline",
638
+ "sourceManifest",
639
+ "sourceFingerprint",
640
+ "payload",
641
+ "discovery",
642
+ "repositoryActions",
643
+ "authorizationGranted",
644
+ "candidateSha256",
645
+ ])
646
+ || candidate?.schemaVersion !== "OwlCodaRunKitSourceCandidateV2"
517
647
  || candidate.status !== "frozen"
518
648
  || candidate.sourceMode !== "dirty_worktree_exact_manifest_v2"
519
649
  || candidate.authorizationGranted !== false
520
- || typeof candidateSha256 !== "string"
650
+ || !/^sha256:[a-f0-9]{64}$/u.test(candidateSha256)
521
651
  || candidateSha256 !== candidateHash(body)
522
- || typeof candidate.runId !== "string"
523
- || typeof candidate.candidateId !== "string"
524
- || typeof candidate.baseline?.branch !== "string"
525
- || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u.test(candidate.baseline?.head)
526
- || typeof candidate.discovery?.fromLease !== "string"
527
- || typeof candidate.discovery?.leasePath !== "string"
652
+ || !validIdentifier(candidate.runId)
653
+ || !validIdentifier(candidate.candidateId)
654
+ || !hasExactKeys(candidate.baseline, ["branch", "head"])
655
+ || typeof candidate.baseline.branch !== "string"
656
+ || candidate.baseline.branch.length === 0
657
+ || !/^[a-f0-9]{40}$/u.test(candidate.baseline.head)
658
+ || !hasExactKeys(candidate.sourceFingerprint, ["sha256"])
659
+ || !/^[a-f0-9]{64}$/u.test(candidate.sourceFingerprint.sha256)
660
+ || !hasExactKeys(candidate.discovery, ["fromLease", "leasePath"])
661
+ || !validIdentifier(candidate.discovery.fromLease)
662
+ || typeof candidate.discovery.leasePath !== "string"
663
+ || candidate.discovery.leasePath.length === 0
528
664
  || !repositoryActionsAreFalse(candidate.repositoryActions)
529
665
  || !manifestContractValid(candidate.sourceManifest)
530
666
  || candidate.sourceFingerprint?.sha256
@@ -643,13 +779,14 @@ export function freezeSourceCandidateWithinControlTransactionV2({
643
779
  const { executionRoot, pinGate } = loadActiveExecution(root, runId);
644
780
  if (pinGate.status !== "valid") return { ...pinGate, authorizationGranted: false };
645
781
  const candidateRoot = path.join(executionRoot, "source-candidates");
646
- if (!existsSync(candidateRoot)) mkdirSync(candidateRoot);
647
- const candidateRootStat = lstatSync(candidateRoot);
648
- if (candidateRootStat.isSymbolicLink() || !candidateRootStat.isDirectory()) {
649
- throw new Error("Source candidate directory must be a regular directory.");
650
- }
651
- if (!within(realpathSync(executionRoot), realpathSync(candidateRoot))) {
652
- throw new Error("Source candidate directory escapes its execution root.");
782
+ if (existsSync(candidateRoot)) {
783
+ const candidateRootStat = lstatSync(candidateRoot);
784
+ if (candidateRootStat.isSymbolicLink() || !candidateRootStat.isDirectory()) {
785
+ throw new Error("Source candidate directory must be a regular directory.");
786
+ }
787
+ if (!within(realpathSync(executionRoot), realpathSync(candidateRoot))) {
788
+ throw new Error("Source candidate directory escapes its execution root.");
789
+ }
653
790
  }
654
791
  const candidatePath = path.join(candidateRoot, `${candidateId}.json`);
655
792
  if (existsSync(candidatePath)) {
@@ -665,12 +802,43 @@ export function freezeSourceCandidateWithinControlTransactionV2({
665
802
  workspaceRoot: root,
666
803
  ownedPaths: lease.ownedPaths,
667
804
  });
668
- const payload = freezePayloadV1({
805
+ const payloadPlan = planPayloadV1({
669
806
  workspaceRoot: root,
670
807
  candidateRoot,
671
808
  candidateId,
672
809
  manifest: statusBefore,
673
810
  });
811
+ const buildCandidate = ({ baseline, sourceManifest, payload }) => {
812
+ const body = {
813
+ schemaVersion: "OwlCodaRunKitSourceCandidateV2",
814
+ runId,
815
+ candidateId,
816
+ status: "frozen",
817
+ sourceMode: "dirty_worktree_exact_manifest_v2",
818
+ baseline,
819
+ sourceManifest,
820
+ sourceFingerprint: {
821
+ sha256: sourceManifest.sha256.slice("sha256:".length),
822
+ },
823
+ payload,
824
+ discovery: {
825
+ fromLease: workItemId,
826
+ leasePath: lease.leaseRelativePath,
827
+ },
828
+ repositoryActions: repositoryActionsFalse(),
829
+ authorizationGranted: false,
830
+ };
831
+ return {
832
+ ...body,
833
+ candidateSha256: candidateHash(body),
834
+ };
835
+ };
836
+ assertSourceCandidateDocumentBoundsV2(buildCandidate({
837
+ baseline: baselineBefore,
838
+ sourceManifest: statusBefore,
839
+ payload: payloadPlan.payload,
840
+ }));
841
+ const payload = persistPayloadPlanV1({ candidateRoot, plan: payloadPlan });
674
842
  const statusAfter = sourceManifestV2({
675
843
  workspaceRoot: root,
676
844
  ownedPaths: lease.ownedPaths,
@@ -682,29 +850,12 @@ export function freezeSourceCandidateWithinControlTransactionV2({
682
850
  ) {
683
851
  throw new Error("Owned workspace source changed during candidate freeze.");
684
852
  }
685
- const body = {
686
- schemaVersion: "OwlCodaRunKitSourceCandidateV2",
687
- runId,
688
- candidateId,
689
- status: "frozen",
690
- sourceMode: "dirty_worktree_exact_manifest_v2",
853
+ const candidate = buildCandidate({
691
854
  baseline: baselineAfter,
692
855
  sourceManifest: statusAfter,
693
- sourceFingerprint: {
694
- sha256: statusAfter.sha256.slice("sha256:".length),
695
- },
696
856
  payload,
697
- discovery: {
698
- fromLease: workItemId,
699
- leasePath: lease.leaseRelativePath,
700
- },
701
- repositoryActions: repositoryActionsFalse(),
702
- authorizationGranted: false,
703
- };
704
- const candidate = {
705
- ...body,
706
- candidateSha256: candidateHash(body),
707
- };
857
+ });
858
+ assertSourceCandidateDocumentBoundsV2(candidate);
708
859
  writeJsonExclusiveAtomically(candidatePath, candidate);
709
860
  return {
710
861
  status: "source_candidate_frozen",
@@ -721,6 +872,49 @@ export function freezeSourceCandidateWithinControlTransactionV2({
721
872
  };
722
873
  }
723
874
 
875
+ export function previewSourceCandidateWithinControlTransactionV2({
876
+ workspaceRoot,
877
+ runId,
878
+ workItemId,
879
+ }) {
880
+ const root = realpathSync(workspaceRoot);
881
+ const { executionRoot, pinGate } = loadActiveExecution(root, runId);
882
+ if (pinGate.status !== "valid") {
883
+ return { ...pinGate, authorizationGranted: false };
884
+ }
885
+ const lease = activeLease({
886
+ workspaceRoot: root,
887
+ executionRoot,
888
+ workItemId,
889
+ });
890
+ const baselineBefore = currentBaseline(root);
891
+ const manifestBefore = sourceManifestV2({
892
+ workspaceRoot: root,
893
+ ownedPaths: lease.ownedPaths,
894
+ });
895
+ const manifestAfter = sourceManifestV2({
896
+ workspaceRoot: root,
897
+ ownedPaths: lease.ownedPaths,
898
+ });
899
+ const baselineAfter = currentBaseline(root);
900
+ if (
901
+ manifestBefore.sha256 !== manifestAfter.sha256
902
+ || canonicalJson(baselineBefore) !== canonicalJson(baselineAfter)
903
+ ) {
904
+ throw new Error("Owned workspace source changed during candidate preview.");
905
+ }
906
+ return {
907
+ status: "source_candidate_previewed",
908
+ exitCode: 0,
909
+ runId,
910
+ workItemId,
911
+ baseline: baselineAfter,
912
+ sourceManifest: manifestAfter,
913
+ sourceFingerprint: manifestAfter.sha256.slice("sha256:".length),
914
+ authorizationGranted: false,
915
+ };
916
+ }
917
+
724
918
  export function freezeSourceCandidateV2(options) {
725
919
  const workspaceRoot = realpathSync(options.workspaceRoot);
726
920
  return withControlTransaction(
@@ -732,31 +926,31 @@ export function freezeSourceCandidateV2(options) {
732
926
  );
733
927
  }
734
928
 
735
- export function verifySourceCandidateV2({
929
+ export function verifySourceCandidateDetailsV2({
736
930
  workspaceRoot,
737
931
  candidatePath,
932
+ requireCurrentWorkspace = true,
738
933
  }) {
739
934
  const root = realpathSync(workspaceRoot);
740
935
  const issueCodes = [];
741
936
  let absoluteCandidatePath;
742
937
  let candidate;
743
938
  try {
744
- absoluteCandidatePath = resolveExistingArtifact(
745
- root,
746
- candidatePath,
747
- "candidatePath",
748
- );
749
- const stat = lstatSync(absoluteCandidatePath);
750
- if (stat.isSymbolicLink() || !stat.isFile()) {
751
- throw new Error("candidate is not a regular file");
752
- }
753
- candidate = readJson(absoluteCandidatePath);
939
+ const relativeCandidatePath = safeRelativePath(candidatePath, "candidatePath");
940
+ candidate = readWorkspaceJsonBounded(root, relativeCandidatePath, {
941
+ maxBytes: SOURCE_CANDIDATE_V2_MAX_BYTES,
942
+ maxNodes: SOURCE_CANDIDATE_V2_MAX_NODES,
943
+ });
944
+ absoluteCandidatePath = path.join(root, relativeCandidatePath);
754
945
  } catch {
755
946
  return {
756
- status: "invalid",
757
- exitCode: 2,
758
- issueCodes: ["source_candidate_unreadable"],
759
- authorizationGranted: false,
947
+ gate: {
948
+ status: "invalid",
949
+ exitCode: 2,
950
+ issueCodes: ["source_candidate_unreadable"],
951
+ authorizationGranted: false,
952
+ },
953
+ candidateDocument: null,
760
954
  };
761
955
  }
762
956
  const candidateSha256 = candidate.candidateSha256;
@@ -792,7 +986,7 @@ export function verifySourceCandidateV2({
792
986
  issueCodes.push("source_candidate_lease_unreadable");
793
987
  }
794
988
  }
795
- if (issueCodes.length === 0) {
989
+ if (issueCodes.length === 0 && requireCurrentWorkspace) {
796
990
  try {
797
991
  const currentManifest = sourceManifestV2({
798
992
  workspaceRoot: root,
@@ -812,7 +1006,7 @@ export function verifySourceCandidateV2({
812
1006
  issueCodes.push("source_candidate_baseline_drift");
813
1007
  }
814
1008
  }
815
- return {
1009
+ const gate = {
816
1010
  status: issueCodes.length === 0 ? "valid" : "invalid",
817
1011
  exitCode: issueCodes.length === 0 ? 0 : 2,
818
1012
  candidatePath: relativeToWorkspace(root, absoluteCandidatePath),
@@ -821,6 +1015,14 @@ export function verifySourceCandidateV2({
821
1015
  issueCodes: [...new Set(issueCodes)].sort(),
822
1016
  authorizationGranted: false,
823
1017
  };
1018
+ return {
1019
+ gate,
1020
+ candidateDocument: gate.status === "valid" ? candidate : null,
1021
+ };
1022
+ }
1023
+
1024
+ export function verifySourceCandidateV2(options) {
1025
+ return verifySourceCandidateDetailsV2(options).gate;
824
1026
  }
825
1027
 
826
1028
  class SimulatedMaterializationInterruption extends Error {
@@ -153,14 +153,49 @@ export function verifyReleaseIdentitySurfacesV1(input) {
153
153
  const matches = [...source.matchAll(pattern)];
154
154
  return matches.length === 1 && matches[0][1] === core.coreManifestSha256;
155
155
  };
156
+ const releasePolicy = input.releasePolicy ?? null;
157
+ const candidateUnpublished = releasePolicy?.status === "release_candidate_unpublished";
158
+ if (candidateUnpublished) {
159
+ requireSurface(
160
+ releasePolicy?.packageName === "owlrunkit"
161
+ && releasePolicy?.candidateVersion === core.coreVersion
162
+ && releasePolicy?.registryRelease === null
163
+ && releasePolicy?.currentPublishedRelease?.packageName === "owlrunkit"
164
+ && /^\d+\.\d+\.\d+$/u.test(
165
+ releasePolicy?.currentPublishedRelease?.version ?? "",
166
+ )
167
+ && releasePolicy.currentPublishedRelease.version !== core.coreVersion,
168
+ "release_policy",
169
+ );
170
+ }
156
171
  const skillExact = (markdown) => {
157
172
  if (typeof markdown !== "string") return false;
158
173
  const hashes = markdown.match(/sha256:[a-f0-9]{64}/gu) ?? [];
159
- return hashes.length === 1
174
+ const identityExact = hashes.length === 1
160
175
  && hashes[0] === core.coreManifestSha256
161
- && markdown.includes(`Bundled release: standalone \`owlrunkit@${core.coreVersion}\``)
162
- && markdown.includes(`Core \`${core.coreVersion}\``)
163
- && !/(?:It is not published|current published release remains)/iu.test(markdown);
176
+ && markdown.includes(`Core \`${core.coreVersion}\``);
177
+ if (!identityExact) return false;
178
+ if (!candidateUnpublished) {
179
+ return markdown.includes(
180
+ `Bundled release: standalone \`owlrunkit@${core.coreVersion}\``,
181
+ ) && !/(?:not published|current published release remains)/iu.test(markdown);
182
+ }
183
+ const prior = releasePolicy.currentPublishedRelease.version;
184
+ return markdown.includes(
185
+ `Bundled candidate: standalone \`owlrunkit@${core.coreVersion}\``,
186
+ )
187
+ && /candidate is not published/iu.test(markdown)
188
+ && new RegExp(
189
+ "current published release remains\\s+`owlrunkit@"
190
+ + escaped(prior)
191
+ + "`",
192
+ "iu",
193
+ ).test(markdown)
194
+ && !markdown.includes(
195
+ `Bundled release: standalone \`owlrunkit@${core.coreVersion}\``,
196
+ )
197
+ && !markdown.includes(`--exact owlrunkit@${core.coreVersion}`)
198
+ && !markdown.includes(`npm install --save-exact owlrunkit@${core.coreVersion}`);
164
199
  };
165
200
 
166
201
  requireSurface(packageExact(input.packageManifest), "package_manifest");
@@ -95,11 +95,14 @@ function nonEmptyString(value, label) {
95
95
 
96
96
  function validateTimestamp(value) {
97
97
  if (typeof value !== "string"
98
- || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value)
98
+ || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/u.test(value)
99
99
  || Number.isNaN(Date.parse(value))) {
100
- throw new Error("Team project successor --at must be an ISO-8601 UTC timestamp.");
100
+ throw new Error(
101
+ "Team project successor --at must be an ISO-8601 UTC timestamp "
102
+ + "(YYYY-MM-DDTHH:mm:ssZ or YYYY-MM-DDTHH:mm:ss.sssZ).",
103
+ );
101
104
  }
102
- return value;
105
+ return new Date(value).toISOString();
103
106
  }
104
107
 
105
108
  function assertHash(value, label) {
@@ -981,7 +984,7 @@ export function successorTeamProjectV1({
981
984
  }) {
982
985
  const root = assertWorkspaceRoot(workspaceRoot);
983
986
  safeIdentifier(transitionId, "transitionId");
984
- validateTimestamp(occurredAt);
987
+ occurredAt = validateTimestamp(occurredAt);
985
988
  nonEmptyString(reason, "Team project successor reason");
986
989
  const runkitPath = path.join(root, RUNKIT_ROOT);
987
990
  assertRealDirectory(path.join(root, ".owlcoda"), "OwlCoda control directory");