@kungfu-tech/buildchain 3.0.9-alpha.1 → 3.0.9-alpha.11
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/actions/promote-buildchain-ref/README.md +4 -0
- package/bin/buildchain.mjs +9 -1
- package/bin/internal/trust-release-release-handlers.mjs +1 -0
- package/contracts/dev-delivery-authority-v2.schema.json +253 -0
- package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
- package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
- package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
- package/contracts/next-development-request-v1.schema.json +66 -0
- package/contracts/next-development-transition-v1.schema.json +292 -0
- package/dist/site/agent-index.json +1 -0
- package/dist/site/artifact-schemas.json +2 -0
- package/dist/site/buildchain-contract.json +92 -27
- package/dist/site/buildchain-site.json +351 -38
- package/dist/site/capability-registry.json +7 -6
- package/dist/site/cli-registry.json +43 -3
- package/dist/site/controller-registry.json +6 -2
- package/dist/site/kfd-claims.json +223 -12
- package/dist/site/kfd-upstream-aggregate.json +9 -9
- package/dist/site/manual-registry.json +23 -7
- package/dist/site/node-api-registry.json +7926 -4533
- package/dist/site/page-registry.json +332 -27
- package/dist/site/public-surface-audit.json +359 -11
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-passport-check-manifest.json +1 -0
- package/dist/site/release-provenance.json +14 -0
- package/dist/site/schemas/dev-delivery-authority-v2.schema.json +442 -0
- package/dist/site/schemas/release-passport-v1.schema.json +3 -0
- package/dist/site/site-manifest.json +19 -11
- package/dist/site/workflow-registry.json +21 -10
- package/docs/MAP.md +5 -2
- package/docs/adopter-delivery-gate.md +106 -0
- package/docs/aws-us-elastic-runner-burst-plane.md +30 -3
- package/docs/cli-reference.md +202 -15
- package/docs/dev-alpha-candidate-patrol.md +8 -0
- package/docs/dev-delivery-qualification-landing-adr.md +137 -0
- package/docs/dev-delivery-warrant.md +149 -22
- package/docs/kfd-support.md +7 -2
- package/docs/next-development-transition.md +119 -0
- package/docs/node-api-reference.md +402 -204
- package/docs/release-governance.md +10 -7
- package/docs/release-passport.md +18 -2
- package/docs/versioning.md +4 -1
- package/package.json +23 -6
- package/packages/core/README.md +25 -0
- package/packages/core/adopter-delivery-gate.js +576 -0
- package/packages/core/adopter-delivery-json.js +66 -0
- package/packages/core/adopter-delivery-passport.js +259 -0
- package/packages/core/adopter-delivery-vectors.js +158 -0
- package/packages/core/adopter-delivery-vectors.json +77 -0
- package/packages/core/artifact-verification-envelope.js +5 -5
- package/packages/core/buildchain-agent-manuals.js +1 -0
- package/packages/core/buildchain-channel-identity.js +2 -3
- package/packages/core/buildchain-compatibility-proof.js +22 -0
- package/packages/core/buildchain-delivery-bootstrap.js +240 -0
- package/packages/core/buildchain-delivery-infrastructure.js +164 -0
- package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
- package/packages/core/channel-candidate.js +8 -0
- package/packages/core/channel-promotion-baseline.js +52 -0
- package/packages/core/dev-alpha-candidate-selection.js +10 -2
- package/packages/core/dev-delivery-authority.js +996 -0
- package/packages/core/dev-delivery-native-proof.js +418 -0
- package/packages/core/dev-delivery-warrant-settlement.js +67 -20
- package/packages/core/dev-delivery-warrant.js +129 -46
- package/packages/core/kfd-adopter-category-driver.js +167 -0
- package/packages/core/kfd-adopter-manifest.js +49 -50
- package/packages/core/legacy-kfd-adopter-driver.js +167 -0
- package/packages/core/next-development-candidate-reservation.js +186 -0
- package/packages/core/next-development-controller.js +726 -0
- package/packages/core/next-development-projection.js +288 -0
- package/packages/core/next-development-transition.js +738 -0
- package/packages/core/paper-agent-entry.js +7 -4
- package/packages/core/paper.js +14 -7
- package/packages/core/published-delivery-authority.js +247 -0
- package/packages/core/release-passport-contract.js +10 -6
- package/packages/core/release-passport.js +37 -13
- package/scripts/aws-macos-jit-controller-core.mjs +239 -4
- package/scripts/aws-macos-jit-controller.mjs +198 -2
- package/scripts/aws-macos-jit-instance-rehydrate.mjs +260 -0
- package/scripts/aws-macos-jit-source-rebind.mjs +235 -14
- package/scripts/aws-macos-jit.mjs +12 -0
- package/scripts/buildchain-cli-help.mjs +10 -2
- package/scripts/check-internal-architecture.mjs +1 -0
- package/scripts/check-inventory.mjs +12 -1
- package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
- package/scripts/dev-delivery-authority.mjs +368 -0
- package/scripts/dev-delivery-native-run.mjs +235 -0
- package/scripts/dev-delivery-proof.mjs +64 -2
- package/scripts/dev-delivery-two-phase-resume.mjs +90 -0
- package/scripts/dev-delivery-two-phase.mjs +577 -0
- package/scripts/dev-delivery-warrant.mjs +48 -6
- package/scripts/dev-pr-delivery-warrant.mjs +19 -14
- package/scripts/generate-buildchain-kfd-witnesses.mjs +2 -2
- package/scripts/generate-channel-build-workflow.mjs +4 -0
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-site-bundle.mjs +8 -3
- package/scripts/init-repo.mjs +135 -62
- package/scripts/next-development-self-dogfood-harness.mjs +409 -0
- package/scripts/next-development-self-dogfood.mjs +532 -0
- package/scripts/next-development-transition.mjs +47 -0
- package/scripts/release-candidate-tail-reseal.mjs +426 -0
- package/scripts/seal-artifact-signing-requests.mjs +7 -4
- package/scripts/site-capability-metadata.mjs +25 -1
- package/scripts/stable-candidate-qualification.mjs +7 -7
- package/scripts/workflow-call-contract.mjs +1 -1
- package/templates/native-dev-delivery.yml +86 -0
|
@@ -0,0 +1,418 @@
|
|
|
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
|
+
|
|
12
|
+
export const NATIVE_QUALIFICATION_PROOF_SCHEMA =
|
|
13
|
+
"kungfu.buildchain.native-qualification-proof/v2";
|
|
14
|
+
const LEGACY_NATIVE_QUALIFICATION_PROOF_SCHEMA =
|
|
15
|
+
"kungfu.buildchain.native-qualification-proof/v1";
|
|
16
|
+
export const NATIVE_PROOF_REUSE_DECISION_SCHEMA =
|
|
17
|
+
"kungfu.buildchain.native-proof-reuse-decision/v1";
|
|
18
|
+
export const DEV_DELIVERY_QUALIFICATION_RECEIPT_SCHEMA =
|
|
19
|
+
"kungfu.buildchain.dev-delivery-qualification-receipt/v1";
|
|
20
|
+
export const NATIVE_PROOF_BASE_DELTA_SCHEMA =
|
|
21
|
+
"kungfu.buildchain.native-proof-base-delta/v1";
|
|
22
|
+
|
|
23
|
+
const NATIVE_QUALIFICATION_ROOT_SEMANTICS = "semantic-native-identity-v1";
|
|
24
|
+
|
|
25
|
+
function exactRoots(values, label) {
|
|
26
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
27
|
+
throw new Error(`${label} must contain at least one content root`);
|
|
28
|
+
}
|
|
29
|
+
return [...new Set(values.map((value) => exactRoot(value, label)))].sort();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizedPaths(values) {
|
|
33
|
+
if (!Array.isArray(values)) throw new Error("paths must be a JSON array");
|
|
34
|
+
return [
|
|
35
|
+
...new Set(
|
|
36
|
+
values
|
|
37
|
+
.map(text)
|
|
38
|
+
.map((entry) => entry.replace(/^\.\//u, "").replace(/\/+$/u, ""))
|
|
39
|
+
.filter(Boolean),
|
|
40
|
+
),
|
|
41
|
+
].sort();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizedRenames(values) {
|
|
45
|
+
if (!Array.isArray(values)) throw new Error("renames must be a JSON array");
|
|
46
|
+
return values
|
|
47
|
+
.map((entry) => ({
|
|
48
|
+
from: normalizedPaths([entry?.from])[0] || "",
|
|
49
|
+
to: normalizedPaths([entry?.to])[0] || "",
|
|
50
|
+
}))
|
|
51
|
+
.filter((entry) => entry.from && entry.to)
|
|
52
|
+
.sort(
|
|
53
|
+
(left, right) =>
|
|
54
|
+
left.from.localeCompare(right.from) || left.to.localeCompare(right.to),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createNativeProofBaseDelta(input = {}) {
|
|
59
|
+
const body = {
|
|
60
|
+
schema: NATIVE_PROOF_BASE_DELTA_SCHEMA,
|
|
61
|
+
repository: repository(input.repository),
|
|
62
|
+
protectedBase: protectedBase(input.protectedBase),
|
|
63
|
+
qualifiedBase: exactSha(input.qualifiedBase, "qualifiedBase"),
|
|
64
|
+
currentBase: exactSha(input.currentBase, "currentBase"),
|
|
65
|
+
graphKnown: input.graphKnown === true,
|
|
66
|
+
attributionComplete: input.attributionComplete === true,
|
|
67
|
+
changedPaths: normalizedPaths(input.changedPaths || []),
|
|
68
|
+
renames: normalizedRenames(input.renames || []),
|
|
69
|
+
};
|
|
70
|
+
return { ...body, deltaRoot: devDeliveryContentRoot(body) };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function proofIdentity(body) {
|
|
74
|
+
const identity = clone(body);
|
|
75
|
+
delete identity.qualifiedAt;
|
|
76
|
+
delete identity.observationRoot;
|
|
77
|
+
return identity;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function proofObservation(body) {
|
|
81
|
+
return { qualifiedAt: body.qualifiedAt };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function rooted(body) {
|
|
85
|
+
return { ...body, decisionRoot: devDeliveryContentRoot(body) };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function createNativeQualificationProof(input = {}) {
|
|
89
|
+
const body = {
|
|
90
|
+
schema: NATIVE_QUALIFICATION_PROOF_SCHEMA,
|
|
91
|
+
rootSemantics: NATIVE_QUALIFICATION_ROOT_SEMANTICS,
|
|
92
|
+
repository: repository(input.repository),
|
|
93
|
+
protectedBase: protectedBase(input.protectedBase),
|
|
94
|
+
sourceIdentityRoot: exactRoot(
|
|
95
|
+
input.sourceIdentityRoot,
|
|
96
|
+
"sourceIdentityRoot",
|
|
97
|
+
),
|
|
98
|
+
sourcePatchRoot: exactRoot(input.sourcePatchRoot, "sourcePatchRoot"),
|
|
99
|
+
planRoot: exactRoot(input.planRoot, "planRoot"),
|
|
100
|
+
closureRoot: exactRoot(input.closureRoot, "closureRoot"),
|
|
101
|
+
dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
|
|
102
|
+
toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"),
|
|
103
|
+
environmentRoot: exactRoot(input.environmentRoot, "environmentRoot"),
|
|
104
|
+
qualifiedBase: exactSha(input.qualifiedBase, "qualifiedBase"),
|
|
105
|
+
affectedPaths: normalizedPaths(input.affectedPaths || []),
|
|
106
|
+
shardEvidenceRoots: exactRoots(
|
|
107
|
+
input.shardEvidenceRoots,
|
|
108
|
+
"shardEvidenceRoots",
|
|
109
|
+
),
|
|
110
|
+
qualifiedAt: timestamp(input.qualifiedAt, "qualifiedAt"),
|
|
111
|
+
};
|
|
112
|
+
body.observationRoot = devDeliveryContentRoot(proofObservation(body));
|
|
113
|
+
return {
|
|
114
|
+
...body,
|
|
115
|
+
proofRoot: devDeliveryContentRoot(proofIdentity(body)),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function verifyNativeQualificationProof(proofInput, expected = {}) {
|
|
120
|
+
try {
|
|
121
|
+
const proof = clone(proofInput || {});
|
|
122
|
+
if (
|
|
123
|
+
![
|
|
124
|
+
NATIVE_QUALIFICATION_PROOF_SCHEMA,
|
|
125
|
+
LEGACY_NATIVE_QUALIFICATION_PROOF_SCHEMA,
|
|
126
|
+
].includes(proof.schema)
|
|
127
|
+
) {
|
|
128
|
+
return { ok: false, reason: "unsupported-schema" };
|
|
129
|
+
}
|
|
130
|
+
const proofRoot = proof.proofRoot;
|
|
131
|
+
delete proof.proofRoot;
|
|
132
|
+
if (proof.rootSemantics !== NATIVE_QUALIFICATION_ROOT_SEMANTICS) {
|
|
133
|
+
return { ok: false, reason: "unsupported-root-semantics" };
|
|
134
|
+
}
|
|
135
|
+
if (devDeliveryContentRoot(proofIdentity(proof)) !== proofRoot) {
|
|
136
|
+
return { ok: false, reason: "proof-root-drift" };
|
|
137
|
+
}
|
|
138
|
+
if (
|
|
139
|
+
devDeliveryContentRoot(proofObservation(proof)) !== proof.observationRoot
|
|
140
|
+
) {
|
|
141
|
+
return { ok: false, reason: "observation-root-drift" };
|
|
142
|
+
}
|
|
143
|
+
for (const [field, value] of Object.entries(expected)) {
|
|
144
|
+
if (value !== undefined && proof[field] !== value) {
|
|
145
|
+
return { ok: false, reason: `${field}-mismatch` };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (proof.schema === NATIVE_QUALIFICATION_PROOF_SCHEMA) {
|
|
149
|
+
createNativeQualificationProof(proof);
|
|
150
|
+
} else {
|
|
151
|
+
repository(proof.repository);
|
|
152
|
+
protectedBase(proof.protectedBase);
|
|
153
|
+
exactSha(proof.qualifiedBase, "qualifiedBase");
|
|
154
|
+
for (const field of [
|
|
155
|
+
"sourceIdentityRoot",
|
|
156
|
+
"sourcePatchRoot",
|
|
157
|
+
"planRoot",
|
|
158
|
+
"closureRoot",
|
|
159
|
+
"dependencyRoot",
|
|
160
|
+
"toolchainRoot",
|
|
161
|
+
]) {
|
|
162
|
+
exactRoot(proof[field], field);
|
|
163
|
+
}
|
|
164
|
+
normalizedPaths(proof.affectedPaths || []);
|
|
165
|
+
exactRoots(proof.shardEvidenceRoots, "shardEvidenceRoots");
|
|
166
|
+
timestamp(proof.qualifiedAt, "qualifiedAt");
|
|
167
|
+
}
|
|
168
|
+
return { ok: true, reason: "exact-native-proof", proofRoot };
|
|
169
|
+
} catch (error) {
|
|
170
|
+
return { ok: false, reason: "invalid-proof", error: error.message };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function pathsOverlap(changedPath, affectedPath) {
|
|
175
|
+
if (affectedPath === "*" || affectedPath === "**") return true;
|
|
176
|
+
return (
|
|
177
|
+
changedPath === affectedPath ||
|
|
178
|
+
changedPath.startsWith(`${affectedPath}/`) ||
|
|
179
|
+
affectedPath.startsWith(`${changedPath}/`)
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function nativeReuseBody(proof, current) {
|
|
184
|
+
const verification = verifyNativeQualificationProof(proof);
|
|
185
|
+
const currentBase = current.currentBase
|
|
186
|
+
? exactSha(current.currentBase, "currentBase")
|
|
187
|
+
: null;
|
|
188
|
+
const changedPaths = normalizedPaths(current.changedPaths || []);
|
|
189
|
+
const base = {
|
|
190
|
+
schema: NATIVE_PROOF_REUSE_DECISION_SCHEMA,
|
|
191
|
+
proofRoot: verification.ok ? proof.proofRoot : null,
|
|
192
|
+
repository: verification.ok ? proof.repository : null,
|
|
193
|
+
protectedBase: verification.ok ? proof.protectedBase : null,
|
|
194
|
+
qualifiedBase: verification.ok ? proof.qualifiedBase : null,
|
|
195
|
+
currentBase,
|
|
196
|
+
graphKnown: current.graphKnown === true,
|
|
197
|
+
attributionComplete: current.attributionComplete === true,
|
|
198
|
+
changedPaths,
|
|
199
|
+
renames: normalizedRenames(current.renames || []),
|
|
200
|
+
baseDeltaRoot: null,
|
|
201
|
+
overlappingPaths: [],
|
|
202
|
+
reusable: false,
|
|
203
|
+
action: "rerun-full-native-qualification",
|
|
204
|
+
reason: verification.reason,
|
|
205
|
+
requiredValidation: "full-native",
|
|
206
|
+
};
|
|
207
|
+
if (!verification.ok) return base;
|
|
208
|
+
|
|
209
|
+
const semanticFields = [
|
|
210
|
+
"sourceIdentityRoot",
|
|
211
|
+
"sourcePatchRoot",
|
|
212
|
+
"planRoot",
|
|
213
|
+
"closureRoot",
|
|
214
|
+
"dependencyRoot",
|
|
215
|
+
"toolchainRoot",
|
|
216
|
+
"environmentRoot",
|
|
217
|
+
];
|
|
218
|
+
for (const field of semanticFields) {
|
|
219
|
+
if (!current[field] || current[field] !== proof[field]) {
|
|
220
|
+
return {
|
|
221
|
+
...base,
|
|
222
|
+
reason: `${field}-changed-or-unknown`,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (!currentBase) {
|
|
227
|
+
return { ...base, reason: "current-base-unknown" };
|
|
228
|
+
}
|
|
229
|
+
const delta = createNativeProofBaseDelta({
|
|
230
|
+
repository: proof.repository,
|
|
231
|
+
protectedBase: proof.protectedBase,
|
|
232
|
+
qualifiedBase: proof.qualifiedBase,
|
|
233
|
+
currentBase,
|
|
234
|
+
graphKnown: current.graphKnown,
|
|
235
|
+
attributionComplete: current.attributionComplete,
|
|
236
|
+
changedPaths,
|
|
237
|
+
renames: current.renames || [],
|
|
238
|
+
});
|
|
239
|
+
base.baseDeltaRoot = delta.deltaRoot;
|
|
240
|
+
base.renames = delta.renames;
|
|
241
|
+
if (currentBase === proof.qualifiedBase) {
|
|
242
|
+
return {
|
|
243
|
+
...base,
|
|
244
|
+
reusable: true,
|
|
245
|
+
action: "accept-fresh-native-proof",
|
|
246
|
+
reason: "exact-qualified-base",
|
|
247
|
+
requiredValidation: "exact-merge-group",
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (
|
|
251
|
+
current.graphKnown !== true ||
|
|
252
|
+
current.attributionComplete !== true ||
|
|
253
|
+
!Array.isArray(current.changedPaths) ||
|
|
254
|
+
!Array.isArray(current.renames)
|
|
255
|
+
) {
|
|
256
|
+
return { ...base, reason: "base-delta-attribution-unknown" };
|
|
257
|
+
}
|
|
258
|
+
if (proof.affectedPaths.length === 0) {
|
|
259
|
+
return { ...base, reason: "affected-closure-paths-unknown" };
|
|
260
|
+
}
|
|
261
|
+
const attributedPaths = [
|
|
262
|
+
...changedPaths,
|
|
263
|
+
...base.renames.flatMap((rename) => [rename.from, rename.to]),
|
|
264
|
+
];
|
|
265
|
+
const overlap = [...new Set(attributedPaths)].filter((changedPath) =>
|
|
266
|
+
proof.affectedPaths.some((affectedPath) =>
|
|
267
|
+
pathsOverlap(changedPath, affectedPath),
|
|
268
|
+
),
|
|
269
|
+
);
|
|
270
|
+
if (overlap.length > 0) {
|
|
271
|
+
return {
|
|
272
|
+
...base,
|
|
273
|
+
action: "rerun-affected-native-shards",
|
|
274
|
+
reason: "base-delta-overlaps-affected-closure",
|
|
275
|
+
requiredValidation: "incremental-or-full-native",
|
|
276
|
+
overlappingPaths: overlap,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
...base,
|
|
281
|
+
reusable: true,
|
|
282
|
+
action: "reuse-native-proof",
|
|
283
|
+
reason: "semantic-source-stable-and-base-delta-disjoint",
|
|
284
|
+
requiredValidation: "exact-merge-group",
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function createNativeProofReuseDecision({ proof, current = {} } = {}) {
|
|
289
|
+
return rooted(nativeReuseBody(proof, current));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function verifyNativeProofReuseDecision(
|
|
293
|
+
decisionInput,
|
|
294
|
+
{ proof, current = {} } = {},
|
|
295
|
+
) {
|
|
296
|
+
try {
|
|
297
|
+
const decision = clone(decisionInput || {});
|
|
298
|
+
if (decision.schema !== NATIVE_PROOF_REUSE_DECISION_SCHEMA) {
|
|
299
|
+
return { ok: false, reason: "unsupported-schema" };
|
|
300
|
+
}
|
|
301
|
+
const decisionRoot = decision.decisionRoot;
|
|
302
|
+
delete decision.decisionRoot;
|
|
303
|
+
if (devDeliveryContentRoot(decision) !== decisionRoot) {
|
|
304
|
+
return { ok: false, reason: "decision-root-drift" };
|
|
305
|
+
}
|
|
306
|
+
const recomputed = nativeReuseBody(proof, current);
|
|
307
|
+
if (JSON.stringify(decision) !== JSON.stringify(recomputed)) {
|
|
308
|
+
return { ok: false, reason: "decision-input-drift" };
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
ok: decision.reusable === true,
|
|
312
|
+
reason: decision.reason,
|
|
313
|
+
decisionRoot,
|
|
314
|
+
action: decision.action,
|
|
315
|
+
};
|
|
316
|
+
} catch (error) {
|
|
317
|
+
return { ok: false, reason: "invalid-decision", error: error.message };
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function createDevDeliveryWarrantQualifier({
|
|
322
|
+
transition,
|
|
323
|
+
assertWarrantMutation,
|
|
324
|
+
} = {}) {
|
|
325
|
+
return function qualifyDevDeliveryWarrant(
|
|
326
|
+
queueInput,
|
|
327
|
+
warrant,
|
|
328
|
+
{
|
|
329
|
+
nativeProof,
|
|
330
|
+
reuseDecision,
|
|
331
|
+
current = {},
|
|
332
|
+
now = new Date().toISOString(),
|
|
333
|
+
} = {},
|
|
334
|
+
) {
|
|
335
|
+
const currentTime = timestamp(now, "now");
|
|
336
|
+
const transaction = transition(
|
|
337
|
+
queueInput,
|
|
338
|
+
(queue, before) => {
|
|
339
|
+
assertWarrantMutation(before, warrant, currentTime);
|
|
340
|
+
if (before.activeWarrant.phase !== "provisional") {
|
|
341
|
+
throw new Error(
|
|
342
|
+
"only a provisional Delivery Warrant can be qualified",
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
const active = before.activeWarrant;
|
|
346
|
+
const proofVerification = verifyNativeQualificationProof(nativeProof, {
|
|
347
|
+
repository: before.repository,
|
|
348
|
+
protectedBase: before.protectedBase,
|
|
349
|
+
sourceIdentityRoot: active.sourceIdentityRoot,
|
|
350
|
+
sourcePatchRoot: active.sourcePatchRoot,
|
|
351
|
+
planRoot: active.planRoot,
|
|
352
|
+
closureRoot: active.closureRoot,
|
|
353
|
+
dependencyRoot: active.dependencyRoot,
|
|
354
|
+
toolchainRoot: active.toolchainRoot,
|
|
355
|
+
environmentRoot: active.environmentRoot,
|
|
356
|
+
});
|
|
357
|
+
if (!proofVerification.ok) {
|
|
358
|
+
throw new Error(`native proof rejected: ${proofVerification.reason}`);
|
|
359
|
+
}
|
|
360
|
+
const reuseVerification = verifyNativeProofReuseDecision(
|
|
361
|
+
reuseDecision,
|
|
362
|
+
{
|
|
363
|
+
proof: nativeProof,
|
|
364
|
+
current: {
|
|
365
|
+
...current,
|
|
366
|
+
sourceIdentityRoot: active.sourceIdentityRoot,
|
|
367
|
+
sourcePatchRoot: active.sourcePatchRoot,
|
|
368
|
+
planRoot: active.planRoot,
|
|
369
|
+
closureRoot: active.closureRoot,
|
|
370
|
+
dependencyRoot: active.dependencyRoot,
|
|
371
|
+
toolchainRoot: active.toolchainRoot,
|
|
372
|
+
environmentRoot: active.environmentRoot,
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
);
|
|
376
|
+
if (!reuseVerification.ok) {
|
|
377
|
+
throw new Error(
|
|
378
|
+
`native proof is not reusable: ${reuseVerification.reason}`,
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
queue.activeWarrant.phase = "qualified";
|
|
382
|
+
queue.activeWarrant.nativeProofRoot = proofVerification.proofRoot;
|
|
383
|
+
queue.activeWarrant.nativeProofReuseRoot =
|
|
384
|
+
reuseVerification.decisionRoot;
|
|
385
|
+
queue.activeWarrant.qualifiedAt = currentTime;
|
|
386
|
+
queue.activeWarrant.nextAction =
|
|
387
|
+
"Enter the GitHub merge queue at the exact fenced PR head; merge_group remains final authority.";
|
|
388
|
+
const candidate = queue.candidates.find(
|
|
389
|
+
(entry) => entry.candidateId === queue.activeWarrant.candidateId,
|
|
390
|
+
);
|
|
391
|
+
candidate.status = "qualified";
|
|
392
|
+
candidate.updatedAt = currentTime;
|
|
393
|
+
return { candidate, warrant: queue.activeWarrant };
|
|
394
|
+
},
|
|
395
|
+
currentTime,
|
|
396
|
+
);
|
|
397
|
+
const receipt = {
|
|
398
|
+
schema: DEV_DELIVERY_QUALIFICATION_RECEIPT_SCHEMA,
|
|
399
|
+
action: "qualified-warrant",
|
|
400
|
+
candidateId: transaction.result.candidate.candidateId,
|
|
401
|
+
fencingToken: transaction.result.warrant.fencingToken,
|
|
402
|
+
leaseGeneration: transaction.result.warrant.generation,
|
|
403
|
+
phase: transaction.result.warrant.phase,
|
|
404
|
+
nativeProofRoot: transaction.result.warrant.nativeProofRoot,
|
|
405
|
+
nativeProofReuseRoot: transaction.result.warrant.nativeProofReuseRoot,
|
|
406
|
+
qualifiedAt: transaction.result.warrant.qualifiedAt,
|
|
407
|
+
expectedOldStateRoot: transaction.expectedOldStateRoot,
|
|
408
|
+
nextStateRoot: transaction.after.stateRoot,
|
|
409
|
+
nextAction: transaction.result.warrant.nextAction,
|
|
410
|
+
};
|
|
411
|
+
return {
|
|
412
|
+
queue: transaction.after,
|
|
413
|
+
warrant: transaction.result.warrant,
|
|
414
|
+
receipt,
|
|
415
|
+
receiptRoot: devDeliveryContentRoot(receipt),
|
|
416
|
+
};
|
|
417
|
+
};
|
|
418
|
+
}
|
|
@@ -2,7 +2,7 @@ import { devDeliveryContentRoot, devDeliveryExactRoot as exactRoot, devDeliveryE
|
|
|
2
2
|
|
|
3
3
|
export const DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA = "kungfu.buildchain.dev-delivery-settlement-receipt/v1";
|
|
4
4
|
|
|
5
|
-
function noopReceipt(queue, identity, candidate, action) {
|
|
5
|
+
function noopReceipt(queue, identity, candidate, action, details = {}) {
|
|
6
6
|
return {
|
|
7
7
|
schema: DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA,
|
|
8
8
|
action,
|
|
@@ -17,9 +17,36 @@ function noopReceipt(queue, identity, candidate, action) {
|
|
|
17
17
|
expectedOldStateRoot: queue.stateRoot,
|
|
18
18
|
nextStateRoot: queue.stateRoot,
|
|
19
19
|
nextAction: "No fenced delivery state remains for this terminal event.",
|
|
20
|
+
...details,
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
function settleTerminalCandidate(queue, identity, candidate) {
|
|
25
|
+
if (candidate.status !== identity.outcome) {
|
|
26
|
+
if (identity.outcome !== "dequeued") throw new Error("terminal candidate outcome does not match the terminal event");
|
|
27
|
+
const receipt = noopReceipt(queue, identity, candidate, "stale-transient-dequeue-noop", {
|
|
28
|
+
terminalClass: "transient-dequeue",
|
|
29
|
+
authoritativeOutcome: candidate.status,
|
|
30
|
+
nextAction: "Retain the authoritative terminal settlement; the stale dequeue cannot rewrite it.",
|
|
31
|
+
});
|
|
32
|
+
return { queue, receipt, receiptRoot: devDeliveryContentRoot(receipt) };
|
|
33
|
+
}
|
|
34
|
+
const receipt = noopReceipt(queue, identity, candidate, "duplicate-terminal-event-noop");
|
|
35
|
+
return { queue, receipt, receiptRoot: devDeliveryContentRoot(receipt) };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function retainDequeuedWarrant(queue, identity, candidate, fencingToken, leaseGeneration, now) {
|
|
39
|
+
const leaseFresh = Date.parse(queue.activeWarrant.expiresAt) > Date.parse(now);
|
|
40
|
+
const receipt = noopReceipt(queue, identity, candidate, leaseFresh ? "transient-dequeue-retained-active-warrant" : "expired-active-warrant-awaits-fenced-settlement", {
|
|
41
|
+
terminalClass: "transient-dequeue",
|
|
42
|
+
fencingToken,
|
|
43
|
+
leaseGeneration,
|
|
44
|
+
warrantExpiresAt: queue.activeWarrant.expiresAt,
|
|
45
|
+
nextAction: leaseFresh ? "Continue the exact active native execution and heartbeat; later candidates remain queued." : "Prove the stale native worker stopped, then close this exact fenced generation before selecting a successor.",
|
|
46
|
+
});
|
|
47
|
+
return { queue, receipt, receiptRoot: devDeliveryContentRoot(receipt) };
|
|
48
|
+
}
|
|
49
|
+
|
|
23
50
|
export function createDevDeliveryTerminalSettler({ normalizeQueue, closeWarrant, cancelQueued, terminalStates }) {
|
|
24
51
|
return function settleDevDeliveryTerminalEvent(queueInput, input, { now = new Date().toISOString() } = {}) {
|
|
25
52
|
const queue = normalizeQueue(queueInput);
|
|
@@ -33,23 +60,39 @@ export function createDevDeliveryTerminalSettler({ normalizeQueue, closeWarrant,
|
|
|
33
60
|
};
|
|
34
61
|
if (!terminalStates.has(identity.outcome)) throw new Error(`outcome must be one of ${[...terminalStates].join(", ")}`);
|
|
35
62
|
const samePullRequest = queue.candidates.filter((entry) => entry.pullRequestNumber === identity.pullRequestNumber);
|
|
36
|
-
const
|
|
63
|
+
const matchingHead = samePullRequest.filter((entry) => entry.sourceHead === identity.sourceHead);
|
|
64
|
+
const activeCandidate = queue.activeWarrant ? matchingHead.find((entry) => entry.candidateId === queue.activeWarrant.candidateId) : null;
|
|
65
|
+
const candidate = activeCandidate || matchingHead.at(-1) || null;
|
|
37
66
|
if (!candidate && samePullRequest.length > 0) throw new Error("terminal event sourceHead does not match the recorded candidate");
|
|
38
67
|
|
|
39
68
|
if (candidate && terminalStates.has(candidate.status)) {
|
|
40
|
-
|
|
41
|
-
const receipt = noopReceipt(queue, identity, candidate, "duplicate-terminal-event-noop");
|
|
42
|
-
return { queue, receipt, receiptRoot: devDeliveryContentRoot(receipt) };
|
|
69
|
+
return settleTerminalCandidate(queue, identity, candidate);
|
|
43
70
|
}
|
|
44
71
|
|
|
45
72
|
if (queue.activeWarrant) {
|
|
46
73
|
if (!candidate || queue.activeWarrant.candidateId !== candidate.candidateId) throw new Error("terminal event does not match the active Delivery Warrant");
|
|
47
74
|
if (!identity.evidenceRoot) throw new Error("active terminal settlement requires evidenceRoot");
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
const fencingToken = exactRoot(input?.fencingToken, "fencingToken");
|
|
76
|
+
const leaseGeneration = positiveInteger(input?.leaseGeneration, "leaseGeneration");
|
|
77
|
+
if (fencingToken !== queue.activeWarrant.fencingToken) throw new Error("stale fencing token");
|
|
78
|
+
if (leaseGeneration !== queue.activeWarrant.generation) throw new Error("stale lease generation");
|
|
79
|
+
if (identity.outcome === "dequeued") {
|
|
80
|
+
return retainDequeuedWarrant(queue, identity, candidate, fencingToken, leaseGeneration, now);
|
|
81
|
+
}
|
|
82
|
+
return closeWarrant(
|
|
83
|
+
queue,
|
|
84
|
+
{
|
|
85
|
+
candidateId: candidate.candidateId,
|
|
86
|
+
fencingToken,
|
|
87
|
+
generation: leaseGeneration,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
outcome: identity.outcome,
|
|
91
|
+
evidenceRoot: identity.evidenceRoot,
|
|
92
|
+
reason: identity.reason,
|
|
93
|
+
now,
|
|
94
|
+
},
|
|
95
|
+
);
|
|
53
96
|
}
|
|
54
97
|
|
|
55
98
|
if (!candidate) {
|
|
@@ -59,15 +102,19 @@ export function createDevDeliveryTerminalSettler({ normalizeQueue, closeWarrant,
|
|
|
59
102
|
if (candidate.status !== "queued") throw new Error(`candidate status ${candidate.status} requires an active Delivery Warrant`);
|
|
60
103
|
if (!["cancelled", "dequeued"].includes(identity.outcome)) throw new Error("queued candidate cannot settle as merged or terminal-failure");
|
|
61
104
|
if (!identity.evidenceRoot) throw new Error("queued terminal settlement requires evidenceRoot");
|
|
62
|
-
return cancelQueued(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
105
|
+
return cancelQueued(
|
|
106
|
+
queue,
|
|
107
|
+
{
|
|
108
|
+
candidateId: candidate.candidateId,
|
|
109
|
+
pullRequestNumber: identity.pullRequestNumber,
|
|
110
|
+
expectedSourceHead: identity.sourceHead,
|
|
111
|
+
observedSourceHead: identity.sourceHead,
|
|
112
|
+
eventAction: identity.eventAction || (identity.outcome === "dequeued" ? "dequeued" : "closed"),
|
|
113
|
+
outcome: identity.outcome,
|
|
114
|
+
evidenceRoot: identity.evidenceRoot,
|
|
115
|
+
reason: identity.reason,
|
|
116
|
+
},
|
|
117
|
+
{ now },
|
|
118
|
+
);
|
|
72
119
|
};
|
|
73
120
|
}
|