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.
- package/CHANGELOG.md +37 -0
- package/README.md +71 -0
- package/docs/architecture/OWLCODA_RUNKIT_0191_PROFILE_RECONCILE_PATCH_PRODUCT_DECISION_20260813.md +11 -5
- package/docs/architecture/OWLCODA_RUNKIT_0192_FORMAL_USABILITY_PRODUCT_DECISION_20260813.md +141 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +50 -7
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
- package/package.json +1 -1
- package/packages/attest/distribution-manifest-v1.json +5 -0
- package/packages/attest/src/quick.mjs +37 -4
- package/registry-release-policy-v1.json +5 -5
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/core-contract.mjs +35 -2
- package/scripts/runkit-contract/efficiency-metrics.mjs +9 -7
- package/scripts/runkit-contract/formal-preflight.mjs +352 -0
- package/scripts/runkit-contract/formal-supplemental-evidence.mjs +713 -0
- package/scripts/runkit-contract/formal-workflow.mjs +529 -59
- package/scripts/runkit-contract/onboarding-doctor.mjs +32 -13
- package/scripts/runkit-contract/project-cli-resolver.mjs +30 -3
- package/scripts/runkit-contract/project-external-delivery.mjs +16 -9
- package/scripts/runkit-contract/quick-attest.mjs +24 -0
- package/scripts/runkit-contract/runkit-cli.mjs +365 -49
- package/scripts/runkit-contract/schemas/formal-check-v2.schema.json +231 -0
- package/scripts/runkit-contract/schemas/formal-envelope-preflight-v1.schema.json +212 -0
- package/scripts/runkit-contract/schemas/formal-evidence-summary-v1.schema.json +114 -0
- package/scripts/runkit-contract/schemas/formal-supplemental-evidence-v1.schema.json +103 -0
- package/scripts/runkit-contract/schemas/team-project-event-v5.schema.json +89 -0
- package/scripts/runkit-contract/schemas/team-project-status-v3.schema.json +173 -0
- package/scripts/runkit-contract/snapshot.mjs +125 -5
- package/scripts/runkit-contract/source-candidate.mjs +271 -69
- package/scripts/runkit-contract/sync-release-identity.mjs +39 -4
- package/scripts/runkit-contract/team-project-successor.mjs +7 -4
- package/scripts/runkit-contract/team-project.mjs +440 -28
- package/scripts/runkit-contract/usage-mode.mjs +9 -3
- 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 >
|
|
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 >
|
|
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
|
-
|
|
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
|
|
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 >
|
|
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(
|
|
253
|
+
const buffer = Buffer.allocUnsafe(before.size);
|
|
245
254
|
let offset = 0;
|
|
246
|
-
while (offset
|
|
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
|
|
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(
|
|
@@ -15,9 +15,13 @@ 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
|
+
])],
|
|
21
25
|
["0.19.0", new Set([
|
|
22
26
|
"sha256:803fb7de76a6ccffd1ec5e0164eeff15a580209fa6dfbef9e561345ab616b403",
|
|
23
27
|
])],
|
|
@@ -523,7 +527,28 @@ function coreFileList(coreContractSource, identifier) {
|
|
|
523
527
|
return values;
|
|
524
528
|
}
|
|
525
529
|
|
|
526
|
-
function
|
|
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) {
|
|
527
552
|
const coreRoot = path.join(packageRoot, "scripts", "runkit-contract");
|
|
528
553
|
const coreContractPath = path.join(coreRoot, "core-contract.mjs");
|
|
529
554
|
assertRegularDescendant(
|
|
@@ -570,7 +595,9 @@ function installedCoreManifestSha256(packageRoot) {
|
|
|
570
595
|
) {
|
|
571
596
|
throw new Error("project_cli_core_closure_too_large");
|
|
572
597
|
}
|
|
573
|
-
return `${relativePath}\tsha256:${sha256(
|
|
598
|
+
return `${relativePath}\tsha256:${sha256(
|
|
599
|
+
coreDependencyBytes(target, relativePath, version),
|
|
600
|
+
)}\n`;
|
|
574
601
|
}).join("");
|
|
575
602
|
return `sha256:${sha256(stream)}`;
|
|
576
603
|
}
|
|
@@ -629,7 +656,7 @@ function verifyInstalledCoreBinding(root, packageRoot, version) {
|
|
|
629
656
|
}
|
|
630
657
|
|
|
631
658
|
function verifyInstalledCoreClosure(packageRoot, version) {
|
|
632
|
-
const actualCoreManifestSha256 = installedCoreManifestSha256(packageRoot);
|
|
659
|
+
const actualCoreManifestSha256 = installedCoreManifestSha256(packageRoot, version);
|
|
633
660
|
const trusted = trustedCoreManifestHashes(version);
|
|
634
661
|
if (!trusted.hashes.has(actualCoreManifestSha256)) {
|
|
635
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}
|
|
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(
|
|
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 =
|
|
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({
|
|
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 =
|
|
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 =
|
|
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
|
+
}
|