owlrunkit 0.19.0 → 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 (36) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +81 -3
  3. package/docs/architecture/OWLCODA_RUNKIT_0190_CONSOLIDATED_DAILY_USE_PRODUCT_DECISION_20260812.md +12 -9
  4. package/docs/architecture/OWLCODA_RUNKIT_0191_PROFILE_RECONCILE_PATCH_PRODUCT_DECISION_20260813.md +36 -0
  5. package/docs/architecture/OWLCODA_RUNKIT_0192_FORMAL_USABILITY_PRODUCT_DECISION_20260813.md +141 -0
  6. package/integrations/codex/skills/owlcoda-runkit/SKILL.md +52 -9
  7. package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
  8. package/package.json +1 -1
  9. package/packages/attest/distribution-manifest-v1.json +10 -0
  10. package/packages/attest/src/quick.mjs +42 -4
  11. package/registry-release-policy-v1.json +5 -5
  12. package/sbom.spdx.json +5 -5
  13. package/scripts/runkit-contract/core-contract.mjs +35 -2
  14. package/scripts/runkit-contract/efficiency-metrics.mjs +9 -7
  15. package/scripts/runkit-contract/formal-preflight.mjs +352 -0
  16. package/scripts/runkit-contract/formal-supplemental-evidence.mjs +713 -0
  17. package/scripts/runkit-contract/formal-workflow.mjs +529 -59
  18. package/scripts/runkit-contract/onboarding-doctor.mjs +32 -13
  19. package/scripts/runkit-contract/profile-onboarding.mjs +52 -33
  20. package/scripts/runkit-contract/project-cli-resolver.mjs +33 -3
  21. package/scripts/runkit-contract/project-external-delivery.mjs +16 -9
  22. package/scripts/runkit-contract/quick-attest.mjs +24 -0
  23. package/scripts/runkit-contract/runkit-cli.mjs +365 -49
  24. package/scripts/runkit-contract/schemas/formal-check-v2.schema.json +231 -0
  25. package/scripts/runkit-contract/schemas/formal-envelope-preflight-v1.schema.json +212 -0
  26. package/scripts/runkit-contract/schemas/formal-evidence-summary-v1.schema.json +114 -0
  27. package/scripts/runkit-contract/schemas/formal-supplemental-evidence-v1.schema.json +103 -0
  28. package/scripts/runkit-contract/schemas/team-project-event-v5.schema.json +89 -0
  29. package/scripts/runkit-contract/schemas/team-project-status-v3.schema.json +173 -0
  30. package/scripts/runkit-contract/snapshot.mjs +125 -5
  31. package/scripts/runkit-contract/source-candidate.mjs +271 -69
  32. package/scripts/runkit-contract/sync-release-identity.mjs +39 -4
  33. package/scripts/runkit-contract/team-project-successor.mjs +7 -4
  34. package/scripts/runkit-contract/team-project.mjs +440 -28
  35. package/scripts/runkit-contract/usage-mode.mjs +9 -3
  36. package/scripts/runkit-contract/verification-envelope.mjs +425 -37
@@ -28,10 +28,12 @@ const MAX_JSON_DEPTH = 64;
28
28
  const MAX_JSON_NODES = 50_000;
29
29
 
30
30
  class StrictJsonParser {
31
- constructor(text) {
31
+ constructor(text, { maxDepth = MAX_JSON_DEPTH, maxNodes = MAX_JSON_NODES } = {}) {
32
32
  this.text = text;
33
33
  this.index = 0;
34
34
  this.nodes = 0;
35
+ this.maxDepth = maxDepth;
36
+ this.maxNodes = maxNodes;
35
37
  }
36
38
 
37
39
  parse() {
@@ -44,7 +46,7 @@ class StrictJsonParser {
44
46
  parseValue(depth) {
45
47
  this.skipWhitespace();
46
48
  this.nodes += 1;
47
- if (this.nodes > MAX_JSON_NODES) throw new Error("JSON input exceeds the node limit");
49
+ if (this.nodes > this.maxNodes) throw new Error("JSON input exceeds the node limit");
48
50
  const character = this.text[this.index];
49
51
  if (character === "{") return this.parseObject(depth);
50
52
  if (character === "[") return this.parseArray(depth);
@@ -165,7 +167,7 @@ class StrictJsonParser {
165
167
 
166
168
  enter(depth) {
167
169
  const nextDepth = depth + 1;
168
- if (nextDepth > MAX_JSON_DEPTH) throw new Error("JSON input exceeds the nesting limit");
170
+ if (nextDepth > this.maxDepth) throw new Error("JSON input exceeds the nesting limit");
169
171
  return nextDepth;
170
172
  }
171
173
 
@@ -180,11 +182,15 @@ class StrictJsonParser {
180
182
  }
181
183
  }
182
184
 
183
- function parseJsonStrict(text) {
184
- if (typeof text !== "string" || Buffer.byteLength(text, "utf8") > MAX_JSON_BYTES) {
185
+ function parseJsonStrict(text, {
186
+ maxBytes = MAX_JSON_BYTES,
187
+ maxDepth = MAX_JSON_DEPTH,
188
+ maxNodes = MAX_JSON_NODES,
189
+ } = {}) {
190
+ if (typeof text !== "string" || Buffer.byteLength(text, "utf8") > maxBytes) {
185
191
  throw new Error("JSON input exceeds the input-byte limit");
186
192
  }
187
- return new StrictJsonParser(text).parse();
193
+ return new StrictJsonParser(text, { maxDepth, maxNodes }).parse();
188
194
  }
189
195
 
190
196
  export function resolveBoundedWorkspaceRoot(workspaceRoot) {
@@ -212,7 +218,10 @@ function identityMatches(left, right) {
212
218
  && left.mtimeMs === right.mtimeMs;
213
219
  }
214
220
 
215
- export function readWorkspaceFileBounded(root, relativePath, { optional = false } = {}) {
221
+ export function readWorkspaceBytesBounded(root, relativePath, {
222
+ optional = false,
223
+ maxBytes = MAX_JSON_BYTES,
224
+ } = {}) {
216
225
  const target = path.resolve(root, relativePath);
217
226
  if (!strictDescendant(root, target)) throw new Error("workspace file escaped the workspace root");
218
227
  const segments = path.relative(root, target).split(path.sep);
@@ -238,17 +247,16 @@ export function readWorkspaceFileBounded(root, relativePath, { optional = false
238
247
  const descriptor = openSync(target, "r");
239
248
  try {
240
249
  const before = fstatSync(descriptor);
241
- if (!before.isFile() || before.size > MAX_JSON_BYTES) {
250
+ if (!before.isFile() || before.size > maxBytes) {
242
251
  throw new Error("workspace file exceeds the input-byte limit");
243
252
  }
244
- const buffer = Buffer.allocUnsafe(MAX_JSON_BYTES + 1);
253
+ const buffer = Buffer.allocUnsafe(before.size);
245
254
  let offset = 0;
246
- while (offset <= MAX_JSON_BYTES) {
255
+ while (offset < buffer.length) {
247
256
  const bytesRead = readSync(descriptor, buffer, offset, buffer.length - offset, offset);
248
257
  if (bytesRead === 0) break;
249
258
  offset += bytesRead;
250
259
  }
251
- if (offset > MAX_JSON_BYTES) throw new Error("workspace file exceeds the input-byte limit");
252
260
  const after = fstatSync(descriptor);
253
261
  const currentStat = lstatSync(target);
254
262
  if (
@@ -260,15 +268,22 @@ export function readWorkspaceFileBounded(root, relativePath, { optional = false
260
268
  ) {
261
269
  throw new Error("workspace file identity changed while reading");
262
270
  }
263
- return new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, offset));
271
+ return buffer.subarray(0, offset);
264
272
  } finally {
265
273
  closeSync(descriptor);
266
274
  }
267
275
  }
268
276
 
277
+ export function readWorkspaceFileBounded(root, relativePath, options) {
278
+ const bytes = readWorkspaceBytesBounded(root, relativePath, options);
279
+ return bytes === null
280
+ ? null
281
+ : new TextDecoder("utf-8", { fatal: true }).decode(bytes);
282
+ }
283
+
269
284
  export function readWorkspaceJsonBounded(root, relativePath, options) {
270
285
  const text = readWorkspaceFileBounded(root, relativePath, options);
271
- return text === null ? null : parseJsonStrict(text);
286
+ return text === null ? null : parseJsonStrict(text, options);
272
287
  }
273
288
 
274
289
  async function readResponseJsonBounded(response) {
@@ -549,6 +564,10 @@ function runtimeIsolationCheck(root) {
549
564
  };
550
565
  }
551
566
 
567
+ export function inspectRuntimeIsolationV1({ workspaceRoot } = {}) {
568
+ return runtimeIsolationCheck(resolveBoundedWorkspaceRoot(workspaceRoot));
569
+ }
570
+
552
571
  function configCheck(root) {
553
572
  try {
554
573
  const config = readWorkspaceJsonBounded(
@@ -1440,9 +1440,47 @@ export function applyDetectedProfilesV3(input = {}) {
1440
1440
  });
1441
1441
  }
1442
1442
 
1443
+ function mergeDetectedProfileDefinition(current, detected) {
1444
+ current.commands ??= [];
1445
+ const addedPaths = detected.paths
1446
+ .filter((value) => !current.paths.includes(value))
1447
+ .sort();
1448
+ current.paths = [...new Set([...current.paths, ...detected.paths])].sort();
1449
+ const commands = new Map(current.commands.map((entry) => [entry.id, entry]));
1450
+ const addedCommandIds = [];
1451
+ const updatedCommandIds = [];
1452
+ for (const detectedCommand of detected.commands) {
1453
+ const currentCommand = commands.get(detectedCommand.id);
1454
+ if (!currentCommand) {
1455
+ current.commands.push(structuredClone(detectedCommand));
1456
+ commands.set(detectedCommand.id, detectedCommand);
1457
+ addedCommandIds.push(detectedCommand.id);
1458
+ } else if (canonicalJson(currentCommand) !== canonicalJson(detectedCommand)) {
1459
+ const index = current.commands.findIndex((entry) => entry.id === detectedCommand.id);
1460
+ current.commands[index] = structuredClone(detectedCommand);
1461
+ commands.set(detectedCommand.id, detectedCommand);
1462
+ updatedCommandIds.push(detectedCommand.id);
1463
+ }
1464
+ }
1465
+ current.commands.sort((left, right) => left.id.localeCompare(right.id));
1466
+ if (
1467
+ addedPaths.length === 0
1468
+ && addedCommandIds.length === 0
1469
+ && updatedCommandIds.length === 0
1470
+ ) return null;
1471
+ return {
1472
+ profileId: current.id,
1473
+ addedPaths,
1474
+ addedCommandIds: addedCommandIds.sort(),
1475
+ updatedCommandIds: updatedCommandIds.sort(),
1476
+ };
1477
+ }
1478
+
1443
1479
  function mergeDetectedProfiles(currentProfiles, detectedProfiles) {
1444
1480
  const profiles = currentProfiles.profiles.map((profile) => structuredClone(profile));
1445
1481
  const byId = new Map(profiles.map((profile) => [profile.id, profile]));
1482
+ const commandOwners = new Map(profiles.flatMap((profile) =>
1483
+ (profile.commands ?? []).map((entry) => [entry.id, profile.id])));
1446
1484
  const addedProfileIds = [];
1447
1485
  const updatedProfiles = [];
1448
1486
  for (const detected of [...detectedProfiles.profiles]
@@ -1456,45 +1494,26 @@ function mergeDetectedProfiles(currentProfiles, detectedProfiles) {
1456
1494
  const primaryCoverageComplete = impact.uncoveredPaths.length === 0
1457
1495
  && !impact.warnings.some((warning) => warning.startsWith("supporting_only_match:"));
1458
1496
  if (primaryCoverageComplete) continue;
1497
+ const existingCommandOwnerIds = [...new Set(detected.commands
1498
+ .map((command) => commandOwners.get(command.id))
1499
+ .filter(Boolean))];
1500
+ if (existingCommandOwnerIds.length === 1) {
1501
+ const owner = byId.get(existingCommandOwnerIds[0]);
1502
+ const update = mergeDetectedProfileDefinition(owner, detected);
1503
+ if (update !== null) updatedProfiles.push(update);
1504
+ for (const command of owner.commands) commandOwners.set(command.id, owner.id);
1505
+ continue;
1506
+ }
1459
1507
  const added = { ...structuredClone(detected), primary: false };
1460
1508
  profiles.push(added);
1461
1509
  byId.set(added.id, added);
1510
+ for (const command of added.commands) commandOwners.set(command.id, added.id);
1462
1511
  addedProfileIds.push(added.id);
1463
1512
  continue;
1464
1513
  }
1465
- const addedPaths = detected.paths
1466
- .filter((value) => !current.paths.includes(value))
1467
- .sort();
1468
- current.paths = [...new Set([...current.paths, ...detected.paths])].sort();
1469
- const commands = new Map(current.commands.map((entry) => [entry.id, entry]));
1470
- const addedCommandIds = [];
1471
- const updatedCommandIds = [];
1472
- for (const detectedCommand of detected.commands) {
1473
- const currentCommand = commands.get(detectedCommand.id);
1474
- if (!currentCommand) {
1475
- current.commands.push(structuredClone(detectedCommand));
1476
- commands.set(detectedCommand.id, detectedCommand);
1477
- addedCommandIds.push(detectedCommand.id);
1478
- } else if (canonicalJson(currentCommand) !== canonicalJson(detectedCommand)) {
1479
- const index = current.commands.findIndex((entry) => entry.id === detectedCommand.id);
1480
- current.commands[index] = structuredClone(detectedCommand);
1481
- commands.set(detectedCommand.id, detectedCommand);
1482
- updatedCommandIds.push(detectedCommand.id);
1483
- }
1484
- }
1485
- current.commands.sort((left, right) => left.id.localeCompare(right.id));
1486
- if (
1487
- addedPaths.length > 0
1488
- || addedCommandIds.length > 0
1489
- || updatedCommandIds.length > 0
1490
- ) {
1491
- updatedProfiles.push({
1492
- profileId: current.id,
1493
- addedPaths,
1494
- addedCommandIds: addedCommandIds.sort(),
1495
- updatedCommandIds: updatedCommandIds.sort(),
1496
- });
1497
- }
1514
+ const update = mergeDetectedProfileDefinition(current, detected);
1515
+ if (update !== null) updatedProfiles.push(update);
1516
+ for (const command of current.commands) commandOwners.set(command.id, current.id);
1498
1517
  }
1499
1518
  return {
1500
1519
  proposedProfiles: {
@@ -15,9 +15,16 @@ 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
19
  const EXACT_VERSION = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/;
19
20
  const SHA256_REF = /^sha256:[a-f0-9]{64}$/;
20
21
  const TRUSTED_PRIOR_CORE_IDENTITIES = new Map([
22
+ ["0.19.1", new Set([
23
+ "sha256:291ef97c93a513eed24a29f75c400b61cefd6a8c0ab228401cf2c6a73ea9e622",
24
+ ])],
25
+ ["0.19.0", new Set([
26
+ "sha256:803fb7de76a6ccffd1ec5e0164eeff15a580209fa6dfbef9e561345ab616b403",
27
+ ])],
21
28
  ["0.18.4", new Set([
22
29
  "sha256:372d4c247e4b40b0fd2f3fe0f5bf928548095bfb7e2e92e9ac66790e2717f77d",
23
30
  ])],
@@ -520,7 +527,28 @@ function coreFileList(coreContractSource, identifier) {
520
527
  return values;
521
528
  }
522
529
 
523
- function installedCoreManifestSha256(packageRoot) {
530
+ function coreDependencyBytes(target, relativePath, version) {
531
+ const bytes = readFileSync(target);
532
+ if (relativePath !== "packages/attest/src/quick.mjs"
533
+ || !SELF_NORMALIZED_QUICK_CORE_VERSIONS.has(version)) return bytes;
534
+ const source = bytes.toString("utf8");
535
+ const escapedVersion = version.replaceAll(".", "\\.");
536
+ const currentIdentity = new RegExp(
537
+ `(coreVersion:\\s*"${escapedVersion}",\\s*coreManifestSha256:\\s*")`
538
+ + "sha256:[a-f0-9]{64}(\")",
539
+ "gu",
540
+ );
541
+ const matches = [...source.matchAll(currentIdentity)];
542
+ if (matches.length !== 1) {
543
+ throw new Error("project_cli_core_contract_invalid");
544
+ }
545
+ return Buffer.from(source.replace(
546
+ currentIdentity,
547
+ `$1sha256:${"0".repeat(64)}$2`,
548
+ ));
549
+ }
550
+
551
+ function installedCoreManifestSha256(packageRoot, version) {
524
552
  const coreRoot = path.join(packageRoot, "scripts", "runkit-contract");
525
553
  const coreContractPath = path.join(coreRoot, "core-contract.mjs");
526
554
  assertRegularDescendant(
@@ -567,7 +595,9 @@ function installedCoreManifestSha256(packageRoot) {
567
595
  ) {
568
596
  throw new Error("project_cli_core_closure_too_large");
569
597
  }
570
- return `${relativePath}\tsha256:${sha256(readFileSync(target))}\n`;
598
+ return `${relativePath}\tsha256:${sha256(
599
+ coreDependencyBytes(target, relativePath, version),
600
+ )}\n`;
571
601
  }).join("");
572
602
  return `sha256:${sha256(stream)}`;
573
603
  }
@@ -626,7 +656,7 @@ function verifyInstalledCoreBinding(root, packageRoot, version) {
626
656
  }
627
657
 
628
658
  function verifyInstalledCoreClosure(packageRoot, version) {
629
- const actualCoreManifestSha256 = installedCoreManifestSha256(packageRoot);
659
+ const actualCoreManifestSha256 = installedCoreManifestSha256(packageRoot, version);
630
660
  const trusted = trustedCoreManifestHashes(version);
631
661
  if (!trusted.hashes.has(actualCoreManifestSha256)) {
632
662
  throw new Error("project_cli_untrusted_core_identity");
@@ -30,6 +30,7 @@ import {
30
30
  appendTeamProjectEventV1,
31
31
  normalizeTeamProjectDefinitionV1,
32
32
  readTeamProjectStatusV2,
33
+ readTeamProjectStatusV3,
33
34
  teamProjectDefinitionBindingV1,
34
35
  withTeamProjectControlLockV1,
35
36
  } from "./team-project.mjs";
@@ -85,11 +86,14 @@ function artifactSha256(value) {
85
86
 
86
87
  function validateTimestamp(value, label) {
87
88
  if (typeof value !== "string"
88
- || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value)
89
+ || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/u.test(value)
89
90
  || Number.isNaN(Date.parse(value))) {
90
- throw new Error(`${label} must be an ISO-8601 UTC timestamp.`);
91
+ throw new Error(
92
+ `${label} must be an ISO-8601 UTC timestamp `
93
+ + "(YYYY-MM-DDTHH:mm:ssZ or YYYY-MM-DDTHH:mm:ss.sssZ).",
94
+ );
91
95
  }
92
- return value;
96
+ return new Date(value).toISOString();
93
97
  }
94
98
 
95
99
  function compareIdentifiers(left, right) {
@@ -256,12 +260,13 @@ export function captureTargetWorkspaceSnapshotV1({
256
260
  apply = false,
257
261
  }) {
258
262
  safeIdentifier(snapshotId, "snapshotId");
259
- validateTimestamp(occurredAt, "Target snapshot capturedAt");
263
+ occurredAt = validateTimestamp(occurredAt, "Target snapshot capturedAt");
260
264
  const controllerRoot = canonicalControllerRoot(controllerWorkspaceRoot);
261
265
  const targetRoot = canonicalTargetRoot(targetWorkspaceRoot);
262
266
  assertDisjointWorkspaces(controllerRoot, targetRoot);
263
267
  const definition = readProjectDefinition(controllerRoot);
264
- const workItem = definition.workItems.find(row => row.id === workItemId);
268
+ const workItem = readTeamProjectStatusV3({ workspaceRoot: controllerRoot })
269
+ .workItems.find(row => row.workItemId === workItemId);
265
270
  if (!workItem) throw new Error(`Unknown team project WorkItem: ${workItemId}.`);
266
271
  const projectBinding = teamProjectDefinitionBindingV1(definition);
267
272
  const selectedAllowedPaths = resolveAllowedPaths(workItem, allowedPaths);
@@ -293,11 +298,12 @@ export function captureTargetWorkspaceSnapshotV1({
293
298
 
294
299
  const persisted = withTeamProjectControlLockV1({
295
300
  workspaceRoot: controllerRoot,
296
- operation({ definition: currentDefinition, definitionBinding }) {
301
+ operation({ definitionBinding }) {
297
302
  if (canonical(definitionBinding) !== canonical(projectBinding)) {
298
303
  throw new Error("Team project identity changed before target snapshot persistence.");
299
304
  }
300
- const currentWorkItem = currentDefinition.workItems.find(row => row.id === workItemId);
305
+ const currentWorkItem = readTeamProjectStatusV3({ workspaceRoot: controllerRoot })
306
+ .workItems.find(row => row.workItemId === workItemId);
301
307
  if (!currentWorkItem
302
308
  || canonical(resolveAllowedPaths(currentWorkItem, selectedAllowedPaths))
303
309
  !== canonical(selectedAllowedPaths)) {
@@ -549,12 +555,13 @@ export function importExternalDeliveryV1({
549
555
  }) {
550
556
  safeIdentifier(deliveryId, "deliveryId");
551
557
  safeIdentifier(producerId, "producerId");
552
- validateTimestamp(occurredAt, "External delivery occurredAt");
558
+ occurredAt = validateTimestamp(occurredAt, "External delivery occurredAt");
553
559
  const controllerRoot = canonicalControllerRoot(controllerWorkspaceRoot);
554
560
  const targetRoot = canonicalTargetRoot(targetWorkspaceRoot);
555
561
  assertDisjointWorkspaces(controllerRoot, targetRoot);
556
562
  const definition = readProjectDefinition(controllerRoot);
557
- const workItem = definition.workItems.find(row => row.id === workItemId);
563
+ const workItem = readTeamProjectStatusV3({ workspaceRoot: controllerRoot })
564
+ .workItems.find(row => row.workItemId === workItemId);
558
565
  if (!workItem) throw new Error(`Unknown team project WorkItem: ${workItemId}.`);
559
566
  const projectBinding = teamProjectDefinitionBindingV1(definition);
560
567
  validateCurrentAssignment(controllerRoot, workItemId, assignmentId);
@@ -1,5 +1,29 @@
1
+ import {
2
+ SUPPORTED_QUICK_CORE_IDENTITIES,
3
+ attestQuickReceiptDetailsFromBytesWithCoreCatalog,
4
+ } from "../../packages/attest/src/quick.mjs";
5
+ import { currentCoreIdentity } from "./core-contract.mjs";
6
+
1
7
  export {
2
8
  attestQuickReceipt,
3
9
  attestQuickReceiptDetails,
10
+ attestQuickReceiptDetailsFromBytes,
4
11
  validQuickReceiptShape,
5
12
  } from "../../packages/attest/src/quick.mjs";
13
+
14
+ function coreBoundCatalog() {
15
+ const current = currentCoreIdentity();
16
+ return [
17
+ current,
18
+ ...SUPPORTED_QUICK_CORE_IDENTITIES.filter((identity) => (
19
+ identity.coreVersion !== current.coreVersion
20
+ )),
21
+ ];
22
+ }
23
+
24
+ export function attestCoreBoundQuickReceiptDetailsFromBytes(options) {
25
+ return attestQuickReceiptDetailsFromBytesWithCoreCatalog(
26
+ options,
27
+ coreBoundCatalog(),
28
+ );
29
+ }