@kungfu-tech/buildchain 4.1.3-alpha.0 → 4.1.3-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 +41 -75
- package/CONTRIBUTING.md +19 -26
- package/architecture/action-taxonomy.json +24 -0
- package/architecture/agent-change-map.md +272 -12
- package/architecture/capability-state-machine-manifest.json +1 -4
- package/architecture/ci-lane-change-budget.json +190 -807
- package/architecture/core-mechanisms.json +3 -6
- package/architecture/decisions/0005-minimal-consumer-contract.md +122 -0
- package/architecture/decisions/0006-business-attempt-journal.md +89 -0
- package/architecture/decisions/0007-hosted-pipeline-controller.md +106 -0
- package/architecture/decisions/0008-pipeline-product-publication.md +120 -0
- package/architecture/decisions/0009-pipeline-version-preparation.md +186 -0
- package/architecture/internal-capabilities.json +371 -9
- package/architecture/maintainability-debt.json +50 -34
- package/architecture/maintainability-policy.json +8 -8
- package/architecture/minimal-consumer-migration.json +5670 -0
- package/architecture/platform-stage-checkpoints.json +31 -7
- package/architecture/product-upstreams.json +20 -0
- package/architecture/release-topology.json +331 -74
- package/architecture/stage-capsule-qualification.json +3 -1
- package/architecture/universal-workflow-bootstrap.json +15 -1
- package/architecture/universal-workflow-capability-policy.json +9 -9
- package/architecture/universal-workflow-fault-campaign.json +3 -1
- package/architecture/workflow-taxonomy.json +77 -328
- package/contracts/release-discussion-v1.schema.json +3 -0
- package/dist/readers/business-attempt.cjs +809 -0
- package/dist/readers/release-discussion.cjs +4 -0
- package/dist/site/buildchain-contract.json +16 -68
- package/dist/site/buildchain-site.json +47 -27
- package/dist/site/capability-registry.json +4 -4
- package/dist/site/controller-registry.json +1 -47
- package/dist/site/kfd-claims.json +499 -494
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +3 -3
- package/dist/site/node-api-registry.json +56 -56
- package/dist/site/page-registry.json +40 -20
- package/dist/site/public-surface-audit.json +286 -349
- package/dist/site/publication-authority-registry.json +203 -523
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +7 -7
- package/dist/site/workflow-registry.json +590 -940
- package/docs/code-organization.md +9 -6
- package/docs/node-api-reference.md +103 -103
- package/docs/release-discussions.md +44 -5
- package/docs/release-governance.md +10 -12
- package/docs/runtime-entry.md +73 -5
- package/docs/runtime-train-validation.md +39 -11
- package/docs/stable-candidate-patrol.md +6 -5
- package/docs/workflow-catalog.md +9 -32
- package/package.json +4 -3
- package/packages/core/adoption/buildchain-kfd-claims.js +7 -7
- package/packages/core/build/standalone/build.js +7 -1
- package/packages/core/consumer/buildchain-config.js +2 -21
- package/packages/core/consumer/contract/entries.js +85 -0
- package/packages/core/consumer/contract/examples.js +96 -0
- package/packages/core/consumer/contract/identity.js +65 -0
- package/packages/core/consumer/contract/inspection.js +45 -0
- package/packages/core/consumer/contract/plan.js +149 -0
- package/packages/core/consumer/contract/products.js +119 -0
- package/packages/core/consumer/contract/reader.js +47 -0
- package/packages/core/consumer/contract/shape.js +46 -0
- package/packages/core/dev-delivery/candidate/admission.js +9 -5
- package/packages/core/dev-delivery/candidate/reservation.js +11 -0
- package/packages/core/dev-delivery/candidate/source-paths.js +12 -4
- package/packages/core/dev-delivery/commands/project-cut-merge-queue-admission.mjs +47 -20
- package/packages/core/dev-delivery/dev-delivery-provider-heartbeat.js +18 -15
- package/packages/core/dev-delivery/native/actions.js +9 -1
- package/packages/core/dev-delivery/native/heartbeat-action.js +16 -0
- package/packages/core/dev-delivery/native/heartbeat.js +6 -4
- package/packages/core/dev-delivery/native/process-entry.js +19 -0
- package/packages/core/dev-delivery/queue/landing-action.js +53 -0
- package/packages/core/dev-delivery/warrant/failure-settlement.js +8 -6
- package/packages/core/dev-delivery/warrant/service.js +7 -0
- package/packages/core/governance/buildchain-publication-authority.js +8 -23
- package/packages/core/governance/github-governance-authority.js +1 -0
- package/packages/core/observability/controller-evidence.js +0 -8
- package/packages/core/paper/operations/bootstrap.js +2 -0
- package/packages/core/paper/paper-npm-bootstrap.js +2 -2
- package/packages/core/providers/github/attempt-index.js +60 -0
- package/packages/core/providers/github/attempt-journal.js +154 -0
- package/packages/core/providers/github/discussions/materials.js +35 -11
- package/packages/core/providers/github/discussions/transport.js +16 -5
- package/packages/core/providers/github/pipeline-checkout.js +49 -0
- package/packages/core/providers/github/pipeline-entry-runs.js +210 -0
- package/packages/core/providers/github/pipeline-events.js +131 -0
- package/packages/core/providers/github/pipeline-integration.js +95 -0
- package/packages/core/providers/github/pipeline-policy.js +240 -0
- package/packages/core/providers/github/pipeline-product-payload.js +50 -0
- package/packages/core/providers/github/pipeline-product-release.js +198 -0
- package/packages/core/providers/github/pipeline-publication-artifacts.js +153 -0
- package/packages/core/providers/github/pipeline-release-evidence.js +30 -0
- package/packages/core/providers/github/pipeline-run-entry.js +86 -0
- package/packages/core/providers/github/pipeline-runs.js +132 -0
- package/packages/core/providers/github/pipeline-source.js +195 -0
- package/packages/core/providers/github/pipeline-stable-baseline.js +86 -0
- package/packages/core/providers/github/pipeline-stable-source.js +372 -0
- package/packages/core/providers/github/pipeline-version-artifacts.js +91 -0
- package/packages/core/providers/github/pipeline-version-readback.js +117 -0
- package/packages/core/providers/github/pipeline-version.js +199 -0
- package/packages/core/providers/github/pipeline-worker.js +108 -0
- package/packages/core/providers/github/release-evidence-assets.js +97 -0
- package/packages/core/publication/candidate/registry-hydration.js +2 -2
- package/packages/core/publication/npm/pack-preview.js +2 -2
- package/packages/core/publication/npm/pack-result.js +31 -0
- package/packages/core/publication/npm/package.js +3 -6
- package/packages/core/publication/npm/pipeline-channel.js +36 -0
- package/packages/core/publication/npm/pipeline-provider.js +137 -0
- package/packages/core/publication/npm/registry.js +12 -9
- package/packages/core/publication/pipeline/actions.js +116 -0
- package/packages/core/publication/pipeline/apply.js +189 -0
- package/packages/core/publication/pipeline/build-segments.js +107 -0
- package/packages/core/publication/pipeline/capsules.js +92 -0
- package/packages/core/publication/pipeline/context.js +39 -0
- package/packages/core/publication/pipeline/development-anchor.js +91 -0
- package/packages/core/publication/pipeline/development-current.js +63 -0
- package/packages/core/publication/pipeline/development-pr.js +112 -0
- package/packages/core/publication/pipeline/development-proof.js +76 -0
- package/packages/core/publication/pipeline/development-transition.js +112 -0
- package/packages/core/publication/pipeline/distribution.js +130 -0
- package/packages/core/publication/pipeline/documents.js +192 -0
- package/packages/core/publication/pipeline/effects.js +192 -0
- package/packages/core/publication/pipeline/files.js +66 -0
- package/packages/core/publication/pipeline/imported-materials.js +32 -0
- package/packages/core/publication/pipeline/journal.js +122 -0
- package/packages/core/publication/pipeline/next-development.js +117 -0
- package/packages/core/publication/pipeline/pack.js +180 -0
- package/packages/core/publication/pipeline/package-policy.js +22 -0
- package/packages/core/publication/pipeline/plan.js +136 -0
- package/packages/core/publication/pipeline/prepare.js +139 -0
- package/packages/core/publication/pipeline/qualification.js +175 -0
- package/packages/core/publication/pipeline/qualify.js +104 -0
- package/packages/core/publication/pipeline/recovery-admission.js +122 -0
- package/packages/core/publication/pipeline/recovery-build-download.js +80 -0
- package/packages/core/publication/pipeline/recovery-build-plan.js +122 -0
- package/packages/core/publication/pipeline/recovery-build-readback.js +68 -0
- package/packages/core/publication/pipeline/recovery-capsules.js +54 -0
- package/packages/core/publication/pipeline/recovery-import.js +80 -0
- package/packages/core/publication/pipeline/recovery-inspection.js +218 -0
- package/packages/core/publication/pipeline/recovery-materials.js +104 -0
- package/packages/core/publication/pipeline/recovery-plan.js +46 -0
- package/packages/core/publication/pipeline/recovery-prepare.js +73 -0
- package/packages/core/publication/pipeline/recovery-qualification.js +128 -0
- package/packages/core/publication/pipeline/recovery-readback.js +56 -0
- package/packages/core/publication/pipeline/recovery-signing.js +58 -0
- package/packages/core/publication/pipeline/sealed-products.js +79 -0
- package/packages/core/publication/pipeline/settle.js +42 -0
- package/packages/core/publication/pipeline/signing.js +140 -0
- package/packages/core/publication/pipeline/source-plan.js +83 -0
- package/packages/core/publication/pipeline/stable-entry.js +219 -0
- package/packages/core/publication/pipeline/stable-products.js +317 -0
- package/packages/core/publication/pipeline/stable-wait.js +170 -0
- package/packages/core/publication/pipeline/stable.js +82 -0
- package/packages/core/publication/pipeline/version-actions.js +93 -0
- package/packages/core/publication/pipeline/version-build.js +130 -0
- package/packages/core/publication/pipeline/version-context.js +143 -0
- package/packages/core/publication/pipeline/version-material.js +36 -0
- package/packages/core/publication/pipeline/version-preparation.js +69 -0
- package/packages/core/publication/pipeline/version-regeneration.js +162 -0
- package/packages/core/publication/pipeline/version-source-guard.js +69 -0
- package/packages/core/publication/pipeline/version.js +81 -0
- package/packages/core/publication/pipeline/worker.js +72 -0
- package/packages/core/publication/publication-reproducibility.js +2 -2
- package/packages/core/release/discussion/binary.js +11 -0
- package/packages/core/release/discussion/checkpoints.js +45 -4
- package/packages/core/release/discussion/envelope.js +1 -0
- package/packages/core/release/discussion/evidence.js +51 -0
- package/packages/core/release/discussion/presentation.js +92 -0
- package/packages/core/release/discussion/publication.js +3 -1
- package/packages/core/release/discussion/qualification.js +1 -0
- package/packages/core/release/discussion/reader.js +9 -0
- package/packages/core/release/discussion/session.js +2 -0
- package/packages/core/release/discussion/store.js +28 -31
- package/packages/core/release/discussion/threads.js +83 -0
- package/packages/core/release/next-development-transition.js +15 -15
- package/packages/core/release/promote-candidate/product-provider-adapters.js +4 -3
- package/packages/core/release/promote-ref/internal/npm-existing-evidence.js +2 -1
- package/packages/core/release/promote-ref/internal/publish-command.js +16 -5
- package/packages/core/release/stable-release-gate.js +11 -0
- package/packages/core/runtime/buildchain-domain.wasm +0 -0
- package/packages/core/runtime/domain-wasm-artifact.js +2 -2
- package/packages/core/runtime/entry/actions.js +27 -25
- package/packages/core/runtime/entry/attempt.js +25 -0
- package/packages/core/runtime/entry/selection.js +14 -0
- package/packages/core/runtime/entry/source.js +62 -0
- package/packages/core/workflow/attempt/identity.js +170 -0
- package/packages/core/workflow/attempt/journal.js +89 -0
- package/packages/core/workflow/attempt/materials.js +72 -0
- package/packages/core/workflow/attempt/reader-entry.js +12 -0
- package/packages/core/workflow/attempt/reader.js +149 -0
- package/packages/core/workflow/attempt/records.js +153 -0
- package/packages/core/workflow/attempt/store.js +75 -0
- package/packages/core/workflow/commands/pipeline-build.mjs +23 -0
- package/packages/core/workflow/pipeline/action-output.js +11 -0
- package/packages/core/workflow/pipeline/actions.js +52 -0
- package/packages/core/workflow/pipeline/build-control.js +140 -0
- package/packages/core/workflow/pipeline/build-evidence.js +210 -0
- package/packages/core/workflow/pipeline/build-qualification.js +145 -0
- package/packages/core/workflow/pipeline/build-result.js +78 -0
- package/packages/core/workflow/pipeline/build.js +142 -0
- package/packages/core/workflow/pipeline/cancellation.js +169 -0
- package/packages/core/workflow/pipeline/channel-control.js +157 -0
- package/packages/core/workflow/pipeline/channel-source.js +53 -0
- package/packages/core/workflow/pipeline/controller.js +141 -0
- package/packages/core/workflow/pipeline/delivery-control.js +249 -0
- package/packages/core/workflow/pipeline/delivery-observation.js +124 -0
- package/packages/core/workflow/pipeline/delivery-request.js +198 -0
- package/packages/core/workflow/pipeline/events.js +84 -0
- package/packages/core/workflow/pipeline/fence.js +42 -0
- package/packages/core/workflow/pipeline/group-control.js +59 -0
- package/packages/core/workflow/pipeline/guard-build.js +74 -0
- package/packages/core/workflow/pipeline/guard.js +158 -0
- package/packages/core/workflow/pipeline/host.js +144 -0
- package/packages/core/workflow/pipeline/integration-status.js +113 -0
- package/packages/core/workflow/pipeline/materials.js +58 -0
- package/packages/core/workflow/pipeline/notifications.js +36 -0
- package/packages/core/workflow/pipeline/parent-notification.js +27 -0
- package/packages/core/workflow/pipeline/platforms.js +21 -0
- package/packages/core/workflow/pipeline/progress.js +108 -0
- package/packages/core/workflow/pipeline/projection.js +20 -0
- package/packages/core/workflow/pipeline/reconcile.js +194 -0
- package/packages/core/workflow/pipeline/record-action.js +27 -0
- package/packages/core/workflow/pipeline/recorder-status.js +69 -0
- package/packages/core/workflow/pipeline/recovery-action.js +30 -0
- package/packages/core/workflow/pipeline/recovery-admission.js +122 -0
- package/packages/core/workflow/pipeline/recovery-build-control.js +95 -0
- package/packages/core/workflow/pipeline/recovery-build-evidence.js +71 -0
- package/packages/core/workflow/pipeline/recovery-build.js +121 -0
- package/packages/core/workflow/pipeline/recovery-controller.js +91 -0
- package/packages/core/workflow/pipeline/recovery-integration.js +79 -0
- package/packages/core/workflow/pipeline/recovery-merge-proof.js +90 -0
- package/packages/core/workflow/pipeline/recovery-ownership-settlement.js +150 -0
- package/packages/core/workflow/pipeline/recovery-ownership.js +91 -0
- package/packages/core/workflow/pipeline/recovery-plan.js +126 -0
- package/packages/core/workflow/pipeline/recovery-runtime.js +68 -0
- package/packages/core/workflow/pipeline/recovery-session.js +129 -0
- package/packages/core/workflow/pipeline/recovery-transition.js +135 -0
- package/packages/core/workflow/pipeline/recovery-unrecorded-build.js +74 -0
- package/packages/core/workflow/pipeline/runtime-source.js +111 -0
- package/packages/core/workflow/pipeline/selection.js +111 -0
- package/packages/core/workflow/pipeline/session.js +72 -0
- package/packages/core/workflow/pipeline/settlement.js +137 -0
- package/packages/core/workflow/pipeline/wake.js +38 -0
- package/packages/core/workflow/pipeline/web-status.js +108 -0
- package/packages/core/workflow/workflow-taxonomy.mjs +67 -47
- package/scripts/build-release-discussion-reader.mjs +11 -7
- package/scripts/check-inventory.mjs +7 -70
- package/scripts/check-pipeline-publication-topology.mjs +50 -0
- package/scripts/check-public-dogfood-contract.mjs +38 -51
- package/scripts/check-release-topology.mjs +15 -8
- package/scripts/check-self-consumer-contract.mjs +133 -0
- package/scripts/check-universal-workflow-bootstrap.mjs +4 -6
- package/scripts/generate-minimal-consumer-contract.mjs +155 -0
- package/scripts/generate-site-bundle.mjs +1 -1
- package/scripts/inventory/binary.mjs +9 -19
- package/scripts/inventory/promotion.mjs +2 -13
- package/scripts/prepare-product-toolchain.mjs +10 -0
- package/scripts/verify-golden-path.mjs +5 -1
- package/scripts/verify-product-platform.mjs +65 -0
- package/scripts/verify-standalone-product.mjs +42 -0
- package/templates/minimal-consumer/binary/.buildchain/buildchain.toml +45 -0
- package/templates/minimal-consumer/binary/.github/workflows/buildchain-recover.yml +30 -0
- package/templates/minimal-consumer/binary/.github/workflows/buildchain.yml +28 -0
- package/templates/minimal-consumer/binary/package.json +6 -0
- package/templates/minimal-consumer/binary/src/build.mjs +5 -0
- package/templates/minimal-consumer/binary/src/hello.c +2 -0
- package/templates/minimal-consumer/binary/src/verify.mjs +3 -0
- package/templates/minimal-consumer/npm/.buildchain/buildchain.toml +46 -0
- package/templates/minimal-consumer/npm/.github/workflows/buildchain-recover.yml +30 -0
- package/templates/minimal-consumer/npm/.github/workflows/buildchain.yml +28 -0
- package/templates/minimal-consumer/npm/package.json +6 -0
- package/templates/minimal-consumer/npm/src/build.mjs +5 -0
- package/templates/minimal-consumer/npm/src/verify.mjs +3 -0
- package/templates/minimal-consumer/paper/.buildchain/buildchain.toml +45 -0
- package/templates/minimal-consumer/paper/.github/workflows/buildchain-recover.yml +30 -0
- package/templates/minimal-consumer/paper/.github/workflows/buildchain.yml +28 -0
- package/templates/minimal-consumer/paper/package.json +6 -0
- package/templates/minimal-consumer/paper/src/build.mjs +12 -0
- package/templates/minimal-consumer/paper/src/verify.mjs +3 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { publicationArtifactProducer } from "./build-segments.js";
|
|
2
|
+
import { recordDigest } from "../../release/discussion/envelope.js";
|
|
3
|
+
import {
|
|
4
|
+
STAGE_CAPSULE_CONTRACT,
|
|
5
|
+
STAGE_CAPSULE_IDENTITY_CONTRACT,
|
|
6
|
+
stageCapsuleIdentityRoot,
|
|
7
|
+
stageCapsuleRoot,
|
|
8
|
+
validateStageCapsule,
|
|
9
|
+
} from "../../build/stage-capsule.js";
|
|
10
|
+
import { verifyPipelineQualification } from "./documents.js";
|
|
11
|
+
|
|
12
|
+
export function pipelineProductCapsules({
|
|
13
|
+
plan,
|
|
14
|
+
materialization,
|
|
15
|
+
qualified,
|
|
16
|
+
bundles,
|
|
17
|
+
evaluatedAt,
|
|
18
|
+
}) {
|
|
19
|
+
verifyPipelineQualification({
|
|
20
|
+
plan,
|
|
21
|
+
materialization,
|
|
22
|
+
qualified,
|
|
23
|
+
evaluatedAt,
|
|
24
|
+
});
|
|
25
|
+
const capsules = qualified.artifacts.map((artifact) => {
|
|
26
|
+
const bundle = bundles.find(
|
|
27
|
+
({ providerArtifact }) =>
|
|
28
|
+
providerArtifact.id === artifact.providerArtifactId,
|
|
29
|
+
);
|
|
30
|
+
if (
|
|
31
|
+
!bundle ||
|
|
32
|
+
!bundle.providerArtifact.expires_at ||
|
|
33
|
+
Date.parse(bundle.providerArtifact.expires_at) <=
|
|
34
|
+
Date.parse(qualified.qualification.issuedAt)
|
|
35
|
+
)
|
|
36
|
+
throw new Error(
|
|
37
|
+
"Product Capsule requires a live provider retention promise",
|
|
38
|
+
);
|
|
39
|
+
const producer = publicationArtifactProducer(
|
|
40
|
+
qualified.build,
|
|
41
|
+
artifact.providerArtifactId,
|
|
42
|
+
);
|
|
43
|
+
const identity = {
|
|
44
|
+
schema: STAGE_CAPSULE_IDENTITY_CONTRACT,
|
|
45
|
+
sourceRoot: recordDigest(qualified.source),
|
|
46
|
+
platform: artifact.platform,
|
|
47
|
+
platformRoot: recordDigest({ platform: artifact.platform }),
|
|
48
|
+
stage: "verify",
|
|
49
|
+
toolchainRoots: [],
|
|
50
|
+
runtimeRoot: recordDigest(producer.plan?.runtime || plan.runtime),
|
|
51
|
+
policyRoot: plan.contractRoot,
|
|
52
|
+
declaredInputs: [
|
|
53
|
+
{ name: "publication-plan", root: producer.plan?.root || plan.root },
|
|
54
|
+
],
|
|
55
|
+
transformationRoot: recordDigest({
|
|
56
|
+
source: qualified.source,
|
|
57
|
+
product: artifact.product,
|
|
58
|
+
artifact: artifact.id,
|
|
59
|
+
kind: artifact.kind,
|
|
60
|
+
}),
|
|
61
|
+
outputManifestRoot: artifact.manifestRoot,
|
|
62
|
+
qualificationRoot: qualified.qualification.receiptRoot,
|
|
63
|
+
observationRoots: [
|
|
64
|
+
{ name: "provider-build", root: qualified.build.root },
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
const capsule = {
|
|
68
|
+
schema: STAGE_CAPSULE_CONTRACT,
|
|
69
|
+
writerAuthority: "typescript-v3",
|
|
70
|
+
rustAuthority: "validation-only",
|
|
71
|
+
identity,
|
|
72
|
+
identityRoot: stageCapsuleIdentityRoot(identity),
|
|
73
|
+
retentionPromise: {
|
|
74
|
+
class: "github-artifact",
|
|
75
|
+
retainUntil: bundle.providerArtifact.expires_at.replace(
|
|
76
|
+
/T(\d{2}:\d{2}:\d{2})Z$/u,
|
|
77
|
+
"T$1.000Z",
|
|
78
|
+
),
|
|
79
|
+
},
|
|
80
|
+
capsuleRoot: `sha256:${"0".repeat(64)}`,
|
|
81
|
+
};
|
|
82
|
+
capsule.capsuleRoot = stageCapsuleRoot(capsule);
|
|
83
|
+
validateStageCapsule(capsule);
|
|
84
|
+
return { id: artifact.id, artifact, capsule };
|
|
85
|
+
});
|
|
86
|
+
const body = {
|
|
87
|
+
schema: "buildchain.pipeline-product-capsules/v1",
|
|
88
|
+
qualificationRoot: qualified.root,
|
|
89
|
+
capsules,
|
|
90
|
+
};
|
|
91
|
+
return { ...body, root: recordDigest(body) };
|
|
92
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { recordDigest } from "../../release/discussion/envelope.js";
|
|
2
|
+
import { resumePipelineSession } from "../../workflow/pipeline/session.js";
|
|
3
|
+
import { pipelinePublicationJournal } from "./journal.js";
|
|
4
|
+
|
|
5
|
+
export async function publicationContext(context, host) {
|
|
6
|
+
if (
|
|
7
|
+
context.schema !== "buildchain.pipeline-publication-context/v1" ||
|
|
8
|
+
context.runId !== host.runId ||
|
|
9
|
+
context.runAttempt !== host.runAttempt
|
|
10
|
+
)
|
|
11
|
+
throw new Error(
|
|
12
|
+
"Publication context is not from this exact provider execution",
|
|
13
|
+
);
|
|
14
|
+
const session = await resumePipelineSession(
|
|
15
|
+
{ ...host, attempt: context.attempt },
|
|
16
|
+
host,
|
|
17
|
+
);
|
|
18
|
+
if (session.observed.generation !== context.generation)
|
|
19
|
+
throw new Error(
|
|
20
|
+
"Publication context belongs to a superseded source generation",
|
|
21
|
+
);
|
|
22
|
+
const journal = pipelinePublicationJournal(session, host);
|
|
23
|
+
await journal.fence();
|
|
24
|
+
const contexts = await journal.materials("publication/context/");
|
|
25
|
+
if (!contexts.some((value) => recordDigest(value) === recordDigest(context)))
|
|
26
|
+
throw new Error(
|
|
27
|
+
"Publication context is not retained in the authoritative attempt",
|
|
28
|
+
);
|
|
29
|
+
return { session, journal, archive: host.productArchive(session) };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function uniquePublicationMaterial(journal, prefix) {
|
|
33
|
+
const values = await journal.materials(prefix);
|
|
34
|
+
if (values.length !== 1)
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Publication requires one exact retained material: ${prefix}`,
|
|
37
|
+
);
|
|
38
|
+
return values[0];
|
|
39
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { recordDigest } from "../../release/discussion/envelope.js";
|
|
3
|
+
import { bindPipelineDevelopmentAnchor } from "./development-transition.js";
|
|
4
|
+
import { githubPipelineVersion } from "../../providers/github/pipeline-version.js";
|
|
5
|
+
import { developmentTransitionReadback } from "./development-proof.js";
|
|
6
|
+
|
|
7
|
+
export async function observePipelineVersionAnchor(context, host, transition) {
|
|
8
|
+
const { plan } = context;
|
|
9
|
+
const commit = await host.source.branchHead(plan.developmentBranch);
|
|
10
|
+
const source = await host.source.source(commit, plan.source.configPath);
|
|
11
|
+
const ancestry =
|
|
12
|
+
plan.channel === "stable"
|
|
13
|
+
? null
|
|
14
|
+
: await host.request(
|
|
15
|
+
`/repos/${host.repository}/compare/${plan.intentSource.commit}...${commit}`,
|
|
16
|
+
);
|
|
17
|
+
if (
|
|
18
|
+
ancestry &&
|
|
19
|
+
(!["identical", "ahead"].includes(ancestry.status) ||
|
|
20
|
+
ancestry.merge_base_commit?.sha !== plan.intentSource.commit)
|
|
21
|
+
)
|
|
22
|
+
throw new Error(
|
|
23
|
+
"Version anchor is not descended from the released development source",
|
|
24
|
+
);
|
|
25
|
+
if (recordDigest(source.plan) !== plan.contractRoot)
|
|
26
|
+
throw new Error("Protected anchor changed the product contract");
|
|
27
|
+
const version = await githubPipelineVersion(
|
|
28
|
+
host.request,
|
|
29
|
+
host.repository,
|
|
30
|
+
).inspect(source.identity, plan.versionPolicy);
|
|
31
|
+
if (version.version === plan.version)
|
|
32
|
+
return {
|
|
33
|
+
state: "waiting",
|
|
34
|
+
reason: "waiting-for-protected-version-anchor",
|
|
35
|
+
transitionRoot: transition.idempotencyKey,
|
|
36
|
+
};
|
|
37
|
+
const pulls = await host.request(
|
|
38
|
+
`/repos/${host.repository}/commits/${commit}/pulls?per_page=100`,
|
|
39
|
+
);
|
|
40
|
+
const candidates = pulls.filter(
|
|
41
|
+
(pr) =>
|
|
42
|
+
pr.base?.ref === plan.developmentBranch && pr.merge_commit_sha === commit,
|
|
43
|
+
);
|
|
44
|
+
if (candidates.length !== 1)
|
|
45
|
+
throw new Error("Version anchor requires one exact protected merged PR");
|
|
46
|
+
const pr = await host.request(
|
|
47
|
+
`/repos/${host.repository}/pulls/${candidates[0].number}`,
|
|
48
|
+
);
|
|
49
|
+
const integration = await host.integration.observe({
|
|
50
|
+
intent: {
|
|
51
|
+
source: { pullRequest: pr.number, targetBranch: plan.developmentBranch },
|
|
52
|
+
},
|
|
53
|
+
generation: {
|
|
54
|
+
source: { commit: pr.head.sha, configPath: plan.source.configPath },
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
if (
|
|
58
|
+
plan.channel === "stable" &&
|
|
59
|
+
Date.parse(pr.merged_at) <=
|
|
60
|
+
Date.parse(transition.completedStable.completedAt)
|
|
61
|
+
)
|
|
62
|
+
return {
|
|
63
|
+
state: "waiting",
|
|
64
|
+
reason: "waiting-for-protected-version-anchor",
|
|
65
|
+
transitionRoot: transition.idempotencyKey,
|
|
66
|
+
};
|
|
67
|
+
const file = plan.versionPolicy.files[0].path;
|
|
68
|
+
const anchor = {
|
|
69
|
+
manifestPath: file,
|
|
70
|
+
manifestRoot: `sha256:${createHash("sha256").update(version.files[file]).digest("hex")}`,
|
|
71
|
+
};
|
|
72
|
+
const bound = bindPipelineDevelopmentAnchor(transition, {
|
|
73
|
+
targetVersion: version.version,
|
|
74
|
+
anchor,
|
|
75
|
+
});
|
|
76
|
+
const verified = developmentTransitionReadback(bound, {
|
|
77
|
+
before: version,
|
|
78
|
+
after: version,
|
|
79
|
+
evidence: integration,
|
|
80
|
+
createdAt: pr.created_at,
|
|
81
|
+
mergedAt: pr.merged_at,
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
state: "success",
|
|
85
|
+
reason: "protected-version-anchor-verified",
|
|
86
|
+
transition: verified,
|
|
87
|
+
source: source.identity,
|
|
88
|
+
integration,
|
|
89
|
+
anchor,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { compareDevelopmentVersions } from "../publication-development.js";
|
|
2
|
+
import { recordDigest } from "../../release/discussion/envelope.js";
|
|
3
|
+
|
|
4
|
+
export async function observeAdvancedPipelineDevelopment(
|
|
5
|
+
context,
|
|
6
|
+
host,
|
|
7
|
+
transition,
|
|
8
|
+
adapter,
|
|
9
|
+
) {
|
|
10
|
+
const { plan } = context;
|
|
11
|
+
const commit = await host.source.branchHead(plan.developmentBranch);
|
|
12
|
+
const source = await host.source.source(commit, plan.source.configPath);
|
|
13
|
+
if (recordDigest(source.plan) !== plan.contractRoot)
|
|
14
|
+
throw new Error(
|
|
15
|
+
"Protected development contract changed before next-version readback",
|
|
16
|
+
);
|
|
17
|
+
const observed = await adapter.inspect(source.identity, plan.versionPolicy);
|
|
18
|
+
const order = compareDevelopmentVersions(
|
|
19
|
+
observed.version,
|
|
20
|
+
transition.target.version,
|
|
21
|
+
);
|
|
22
|
+
if (order < 0) return null;
|
|
23
|
+
const pulls = await host.request(
|
|
24
|
+
`/repos/${host.repository}/commits/${commit}/pulls?per_page=100`,
|
|
25
|
+
);
|
|
26
|
+
const matches = pulls.filter(
|
|
27
|
+
(pr) =>
|
|
28
|
+
pr.base?.ref === plan.developmentBranch && pr.merge_commit_sha === commit,
|
|
29
|
+
);
|
|
30
|
+
if (matches.length !== 1)
|
|
31
|
+
throw new Error(
|
|
32
|
+
"Advanced development requires one exact protected merged PR",
|
|
33
|
+
);
|
|
34
|
+
const pr = await host.request(
|
|
35
|
+
`/repos/${host.repository}/pulls/${matches[0].number}`,
|
|
36
|
+
);
|
|
37
|
+
const integration = await host.integration.observe({
|
|
38
|
+
intent: {
|
|
39
|
+
source: { pullRequest: pr.number, targetBranch: plan.developmentBranch },
|
|
40
|
+
},
|
|
41
|
+
generation: {
|
|
42
|
+
source: { commit: pr.head.sha, configPath: plan.source.configPath },
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
if (integration.mergeCommit !== commit)
|
|
46
|
+
throw new Error(
|
|
47
|
+
"Advanced development proof differs from the observed protected commit",
|
|
48
|
+
);
|
|
49
|
+
return {
|
|
50
|
+
state: "success",
|
|
51
|
+
reason:
|
|
52
|
+
order === 0
|
|
53
|
+
? "protected-development-already-current"
|
|
54
|
+
: "protected-development-already-advanced",
|
|
55
|
+
transitionRoot: transition.idempotencyKey,
|
|
56
|
+
targetVersion: transition.target.version,
|
|
57
|
+
observedVersion: observed.version,
|
|
58
|
+
source: source.identity,
|
|
59
|
+
integration,
|
|
60
|
+
filesRoot: recordDigest(observed.files),
|
|
61
|
+
publicationOutcome: "preserved-success",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { recordDigest } from "../../release/discussion/envelope.js";
|
|
2
|
+
import {
|
|
3
|
+
preparePipelineVersionContext,
|
|
4
|
+
retainedPipelineVersionRegeneration,
|
|
5
|
+
} from "./version-context.js";
|
|
6
|
+
|
|
7
|
+
export async function prepareNextDevelopmentPullRequest(
|
|
8
|
+
context,
|
|
9
|
+
host,
|
|
10
|
+
journal,
|
|
11
|
+
transition,
|
|
12
|
+
adapter,
|
|
13
|
+
) {
|
|
14
|
+
const { plan } = context;
|
|
15
|
+
const retained = await journal.materials(
|
|
16
|
+
"publication/next-development-source/",
|
|
17
|
+
);
|
|
18
|
+
if (retained.length > 1)
|
|
19
|
+
throw new Error("Next-development source reservation is ambiguous");
|
|
20
|
+
let request = retained[0];
|
|
21
|
+
if (!request) {
|
|
22
|
+
const baseCommit = await host.source.branchHead(plan.developmentBranch);
|
|
23
|
+
const source = await host.source.source(baseCommit, plan.source.configPath);
|
|
24
|
+
if (recordDigest(source.plan) !== plan.contractRoot)
|
|
25
|
+
throw new Error(
|
|
26
|
+
"Protected development contract changed before version preparation",
|
|
27
|
+
);
|
|
28
|
+
const input = await adapter.inspect(source.identity, plan.versionPolicy);
|
|
29
|
+
if (
|
|
30
|
+
plan.channel === "stable"
|
|
31
|
+
? input.version.split("-alpha.")[0] !== plan.version
|
|
32
|
+
: input.version !== plan.version
|
|
33
|
+
)
|
|
34
|
+
throw new Error(
|
|
35
|
+
"Development version already changed; reconcile its protected provenance before preparing another PR",
|
|
36
|
+
);
|
|
37
|
+
request = {
|
|
38
|
+
source: source.identity,
|
|
39
|
+
versionPolicy: plan.versionPolicy,
|
|
40
|
+
version: transition.target.version,
|
|
41
|
+
root: recordDigest({
|
|
42
|
+
transitionRoot: transition.idempotencyKey,
|
|
43
|
+
source: source.identity,
|
|
44
|
+
}),
|
|
45
|
+
sourceTimestamp: input.sourceTimestamp,
|
|
46
|
+
};
|
|
47
|
+
await journal.record("publication/next-development-source", request, {
|
|
48
|
+
phase: "next-development",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const regeneration = await retainedPipelineVersionRegeneration(
|
|
52
|
+
journal,
|
|
53
|
+
request,
|
|
54
|
+
"development",
|
|
55
|
+
host.runtime,
|
|
56
|
+
);
|
|
57
|
+
if (request.versionPolicy.derived_files?.length && !regeneration)
|
|
58
|
+
return {
|
|
59
|
+
state: "waiting",
|
|
60
|
+
reason: "next-development-version-regeneration-required",
|
|
61
|
+
versionContext: await preparePipelineVersionContext(
|
|
62
|
+
request,
|
|
63
|
+
"development",
|
|
64
|
+
context,
|
|
65
|
+
host,
|
|
66
|
+
journal,
|
|
67
|
+
context.recovery?.execution.publisher.workflowSha ||
|
|
68
|
+
plan.publisher.workflowSha,
|
|
69
|
+
),
|
|
70
|
+
};
|
|
71
|
+
await journal.fence();
|
|
72
|
+
const materialization = await adapter.materializeDevelopment(
|
|
73
|
+
request,
|
|
74
|
+
regeneration,
|
|
75
|
+
);
|
|
76
|
+
const branch = materialization.branch;
|
|
77
|
+
const pulls = await host.request(
|
|
78
|
+
`/repos/${host.repository}/pulls?state=all&head=${encodeURIComponent(`${host.repository.split("/")[0]}:${branch}`)}&base=${encodeURIComponent(plan.developmentBranch)}&per_page=100`,
|
|
79
|
+
);
|
|
80
|
+
if (!Array.isArray(pulls) || pulls.length > 1)
|
|
81
|
+
throw new Error("Next-development PR identity is ambiguous");
|
|
82
|
+
let pr = pulls[0];
|
|
83
|
+
if (!pr) {
|
|
84
|
+
await journal.fence();
|
|
85
|
+
if (!host.pullRequests)
|
|
86
|
+
throw new Error(
|
|
87
|
+
"Next-development requires the repository-authorized automation App or token to trigger ordinary PR checks",
|
|
88
|
+
);
|
|
89
|
+
pr = await host.pullRequests(`/repos/${host.repository}/pulls`, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
body: {
|
|
92
|
+
title: `chore(version): prepare ${transition.target.version}`,
|
|
93
|
+
head: branch,
|
|
94
|
+
base: plan.developmentBranch,
|
|
95
|
+
body: `Prepare the declared next development version after ${plan.tag}.\n\nBuildchain-Parent-Attempt: ${context.attempt}\nBuildchain-Transition: ${transition.idempotencyKey}\n\nThe normal protected pipeline verifies the declared products before merge.`,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const selected = {
|
|
100
|
+
schema: "buildchain.pipeline-next-development-pr/v1",
|
|
101
|
+
number: pr.number,
|
|
102
|
+
branch,
|
|
103
|
+
base: plan.developmentBranch,
|
|
104
|
+
source: materialization.source,
|
|
105
|
+
materialization,
|
|
106
|
+
transitionRoot: transition.idempotencyKey,
|
|
107
|
+
};
|
|
108
|
+
await journal.record("publication/next-development-pr", selected, {
|
|
109
|
+
phase: "next-development",
|
|
110
|
+
});
|
|
111
|
+
return selected;
|
|
112
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { stablePipelineDevelopmentReadback } from "./development-transition.js";
|
|
3
|
+
import { recordDigest } from "../../release/discussion/envelope.js";
|
|
4
|
+
import {
|
|
5
|
+
nextDevelopmentRoot,
|
|
6
|
+
recordNextDevelopmentMaterialization,
|
|
7
|
+
advanceNextDevelopmentTransition,
|
|
8
|
+
} from "../../release/next-development-transition.js";
|
|
9
|
+
|
|
10
|
+
export function developmentTransitionReadback(
|
|
11
|
+
transition,
|
|
12
|
+
{ before, after, evidence, createdAt, mergedAt },
|
|
13
|
+
) {
|
|
14
|
+
const hash = (bytes) =>
|
|
15
|
+
`sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
16
|
+
const paths = [
|
|
17
|
+
...transition.adapter.sourcePaths,
|
|
18
|
+
...transition.adapter.derivedPaths,
|
|
19
|
+
]
|
|
20
|
+
.sort()
|
|
21
|
+
.map((file) => {
|
|
22
|
+
if (
|
|
23
|
+
typeof before.files[file] !== "string" ||
|
|
24
|
+
typeof after.files[file] !== "string"
|
|
25
|
+
)
|
|
26
|
+
throw new Error("Next-development lacks exact declared file bytes");
|
|
27
|
+
return {
|
|
28
|
+
path: file,
|
|
29
|
+
beforeRoot: hash(before.files[file]),
|
|
30
|
+
afterRoot: hash(after.files[file]),
|
|
31
|
+
changed: before.files[file] !== after.files[file],
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
if (after.version !== transition.target.version)
|
|
35
|
+
throw new Error(
|
|
36
|
+
"Next-development materialization does not match its authorized target",
|
|
37
|
+
);
|
|
38
|
+
const body = {
|
|
39
|
+
targetVersion: transition.target.version,
|
|
40
|
+
anchorRoot: transition.target.anchor?.manifestRoot || null,
|
|
41
|
+
paths,
|
|
42
|
+
};
|
|
43
|
+
if (!transition.completedAlpha)
|
|
44
|
+
return stablePipelineDevelopmentReadback(
|
|
45
|
+
transition,
|
|
46
|
+
body,
|
|
47
|
+
evidence,
|
|
48
|
+
createdAt,
|
|
49
|
+
mergedAt,
|
|
50
|
+
);
|
|
51
|
+
let state = recordNextDevelopmentMaterialization(transition, {
|
|
52
|
+
materialization: {
|
|
53
|
+
...body,
|
|
54
|
+
materializationRoot: nextDevelopmentRoot(body),
|
|
55
|
+
},
|
|
56
|
+
evidenceRoot: recordDigest(evidence),
|
|
57
|
+
recordedAt: new Date(createdAt).toISOString(),
|
|
58
|
+
});
|
|
59
|
+
state = advanceNextDevelopmentTransition(state, {
|
|
60
|
+
to: "pr-pending",
|
|
61
|
+
event: "protected-version-pr-observed",
|
|
62
|
+
evidenceRoot: recordDigest(evidence),
|
|
63
|
+
expectedStateRoot: state.state.stateRoot,
|
|
64
|
+
recordedAt: new Date(createdAt).toISOString(),
|
|
65
|
+
});
|
|
66
|
+
if (mergedAt)
|
|
67
|
+
for (const phase of ["merged", "verified"])
|
|
68
|
+
state = advanceNextDevelopmentTransition(state, {
|
|
69
|
+
to: phase,
|
|
70
|
+
event: `protected-version-${phase}`,
|
|
71
|
+
evidenceRoot: recordDigest(evidence),
|
|
72
|
+
expectedStateRoot: state.state.stateRoot,
|
|
73
|
+
recordedAt: new Date(mergedAt).toISOString(),
|
|
74
|
+
});
|
|
75
|
+
return state;
|
|
76
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createNextDevelopmentTransition,
|
|
3
|
+
bindNextDevelopmentAnchor,
|
|
4
|
+
nextDevelopmentRoot,
|
|
5
|
+
} from "../../release/next-development-transition.js";
|
|
6
|
+
import { createStableDevelopmentTransition } from "../publication-development.js";
|
|
7
|
+
import { recordDigest } from "../../release/discussion/envelope.js";
|
|
8
|
+
|
|
9
|
+
export function createPipelineDevelopmentTransition(context, publication) {
|
|
10
|
+
const { plan, materialization } = context;
|
|
11
|
+
const input = {
|
|
12
|
+
repository: plan.source.repository,
|
|
13
|
+
model: {
|
|
14
|
+
strategy: plan.versionPolicy.strategy,
|
|
15
|
+
next: plan.versionPolicy.strategy === "semver" ? "auto" : "manual",
|
|
16
|
+
},
|
|
17
|
+
sourcePaths: [
|
|
18
|
+
...new Set(plan.versionPolicy.files.map(({ path }) => path)),
|
|
19
|
+
].sort(),
|
|
20
|
+
derivedPaths: [...(plan.versionPolicy.derived_files || [])].sort(),
|
|
21
|
+
};
|
|
22
|
+
const completed = {
|
|
23
|
+
outcome: "succeeded",
|
|
24
|
+
version: plan.version,
|
|
25
|
+
exactTag: plan.tag,
|
|
26
|
+
releaseSha: materialization.source.commit,
|
|
27
|
+
treeSha: materialization.source.tree,
|
|
28
|
+
publicationRoot: publication.release.receiptRoot,
|
|
29
|
+
completedAt: publication.completedAt,
|
|
30
|
+
};
|
|
31
|
+
if (plan.channel === "alpha")
|
|
32
|
+
return createNextDevelopmentTransition({
|
|
33
|
+
...input,
|
|
34
|
+
completedAlpha: completed,
|
|
35
|
+
});
|
|
36
|
+
const adapter = {
|
|
37
|
+
sourcePaths: input.sourcePaths,
|
|
38
|
+
derivedPaths: input.derivedPaths,
|
|
39
|
+
};
|
|
40
|
+
if (input.model.strategy === "semver")
|
|
41
|
+
return {
|
|
42
|
+
...createStableDevelopmentTransition({
|
|
43
|
+
...input,
|
|
44
|
+
completedStable: completed,
|
|
45
|
+
}),
|
|
46
|
+
adapter,
|
|
47
|
+
};
|
|
48
|
+
const identity = {
|
|
49
|
+
contract: "buildchain.pipeline-stable-anchor-transition/v1",
|
|
50
|
+
...input,
|
|
51
|
+
completedStable: completed,
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
...identity,
|
|
55
|
+
idempotencyKey: nextDevelopmentRoot(identity),
|
|
56
|
+
adapter,
|
|
57
|
+
target: { version: null, anchor: null },
|
|
58
|
+
state: { status: "waiting-anchor" },
|
|
59
|
+
publicationOutcome: "preserved-success",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function bindPipelineDevelopmentAnchor(transition, input) {
|
|
64
|
+
if (transition.completedAlpha)
|
|
65
|
+
return bindNextDevelopmentAnchor(transition, input);
|
|
66
|
+
if (
|
|
67
|
+
transition.contract !== "buildchain.pipeline-stable-anchor-transition/v1" ||
|
|
68
|
+
transition.state.status !== "waiting-anchor" ||
|
|
69
|
+
!input.targetVersion ||
|
|
70
|
+
!input.anchor?.manifestRoot
|
|
71
|
+
)
|
|
72
|
+
throw new Error(
|
|
73
|
+
"Stable manual development requires its explicit protected anchor",
|
|
74
|
+
);
|
|
75
|
+
return {
|
|
76
|
+
...transition,
|
|
77
|
+
target: { version: input.targetVersion, anchor: input.anchor },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function stablePipelineDevelopmentReadback(
|
|
82
|
+
transition,
|
|
83
|
+
materialization,
|
|
84
|
+
evidence,
|
|
85
|
+
createdAt,
|
|
86
|
+
mergedAt,
|
|
87
|
+
) {
|
|
88
|
+
if (
|
|
89
|
+
![
|
|
90
|
+
"buildchain.stable-development-transition/v1",
|
|
91
|
+
"buildchain.pipeline-stable-anchor-transition/v1",
|
|
92
|
+
].includes(transition.contract)
|
|
93
|
+
)
|
|
94
|
+
throw new Error("Unknown stable development transition");
|
|
95
|
+
const state = {
|
|
96
|
+
status: mergedAt ? "verified" : "pr-pending",
|
|
97
|
+
evidenceRoot: recordDigest(evidence),
|
|
98
|
+
recordedAt: new Date(mergedAt || createdAt).toISOString(),
|
|
99
|
+
materializationRoot: nextDevelopmentRoot(materialization),
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
...transition,
|
|
103
|
+
materialization,
|
|
104
|
+
state: {
|
|
105
|
+
...state,
|
|
106
|
+
stateRoot: nextDevelopmentRoot({
|
|
107
|
+
transitionRoot: transition.idempotencyKey,
|
|
108
|
+
...state,
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|