@kungfu-tech/buildchain 4.0.0 → 4.0.1-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/AGENTS.md +13 -5
- package/actions/promote-buildchain-ref/README.md +11 -6
- 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 +219 -7
- package/architecture/maintainability-policy.json +246 -29
- 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 +81 -0
- package/architecture/v4-floating-consumer-policy.md +32 -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-runtime-ref-resume-authority.json +64 -0
- package/architecture/v4-stage-capsule-qualification.json +2 -2
- 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/bin/internal/trust-release-release-handlers.mjs +5 -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 +59 -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-runtime-ref-resume-authority-v1/scenario.json +15 -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 +107 -0
- package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/contracts/v4-runtime-ref-resume-authority-v1.schema.json +235 -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 +1562 -44
- package/dist/site/buildchain-site.json +684 -42
- package/dist/site/capability-registry.json +17 -14
- package/dist/site/cli-registry.json +205 -3
- package/dist/site/controller-registry.json +62 -6
- package/dist/site/kfd-claims.json +551 -22
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +55 -9
- package/dist/site/node-api-registry.json +15247 -8690
- package/dist/site/page-registry.json +647 -29
- package/dist/site/public-surface-audit.json +876 -55
- package/dist/site/publication-authority-registry.json +44 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +26 -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 +37 -13
- package/dist/site/workflow-registry.json +200 -22
- package/docs/MAP.md +10 -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 +361 -30
- package/docs/lifecycle-protocol.md +41 -0
- package/docs/next-development-transition.md +118 -0
- package/docs/node-api-reference.md +748 -322
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/reusable-build-surface.md +41 -0
- package/docs/runtime-train-validation.md +10 -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-runtime-ref-resume-authority.md +69 -0
- package/docs/v4-tail-reseal.md +73 -0
- package/docs/versioning.md +4 -2
- package/package.json +36 -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/artifact-signing.js +61 -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 +150 -1
- 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-proof.js +37 -3
- 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 +7 -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/publication-authority.js +5 -5
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-candidate-recovery.js +66 -11
- package/packages/core/release-candidate.js +79 -19
- package/packages/core/release-passport.js +271 -38
- 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 +338 -0
- package/packages/core/v4-floating-consumer-policy.js +569 -0
- package/packages/core/v4-floating-consumer-release-passport.js +153 -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/v4-runtime-ref-resume-authority.js +625 -0
- package/packages/core/v4-runtime-selector-persistence.js +228 -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 +72 -2
- package/scripts/audit-publication-control-plane.mjs +31 -31
- 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 +217 -0
- package/scripts/check-v4-public-dogfood-contract.mjs +6 -11
- 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 +547 -0
- package/scripts/dev-delivery-two-phase-resume.mjs +345 -0
- package/scripts/dev-delivery-two-phase.mjs +573 -0
- package/scripts/dev-delivery-warrant-options.mjs +266 -0
- package/scripts/dev-delivery-warrant-store.mjs +231 -0
- package/scripts/dev-delivery-warrant.mjs +232 -194
- package/scripts/dev-pr-auto-merge.mjs +37 -48
- package/scripts/dev-pr-delivery-warrant.mjs +216 -2
- package/scripts/dev-pr-prequeue-guard.mjs +399 -0
- package/scripts/dev-qualification-patrol.mjs +9 -9
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/ensure-github-release.mjs +3 -3
- package/scripts/generate-channel-build-workflow.mjs +3 -0
- package/scripts/generate-channel-promotion-workflow.mjs +114 -14
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-release-candidate-passport.mjs +192 -32
- package/scripts/generate-site-bundle.mjs +69 -4
- package/scripts/init-repo.mjs +26 -4
- package/scripts/inspect-artifact-signing-requests.mjs +6 -0
- 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 +103 -91
- 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 +565 -41
- package/scripts/seal-artifact-signing-requests.mjs +6 -0
- package/scripts/site-capability-metadata.mjs +43 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v4-consumer-policy.mjs +231 -0
- 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,153 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
v4FloatingConsumerDocumentRoot,
|
|
5
|
+
verifyV4FloatingConsumerPolicyCertification,
|
|
6
|
+
} from "./v4-floating-consumer-evidence.js";
|
|
7
|
+
|
|
8
|
+
const SHA256_ROOT = /^sha256:[0-9a-f]{64}$/u;
|
|
9
|
+
|
|
10
|
+
export function isV4PromotionRouting(value = undefined) {
|
|
11
|
+
return [
|
|
12
|
+
value?.router?.ref,
|
|
13
|
+
value?.shell?.ref,
|
|
14
|
+
value?.runtime?.requestedRef,
|
|
15
|
+
].some((ref) => /^v4(?:-alpha)?$/u.test(String(ref || "")));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function resolveInside(root, relative, label) {
|
|
19
|
+
const resolvedRoot = path.resolve(root);
|
|
20
|
+
const file = path.resolve(resolvedRoot, relative);
|
|
21
|
+
if (!file.startsWith(`${resolvedRoot}${path.sep}`)) {
|
|
22
|
+
throw new Error(`${label} escapes caller root: ${relative}`);
|
|
23
|
+
}
|
|
24
|
+
return file;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveCallerLock(root, relative, label) {
|
|
28
|
+
const file = resolveInside(root, relative, `${label} contract lock`);
|
|
29
|
+
if (!fs.existsSync(file)) {
|
|
30
|
+
throw new Error(`${label} contract lock is missing: ${relative}`);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
path: relative,
|
|
34
|
+
root: v4FloatingConsumerDocumentRoot(
|
|
35
|
+
JSON.parse(fs.readFileSync(file, "utf8")),
|
|
36
|
+
),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function resolveV4FloatingConsumerCallerLocks(value, cwd) {
|
|
41
|
+
const certification = value?.certification || value;
|
|
42
|
+
const declared = certification?.authority || {};
|
|
43
|
+
const stableLockPath = declared?.contractLocks?.stable?.path || "";
|
|
44
|
+
const alphaLockPath = declared?.contractLocks?.alpha?.path || "";
|
|
45
|
+
return {
|
|
46
|
+
stableLockRoot: resolveCallerLock(cwd, stableLockPath, "stable").root,
|
|
47
|
+
alphaLockRoot: resolveCallerLock(cwd, alphaLockPath, "alpha").root,
|
|
48
|
+
stableLockPath,
|
|
49
|
+
alphaLockPath,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function normalizeCertification(value, expected) {
|
|
54
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
55
|
+
throw new Error("v4ConsumerPolicyCertification must be a JSON object");
|
|
56
|
+
}
|
|
57
|
+
const certification = value.certification || value;
|
|
58
|
+
const verification = verifyV4FloatingConsumerPolicyCertification({
|
|
59
|
+
certification,
|
|
60
|
+
certificationRoot: value.certificationRoot || "",
|
|
61
|
+
expectedCertificationRoot: expected.certificationRoot,
|
|
62
|
+
repository: expected.repository,
|
|
63
|
+
sourceSha: expected.sourceSha,
|
|
64
|
+
resolvedRuntimeSha: expected.runtimeSha,
|
|
65
|
+
policyRoot: expected.policyRoot,
|
|
66
|
+
scannerRoot: expected.scannerRoot,
|
|
67
|
+
stableLockRoot: expected.stableLockRoot,
|
|
68
|
+
alphaLockRoot: expected.alphaLockRoot,
|
|
69
|
+
stableLockPath: expected.stableLockPath,
|
|
70
|
+
alphaLockPath: expected.alphaLockPath,
|
|
71
|
+
});
|
|
72
|
+
if (!verification.ok) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`v4 consumer policy certification invalid: ${verification.failures.map((failure) => failure.code).join(", ")}`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return { certificationRoot: verification.certificationRoot, certification };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function resolveV4ConsumerPolicyCertificationIdentity({
|
|
81
|
+
release = {},
|
|
82
|
+
routing = {},
|
|
83
|
+
runtimeResume = undefined,
|
|
84
|
+
sourceSha = "",
|
|
85
|
+
} = {}) {
|
|
86
|
+
const builtSourceSha =
|
|
87
|
+
release?.builtSourceSha || release?.built_source_sha || "";
|
|
88
|
+
return {
|
|
89
|
+
sourceSha:
|
|
90
|
+
release?.treeEquivalent === true && builtSourceSha
|
|
91
|
+
? builtSourceSha
|
|
92
|
+
: sourceSha,
|
|
93
|
+
runtimeSha:
|
|
94
|
+
runtimeResume?.lineage?.attempts?.build?.runtimeSha ||
|
|
95
|
+
routing?.runtime?.resolvedSha ||
|
|
96
|
+
"",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function requireV4ConsumerPolicyCertification({
|
|
101
|
+
value,
|
|
102
|
+
repository,
|
|
103
|
+
sourceSha,
|
|
104
|
+
runtimeSha = "",
|
|
105
|
+
routing,
|
|
106
|
+
cwd,
|
|
107
|
+
certificationRoot,
|
|
108
|
+
}) {
|
|
109
|
+
const v4Routing = isV4PromotionRouting(routing);
|
|
110
|
+
if (v4Routing && !value) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
"Buildchain v4 Release Passport requires an external floating consumer policy certification",
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
if (!value) return undefined;
|
|
116
|
+
if (v4Routing && !SHA256_ROOT.test(String(certificationRoot || ""))) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
"Buildchain v4 Release Passport requires an expected external certification root",
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return normalizeCertification(value, {
|
|
122
|
+
repository,
|
|
123
|
+
sourceSha,
|
|
124
|
+
runtimeSha: runtimeSha || routing?.runtime?.resolvedSha || "",
|
|
125
|
+
certificationRoot,
|
|
126
|
+
...(v4Routing ? resolveV4FloatingConsumerCallerLocks(value, cwd) : {}),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function releasePassportCertificationVerificationOptions({
|
|
131
|
+
evidence,
|
|
132
|
+
passport,
|
|
133
|
+
routing,
|
|
134
|
+
}) {
|
|
135
|
+
const authority = evidence?.certification?.authority || {};
|
|
136
|
+
const identity = resolveV4ConsumerPolicyCertificationIdentity({
|
|
137
|
+
release: passport?.release,
|
|
138
|
+
routing,
|
|
139
|
+
runtimeResume: passport?.v4RuntimeResume,
|
|
140
|
+
sourceSha: passport?.release?.sourceSha || "",
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
certification: evidence?.certification,
|
|
144
|
+
certificationRoot: evidence?.certificationRoot,
|
|
145
|
+
repository: passport?.product?.repository || "",
|
|
146
|
+
sourceSha: identity.sourceSha,
|
|
147
|
+
resolvedRuntimeSha: identity.runtimeSha,
|
|
148
|
+
stableLockRoot: authority.contractLocks?.stable?.root || "",
|
|
149
|
+
alphaLockRoot: authority.contractLocks?.alpha?.root || "",
|
|
150
|
+
stableLockPath: authority.contractLocks?.stable?.path || "",
|
|
151
|
+
alphaLockPath: authority.contractLocks?.alpha?.path || "",
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RELEASE_TAIL_TRANSACTION_POLICY,
|
|
3
|
+
compileReleaseTailDeclaration,
|
|
4
|
+
createReleaseTailTransaction,
|
|
5
|
+
parseReleaseTailDeclaration,
|
|
6
|
+
releaseTailRoot,
|
|
7
|
+
validateReleaseTailTransaction,
|
|
8
|
+
} from "./release-tail-provider-plane.js";
|
|
9
|
+
import {
|
|
10
|
+
V4PublicationRehearsalFault,
|
|
11
|
+
publicationRehearsalByteSorted as byteSorted,
|
|
12
|
+
publicationRehearsalExactKeys as exactKeys,
|
|
13
|
+
publicationRehearsalFault as fault,
|
|
14
|
+
publicationRehearsalRelativePath as relativePath,
|
|
15
|
+
publicationRehearsalRoot as root,
|
|
16
|
+
publicationRehearsalText as text,
|
|
17
|
+
publicationRehearsalToken as token,
|
|
18
|
+
validateV4PublicationRehearsalProviderBindings,
|
|
19
|
+
} from "./v4-publication-rehearsal-provider-bindings.js";
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
V4PublicationRehearsalFault,
|
|
23
|
+
exactKeys as assertPublicationRehearsalExactKeys,
|
|
24
|
+
fault as publicationRehearsalFault,
|
|
25
|
+
root as publicationRehearsalRoot,
|
|
26
|
+
validateV4PublicationRehearsalProviderBindings,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const V4_PUBLICATION_REHEARSAL_CAPSULE_CONTRACT =
|
|
30
|
+
"buildchain-v4-publication-rehearsal-capsule/v1";
|
|
31
|
+
export const V4_PUBLICATION_REHEARSAL_EVIDENCE_CONTRACT =
|
|
32
|
+
"buildchain-v4-publication-rehearsal-evidence/v1";
|
|
33
|
+
export const V4_PUBLICATION_REHEARSAL_AUTHORITY_CONTRACT =
|
|
34
|
+
"buildchain-v4-publication-rehearsal-authority/v1";
|
|
35
|
+
export const V4_PUBLICATION_REHEARSAL_PROVIDER_POLICY_CONTRACT =
|
|
36
|
+
"buildchain-v4-publication-rehearsal-provider-policy/v1";
|
|
37
|
+
export const V4_PUBLICATION_REHEARSAL_CORE_VERSION =
|
|
38
|
+
RELEASE_TAIL_TRANSACTION_POLICY;
|
|
39
|
+
|
|
40
|
+
const ROOT = /^sha256:[0-9a-f]{64}$/u;
|
|
41
|
+
const SHA = /^[0-9a-f]{40}$/u;
|
|
42
|
+
const TOKEN = /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/u;
|
|
43
|
+
const MODES = new Set(["simulate", "replay", "provider"]);
|
|
44
|
+
|
|
45
|
+
function sourceRoot(source) {
|
|
46
|
+
return releaseTailRoot({
|
|
47
|
+
repository: source.repository,
|
|
48
|
+
revision: source.revision,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function candidateRoot(files) {
|
|
53
|
+
return releaseTailRoot(
|
|
54
|
+
files.map(({ role, path: filePath, size, root: fileRoot }) => ({
|
|
55
|
+
role,
|
|
56
|
+
path: filePath,
|
|
57
|
+
size,
|
|
58
|
+
root: fileRoot,
|
|
59
|
+
})),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function policyPayload(policy) {
|
|
64
|
+
return {
|
|
65
|
+
schema: policy.schema,
|
|
66
|
+
effectDefault: policy.effectDefault,
|
|
67
|
+
allowedCapabilityIds: policy.allowedCapabilityIds,
|
|
68
|
+
allowedAdapters: policy.allowedAdapters,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function capsulePayload(capsule) {
|
|
73
|
+
const payload = structuredClone(capsule);
|
|
74
|
+
delete payload.capsuleRoot;
|
|
75
|
+
return payload;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function normalizeFile(value, index) {
|
|
79
|
+
const location = `$/files/${index}`;
|
|
80
|
+
exactKeys(value, ["role", "path", "size", "root"], location);
|
|
81
|
+
if (!Number.isSafeInteger(value.size) || value.size < 0)
|
|
82
|
+
fault(
|
|
83
|
+
"invalid-publication-rehearsal-file",
|
|
84
|
+
`${location}/size`,
|
|
85
|
+
"non-negative safe integer required",
|
|
86
|
+
);
|
|
87
|
+
return {
|
|
88
|
+
role: token(value.role, `${location}/role`),
|
|
89
|
+
path: relativePath(value.path, `${location}/path`),
|
|
90
|
+
size: value.size,
|
|
91
|
+
root: root(value.root, `${location}/root`),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function normalizeBinding(value, location) {
|
|
96
|
+
exactKeys(value, ["path", "root"], location);
|
|
97
|
+
return {
|
|
98
|
+
path: relativePath(value.path, `${location}/path`),
|
|
99
|
+
root: root(value.root, `${location}/root`),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function normalizeCandidateInputs(value) {
|
|
104
|
+
exactKeys(value.candidate, ["root"], "$/candidate");
|
|
105
|
+
root(value.candidate.root, "$/candidate/root");
|
|
106
|
+
if (!Array.isArray(value.files) || value.files.length === 0)
|
|
107
|
+
fault(
|
|
108
|
+
"invalid-publication-rehearsal-files",
|
|
109
|
+
"$/files",
|
|
110
|
+
"non-empty file inventory required",
|
|
111
|
+
);
|
|
112
|
+
const files = value.files.map(normalizeFile);
|
|
113
|
+
const paths = files.map((entry) => entry.path);
|
|
114
|
+
if (
|
|
115
|
+
JSON.stringify(paths) !==
|
|
116
|
+
JSON.stringify(
|
|
117
|
+
[...new Set(paths)].sort((left, right) =>
|
|
118
|
+
Buffer.from(left).compare(Buffer.from(right)),
|
|
119
|
+
),
|
|
120
|
+
) ||
|
|
121
|
+
value.candidate.root !== candidateRoot(files)
|
|
122
|
+
)
|
|
123
|
+
fault(
|
|
124
|
+
"publication-rehearsal-candidate-root-mismatch",
|
|
125
|
+
"$/candidate/root",
|
|
126
|
+
"candidate inventory is unordered, duplicated, or drifted",
|
|
127
|
+
);
|
|
128
|
+
const manifest = normalizeBinding(value.manifest, "$/manifest");
|
|
129
|
+
const config = normalizeBinding(value.config, "$/config");
|
|
130
|
+
for (const [name, binding] of Object.entries({ manifest, config }))
|
|
131
|
+
if (
|
|
132
|
+
!files.some(
|
|
133
|
+
(entry) => entry.path === binding.path && entry.root === binding.root,
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
fault(
|
|
137
|
+
`publication-rehearsal-${name}-root-mismatch`,
|
|
138
|
+
`$/${name}`,
|
|
139
|
+
`${name} is not bound by the candidate inventory`,
|
|
140
|
+
);
|
|
141
|
+
const providerBindings = validateV4PublicationRehearsalProviderBindings(
|
|
142
|
+
value.providerBindings,
|
|
143
|
+
);
|
|
144
|
+
root(value.providerBindingsRoot, "$/providerBindingsRoot");
|
|
145
|
+
if (value.providerBindingsRoot !== releaseTailRoot(providerBindings))
|
|
146
|
+
fault(
|
|
147
|
+
"publication-rehearsal-provider-bindings-root-mismatch",
|
|
148
|
+
"$/providerBindingsRoot",
|
|
149
|
+
"provider bindings payload drifted",
|
|
150
|
+
);
|
|
151
|
+
const inventoryPaths = new Set(paths);
|
|
152
|
+
const requiredInputs = [
|
|
153
|
+
...Object.values(providerBindings.artifacts).map(({ path }) => path),
|
|
154
|
+
...Object.values(providerBindings.documents).map(({ path }) => path),
|
|
155
|
+
...providerBindings.evidence.inputs,
|
|
156
|
+
];
|
|
157
|
+
for (const inputPath of requiredInputs)
|
|
158
|
+
if (!inventoryPaths.has(inputPath))
|
|
159
|
+
fault(
|
|
160
|
+
"publication-rehearsal-provider-binding-file-missing",
|
|
161
|
+
"$/providerBindings",
|
|
162
|
+
`provider input is absent from files: ${inputPath}`,
|
|
163
|
+
);
|
|
164
|
+
return { files, manifest, config, providerBindings };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function normalizeProviderPolicy(value, declaration) {
|
|
168
|
+
exactKeys(
|
|
169
|
+
value,
|
|
170
|
+
[
|
|
171
|
+
"schema",
|
|
172
|
+
"effectDefault",
|
|
173
|
+
"allowedCapabilityIds",
|
|
174
|
+
"allowedAdapters",
|
|
175
|
+
"root",
|
|
176
|
+
],
|
|
177
|
+
"$/providerPolicy",
|
|
178
|
+
);
|
|
179
|
+
if (
|
|
180
|
+
value.schema !== V4_PUBLICATION_REHEARSAL_PROVIDER_POLICY_CONTRACT ||
|
|
181
|
+
value.effectDefault !== "disabled"
|
|
182
|
+
)
|
|
183
|
+
fault(
|
|
184
|
+
"invalid-publication-rehearsal-provider-policy",
|
|
185
|
+
"$/providerPolicy",
|
|
186
|
+
"provider rehearsal effects must default to disabled",
|
|
187
|
+
);
|
|
188
|
+
const capabilityIds = byteSorted(
|
|
189
|
+
value.allowedCapabilityIds,
|
|
190
|
+
"$/providerPolicy/allowedCapabilityIds",
|
|
191
|
+
text,
|
|
192
|
+
);
|
|
193
|
+
const adapters = byteSorted(
|
|
194
|
+
value.allowedAdapters,
|
|
195
|
+
"$/providerPolicy/allowedAdapters",
|
|
196
|
+
token,
|
|
197
|
+
);
|
|
198
|
+
const expectedCapabilityIds = declaration.capabilities
|
|
199
|
+
.map(({ id }) => id)
|
|
200
|
+
.sort((left, right) => Buffer.from(left).compare(Buffer.from(right)));
|
|
201
|
+
const expectedAdapters = [
|
|
202
|
+
...new Set(declaration.capabilities.map(({ adapter }) => adapter)),
|
|
203
|
+
].sort((left, right) => Buffer.from(left).compare(Buffer.from(right)));
|
|
204
|
+
if (
|
|
205
|
+
JSON.stringify(capabilityIds) !== JSON.stringify(expectedCapabilityIds) ||
|
|
206
|
+
JSON.stringify(adapters) !== JSON.stringify(expectedAdapters)
|
|
207
|
+
)
|
|
208
|
+
fault(
|
|
209
|
+
"publication-rehearsal-provider-policy-mismatch",
|
|
210
|
+
"$/providerPolicy",
|
|
211
|
+
"policy must enumerate the exact shared-core capabilities and adapters",
|
|
212
|
+
);
|
|
213
|
+
const normalized = {
|
|
214
|
+
schema: value.schema,
|
|
215
|
+
effectDefault: value.effectDefault,
|
|
216
|
+
allowedCapabilityIds: capabilityIds,
|
|
217
|
+
allowedAdapters: adapters,
|
|
218
|
+
root: root(value.root, "$/providerPolicy/root"),
|
|
219
|
+
};
|
|
220
|
+
if (normalized.root !== releaseTailRoot(policyPayload(normalized)))
|
|
221
|
+
fault(
|
|
222
|
+
"publication-rehearsal-provider-policy-root-mismatch",
|
|
223
|
+
"$/providerPolicy/root",
|
|
224
|
+
"provider policy root drifted",
|
|
225
|
+
);
|
|
226
|
+
return normalized;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function normalizeObservation(value, index, operationIds) {
|
|
230
|
+
const location = `$/expectedObservations/entries/${index}`;
|
|
231
|
+
exactKeys(value, ["operationId", "readbacks", "apply"], location);
|
|
232
|
+
const operationId = root(value.operationId, `${location}/operationId`);
|
|
233
|
+
if (!operationIds.has(operationId))
|
|
234
|
+
fault(
|
|
235
|
+
"publication-rehearsal-observation-operation-mismatch",
|
|
236
|
+
`${location}/operationId`,
|
|
237
|
+
"unknown operation",
|
|
238
|
+
);
|
|
239
|
+
if (!Array.isArray(value.readbacks) || value.readbacks.length === 0)
|
|
240
|
+
fault(
|
|
241
|
+
"invalid-publication-rehearsal-observation",
|
|
242
|
+
`${location}/readbacks`,
|
|
243
|
+
"non-empty readback sequence required",
|
|
244
|
+
);
|
|
245
|
+
for (const [readbackIndex, readback] of value.readbacks.entries()) {
|
|
246
|
+
exactKeys(
|
|
247
|
+
readback,
|
|
248
|
+
["outcome", "subjectRoot", "targetRoot", "evidenceRoots", "providerCode"],
|
|
249
|
+
`${location}/readbacks/${readbackIndex}`,
|
|
250
|
+
);
|
|
251
|
+
if (
|
|
252
|
+
!new Set(["observed", "absent", "transient", "conflict"]).has(
|
|
253
|
+
readback.outcome,
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
fault(
|
|
257
|
+
"invalid-publication-rehearsal-observation",
|
|
258
|
+
`${location}/readbacks/${readbackIndex}/outcome`,
|
|
259
|
+
"unsupported outcome",
|
|
260
|
+
);
|
|
261
|
+
if (readback.subjectRoot)
|
|
262
|
+
root(
|
|
263
|
+
readback.subjectRoot,
|
|
264
|
+
`${location}/readbacks/${readbackIndex}/subjectRoot`,
|
|
265
|
+
);
|
|
266
|
+
if (readback.targetRoot)
|
|
267
|
+
root(
|
|
268
|
+
readback.targetRoot,
|
|
269
|
+
`${location}/readbacks/${readbackIndex}/targetRoot`,
|
|
270
|
+
);
|
|
271
|
+
byteSorted(
|
|
272
|
+
readback.evidenceRoots,
|
|
273
|
+
`${location}/readbacks/${readbackIndex}/evidenceRoots`,
|
|
274
|
+
root,
|
|
275
|
+
true,
|
|
276
|
+
);
|
|
277
|
+
text(
|
|
278
|
+
readback.providerCode,
|
|
279
|
+
`${location}/readbacks/${readbackIndex}/providerCode`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
exactKeys(
|
|
283
|
+
value.apply,
|
|
284
|
+
["outcome", "code", "classification"],
|
|
285
|
+
`${location}/apply`,
|
|
286
|
+
);
|
|
287
|
+
if (!new Set(["applied", "transient", "conflict"]).has(value.apply.outcome))
|
|
288
|
+
fault(
|
|
289
|
+
"invalid-publication-rehearsal-observation",
|
|
290
|
+
`${location}/apply/outcome`,
|
|
291
|
+
"unsupported apply outcome",
|
|
292
|
+
);
|
|
293
|
+
text(value.apply.code, `${location}/apply/code`);
|
|
294
|
+
text(value.apply.classification, `${location}/apply/classification`);
|
|
295
|
+
return structuredClone(value);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function createV4PublicationRehearsalProviderPolicy(declarationInput) {
|
|
299
|
+
const declaration = parseReleaseTailDeclaration(declarationInput);
|
|
300
|
+
const body = {
|
|
301
|
+
schema: V4_PUBLICATION_REHEARSAL_PROVIDER_POLICY_CONTRACT,
|
|
302
|
+
effectDefault: "disabled",
|
|
303
|
+
allowedCapabilityIds: declaration.capabilities
|
|
304
|
+
.map(({ id }) => id)
|
|
305
|
+
.sort((left, right) => Buffer.from(left).compare(Buffer.from(right))),
|
|
306
|
+
allowedAdapters: [
|
|
307
|
+
...new Set(declaration.capabilities.map(({ adapter }) => adapter)),
|
|
308
|
+
].sort((left, right) => Buffer.from(left).compare(Buffer.from(right))),
|
|
309
|
+
};
|
|
310
|
+
return { ...body, root: releaseTailRoot(body) };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function createV4PublicationRehearsalCapsule(input) {
|
|
314
|
+
const declaration = parseReleaseTailDeclaration(input.declaration);
|
|
315
|
+
const transaction =
|
|
316
|
+
input.transaction || createReleaseTailTransaction(declaration);
|
|
317
|
+
const files = structuredClone(input.files || []).sort((left, right) =>
|
|
318
|
+
Buffer.from(left.path).compare(Buffer.from(right.path)),
|
|
319
|
+
);
|
|
320
|
+
const source = {
|
|
321
|
+
repository: text(input.source?.repository, "$/source/repository"),
|
|
322
|
+
revision: text(input.source?.revision, "$/source/revision"),
|
|
323
|
+
};
|
|
324
|
+
const policy =
|
|
325
|
+
input.providerPolicy ||
|
|
326
|
+
createV4PublicationRehearsalProviderPolicy(declaration);
|
|
327
|
+
const observations = structuredClone(input.expectedObservations || []);
|
|
328
|
+
const providerBindings = validateV4PublicationRehearsalProviderBindings(
|
|
329
|
+
input.providerBindings,
|
|
330
|
+
);
|
|
331
|
+
const body = {
|
|
332
|
+
schema: V4_PUBLICATION_REHEARSAL_CAPSULE_CONTRACT,
|
|
333
|
+
coreVersion: V4_PUBLICATION_REHEARSAL_CORE_VERSION,
|
|
334
|
+
source: { ...source, root: sourceRoot(source) },
|
|
335
|
+
candidate: { root: candidateRoot(files) },
|
|
336
|
+
manifest: structuredClone(input.manifest),
|
|
337
|
+
config: structuredClone(input.config),
|
|
338
|
+
providerBindings,
|
|
339
|
+
providerBindingsRoot: releaseTailRoot(providerBindings),
|
|
340
|
+
providerPolicy: structuredClone(policy),
|
|
341
|
+
expectedObservations: {
|
|
342
|
+
entries: observations,
|
|
343
|
+
root: releaseTailRoot(observations),
|
|
344
|
+
},
|
|
345
|
+
declaration,
|
|
346
|
+
transaction: structuredClone(transaction),
|
|
347
|
+
files,
|
|
348
|
+
};
|
|
349
|
+
return validateV4PublicationRehearsalCapsule({
|
|
350
|
+
...body,
|
|
351
|
+
capsuleRoot: releaseTailRoot(body),
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export function validateV4PublicationRehearsalCapsule(value) {
|
|
356
|
+
exactKeys(
|
|
357
|
+
value,
|
|
358
|
+
[
|
|
359
|
+
"schema",
|
|
360
|
+
"coreVersion",
|
|
361
|
+
"source",
|
|
362
|
+
"candidate",
|
|
363
|
+
"manifest",
|
|
364
|
+
"config",
|
|
365
|
+
"providerBindings",
|
|
366
|
+
"providerBindingsRoot",
|
|
367
|
+
"providerPolicy",
|
|
368
|
+
"expectedObservations",
|
|
369
|
+
"declaration",
|
|
370
|
+
"transaction",
|
|
371
|
+
"files",
|
|
372
|
+
"capsuleRoot",
|
|
373
|
+
],
|
|
374
|
+
"$",
|
|
375
|
+
);
|
|
376
|
+
if (
|
|
377
|
+
value.schema !== V4_PUBLICATION_REHEARSAL_CAPSULE_CONTRACT ||
|
|
378
|
+
value.coreVersion !== V4_PUBLICATION_REHEARSAL_CORE_VERSION
|
|
379
|
+
)
|
|
380
|
+
fault(
|
|
381
|
+
"unsupported-publication-rehearsal-core-version",
|
|
382
|
+
"$/coreVersion",
|
|
383
|
+
"capsule must bind the active production release-tail core",
|
|
384
|
+
);
|
|
385
|
+
exactKeys(value.source, ["repository", "revision", "root"], "$/source");
|
|
386
|
+
text(value.source.repository, "$/source/repository");
|
|
387
|
+
if (!SHA.test(value.source.revision))
|
|
388
|
+
fault(
|
|
389
|
+
"invalid-publication-rehearsal-source",
|
|
390
|
+
"$/source/revision",
|
|
391
|
+
"exact lowercase Git SHA required",
|
|
392
|
+
);
|
|
393
|
+
root(value.source.root, "$/source/root");
|
|
394
|
+
if (value.source.root !== sourceRoot(value.source))
|
|
395
|
+
fault(
|
|
396
|
+
"publication-rehearsal-source-root-mismatch",
|
|
397
|
+
"$/source/root",
|
|
398
|
+
"source root drifted",
|
|
399
|
+
);
|
|
400
|
+
const { files, manifest, config, providerBindings } =
|
|
401
|
+
normalizeCandidateInputs(value);
|
|
402
|
+
const declaration = parseReleaseTailDeclaration(value.declaration);
|
|
403
|
+
const plan = compileReleaseTailDeclaration(declaration);
|
|
404
|
+
const transactionValidation = validateReleaseTailTransaction(
|
|
405
|
+
value.transaction,
|
|
406
|
+
);
|
|
407
|
+
const initial = createReleaseTailTransaction(plan);
|
|
408
|
+
if (
|
|
409
|
+
!transactionValidation.valid ||
|
|
410
|
+
value.transaction.declarationRoot !== plan.declarationRoot ||
|
|
411
|
+
value.transaction.planRoot !== plan.planRoot ||
|
|
412
|
+
value.transaction.transactionRoot !== initial.transactionRoot
|
|
413
|
+
)
|
|
414
|
+
fault(
|
|
415
|
+
"publication-rehearsal-core-binding-mismatch",
|
|
416
|
+
"$/transaction",
|
|
417
|
+
"transaction does not belong to the shared production planner",
|
|
418
|
+
);
|
|
419
|
+
const providerPolicy = normalizeProviderPolicy(
|
|
420
|
+
value.providerPolicy,
|
|
421
|
+
declaration,
|
|
422
|
+
);
|
|
423
|
+
exactKeys(
|
|
424
|
+
value.expectedObservations,
|
|
425
|
+
["entries", "root"],
|
|
426
|
+
"$/expectedObservations",
|
|
427
|
+
);
|
|
428
|
+
if (!Array.isArray(value.expectedObservations.entries))
|
|
429
|
+
fault(
|
|
430
|
+
"invalid-publication-rehearsal-observations",
|
|
431
|
+
"$/expectedObservations/entries",
|
|
432
|
+
"array required",
|
|
433
|
+
);
|
|
434
|
+
const operationIds = new Set(value.transaction.operationOrder);
|
|
435
|
+
const expectedObservations = value.expectedObservations.entries.map(
|
|
436
|
+
(entry, index) => normalizeObservation(entry, index, operationIds),
|
|
437
|
+
);
|
|
438
|
+
if (
|
|
439
|
+
new Set(expectedObservations.map(({ operationId }) => operationId)).size !==
|
|
440
|
+
expectedObservations.length ||
|
|
441
|
+
value.expectedObservations.root !== releaseTailRoot(expectedObservations)
|
|
442
|
+
)
|
|
443
|
+
fault(
|
|
444
|
+
"publication-rehearsal-expected-observation-root-mismatch",
|
|
445
|
+
"$/expectedObservations/root",
|
|
446
|
+
"expected observations are duplicated or drifted",
|
|
447
|
+
);
|
|
448
|
+
root(value.capsuleRoot, "$/capsuleRoot");
|
|
449
|
+
if (value.capsuleRoot !== releaseTailRoot(capsulePayload(value)))
|
|
450
|
+
fault(
|
|
451
|
+
"publication-rehearsal-capsule-root-mismatch",
|
|
452
|
+
"$/capsuleRoot",
|
|
453
|
+
"capsule payload drifted",
|
|
454
|
+
);
|
|
455
|
+
return {
|
|
456
|
+
...structuredClone(value),
|
|
457
|
+
manifest,
|
|
458
|
+
config,
|
|
459
|
+
providerBindings,
|
|
460
|
+
providerPolicy,
|
|
461
|
+
expectedObservations: {
|
|
462
|
+
entries: expectedObservations,
|
|
463
|
+
root: value.expectedObservations.root,
|
|
464
|
+
},
|
|
465
|
+
declaration,
|
|
466
|
+
files,
|
|
467
|
+
};
|
|
468
|
+
}
|