@kungfu-tech/buildchain 4.0.1-alpha.5 → 4.0.1-alpha.55
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/README.md +9 -6
- package/actions/release-tail/README.md +8 -0
- package/actions/v4-release-candidate-promote/README.md +3 -0
- package/architecture/agent-change-map.md +1015 -0
- package/architecture/ci-lane-change-budget.json +256 -0
- package/architecture/decisions/0004-v4-declarative-release-tail.md +47 -0
- package/architecture/internal-capabilities.json +220 -15
- package/architecture/maintainability-debt.json +3229 -0
- package/architecture/maintainability-policy.json +489 -132
- package/architecture/release-tail-contract-inventory.json +5 -2
- package/architecture/v3-core-mechanism-inventory.json +4 -3
- package/architecture/v3-v4-capability-closure.md +59 -0
- package/architecture/v3-v4-live-capability-inventory.json +102404 -0
- package/architecture/v4-adopter-delivery-parity.json +32 -5
- package/architecture/v4-cross-platform-adopter-qualification.json +52 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +3 -3
- package/architecture/v4-floating-consumer-policy.json +6 -0
- package/architecture/v4-publication-rehearsal-parity.json +84 -0
- package/architecture/v4-runtime-semantic-closure.json +710 -0
- package/bin/buildchain.mjs +7 -1
- package/bin/internal/adopter-delivery-cli.mjs +70 -0
- package/bin/internal/command-registry.mjs +1 -0
- package/bin/internal/trust-release-release-handlers.mjs +5 -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-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 +1 -1
- package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
- package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
- package/contracts/v4-publication-qualification-v1.schema.json +51 -0
- package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/agent-index.json +4 -0
- package/dist/site/artifact-schemas.json +8 -0
- package/dist/site/buildchain-contract.json +125 -34
- package/dist/site/buildchain-site.json +308 -33
- package/dist/site/capability-registry.json +13 -12
- package/dist/site/cli-registry.json +57 -2
- package/dist/site/controller-registry.json +41 -5
- package/dist/site/kfd-claims.json +438 -24
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +22 -7
- package/dist/site/node-api-registry.json +12843 -9314
- package/dist/site/page-registry.json +288 -21
- package/dist/site/public-surface-audit.json +281 -21
- package/dist/site/publication-authority-registry.json +66 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +20 -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-publication-qualification-v1.schema.json +51 -0
- package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/site-manifest.json +19 -11
- package/dist/site/workflow-registry.json +195 -17
- package/docs/MAP.md +4 -2
- package/docs/cli-reference.md +70 -2
- package/docs/cli.md +8 -0
- package/docs/dev-delivery-warrant.md +34 -10
- package/docs/node-api-reference.md +524 -279
- package/docs/publish-transaction.md +11 -4
- package/docs/release-candidate.md +7 -0
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/v4-adopter-delivery.md +152 -0
- package/docs/v4-declarative-release-tail-migration.md +43 -0
- package/docs/v4-publication-rehearsal.md +130 -0
- package/package.json +24 -4
- package/packages/core/adopter-delivery-passport.js +295 -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 +1 -0
- package/packages/core/buildchain-config.js +84 -0
- package/packages/core/buildchain-contract.js +31 -1
- 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 +2 -0
- package/packages/core/ci-lane-change-budget.js +247 -0
- package/packages/core/dev-delivery-native-proof.js +43 -27
- package/packages/core/dev-delivery-warrant-settlement.js +239 -3
- package/packages/core/dev-delivery-warrant-state.js +46 -10
- package/packages/core/dev-delivery-warrant.js +29 -7
- package/packages/core/github-governance-authority.js +5 -4
- package/packages/core/index.js +6 -0
- package/packages/core/legacy-kfd-adopter-driver.js +167 -0
- package/packages/core/paper-scaffold-content.js +401 -0
- package/packages/core/paper.js +12 -378
- package/packages/core/publication-control-plane-audit.js +135 -111
- package/packages/core/publication-rehearsal-projection.js +104 -0
- package/packages/core/publication-rehearsal-runtime.js +190 -0
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-candidate-recovery.js +71 -15
- package/packages/core/release-candidate.js +2 -2
- package/packages/core/release-passport.js +95 -170
- package/packages/core/v4-adopter-delivery-parity.js +3 -3
- package/packages/core/v4-adopter-delivery.js +359 -0
- package/packages/core/v4-cross-platform-adopter-qualification.js +343 -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 +139 -35
- package/packages/core/v4-floating-consumer-release-passport.js +154 -8
- package/packages/core/v4-publication-qualification.js +222 -0
- 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/workflow-yaml-contract.js +24 -2
- package/scripts/artifact-signing-controller-core.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +17 -17
- package/scripts/buildchain-cli-help.mjs +13 -0
- package/scripts/check-ci-lane-change-budget.mjs +100 -0
- package/scripts/check-internal-architecture.mjs +23 -3
- package/scripts/check-inventory.mjs +24 -6
- package/scripts/check-maintainability.mjs +145 -4
- package/scripts/check-project-cut-merge-queue-admission.mjs +87 -0
- package/scripts/check-v3-v4-capability-inventory.mjs +574 -0
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +30 -4
- package/scripts/check-v4-runtime-semantic-closure.mjs +266 -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-warrant-options.mjs +27 -11
- package/scripts/dev-delivery-warrant-store.mjs +10 -6
- package/scripts/dev-delivery-warrant.mjs +50 -6
- package/scripts/dev-pr-auto-merge.mjs +25 -11
- package/scripts/dev-pr-delivery-warrant.mjs +45 -0
- package/scripts/dev-pr-prequeue-guard.mjs +0 -18
- package/scripts/dev-qualification-patrol.mjs +9 -9
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/generate-agent-change-map.mjs +118 -0
- package/scripts/generate-channel-promotion-workflow.mjs +2 -2
- package/scripts/generate-release-candidate-passport.mjs +177 -0
- package/scripts/generate-site-bundle.mjs +36 -0
- package/scripts/maintainability-domain-metrics.mjs +237 -0
- package/scripts/maintainability-governance.mjs +473 -0
- package/scripts/maintainability-metrics.mjs +28 -6
- package/scripts/project-cut-merge-queue-admission.mjs +394 -0
- package/scripts/promotion-channel-router.mjs +4 -4
- package/scripts/promotion-routing-evidence.mjs +35 -6
- package/scripts/release-candidate-anchor-provenance.mjs +99 -0
- package/scripts/release-candidate-resolver.mjs +44 -0
- package/scripts/release-line-policy.mjs +25 -15
- package/scripts/release-tail.mjs +68 -3
- package/scripts/resume-from-candidate-run.mjs +595 -65
- package/scripts/run-lifecycle-core.mjs +341 -376
- package/scripts/site-capability-metadata.mjs +21 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v3-v4-capability-catalog.mjs +439 -0
- package/scripts/v4-consumer-policy.mjs +4 -0
- package/scripts/v4-cross-platform-adopter-qualification.mjs +228 -0
- package/scripts/v4-declarative-promotion-admission.mjs +42 -0
- package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ADOPTER_DELIVERY_GATE_RESULT_CONTRACT,
|
|
3
|
+
adopterDeliveryGateDigest,
|
|
4
|
+
} from "./adopter-delivery-gate.js";
|
|
5
|
+
|
|
6
|
+
export const ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT =
|
|
7
|
+
"kungfu-buildchain-adopter-delivery-passport-binding";
|
|
8
|
+
|
|
9
|
+
const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
10
|
+
const EXACT_SHA_PATTERN = /^[0-9a-f]{40}$/u;
|
|
11
|
+
|
|
12
|
+
export function releasePassportKfdAdopterSourceSha({
|
|
13
|
+
passportSourceSha = "",
|
|
14
|
+
release = undefined,
|
|
15
|
+
} = {}) {
|
|
16
|
+
const exactSha = (value) =>
|
|
17
|
+
EXACT_SHA_PATTERN.test(String(value || "").trim());
|
|
18
|
+
const candidateSourceSha = String(release?.candidateSourceSha || "").trim();
|
|
19
|
+
const builtSourceSha = String(release?.builtSourceSha || "").trim();
|
|
20
|
+
const promotionChannelSha = String(release?.promotionChannelSha || "").trim();
|
|
21
|
+
const candidateSourceTreeSha = String(
|
|
22
|
+
release?.candidateSourceTreeSha || "",
|
|
23
|
+
).trim();
|
|
24
|
+
const builtSourceTreeSha = String(release?.builtSourceTreeSha || "").trim();
|
|
25
|
+
const promotionChannelTreeSha = String(
|
|
26
|
+
release?.promotionChannelTreeSha || "",
|
|
27
|
+
).trim();
|
|
28
|
+
if (
|
|
29
|
+
release?.treeEquivalent !== true ||
|
|
30
|
+
!exactSha(passportSourceSha) ||
|
|
31
|
+
!exactSha(candidateSourceSha) ||
|
|
32
|
+
!exactSha(builtSourceSha) ||
|
|
33
|
+
!exactSha(promotionChannelSha) ||
|
|
34
|
+
candidateSourceSha !== builtSourceSha ||
|
|
35
|
+
promotionChannelSha !== passportSourceSha ||
|
|
36
|
+
!exactSha(candidateSourceTreeSha) ||
|
|
37
|
+
!exactSha(builtSourceTreeSha) ||
|
|
38
|
+
!exactSha(promotionChannelTreeSha) ||
|
|
39
|
+
candidateSourceTreeSha !== builtSourceTreeSha ||
|
|
40
|
+
candidateSourceTreeSha !== promotionChannelTreeSha
|
|
41
|
+
) {
|
|
42
|
+
return passportSourceSha;
|
|
43
|
+
}
|
|
44
|
+
return candidateSourceSha;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function issue(code, message, details = {}) {
|
|
48
|
+
return { code, message, details };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function validateRootedJson(value, rootField, code, label) {
|
|
52
|
+
const root = value?.[rootField];
|
|
53
|
+
if (!ROOT_PATTERN.test(root ?? "")) {
|
|
54
|
+
return [issue(code, `${label} must be a canonical sha256 root`)];
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const preimage = structuredClone(value);
|
|
58
|
+
delete preimage[rootField];
|
|
59
|
+
return adopterDeliveryGateDigest(preimage) === root
|
|
60
|
+
? []
|
|
61
|
+
: [issue(code, `${label} does not match its exact JSON closure`)];
|
|
62
|
+
} catch {
|
|
63
|
+
return [issue(code, `${label} requires finite acyclic JSON`)];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function validateGateIdentity(result) {
|
|
68
|
+
const issues = [];
|
|
69
|
+
if (
|
|
70
|
+
result.schemaVersion !== 1 ||
|
|
71
|
+
result.contract !== ADOPTER_DELIVERY_GATE_RESULT_CONTRACT
|
|
72
|
+
) {
|
|
73
|
+
issues.push(
|
|
74
|
+
issue(
|
|
75
|
+
"adopterDelivery.gateResult.contract",
|
|
76
|
+
"adopter delivery gate result contract is unsupported",
|
|
77
|
+
),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (
|
|
81
|
+
!result.project ||
|
|
82
|
+
!result.artifact ||
|
|
83
|
+
!result.protocol ||
|
|
84
|
+
!result.artifactProfile
|
|
85
|
+
) {
|
|
86
|
+
issues.push(
|
|
87
|
+
issue(
|
|
88
|
+
"adopterDelivery.gateResult.closure",
|
|
89
|
+
"gate result must retain project, artifact, protocol, and artifact-profile bindings",
|
|
90
|
+
),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (!ROOT_PATTERN.test(result.artifact?.root ?? "")) {
|
|
94
|
+
issues.push(
|
|
95
|
+
issue(
|
|
96
|
+
"adopterDelivery.gateResult.artifact.root",
|
|
97
|
+
"delivery artifact root must be a canonical sha256 root",
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (result.qualifying !== false || result.selfCertified !== false) {
|
|
102
|
+
issues.push(
|
|
103
|
+
issue(
|
|
104
|
+
"adopterDelivery.gateResult.nonClaims",
|
|
105
|
+
"delivery evidence must remain non-qualifying and non-self-certifying",
|
|
106
|
+
),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return issues;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function validatePassedGate(result) {
|
|
113
|
+
if (result.status !== "passed") {
|
|
114
|
+
return result.status === "failed"
|
|
115
|
+
? []
|
|
116
|
+
: [
|
|
117
|
+
issue(
|
|
118
|
+
"adopterDelivery.gateResult.status",
|
|
119
|
+
"gate status must be passed or failed",
|
|
120
|
+
),
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
const passed =
|
|
124
|
+
result.protocol?.loaded === true &&
|
|
125
|
+
ROOT_PATTERN.test(result.protocol?.reportRoot ?? "") &&
|
|
126
|
+
result.artifactProfile?.loaded === true &&
|
|
127
|
+
result.artifactProfile?.valid === true &&
|
|
128
|
+
result.semanticReport !== null &&
|
|
129
|
+
(result.issues?.length ?? -1) === 0;
|
|
130
|
+
return passed
|
|
131
|
+
? []
|
|
132
|
+
: [
|
|
133
|
+
issue(
|
|
134
|
+
"adopterDelivery.gateResult.status",
|
|
135
|
+
"a passed gate must retain successful exact driver and artifact-profile evidence",
|
|
136
|
+
),
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function validateAdopterDeliveryGateResult(result) {
|
|
141
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
142
|
+
return {
|
|
143
|
+
valid: false,
|
|
144
|
+
passed: false,
|
|
145
|
+
issues: [
|
|
146
|
+
issue(
|
|
147
|
+
"adopterDelivery.gateResult",
|
|
148
|
+
"adopter delivery gate result must be an object",
|
|
149
|
+
),
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const issues = [
|
|
154
|
+
...validateGateIdentity(result),
|
|
155
|
+
...validateRootedJson(
|
|
156
|
+
result,
|
|
157
|
+
"gateRoot",
|
|
158
|
+
"adopterDelivery.gateResult.gateRoot",
|
|
159
|
+
"adopter delivery gate root",
|
|
160
|
+
),
|
|
161
|
+
...validatePassedGate(result),
|
|
162
|
+
];
|
|
163
|
+
const valid = issues.length === 0;
|
|
164
|
+
return { valid, passed: valid && result.status === "passed", issues };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function createAdopterDeliveryPassportBinding(gateResult) {
|
|
168
|
+
const normalized = structuredClone(gateResult);
|
|
169
|
+
const validation = validateAdopterDeliveryGateResult(normalized);
|
|
170
|
+
const binding = {
|
|
171
|
+
schemaVersion: 1,
|
|
172
|
+
contract: ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT,
|
|
173
|
+
valid: validation.passed,
|
|
174
|
+
qualifying: false,
|
|
175
|
+
selfCertified: false,
|
|
176
|
+
gateResult: normalized,
|
|
177
|
+
validationIssues: validation.issues,
|
|
178
|
+
nonClaims: [
|
|
179
|
+
"A passing adopter delivery gate does not grant runtime permission or release authority.",
|
|
180
|
+
"The selected protocol driver retains semantic authority; Buildchain only binds its exact result into delivery evidence.",
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
binding.bindingRoot = adopterDeliveryGateDigest(binding);
|
|
184
|
+
return binding;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function normalizeAdopterDeliveryPassportBinding(value) {
|
|
188
|
+
if (!value) return undefined;
|
|
189
|
+
return value.contract === ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT
|
|
190
|
+
? structuredClone(value)
|
|
191
|
+
: createAdopterDeliveryPassportBinding(value);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function validateAdopterDeliveryPassportBinding(
|
|
195
|
+
binding,
|
|
196
|
+
{ expectedProjectRepository = "" } = {},
|
|
197
|
+
) {
|
|
198
|
+
if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
|
|
199
|
+
return [
|
|
200
|
+
issue(
|
|
201
|
+
"adopterDelivery.binding",
|
|
202
|
+
"adopter delivery passport binding must be an object",
|
|
203
|
+
),
|
|
204
|
+
];
|
|
205
|
+
}
|
|
206
|
+
const gateValidation = validateAdopterDeliveryGateResult(binding.gateResult);
|
|
207
|
+
const issues = [
|
|
208
|
+
...validateRootedJson(
|
|
209
|
+
binding,
|
|
210
|
+
"bindingRoot",
|
|
211
|
+
"adopterDelivery.binding.bindingRoot",
|
|
212
|
+
"adopter delivery binding root",
|
|
213
|
+
),
|
|
214
|
+
...gateValidation.issues,
|
|
215
|
+
];
|
|
216
|
+
if (
|
|
217
|
+
binding.schemaVersion !== 1 ||
|
|
218
|
+
binding.contract !== ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT
|
|
219
|
+
) {
|
|
220
|
+
issues.push(
|
|
221
|
+
issue(
|
|
222
|
+
"adopterDelivery.binding.contract",
|
|
223
|
+
"adopter delivery passport binding contract is unsupported",
|
|
224
|
+
),
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
if (
|
|
228
|
+
binding.valid !== gateValidation.passed ||
|
|
229
|
+
binding.qualifying !== false ||
|
|
230
|
+
binding.selfCertified !== false
|
|
231
|
+
) {
|
|
232
|
+
issues.push(
|
|
233
|
+
issue(
|
|
234
|
+
"adopterDelivery.binding.status",
|
|
235
|
+
"binding status must preserve a passing non-authoritative gate result",
|
|
236
|
+
),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
if (!gateValidation.passed) {
|
|
240
|
+
issues.push(
|
|
241
|
+
issue(
|
|
242
|
+
"adopterDelivery.status",
|
|
243
|
+
"release passport requires a passed adopter delivery gate",
|
|
244
|
+
),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
if (
|
|
248
|
+
expectedProjectRepository &&
|
|
249
|
+
binding.gateResult?.project?.adopterId !== expectedProjectRepository
|
|
250
|
+
) {
|
|
251
|
+
issues.push(
|
|
252
|
+
issue(
|
|
253
|
+
"adopterDelivery.project",
|
|
254
|
+
"delivery project must match the release passport product repository",
|
|
255
|
+
),
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return issues;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function sameJson(left, right) {
|
|
262
|
+
try {
|
|
263
|
+
return adopterDeliveryGateDigest(left) === adopterDeliveryGateDigest(right);
|
|
264
|
+
} catch {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function validateAdopterDeliveryReleaseEvidence({
|
|
270
|
+
binding,
|
|
271
|
+
artifactBinding,
|
|
272
|
+
expectedProjectRepository = "",
|
|
273
|
+
} = {}) {
|
|
274
|
+
if (!binding && !artifactBinding) return [];
|
|
275
|
+
if (!binding) {
|
|
276
|
+
return [
|
|
277
|
+
issue(
|
|
278
|
+
"adopterDelivery.passport",
|
|
279
|
+
"artifact evidence cannot introduce delivery authority absent from the passport",
|
|
280
|
+
),
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
const issues = validateAdopterDeliveryPassportBinding(binding, {
|
|
284
|
+
expectedProjectRepository,
|
|
285
|
+
});
|
|
286
|
+
if (!artifactBinding || !sameJson(artifactBinding, binding)) {
|
|
287
|
+
issues.push(
|
|
288
|
+
issue(
|
|
289
|
+
"adopterDelivery.artifactEvidence",
|
|
290
|
+
"passport and artifact evidence must bind the same exact delivery result",
|
|
291
|
+
),
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
return issues;
|
|
295
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import vectorSuite from "./adopter-delivery-vectors.json" with { type: "json" };
|
|
2
|
+
|
|
3
|
+
import { adopterDeliveryGateDigest } from "./adopter-delivery-gate.js";
|
|
4
|
+
|
|
5
|
+
export const ADOPTER_DELIVERY_VECTOR_SUITE_CONTRACT =
|
|
6
|
+
"kungfu-buildchain-adopter-delivery-vectors/v1";
|
|
7
|
+
|
|
8
|
+
const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
9
|
+
const REQUIRED_CASES = new Map([
|
|
10
|
+
["golden-two-driver-offline-replay", "golden"],
|
|
11
|
+
["negative-driver-mismatch", "negative"],
|
|
12
|
+
["negative-category-conflict", "negative"],
|
|
13
|
+
["negative-evidence-substitution", "negative"],
|
|
14
|
+
["negative-stale-package-cut", "negative"],
|
|
15
|
+
["fault-driver-throw", "fault"],
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function freezeJson(value) {
|
|
19
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
20
|
+
Object.freeze(value);
|
|
21
|
+
for (const child of Object.values(value)) freezeJson(child);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function issue(code, path, message) {
|
|
27
|
+
return { code, path, message };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function suiteDigest(suite) {
|
|
31
|
+
const preimage = structuredClone(suite);
|
|
32
|
+
delete preimage.suiteRoot;
|
|
33
|
+
return adopterDeliveryGateDigest(preimage);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function validateAdopterDeliveryVectorSuite(suite) {
|
|
37
|
+
const issues = [];
|
|
38
|
+
if (!suite || typeof suite !== "object" || Array.isArray(suite)) {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
issues: [
|
|
42
|
+
issue(
|
|
43
|
+
"adopter-delivery-vectors.invalid",
|
|
44
|
+
"/",
|
|
45
|
+
"Vector suite must be a JSON object.",
|
|
46
|
+
),
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (
|
|
51
|
+
suite.schemaVersion !== 1 ||
|
|
52
|
+
suite.contract !== ADOPTER_DELIVERY_VECTOR_SUITE_CONTRACT
|
|
53
|
+
) {
|
|
54
|
+
issues.push(
|
|
55
|
+
issue(
|
|
56
|
+
"adopter-delivery-vectors.contract",
|
|
57
|
+
"/contract",
|
|
58
|
+
"Vector suite contract is unsupported.",
|
|
59
|
+
),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (!Array.isArray(suite.cases)) {
|
|
63
|
+
issues.push(
|
|
64
|
+
issue(
|
|
65
|
+
"adopter-delivery-vectors.cases",
|
|
66
|
+
"/cases",
|
|
67
|
+
"Vector suite cases must be an array.",
|
|
68
|
+
),
|
|
69
|
+
);
|
|
70
|
+
} else {
|
|
71
|
+
const seen = new Set();
|
|
72
|
+
for (const [index, entry] of suite.cases.entries()) {
|
|
73
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
74
|
+
issues.push(
|
|
75
|
+
issue(
|
|
76
|
+
"adopter-delivery-vectors.case",
|
|
77
|
+
`/cases/${index}`,
|
|
78
|
+
"Vector case must be an object.",
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (typeof entry.id !== "string" || seen.has(entry.id)) {
|
|
84
|
+
issues.push(
|
|
85
|
+
issue(
|
|
86
|
+
"adopter-delivery-vectors.case-id",
|
|
87
|
+
`/cases/${index}/id`,
|
|
88
|
+
"Vector case id must be non-empty and unique.",
|
|
89
|
+
),
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
seen.add(entry.id);
|
|
93
|
+
if (!entry.expected || typeof entry.expected !== "object") {
|
|
94
|
+
issues.push(
|
|
95
|
+
issue(
|
|
96
|
+
"adopter-delivery-vectors.expected",
|
|
97
|
+
`/cases/${index}/expected`,
|
|
98
|
+
"Vector case must declare its expected result.",
|
|
99
|
+
),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (const [id, className] of REQUIRED_CASES) {
|
|
104
|
+
const entry = suite.cases.find((candidate) => candidate?.id === id);
|
|
105
|
+
if (!entry || entry.class !== className) {
|
|
106
|
+
issues.push(
|
|
107
|
+
issue(
|
|
108
|
+
"adopter-delivery-vectors.required-case",
|
|
109
|
+
"/cases",
|
|
110
|
+
`Required ${className} vector ${id} is missing.`,
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!ROOT_PATTERN.test(suite.suiteRoot ?? "")) {
|
|
117
|
+
issues.push(
|
|
118
|
+
issue(
|
|
119
|
+
"adopter-delivery-vectors.root",
|
|
120
|
+
"/suiteRoot",
|
|
121
|
+
"Vector suite root must be a canonical sha256 root.",
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
} else {
|
|
125
|
+
try {
|
|
126
|
+
if (suiteDigest(suite) !== suite.suiteRoot) {
|
|
127
|
+
issues.push(
|
|
128
|
+
issue(
|
|
129
|
+
"adopter-delivery-vectors.root",
|
|
130
|
+
"/suiteRoot",
|
|
131
|
+
"Vector suite root does not match the exact JSON closure.",
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
issues.push(
|
|
137
|
+
issue(
|
|
138
|
+
"adopter-delivery-vectors.root",
|
|
139
|
+
"/suiteRoot",
|
|
140
|
+
"Vector suite must contain finite acyclic JSON.",
|
|
141
|
+
),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { valid: issues.length === 0, issues };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const ADOPTER_DELIVERY_VECTOR_SUITE = freezeJson(
|
|
149
|
+
structuredClone(vectorSuite),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
export function getAdopterDeliveryVector(id) {
|
|
153
|
+
const entry = ADOPTER_DELIVERY_VECTOR_SUITE.cases.find(
|
|
154
|
+
(candidate) => candidate.id === id,
|
|
155
|
+
);
|
|
156
|
+
if (!entry) throw new RangeError(`Unknown adopter delivery vector: ${id}`);
|
|
157
|
+
return structuredClone(entry);
|
|
158
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"contract": "kungfu-buildchain-adopter-delivery-vectors/v1",
|
|
4
|
+
"rootAlgorithm": "sha256-buildchain-canonical-json-v1",
|
|
5
|
+
"authorities": {
|
|
6
|
+
"kfdPackage": "@kungfu-tech/kfd@1.0.0-alpha.65",
|
|
7
|
+
"categoryProfileVectors": "@kungfu-tech/kfd/adopter-conformance/category-profile-vectors.json",
|
|
8
|
+
"categoryInstanceVectors": "@kungfu-tech/kfd/adopter-conformance/category-instance-vectors.json"
|
|
9
|
+
},
|
|
10
|
+
"cases": [
|
|
11
|
+
{
|
|
12
|
+
"id": "golden-two-driver-offline-replay",
|
|
13
|
+
"class": "golden",
|
|
14
|
+
"surface": "protocol-neutral-gate",
|
|
15
|
+
"operation": "evaluate identical exact requests with two isolated protocol drivers and replay each request offline",
|
|
16
|
+
"expected": {
|
|
17
|
+
"statuses": ["passed", "passed"],
|
|
18
|
+
"issueCodes": [],
|
|
19
|
+
"deterministicRoots": true,
|
|
20
|
+
"qualifying": false,
|
|
21
|
+
"selfCertified": false
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "negative-driver-mismatch",
|
|
26
|
+
"class": "negative",
|
|
27
|
+
"surface": "protocol-neutral-gate",
|
|
28
|
+
"operation": "select an exact protocol identity and version that are not registered",
|
|
29
|
+
"expected": {
|
|
30
|
+
"status": "failed",
|
|
31
|
+
"issueCodes": ["delivery-driver-unknown"]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "negative-category-conflict",
|
|
36
|
+
"class": "negative",
|
|
37
|
+
"surface": "published-kfd-category-driver",
|
|
38
|
+
"operation": "append a duplicate published category profile to the exact KFD instance selection",
|
|
39
|
+
"expected": {
|
|
40
|
+
"status": "failed",
|
|
41
|
+
"issueCodes": ["acp-composition-invalid"]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "negative-evidence-substitution",
|
|
46
|
+
"class": "negative",
|
|
47
|
+
"surface": "release-passport-binding",
|
|
48
|
+
"operation": "substitute the artifact root in artifact evidence after binding the passport",
|
|
49
|
+
"expected": {
|
|
50
|
+
"status": "failed",
|
|
51
|
+
"issueCodes": ["adopterDelivery.artifactEvidence"]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "negative-stale-package-cut",
|
|
56
|
+
"class": "negative",
|
|
57
|
+
"surface": "published-kfd-category-driver",
|
|
58
|
+
"operation": "replace the KFD instance package version with an uninstalled stale cut",
|
|
59
|
+
"expected": {
|
|
60
|
+
"status": "failed",
|
|
61
|
+
"issueCodes": ["delivery-kfd-package-cut-mismatch"]
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "fault-driver-throw",
|
|
66
|
+
"class": "fault",
|
|
67
|
+
"surface": "protocol-neutral-gate",
|
|
68
|
+
"operation": "throw a private exception from the selected protocol driver",
|
|
69
|
+
"expected": {
|
|
70
|
+
"status": "failed",
|
|
71
|
+
"issueCodes": ["delivery-driver-error"],
|
|
72
|
+
"privateDetailRedacted": true
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"suiteRoot": "sha256:c978707556406ffda4ef4192032332c01c2da1cebb6ad8c4f0edfc65d5cef0c7"
|
|
77
|
+
}
|
|
@@ -30,6 +30,7 @@ export const BUILDCHAIN_AGENT_MANUALS = Object.freeze([
|
|
|
30
30
|
{ id: "kfd-agent-hub", title: "KFD Agent Hub Builder flow", path: "docs/kfd-agent-hub.md", plane: "use" },
|
|
31
31
|
{ id: "lifecycle-protocol", title: "Lifecycle protocol", path: "docs/lifecycle-protocol.md", plane: "use" },
|
|
32
32
|
{ id: "reusable-build-surface", title: "Reusable build surface", path: "docs/reusable-build-surface.md", plane: "use" },
|
|
33
|
+
{ id: "v4-adopter-delivery", title: "V4 public adopter delivery", path: "docs/v4-adopter-delivery.md", plane: "verify" },
|
|
33
34
|
{ id: "publish-transaction", title: "Publish transaction", path: "docs/publish-transaction.md", plane: "verify" },
|
|
34
35
|
{ id: "release-tail-contract", title: "Declarative release-tail contract", path: "docs/release-tail-contract.md", plane: "verify" },
|
|
35
36
|
{ id: "release-tail-provider-plane", title: "Declarative release-tail provider plane", path: "docs/release-tail-provider-plane.md", plane: "use" },
|
|
@@ -236,6 +236,16 @@ export function normalizeBuildchainConfig(config) {
|
|
|
236
236
|
if (normalized.publication !== undefined) {
|
|
237
237
|
normalized.publication = normalizePublicationSection(normalized.publication);
|
|
238
238
|
}
|
|
239
|
+
if (normalized.publication_rehearsal !== undefined) {
|
|
240
|
+
normalized.publication_rehearsal = normalizePublicationRehearsalSection(
|
|
241
|
+
normalized.publication_rehearsal,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
if (normalized.adopter_delivery !== undefined) {
|
|
245
|
+
normalized.adopter_delivery = normalizeAdopterDeliverySection(
|
|
246
|
+
normalized.adopter_delivery,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
239
249
|
if (normalized.consumers !== undefined) {
|
|
240
250
|
normalized.consumers = normalizeConsumersSection(normalized.consumers);
|
|
241
251
|
}
|
|
@@ -700,6 +710,80 @@ function normalizePublicationSection(publication) {
|
|
|
700
710
|
};
|
|
701
711
|
}
|
|
702
712
|
|
|
713
|
+
function normalizePublicationRehearsalSection(rehearsal) {
|
|
714
|
+
assertPlainObject(rehearsal, "publication_rehearsal");
|
|
715
|
+
const allowed = ["contract", "capsule_path", "candidate_root", "state_path", "evidence_path", "effect_default"];
|
|
716
|
+
const unknown = Object.keys(rehearsal).filter((key) => !allowed.includes(key));
|
|
717
|
+
if (unknown.length > 0) {
|
|
718
|
+
throw new Error(`publication_rehearsal contains unsupported fields: ${unknown.join(", ")}`);
|
|
719
|
+
}
|
|
720
|
+
const normalized = {
|
|
721
|
+
contract: assertString(rehearsal.contract, "publication_rehearsal.contract"),
|
|
722
|
+
capsulePath: posixPath(assertString(rehearsal.capsule_path, "publication_rehearsal.capsule_path")),
|
|
723
|
+
candidateRoot: posixPath(assertString(rehearsal.candidate_root, "publication_rehearsal.candidate_root")),
|
|
724
|
+
statePath: posixPath(assertString(rehearsal.state_path, "publication_rehearsal.state_path")),
|
|
725
|
+
evidencePath: posixPath(assertString(rehearsal.evidence_path, "publication_rehearsal.evidence_path")),
|
|
726
|
+
effectDefault: assertString(rehearsal.effect_default, "publication_rehearsal.effect_default"),
|
|
727
|
+
};
|
|
728
|
+
if (
|
|
729
|
+
normalized.contract !== "buildchain-v4-publication-rehearsal-capsule/v1" ||
|
|
730
|
+
normalized.effectDefault !== "disabled"
|
|
731
|
+
) {
|
|
732
|
+
throw new Error("publication_rehearsal must use the v4 capsule contract with effect_default = disabled");
|
|
733
|
+
}
|
|
734
|
+
for (const [key, value] of Object.entries({
|
|
735
|
+
capsule_path: normalized.capsulePath,
|
|
736
|
+
candidate_root: normalized.candidateRoot,
|
|
737
|
+
state_path: normalized.statePath,
|
|
738
|
+
evidence_path: normalized.evidencePath,
|
|
739
|
+
})) {
|
|
740
|
+
if (value.startsWith("/") || value.includes("\\") || value.split("/").includes("..")) {
|
|
741
|
+
throw new Error(`publication_rehearsal.${key} must be repository-relative`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return normalized;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function normalizeAdopterDeliverySection(delivery) {
|
|
748
|
+
assertPlainObject(delivery, "adopter_delivery");
|
|
749
|
+
const allowed = ["contract", "input_path", "readback_path", "bootstrap_path", "archive_path", "result_path", "driver_selector", "artifact_profile_selector"];
|
|
750
|
+
const unknown = Object.keys(delivery).filter((key) => !allowed.includes(key));
|
|
751
|
+
if (unknown.length > 0) {
|
|
752
|
+
throw new Error(`adopter_delivery contains unsupported fields: ${unknown.join(", ")}`);
|
|
753
|
+
}
|
|
754
|
+
const normalized = {
|
|
755
|
+
contract: assertString(delivery.contract, "adopter_delivery.contract"),
|
|
756
|
+
inputPath: posixPath(assertString(delivery.input_path, "adopter_delivery.input_path")),
|
|
757
|
+
readbackPath: posixPath(assertString(delivery.readback_path, "adopter_delivery.readback_path")),
|
|
758
|
+
bootstrapPath: posixPath(assertString(delivery.bootstrap_path, "adopter_delivery.bootstrap_path")),
|
|
759
|
+
archivePath: posixPath(assertString(delivery.archive_path, "adopter_delivery.archive_path")),
|
|
760
|
+
resultPath: posixPath(assertString(delivery.result_path, "adopter_delivery.result_path")),
|
|
761
|
+
driverSelector: assertString(delivery.driver_selector, "adopter_delivery.driver_selector"),
|
|
762
|
+
artifactProfileSelector: assertString(delivery.artifact_profile_selector, "adopter_delivery.artifact_profile_selector"),
|
|
763
|
+
};
|
|
764
|
+
if (normalized.contract !== "kungfu-buildchain-v4-adopter-delivery/v1") {
|
|
765
|
+
throw new Error("adopter_delivery contract is unsupported");
|
|
766
|
+
}
|
|
767
|
+
if (
|
|
768
|
+
!["json-assertion", "kfd-category", "legacy-kfd"].includes(normalized.driverSelector) ||
|
|
769
|
+
!["git-commit", "package"].includes(normalized.artifactProfileSelector)
|
|
770
|
+
) {
|
|
771
|
+
throw new Error("adopter_delivery selector is unsupported");
|
|
772
|
+
}
|
|
773
|
+
for (const [key, value] of Object.entries({
|
|
774
|
+
input_path: normalized.inputPath,
|
|
775
|
+
readback_path: normalized.readbackPath,
|
|
776
|
+
bootstrap_path: normalized.bootstrapPath,
|
|
777
|
+
archive_path: normalized.archivePath,
|
|
778
|
+
result_path: normalized.resultPath,
|
|
779
|
+
})) {
|
|
780
|
+
if (value.startsWith("/") || value.includes("\\") || value.split("/").includes("..")) {
|
|
781
|
+
throw new Error(`adopter_delivery.${key} must be repository-relative`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return normalized;
|
|
785
|
+
}
|
|
786
|
+
|
|
703
787
|
function normalizePublicationArchive(archive = undefined) {
|
|
704
788
|
if (archive === undefined) {
|
|
705
789
|
return undefined;
|
|
@@ -166,6 +166,36 @@ function declarativeAuditableDemoSurface(root, pkg, majorLine) {
|
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
function v4ReleaseCandidatePromoteSurface(root, majorLine) {
|
|
170
|
+
return surface(root, {
|
|
171
|
+
id: "v4-release-candidate-promote-action",
|
|
172
|
+
kind: "action",
|
|
173
|
+
path: "actions/v4-release-candidate-promote/action.yml",
|
|
174
|
+
publicRef: `kungfu-systems/buildchain/actions/v4-release-candidate-promote@${majorLine}`,
|
|
175
|
+
requiredInputs: [
|
|
176
|
+
"token", "repository", "source-sha", "version", "tag", "channel",
|
|
177
|
+
"candidate-passport-path", "stage-capsules-path",
|
|
178
|
+
"publication-qualification-path",
|
|
179
|
+
],
|
|
180
|
+
requiredOutputs: [
|
|
181
|
+
"release-passport-path", "release-passport-root", "transaction-state",
|
|
182
|
+
"declaration-root", "transaction-root", "state-root",
|
|
183
|
+
"receipt-roots-json",
|
|
184
|
+
],
|
|
185
|
+
breakingDefaults: {
|
|
186
|
+
statePath: ".buildchain/release-tail/state.json",
|
|
187
|
+
executionBoundary: "built-in-provider-plane-only",
|
|
188
|
+
},
|
|
189
|
+
optionalInputs: ["artifact-paths", "state-path", "failure-after-capability"],
|
|
190
|
+
guarantees: [
|
|
191
|
+
"v4 promotion consumes only sealed candidate, Stage Capsule, qualification, and artifact evidence",
|
|
192
|
+
"GitHub Release, signed-channel commit, activation, readback, and released-evidence synthesis use built-in provider adapters",
|
|
193
|
+
"durable provider checkpoints resume completed capabilities without replay",
|
|
194
|
+
"no consumer command, script, shell, checkout, or executable tail hook is accepted",
|
|
195
|
+
],
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
169
199
|
export function createBuildchainContractWorld({
|
|
170
200
|
root = process.cwd(),
|
|
171
201
|
packageJson = undefined,
|
|
@@ -484,7 +514,7 @@ export function createBuildchainContractWorld({
|
|
|
484
514
|
"Build Images owns encoding, Buildchain owns qualification and receipts, and site repositories own browser loading and accessibility behavior",
|
|
485
515
|
"media qualification does not claim browser playback, responsive layout, reduced-motion behavior, accessibility, or production deployment",
|
|
486
516
|
],
|
|
487
|
-
}), declarativeAuditableDemoSurface(root, pkg, majorLine),
|
|
517
|
+
}), declarativeAuditableDemoSurface(root, pkg, majorLine), v4ReleaseCandidatePromoteSurface(root, majorLine),
|
|
488
518
|
surface(root, {
|
|
489
519
|
id: "promote-buildchain-ref-action",
|
|
490
520
|
kind: "action",
|