@kungfu-tech/buildchain 3.0.9-alpha.1 → 3.0.9-alpha.10
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/bin/buildchain.mjs +9 -1
- 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 +74 -14
- package/dist/site/buildchain-site.json +334 -36
- package/dist/site/capability-registry.json +7 -6
- package/dist/site/cli-registry.json +41 -2
- package/dist/site/kfd-claims.json +193 -9
- package/dist/site/kfd-upstream-aggregate.json +9 -9
- package/dist/site/manual-registry.json +23 -7
- package/dist/site/node-api-registry.json +7789 -4648
- package/dist/site/page-registry.json +315 -25
- package/dist/site/public-surface-audit.json +353 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-passport-check-manifest.json +1 -0
- package/dist/site/release-provenance.json +12 -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 +15 -7
- 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 +200 -13
- 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 +381 -204
- package/docs/release-governance.md +10 -7
- package/docs/release-passport.md +8 -2
- package/docs/versioning.md +4 -1
- package/package.json +21 -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/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 +128 -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/release-passport-contract.js +10 -6
- package/packages/core/release-passport.js +29 -12
- 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 +9 -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 +47 -5
- 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 +23 -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
|
@@ -2,10 +2,12 @@ import { devDeliveryClone as clone, devDeliveryContentRoot, devDeliveryExactRoot
|
|
|
2
2
|
import { chainedDevDeliveryAttemptInput, createDevDeliveryCandidateIdentity, validateDevDeliveryCandidateChain } from "./dev-delivery-candidate-identity.js";
|
|
3
3
|
import { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA, createCancelQueuedDevDeliveryCandidate } from "./dev-delivery-warrant-cancellation.js";
|
|
4
4
|
import { DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA, createDevDeliveryTerminalSettler } from "./dev-delivery-warrant-settlement.js";
|
|
5
|
+
import { createDevDeliveryWarrantQualifier } from "./dev-delivery-native-proof.js";
|
|
5
6
|
import { compareReleaseBlockerPriority, normalizeReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
|
|
6
7
|
export { devDeliveryContentRoot } from "./dev-delivery-common.js";
|
|
7
8
|
export { RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA, createReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
|
|
8
9
|
export { SOURCE_QUALIFICATION_PROOF_SCHEMA, PROJECT_CUT_REPLAY_PROOF_SCHEMA, INTEGRATION_DELIVERY_PROOF_SCHEMA, classifyDevDeliveryDelta, createIntegrationDeliveryProof, createProjectCutReplayPlan, createProjectCutReplayProof, createSourceQualificationProof, verifyIntegrationDeliveryProof, verifyProjectCutReplayProof, verifySourceQualificationProof } from "./dev-delivery-proof.js";
|
|
10
|
+
export { DEV_DELIVERY_QUALIFICATION_RECEIPT_SCHEMA, NATIVE_QUALIFICATION_PROOF_SCHEMA, NATIVE_PROOF_REUSE_DECISION_SCHEMA, NATIVE_PROOF_BASE_DELTA_SCHEMA, createNativeProofBaseDelta, createNativeProofReuseDecision, createNativeQualificationProof, verifyNativeProofReuseDecision, verifyNativeQualificationProof } from "./dev-delivery-native-proof.js";
|
|
9
11
|
export { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA };
|
|
10
12
|
export const DEV_DELIVERY_QUEUE_CONTRACT = "kungfu-buildchain-dev-delivery-warrant-queue";
|
|
11
13
|
export const DEV_DELIVERY_WARRANT_SCHEMA = "kungfu.buildchain.dev-delivery-warrant/v1";
|
|
@@ -21,7 +23,14 @@ export const DEV_DELIVERY_PRIORITIES = Object.freeze({
|
|
|
21
23
|
|
|
22
24
|
export const DEV_DELIVERY_CLASSES = Object.freeze(["non-native-fast", "native-proof-required", "cross-platform", "release"]);
|
|
23
25
|
|
|
24
|
-
const TERMINAL_STATES = new Set(["merged", "terminal-failure", "dequeued", "cancelled"]);
|
|
26
|
+
const TERMINAL_STATES = new Set(["merged", "terminal-failure", "dequeued", "cancelled", "superseded"]);
|
|
27
|
+
const TERMINAL_CLASSES = Object.freeze({
|
|
28
|
+
merged: "authoritative-completion",
|
|
29
|
+
"terminal-failure": "authoritative-failure",
|
|
30
|
+
cancelled: "authoritative-cancellation",
|
|
31
|
+
superseded: "authoritative-supersession",
|
|
32
|
+
dequeued: "transient-dequeue",
|
|
33
|
+
});
|
|
25
34
|
function nonNegativeInteger(value, label, fallback = 0) {
|
|
26
35
|
const parsed = Number(value ?? fallback);
|
|
27
36
|
if (!Number.isInteger(parsed) || parsed < 0) throw new Error(`${label} must be a non-negative integer`);
|
|
@@ -59,7 +68,7 @@ function normalizeCandidate(input, expected) {
|
|
|
59
68
|
throw new Error(`candidateId mismatch for PR #${identity.pullRequestNumber}`);
|
|
60
69
|
}
|
|
61
70
|
const status = text(input.status || "queued");
|
|
62
|
-
if (!["queued", "selected", "proving", "waiting", "blocked", ...TERMINAL_STATES].includes(status)) {
|
|
71
|
+
if (!["queued", "selected", "provisional", "proving", "qualified", "waiting", "blocked", ...TERMINAL_STATES].includes(status)) {
|
|
63
72
|
throw new Error(`unsupported candidate status ${status || "<empty>"}`);
|
|
64
73
|
}
|
|
65
74
|
const candidate = {
|
|
@@ -71,7 +80,14 @@ function normalizeCandidate(input, expected) {
|
|
|
71
80
|
closureRoot: exactRoot(input.closureRoot, "closureRoot"),
|
|
72
81
|
dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
|
|
73
82
|
toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"),
|
|
74
|
-
...(
|
|
83
|
+
...(input.environmentRoot
|
|
84
|
+
? { environmentRoot: exactRoot(input.environmentRoot, "environmentRoot") }
|
|
85
|
+
: {}),
|
|
86
|
+
...(Object.hasOwn(input, "sourceWorkflowRunId")
|
|
87
|
+
? {
|
|
88
|
+
sourceWorkflowRunId: nonNegativeInteger(input.sourceWorkflowRunId, "candidate sourceWorkflowRunId", 0),
|
|
89
|
+
}
|
|
90
|
+
: {}),
|
|
75
91
|
priority: priority(input.priority),
|
|
76
92
|
enqueuedAt: timestamp(input.enqueuedAt, "candidate enqueuedAt"),
|
|
77
93
|
updatedAt: timestamp(input.updatedAt || input.enqueuedAt, "candidate updatedAt"),
|
|
@@ -80,6 +96,10 @@ function normalizeCandidate(input, expected) {
|
|
|
80
96
|
status,
|
|
81
97
|
terminal: input.terminal || null,
|
|
82
98
|
};
|
|
99
|
+
if (Object.hasOwn(input, "affectedPaths")) {
|
|
100
|
+
if (!Array.isArray(input.affectedPaths)) throw new Error("candidate affectedPaths must be an array");
|
|
101
|
+
candidate.affectedPaths = [...new Set(input.affectedPaths.map(text).filter(Boolean))].sort();
|
|
102
|
+
}
|
|
83
103
|
if (Object.hasOwn(input, "releaseBlockerPriority")) candidate.releaseBlockerPriority = normalizeReleaseBlockerPriorityClaim(input.releaseBlockerPriority, candidate, expected);
|
|
84
104
|
return candidate;
|
|
85
105
|
}
|
|
@@ -140,12 +160,20 @@ export function normalizeDevDeliveryQueue(input, expected = {}) {
|
|
|
140
160
|
warrant.generation = positiveInteger(warrant.generation, "Warrant generation");
|
|
141
161
|
warrant.issuedAt = timestamp(warrant.issuedAt, "Warrant issuedAt");
|
|
142
162
|
warrant.expiresAt = timestamp(warrant.expiresAt, "Warrant expiresAt");
|
|
143
|
-
const
|
|
163
|
+
const warrantPhase = text(warrant.phase || "qualified");
|
|
164
|
+
if (!["provisional", "qualified"].includes(warrantPhase)) throw new Error("active Warrant phase is unsupported");
|
|
165
|
+
if (Object.hasOwn(warrant, "phase")) warrant.phase = warrantPhase;
|
|
166
|
+
if (warrantPhase === "qualified") {
|
|
167
|
+
if (warrant.nativeProofRoot) warrant.nativeProofRoot = exactRoot(warrant.nativeProofRoot, "Warrant nativeProofRoot");
|
|
168
|
+
if (warrant.nativeProofReuseRoot) warrant.nativeProofReuseRoot = exactRoot(warrant.nativeProofReuseRoot, "Warrant nativeProofReuseRoot");
|
|
169
|
+
if (warrant.qualifiedAt) warrant.qualifiedAt = timestamp(warrant.qualifiedAt, "Warrant qualifiedAt");
|
|
170
|
+
}
|
|
171
|
+
const activeCandidates = queue.candidates.filter((candidate) => ["selected", "provisional", "proving", "qualified", "waiting", "blocked"].includes(candidate.status));
|
|
144
172
|
if (activeCandidates.length !== 1 || activeCandidates[0].candidateId !== warrant.candidateId) {
|
|
145
173
|
throw new Error("exactly one active candidate must match the active Warrant");
|
|
146
174
|
}
|
|
147
175
|
if (warrant.releaseBlockerPriority?.claimRoot !== activeCandidates[0].releaseBlockerPriority?.claimRoot) throw new Error("active Warrant release-blocker priority drift");
|
|
148
|
-
} else if (queue.candidates.some((candidate) => ["selected", "proving", "waiting", "blocked"].includes(candidate.status))) {
|
|
176
|
+
} else if (queue.candidates.some((candidate) => ["selected", "provisional", "proving", "qualified", "waiting", "blocked"].includes(candidate.status))) {
|
|
149
177
|
throw new Error("active candidate exists without an active Warrant");
|
|
150
178
|
}
|
|
151
179
|
const rooted = withQueueRoot(queue);
|
|
@@ -183,7 +211,8 @@ function submissionReceipt({ before, after, candidate, action, now }) {
|
|
|
183
211
|
planRoot: candidate.planRoot,
|
|
184
212
|
closureRoot: candidate.closureRoot,
|
|
185
213
|
dependencyRoot: candidate.dependencyRoot,
|
|
186
|
-
toolchainRoot: candidate.toolchainRoot,
|
|
214
|
+
toolchainRoot: candidate.toolchainRoot, ...(candidate.environmentRoot ? { environmentRoot: candidate.environmentRoot } : {}),
|
|
215
|
+
...(Object.hasOwn(candidate, "affectedPaths") ? { affectedPaths: candidate.affectedPaths } : {}),
|
|
187
216
|
sourceWorkflowRunId: candidate.sourceWorkflowRunId,
|
|
188
217
|
...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
|
|
189
218
|
deliveryClass: candidate.deliveryClass,
|
|
@@ -198,6 +227,21 @@ function submissionReceipt({ before, after, candidate, action, now }) {
|
|
|
198
227
|
};
|
|
199
228
|
}
|
|
200
229
|
|
|
230
|
+
function reconcileSourceWorkflowRunId(existing, attempted, activeCandidateId, currentTime) {
|
|
231
|
+
const current = existing.sourceWorkflowRunId || 0;
|
|
232
|
+
const next = attempted.sourceWorkflowRunId || 0;
|
|
233
|
+
if (existing.sourceHead !== attempted.sourceHead) {
|
|
234
|
+
existing.sourceWorkflowRunId = next;
|
|
235
|
+
return "head-change";
|
|
236
|
+
}
|
|
237
|
+
if (current > 0 && next > 0 && current !== next) throw new Error("candidate sourceWorkflowRunId cannot change for the same sourceHead");
|
|
238
|
+
if (current !== 0 || next === 0) return "retained";
|
|
239
|
+
if (activeCandidateId === existing.candidateId) return "active";
|
|
240
|
+
existing.sourceWorkflowRunId = next;
|
|
241
|
+
existing.updatedAt = currentTime;
|
|
242
|
+
return "upgraded";
|
|
243
|
+
}
|
|
244
|
+
|
|
201
245
|
export function submitDevDeliveryCandidate(queueInput, input, { now = new Date().toISOString() } = {}) {
|
|
202
246
|
const transaction = transition(
|
|
203
247
|
queueInput,
|
|
@@ -213,7 +257,13 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
|
|
|
213
257
|
},
|
|
214
258
|
queue,
|
|
215
259
|
);
|
|
216
|
-
const chainedInput = chainedDevDeliveryAttemptInput({
|
|
260
|
+
const chainedInput = chainedDevDeliveryAttemptInput({
|
|
261
|
+
queue,
|
|
262
|
+
candidate,
|
|
263
|
+
input,
|
|
264
|
+
currentTime,
|
|
265
|
+
terminalStates: TERMINAL_STATES,
|
|
266
|
+
});
|
|
217
267
|
const attemptedCandidate = chainedInput ? normalizeCandidate(chainedInput, queue) : candidate;
|
|
218
268
|
const conflicting = queue.candidates.find((entry) => entry.pullRequestNumber === attemptedCandidate.pullRequestNumber && entry.candidateId !== attemptedCandidate.candidateId && !TERMINAL_STATES.has(entry.status));
|
|
219
269
|
if (conflicting) throw new Error(`PR #${candidate.pullRequestNumber} already has a different active semantic source`);
|
|
@@ -224,10 +274,12 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
|
|
|
224
274
|
if (TERMINAL_STATES.has(existing.status)) {
|
|
225
275
|
throw new Error(`candidate ${attemptedCandidate.candidateId} is terminal and cannot be resubmitted`);
|
|
226
276
|
}
|
|
227
|
-
const exactProofFields = ["sourcePatchRoot", "sourceProofRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot"];
|
|
228
|
-
const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]) && existing.releaseBlockerPriority?.claimRoot === attemptedCandidate.releaseBlockerPriority?.claimRoot;
|
|
277
|
+
const exactProofFields = ["sourcePatchRoot", "sourceProofRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot", "environmentRoot"];
|
|
278
|
+
const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]) && JSON.stringify(existing.affectedPaths || []) === JSON.stringify(attemptedCandidate.affectedPaths || []) && existing.releaseBlockerPriority?.claimRoot === attemptedCandidate.releaseBlockerPriority?.claimRoot;
|
|
279
|
+
const bindingAction = reconcileSourceWorkflowRunId(existing, attemptedCandidate, before.activeWarrant?.candidateId, currentTime);
|
|
229
280
|
if (existing.sourceHead === attemptedCandidate.sourceHead && exactProofMatches) {
|
|
230
|
-
|
|
281
|
+
if (bindingAction === "active") return { candidate: existing, action: "active-warrant-retained-noop" };
|
|
282
|
+
action = bindingAction === "upgraded" ? "source-workflow-run-upgrade-retained-age" : "duplicate-noop";
|
|
231
283
|
selected = existing;
|
|
232
284
|
} else {
|
|
233
285
|
if (before.activeWarrant?.candidateId === existing.candidateId) {
|
|
@@ -241,6 +293,8 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
|
|
|
241
293
|
const headChanged = existing.sourceHead !== attemptedCandidate.sourceHead;
|
|
242
294
|
existing.sourceHead = attemptedCandidate.sourceHead;
|
|
243
295
|
for (const field of exactProofFields) existing[field] = attemptedCandidate[field];
|
|
296
|
+
if (Object.hasOwn(attemptedCandidate, "affectedPaths")) existing.affectedPaths = attemptedCandidate.affectedPaths;
|
|
297
|
+
else delete existing.affectedPaths;
|
|
244
298
|
if (Object.hasOwn(attemptedCandidate, "releaseBlockerPriority")) existing.releaseBlockerPriority = attemptedCandidate.releaseBlockerPriority;
|
|
245
299
|
else delete existing.releaseBlockerPriority;
|
|
246
300
|
existing.updatedAt = currentTime;
|
|
@@ -308,26 +362,26 @@ function warrantToken(input) {
|
|
|
308
362
|
|
|
309
363
|
export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOString(), leaseSeconds } = {}) {
|
|
310
364
|
const currentTime = timestamp(now, "now");
|
|
311
|
-
|
|
365
|
+
const queue = normalizeDevDeliveryQueue(queueInput);
|
|
312
366
|
let recoveryReceipt = null;
|
|
313
367
|
if (queue.activeWarrant && Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(currentTime)) {
|
|
314
368
|
const recovered = recoverExpiredDevDeliveryWarrant(queue, {
|
|
315
369
|
now: currentTime,
|
|
316
370
|
});
|
|
317
|
-
queue = recovered.queue;
|
|
318
371
|
recoveryReceipt = recovered.receipt;
|
|
319
372
|
}
|
|
320
373
|
if (queue.activeWarrant) {
|
|
374
|
+
const expired = Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(currentTime);
|
|
321
375
|
const receipt = {
|
|
322
376
|
schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
|
|
323
|
-
selected:
|
|
324
|
-
reason: "non-preemptive-active-warrant",
|
|
377
|
+
selected: !expired,
|
|
378
|
+
reason: expired ? "expired-active-warrant-awaits-fenced-settlement" : "non-preemptive-active-warrant",
|
|
325
379
|
candidateId: queue.activeWarrant.candidateId,
|
|
326
380
|
fencingToken: queue.activeWarrant.fencingToken,
|
|
327
381
|
leaseGeneration: queue.activeWarrant.generation,
|
|
328
382
|
expectedOldStateRoot: recoveryReceipt?.expectedOldStateRoot || queue.stateRoot,
|
|
329
383
|
nextStateRoot: queue.stateRoot,
|
|
330
|
-
nextAction: "Continue the active delivery attempt; later candidates remain visibly queued.",
|
|
384
|
+
nextAction: expired ? "Prove the stale native worker stopped, then close this exact fenced generation before selecting a successor." : "Continue the active delivery attempt; later candidates remain visibly queued.",
|
|
331
385
|
};
|
|
332
386
|
return {
|
|
333
387
|
queue,
|
|
@@ -378,16 +432,18 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
378
432
|
planRoot: candidate.planRoot,
|
|
379
433
|
closureRoot: candidate.closureRoot,
|
|
380
434
|
dependencyRoot: candidate.dependencyRoot,
|
|
381
|
-
toolchainRoot: candidate.toolchainRoot,
|
|
435
|
+
toolchainRoot: candidate.toolchainRoot, ...(candidate.environmentRoot ? { environmentRoot: candidate.environmentRoot } : {}),
|
|
436
|
+
...(Object.hasOwn(candidate, "affectedPaths") ? { affectedPaths: candidate.affectedPaths } : {}),
|
|
382
437
|
sourceWorkflowRunId: candidate.sourceWorkflowRunId,
|
|
383
438
|
...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
|
|
384
439
|
deliveryClass: candidate.deliveryClass,
|
|
385
440
|
generation: next.fencingCounter,
|
|
441
|
+
phase: "provisional",
|
|
386
442
|
expectedOldStateRoot: before.stateRoot,
|
|
387
443
|
issuedAt,
|
|
388
444
|
expiresAt: new Date(Date.parse(issuedAt) + effectiveLeaseSeconds * 1000).toISOString(),
|
|
389
445
|
fencingToken: "",
|
|
390
|
-
nextAction: "
|
|
446
|
+
nextAction: "Run or reuse the exact semantic native proof, then atomically qualify this fenced Warrant.",
|
|
391
447
|
};
|
|
392
448
|
warrant.fencingToken = warrantToken(warrant);
|
|
393
449
|
next.activeWarrant = warrant;
|
|
@@ -414,9 +470,13 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
414
470
|
planRoot: transaction.result.candidate.planRoot,
|
|
415
471
|
closureRoot: transaction.result.candidate.closureRoot,
|
|
416
472
|
dependencyRoot: transaction.result.candidate.dependencyRoot,
|
|
417
|
-
toolchainRoot: transaction.result.candidate.toolchainRoot,
|
|
473
|
+
toolchainRoot: transaction.result.candidate.toolchainRoot, ...(transaction.result.candidate.environmentRoot ? { environmentRoot: transaction.result.candidate.environmentRoot } : {}),
|
|
418
474
|
sourceWorkflowRunId: transaction.result.candidate.sourceWorkflowRunId,
|
|
419
|
-
...(Object.hasOwn(transaction.result.candidate, "releaseBlockerPriority")
|
|
475
|
+
...(Object.hasOwn(transaction.result.candidate, "releaseBlockerPriority")
|
|
476
|
+
? {
|
|
477
|
+
releaseBlockerPriority: transaction.result.candidate.releaseBlockerPriority,
|
|
478
|
+
}
|
|
479
|
+
: {}),
|
|
420
480
|
deliveryClass: transaction.result.candidate.deliveryClass,
|
|
421
481
|
queueAgeSeconds: selected.priority.ageSeconds,
|
|
422
482
|
basePriority: selected.priority.basePriority,
|
|
@@ -454,7 +514,7 @@ export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Dat
|
|
|
454
514
|
const effectiveLeaseSeconds = positiveInteger(leaseSeconds, "leaseSeconds", queue.policy.leaseSeconds);
|
|
455
515
|
queue.activeWarrant.expiresAt = new Date(Date.parse(currentTime) + effectiveLeaseSeconds * 1000).toISOString();
|
|
456
516
|
const candidate = queue.candidates.find((entry) => entry.candidateId === queue.activeWarrant.candidateId);
|
|
457
|
-
candidate.status = "proving";
|
|
517
|
+
candidate.status = queue.activeWarrant.phase === "qualified" ? "qualified" : "proving";
|
|
458
518
|
candidate.updatedAt = currentTime;
|
|
459
519
|
return { candidate, warrant: queue.activeWarrant };
|
|
460
520
|
},
|
|
@@ -478,6 +538,13 @@ export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Dat
|
|
|
478
538
|
};
|
|
479
539
|
}
|
|
480
540
|
|
|
541
|
+
const qualifyDevDeliveryWarrantTransition = createDevDeliveryWarrantQualifier({
|
|
542
|
+
transition,
|
|
543
|
+
assertWarrantMutation,
|
|
544
|
+
});
|
|
545
|
+
export function qualifyDevDeliveryWarrant(queueInput, warrant, options) {
|
|
546
|
+
return qualifyDevDeliveryWarrantTransition(queueInput, warrant, options);
|
|
547
|
+
}
|
|
481
548
|
export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().toISOString() } = {}) {
|
|
482
549
|
const currentTime = timestamp(now, "now");
|
|
483
550
|
const before = normalizeDevDeliveryQueue(queueInput);
|
|
@@ -496,33 +563,21 @@ export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().
|
|
|
496
563
|
receiptRoot: devDeliveryContentRoot(receipt),
|
|
497
564
|
};
|
|
498
565
|
}
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
(queue) => {
|
|
502
|
-
const expired = clone(queue.activeWarrant);
|
|
503
|
-
const candidate = queue.candidates.find((entry) => entry.candidateId === expired.candidateId);
|
|
504
|
-
candidate.status = "queued";
|
|
505
|
-
candidate.recoveries += 1;
|
|
506
|
-
candidate.attempts += 1;
|
|
507
|
-
candidate.updatedAt = currentTime;
|
|
508
|
-
queue.activeWarrant = null;
|
|
509
|
-
return { candidate, expired };
|
|
510
|
-
},
|
|
511
|
-
currentTime,
|
|
512
|
-
);
|
|
566
|
+
const expired = before.activeWarrant;
|
|
567
|
+
const candidate = before.candidates.find((entry) => entry.candidateId === expired.candidateId);
|
|
513
568
|
const receipt = {
|
|
514
569
|
schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
|
|
515
|
-
action: "
|
|
516
|
-
candidateId:
|
|
517
|
-
rejectedFencingToken:
|
|
518
|
-
expiredLeaseGeneration:
|
|
519
|
-
retainedEnqueuedAt:
|
|
520
|
-
expectedOldStateRoot:
|
|
521
|
-
nextStateRoot:
|
|
522
|
-
nextAction: "
|
|
570
|
+
action: "expired-lease-fenced-stop-required",
|
|
571
|
+
candidateId: candidate.candidateId,
|
|
572
|
+
rejectedFencingToken: expired.fencingToken,
|
|
573
|
+
expiredLeaseGeneration: expired.generation,
|
|
574
|
+
retainedEnqueuedAt: candidate.enqueuedAt,
|
|
575
|
+
expectedOldStateRoot: before.stateRoot,
|
|
576
|
+
nextStateRoot: before.stateRoot,
|
|
577
|
+
nextAction: "Prove the stale native worker stopped, then close this exact fenced generation before selecting a successor.",
|
|
523
578
|
};
|
|
524
579
|
return {
|
|
525
|
-
queue:
|
|
580
|
+
queue: before,
|
|
526
581
|
receipt,
|
|
527
582
|
receiptRoot: devDeliveryContentRoot(receipt),
|
|
528
583
|
};
|
|
@@ -534,16 +589,22 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
|
|
|
534
589
|
if (!TERMINAL_STATES.has(normalizedOutcome)) {
|
|
535
590
|
throw new Error(`outcome must be one of ${[...TERMINAL_STATES].join(", ")}`);
|
|
536
591
|
}
|
|
592
|
+
if (normalizedOutcome === "dequeued") {
|
|
593
|
+
throw new Error("transient dequeue cannot close an active Delivery Warrant");
|
|
594
|
+
}
|
|
537
595
|
const transaction = transition(
|
|
538
596
|
queueInput,
|
|
539
597
|
(queue, before) => {
|
|
540
|
-
assertWarrantMutation(before, warrant, currentTime, {
|
|
598
|
+
assertWarrantMutation(before, warrant, currentTime, {
|
|
599
|
+
allowExpired: true,
|
|
600
|
+
});
|
|
541
601
|
const active = clone(queue.activeWarrant);
|
|
542
602
|
const candidate = queue.candidates.find((entry) => entry.candidateId === active.candidateId);
|
|
543
603
|
candidate.status = normalizedOutcome;
|
|
544
604
|
candidate.updatedAt = currentTime;
|
|
545
605
|
candidate.terminal = {
|
|
546
606
|
outcome: normalizedOutcome,
|
|
607
|
+
terminalClass: TERMINAL_CLASSES[normalizedOutcome],
|
|
547
608
|
reason: text(reason),
|
|
548
609
|
evidenceRoot: exactRoot(evidenceRoot, "evidenceRoot"),
|
|
549
610
|
closedAt: currentTime,
|
|
@@ -559,6 +620,7 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
|
|
|
559
620
|
schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
|
|
560
621
|
action: "terminal-closeout",
|
|
561
622
|
outcome: normalizedOutcome,
|
|
623
|
+
terminalClass: TERMINAL_CLASSES[normalizedOutcome],
|
|
562
624
|
reason: text(reason),
|
|
563
625
|
candidateId: transaction.result.candidate.candidateId,
|
|
564
626
|
fencingToken: transaction.result.active.fencingToken,
|
|
@@ -576,8 +638,15 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
|
|
|
576
638
|
}
|
|
577
639
|
|
|
578
640
|
const cancelQueuedDevDeliveryCandidateTransition = createCancelQueuedDevDeliveryCandidate(normalizeDevDeliveryQueue);
|
|
579
|
-
export function cancelQueuedDevDeliveryCandidate(queueInput, input, options) {
|
|
580
|
-
|
|
641
|
+
export function cancelQueuedDevDeliveryCandidate(queueInput, input, options) {
|
|
642
|
+
return cancelQueuedDevDeliveryCandidateTransition(queueInput, input, options);
|
|
643
|
+
}
|
|
644
|
+
export const settleDevDeliveryTerminalEvent = createDevDeliveryTerminalSettler({
|
|
645
|
+
normalizeQueue: normalizeDevDeliveryQueue,
|
|
646
|
+
closeWarrant: closeDevDeliveryWarrant,
|
|
647
|
+
cancelQueued: cancelQueuedDevDeliveryCandidate,
|
|
648
|
+
terminalStates: TERMINAL_STATES,
|
|
649
|
+
});
|
|
581
650
|
|
|
582
651
|
export function observeDevDeliveryQueue(queueInput, { now = new Date().toISOString() } = {}) {
|
|
583
652
|
const queue = normalizeDevDeliveryQueue(queueInput);
|
|
@@ -604,6 +673,19 @@ export function observeDevDeliveryQueue(queueInput, { now = new Date().toISOStri
|
|
|
604
673
|
queueAgeSeconds: entry.priority.ageSeconds,
|
|
605
674
|
attempts: entry.candidate.attempts,
|
|
606
675
|
recoveries: entry.candidate.recoveries,
|
|
676
|
+
assignmentRoot: entry.candidate.assignmentRoot,
|
|
677
|
+
initiativeRoot: entry.candidate.initiativeRoot,
|
|
678
|
+
sourceIdentityRoot: entry.candidate.sourceIdentityRoot,
|
|
679
|
+
sourcePatchRoot: entry.candidate.sourcePatchRoot,
|
|
680
|
+
sourceProofRoot: entry.candidate.sourceProofRoot,
|
|
681
|
+
planRoot: entry.candidate.planRoot,
|
|
682
|
+
closureRoot: entry.candidate.closureRoot,
|
|
683
|
+
dependencyRoot: entry.candidate.dependencyRoot,
|
|
684
|
+
toolchainRoot: entry.candidate.toolchainRoot, ...(entry.candidate.environmentRoot ? { environmentRoot: entry.candidate.environmentRoot } : {}),
|
|
685
|
+
sourceWorkflowRunId: entry.candidate.sourceWorkflowRunId || 0,
|
|
686
|
+
affectedPaths: entry.candidate.affectedPaths || [],
|
|
687
|
+
deliveryClass: entry.candidate.deliveryClass,
|
|
688
|
+
priority: entry.candidate.priority,
|
|
607
689
|
nextAction: index === 0 && !queue.activeWarrant ? "eligible-for-selection" : "wait-for-active-warrant",
|
|
608
690
|
})),
|
|
609
691
|
observedAt: currentTime,
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import kfdCategoryCatalog from "@kungfu-tech/kfd/adopter-conformance/category-profiles.json" with { type: "json" };
|
|
2
|
+
import kfdPackageJson from "@kungfu-tech/kfd/package.json" with { type: "json" };
|
|
3
|
+
import { resolveAdopterCategoryProfiles } from "@kungfu-tech/kfd/adopter-conformance/category-profile-resolver";
|
|
4
|
+
import { verifyAdopterCategoryInstanceManifest } from "@kungfu-tech/kfd/adopter-conformance/category-instance-verifier";
|
|
5
|
+
import { verifyAdopterManifestFromPackage } from "@kungfu-tech/kfd/adopter-conformance/toolchain";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
ADOPTER_PROTOCOL_DRIVER_INTERFACE,
|
|
9
|
+
adopterDeliveryGateDigest,
|
|
10
|
+
defineAdopterProtocolDriver,
|
|
11
|
+
} from "./adopter-delivery-gate.js";
|
|
12
|
+
|
|
13
|
+
export const KFD_ADOPTER_CATEGORY_PROTOCOL_ID =
|
|
14
|
+
"kfd.adopter-category/instance-manifest";
|
|
15
|
+
export const KFD_ADOPTER_CATEGORY_PROTOCOL_VERSION = "1.0.0";
|
|
16
|
+
export const KFD_ADOPTER_CATEGORY_DRIVER_REPORT_CONTRACT =
|
|
17
|
+
"kungfu-buildchain-kfd-adopter-category-driver-report";
|
|
18
|
+
|
|
19
|
+
function freezeJson(value) {
|
|
20
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
21
|
+
Object.freeze(value);
|
|
22
|
+
for (const child of Object.values(value)) freezeJson(child);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const publishedCatalog = freezeJson(structuredClone(kfdCategoryCatalog));
|
|
28
|
+
|
|
29
|
+
function catalogCopy() {
|
|
30
|
+
return structuredClone(publishedCatalog);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function sameJson(left, right) {
|
|
34
|
+
try {
|
|
35
|
+
return adopterDeliveryGateDigest(left) === adopterDeliveryGateDigest(right);
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function issue(code, path, message) {
|
|
42
|
+
return { code, path, message };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function resolvePublishedKfdAdopterCategoryProfiles(selection) {
|
|
46
|
+
return resolveAdopterCategoryProfiles(
|
|
47
|
+
structuredClone(selection),
|
|
48
|
+
catalogCopy(),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function verifyPublishedKfdAdopterCategoryInstance(
|
|
53
|
+
instanceManifest,
|
|
54
|
+
{ adopterManifest, verifiedAt, maxAgeSeconds } = {},
|
|
55
|
+
) {
|
|
56
|
+
const adopterReport = verifyAdopterManifestFromPackage(adopterManifest, {
|
|
57
|
+
packageArtifactRoot: instanceManifest?.kfdCut?.packageRoot,
|
|
58
|
+
verifiedAt,
|
|
59
|
+
maxAgeSeconds,
|
|
60
|
+
});
|
|
61
|
+
return verifyAdopterCategoryInstanceManifest(instanceManifest, {
|
|
62
|
+
catalog: catalogCopy(),
|
|
63
|
+
adopterManifest,
|
|
64
|
+
adopterReport,
|
|
65
|
+
verifiedAt,
|
|
66
|
+
maxAgeSeconds,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function deliveryBindingIssues(request, adopterManifest) {
|
|
71
|
+
const instance = request.declaration;
|
|
72
|
+
const issues = [];
|
|
73
|
+
if (
|
|
74
|
+
request.project.instanceId !== instance?.instanceId ||
|
|
75
|
+
request.project.adopterId !== instance?.project?.adopterId
|
|
76
|
+
) {
|
|
77
|
+
issues.push(
|
|
78
|
+
issue(
|
|
79
|
+
"delivery-kfd-instance-binding-mismatch",
|
|
80
|
+
"/project",
|
|
81
|
+
"Delivery project identity must match the KFD category instance.",
|
|
82
|
+
),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (!sameJson(request.artifact, instance?.project?.artifact)) {
|
|
86
|
+
issues.push(
|
|
87
|
+
issue(
|
|
88
|
+
"delivery-kfd-instance-binding-mismatch",
|
|
89
|
+
"/artifact",
|
|
90
|
+
"Delivery artifact must match the KFD category instance artifact.",
|
|
91
|
+
),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (
|
|
95
|
+
adopterManifest?.kfdCut?.package?.name !== kfdPackageJson.name ||
|
|
96
|
+
adopterManifest?.kfdCut?.package?.version !== kfdPackageJson.version ||
|
|
97
|
+
instance?.kfdCut?.packageVersion !== kfdPackageJson.version
|
|
98
|
+
) {
|
|
99
|
+
issues.push(
|
|
100
|
+
issue(
|
|
101
|
+
"delivery-kfd-package-cut-mismatch",
|
|
102
|
+
"/declaration/kfdCut",
|
|
103
|
+
"KFD declarations must bind the exact installed published package version.",
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return issues;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function driverReport(categoryReport, adapterIssues) {
|
|
111
|
+
const valid = categoryReport?.valid === true && adapterIssues.length === 0;
|
|
112
|
+
const report = {
|
|
113
|
+
schemaVersion: 1,
|
|
114
|
+
contract: KFD_ADOPTER_CATEGORY_DRIVER_REPORT_CONTRACT,
|
|
115
|
+
valid,
|
|
116
|
+
conforming: valid,
|
|
117
|
+
qualifying: false,
|
|
118
|
+
selfCertified: false,
|
|
119
|
+
independentlyCertified: false,
|
|
120
|
+
kfdPackage: {
|
|
121
|
+
name: kfdPackageJson.name,
|
|
122
|
+
version: kfdPackageJson.version,
|
|
123
|
+
},
|
|
124
|
+
categoryReport,
|
|
125
|
+
adapterIssues,
|
|
126
|
+
nonClaims: [
|
|
127
|
+
"Category conformance does not grant runtime permission or release authorization.",
|
|
128
|
+
"Buildchain carries the published KFD decision and does not certify or reinterpret it.",
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
valid,
|
|
133
|
+
report,
|
|
134
|
+
reportRoot: adopterDeliveryGateDigest(report),
|
|
135
|
+
issues: [...(categoryReport?.issues ?? []), ...adapterIssues],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function createKfdAdopterCategoryProtocolDriver() {
|
|
140
|
+
return defineAdopterProtocolDriver({
|
|
141
|
+
interface: ADOPTER_PROTOCOL_DRIVER_INTERFACE,
|
|
142
|
+
id: KFD_ADOPTER_CATEGORY_PROTOCOL_ID,
|
|
143
|
+
version: KFD_ADOPTER_CATEGORY_PROTOCOL_VERSION,
|
|
144
|
+
verify({ request, context }) {
|
|
145
|
+
const adapterIssues = deliveryBindingIssues(
|
|
146
|
+
request,
|
|
147
|
+
context?.adopterManifest,
|
|
148
|
+
);
|
|
149
|
+
let categoryReport = null;
|
|
150
|
+
try {
|
|
151
|
+
categoryReport = verifyPublishedKfdAdopterCategoryInstance(
|
|
152
|
+
request.declaration,
|
|
153
|
+
context,
|
|
154
|
+
);
|
|
155
|
+
} catch {
|
|
156
|
+
adapterIssues.push(
|
|
157
|
+
issue(
|
|
158
|
+
"delivery-kfd-verification-context-invalid",
|
|
159
|
+
"/protocol",
|
|
160
|
+
"Published KFD verification could not reproduce from the supplied exact context.",
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return driverReport(categoryReport, adapterIssues);
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|