owlrunkit 0.20.0 → 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 (44) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +63 -10
  3. package/docs/architecture/OWLCODA_RUNKIT_0201_RELEASE_TRUTH_AND_PROFILE_NORMALIZATION_PATCH_DECISION_20260813.md +95 -0
  4. package/docs/architecture/OWLCODA_RUNKIT_021_TEAM_DELIVERY_PHASE2_PRODUCT_DECISION_20260813.md +249 -0
  5. package/integrations/codex/skills/owlcoda-runkit/SKILL.md +92 -12
  6. package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
  7. package/package.json +1 -1
  8. package/packages/attest/README.md +7 -1
  9. package/packages/attest/consumer-surface-v3.json +275 -0
  10. package/packages/attest/distribution-manifest-v1.json +14 -0
  11. package/packages/attest/schemas/canonical-candidate-identity-v1.schema.json +112 -0
  12. package/packages/attest/schemas/foreign-workspace-filesystem-identity-v1.schema.json +32 -0
  13. package/packages/attest/schemas/quick-verification-receipt-v4.schema.json +226 -0
  14. package/packages/attest/src/candidate-identity.mjs +454 -0
  15. package/packages/attest/src/index.mjs +11 -0
  16. package/packages/attest/src/quick-receipt-contract.mjs +126 -5
  17. package/packages/attest/src/quick.mjs +43 -8
  18. package/{registry-release-policy-v1.json → registry-release-policy-v2.json} +8 -9
  19. package/sbom.spdx.json +4 -4
  20. package/scripts/runkit-contract/core-contract.mjs +14 -1
  21. package/scripts/runkit-contract/fleet-skill-plan.mjs +223 -0
  22. package/scripts/runkit-contract/formal-workflow.mjs +3 -1
  23. package/scripts/runkit-contract/inspect-presentation.mjs +56 -0
  24. package/scripts/runkit-contract/install-codex-skill.mjs +1 -0
  25. package/scripts/runkit-contract/onboarding-bootstrap.mjs +15 -0
  26. package/scripts/runkit-contract/onboarding-doctor.mjs +201 -7
  27. package/scripts/runkit-contract/profile-onboarding.mjs +6 -0
  28. package/scripts/runkit-contract/project-cli-resolver.mjs +9 -1
  29. package/scripts/runkit-contract/quick-metrics.mjs +9 -5
  30. package/scripts/runkit-contract/quick-verify.mjs +108 -24
  31. package/scripts/runkit-contract/registry-adoption-gate.mjs +12 -3
  32. package/scripts/runkit-contract/registry-adoption.mjs +10 -5
  33. package/scripts/runkit-contract/runkit-cli.mjs +289 -19
  34. package/scripts/runkit-contract/schemas/delivery-lifecycle-v1.schema.json +91 -0
  35. package/scripts/runkit-contract/schemas/external-gate-v1.schema.json +68 -0
  36. package/scripts/runkit-contract/schemas/fleet-skill-upgrade-plan-v1.schema.json +140 -0
  37. package/scripts/runkit-contract/schemas/inspect-compact-v1.schema.json +93 -0
  38. package/scripts/runkit-contract/schemas/team-delivery-status-v2.schema.json +219 -0
  39. package/scripts/runkit-contract/schemas/team-project-event-v6.schema.json +82 -0
  40. package/scripts/runkit-contract/schemas/team-project-event-v7.schema.json +80 -0
  41. package/scripts/runkit-contract/schemas/team-project-status-v4.schema.json +208 -0
  42. package/scripts/runkit-contract/sync-release-identity.mjs +87 -0
  43. package/scripts/runkit-contract/team-delivery.mjs +128 -3
  44. package/scripts/runkit-contract/team-project.mjs +970 -9
@@ -176,6 +176,62 @@ export function buildInspectSummary(
176
176
  };
177
177
  }
178
178
 
179
+ function compactWarningSummary(projectDriver) {
180
+ const byCode = new Map();
181
+ for (const warning of projectDriver?.warnings ?? []) {
182
+ const current = byCode.get(warning.code) ?? {
183
+ code: warning.code,
184
+ count: 0,
185
+ blocking: warning.blocking === true,
186
+ };
187
+ current.count += 1;
188
+ current.blocking ||= warning.blocking === true;
189
+ byCode.set(warning.code, current);
190
+ }
191
+ return {
192
+ count: [...byCode.values()].reduce((total, group) => total + group.count, 0),
193
+ groups: [...byCode.values()].sort((left, right) => compareCodeUnits(left.code, right.code)),
194
+ };
195
+ }
196
+
197
+ export function buildInspectCompactV1(inspected) {
198
+ const projectDriver = inspected.projectDriver ?? null;
199
+ const summary = inspected.summary;
200
+ return {
201
+ schemaVersion: "OwlCodaRunKitInspectCompactV1",
202
+ status: "inspected_compact",
203
+ exitCode: inspected.exitCode,
204
+ projectTruthHash: projectDriver?.projectTruthHash ?? null,
205
+ progress: {
206
+ overall: projectDriver?.overall ?? null,
207
+ completedWorkItems: projectDriver?.counts?.completed ?? null,
208
+ totalWorkItems: projectDriver?.counts?.total ?? null,
209
+ },
210
+ dominantGap: {
211
+ code: summary.dominantGap.code,
212
+ reason: summary.dominantGap.reasons[0] ?? null,
213
+ },
214
+ ownerAgentId: projectDriver?.nextActorId
215
+ ?? projectDriver?.dominantGap?.agentId
216
+ ?? null,
217
+ nextAction: summary.recommendedNextAction ?? summary.nextAllowedAction,
218
+ warningSummary: compactWarningSummary(projectDriver),
219
+ execution: {
220
+ state: summary.currentExecution.state,
221
+ openCount: summary.currentExecution.openCount,
222
+ activeLeaseCount: summary.leases.activeCount,
223
+ },
224
+ authorityBoundary: {
225
+ git: false,
226
+ release: false,
227
+ deployment: false,
228
+ production: false,
229
+ business: false,
230
+ },
231
+ authorizationGranted: false,
232
+ };
233
+ }
234
+
179
235
  function value(value) {
180
236
  if (value === null || value === undefined || value === "") return "none";
181
237
  return String(value);
@@ -786,6 +786,7 @@ export async function runCli(argv = process.argv.slice(2)) {
786
786
  workspaceRoots: options.workspace ?? null,
787
787
  fleetManifestPath: options["fleet-manifest"] ?? null,
788
788
  fleetRoots: options["fleet-root"] ?? null,
789
+ fleetRegistryPath: options.registry ?? null,
789
790
  });
790
791
  return { exitCode: 0, ...output };
791
792
  }
@@ -46,6 +46,16 @@ const TRANSACTION_PATHS = Object.freeze([
46
46
  "adoption",
47
47
  ]);
48
48
 
49
+ function shellQuote(value) {
50
+ return `'${String(value).replaceAll("'", `'"'"'`)}'`;
51
+ }
52
+
53
+ function requestedSuccessorSpec(value) {
54
+ return /^owlrunkit@[0-9]+\.[0-9]+\.[0-9]+$/u.test(value ?? "")
55
+ ? value
56
+ : null;
57
+ }
58
+
49
59
  function pathEntryExists(filePath) {
50
60
  try {
51
61
  lstatSync(filePath);
@@ -301,6 +311,7 @@ export async function planOnboardingBootstrapV1({
301
311
  } = {}) {
302
312
  const root = resolveBoundedWorkspaceRoot(workspaceRoot);
303
313
  const issueCodes = [];
314
+ const requestedSuccessor = requestedSuccessorSpec(exactSpec);
304
315
  if (exactSpec !== EXACT_SPEC) issueCodes.push("bootstrap_exact_spec_required");
305
316
  const local = readLocalInstallBinding({ workspaceRoot: root });
306
317
  if (local.status !== "bound") {
@@ -385,6 +396,8 @@ export async function planOnboardingBootstrapV1({
385
396
  issueCodes: [...new Set(issueCodes)].sort(),
386
397
  nextCommand: issueCodes.length === 0
387
398
  ? `npx --no-install owlrunkit bootstrap --workspace ${JSON.stringify(root)} --exact ${EXACT_SPEC} --apply`
399
+ : issueCodes.includes("bootstrap_exact_spec_required") && requestedSuccessor !== null
400
+ ? `cd ${shellQuote(root)} && npm install --save-exact ${requestedSuccessor} && npx --no-install owlrunkit bootstrap --workspace ${shellQuote(root)} --exact ${requestedSuccessor} --dry-run`
388
401
  : "repair bootstrap preflight issues and rerun --dry-run",
389
402
  authorizationGranted: false,
390
403
  };
@@ -573,6 +586,7 @@ export async function applyOnboardingBootstrapV1({
573
586
  "doctor",
574
587
  () => runDoctor({
575
588
  workspaceRoot: root,
589
+ discoverSharedSkill: true,
576
590
  registryUrl: OFFICIAL_NPM_REGISTRY,
577
591
  registryClient,
578
592
  }),
@@ -677,6 +691,7 @@ export function formatOnboardingBootstrapHumanV1(result) {
677
691
  const issues = result.issueCodes?.join(", ") || "unknown issue";
678
692
  return [
679
693
  `Bootstrap blocked: ${issues}`,
694
+ ...(result.nextCommand ? [`Repair: ${result.nextCommand}`] : []),
680
695
  `Rollback: ${result.rollbackOutcome ?? "not_required"}.`,
681
696
  ...(result.receiptPath ? [`Receipt: ${result.receiptPath}`] : []),
682
697
  "No Git, release, deployment, production, or business authority was granted.",
@@ -1,12 +1,15 @@
1
1
  import {
2
2
  closeSync,
3
+ existsSync,
3
4
  fstatSync,
4
5
  lstatSync,
5
6
  openSync,
6
7
  readSync,
7
8
  realpathSync,
8
9
  } from "node:fs";
10
+ import { homedir } from "node:os";
9
11
  import path from "node:path";
12
+ import { userInfo } from "node:os";
10
13
 
11
14
  import {
12
15
  CORE_VERSION,
@@ -20,12 +23,19 @@ import {
20
23
  } from "./registry-adoption-gate.mjs";
21
24
  import { resolveProjectCli } from "./project-cli-resolver.mjs";
22
25
  import { inspectProjectControlState } from "./project-control-state.mjs";
26
+ import {
27
+ inspectFleetForSkillChange,
28
+ inspectInstalledSkillCompatibility,
29
+ } from "./install-codex-skill.mjs";
23
30
 
24
31
  const PACKAGE_NAME = "owlrunkit";
25
32
  const EXPECTED_VERSION = CORE_VERSION;
26
33
  const MAX_JSON_BYTES = 1_048_576;
27
34
  const MAX_JSON_DEPTH = 64;
28
35
  const MAX_JSON_NODES = 50_000;
36
+ const PACKAGE_SKILL_RELATIVE = path.join(
37
+ "integrations", "codex", "skills", "owlcoda-runkit",
38
+ );
29
39
 
30
40
  class StrictJsonParser {
31
41
  constructor(text, { maxDepth = MAX_JSON_DEPTH, maxNodes = MAX_JSON_NODES } = {}) {
@@ -286,6 +296,81 @@ export function readWorkspaceJsonBounded(root, relativePath, options) {
286
296
  return text === null ? null : parseJsonStrict(text, options);
287
297
  }
288
298
 
299
+ function parsedSkillRoot(requestedRoot) {
300
+ if (typeof requestedRoot !== "string" || requestedRoot.length === 0) return null;
301
+ try {
302
+ const root = realpathSync(requestedRoot);
303
+ const skillText = readWorkspaceFileBounded(root, "SKILL.md");
304
+ const config = readWorkspaceJsonBounded(
305
+ root,
306
+ path.join("assets", "templates", "config.json"),
307
+ );
308
+ const version = skillText.match(/standalone `owlrunkit@(\d+\.\d+\.\d+)`/u)?.[1] ?? null;
309
+ const configCoreVersion = config?.core?.coreVersion ?? null;
310
+ return {
311
+ root,
312
+ skillPath: path.join(root, "SKILL.md"),
313
+ version,
314
+ configCoreVersion,
315
+ status: version !== null && configCoreVersion === version ? "readable" : "invalid",
316
+ };
317
+ } catch {
318
+ return {
319
+ root: path.resolve(requestedRoot),
320
+ skillPath: path.resolve(requestedRoot, "SKILL.md"),
321
+ version: null,
322
+ configCoreVersion: null,
323
+ status: "invalid",
324
+ };
325
+ }
326
+ }
327
+
328
+ function discoveredSharedSkillRoot() {
329
+ const requested = process.env.OWLCODA_RUNKIT_PROCESS_SKILL_ROOT;
330
+ if (typeof requested === "string" && requested.length > 0) return requested;
331
+ const candidates = [
332
+ path.join(homedir(), ".agents", "skills", "owlcoda-runkit"),
333
+ path.join(homedir(), ".codex", "skills", "owlcoda-runkit"),
334
+ ];
335
+ return candidates.find(candidate => existsSync(candidate)) ?? null;
336
+ }
337
+
338
+ export function inspectDoctorSkillStateV1({ packageRoot, sharedSkillRoot } = {}) {
339
+ const packageSkill = typeof packageRoot === "string"
340
+ ? parsedSkillRoot(path.join(packageRoot, PACKAGE_SKILL_RELATIVE))
341
+ : null;
342
+ const selectedSharedRoot = sharedSkillRoot ?? discoveredSharedSkillRoot();
343
+ const sharedSkill = selectedSharedRoot === null ? null : parsedSkillRoot(selectedSharedRoot);
344
+ const warningCodes = [];
345
+ if (sharedSkill === null) warningCodes.push("shared_skill_not_found");
346
+ else if (sharedSkill.status !== "readable") warningCodes.push("shared_skill_invalid");
347
+ else if (packageSkill?.status === "readable" && sharedSkill.version !== packageSkill.version) {
348
+ warningCodes.push("shared_skill_version_drift");
349
+ }
350
+ const packageIssueCodes = [];
351
+ if (packageSkill?.status !== "readable") warningCodes.push("package_skill_unavailable");
352
+ const skillDrift = warningCodes.includes("shared_skill_version_drift");
353
+ return {
354
+ schemaVersion: "OwlCodaRunKitDoctorSkillStateV1",
355
+ packageVersion: packageSkill?.version ?? null,
356
+ configCoreVersion: packageSkill?.configCoreVersion ?? null,
357
+ packageSkillVersion: packageSkill?.version ?? null,
358
+ packageSkillPath: packageSkill?.skillPath ?? null,
359
+ processSkillVersion: sharedSkill?.version ?? null,
360
+ processSkillPath: sharedSkill?.skillPath ?? null,
361
+ loadedSkillVersion: "unknown",
362
+ skillDrift,
363
+ blocking: false,
364
+ issueCodes: packageIssueCodes,
365
+ warningCodes,
366
+ recommendedAction: skillDrift
367
+ ? `Start a new Agent session and load the package-bound Skill at ${packageSkill.skillPath}.`
368
+ : warningCodes.length > 0
369
+ ? `Use the package-bound Skill at ${packageSkill?.skillPath ?? "the installed package"}.`
370
+ : "none",
371
+ };
372
+ }
373
+
289
374
  async function readResponseJsonBounded(response) {
290
375
  if (!response.body) throw new Error("registry response body is missing");
291
376
  const reader = response.body.getReader();
@@ -577,7 +662,13 @@ function configCheck(root) {
577
662
  );
578
663
  if (config === null) return { status: "missing" };
579
664
  const gate = validateProjectConfigV2(config);
580
- return gate.valid ? { status: "ok" } : { status: "invalid", issues: gate.issues };
665
+ return gate.valid
666
+ ? {
667
+ status: "ok",
668
+ coreVersion: config.core.coreVersion,
669
+ coreManifestSha256: config.core.coreManifestSha256,
670
+ }
671
+ : { status: "invalid", issues: gate.issues };
581
672
  } catch {
582
673
  return { status: "invalid" };
583
674
  }
@@ -689,11 +780,17 @@ export function readAdoptionReadiness({ workspaceRoot } = {}) {
689
780
  return adoptionCheck(root);
690
781
  }
691
782
 
692
- export async function runDoctor({
783
+ async function buildDoctorReport({
693
784
  workspaceRoot,
785
+ sharedSkillRoot,
786
+ discoverSharedSkill = false,
694
787
  registryUrl,
695
788
  timeoutMs = 1_500,
696
789
  registryClient,
790
+ includeSharedSkill = false,
791
+ skillRoot = path.join(userInfo().homedir, ".agents", "skills", "owlcoda-runkit"),
792
+ skillInspector = inspectInstalledSkillCompatibility,
793
+ skillFleetInspector = inspectFleetForSkillChange,
697
794
  } = {}) {
698
795
  const root = resolveBoundedWorkspaceRoot(workspaceRoot);
699
796
  const core = currentCoreIdentity();
@@ -715,6 +812,72 @@ export async function runDoctor({
715
812
  const profiles = await doctorProfilesCheck(root);
716
813
  const adoption = adoptionCheck(root, installed);
717
814
  const runtimeIsolation = runtimeIsolationCheck(root);
815
+ const skill = includeSharedSkill && (sharedSkillRoot !== undefined || discoverSharedSkill)
816
+ ? {
817
+ ...inspectDoctorSkillStateV1({
818
+ packageRoot: installed.status === "bound" ? installed.packageRoot : undefined,
819
+ sharedSkillRoot,
820
+ }),
821
+ packageVersion: installed.status === "bound" ? installed.version : null,
822
+ configCoreVersion: config.status === "ok" ? config.coreVersion : null,
823
+ }
824
+ : includeSharedSkill
825
+ ? {
826
+ schemaVersion: "OwlCodaRunKitDoctorSkillStateV1",
827
+ packageVersion: installed.status === "bound" ? installed.version : null,
828
+ configCoreVersion: core.coreVersion,
829
+ packageSkillVersion: null,
830
+ packageSkillPath: null,
831
+ processSkillVersion: null,
832
+ processSkillPath: null,
833
+ loadedSkillVersion: "unknown",
834
+ skillDrift: false,
835
+ blocking: false,
836
+ issueCodes: [],
837
+ warningCodes: [],
838
+ recommendedAction: "none",
839
+ }
840
+ : null;
841
+ let sharedSkill = null;
842
+ if (includeSharedSkill) {
843
+ let inspectedSkill;
844
+ try {
845
+ inspectedSkill = await skillInspector({ targetRoot: skillRoot });
846
+ } catch (error) {
847
+ inspectedSkill = {
848
+ status: "invalid",
849
+ installedSkillVersion: null,
850
+ issues: [error instanceof Error ? error.message : String(error)],
851
+ };
852
+ }
853
+ let activationBlockedReason = null;
854
+ if (inspectedSkill.status !== "valid") {
855
+ try {
856
+ await skillFleetInspector();
857
+ } catch (error) {
858
+ activationBlockedReason = error instanceof Error ? error.message : String(error);
859
+ }
860
+ }
861
+ const sharedSkillDrift = inspectedSkill.status === "version_mismatch";
862
+ sharedSkill = {
863
+ status: inspectedSkill.status === "valid"
864
+ ? "valid"
865
+ : sharedSkillDrift
866
+ ? "expected_nonblocking_drift"
867
+ : "unavailable_nonblocking",
868
+ blocking: false,
869
+ projectCliVersion: installed.version ?? null,
870
+ projectCoreVersion: core.coreVersion,
871
+ sharedSkillVersion: inspectedSkill.installedSkillVersion ?? null,
872
+ issueCodes: [...new Set(inspectedSkill.issues ?? [])].sort(),
873
+ activationBlockedReason,
874
+ recommendedAction: inspectedSkill.status === "valid"
875
+ ? null
876
+ : activationBlockedReason?.startsWith("skill_update_blocked_active_execution:")
877
+ ? "wait_for_active_execution_or_lease_then_refresh_shared_skill"
878
+ : "run_fleet_safe_shared_skill_refresh",
879
+ };
880
+ }
718
881
  const checks = {
719
882
  node: {
720
883
  status: nodeOk ? "ok" : "unsupported",
@@ -744,6 +907,8 @@ export async function runDoctor({
744
907
  detected: readProjectScripts(root),
745
908
  },
746
909
  runtimeIsolation,
910
+ ...(skill === null ? {} : { skill }),
911
+ ...(sharedSkill === null ? {} : { sharedSkill }),
747
912
  lifecycle: {
748
913
  status: lifecycle.status,
749
914
  activeRunIds: lifecycle.activeRunIds,
@@ -762,6 +927,7 @@ export async function runDoctor({
762
927
  if (installed.status !== "bound") {
763
928
  issueCodes.push(...(installed.issueCodes ?? ["registry_install_binding_mismatch"]));
764
929
  }
930
+ issueCodes.push(...(skill?.issueCodes ?? []));
765
931
  if (
766
932
  registry.status === "registry_verified"
767
933
  && (
@@ -779,11 +945,14 @@ export async function runDoctor({
779
945
  ...runtimeIsolation.affectedScripts
780
946
  .map(({ script, tool }) => `runkit_runtime_collection_risk:${tool}:${script}`),
781
947
  ...(profileLauncherWarningCount > 0 ? ["profile_launcher_warnings"] : []),
948
+ ...(skill?.warningCodes ?? []),
782
949
  ].sort();
783
950
  const lifecycleNextAction = lifecycle.recovery.nextAllowedAction;
784
- const maintenanceNextAction = profileLauncherWarningCount > 0
785
- ? "review_profile_launchers"
786
- : null;
951
+ const maintenanceNextAction = skill?.skillDrift
952
+ ? "reload_package_bound_skill"
953
+ : profileLauncherWarningCount > 0
954
+ ? "review_profile_launchers"
955
+ : null;
787
956
  const nextAllowedAction = uniqueIssues.length === 0
788
957
  ? lifecycleNextAction !== "plan_new_execution"
789
958
  ? lifecycleNextAction
@@ -802,7 +971,9 @@ export async function runDoctor({
802
971
  ? "repair_execution_artifacts"
803
972
  : "repair_registry_binding";
804
973
  return {
805
- schemaVersion: "OwlCodaRunKitDoctorReportV1",
974
+ schemaVersion: includeSharedSkill
975
+ ? "OwlCodaRunKitDoctorReportV2"
976
+ : "OwlCodaRunKitDoctorReportV1",
806
977
  status: uniqueIssues.length === 0 ? "ready" : "issues_found",
807
978
  checks,
808
979
  issueCodes: uniqueIssues,
@@ -810,7 +981,9 @@ export async function runDoctor({
810
981
  profileLauncherWarnings: {
811
982
  count: profileLauncherWarningCount,
812
983
  blocking: false,
813
- recommendedAction: maintenanceNextAction,
984
+ recommendedAction: profileLauncherWarningCount > 0
985
+ ? "review_profile_launchers"
986
+ : null,
814
987
  },
815
988
  lifecycleNextAction,
816
989
  maintenanceNextAction,
@@ -819,6 +992,14 @@ export async function runDoctor({
819
992
  };
820
993
  }
821
994
 
995
+ export async function runDoctor(options = {}) {
996
+ return buildDoctorReport({ ...options, includeSharedSkill: false });
997
+ }
998
+
999
+ export async function runDoctorV2(options = {}) {
1000
+ return buildDoctorReport({ ...options, includeSharedSkill: true });
1001
+ }
1002
+
822
1003
  export function formatDoctorHuman(report) {
823
1004
  const otherWarnings = report.warningCodes.filter(
824
1005
  (code) => code !== "profile_launcher_warnings",
@@ -828,9 +1009,22 @@ export function formatDoctorHuman(report) {
828
1009
  `Node: ${report.checks.node.status} (${report.checks.node.version})`,
829
1010
  `Core: ${report.checks.core.status} (${report.checks.core.version})`,
830
1011
  `Installed package: ${report.checks.installedPackage.status}`,
1012
+ ...(report.checks.skill
1013
+ ? [
1014
+ `Skill: package ${report.checks.skill.packageSkillVersion ?? "unknown"}; process ${report.checks.skill.processSkillVersion ?? "unknown"}${report.checks.skill.skillDrift ? " (drift, non-blocking)" : ""}; loaded ${report.checks.skill.loadedSkillVersion}.`,
1015
+ `Package-bound Skill: ${report.checks.skill.packageSkillPath ?? "unavailable"}`,
1016
+ `Process-wide Skill: ${report.checks.skill.processSkillPath ?? "unavailable"}`,
1017
+ ]
1018
+ : []),
831
1019
  `Registry: ${report.checks.registry.status}`,
832
1020
  `Profiles: ${report.checks.profiles.status}`,
833
1021
  `Adoption: ${report.checks.adoption.status}`,
1022
+ ...(report.checks.sharedSkill
1023
+ ? [`Shared Skill: ${report.checks.sharedSkill.status} (${report.checks.sharedSkill.sharedSkillVersion ?? "missing"}; project Core ${report.checks.sharedSkill.projectCoreVersion})`]
1024
+ : []),
1025
+ ...(report.checks.sharedSkill?.activationBlockedReason
1026
+ ? [`Shared Skill activation: ${report.checks.sharedSkill.activationBlockedReason}`]
1027
+ : []),
834
1028
  `Lifecycle: ${report.checks.lifecycle.status}`,
835
1029
  ...(report.issueCodes.length > 0 ? [`Issues: ${report.issueCodes.join(", ")}`] : []),
836
1030
  ...(report.profileLauncherWarnings.count > 0
@@ -1826,6 +1826,12 @@ export function reconcileProfilesV2({
1826
1826
  authorizationGranted: false,
1827
1827
  };
1828
1828
  if (!apply || proposedProfilesSha256 === currentProfilesSha256) return plan;
1829
+ if (normalizationOnly) {
1830
+ return {
1831
+ ...plan,
1832
+ status: "profiles_already_current",
1833
+ };
1834
+ }
1829
1835
 
1830
1836
  let runtimeRoot;
1831
1837
  let receiptRoot;
@@ -15,10 +15,18 @@ 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", "0.20.0"]);
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
+ ])],
22
30
  ["0.19.2", new Set([
23
31
  "sha256:c46ae9d8ed0572e5636ab5725f922f8db3ee5bf0f966b200bce7b2287c9267c8",
24
32
  ])],
@@ -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");