@kungfu-tech/buildchain 4.0.1-alpha.2 → 4.0.1-alpha.21
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/release-tail/README.md +8 -0
- package/architecture/ci-lane-change-budget.json +187 -0
- package/architecture/decisions/0002-next-development-transition.md +101 -0
- package/architecture/decisions/0003-two-phase-delivery-warrant.md +152 -0
- package/architecture/internal-capabilities.json +211 -8
- package/architecture/maintainability-policy.json +113 -28
- package/architecture/release-tail-contract-inventory.json +5 -2
- package/architecture/v3-core-mechanism-inventory.json +91 -0
- package/architecture/v4-adopter-delivery-parity.json +32 -5
- package/architecture/v4-capability-state-machine-manifest.json +73 -0
- package/architecture/v4-compatibility-facts-parity.json +77 -0
- package/architecture/v4-delivery-authority-parity.json +250 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +41 -9
- package/architecture/v4-floating-consumer-policy.json +6 -0
- package/architecture/v4-next-development-parity.json +192 -0
- package/architecture/v4-publication-rehearsal-parity.json +84 -0
- package/architecture/v4-release-train-parity.json +1 -0
- package/architecture/v4-tail-reseal-parity.json +150 -0
- package/bin/buildchain.mjs +32 -3
- package/bin/internal/adopter-delivery-cli.mjs +70 -0
- package/bin/internal/command-registry.mjs +3 -0
- package/bin/internal/compatibility-facts-cli.mjs +101 -0
- package/contracts/dev-delivery-authority-v2.schema.json +662 -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/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
- package/contracts/fixtures/v4-compatibility-facts-v1/shared.json +1129 -0
- package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +7 -1
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +229 -0
- package/contracts/next-development-request-v1.schema.json +66 -0
- package/contracts/next-development-transition-v1.schema.json +292 -0
- package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
- package/contracts/v4-compatibility-facts-v1.schema.json +230 -0
- package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
- package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/contracts/v4-tail-reseal-v1.schema.json +276 -0
- package/dist/site/agent-index.json +5 -0
- package/dist/site/artifact-schemas.json +10 -0
- package/dist/site/buildchain-contract.json +1475 -27
- package/dist/site/buildchain-site.json +627 -31
- package/dist/site/capability-registry.json +17 -14
- package/dist/site/cli-registry.json +205 -3
- package/dist/site/kfd-claims.json +490 -16
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +52 -6
- package/dist/site/node-api-registry.json +14736 -8851
- package/dist/site/page-registry.json +593 -21
- package/dist/site/public-surface-audit.json +837 -48
- package/dist/site/publication-authority-registry.json +44 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +24 -0
- package/dist/site/schemas/dev-delivery-authority-v2.schema.json +662 -0
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
- package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
- package/dist/site/schemas/v4-compatibility-facts-v1.schema.json +230 -0
- package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/site-manifest.json +34 -10
- package/dist/site/workflow-registry.json +160 -10
- package/docs/MAP.md +8 -2
- package/docs/cli-reference.md +404 -13
- package/docs/cli.md +8 -0
- package/docs/dev-alpha-candidate-patrol.md +8 -0
- package/docs/dev-delivery-qualification-landing-adr.md +251 -0
- package/docs/dev-delivery-warrant.md +351 -38
- package/docs/next-development-transition.md +118 -0
- package/docs/node-api-reference.md +646 -293
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/v4-adopter-delivery.md +133 -0
- package/docs/v4-compatibility-facts.md +91 -0
- package/docs/v4-publication-rehearsal.md +130 -0
- package/docs/v4-tail-reseal.md +73 -0
- package/docs/versioning.md +4 -3
- package/package.json +34 -6
- 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/buildchain-agent-manuals.js +3 -0
- package/packages/core/buildchain-compatibility-authority.js +336 -0
- package/packages/core/buildchain-compatibility-fact.js +394 -0
- package/packages/core/buildchain-compatibility-facts.json +217 -0
- package/packages/core/buildchain-compatibility-proof.js +566 -0
- package/packages/core/buildchain-config.js +84 -0
- package/packages/core/buildchain-contract.js +96 -143
- 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/buildchain-publication-authority.js +1 -0
- package/packages/core/channel-candidate.js +8 -0
- package/packages/core/channel-promotion-baseline.js +55 -0
- package/packages/core/ci-lane-change-budget.js +247 -0
- package/packages/core/dev-alpha-candidate-selection.js +10 -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 +592 -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 +562 -0
- package/packages/core/dev-delivery-process-boundary.js +563 -0
- package/packages/core/dev-delivery-provider-attempt.js +127 -0
- package/packages/core/dev-delivery-provider-heartbeat.js +382 -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 +587 -0
- package/packages/core/dev-delivery-warrant.js +328 -370
- package/packages/core/github-governance-authority.js +1 -0
- package/packages/core/index.js +5 -0
- package/packages/core/kungfu-temporal-fact.js +557 -0
- 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 +728 -0
- package/packages/core/next-development-projection.js +287 -0
- package/packages/core/next-development-transition.js +738 -0
- package/packages/core/paper-agent-entry.js +11 -5
- package/packages/core/paper.js +16 -5
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-candidate-recovery.js +70 -15
- package/packages/core/release-candidate.js +2 -2
- package/packages/core/release-passport.js +36 -9
- package/packages/core/v4-adopter-delivery-parity.js +3 -3
- package/packages/core/v4-adopter-delivery.js +359 -0
- package/packages/core/v4-canonical-contracts.js +5 -0
- package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
- package/packages/core/v4-floating-consumer-evidence.js +18 -4
- package/packages/core/v4-floating-consumer-policy.js +143 -20
- package/packages/core/v4-floating-consumer-release-passport.js +32 -5
- package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
- package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
- package/packages/core/v4-publication-rehearsal.js +481 -0
- package/packages/core/v4-tail-reseal-contract.js +26 -0
- package/packages/core/v4-tail-reseal-github.js +178 -0
- package/packages/core/v4-tail-reseal-receipt.js +225 -0
- package/packages/core/v4-tail-reseal.js +594 -0
- package/packages/core/workflow-yaml-contract.js +24 -2
- package/scripts/buildchain-cli-help.mjs +41 -2
- package/scripts/check-ci-lane-change-budget.mjs +100 -0
- package/scripts/check-inventory.mjs +14 -3
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +23 -4
- package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
- 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-source-proof-replay.mjs +153 -0
- package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
- 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 +231 -0
- package/scripts/dev-delivery-warrant.mjs +232 -194
- package/scripts/dev-pr-auto-merge.mjs +1 -1
- package/scripts/dev-pr-delivery-warrant.mjs +11 -0
- package/scripts/dev-qualification-patrol.mjs +9 -9
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/generate-channel-promotion-workflow.mjs +2 -2
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-release-candidate-passport.mjs +152 -0
- package/scripts/generate-site-bundle.mjs +69 -4
- package/scripts/init-repo.mjs +22 -4
- package/scripts/next-development-self-dogfood-harness.mjs +460 -0
- package/scripts/next-development-self-dogfood.mjs +597 -0
- package/scripts/next-development-transition.mjs +47 -0
- package/scripts/npm-publish-transaction.mjs +2 -2
- package/scripts/promotion-channel-router.mjs +4 -4
- package/scripts/release-candidate-anchor-provenance.mjs +99 -0
- package/scripts/release-tail.mjs +51 -3
- package/scripts/release-train-self-dogfood.mjs +94 -92
- package/scripts/resume-from-candidate-run.mjs +557 -30
- package/scripts/site-capability-metadata.mjs +41 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v4-consumer-policy.mjs +5 -5
- package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
- package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
- package/scripts/v4-tail-reseal.mjs +416 -0
- package/templates/native-dev-delivery.yml +143 -0
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
import {
|
|
2
|
+
devDeliveryClone as clone,
|
|
3
|
+
devDeliveryContentRoot,
|
|
4
|
+
devDeliveryExactRoot as exactRoot,
|
|
5
|
+
devDeliveryExactSha as exactSha,
|
|
6
|
+
devDeliveryPositiveInteger as positiveInteger,
|
|
7
|
+
devDeliveryProtectedBase as protectedBase,
|
|
8
|
+
devDeliveryRepository as repository,
|
|
9
|
+
devDeliveryText as text,
|
|
10
|
+
devDeliveryTimestamp as timestamp,
|
|
11
|
+
} from "./dev-delivery-common.js";
|
|
12
|
+
import {
|
|
13
|
+
chainedDevDeliveryAttemptInput,
|
|
14
|
+
createDevDeliveryCandidateIdentity,
|
|
15
|
+
validateDevDeliveryCandidateChain,
|
|
16
|
+
} from "./dev-delivery-candidate-identity.js";
|
|
17
|
+
import {
|
|
18
|
+
createDevDeliveryWarrantToken,
|
|
19
|
+
normalizeNativeCommandContract,
|
|
20
|
+
validateActiveDevDeliveryWarrant,
|
|
21
|
+
} from "./dev-delivery-native-proof.js";
|
|
22
|
+
import { normalizeProviderFailureAuthorityBinding } from "./dev-delivery-warrant-settlement.js";
|
|
23
|
+
import {
|
|
24
|
+
compareReleaseBlockerPriority,
|
|
25
|
+
normalizeReleaseBlockerPriorityClaim,
|
|
26
|
+
} from "./release-blocker-priority.js";
|
|
27
|
+
|
|
28
|
+
export const DEV_DELIVERY_QUEUE_CONTRACT =
|
|
29
|
+
"kungfu-buildchain-dev-delivery-warrant-queue";
|
|
30
|
+
export const DEV_DELIVERY_WARRANT_SCHEMA =
|
|
31
|
+
"kungfu.buildchain.dev-delivery-warrant/v1";
|
|
32
|
+
export const DEV_DELIVERY_SUBMISSION_RECEIPT_SCHEMA =
|
|
33
|
+
"kungfu.buildchain.dev-delivery-submission-receipt/v1";
|
|
34
|
+
export const DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA =
|
|
35
|
+
"kungfu.buildchain.dev-delivery-selection-receipt/v1";
|
|
36
|
+
export const DEV_DELIVERY_LEASE_RECEIPT_SCHEMA =
|
|
37
|
+
"kungfu.buildchain.dev-delivery-lease-receipt/v1";
|
|
38
|
+
export const DEV_DELIVERY_PRIORITIES = Object.freeze({
|
|
39
|
+
ordinary: 0,
|
|
40
|
+
expedited: 1,
|
|
41
|
+
emergency: 2,
|
|
42
|
+
});
|
|
43
|
+
export const DEV_DELIVERY_CLASSES = Object.freeze([
|
|
44
|
+
"non-native-fast",
|
|
45
|
+
"native-proof-required",
|
|
46
|
+
"cross-platform",
|
|
47
|
+
"release",
|
|
48
|
+
]);
|
|
49
|
+
export const TERMINAL_STATES = new Set([
|
|
50
|
+
"merged",
|
|
51
|
+
"terminal-failure",
|
|
52
|
+
"dequeued",
|
|
53
|
+
"cancelled",
|
|
54
|
+
"superseded",
|
|
55
|
+
]);
|
|
56
|
+
export function issueDevDeliveryWarrant(
|
|
57
|
+
queue,
|
|
58
|
+
before,
|
|
59
|
+
candidate,
|
|
60
|
+
currentTime,
|
|
61
|
+
leaseSeconds,
|
|
62
|
+
) {
|
|
63
|
+
queue.fencingCounter += 1;
|
|
64
|
+
const effectiveLeaseSeconds = positiveInteger(
|
|
65
|
+
leaseSeconds,
|
|
66
|
+
"leaseSeconds",
|
|
67
|
+
queue.policy.leaseSeconds,
|
|
68
|
+
);
|
|
69
|
+
const requiresNativeQualification = Boolean(candidate.environmentRoot);
|
|
70
|
+
const warrant = {
|
|
71
|
+
schema: DEV_DELIVERY_WARRANT_SCHEMA,
|
|
72
|
+
...(requiresNativeQualification ? { phase: "provisional" } : {}),
|
|
73
|
+
repository: queue.repository,
|
|
74
|
+
protectedBase: queue.protectedBase,
|
|
75
|
+
candidateId: candidate.candidateId,
|
|
76
|
+
pullRequestNumber: candidate.pullRequestNumber,
|
|
77
|
+
sourceHead: candidate.sourceHead,
|
|
78
|
+
assignmentRoot: candidate.assignmentRoot,
|
|
79
|
+
initiativeRoot: candidate.initiativeRoot,
|
|
80
|
+
sourceIdentityRoot: candidate.sourceIdentityRoot,
|
|
81
|
+
sourcePatchRoot: candidate.sourcePatchRoot,
|
|
82
|
+
sourceProofRoot: candidate.sourceProofRoot,
|
|
83
|
+
planRoot: candidate.planRoot,
|
|
84
|
+
closureRoot: candidate.closureRoot,
|
|
85
|
+
dependencyRoot: candidate.dependencyRoot,
|
|
86
|
+
toolchainRoot: candidate.toolchainRoot,
|
|
87
|
+
...(candidate.environmentRoot
|
|
88
|
+
? { environmentRoot: candidate.environmentRoot }
|
|
89
|
+
: {}),
|
|
90
|
+
...(candidate.nativeCommandContract
|
|
91
|
+
? { nativeCommandContract: candidate.nativeCommandContract }
|
|
92
|
+
: {}),
|
|
93
|
+
...(candidate.affectedPaths
|
|
94
|
+
? { affectedPaths: candidate.affectedPaths }
|
|
95
|
+
: {}),
|
|
96
|
+
...(candidate.shardEvidenceRoots
|
|
97
|
+
? { shardEvidenceRoots: candidate.shardEvidenceRoots }
|
|
98
|
+
: {}),
|
|
99
|
+
sourceWorkflowRunId: candidate.sourceWorkflowRunId,
|
|
100
|
+
...(Object.hasOwn(candidate, "releaseBlockerPriority")
|
|
101
|
+
? { releaseBlockerPriority: candidate.releaseBlockerPriority }
|
|
102
|
+
: {}),
|
|
103
|
+
deliveryClass: candidate.deliveryClass,
|
|
104
|
+
priority: candidate.priority,
|
|
105
|
+
generation: queue.fencingCounter,
|
|
106
|
+
expectedOldStateRoot: before.stateRoot,
|
|
107
|
+
issuedAt: currentTime,
|
|
108
|
+
expiresAt: new Date(
|
|
109
|
+
Date.parse(currentTime) + effectiveLeaseSeconds * 1000,
|
|
110
|
+
).toISOString(),
|
|
111
|
+
fencingToken: "",
|
|
112
|
+
nextAction: requiresNativeQualification
|
|
113
|
+
? "Run or reuse exact native proof under this fence, then atomically qualify before merge admission."
|
|
114
|
+
: "Replay on the exact current dev base, prove required checks, then enqueue the unchanged PR head.",
|
|
115
|
+
};
|
|
116
|
+
warrant.fencingToken = createDevDeliveryWarrantToken(warrant);
|
|
117
|
+
return warrant;
|
|
118
|
+
}
|
|
119
|
+
function nonNegativeInteger(value, label, fallback = 0) {
|
|
120
|
+
const parsed = Number(value ?? fallback);
|
|
121
|
+
if (!Number.isInteger(parsed) || parsed < 0)
|
|
122
|
+
throw new Error(`${label} must be a non-negative integer`);
|
|
123
|
+
return parsed;
|
|
124
|
+
}
|
|
125
|
+
function priority(value = "ordinary") {
|
|
126
|
+
const normalized = text(value || "ordinary").toLowerCase();
|
|
127
|
+
if (!(normalized in DEV_DELIVERY_PRIORITIES))
|
|
128
|
+
throw new Error(
|
|
129
|
+
`priority must be one of ${Object.keys(DEV_DELIVERY_PRIORITIES).join(", ")}`,
|
|
130
|
+
);
|
|
131
|
+
return normalized;
|
|
132
|
+
}
|
|
133
|
+
function deliveryClass(value) {
|
|
134
|
+
const normalized = text(value);
|
|
135
|
+
if (!DEV_DELIVERY_CLASSES.includes(normalized))
|
|
136
|
+
throw new Error(
|
|
137
|
+
`deliveryClass must be one of ${DEV_DELIVERY_CLASSES.join(", ")}`,
|
|
138
|
+
);
|
|
139
|
+
return normalized;
|
|
140
|
+
}
|
|
141
|
+
function exactRoots(values, label) {
|
|
142
|
+
if (!Array.isArray(values)) throw new Error(`${label} must be an array`);
|
|
143
|
+
return [...new Set(values.map((value) => exactRoot(value, label)))].sort();
|
|
144
|
+
}
|
|
145
|
+
function normalizePolicy(policy = {}) {
|
|
146
|
+
return {
|
|
147
|
+
agingSeconds: positiveInteger(
|
|
148
|
+
policy.agingSeconds,
|
|
149
|
+
"policy agingSeconds",
|
|
150
|
+
900,
|
|
151
|
+
),
|
|
152
|
+
maxPriority: positiveInteger(policy.maxPriority, "policy maxPriority", 2),
|
|
153
|
+
leaseSeconds: positiveInteger(
|
|
154
|
+
policy.leaseSeconds,
|
|
155
|
+
"policy leaseSeconds",
|
|
156
|
+
3600,
|
|
157
|
+
),
|
|
158
|
+
emergencyPolicy: text(policy.emergencyPolicy || "reviewed-explicit-only"),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function lacksLiveNativeProof(candidate, status, allowLegacyV3Readback) {
|
|
163
|
+
const { environmentRoot, nativeCommandContract } = candidate;
|
|
164
|
+
const native =
|
|
165
|
+
candidate.deliveryClass !== "non-native-fast" ||
|
|
166
|
+
environmentRoot ||
|
|
167
|
+
nativeCommandContract;
|
|
168
|
+
return (
|
|
169
|
+
native &&
|
|
170
|
+
(!environmentRoot ||
|
|
171
|
+
(!nativeCommandContract && allowLegacyV3Readback !== true)) &&
|
|
172
|
+
!TERMINAL_STATES.has(status)
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function normalizeCandidate(input, expected) {
|
|
177
|
+
const identity = createDevDeliveryCandidateIdentity(
|
|
178
|
+
input,
|
|
179
|
+
expected,
|
|
180
|
+
deliveryClass,
|
|
181
|
+
);
|
|
182
|
+
if (input.candidateId && input.candidateId !== identity.candidateId)
|
|
183
|
+
throw new Error(
|
|
184
|
+
`candidateId mismatch for PR #${identity.pullRequestNumber}`,
|
|
185
|
+
);
|
|
186
|
+
const status = text(input.status || "queued");
|
|
187
|
+
if (
|
|
188
|
+
![
|
|
189
|
+
"queued",
|
|
190
|
+
"selected",
|
|
191
|
+
"proving",
|
|
192
|
+
"waiting",
|
|
193
|
+
"blocked",
|
|
194
|
+
"qualified",
|
|
195
|
+
...TERMINAL_STATES,
|
|
196
|
+
].includes(status)
|
|
197
|
+
)
|
|
198
|
+
throw new Error(`unsupported candidate status ${status || "<empty>"}`);
|
|
199
|
+
const candidate = {
|
|
200
|
+
...identity,
|
|
201
|
+
sourceHead: exactSha(input.sourceHead, "sourceHead"),
|
|
202
|
+
sourcePatchRoot: exactRoot(input.sourcePatchRoot, "sourcePatchRoot"),
|
|
203
|
+
sourceProofRoot: exactRoot(input.sourceProofRoot, "sourceProofRoot"),
|
|
204
|
+
planRoot: exactRoot(input.planRoot, "planRoot"),
|
|
205
|
+
closureRoot: exactRoot(input.closureRoot, "closureRoot"),
|
|
206
|
+
dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
|
|
207
|
+
toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"),
|
|
208
|
+
...(input.environmentRoot
|
|
209
|
+
? { environmentRoot: exactRoot(input.environmentRoot, "environmentRoot") }
|
|
210
|
+
: {}),
|
|
211
|
+
...(input.nativeCommandContract
|
|
212
|
+
? {
|
|
213
|
+
nativeCommandContract: normalizeNativeCommandContract(
|
|
214
|
+
input.nativeCommandContract,
|
|
215
|
+
),
|
|
216
|
+
}
|
|
217
|
+
: {}),
|
|
218
|
+
...(Object.hasOwn(input, "sourceWorkflowRunId")
|
|
219
|
+
? {
|
|
220
|
+
sourceWorkflowRunId: nonNegativeInteger(
|
|
221
|
+
input.sourceWorkflowRunId,
|
|
222
|
+
"candidate sourceWorkflowRunId",
|
|
223
|
+
0,
|
|
224
|
+
),
|
|
225
|
+
}
|
|
226
|
+
: {}),
|
|
227
|
+
priority: priority(input.priority),
|
|
228
|
+
enqueuedAt: timestamp(input.enqueuedAt, "candidate enqueuedAt"),
|
|
229
|
+
updatedAt: timestamp(
|
|
230
|
+
input.updatedAt || input.enqueuedAt,
|
|
231
|
+
"candidate updatedAt",
|
|
232
|
+
),
|
|
233
|
+
attempts: positiveInteger(input.attempts, "candidate attempts", 1),
|
|
234
|
+
recoveries: nonNegativeInteger(input.recoveries, "candidate recoveries", 0),
|
|
235
|
+
status,
|
|
236
|
+
terminal: input.terminal || null,
|
|
237
|
+
};
|
|
238
|
+
if (candidate.terminal) {
|
|
239
|
+
const failureAuthority = normalizeProviderFailureAuthorityBinding(
|
|
240
|
+
candidate.terminal,
|
|
241
|
+
);
|
|
242
|
+
if (failureAuthority && status !== "terminal-failure") {
|
|
243
|
+
throw new Error(
|
|
244
|
+
"provider failure authority can exist only on terminal-failure state",
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
if (failureAuthority) {
|
|
248
|
+
candidate.terminal = { ...candidate.terminal, ...failureAuthority };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (Object.hasOwn(input, "affectedPaths")) {
|
|
252
|
+
if (!Array.isArray(input.affectedPaths))
|
|
253
|
+
throw new Error("candidate affectedPaths must be an array");
|
|
254
|
+
candidate.affectedPaths = [
|
|
255
|
+
...new Set(input.affectedPaths.map(text).filter(Boolean)),
|
|
256
|
+
].sort();
|
|
257
|
+
}
|
|
258
|
+
if (Object.hasOwn(input, "shardEvidenceRoots")) {
|
|
259
|
+
candidate.shardEvidenceRoots = exactRoots(
|
|
260
|
+
input.shardEvidenceRoots,
|
|
261
|
+
"candidate shardEvidenceRoots",
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
if (lacksLiveNativeProof(candidate, status, expected.allowLegacyV3Readback))
|
|
265
|
+
throw new Error("live native candidate requires exact native proof");
|
|
266
|
+
if (Object.hasOwn(input, "releaseBlockerPriority"))
|
|
267
|
+
candidate.releaseBlockerPriority = normalizeReleaseBlockerPriorityClaim(
|
|
268
|
+
input.releaseBlockerPriority,
|
|
269
|
+
candidate,
|
|
270
|
+
expected,
|
|
271
|
+
);
|
|
272
|
+
return candidate;
|
|
273
|
+
}
|
|
274
|
+
function queueBody(queue) {
|
|
275
|
+
const body = clone(queue);
|
|
276
|
+
delete body.stateRoot;
|
|
277
|
+
return body;
|
|
278
|
+
}
|
|
279
|
+
function withQueueRoot(queue) {
|
|
280
|
+
return {
|
|
281
|
+
...queueBody(queue),
|
|
282
|
+
stateRoot: devDeliveryContentRoot(queueBody(queue)),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
export function createDevDeliveryQueue({
|
|
286
|
+
repository: repositoryInput,
|
|
287
|
+
protectedBase: protectedBaseInput,
|
|
288
|
+
policy = {},
|
|
289
|
+
now = new Date().toISOString(),
|
|
290
|
+
} = {}) {
|
|
291
|
+
return withQueueRoot({
|
|
292
|
+
schemaVersion: 1,
|
|
293
|
+
contract: DEV_DELIVERY_QUEUE_CONTRACT,
|
|
294
|
+
repository: repository(repositoryInput),
|
|
295
|
+
protectedBase: protectedBase(protectedBaseInput),
|
|
296
|
+
generation: 0,
|
|
297
|
+
fencingCounter: 0,
|
|
298
|
+
policy: normalizePolicy(policy),
|
|
299
|
+
activeWarrant: null,
|
|
300
|
+
candidates: [],
|
|
301
|
+
updatedAt: timestamp(now, "now"),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
export function normalizeDevDeliveryQueue(input, expected = {}) {
|
|
305
|
+
const queue = clone(input || {});
|
|
306
|
+
if (
|
|
307
|
+
queue.contract !== DEV_DELIVERY_QUEUE_CONTRACT ||
|
|
308
|
+
Number(queue.schemaVersion) !== 1
|
|
309
|
+
) {
|
|
310
|
+
throw new Error(
|
|
311
|
+
`dev delivery queue must use ${DEV_DELIVERY_QUEUE_CONTRACT} schemaVersion 1`,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
queue.repository = repository(queue.repository);
|
|
315
|
+
queue.protectedBase = protectedBase(queue.protectedBase);
|
|
316
|
+
if (expected.repository && queue.repository !== expected.repository)
|
|
317
|
+
throw new Error(
|
|
318
|
+
`dev delivery queue repository mismatch: ${queue.repository} != ${expected.repository}`,
|
|
319
|
+
);
|
|
320
|
+
if (
|
|
321
|
+
expected.protectedBase &&
|
|
322
|
+
queue.protectedBase !== expected.protectedBase
|
|
323
|
+
) {
|
|
324
|
+
throw new Error(
|
|
325
|
+
`dev delivery queue protectedBase mismatch: ${queue.protectedBase} != ${expected.protectedBase}`,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
queue.generation = nonNegativeInteger(queue.generation, "queue generation");
|
|
329
|
+
queue.fencingCounter = nonNegativeInteger(
|
|
330
|
+
queue.fencingCounter,
|
|
331
|
+
"queue fencingCounter",
|
|
332
|
+
);
|
|
333
|
+
queue.policy = normalizePolicy(queue.policy);
|
|
334
|
+
const allowLegacyV3Readback =
|
|
335
|
+
expected.allowLegacyV3Readback === true &&
|
|
336
|
+
!(queue.candidates || []).some(
|
|
337
|
+
(candidate) => candidate?.nativeCommandContract,
|
|
338
|
+
) &&
|
|
339
|
+
!queue.activeWarrant?.nativeCommandContract &&
|
|
340
|
+
!queue.activeWarrant?.nativeExecutionReceiptRoot &&
|
|
341
|
+
!queue.activeWarrant?.qualificationReceiptRoot;
|
|
342
|
+
queue.candidates = (queue.candidates || []).map((candidate) =>
|
|
343
|
+
normalizeCandidate(candidate, { ...queue, allowLegacyV3Readback }),
|
|
344
|
+
);
|
|
345
|
+
validateDevDeliveryCandidateChain(queue.candidates, TERMINAL_STATES);
|
|
346
|
+
queue.updatedAt = timestamp(queue.updatedAt, "queue updatedAt");
|
|
347
|
+
if (queue.activeWarrant) {
|
|
348
|
+
validateActiveDevDeliveryWarrant(queue, { allowLegacyV3Readback });
|
|
349
|
+
} else if (
|
|
350
|
+
queue.candidates.some((candidate) =>
|
|
351
|
+
["selected", "proving", "waiting", "blocked", "qualified"].includes(
|
|
352
|
+
candidate.status,
|
|
353
|
+
),
|
|
354
|
+
)
|
|
355
|
+
) {
|
|
356
|
+
throw new Error("active candidate exists without an active Warrant");
|
|
357
|
+
}
|
|
358
|
+
const rooted = withQueueRoot(queue);
|
|
359
|
+
if (input.stateRoot && input.stateRoot !== rooted.stateRoot)
|
|
360
|
+
throw new Error("dev delivery queue stateRoot drift");
|
|
361
|
+
return rooted;
|
|
362
|
+
}
|
|
363
|
+
export function transitionDevDeliveryQueue(queueInput, mutate, nowInput) {
|
|
364
|
+
const before = normalizeDevDeliveryQueue(queueInput);
|
|
365
|
+
const expectedOldStateRoot = before.stateRoot;
|
|
366
|
+
const queue = clone(before);
|
|
367
|
+
delete queue.stateRoot;
|
|
368
|
+
const now = timestamp(nowInput, "now");
|
|
369
|
+
const result = mutate(queue, before, now);
|
|
370
|
+
queue.generation += 1;
|
|
371
|
+
queue.updatedAt = now;
|
|
372
|
+
const after = withQueueRoot(queue);
|
|
373
|
+
return { before, after, expectedOldStateRoot, result };
|
|
374
|
+
}
|
|
375
|
+
function submissionReceipt({ before, after, candidate, action, now }) {
|
|
376
|
+
const queueAgeSeconds = Math.max(
|
|
377
|
+
0,
|
|
378
|
+
Math.floor((Date.parse(now) - Date.parse(candidate.enqueuedAt)) / 1000),
|
|
379
|
+
);
|
|
380
|
+
return {
|
|
381
|
+
schema: DEV_DELIVERY_SUBMISSION_RECEIPT_SCHEMA,
|
|
382
|
+
repository: after.repository,
|
|
383
|
+
protectedBase: after.protectedBase,
|
|
384
|
+
candidateId: candidate.candidateId,
|
|
385
|
+
pullRequestNumber: candidate.pullRequestNumber,
|
|
386
|
+
sourceHead: candidate.sourceHead,
|
|
387
|
+
assignmentRoot: candidate.assignmentRoot,
|
|
388
|
+
initiativeRoot: candidate.initiativeRoot,
|
|
389
|
+
sourceIdentityRoot: candidate.sourceIdentityRoot,
|
|
390
|
+
sourcePatchRoot: candidate.sourcePatchRoot,
|
|
391
|
+
sourceProofRoot: candidate.sourceProofRoot,
|
|
392
|
+
planRoot: candidate.planRoot,
|
|
393
|
+
closureRoot: candidate.closureRoot,
|
|
394
|
+
dependencyRoot: candidate.dependencyRoot,
|
|
395
|
+
toolchainRoot: candidate.toolchainRoot,
|
|
396
|
+
sourceWorkflowRunId: candidate.sourceWorkflowRunId,
|
|
397
|
+
...(candidate.nativeCommandContract
|
|
398
|
+
? { nativeCommandContract: candidate.nativeCommandContract }
|
|
399
|
+
: {}),
|
|
400
|
+
...(candidate.shardEvidenceRoots
|
|
401
|
+
? { shardEvidenceRoots: candidate.shardEvidenceRoots }
|
|
402
|
+
: {}),
|
|
403
|
+
...(Object.hasOwn(candidate, "releaseBlockerPriority")
|
|
404
|
+
? { releaseBlockerPriority: candidate.releaseBlockerPriority }
|
|
405
|
+
: {}),
|
|
406
|
+
deliveryClass: candidate.deliveryClass,
|
|
407
|
+
priority: candidate.priority,
|
|
408
|
+
retainedEnqueuedAt: candidate.enqueuedAt,
|
|
409
|
+
queueAgeSeconds,
|
|
410
|
+
attempts: candidate.attempts,
|
|
411
|
+
action,
|
|
412
|
+
expectedOldStateRoot: before.stateRoot,
|
|
413
|
+
nextStateRoot: after.stateRoot,
|
|
414
|
+
nextAction:
|
|
415
|
+
"Wait for deterministic Warrant selection or observe the active predecessor.",
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
export function submitDevDeliveryCandidate(
|
|
419
|
+
queueInput,
|
|
420
|
+
input,
|
|
421
|
+
{ now = new Date().toISOString() } = {},
|
|
422
|
+
) {
|
|
423
|
+
const transaction = transitionDevDeliveryQueue(
|
|
424
|
+
queueInput,
|
|
425
|
+
(queue, before, currentTime) => {
|
|
426
|
+
const candidate = normalizeCandidate(
|
|
427
|
+
{
|
|
428
|
+
...input,
|
|
429
|
+
enqueuedAt: input.enqueuedAt || currentTime,
|
|
430
|
+
updatedAt: currentTime,
|
|
431
|
+
attempts: input.attempts || 1,
|
|
432
|
+
recoveries: input.recoveries || 0,
|
|
433
|
+
status: "queued",
|
|
434
|
+
},
|
|
435
|
+
queue,
|
|
436
|
+
);
|
|
437
|
+
const chainedInput = chainedDevDeliveryAttemptInput({
|
|
438
|
+
queue,
|
|
439
|
+
candidate,
|
|
440
|
+
input,
|
|
441
|
+
currentTime,
|
|
442
|
+
terminalStates: TERMINAL_STATES,
|
|
443
|
+
});
|
|
444
|
+
const attemptedCandidate = chainedInput
|
|
445
|
+
? normalizeCandidate(chainedInput, queue)
|
|
446
|
+
: candidate;
|
|
447
|
+
const conflicting = queue.candidates.find(
|
|
448
|
+
(entry) =>
|
|
449
|
+
entry.pullRequestNumber === attemptedCandidate.pullRequestNumber &&
|
|
450
|
+
entry.candidateId !== attemptedCandidate.candidateId &&
|
|
451
|
+
!TERMINAL_STATES.has(entry.status),
|
|
452
|
+
);
|
|
453
|
+
if (conflicting)
|
|
454
|
+
throw new Error(
|
|
455
|
+
`PR #${candidate.pullRequestNumber} already has a different active semantic source`,
|
|
456
|
+
);
|
|
457
|
+
const existing = queue.candidates.find(
|
|
458
|
+
(entry) => entry.candidateId === attemptedCandidate.candidateId,
|
|
459
|
+
);
|
|
460
|
+
let action = "submitted";
|
|
461
|
+
let selected = attemptedCandidate;
|
|
462
|
+
if (existing) {
|
|
463
|
+
if (TERMINAL_STATES.has(existing.status)) {
|
|
464
|
+
throw new Error(
|
|
465
|
+
`candidate ${attemptedCandidate.candidateId} is terminal and cannot be resubmitted`,
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
const exactProofFields = [
|
|
469
|
+
"sourcePatchRoot",
|
|
470
|
+
"sourceProofRoot",
|
|
471
|
+
"planRoot",
|
|
472
|
+
"closureRoot",
|
|
473
|
+
"dependencyRoot",
|
|
474
|
+
"toolchainRoot",
|
|
475
|
+
"environmentRoot",
|
|
476
|
+
"sourceWorkflowRunId",
|
|
477
|
+
];
|
|
478
|
+
const exactProofMatches =
|
|
479
|
+
exactProofFields.every(
|
|
480
|
+
(field) => existing[field] === attemptedCandidate[field],
|
|
481
|
+
) &&
|
|
482
|
+
JSON.stringify(existing.affectedPaths || []) ===
|
|
483
|
+
JSON.stringify(attemptedCandidate.affectedPaths || []) &&
|
|
484
|
+
JSON.stringify(existing.shardEvidenceRoots || []) ===
|
|
485
|
+
JSON.stringify(attemptedCandidate.shardEvidenceRoots || []) &&
|
|
486
|
+
existing.nativeCommandContract?.commandRoot ===
|
|
487
|
+
attemptedCandidate.nativeCommandContract?.commandRoot &&
|
|
488
|
+
existing.releaseBlockerPriority?.claimRoot ===
|
|
489
|
+
attemptedCandidate.releaseBlockerPriority?.claimRoot;
|
|
490
|
+
if (
|
|
491
|
+
existing.sourceHead === attemptedCandidate.sourceHead &&
|
|
492
|
+
exactProofMatches
|
|
493
|
+
) {
|
|
494
|
+
action = "duplicate-noop";
|
|
495
|
+
selected = existing;
|
|
496
|
+
} else {
|
|
497
|
+
if (before.activeWarrant?.candidateId === existing.candidateId) {
|
|
498
|
+
throw new Error(
|
|
499
|
+
"selected candidate sourceHead cannot change before terminal Warrant closeout",
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
const headChanged =
|
|
503
|
+
existing.sourceHead !== attemptedCandidate.sourceHead;
|
|
504
|
+
existing.sourceHead = attemptedCandidate.sourceHead;
|
|
505
|
+
for (const field of exactProofFields)
|
|
506
|
+
existing[field] = attemptedCandidate[field];
|
|
507
|
+
if (attemptedCandidate.nativeCommandContract)
|
|
508
|
+
existing.nativeCommandContract =
|
|
509
|
+
attemptedCandidate.nativeCommandContract;
|
|
510
|
+
else delete existing.nativeCommandContract;
|
|
511
|
+
if (attemptedCandidate.shardEvidenceRoots)
|
|
512
|
+
existing.shardEvidenceRoots = attemptedCandidate.shardEvidenceRoots;
|
|
513
|
+
else delete existing.shardEvidenceRoots;
|
|
514
|
+
if (Object.hasOwn(attemptedCandidate, "affectedPaths"))
|
|
515
|
+
existing.affectedPaths = attemptedCandidate.affectedPaths;
|
|
516
|
+
else delete existing.affectedPaths;
|
|
517
|
+
if (Object.hasOwn(attemptedCandidate, "releaseBlockerPriority"))
|
|
518
|
+
existing.releaseBlockerPriority =
|
|
519
|
+
attemptedCandidate.releaseBlockerPriority;
|
|
520
|
+
else delete existing.releaseBlockerPriority;
|
|
521
|
+
existing.updatedAt = currentTime;
|
|
522
|
+
existing.attempts += 1;
|
|
523
|
+
existing.status = "queued";
|
|
524
|
+
action = headChanged
|
|
525
|
+
? "safe-head-repair-retained-age"
|
|
526
|
+
: "safe-proof-refresh-retained-age";
|
|
527
|
+
selected = existing;
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
queue.candidates.push(attemptedCandidate);
|
|
531
|
+
}
|
|
532
|
+
return { candidate: selected, action };
|
|
533
|
+
},
|
|
534
|
+
now,
|
|
535
|
+
);
|
|
536
|
+
const receipt = submissionReceipt({
|
|
537
|
+
before: transaction.before,
|
|
538
|
+
after: transaction.after,
|
|
539
|
+
candidate: transaction.result.candidate,
|
|
540
|
+
action: transaction.result.action,
|
|
541
|
+
now: timestamp(now, "now"),
|
|
542
|
+
});
|
|
543
|
+
return {
|
|
544
|
+
queue: transaction.after,
|
|
545
|
+
receipt,
|
|
546
|
+
receiptRoot: devDeliveryContentRoot(receipt),
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function effectivePriority(candidate, policy, now) {
|
|
550
|
+
const ageSeconds = Math.max(
|
|
551
|
+
0,
|
|
552
|
+
Math.floor((Date.parse(now) - Date.parse(candidate.enqueuedAt)) / 1000),
|
|
553
|
+
);
|
|
554
|
+
const agingBoost = Math.floor(ageSeconds / policy.agingSeconds);
|
|
555
|
+
return {
|
|
556
|
+
ageSeconds,
|
|
557
|
+
basePriority: DEV_DELIVERY_PRIORITIES[candidate.priority],
|
|
558
|
+
agingBoost,
|
|
559
|
+
score: Math.min(
|
|
560
|
+
policy.maxPriority,
|
|
561
|
+
DEV_DELIVERY_PRIORITIES[candidate.priority] + agingBoost,
|
|
562
|
+
),
|
|
563
|
+
releaseBlocker: Boolean(candidate.releaseBlockerPriority),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
export function rankDevDeliveryCandidates(
|
|
567
|
+
queueInput,
|
|
568
|
+
{ now = new Date().toISOString(), allowLegacyV3Readback = false } = {},
|
|
569
|
+
) {
|
|
570
|
+
const queue = normalizeDevDeliveryQueue(queueInput, {
|
|
571
|
+
allowLegacyV3Readback,
|
|
572
|
+
});
|
|
573
|
+
const currentTime = timestamp(now, "now");
|
|
574
|
+
return queue.candidates
|
|
575
|
+
.filter((candidate) => candidate.status === "queued")
|
|
576
|
+
.map((candidate) => ({
|
|
577
|
+
candidate,
|
|
578
|
+
priority: effectivePriority(candidate, queue.policy, currentTime),
|
|
579
|
+
}))
|
|
580
|
+
.sort(
|
|
581
|
+
(left, right) =>
|
|
582
|
+
compareReleaseBlockerPriority(left, right) ||
|
|
583
|
+
right.priority.score - left.priority.score ||
|
|
584
|
+
left.candidate.enqueuedAt.localeCompare(right.candidate.enqueuedAt) ||
|
|
585
|
+
left.candidate.candidateId.localeCompare(right.candidate.candidateId),
|
|
586
|
+
);
|
|
587
|
+
}
|