owlrunkit 0.19.1 → 0.20.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.
- package/CHANGELOG.md +52 -0
- package/README.md +107 -3
- 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/docs/architecture/OWLCODA_RUNKIT_020_TEAM_DELIVERY_PHASE1_PRODUCT_DECISION_20260813.md +296 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +74 -9
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
- package/package.json +1 -1
- package/packages/attest/distribution-manifest-v1.json +10 -0
- package/packages/attest/src/quick.mjs +42 -4
- package/registry-release-policy-v1.json +5 -5
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/core-contract.mjs +42 -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 +33 -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 +445 -52
- package/scripts/runkit-contract/schemas/candidate-transfer-packet-v1.schema.json +98 -0
- 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-acceptance-packet-v2.schema.json +109 -0
- package/scripts/runkit-contract/schemas/team-delivery-status-v1.schema.json +72 -0
- package/scripts/runkit-contract/schemas/team-packet-validation-v1.schema.json +33 -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/schemas/team-recommendation-v1.schema.json +121 -0
- package/scripts/runkit-contract/schemas/team-task-packet-v2.schema.json +228 -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-delivery.mjs +906 -0
- 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
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
realpathSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
attestCoreBoundQuickReceiptDetailsFromBytes,
|
|
13
|
+
} from "./quick-attest.mjs";
|
|
14
|
+
import {
|
|
15
|
+
decodeUtf8Strict,
|
|
16
|
+
parseJsonStrict,
|
|
17
|
+
readFileBytesBounded,
|
|
18
|
+
} from "../../packages/attest/src/formal.mjs";
|
|
19
|
+
import { persistEvidenceBytes } from "./evidence-store.mjs";
|
|
20
|
+
import { readFormalChecksV1 } from "./formal-workflow.mjs";
|
|
21
|
+
import {
|
|
22
|
+
readWorkspaceBytesBounded,
|
|
23
|
+
readWorkspaceFileBounded,
|
|
24
|
+
readWorkspaceJsonBounded,
|
|
25
|
+
} from "./onboarding-doctor.mjs";
|
|
26
|
+
import { withControlTransaction } from "./lease-lifecycle.mjs";
|
|
27
|
+
import {
|
|
28
|
+
loadActiveExecution,
|
|
29
|
+
relativeToWorkspace,
|
|
30
|
+
safeIdentifier,
|
|
31
|
+
safeRelativePath,
|
|
32
|
+
sha256,
|
|
33
|
+
writeJsonExclusiveAtomically,
|
|
34
|
+
} from "./provenance-common.mjs";
|
|
35
|
+
import { verifySourceCandidateDetailsV2 } from "./source-candidate.mjs";
|
|
36
|
+
|
|
37
|
+
const SUPPLEMENTAL_SCHEMA = "OwlCodaRunKitFormalSupplementalEvidenceV1";
|
|
38
|
+
const SUMMARY_SCHEMA = "OwlCodaRunKitFormalEvidenceSummaryV1";
|
|
39
|
+
const KINDS = new Set(["supplemental-local", "networked"]);
|
|
40
|
+
|
|
41
|
+
function canonical(value) {
|
|
42
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
43
|
+
if (value && typeof value === "object") {
|
|
44
|
+
return `{${Object.keys(value).sort()
|
|
45
|
+
.map(key => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
46
|
+
.join(",")}}`;
|
|
47
|
+
}
|
|
48
|
+
const encoded = JSON.stringify(value);
|
|
49
|
+
if (encoded === undefined) throw new Error("Formal supplemental evidence is not canonical JSON.");
|
|
50
|
+
return encoded;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function within(root, candidate) {
|
|
54
|
+
const relative = path.relative(root, candidate);
|
|
55
|
+
return relative === ""
|
|
56
|
+
|| (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readRegularJson(filePath, label) {
|
|
60
|
+
try {
|
|
61
|
+
const { bytes } = readFileBytesBounded(filePath);
|
|
62
|
+
return parseJsonStrict(decodeUtf8Strict(bytes));
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new Error(`${label} must be a bounded regular JSON file without symlinks.`, {
|
|
65
|
+
cause: error,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function writeOrResume(filePath, value, label) {
|
|
71
|
+
if (!existsSync(filePath)) {
|
|
72
|
+
writeJsonExclusiveAtomically(filePath, value);
|
|
73
|
+
return { value, resumed: false };
|
|
74
|
+
}
|
|
75
|
+
const existing = readRegularJson(filePath, label);
|
|
76
|
+
if (canonical(existing) !== canonical(value)) {
|
|
77
|
+
throw new Error(`Existing ${label} differs from the resumed command.`);
|
|
78
|
+
}
|
|
79
|
+
return { value: existing, resumed: true };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function supplementalRoot(executionRoot) {
|
|
83
|
+
return path.join(executionRoot, "formal-supplemental-evidence");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function ensureRegularDirectory(directory, label) {
|
|
87
|
+
if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
|
|
88
|
+
const stat = lstatSync(directory);
|
|
89
|
+
if (
|
|
90
|
+
stat.isSymbolicLink()
|
|
91
|
+
|| !stat.isDirectory()
|
|
92
|
+
|| realpathSync(directory) !== path.resolve(directory)
|
|
93
|
+
) {
|
|
94
|
+
throw new Error(`${label} must be a regular directory without symlinks.`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function exactKeys(value, expected) {
|
|
99
|
+
return value !== null
|
|
100
|
+
&& typeof value === "object"
|
|
101
|
+
&& !Array.isArray(value)
|
|
102
|
+
&& Object.keys(value).sort().join("\0") === [...expected].sort().join("\0");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function validHash(value, prefixed = false) {
|
|
106
|
+
return new RegExp(prefixed ? "^sha256:[a-f0-9]{64}$" : "^[a-f0-9]{64}$", "u")
|
|
107
|
+
.test(value ?? "");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function frozenQuickMaterials({ workspaceRoot, receiptPath, attestation }) {
|
|
111
|
+
const receiptRoot = path.dirname(receiptPath);
|
|
112
|
+
return attestation.verifiedMaterials
|
|
113
|
+
.filter(material => within(receiptRoot, material.path) || material.path === receiptPath)
|
|
114
|
+
.map(material => {
|
|
115
|
+
const { bytes, absolutePath } = readFileBytesBounded(material.path);
|
|
116
|
+
if (absolutePath !== material.path) {
|
|
117
|
+
throw new Error("Formal supplemental material path must not use symlinks.");
|
|
118
|
+
}
|
|
119
|
+
const stored = persistEvidenceBytes({
|
|
120
|
+
workspaceRoot,
|
|
121
|
+
bytes,
|
|
122
|
+
encoding: "raw",
|
|
123
|
+
});
|
|
124
|
+
if (stored.sha256 !== material.sha256) {
|
|
125
|
+
throw new Error("Formal supplemental material changed after Quick attestation.");
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
sourcePath: relativeToWorkspace(workspaceRoot, material.path),
|
|
129
|
+
evidenceObjectPath: stored.path,
|
|
130
|
+
sha256: stored.sha256,
|
|
131
|
+
sizeBytes: stored.sizeBytes,
|
|
132
|
+
};
|
|
133
|
+
})
|
|
134
|
+
.sort((left, right) => left.sourcePath.localeCompare(right.sourcePath, "en"));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function validateFrozenMaterial(value, workspaceRoot) {
|
|
138
|
+
if (!exactKeys(value, [
|
|
139
|
+
"sourcePath", "evidenceObjectPath", "sha256", "sizeBytes",
|
|
140
|
+
])) return false;
|
|
141
|
+
let sourcePath;
|
|
142
|
+
let evidenceObjectPath;
|
|
143
|
+
try {
|
|
144
|
+
sourcePath = safeRelativePath(value.sourcePath, "frozen material sourcePath");
|
|
145
|
+
evidenceObjectPath = safeRelativePath(
|
|
146
|
+
value.evidenceObjectPath,
|
|
147
|
+
"frozen material evidenceObjectPath",
|
|
148
|
+
);
|
|
149
|
+
} catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
if (
|
|
153
|
+
sourcePath !== value.sourcePath
|
|
154
|
+
|| evidenceObjectPath !== value.evidenceObjectPath
|
|
155
|
+
|| !evidenceObjectPath.startsWith(".owlcoda/runkit/evidence/raw-sha256/")
|
|
156
|
+
|| !validHash(value.sha256, true)
|
|
157
|
+
|| !Number.isSafeInteger(value.sizeBytes)
|
|
158
|
+
|| value.sizeBytes < 0
|
|
159
|
+
) return false;
|
|
160
|
+
try {
|
|
161
|
+
const bytes = readWorkspaceBytesBounded(
|
|
162
|
+
workspaceRoot,
|
|
163
|
+
evidenceObjectPath,
|
|
164
|
+
{ maxBytes: Math.max(value.sizeBytes, 1) },
|
|
165
|
+
);
|
|
166
|
+
const sourceBytes = readWorkspaceBytesBounded(
|
|
167
|
+
workspaceRoot,
|
|
168
|
+
sourcePath,
|
|
169
|
+
{ maxBytes: Math.max(value.sizeBytes, 1) },
|
|
170
|
+
);
|
|
171
|
+
return bytes.byteLength === value.sizeBytes
|
|
172
|
+
&& sourceBytes.byteLength === value.sizeBytes
|
|
173
|
+
&& `sha256:${sha256(bytes)}` === value.sha256
|
|
174
|
+
&& `sha256:${sha256(sourceBytes)}` === value.sha256;
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function validateSupplemental(value, { runId, evidenceId, workspaceRoot }) {
|
|
181
|
+
const { supplementalEvidenceSha256, ...body } = value ?? {};
|
|
182
|
+
let receiptPath;
|
|
183
|
+
let candidatePath;
|
|
184
|
+
try {
|
|
185
|
+
receiptPath = safeRelativePath(value?.quickEvidence?.receiptPath, "receiptPath");
|
|
186
|
+
candidatePath = safeRelativePath(value?.sourceBinding?.candidatePath, "candidatePath");
|
|
187
|
+
} catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
return exactKeys(value, [
|
|
191
|
+
"schemaVersion", "runId", "evidenceId", "kind", "classificationBasis",
|
|
192
|
+
"status", "gating", "sourceBinding", "quickEvidence", "limitations",
|
|
193
|
+
"frozenMaterials", "authorizationGranted", "supplementalEvidenceSha256",
|
|
194
|
+
])
|
|
195
|
+
&& value.schemaVersion === SUPPLEMENTAL_SCHEMA
|
|
196
|
+
&& value.runId === runId
|
|
197
|
+
&& value.evidenceId === evidenceId
|
|
198
|
+
&& KINDS.has(value.kind)
|
|
199
|
+
&& value.classificationBasis === "operator_declared_not_attested"
|
|
200
|
+
&& value.status === "attached"
|
|
201
|
+
&& value.gating === false
|
|
202
|
+
&& value.authorizationGranted === false
|
|
203
|
+
&& supplementalEvidenceSha256 === sha256(canonical(body))
|
|
204
|
+
&& exactKeys(value.sourceBinding, [
|
|
205
|
+
"candidatePath", "candidateSha256", "sourceFingerprint", "quickWorkspaceFingerprint",
|
|
206
|
+
])
|
|
207
|
+
&& candidatePath === value.sourceBinding.candidatePath
|
|
208
|
+
&& validHash(value.sourceBinding.candidateSha256, true)
|
|
209
|
+
&& validHash(value.sourceBinding.sourceFingerprint)
|
|
210
|
+
&& validHash(value.sourceBinding.quickWorkspaceFingerprint, true)
|
|
211
|
+
&& exactKeys(value.quickEvidence, [
|
|
212
|
+
"receiptId", "receiptPath", "receiptSha256", "receiptSchemaVersion",
|
|
213
|
+
"exactCommand", "attestationDecision", "attestationIssueCodes",
|
|
214
|
+
])
|
|
215
|
+
&& receiptPath === value.quickEvidence.receiptPath
|
|
216
|
+
&& typeof value.quickEvidence.receiptId === "string"
|
|
217
|
+
&& value.quickEvidence.receiptId.length > 0
|
|
218
|
+
&& validHash(value.quickEvidence.receiptSha256, true)
|
|
219
|
+
&& typeof value.quickEvidence.receiptSchemaVersion === "string"
|
|
220
|
+
&& value.quickEvidence.receiptSchemaVersion.length > 0
|
|
221
|
+
&& value.quickEvidence.exactCommand !== null
|
|
222
|
+
&& typeof value.quickEvidence.exactCommand === "object"
|
|
223
|
+
&& !Array.isArray(value.quickEvidence.exactCommand)
|
|
224
|
+
&& value.quickEvidence.attestationDecision === "GO"
|
|
225
|
+
&& Array.isArray(value.quickEvidence.attestationIssueCodes)
|
|
226
|
+
&& value.quickEvidence.attestationIssueCodes.every(code => typeof code === "string")
|
|
227
|
+
&& Array.isArray(value.frozenMaterials)
|
|
228
|
+
&& value.frozenMaterials.length > 0
|
|
229
|
+
&& value.frozenMaterials.every(material => validateFrozenMaterial(material, workspaceRoot))
|
|
230
|
+
&& value.frozenMaterials.some(material => (
|
|
231
|
+
material.sourcePath === value.quickEvidence.receiptPath
|
|
232
|
+
&& material.sha256 === value.quickEvidence.receiptSha256
|
|
233
|
+
))
|
|
234
|
+
&& Array.isArray(value.limitations)
|
|
235
|
+
&& value.limitations.length > 0
|
|
236
|
+
&& value.limitations.every(limit => typeof limit === "string" && limit.length > 0);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function readSupplementals(workspaceRoot, executionRoot, runId) {
|
|
240
|
+
const root = supplementalRoot(executionRoot);
|
|
241
|
+
if (!existsSync(root)) return [];
|
|
242
|
+
const stat = lstatSync(root);
|
|
243
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
244
|
+
throw new Error("Formal supplemental evidence root must be a regular directory.");
|
|
245
|
+
}
|
|
246
|
+
return readdirSync(root, { withFileTypes: true })
|
|
247
|
+
.filter(entry => entry.name.endsWith(".json"))
|
|
248
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
249
|
+
.map(entry => {
|
|
250
|
+
if (entry.isSymbolicLink() || !entry.isFile()) {
|
|
251
|
+
throw new Error(`Formal supplemental evidence must be a regular file: ${entry.name}`);
|
|
252
|
+
}
|
|
253
|
+
const artifactPath = path.join(root, entry.name);
|
|
254
|
+
const value = readWorkspaceJsonBounded(
|
|
255
|
+
workspaceRoot,
|
|
256
|
+
relativeToWorkspace(workspaceRoot, artifactPath),
|
|
257
|
+
);
|
|
258
|
+
const evidenceId = entry.name.slice(0, -".json".length);
|
|
259
|
+
if (!validateSupplemental(value, { runId, evidenceId, workspaceRoot })) {
|
|
260
|
+
throw new Error(`Formal supplemental evidence contract is invalid: ${entry.name}`);
|
|
261
|
+
}
|
|
262
|
+
return value;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function latestCurrentCandidate({ workspaceRoot, executionRoot, runId }) {
|
|
267
|
+
const checks = readFormalChecksV1({ workspaceRoot, executionRoot, runId });
|
|
268
|
+
if (checks.length === 0) {
|
|
269
|
+
throw new Error("Formal supplemental evidence requires at least one existing Formal check candidate.");
|
|
270
|
+
}
|
|
271
|
+
const latest = checks.at(-1);
|
|
272
|
+
const details = verifySourceCandidateDetailsV2({
|
|
273
|
+
workspaceRoot,
|
|
274
|
+
candidatePath: latest.candidatePath,
|
|
275
|
+
});
|
|
276
|
+
if (
|
|
277
|
+
details.gate.status !== "valid"
|
|
278
|
+
|| details.gate.candidatePath !== latest.candidatePath
|
|
279
|
+
|| details.gate.candidateSha256 !== latest.candidateSha256
|
|
280
|
+
|| details.gate.sourceFingerprint !== latest.sourceFingerprint
|
|
281
|
+
) {
|
|
282
|
+
throw new Error("Formal supplemental evidence requires the latest source candidate to remain current.");
|
|
283
|
+
}
|
|
284
|
+
const candidate = details.candidateDocument;
|
|
285
|
+
return { checks, latest, candidate };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function quickReceiptMatchesCandidate(receipt, candidate) {
|
|
289
|
+
if (receipt.workspaceAfter.headCommit !== candidate.baseline.head) return false;
|
|
290
|
+
const expected = new Map();
|
|
291
|
+
for (const entry of candidate.sourceManifest.entries) {
|
|
292
|
+
if (entry.operation === "deleted") {
|
|
293
|
+
expected.set(entry.path, { path: entry.path, state: "deleted", sha256: null });
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (entry.operation === "renamed") {
|
|
297
|
+
expected.set(entry.previousPath, {
|
|
298
|
+
path: entry.previousPath,
|
|
299
|
+
state: "deleted",
|
|
300
|
+
sha256: null,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
expected.set(entry.path, {
|
|
304
|
+
path: entry.path,
|
|
305
|
+
state: entry.status === "??" ? "untracked" : "modified",
|
|
306
|
+
sha256: `sha256:${entry.sha256}`,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
const actual = [...receipt.workspaceAfter.dirtyOverlay]
|
|
310
|
+
.sort((left, right) => left.path.localeCompare(right.path, "en"));
|
|
311
|
+
const projected = [...expected.values()]
|
|
312
|
+
.sort((left, right) => left.path.localeCompare(right.path, "en"));
|
|
313
|
+
return canonical(actual) === canonical(projected);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function attachWithinControlTransaction({
|
|
317
|
+
workspaceRoot,
|
|
318
|
+
runId,
|
|
319
|
+
evidenceId,
|
|
320
|
+
kind,
|
|
321
|
+
receiptPath,
|
|
322
|
+
}) {
|
|
323
|
+
safeIdentifier(evidenceId, "evidenceId");
|
|
324
|
+
if (!KINDS.has(kind)) {
|
|
325
|
+
throw new Error("Formal supplemental evidence kind must be supplemental-local or networked.");
|
|
326
|
+
}
|
|
327
|
+
const root = realpathSync(workspaceRoot);
|
|
328
|
+
const { executionRoot, pinGate } = loadActiveExecution(root, runId);
|
|
329
|
+
if (pinGate.status !== "valid") return { ...pinGate, authorizationGranted: false };
|
|
330
|
+
if (existsSync(path.join(executionRoot, "closeout-receipt.json"))) {
|
|
331
|
+
throw new Error(`Execution is already closed: ${runId}`);
|
|
332
|
+
}
|
|
333
|
+
const requestedPath = path.resolve(receiptPath);
|
|
334
|
+
if (!within(root, requestedPath)) {
|
|
335
|
+
throw new Error("Formal supplemental Quick receipt must remain inside the workspace.");
|
|
336
|
+
}
|
|
337
|
+
const requestedRelativePath = relativeToWorkspace(root, requestedPath);
|
|
338
|
+
const requestedText = readWorkspaceFileBounded(root, requestedRelativePath);
|
|
339
|
+
const requestedReceipt = {
|
|
340
|
+
absolutePath: requestedPath,
|
|
341
|
+
bytes: Buffer.from(requestedText, "utf8"),
|
|
342
|
+
};
|
|
343
|
+
const receipt = parseJsonStrict(decodeUtf8Strict(requestedReceipt.bytes));
|
|
344
|
+
const attested = attestCoreBoundQuickReceiptDetailsFromBytes({
|
|
345
|
+
receiptPath: requestedReceipt.absolutePath,
|
|
346
|
+
receiptBytes: requestedReceipt.bytes,
|
|
347
|
+
workspaceRoot: root,
|
|
348
|
+
});
|
|
349
|
+
if (attested.attestation.decision !== "GO") {
|
|
350
|
+
throw new Error(
|
|
351
|
+
`Formal supplemental Quick attestation must be GO: ${attested.attestation.issueCodes.join(",")}.`,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
const { latest, candidate } = latestCurrentCandidate({
|
|
355
|
+
workspaceRoot: root,
|
|
356
|
+
executionRoot,
|
|
357
|
+
runId,
|
|
358
|
+
});
|
|
359
|
+
if (!quickReceiptMatchesCandidate(receipt, candidate)) {
|
|
360
|
+
throw new Error("Formal supplemental Quick receipt does not describe the current source candidate.");
|
|
361
|
+
}
|
|
362
|
+
const frozenMaterials = frozenQuickMaterials({
|
|
363
|
+
workspaceRoot: root,
|
|
364
|
+
receiptPath: requestedReceipt.absolutePath,
|
|
365
|
+
attestation: attested.attestation,
|
|
366
|
+
});
|
|
367
|
+
if (frozenMaterials.length === 0) {
|
|
368
|
+
throw new Error("Formal supplemental evidence requires frozen Quick receipt materials.");
|
|
369
|
+
}
|
|
370
|
+
const body = {
|
|
371
|
+
schemaVersion: SUPPLEMENTAL_SCHEMA,
|
|
372
|
+
runId,
|
|
373
|
+
evidenceId,
|
|
374
|
+
kind,
|
|
375
|
+
classificationBasis: "operator_declared_not_attested",
|
|
376
|
+
status: "attached",
|
|
377
|
+
gating: false,
|
|
378
|
+
sourceBinding: {
|
|
379
|
+
candidatePath: latest.candidatePath,
|
|
380
|
+
candidateSha256: candidate.candidateSha256,
|
|
381
|
+
sourceFingerprint: latest.sourceFingerprint,
|
|
382
|
+
quickWorkspaceFingerprint: attested.sourceFingerprint,
|
|
383
|
+
},
|
|
384
|
+
quickEvidence: {
|
|
385
|
+
receiptId: receipt.receiptId,
|
|
386
|
+
receiptPath: requestedRelativePath,
|
|
387
|
+
receiptSha256: attested.attestation.subjectRef.receiptSha256,
|
|
388
|
+
receiptSchemaVersion: receipt.schemaVersion,
|
|
389
|
+
exactCommand: structuredClone(receipt.exactCommand),
|
|
390
|
+
attestationDecision: attested.attestation.decision,
|
|
391
|
+
attestationIssueCodes: [...attested.attestation.issueCodes],
|
|
392
|
+
},
|
|
393
|
+
limitations: [
|
|
394
|
+
"supplemental_evidence_is_not_formal_sandbox_evidence",
|
|
395
|
+
"network_classification_is_operator_declared_not_attested",
|
|
396
|
+
"supplemental_evidence_cannot_grant_acceptance_or_authority",
|
|
397
|
+
],
|
|
398
|
+
frozenMaterials,
|
|
399
|
+
authorizationGranted: false,
|
|
400
|
+
};
|
|
401
|
+
const value = {
|
|
402
|
+
...body,
|
|
403
|
+
supplementalEvidenceSha256: sha256(canonical(body)),
|
|
404
|
+
};
|
|
405
|
+
const evidenceRoot = supplementalRoot(executionRoot);
|
|
406
|
+
ensureRegularDirectory(evidenceRoot, "Formal supplemental evidence root");
|
|
407
|
+
const artifactPath = path.join(evidenceRoot, `${evidenceId}.json`);
|
|
408
|
+
const written = writeOrResume(artifactPath, value, "Formal supplemental evidence");
|
|
409
|
+
return {
|
|
410
|
+
status: "formal_supplemental_evidence_attached",
|
|
411
|
+
exitCode: 0,
|
|
412
|
+
runId,
|
|
413
|
+
evidenceId,
|
|
414
|
+
kind,
|
|
415
|
+
sourceFingerprint: latest.sourceFingerprint,
|
|
416
|
+
quickWorkspaceFingerprint: attested.sourceFingerprint,
|
|
417
|
+
evidencePath: relativeToWorkspace(root, artifactPath),
|
|
418
|
+
supplementalEvidenceSha256: written.value.supplementalEvidenceSha256,
|
|
419
|
+
gating: false,
|
|
420
|
+
resumed: written.resumed,
|
|
421
|
+
authorizationGranted: false,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export function attachFormalSupplementalEvidenceV1(options) {
|
|
426
|
+
const root = realpathSync(options.workspaceRoot);
|
|
427
|
+
return withControlTransaction(root, () => attachWithinControlTransaction({
|
|
428
|
+
...options,
|
|
429
|
+
workspaceRoot: root,
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export function summarizeFormalEvidenceWithinControlTransaction({
|
|
434
|
+
workspaceRoot,
|
|
435
|
+
runId,
|
|
436
|
+
summaryId,
|
|
437
|
+
}) {
|
|
438
|
+
safeIdentifier(summaryId, "summaryId");
|
|
439
|
+
const root = realpathSync(workspaceRoot);
|
|
440
|
+
const { executionRoot, pinGate } = loadActiveExecution(root, runId);
|
|
441
|
+
if (pinGate.status !== "valid") return { ...pinGate, authorizationGranted: false };
|
|
442
|
+
const { checks, latest, candidate } = latestCurrentCandidate({
|
|
443
|
+
workspaceRoot: root,
|
|
444
|
+
executionRoot,
|
|
445
|
+
runId,
|
|
446
|
+
});
|
|
447
|
+
const allSupplemental = readSupplementals(root, executionRoot, runId);
|
|
448
|
+
const currentSupplemental = allSupplemental.filter(row => (
|
|
449
|
+
row.runId === runId
|
|
450
|
+
&& row.sourceBinding.candidatePath === latest.candidatePath
|
|
451
|
+
&& row.sourceBinding.sourceFingerprint === latest.sourceFingerprint
|
|
452
|
+
&& row.sourceBinding.candidateSha256 === candidate.candidateSha256
|
|
453
|
+
));
|
|
454
|
+
for (const row of currentSupplemental) {
|
|
455
|
+
const receiptText = readWorkspaceFileBounded(
|
|
456
|
+
root,
|
|
457
|
+
row.quickEvidence.receiptPath,
|
|
458
|
+
);
|
|
459
|
+
const quickReceipt = {
|
|
460
|
+
absolutePath: path.join(root, row.quickEvidence.receiptPath),
|
|
461
|
+
bytes: Buffer.from(receiptText, "utf8"),
|
|
462
|
+
};
|
|
463
|
+
const receipt = parseJsonStrict(decodeUtf8Strict(quickReceipt.bytes));
|
|
464
|
+
const attested = attestCoreBoundQuickReceiptDetailsFromBytes({
|
|
465
|
+
receiptPath: quickReceipt.absolutePath,
|
|
466
|
+
receiptBytes: quickReceipt.bytes,
|
|
467
|
+
workspaceRoot: root,
|
|
468
|
+
});
|
|
469
|
+
if (
|
|
470
|
+
attested.attestation.decision !== "GO"
|
|
471
|
+
|| attested.attestation.subjectRef.receiptSha256
|
|
472
|
+
!== row.quickEvidence.receiptSha256
|
|
473
|
+
|| attested.sourceFingerprint
|
|
474
|
+
!== row.sourceBinding.quickWorkspaceFingerprint
|
|
475
|
+
|| receipt.receiptId !== row.quickEvidence.receiptId
|
|
476
|
+
|| receipt.schemaVersion !== row.quickEvidence.receiptSchemaVersion
|
|
477
|
+
|| canonical(receipt.exactCommand) !== canonical(row.quickEvidence.exactCommand)
|
|
478
|
+
|| canonical(attested.attestation.issueCodes)
|
|
479
|
+
!== canonical(row.quickEvidence.attestationIssueCodes)
|
|
480
|
+
|| !quickReceiptMatchesCandidate(receipt, candidate)
|
|
481
|
+
) {
|
|
482
|
+
throw new Error(
|
|
483
|
+
`Formal supplemental Quick attestation must remain GO: ${row.evidenceId}.`,
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const currentChecks = checks.filter(row => (
|
|
488
|
+
row.sourceFingerprint === latest.sourceFingerprint
|
|
489
|
+
&& row.verificationContextFingerprint
|
|
490
|
+
=== latest.verificationContextFingerprint
|
|
491
|
+
));
|
|
492
|
+
const body = {
|
|
493
|
+
schemaVersion: SUMMARY_SCHEMA,
|
|
494
|
+
runId,
|
|
495
|
+
summaryId,
|
|
496
|
+
sourceBinding: {
|
|
497
|
+
candidatePath: latest.candidatePath,
|
|
498
|
+
candidateSha256: candidate.candidateSha256,
|
|
499
|
+
sourceFingerprint: latest.sourceFingerprint,
|
|
500
|
+
},
|
|
501
|
+
formalChecks: currentChecks.map(row => ({
|
|
502
|
+
checkId: row.checkId,
|
|
503
|
+
status: row.status,
|
|
504
|
+
failureCategory: row.failureCategory ?? null,
|
|
505
|
+
formalEligible: row.status === "formal_check_passed",
|
|
506
|
+
snapshotPath: row.snapshotPath,
|
|
507
|
+
envelopeReceiptPath: row.envelopeReceiptPath ?? null,
|
|
508
|
+
})),
|
|
509
|
+
supplementalEvidence: currentSupplemental.map(row => ({
|
|
510
|
+
evidenceId: row.evidenceId,
|
|
511
|
+
kind: row.kind,
|
|
512
|
+
classificationBasis: row.classificationBasis,
|
|
513
|
+
evidencePath: relativeToWorkspace(
|
|
514
|
+
root,
|
|
515
|
+
path.join(supplementalRoot(executionRoot), `${row.evidenceId}.json`),
|
|
516
|
+
),
|
|
517
|
+
supplementalEvidenceSha256: row.supplementalEvidenceSha256,
|
|
518
|
+
gating: false,
|
|
519
|
+
})),
|
|
520
|
+
staleFormalCheckCount: checks.length - currentChecks.length,
|
|
521
|
+
staleSupplementalEvidenceCount: allSupplemental.length - currentSupplemental.length,
|
|
522
|
+
formalAcceptanceDerivedOnlyFromFormalChecks: true,
|
|
523
|
+
limitations: [
|
|
524
|
+
"supplemental_evidence_is_non_gating",
|
|
525
|
+
"network_classification_is_operator_declared_not_attested",
|
|
526
|
+
"source_acceptance_does_not_grant_git_release_deployment_or_business_authority",
|
|
527
|
+
],
|
|
528
|
+
authorizationGranted: false,
|
|
529
|
+
};
|
|
530
|
+
const value = { ...body, evidenceSummarySha256: sha256(canonical(body)) };
|
|
531
|
+
const artifactPath = path.join(
|
|
532
|
+
executionRoot,
|
|
533
|
+
"formal-evidence-summaries",
|
|
534
|
+
`${summaryId}.json`,
|
|
535
|
+
);
|
|
536
|
+
ensureRegularDirectory(
|
|
537
|
+
path.dirname(artifactPath),
|
|
538
|
+
"Formal evidence summary root",
|
|
539
|
+
);
|
|
540
|
+
const written = writeOrResume(artifactPath, value, "Formal evidence summary");
|
|
541
|
+
return {
|
|
542
|
+
status: "formal_evidence_summarized",
|
|
543
|
+
exitCode: 0,
|
|
544
|
+
runId,
|
|
545
|
+
evidenceSummaryPath: relativeToWorkspace(root, artifactPath),
|
|
546
|
+
evidenceSummarySha256: written.value.evidenceSummarySha256,
|
|
547
|
+
formalCheckCount: written.value.formalChecks.length,
|
|
548
|
+
supplementalEvidenceCount: written.value.supplementalEvidence.length,
|
|
549
|
+
staleSupplementalEvidenceCount: written.value.staleSupplementalEvidenceCount,
|
|
550
|
+
resumed: written.resumed,
|
|
551
|
+
authorizationGranted: false,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export function summarizeFormalEvidenceV1(options) {
|
|
556
|
+
const root = realpathSync(options.workspaceRoot);
|
|
557
|
+
return withControlTransaction(root, () => summarizeFormalEvidenceWithinControlTransaction({
|
|
558
|
+
...options,
|
|
559
|
+
workspaceRoot: root,
|
|
560
|
+
}));
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function validateSummaryDocument(value, {
|
|
564
|
+
workspaceRoot,
|
|
565
|
+
runId,
|
|
566
|
+
summaryId,
|
|
567
|
+
}) {
|
|
568
|
+
const { evidenceSummarySha256, ...body } = value ?? {};
|
|
569
|
+
if (
|
|
570
|
+
!exactKeys(value, [
|
|
571
|
+
"schemaVersion",
|
|
572
|
+
"runId",
|
|
573
|
+
"summaryId",
|
|
574
|
+
"sourceBinding",
|
|
575
|
+
"formalChecks",
|
|
576
|
+
"supplementalEvidence",
|
|
577
|
+
"staleFormalCheckCount",
|
|
578
|
+
"staleSupplementalEvidenceCount",
|
|
579
|
+
"formalAcceptanceDerivedOnlyFromFormalChecks",
|
|
580
|
+
"limitations",
|
|
581
|
+
"authorizationGranted",
|
|
582
|
+
"evidenceSummarySha256",
|
|
583
|
+
])
|
|
584
|
+
|| value.schemaVersion !== SUMMARY_SCHEMA
|
|
585
|
+
|| value.runId !== runId
|
|
586
|
+
|| value.summaryId !== summaryId
|
|
587
|
+
|| evidenceSummarySha256 !== sha256(canonical(body))
|
|
588
|
+
|| !exactKeys(value.sourceBinding, [
|
|
589
|
+
"candidatePath", "candidateSha256", "sourceFingerprint",
|
|
590
|
+
])
|
|
591
|
+
|| !validHash(value.sourceBinding.candidateSha256, true)
|
|
592
|
+
|| !validHash(value.sourceBinding.sourceFingerprint)
|
|
593
|
+
|| !Array.isArray(value.formalChecks)
|
|
594
|
+
|| !Array.isArray(value.supplementalEvidence)
|
|
595
|
+
|| !Number.isSafeInteger(value.staleFormalCheckCount)
|
|
596
|
+
|| value.staleFormalCheckCount < 0
|
|
597
|
+
|| !Number.isSafeInteger(value.staleSupplementalEvidenceCount)
|
|
598
|
+
|| value.staleSupplementalEvidenceCount < 0
|
|
599
|
+
|| value.formalAcceptanceDerivedOnlyFromFormalChecks !== true
|
|
600
|
+
|| !Array.isArray(value.limitations)
|
|
601
|
+
|| value.limitations.length === 0
|
|
602
|
+
|| value.limitations.some(limit => typeof limit !== "string" || limit.length === 0)
|
|
603
|
+
|| value.authorizationGranted !== false
|
|
604
|
+
) {
|
|
605
|
+
throw new Error("Formal evidence summary contract is invalid.");
|
|
606
|
+
}
|
|
607
|
+
for (const check of value.formalChecks) {
|
|
608
|
+
if (
|
|
609
|
+
!exactKeys(check, [
|
|
610
|
+
"checkId",
|
|
611
|
+
"status",
|
|
612
|
+
"failureCategory",
|
|
613
|
+
"formalEligible",
|
|
614
|
+
"snapshotPath",
|
|
615
|
+
"envelopeReceiptPath",
|
|
616
|
+
])
|
|
617
|
+
|| typeof check.checkId !== "string"
|
|
618
|
+
|| !new Set(["formal_check_passed", "formal_check_failed"]).has(check.status)
|
|
619
|
+
|| typeof check.snapshotPath !== "string"
|
|
620
|
+
|| !(check.envelopeReceiptPath === null
|
|
621
|
+
|| typeof check.envelopeReceiptPath === "string")
|
|
622
|
+
|| (check.status === "formal_check_passed"
|
|
623
|
+
? check.formalEligible !== true || check.failureCategory !== null
|
|
624
|
+
: check.formalEligible !== false
|
|
625
|
+
|| !new Set([
|
|
626
|
+
"source_check_failed",
|
|
627
|
+
"envelope_capability_missing",
|
|
628
|
+
"toolchain_resolution_failed",
|
|
629
|
+
"network_policy_blocked",
|
|
630
|
+
]).has(check.failureCategory))
|
|
631
|
+
) {
|
|
632
|
+
throw new Error("Formal evidence summary check contract is invalid.");
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
for (const row of value.supplementalEvidence) {
|
|
636
|
+
if (!exactKeys(row, [
|
|
637
|
+
"evidenceId",
|
|
638
|
+
"kind",
|
|
639
|
+
"classificationBasis",
|
|
640
|
+
"evidencePath",
|
|
641
|
+
"supplementalEvidenceSha256",
|
|
642
|
+
"gating",
|
|
643
|
+
])) {
|
|
644
|
+
throw new Error("Formal evidence summary supplemental row is invalid.");
|
|
645
|
+
}
|
|
646
|
+
safeIdentifier(row.evidenceId, "evidenceId");
|
|
647
|
+
const expectedPath = `.owlcoda/runkit/executions/${runId}`
|
|
648
|
+
+ `/formal-supplemental-evidence/${row.evidenceId}.json`;
|
|
649
|
+
if (
|
|
650
|
+
row.evidencePath !== expectedPath
|
|
651
|
+
|| !KINDS.has(row.kind)
|
|
652
|
+
|| row.classificationBasis !== "operator_declared_not_attested"
|
|
653
|
+
|| !validHash(row.supplementalEvidenceSha256)
|
|
654
|
+
|| row.gating !== false
|
|
655
|
+
) {
|
|
656
|
+
throw new Error("Formal evidence summary supplemental row is invalid.");
|
|
657
|
+
}
|
|
658
|
+
const artifact = readWorkspaceJsonBounded(workspaceRoot, row.evidencePath);
|
|
659
|
+
if (
|
|
660
|
+
!validateSupplemental(artifact, {
|
|
661
|
+
runId,
|
|
662
|
+
evidenceId: row.evidenceId,
|
|
663
|
+
workspaceRoot,
|
|
664
|
+
})
|
|
665
|
+
|| artifact.supplementalEvidenceSha256
|
|
666
|
+
!== row.supplementalEvidenceSha256
|
|
667
|
+
) {
|
|
668
|
+
throw new Error("Formal evidence summary supplemental binding is invalid.");
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return value;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export function readFormalEvidenceSummaryV1({
|
|
675
|
+
workspaceRoot,
|
|
676
|
+
runId,
|
|
677
|
+
summaryId,
|
|
678
|
+
}) {
|
|
679
|
+
safeIdentifier(runId, "runId");
|
|
680
|
+
safeIdentifier(summaryId, "summaryId");
|
|
681
|
+
const root = realpathSync(workspaceRoot);
|
|
682
|
+
const artifactPath = path.join(
|
|
683
|
+
root,
|
|
684
|
+
".owlcoda",
|
|
685
|
+
"runkit",
|
|
686
|
+
"executions",
|
|
687
|
+
runId,
|
|
688
|
+
"formal-evidence-summaries",
|
|
689
|
+
`${summaryId}.json`,
|
|
690
|
+
);
|
|
691
|
+
if (!existsSync(artifactPath)) return null;
|
|
692
|
+
const value = validateSummaryDocument(
|
|
693
|
+
readWorkspaceJsonBounded(
|
|
694
|
+
root,
|
|
695
|
+
relativeToWorkspace(root, artifactPath),
|
|
696
|
+
),
|
|
697
|
+
{ workspaceRoot: root, runId, summaryId },
|
|
698
|
+
);
|
|
699
|
+
const { evidenceSummarySha256 } = value;
|
|
700
|
+
return {
|
|
701
|
+
evidenceSummaryPath: relativeToWorkspace(root, artifactPath),
|
|
702
|
+
evidenceSummarySha256,
|
|
703
|
+
formalCheckCount: value.formalChecks.length,
|
|
704
|
+
supplementalEvidenceCount: value.supplementalEvidence.length,
|
|
705
|
+
staleSupplementalEvidenceCount: value.staleSupplementalEvidenceCount,
|
|
706
|
+
summaryDocument: value,
|
|
707
|
+
authorizationGranted: false,
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
export function reattestFormalEvidenceSummaryV1(options) {
|
|
712
|
+
return readFormalEvidenceSummaryV1(options);
|
|
713
|
+
}
|