@kungfu-tech/buildchain 4.0.1-alpha.2 → 4.0.1-alpha.3
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/architecture/decisions/0003-two-phase-delivery-warrant.md +152 -0
- package/architecture/internal-capabilities.json +94 -0
- package/architecture/maintainability-policy.json +75 -8
- package/architecture/v3-core-mechanism-inventory.json +2 -0
- package/architecture/v4-delivery-authority-parity.json +250 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +29 -6
- package/bin/buildchain.mjs +9 -1
- package/contracts/dev-delivery-authority-v2.schema.json +662 -0
- package/dist/site/agent-index.json +1 -0
- package/dist/site/artifact-schemas.json +2 -0
- package/dist/site/buildchain-contract.json +89 -6
- package/dist/site/buildchain-site.json +169 -18
- package/dist/site/capability-registry.json +2 -2
- package/dist/site/cli-registry.json +46 -2
- package/dist/site/kfd-claims.json +43 -9
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +3 -3
- package/dist/site/node-api-registry.json +1694 -165
- package/dist/site/page-registry.json +162 -11
- package/dist/site/public-surface-audit.json +367 -7
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +2 -0
- package/dist/site/schemas/dev-delivery-authority-v2.schema.json +662 -0
- package/dist/site/site-manifest.json +7 -7
- package/dist/site/workflow-registry.json +22 -6
- package/docs/MAP.md +1 -0
- package/docs/cli-reference.md +211 -13
- package/docs/dev-delivery-qualification-landing-adr.md +251 -0
- package/docs/dev-delivery-warrant.md +326 -38
- package/docs/node-api-reference.md +113 -53
- package/package.json +5 -1
- package/packages/core/buildchain-contract.js +3 -2
- package/packages/core/dev-delivery-authority-candidate.js +270 -0
- package/packages/core/dev-delivery-authority-evidence.js +146 -0
- package/packages/core/dev-delivery-authority-landing.js +461 -0
- package/packages/core/dev-delivery-authority-observation.js +48 -0
- package/packages/core/dev-delivery-authority-qualification.js +591 -0
- package/packages/core/dev-delivery-authority-settlement.js +213 -0
- package/packages/core/dev-delivery-authority-state.js +583 -0
- package/packages/core/dev-delivery-candidate-identity.js +13 -0
- package/packages/core/dev-delivery-contract-surface.js +76 -0
- package/packages/core/dev-delivery-execution-failure.js +133 -0
- package/packages/core/dev-delivery-execution-transfer.js +572 -0
- package/packages/core/dev-delivery-landing-admission-core.js +119 -0
- package/packages/core/dev-delivery-landing-readback.js +598 -0
- package/packages/core/dev-delivery-landing-terminal-evidence.js +271 -0
- package/packages/core/dev-delivery-landing-testing-port.js +6 -0
- package/packages/core/dev-delivery-native-execution.js +110 -0
- package/packages/core/dev-delivery-native-proof.js +546 -0
- package/packages/core/dev-delivery-process-boundary.js +551 -0
- package/packages/core/dev-delivery-provider-attempt.js +127 -0
- package/packages/core/dev-delivery-provider-heartbeat.js +370 -0
- package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
- package/packages/core/dev-delivery-warrant-qualification.js +145 -0
- package/packages/core/dev-delivery-warrant-settlement.js +237 -36
- package/packages/core/dev-delivery-warrant-state.js +565 -0
- package/packages/core/dev-delivery-warrant.js +320 -368
- package/scripts/buildchain-cli-help.mjs +11 -2
- package/scripts/dev-delivery-authority-command-adapters.mjs +206 -0
- package/scripts/dev-delivery-authority-provider.mjs +28 -0
- package/scripts/dev-delivery-authority.mjs +490 -0
- package/scripts/dev-delivery-native-run.mjs +177 -0
- package/scripts/dev-delivery-process-boundary.mjs +260 -0
- package/scripts/dev-delivery-proof.mjs +67 -2
- package/scripts/dev-delivery-provider-heartbeat.mjs +215 -0
- package/scripts/dev-delivery-two-phase-resume.mjs +345 -0
- package/scripts/dev-delivery-two-phase.mjs +573 -0
- package/scripts/dev-delivery-warrant-options.mjs +266 -0
- package/scripts/dev-delivery-warrant-store.mjs +227 -0
- package/scripts/dev-delivery-warrant.mjs +227 -193
- package/scripts/dev-pr-delivery-warrant.mjs +10 -0
- package/scripts/generate-site-bundle.mjs +26 -4
- package/scripts/npm-publish-transaction.mjs +2 -2
- package/scripts/site-capability-metadata.mjs +12 -0
- package/templates/native-dev-delivery.yml +141 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import {
|
|
2
|
+
devDeliveryClone as clone,
|
|
3
|
+
devDeliveryContentRoot,
|
|
4
|
+
devDeliveryExactRoot as exactRoot,
|
|
5
|
+
devDeliveryExactSha as exactSha,
|
|
6
|
+
devDeliveryProtectedBase as protectedBase,
|
|
7
|
+
devDeliveryRepository as repository,
|
|
8
|
+
devDeliveryText as text,
|
|
9
|
+
devDeliveryTimestamp as timestamp,
|
|
10
|
+
} from "./dev-delivery-common.js";
|
|
11
|
+
import {
|
|
12
|
+
NATIVE_EXECUTION_BINDING_SCHEMA,
|
|
13
|
+
NATIVE_EXECUTION_RECEIPT_SCHEMA,
|
|
14
|
+
NATIVE_COMMAND_CONTRACT_SCHEMA,
|
|
15
|
+
createNativeCommandContract,
|
|
16
|
+
createNativeExecutionBinding,
|
|
17
|
+
createNativeExecutionReceipt,
|
|
18
|
+
normalizeNativeCommandContract,
|
|
19
|
+
verifyNativeExecutionReceipt,
|
|
20
|
+
} from "./dev-delivery-native-execution.js";
|
|
21
|
+
export {
|
|
22
|
+
NATIVE_EXECUTION_BINDING_SCHEMA,
|
|
23
|
+
NATIVE_EXECUTION_RECEIPT_SCHEMA,
|
|
24
|
+
NATIVE_COMMAND_CONTRACT_SCHEMA,
|
|
25
|
+
createNativeCommandContract,
|
|
26
|
+
createNativeExecutionBinding,
|
|
27
|
+
createNativeExecutionReceipt,
|
|
28
|
+
normalizeNativeCommandContract,
|
|
29
|
+
verifyNativeExecutionReceipt,
|
|
30
|
+
};
|
|
31
|
+
export const NATIVE_QUALIFICATION_PROOF_SCHEMA =
|
|
32
|
+
"kungfu.buildchain.native-qualification-proof/v4";
|
|
33
|
+
const LEGACY_NATIVE_QUALIFICATION_PROOF_V3_SCHEMA =
|
|
34
|
+
"kungfu.buildchain.native-qualification-proof/v3";
|
|
35
|
+
const LEGACY_NATIVE_QUALIFICATION_PROOF_V2_SCHEMA =
|
|
36
|
+
"kungfu.buildchain.native-qualification-proof/v2";
|
|
37
|
+
const LEGACY_NATIVE_QUALIFICATION_PROOF_SCHEMA =
|
|
38
|
+
"kungfu.buildchain.native-qualification-proof/v1";
|
|
39
|
+
export const NATIVE_PROOF_REUSE_DECISION_SCHEMA =
|
|
40
|
+
"kungfu.buildchain.native-proof-reuse-decision/v1";
|
|
41
|
+
export const DEV_DELIVERY_QUALIFICATION_RECEIPT_SCHEMA =
|
|
42
|
+
"kungfu.buildchain.dev-delivery-qualification-receipt/v1";
|
|
43
|
+
export const NATIVE_PROOF_BASE_DELTA_SCHEMA =
|
|
44
|
+
"kungfu.buildchain.native-proof-base-delta/v1";
|
|
45
|
+
const NATIVE_QUALIFICATION_ROOT_SEMANTICS = "semantic-native-identity-v1";
|
|
46
|
+
export function validateActiveDevDeliveryWarrant(queue) {
|
|
47
|
+
const warrant = queue.activeWarrant;
|
|
48
|
+
if (warrant.schema !== "kungfu.buildchain.dev-delivery-warrant/v1")
|
|
49
|
+
throw new Error("active Warrant schema is unsupported");
|
|
50
|
+
warrant.candidateId = exactRoot(warrant.candidateId, "Warrant candidateId");
|
|
51
|
+
warrant.fencingToken = exactRoot(
|
|
52
|
+
warrant.fencingToken,
|
|
53
|
+
"Warrant fencingToken",
|
|
54
|
+
);
|
|
55
|
+
warrant.expectedOldStateRoot = exactRoot(
|
|
56
|
+
warrant.expectedOldStateRoot,
|
|
57
|
+
"Warrant expectedOldStateRoot",
|
|
58
|
+
);
|
|
59
|
+
if (
|
|
60
|
+
!Number.isInteger(Number(warrant.generation)) ||
|
|
61
|
+
Number(warrant.generation) < 1
|
|
62
|
+
)
|
|
63
|
+
throw new Error("Warrant generation must be a positive integer");
|
|
64
|
+
warrant.generation = Number(warrant.generation);
|
|
65
|
+
warrant.issuedAt = timestamp(warrant.issuedAt, "Warrant issuedAt");
|
|
66
|
+
warrant.expiresAt = timestamp(warrant.expiresAt, "Warrant expiresAt");
|
|
67
|
+
const historicalPhaseLess = !Object.hasOwn(warrant, "phase");
|
|
68
|
+
const phase = historicalPhaseLess ? "legacy-active" : warrant.phase;
|
|
69
|
+
if (!["legacy-active", "provisional", "qualified"].includes(phase))
|
|
70
|
+
throw new Error("active Warrant phase is unsupported");
|
|
71
|
+
if (warrant.phase === "qualified") {
|
|
72
|
+
warrant.nativeProofRoot = exactRoot(
|
|
73
|
+
warrant.nativeProofRoot,
|
|
74
|
+
"Warrant nativeProofRoot",
|
|
75
|
+
);
|
|
76
|
+
warrant.nativeProofReuseRoot = exactRoot(
|
|
77
|
+
warrant.nativeProofReuseRoot,
|
|
78
|
+
"Warrant nativeProofReuseRoot",
|
|
79
|
+
);
|
|
80
|
+
warrant.nativeExecutionReceiptRoot = exactRoot(
|
|
81
|
+
warrant.nativeExecutionReceiptRoot,
|
|
82
|
+
"Warrant nativeExecutionReceiptRoot",
|
|
83
|
+
);
|
|
84
|
+
warrant.qualificationReceiptRoot = exactRoot(
|
|
85
|
+
warrant.qualificationReceiptRoot,
|
|
86
|
+
"Warrant qualificationReceiptRoot",
|
|
87
|
+
);
|
|
88
|
+
warrant.qualifiedAt = timestamp(warrant.qualifiedAt, "Warrant qualifiedAt");
|
|
89
|
+
} else if (
|
|
90
|
+
warrant.phase === "provisional" &&
|
|
91
|
+
(warrant.nativeProofRoot ||
|
|
92
|
+
warrant.nativeProofReuseRoot ||
|
|
93
|
+
warrant.nativeExecutionReceiptRoot ||
|
|
94
|
+
warrant.qualificationReceiptRoot ||
|
|
95
|
+
warrant.qualifiedAt)
|
|
96
|
+
) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
"provisional Warrant cannot carry qualified native evidence",
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
const active = queue.candidates.filter((candidate) =>
|
|
102
|
+
["selected", "proving", "waiting", "blocked", "qualified"].includes(
|
|
103
|
+
candidate.status,
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
if (active.length !== 1 || active[0].candidateId !== warrant.candidateId)
|
|
107
|
+
throw new Error(
|
|
108
|
+
"exactly one active candidate must match the active Warrant",
|
|
109
|
+
);
|
|
110
|
+
if (
|
|
111
|
+
historicalPhaseLess &&
|
|
112
|
+
(active[0].environmentRoot ||
|
|
113
|
+
warrant.nativeProofRoot ||
|
|
114
|
+
warrant.nativeProofReuseRoot ||
|
|
115
|
+
warrant.qualifiedAt)
|
|
116
|
+
) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
"phase-less active Warrant must use the historical non-native contract",
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
if (phase === "provisional" && active[0].status === "qualified")
|
|
122
|
+
throw new Error("provisional Warrant cannot bind a qualified candidate");
|
|
123
|
+
if (warrant.phase === "qualified" && active[0].status !== "qualified")
|
|
124
|
+
throw new Error("qualified Warrant must bind a qualified candidate");
|
|
125
|
+
if (
|
|
126
|
+
warrant.releaseBlockerPriority?.claimRoot !==
|
|
127
|
+
active[0].releaseBlockerPriority?.claimRoot
|
|
128
|
+
)
|
|
129
|
+
throw new Error("active Warrant release-blocker priority drift");
|
|
130
|
+
if (
|
|
131
|
+
warrant.nativeCommandContract?.commandRoot !==
|
|
132
|
+
active[0].nativeCommandContract?.commandRoot
|
|
133
|
+
)
|
|
134
|
+
throw new Error("active Warrant native command contract drift");
|
|
135
|
+
if (
|
|
136
|
+
JSON.stringify(warrant.shardEvidenceRoots || []) !==
|
|
137
|
+
JSON.stringify(active[0].shardEvidenceRoots || [])
|
|
138
|
+
)
|
|
139
|
+
throw new Error("active Warrant shard evidence roots drift");
|
|
140
|
+
}
|
|
141
|
+
export function createDevDeliveryWarrantToken(input) {
|
|
142
|
+
return devDeliveryContentRoot({
|
|
143
|
+
schema: "kungfu.buildchain.dev-delivery-warrant/v1",
|
|
144
|
+
repository: input.repository,
|
|
145
|
+
protectedBase: input.protectedBase,
|
|
146
|
+
candidateId: input.candidateId,
|
|
147
|
+
phase: input.phase,
|
|
148
|
+
generation: input.generation,
|
|
149
|
+
expectedOldStateRoot: input.expectedOldStateRoot,
|
|
150
|
+
issuedAt: input.issuedAt,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function exactRoots(values, label) {
|
|
154
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
155
|
+
throw new Error(`${label} must contain at least one content root`);
|
|
156
|
+
}
|
|
157
|
+
return [...new Set(values.map((value) => exactRoot(value, label)))].sort();
|
|
158
|
+
}
|
|
159
|
+
function normalizedPaths(values) {
|
|
160
|
+
if (!Array.isArray(values)) throw new Error("paths must be a JSON array");
|
|
161
|
+
return [
|
|
162
|
+
...new Set(
|
|
163
|
+
values
|
|
164
|
+
.map(text)
|
|
165
|
+
.map((entry) => entry.replace(/^\.\//u, "").replace(/\/+$/u, ""))
|
|
166
|
+
.filter(Boolean),
|
|
167
|
+
),
|
|
168
|
+
].sort();
|
|
169
|
+
}
|
|
170
|
+
function normalizedRenames(values) {
|
|
171
|
+
if (!Array.isArray(values)) throw new Error("renames must be a JSON array");
|
|
172
|
+
return values
|
|
173
|
+
.map((entry) => ({
|
|
174
|
+
from: normalizedPaths([entry?.from])[0] || "",
|
|
175
|
+
to: normalizedPaths([entry?.to])[0] || "",
|
|
176
|
+
}))
|
|
177
|
+
.filter((entry) => entry.from && entry.to)
|
|
178
|
+
.sort(
|
|
179
|
+
(left, right) =>
|
|
180
|
+
left.from.localeCompare(right.from) || left.to.localeCompare(right.to),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
export function createNativeProofBaseDelta(input = {}) {
|
|
184
|
+
const body = {
|
|
185
|
+
schema: NATIVE_PROOF_BASE_DELTA_SCHEMA,
|
|
186
|
+
repository: repository(input.repository),
|
|
187
|
+
protectedBase: protectedBase(input.protectedBase),
|
|
188
|
+
qualifiedBase: exactSha(input.qualifiedBase, "qualifiedBase"),
|
|
189
|
+
currentBase: exactSha(input.currentBase, "currentBase"),
|
|
190
|
+
graphKnown: input.graphKnown === true,
|
|
191
|
+
attributionComplete: input.attributionComplete === true,
|
|
192
|
+
changedPaths: normalizedPaths(input.changedPaths || []),
|
|
193
|
+
renames: normalizedRenames(input.renames || []),
|
|
194
|
+
};
|
|
195
|
+
return { ...body, deltaRoot: devDeliveryContentRoot(body) };
|
|
196
|
+
}
|
|
197
|
+
function proofIdentity(body) {
|
|
198
|
+
const identity = clone(body);
|
|
199
|
+
delete identity.qualifiedAt;
|
|
200
|
+
delete identity.observationRoot;
|
|
201
|
+
return identity;
|
|
202
|
+
}
|
|
203
|
+
function proofObservation(body) {
|
|
204
|
+
return { qualifiedAt: body.qualifiedAt };
|
|
205
|
+
}
|
|
206
|
+
function rooted(body) {
|
|
207
|
+
return { ...body, decisionRoot: devDeliveryContentRoot(body) };
|
|
208
|
+
}
|
|
209
|
+
export function createNativeQualificationProof(input = {}) {
|
|
210
|
+
const sourceHead = exactSha(input.sourceHead, "sourceHead");
|
|
211
|
+
const qualifiedBase = exactSha(input.qualifiedBase, "qualifiedBase");
|
|
212
|
+
const toolchainRoot = exactRoot(input.toolchainRoot, "toolchainRoot");
|
|
213
|
+
const environmentRoot = exactRoot(input.environmentRoot, "environmentRoot");
|
|
214
|
+
const nativeCommandRoot = exactRoot(
|
|
215
|
+
input.nativeCommandRoot,
|
|
216
|
+
"nativeCommandRoot",
|
|
217
|
+
);
|
|
218
|
+
const executionReceipt = verifyNativeExecutionReceipt(
|
|
219
|
+
input.nativeExecutionReceipt,
|
|
220
|
+
{
|
|
221
|
+
repository: repository(input.repository),
|
|
222
|
+
protectedBase: protectedBase(input.protectedBase),
|
|
223
|
+
sourceHead,
|
|
224
|
+
qualifiedBase,
|
|
225
|
+
nativeCommandRoot,
|
|
226
|
+
toolchainRoot,
|
|
227
|
+
environmentRoot,
|
|
228
|
+
},
|
|
229
|
+
);
|
|
230
|
+
if (!executionReceipt.ok) {
|
|
231
|
+
throw new Error(
|
|
232
|
+
`native execution receipt rejected: ${executionReceipt.reason}`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
const body = {
|
|
236
|
+
schema: NATIVE_QUALIFICATION_PROOF_SCHEMA,
|
|
237
|
+
rootSemantics: NATIVE_QUALIFICATION_ROOT_SEMANTICS,
|
|
238
|
+
repository: repository(input.repository),
|
|
239
|
+
protectedBase: protectedBase(input.protectedBase),
|
|
240
|
+
sourceIdentityRoot: exactRoot(
|
|
241
|
+
input.sourceIdentityRoot,
|
|
242
|
+
"sourceIdentityRoot",
|
|
243
|
+
),
|
|
244
|
+
sourcePatchRoot: exactRoot(input.sourcePatchRoot, "sourcePatchRoot"),
|
|
245
|
+
planRoot: exactRoot(input.planRoot, "planRoot"),
|
|
246
|
+
closureRoot: exactRoot(input.closureRoot, "closureRoot"),
|
|
247
|
+
dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
|
|
248
|
+
toolchainRoot,
|
|
249
|
+
environmentRoot,
|
|
250
|
+
sourceHead,
|
|
251
|
+
qualifiedBase,
|
|
252
|
+
nativeCommandRoot,
|
|
253
|
+
nativeExecutionBindingRoot: executionReceipt.executionBindingRoot,
|
|
254
|
+
nativeExecutionReceiptRoot: executionReceipt.receiptRoot,
|
|
255
|
+
nativeExecutionReceipt: executionReceipt.receipt,
|
|
256
|
+
affectedPaths: normalizedPaths(input.affectedPaths || []),
|
|
257
|
+
shardEvidenceRoots: exactRoots(
|
|
258
|
+
[...(input.shardEvidenceRoots || []), executionReceipt.receiptRoot],
|
|
259
|
+
"shardEvidenceRoots",
|
|
260
|
+
),
|
|
261
|
+
qualifiedAt: timestamp(input.qualifiedAt, "qualifiedAt"),
|
|
262
|
+
};
|
|
263
|
+
body.observationRoot = devDeliveryContentRoot(proofObservation(body));
|
|
264
|
+
return {
|
|
265
|
+
...body,
|
|
266
|
+
proofRoot: devDeliveryContentRoot(proofIdentity(body)),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
export function verifyNativeQualificationProof(proofInput, expected = {}) {
|
|
270
|
+
try {
|
|
271
|
+
const proof = clone(proofInput || {});
|
|
272
|
+
if (
|
|
273
|
+
![
|
|
274
|
+
NATIVE_QUALIFICATION_PROOF_SCHEMA,
|
|
275
|
+
LEGACY_NATIVE_QUALIFICATION_PROOF_V3_SCHEMA,
|
|
276
|
+
LEGACY_NATIVE_QUALIFICATION_PROOF_V2_SCHEMA,
|
|
277
|
+
LEGACY_NATIVE_QUALIFICATION_PROOF_SCHEMA,
|
|
278
|
+
].includes(proof.schema)
|
|
279
|
+
) {
|
|
280
|
+
return { ok: false, reason: "unsupported-schema" };
|
|
281
|
+
}
|
|
282
|
+
const proofRoot = proof.proofRoot;
|
|
283
|
+
delete proof.proofRoot;
|
|
284
|
+
if (proof.rootSemantics !== NATIVE_QUALIFICATION_ROOT_SEMANTICS) {
|
|
285
|
+
return { ok: false, reason: "unsupported-root-semantics" };
|
|
286
|
+
}
|
|
287
|
+
if (devDeliveryContentRoot(proofIdentity(proof)) !== proofRoot) {
|
|
288
|
+
return { ok: false, reason: "proof-root-drift" };
|
|
289
|
+
}
|
|
290
|
+
if (
|
|
291
|
+
devDeliveryContentRoot(proofObservation(proof)) !== proof.observationRoot
|
|
292
|
+
) {
|
|
293
|
+
return { ok: false, reason: "observation-root-drift" };
|
|
294
|
+
}
|
|
295
|
+
for (const [field, value] of Object.entries(expected)) {
|
|
296
|
+
if (value !== undefined && proof[field] !== value) {
|
|
297
|
+
return { ok: false, reason: `${field}-mismatch` };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (proof.schema === NATIVE_QUALIFICATION_PROOF_SCHEMA) {
|
|
301
|
+
repository(proof.repository);
|
|
302
|
+
protectedBase(proof.protectedBase);
|
|
303
|
+
exactSha(proof.sourceHead, "sourceHead");
|
|
304
|
+
exactSha(proof.qualifiedBase, "qualifiedBase");
|
|
305
|
+
for (const field of [
|
|
306
|
+
"sourceIdentityRoot",
|
|
307
|
+
"sourcePatchRoot",
|
|
308
|
+
"planRoot",
|
|
309
|
+
"closureRoot",
|
|
310
|
+
"dependencyRoot",
|
|
311
|
+
"toolchainRoot",
|
|
312
|
+
"environmentRoot",
|
|
313
|
+
"nativeCommandRoot",
|
|
314
|
+
"nativeExecutionBindingRoot",
|
|
315
|
+
"nativeExecutionReceiptRoot",
|
|
316
|
+
]) {
|
|
317
|
+
exactRoot(proof[field], field);
|
|
318
|
+
}
|
|
319
|
+
const expectedBindingRoot = devDeliveryContentRoot(
|
|
320
|
+
createNativeExecutionBinding(proof),
|
|
321
|
+
);
|
|
322
|
+
if (proof.nativeExecutionBindingRoot !== expectedBindingRoot) {
|
|
323
|
+
return { ok: false, reason: "native-execution-binding-root-drift" };
|
|
324
|
+
}
|
|
325
|
+
const shardEvidenceRoots = exactRoots(
|
|
326
|
+
proof.shardEvidenceRoots,
|
|
327
|
+
"shardEvidenceRoots",
|
|
328
|
+
);
|
|
329
|
+
if (!shardEvidenceRoots.includes(proof.nativeExecutionReceiptRoot)) {
|
|
330
|
+
return { ok: false, reason: "native-execution-receipt-unbound" };
|
|
331
|
+
}
|
|
332
|
+
if (!proof.nativeExecutionReceipt) {
|
|
333
|
+
return {
|
|
334
|
+
ok: false,
|
|
335
|
+
reason: "native-execution-receipt-bytes-missing",
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
const executionReceipt = verifyNativeExecutionReceipt(
|
|
339
|
+
proof.nativeExecutionReceipt,
|
|
340
|
+
{
|
|
341
|
+
repository: proof.repository,
|
|
342
|
+
protectedBase: proof.protectedBase,
|
|
343
|
+
sourceHead: proof.sourceHead,
|
|
344
|
+
qualifiedBase: proof.qualifiedBase,
|
|
345
|
+
nativeCommandRoot: proof.nativeCommandRoot,
|
|
346
|
+
toolchainRoot: proof.toolchainRoot,
|
|
347
|
+
environmentRoot: proof.environmentRoot,
|
|
348
|
+
},
|
|
349
|
+
);
|
|
350
|
+
if (!executionReceipt.ok) {
|
|
351
|
+
return {
|
|
352
|
+
ok: false,
|
|
353
|
+
reason: `native-execution-${executionReceipt.reason}`,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
if (
|
|
357
|
+
executionReceipt.receiptRoot !== proof.nativeExecutionReceiptRoot ||
|
|
358
|
+
executionReceipt.executionBindingRoot !==
|
|
359
|
+
proof.nativeExecutionBindingRoot
|
|
360
|
+
) {
|
|
361
|
+
return { ok: false, reason: "native-execution-receipt-root-drift" };
|
|
362
|
+
}
|
|
363
|
+
if (
|
|
364
|
+
Date.parse(proof.qualifiedAt) <
|
|
365
|
+
Date.parse(executionReceipt.receipt.completedAt)
|
|
366
|
+
) {
|
|
367
|
+
return {
|
|
368
|
+
ok: false,
|
|
369
|
+
reason: "native-execution-qualified-before-completion",
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
normalizedPaths(proof.affectedPaths || []);
|
|
373
|
+
timestamp(proof.qualifiedAt, "qualifiedAt");
|
|
374
|
+
} else {
|
|
375
|
+
repository(proof.repository);
|
|
376
|
+
protectedBase(proof.protectedBase);
|
|
377
|
+
exactSha(proof.qualifiedBase, "qualifiedBase");
|
|
378
|
+
for (const field of [
|
|
379
|
+
"sourceIdentityRoot",
|
|
380
|
+
"sourcePatchRoot",
|
|
381
|
+
"planRoot",
|
|
382
|
+
"closureRoot",
|
|
383
|
+
"dependencyRoot",
|
|
384
|
+
"toolchainRoot",
|
|
385
|
+
]) {
|
|
386
|
+
exactRoot(proof[field], field);
|
|
387
|
+
}
|
|
388
|
+
normalizedPaths(proof.affectedPaths || []);
|
|
389
|
+
exactRoots(proof.shardEvidenceRoots, "shardEvidenceRoots");
|
|
390
|
+
timestamp(proof.qualifiedAt, "qualifiedAt");
|
|
391
|
+
}
|
|
392
|
+
return { ok: true, reason: "exact-native-proof", proofRoot };
|
|
393
|
+
} catch (error) {
|
|
394
|
+
return { ok: false, reason: "invalid-proof", error: error.message };
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function pathsOverlap(changedPath, affectedPath) {
|
|
398
|
+
if (affectedPath === "*" || affectedPath === "**") return true;
|
|
399
|
+
return (
|
|
400
|
+
changedPath === affectedPath ||
|
|
401
|
+
changedPath.startsWith(`${affectedPath}/`) ||
|
|
402
|
+
affectedPath.startsWith(`${changedPath}/`)
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
function nativeReuseBody(proof, current) {
|
|
406
|
+
const verification = verifyNativeQualificationProof(proof);
|
|
407
|
+
const currentBase = current.currentBase
|
|
408
|
+
? exactSha(current.currentBase, "currentBase")
|
|
409
|
+
: null;
|
|
410
|
+
const changedPaths = normalizedPaths(current.changedPaths || []);
|
|
411
|
+
const base = {
|
|
412
|
+
schema: NATIVE_PROOF_REUSE_DECISION_SCHEMA,
|
|
413
|
+
proofRoot: verification.ok ? proof.proofRoot : null,
|
|
414
|
+
repository: verification.ok ? proof.repository : null,
|
|
415
|
+
protectedBase: verification.ok ? proof.protectedBase : null,
|
|
416
|
+
qualifiedBase: verification.ok ? proof.qualifiedBase : null,
|
|
417
|
+
currentBase,
|
|
418
|
+
graphKnown: current.graphKnown === true,
|
|
419
|
+
attributionComplete: current.attributionComplete === true,
|
|
420
|
+
changedPaths,
|
|
421
|
+
renames: normalizedRenames(current.renames || []),
|
|
422
|
+
baseDeltaRoot: null,
|
|
423
|
+
overlappingPaths: [],
|
|
424
|
+
reusable: false,
|
|
425
|
+
action: "rerun-full-native-qualification",
|
|
426
|
+
reason: verification.reason,
|
|
427
|
+
requiredValidation: "full-native",
|
|
428
|
+
};
|
|
429
|
+
if (!verification.ok) return base;
|
|
430
|
+
if (proof.schema !== NATIVE_QUALIFICATION_PROOF_SCHEMA) {
|
|
431
|
+
return {
|
|
432
|
+
...base,
|
|
433
|
+
reason: "native-execution-evidence-unbound",
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
const semanticFields = [
|
|
437
|
+
"sourceHead",
|
|
438
|
+
"sourceIdentityRoot",
|
|
439
|
+
"sourcePatchRoot",
|
|
440
|
+
"planRoot",
|
|
441
|
+
"closureRoot",
|
|
442
|
+
"dependencyRoot",
|
|
443
|
+
"toolchainRoot",
|
|
444
|
+
"environmentRoot",
|
|
445
|
+
"nativeCommandRoot",
|
|
446
|
+
];
|
|
447
|
+
for (const field of semanticFields) {
|
|
448
|
+
if (!current[field] || current[field] !== proof[field]) {
|
|
449
|
+
return {
|
|
450
|
+
...base,
|
|
451
|
+
reason: `${field}-changed-or-unknown`,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (!currentBase) {
|
|
456
|
+
return { ...base, reason: "current-base-unknown" };
|
|
457
|
+
}
|
|
458
|
+
const delta = createNativeProofBaseDelta({
|
|
459
|
+
repository: proof.repository,
|
|
460
|
+
protectedBase: proof.protectedBase,
|
|
461
|
+
qualifiedBase: proof.qualifiedBase,
|
|
462
|
+
currentBase,
|
|
463
|
+
graphKnown: current.graphKnown,
|
|
464
|
+
attributionComplete: current.attributionComplete,
|
|
465
|
+
changedPaths,
|
|
466
|
+
renames: current.renames || [],
|
|
467
|
+
});
|
|
468
|
+
base.baseDeltaRoot = delta.deltaRoot;
|
|
469
|
+
base.renames = delta.renames;
|
|
470
|
+
if (currentBase === proof.qualifiedBase) {
|
|
471
|
+
return {
|
|
472
|
+
...base,
|
|
473
|
+
reusable: true,
|
|
474
|
+
action: "accept-fresh-native-proof",
|
|
475
|
+
reason: "exact-qualified-base",
|
|
476
|
+
requiredValidation: "exact-merge-group",
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
if (
|
|
480
|
+
current.graphKnown !== true ||
|
|
481
|
+
current.attributionComplete !== true ||
|
|
482
|
+
!Array.isArray(current.changedPaths) ||
|
|
483
|
+
!Array.isArray(current.renames)
|
|
484
|
+
) {
|
|
485
|
+
return { ...base, reason: "base-delta-attribution-unknown" };
|
|
486
|
+
}
|
|
487
|
+
if (proof.affectedPaths.length === 0) {
|
|
488
|
+
return { ...base, reason: "affected-closure-paths-unknown" };
|
|
489
|
+
}
|
|
490
|
+
const attributedPaths = [
|
|
491
|
+
...changedPaths,
|
|
492
|
+
...base.renames.flatMap((rename) => [rename.from, rename.to]),
|
|
493
|
+
];
|
|
494
|
+
const overlap = [...new Set(attributedPaths)].filter((changedPath) =>
|
|
495
|
+
proof.affectedPaths.some((affectedPath) =>
|
|
496
|
+
pathsOverlap(changedPath, affectedPath),
|
|
497
|
+
),
|
|
498
|
+
);
|
|
499
|
+
if (overlap.length > 0) {
|
|
500
|
+
return {
|
|
501
|
+
...base,
|
|
502
|
+
action: "rerun-affected-native-shards",
|
|
503
|
+
reason: "base-delta-overlaps-affected-closure",
|
|
504
|
+
requiredValidation: "incremental-or-full-native",
|
|
505
|
+
overlappingPaths: overlap,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
return {
|
|
509
|
+
...base,
|
|
510
|
+
reusable: true,
|
|
511
|
+
action: "reuse-native-proof",
|
|
512
|
+
reason: "semantic-source-stable-and-base-delta-disjoint",
|
|
513
|
+
requiredValidation: "exact-merge-group",
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
export function createNativeProofReuseDecision({ proof, current = {} } = {}) {
|
|
517
|
+
return rooted(nativeReuseBody(proof, current));
|
|
518
|
+
}
|
|
519
|
+
export function verifyNativeProofReuseDecision(
|
|
520
|
+
decisionInput,
|
|
521
|
+
{ proof, current = {} } = {},
|
|
522
|
+
) {
|
|
523
|
+
try {
|
|
524
|
+
const decision = clone(decisionInput || {});
|
|
525
|
+
if (decision.schema !== NATIVE_PROOF_REUSE_DECISION_SCHEMA) {
|
|
526
|
+
return { ok: false, reason: "unsupported-schema" };
|
|
527
|
+
}
|
|
528
|
+
const decisionRoot = decision.decisionRoot;
|
|
529
|
+
delete decision.decisionRoot;
|
|
530
|
+
if (devDeliveryContentRoot(decision) !== decisionRoot) {
|
|
531
|
+
return { ok: false, reason: "decision-root-drift" };
|
|
532
|
+
}
|
|
533
|
+
const recomputed = nativeReuseBody(proof, current);
|
|
534
|
+
if (JSON.stringify(decision) !== JSON.stringify(recomputed)) {
|
|
535
|
+
return { ok: false, reason: "decision-input-drift" };
|
|
536
|
+
}
|
|
537
|
+
return {
|
|
538
|
+
ok: decision.reusable === true,
|
|
539
|
+
reason: decision.reason,
|
|
540
|
+
decisionRoot,
|
|
541
|
+
action: decision.action,
|
|
542
|
+
};
|
|
543
|
+
} catch (error) {
|
|
544
|
+
return { ok: false, reason: "invalid-decision", error: error.message };
|
|
545
|
+
}
|
|
546
|
+
}
|