@kungfu-tech/buildchain 4.0.1-alpha.4 → 4.0.1-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actions/release-tail/README.md +8 -0
- package/architecture/decisions/0002-next-development-transition.md +101 -0
- package/architecture/internal-capabilities.json +117 -8
- package/architecture/maintainability-policy.json +38 -20
- package/architecture/release-tail-contract-inventory.json +5 -2
- package/architecture/v3-core-mechanism-inventory.json +89 -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-next-development-parity.json +192 -0
- package/architecture/v4-publication-rehearsal-parity.json +84 -0
- package/architecture/v4-release-train-parity.json +1 -0
- package/architecture/v4-tail-reseal-parity.json +150 -0
- package/bin/buildchain.mjs +23 -2
- package/bin/internal/adopter-delivery-cli.mjs +70 -0
- package/bin/internal/command-registry.mjs +3 -0
- package/bin/internal/compatibility-facts-cli.mjs +101 -0
- package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
- package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
- package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
- package/contracts/fixtures/v4-compatibility-facts-v1/shared.json +1129 -0
- package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +7 -1
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +229 -0
- package/contracts/next-development-request-v1.schema.json +66 -0
- package/contracts/next-development-transition-v1.schema.json +292 -0
- package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
- package/contracts/v4-compatibility-facts-v1.schema.json +230 -0
- package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/contracts/v4-tail-reseal-v1.schema.json +276 -0
- package/dist/site/agent-index.json +4 -0
- package/dist/site/artifact-schemas.json +8 -0
- package/dist/site/buildchain-contract.json +1393 -28
- package/dist/site/buildchain-site.json +462 -27
- package/dist/site/capability-registry.json +17 -14
- package/dist/site/cli-registry.json +159 -1
- package/dist/site/kfd-claims.json +429 -15
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +52 -6
- package/dist/site/node-api-registry.json +13972 -9697
- package/dist/site/page-registry.json +428 -17
- package/dist/site/public-surface-audit.json +429 -11
- package/dist/site/publication-authority-registry.json +22 -4
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +21 -0
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
- package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
- package/dist/site/schemas/v4-compatibility-facts-v1.schema.json +230 -0
- package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/site-manifest.json +34 -10
- package/dist/site/workflow-registry.json +121 -8
- package/docs/MAP.md +7 -2
- package/docs/cli-reference.md +193 -0
- package/docs/cli.md +8 -0
- package/docs/dev-alpha-candidate-patrol.md +8 -0
- package/docs/next-development-transition.md +118 -0
- package/docs/node-api-reference.md +448 -168
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/v4-adopter-delivery.md +133 -0
- package/docs/v4-compatibility-facts.md +91 -0
- package/docs/v4-publication-rehearsal.md +130 -0
- package/docs/v4-tail-reseal.md +73 -0
- package/docs/versioning.md +4 -3
- package/package.json +29 -6
- package/packages/core/adopter-delivery-passport.js +259 -0
- package/packages/core/adopter-delivery-vectors.js +158 -0
- package/packages/core/adopter-delivery-vectors.json +77 -0
- package/packages/core/buildchain-agent-manuals.js +3 -0
- package/packages/core/buildchain-compatibility-authority.js +336 -0
- package/packages/core/buildchain-compatibility-fact.js +394 -0
- package/packages/core/buildchain-compatibility-facts.json +217 -0
- package/packages/core/buildchain-compatibility-proof.js +566 -0
- package/packages/core/buildchain-config.js +84 -0
- package/packages/core/buildchain-contract.js +93 -141
- 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/channel-candidate.js +8 -0
- package/packages/core/channel-promotion-baseline.js +55 -0
- package/packages/core/dev-alpha-candidate-selection.js +10 -2
- package/packages/core/dev-delivery-execution-transfer.js +21 -1
- package/packages/core/dev-delivery-process-boundary.js +19 -6
- package/packages/core/dev-delivery-provider-heartbeat.js +18 -6
- package/packages/core/kungfu-temporal-fact.js +557 -0
- package/packages/core/legacy-kfd-adopter-driver.js +167 -0
- package/packages/core/next-development-candidate-reservation.js +186 -0
- package/packages/core/next-development-controller.js +728 -0
- package/packages/core/next-development-projection.js +287 -0
- package/packages/core/next-development-transition.js +738 -0
- package/packages/core/paper-agent-entry.js +11 -5
- package/packages/core/paper.js +16 -5
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-passport.js +26 -8
- 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-floating-consumer-policy.js +29 -9
- package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
- package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
- package/packages/core/v4-publication-rehearsal.js +481 -0
- package/packages/core/v4-tail-reseal-contract.js +26 -0
- package/packages/core/v4-tail-reseal-github.js +178 -0
- package/packages/core/v4-tail-reseal-receipt.js +225 -0
- package/packages/core/v4-tail-reseal.js +594 -0
- package/scripts/buildchain-cli-help.mjs +30 -0
- package/scripts/check-inventory.mjs +14 -3
- package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
- package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
- package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-release-candidate-passport.mjs +152 -0
- package/scripts/generate-site-bundle.mjs +46 -3
- package/scripts/init-repo.mjs +22 -4
- package/scripts/next-development-self-dogfood-harness.mjs +460 -0
- package/scripts/next-development-self-dogfood.mjs +597 -0
- package/scripts/next-development-transition.mjs +47 -0
- package/scripts/release-tail.mjs +51 -3
- package/scripts/resume-from-candidate-run.mjs +545 -15
- package/scripts/site-capability-metadata.mjs +28 -0
- package/scripts/v4-consumer-policy.mjs +1 -5
- package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
- package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
- package/scripts/v4-tail-reseal.mjs +416 -0
|
@@ -50,6 +50,27 @@ const DEV_DELIVERY_AUTHORITY_SCHEMA =
|
|
|
50
50
|
"schemas/dev-delivery-authority-v2.schema.json";
|
|
51
51
|
const DEV_DELIVERY_AUTHORITY_SCHEMA_SOURCE =
|
|
52
52
|
"contracts/dev-delivery-authority-v2.schema.json";
|
|
53
|
+
const V4_COMPATIBILITY_FACTS_SCHEMA =
|
|
54
|
+
"schemas/v4-compatibility-facts-v1.schema.json";
|
|
55
|
+
const V4_COMPATIBILITY_FACTS_SCHEMA_SOURCE =
|
|
56
|
+
"contracts/v4-compatibility-facts-v1.schema.json";
|
|
57
|
+
const V4_PUBLICATION_REHEARSAL_SCHEMA =
|
|
58
|
+
"schemas/v4-publication-rehearsal-capsule-v1.schema.json";
|
|
59
|
+
const V4_PUBLICATION_REHEARSAL_SCHEMA_SOURCE =
|
|
60
|
+
"contracts/v4-publication-rehearsal-capsule-v1.schema.json";
|
|
61
|
+
const V4_ADOPTER_DELIVERY_SCHEMA =
|
|
62
|
+
"schemas/v4-adopter-delivery-v1.schema.json";
|
|
63
|
+
const V4_ADOPTER_DELIVERY_SCHEMA_SOURCE =
|
|
64
|
+
"contracts/v4-adopter-delivery-v1.schema.json";
|
|
65
|
+
const RELEASE_TAIL_CAPABILITIES_SCHEMA =
|
|
66
|
+
"schemas/release-tail-capabilities-v1.schema.json";
|
|
67
|
+
const RELEASE_TAIL_CAPABILITIES_SCHEMA_SOURCE =
|
|
68
|
+
"contracts/release-tail-capabilities-v1.schema.json";
|
|
69
|
+
const PUBLICATION_REHEARSAL_SITE_SCHEMAS = [
|
|
70
|
+
V4_PUBLICATION_REHEARSAL_SCHEMA,
|
|
71
|
+
V4_ADOPTER_DELIVERY_SCHEMA,
|
|
72
|
+
RELEASE_TAIL_CAPABILITIES_SCHEMA,
|
|
73
|
+
];
|
|
53
74
|
const root = path.resolve(import.meta.dirname, "..");
|
|
54
75
|
const outputDir = path.join(root, "dist", "site");
|
|
55
76
|
const requireFromHere = createRequire(import.meta.url);
|
|
@@ -78,9 +99,12 @@ function stableJson(value) {
|
|
|
78
99
|
}
|
|
79
100
|
|
|
80
101
|
function siteFileBytes(name, value) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
102
|
+
if (name === DEV_DELIVERY_AUTHORITY_SCHEMA) return readText(DEV_DELIVERY_AUTHORITY_SCHEMA_SOURCE);
|
|
103
|
+
if (name === V4_COMPATIBILITY_FACTS_SCHEMA) return readText(V4_COMPATIBILITY_FACTS_SCHEMA_SOURCE);
|
|
104
|
+
if (name === V4_PUBLICATION_REHEARSAL_SCHEMA) return readText(V4_PUBLICATION_REHEARSAL_SCHEMA_SOURCE);
|
|
105
|
+
if (name === V4_ADOPTER_DELIVERY_SCHEMA) return readText(V4_ADOPTER_DELIVERY_SCHEMA_SOURCE);
|
|
106
|
+
if (name === RELEASE_TAIL_CAPABILITIES_SCHEMA) return readText(RELEASE_TAIL_CAPABILITIES_SCHEMA_SOURCE);
|
|
107
|
+
return stableJson(value);
|
|
84
108
|
}
|
|
85
109
|
|
|
86
110
|
function writeSiteFile(name, value) {
|
|
@@ -383,13 +407,16 @@ const manualMetaById = new Map(Object.entries({
|
|
|
383
407
|
"release-activation-transaction": { capabilityGroup: "release-passport-trust", audience: ["release-operator", "agent"], maturity: "preview", order: 125 },
|
|
384
408
|
"release-candidate": { capabilityGroup: "reusable-build", audience: ["release-operator", "consumer"], maturity: "stable", order: 130 },
|
|
385
409
|
"release-train": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 132 },
|
|
410
|
+
"next-development-transition": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 133 },
|
|
386
411
|
"stable-candidate-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer"], maturity: "preview", order: 135 },
|
|
387
412
|
"dev-qualification-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 136 },
|
|
388
413
|
"dev-alpha-candidate-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 137 },
|
|
389
414
|
"v4-canonical-contracts": { capabilityGroup: "governance-versioning", audience: ["developer", "maintainer", "agent"], maturity: "preview", order: 138 },
|
|
415
|
+
"v4-compatibility-facts": { capabilityGroup: "governance-versioning", audience: ["developer", "maintainer", "agent"], maturity: "preview", order: 139 },
|
|
390
416
|
"observed-evidence-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 140 },
|
|
391
417
|
"engineering-housekeeper": { capabilityGroup: "governance-versioning", audience: ["maintainer", "consumer", "agent"], maturity: "preview", order: 145 },
|
|
392
418
|
"reusable-build-surface": { capabilityGroup: "reusable-build", audience: ["consumer", "release-operator"], maturity: "stable", order: 200 },
|
|
419
|
+
"v4-adopter-delivery": { capabilityGroup: "reusable-build", audience: ["consumer", "developer", "agent"], maturity: "preview", order: 205 },
|
|
393
420
|
"lifecycle-protocol": { capabilityGroup: "reusable-build", audience: ["consumer", "developer"], maturity: "stable", order: 210 },
|
|
394
421
|
"runtime-train-validation": { capabilityGroup: "governance-versioning", audience: ["maintainer", "consumer"], maturity: "stable", order: 220 },
|
|
395
422
|
"kfd-support": { capabilityGroup: "kfd-trust", audience: ["agent", "maintainer"], maturity: "stable", order: 300 },
|
|
@@ -753,6 +780,7 @@ function buildSiteBundle() {
|
|
|
753
780
|
"docs/reusable-build-surface.md",
|
|
754
781
|
"docs/release-candidate.md",
|
|
755
782
|
"docs/release-train.md",
|
|
783
|
+
"docs/next-development-transition.md",
|
|
756
784
|
"docs/stable-candidate-patrol.md",
|
|
757
785
|
"docs/dev-qualification-patrol.md",
|
|
758
786
|
"docs/dev-alpha-candidate-patrol.md",
|
|
@@ -948,6 +976,8 @@ function buildSiteBundle() {
|
|
|
948
976
|
"schemas/kfd-agent-hub-adoption.schema.json",
|
|
949
977
|
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
950
978
|
"schemas/dev-delivery-authority-v2.schema.json",
|
|
979
|
+
"schemas/v4-compatibility-facts-v1.schema.json",
|
|
980
|
+
...PUBLICATION_REHEARSAL_SITE_SCHEMAS,
|
|
951
981
|
"kfd-support.json",
|
|
952
982
|
"artifact-evidence.json",
|
|
953
983
|
"product-mechanism.json",
|
|
@@ -983,6 +1013,8 @@ function buildSiteBundle() {
|
|
|
983
1013
|
"schemas/kfd-agent-hub-adoption.schema.json",
|
|
984
1014
|
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
985
1015
|
"schemas/dev-delivery-authority-v2.schema.json",
|
|
1016
|
+
"schemas/v4-compatibility-facts-v1.schema.json",
|
|
1017
|
+
...PUBLICATION_REHEARSAL_SITE_SCHEMAS,
|
|
986
1018
|
"buildchain-contract.json",
|
|
987
1019
|
"kfd-claims.json",
|
|
988
1020
|
"product-mechanism.json",
|
|
@@ -1114,6 +1146,8 @@ function buildSiteBundle() {
|
|
|
1114
1146
|
"schemas/kfd-agent-hub-adoption.schema.json",
|
|
1115
1147
|
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
1116
1148
|
"schemas/dev-delivery-authority-v2.schema.json",
|
|
1149
|
+
"schemas/v4-compatibility-facts-v1.schema.json",
|
|
1150
|
+
...PUBLICATION_REHEARSAL_SITE_SCHEMAS,
|
|
1117
1151
|
"buildchain-contract.json",
|
|
1118
1152
|
"kfd-upstream-aggregate.json",
|
|
1119
1153
|
"kfd-claims.json",
|
|
@@ -1291,6 +1325,15 @@ function buildSiteBundle() {
|
|
|
1291
1325
|
[DEV_DELIVERY_AUTHORITY_SCHEMA]: readJson(
|
|
1292
1326
|
DEV_DELIVERY_AUTHORITY_SCHEMA_SOURCE,
|
|
1293
1327
|
),
|
|
1328
|
+
[V4_COMPATIBILITY_FACTS_SCHEMA]: readJson(
|
|
1329
|
+
V4_COMPATIBILITY_FACTS_SCHEMA_SOURCE,
|
|
1330
|
+
),
|
|
1331
|
+
...Object.fromEntries(
|
|
1332
|
+
PUBLICATION_REHEARSAL_SITE_SCHEMAS.map((name) => [
|
|
1333
|
+
name,
|
|
1334
|
+
JSON.parse(siteFileBytes(name)),
|
|
1335
|
+
]),
|
|
1336
|
+
),
|
|
1294
1337
|
"badge-endpoint-registry.json": badgeEndpointRegistry,
|
|
1295
1338
|
"publication-registry.json": publicationRegistry,
|
|
1296
1339
|
"buildchain-contract.json": createBuildchainContractWorld({ root, controllerRegistry }),
|
package/scripts/init-repo.mjs
CHANGED
|
@@ -4,6 +4,11 @@ import path from "node:path";
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { BUILDCHAIN_CONFIG_PATH } from "../packages/core/buildchain-layout.js";
|
|
6
6
|
import { detectPackageManager, assertPackageManager } from "../packages/core/package-manager.js";
|
|
7
|
+
import {
|
|
8
|
+
appendNextDevelopmentToml,
|
|
9
|
+
mergeNextDevelopmentAgentInstructions,
|
|
10
|
+
nextDevelopmentWorkflowHeader,
|
|
11
|
+
} from "../packages/core/next-development-projection.js";
|
|
7
12
|
|
|
8
13
|
const BUILDCHAIN_WORKFLOW_REF = "kungfu-systems/buildchain/.github/workflows/.build.yml@v4";
|
|
9
14
|
const DEFAULT_PUBLICATION_LATEX_IMAGE = "ghcr.io/kungfu-systems/build-images/latex-pdf-builder";
|
|
@@ -327,7 +332,7 @@ function workflowArtifactPaths(type) {
|
|
|
327
332
|
}
|
|
328
333
|
|
|
329
334
|
function publicationWorkflowYaml() {
|
|
330
|
-
return
|
|
335
|
+
return `${nextDevelopmentWorkflowHeader()}# Commit both .buildchain/contract-lock.json and .buildchain/alpha-contract-lock.json.
|
|
331
336
|
# Persist only @v4 or @v4-alpha; pass temporary train/SHA runtimes through workflow_dispatch.
|
|
332
337
|
name: Build
|
|
333
338
|
|
|
@@ -361,7 +366,7 @@ jobs:
|
|
|
361
366
|
}
|
|
362
367
|
|
|
363
368
|
function workflowYaml({ type, runnerPreset, artifactName }) {
|
|
364
|
-
return
|
|
369
|
+
return `${nextDevelopmentWorkflowHeader()}# Buildchain v4 lifecycle checkpoints are governed by architecture/v4-platform-stage-checkpoints.json.
|
|
365
370
|
# Do not add undeclared runner-only inputs, outputs, environment, or provider effects.
|
|
366
371
|
# Commit both .buildchain/contract-lock.json and .buildchain/alpha-contract-lock.json.
|
|
367
372
|
# Persist only @v4 or @v4-alpha; pass temporary train/SHA runtimes through workflow_dispatch.
|
|
@@ -407,6 +412,13 @@ function writeIfAllowed(filePath, content, { force }) {
|
|
|
407
412
|
return filePath;
|
|
408
413
|
}
|
|
409
414
|
|
|
415
|
+
function writeManagedAgentEntry(filePath, current) {
|
|
416
|
+
const content = mergeNextDevelopmentAgentInstructions(current);
|
|
417
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
418
|
+
fs.writeFileSync(filePath, content);
|
|
419
|
+
return filePath;
|
|
420
|
+
}
|
|
421
|
+
|
|
410
422
|
export function initBuildchainRepo({
|
|
411
423
|
cwd = process.cwd(),
|
|
412
424
|
type = "package",
|
|
@@ -417,7 +429,7 @@ export function initBuildchainRepo({
|
|
|
417
429
|
} = {}) {
|
|
418
430
|
const resolvedCwd = path.resolve(cwd);
|
|
419
431
|
const manager = detectOrDefaultPackageManager(resolvedCwd, packageManager);
|
|
420
|
-
const toml = (() => {
|
|
432
|
+
const toml = appendNextDevelopmentToml((() => {
|
|
421
433
|
if (type === "package") {
|
|
422
434
|
return packageToml(resolvedCwd, manager);
|
|
423
435
|
}
|
|
@@ -437,7 +449,12 @@ export function initBuildchainRepo({
|
|
|
437
449
|
return anchoredPackageToml(resolvedCwd, manager);
|
|
438
450
|
}
|
|
439
451
|
throw new Error("init --type must be one of package, native, web-surface, infra-contract, publication-artifact, or anchored-package");
|
|
440
|
-
})();
|
|
452
|
+
})());
|
|
453
|
+
|
|
454
|
+
const agentsPath = path.join(resolvedCwd, "AGENTS.md");
|
|
455
|
+
const currentAgents = fs.existsSync(agentsPath)
|
|
456
|
+
? fs.readFileSync(agentsPath, "utf8")
|
|
457
|
+
: "";
|
|
441
458
|
|
|
442
459
|
const written = [
|
|
443
460
|
writeIfAllowed(path.join(resolvedCwd, BUILDCHAIN_CONFIG_PATH), toml, { force }),
|
|
@@ -452,6 +469,7 @@ export function initBuildchainRepo({
|
|
|
452
469
|
}),
|
|
453
470
|
{ force },
|
|
454
471
|
),
|
|
472
|
+
writeManagedAgentEntry(agentsPath, currentAgents),
|
|
455
473
|
];
|
|
456
474
|
|
|
457
475
|
if (type === "anchored-package" && !fs.existsSync(path.join(resolvedCwd, "release.json"))) {
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
import { validateNextDevelopmentController } from "../packages/core/next-development-controller.js";
|
|
9
|
+
import {
|
|
10
|
+
NEXT_DEVELOPMENT_REQUEST_CONTRACT,
|
|
11
|
+
NEXT_DEVELOPMENT_TRANSITION_CONTRACT,
|
|
12
|
+
materializeNextDevelopmentTransition,
|
|
13
|
+
nextDevelopmentRoot,
|
|
14
|
+
} from "../packages/core/next-development-transition.js";
|
|
15
|
+
|
|
16
|
+
const ROOT = /^sha256:[0-9a-f]{64}$/u;
|
|
17
|
+
const SHA = /^[0-9a-f]{40}$/u;
|
|
18
|
+
const REPOSITORY = /^[^/\s]+\/[^/\s]+$/u;
|
|
19
|
+
const ANCHOR_BYTES = '{"upstream":"v22.1.0"}\n';
|
|
20
|
+
|
|
21
|
+
export const RECORDED_AT = "2026-08-11T02:00:00.000Z";
|
|
22
|
+
export const PROTECTED_DEV_BRANCH = "dev/v4/v4.0";
|
|
23
|
+
export const HOSTED_READBACK_PATHS = [
|
|
24
|
+
".buildchain/release-impact.json",
|
|
25
|
+
"dist/site/buildchain-contract.json",
|
|
26
|
+
"package.json",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export function required(value, label) {
|
|
30
|
+
const normalized = String(value || "").trim();
|
|
31
|
+
if (!normalized) throw new Error(`${label} is required`);
|
|
32
|
+
return normalized;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function exactRoot(value, label) {
|
|
36
|
+
const normalized = required(value, label).toLowerCase();
|
|
37
|
+
if (!ROOT.test(normalized)) throw new Error(`${label} must be a sha256 root`);
|
|
38
|
+
return normalized;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function exactSha(value, label) {
|
|
42
|
+
const normalized = required(value, label).toLowerCase();
|
|
43
|
+
if (!SHA.test(normalized)) {
|
|
44
|
+
throw new Error(`${label} must be an exact 40-character Git SHA`);
|
|
45
|
+
}
|
|
46
|
+
return normalized;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function repository(value, label = "repository") {
|
|
50
|
+
const normalized = required(value, label);
|
|
51
|
+
if (!REPOSITORY.test(normalized))
|
|
52
|
+
throw new Error(`${label} must be owner/repo`);
|
|
53
|
+
return normalized;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function fixtureSha(label) {
|
|
57
|
+
return nextDevelopmentRoot({ label }).slice("sha256:".length, 40 + 7);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function completedAlpha({ runtimeSha, contractRoot, model }) {
|
|
61
|
+
const version = model === "anchored" ? "22.0.0-alpha.1" : "4.0.9-alpha.1";
|
|
62
|
+
return {
|
|
63
|
+
outcome: "succeeded",
|
|
64
|
+
version,
|
|
65
|
+
exactTag: `v${version}`,
|
|
66
|
+
releaseSha: runtimeSha,
|
|
67
|
+
treeSha: fixtureSha(`completed-alpha-tree:${model}`),
|
|
68
|
+
publicationRoot: nextDevelopmentRoot({ runtimeSha, contractRoot, model }),
|
|
69
|
+
completedAt: "2026-08-11T01:00:00.000Z",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function adapterCheckout(model, version) {
|
|
74
|
+
const cwd = fs.mkdtempSync(
|
|
75
|
+
path.join(
|
|
76
|
+
os.tmpdir(),
|
|
77
|
+
`buildchain-next-development-self-dogfood-${model}-`,
|
|
78
|
+
),
|
|
79
|
+
);
|
|
80
|
+
fs.mkdirSync(path.join(cwd, ".buildchain"), { recursive: true });
|
|
81
|
+
fs.writeFileSync(
|
|
82
|
+
path.join(cwd, ".buildchain", "buildchain.toml"),
|
|
83
|
+
`schema = 1
|
|
84
|
+
|
|
85
|
+
[version]
|
|
86
|
+
required = true
|
|
87
|
+
strategy = "${model === "anchored" ? "anchored" : "semver"}"
|
|
88
|
+
next = "${model === "anchored" ? "manual" : "auto"}"
|
|
89
|
+
${model === "anchored" ? 'manifest = "release.json"\n' : ""}
|
|
90
|
+
[[version.files]]
|
|
91
|
+
type = "json"
|
|
92
|
+
path = "package.json"
|
|
93
|
+
key = "version"
|
|
94
|
+
`,
|
|
95
|
+
);
|
|
96
|
+
fs.writeFileSync(
|
|
97
|
+
path.join(cwd, "package.json"),
|
|
98
|
+
`${JSON.stringify({ name: "self-dogfood-fixture", version }, null, 2)}\n`,
|
|
99
|
+
);
|
|
100
|
+
if (model === "anchored")
|
|
101
|
+
fs.writeFileSync(path.join(cwd, "release.json"), ANCHOR_BYTES);
|
|
102
|
+
return cwd;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function anchorInput() {
|
|
106
|
+
return {
|
|
107
|
+
targetVersion: "22.1.0",
|
|
108
|
+
anchor: {
|
|
109
|
+
manifestPath: "release.json",
|
|
110
|
+
manifestRoot: `sha256:${crypto.createHash("sha256").update(ANCHOR_BYTES).digest("hex")}`,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function adapterRequest(route, model, reviewedInput) {
|
|
116
|
+
return {
|
|
117
|
+
contract: NEXT_DEVELOPMENT_REQUEST_CONTRACT,
|
|
118
|
+
repository: route.repository,
|
|
119
|
+
completedAlpha: completedAlpha({ ...route, model }),
|
|
120
|
+
recordedAt: RECORDED_AT,
|
|
121
|
+
...(reviewedInput || {}),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function adapterTransition(route, model) {
|
|
126
|
+
const alpha = completedAlpha({ ...route, model });
|
|
127
|
+
const cwd = adapterCheckout(model, alpha.version);
|
|
128
|
+
try {
|
|
129
|
+
return materializeNextDevelopmentTransition({
|
|
130
|
+
cwd,
|
|
131
|
+
request: adapterRequest(route, model),
|
|
132
|
+
});
|
|
133
|
+
} finally {
|
|
134
|
+
fs.rmSync(cwd, { recursive: true, force: true });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function operationBody(record) {
|
|
139
|
+
const body = structuredClone(record);
|
|
140
|
+
delete body.operationRoot;
|
|
141
|
+
return body;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function validateAdapterOperation(record) {
|
|
145
|
+
exactRoot(record?.operationKey, "adapter operation key");
|
|
146
|
+
exactRoot(record?.idempotencyKey, "adapter idempotency key");
|
|
147
|
+
exactRoot(record?.materializationRoot, "adapter materialization root");
|
|
148
|
+
if (
|
|
149
|
+
record?.adapterContract !== NEXT_DEVELOPMENT_TRANSITION_CONTRACT ||
|
|
150
|
+
!["semver", "anchored"].includes(record?.model)
|
|
151
|
+
) {
|
|
152
|
+
throw new Error("real version-state adapter operation contract mismatch");
|
|
153
|
+
}
|
|
154
|
+
if (record.operationRoot !== nextDevelopmentRoot(operationBody(record))) {
|
|
155
|
+
throw new Error("real version-state adapter operation root drifted");
|
|
156
|
+
}
|
|
157
|
+
return structuredClone(record);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function validateSelfDogfoodRoute(route) {
|
|
161
|
+
exactSha(route?.runtimeSha, "route.runtimeSha");
|
|
162
|
+
const targetRepository = repository(route?.repository, "route.repository");
|
|
163
|
+
if (
|
|
164
|
+
targetRepository !== "kungfu-systems/buildchain" ||
|
|
165
|
+
route.runtimeRef !== "v4-alpha" ||
|
|
166
|
+
route.publicWorkflow !==
|
|
167
|
+
`${targetRepository}/.github/workflows/build.yml@v4-alpha` ||
|
|
168
|
+
route.protectedDevBranch !== PROTECTED_DEV_BRANCH ||
|
|
169
|
+
JSON.stringify(route.hostedReadbackPaths) !==
|
|
170
|
+
JSON.stringify(HOSTED_READBACK_PATHS)
|
|
171
|
+
) {
|
|
172
|
+
throw new Error("next-development evidence public route drifted");
|
|
173
|
+
}
|
|
174
|
+
return targetRepository;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function validateRecoveryEvidence(recovery) {
|
|
178
|
+
const sameRunner =
|
|
179
|
+
required(recovery?.faultRunnerId, "faultRunnerId") ===
|
|
180
|
+
required(recovery?.resumeRunnerId, "resumeRunnerId");
|
|
181
|
+
const unchangedDev =
|
|
182
|
+
exactSha(
|
|
183
|
+
recovery?.protectedDevMovement?.initialSha,
|
|
184
|
+
"protected Dev initial SHA",
|
|
185
|
+
) ===
|
|
186
|
+
exactSha(
|
|
187
|
+
recovery?.protectedDevMovement?.movedSha,
|
|
188
|
+
"protected Dev moved SHA",
|
|
189
|
+
);
|
|
190
|
+
if (
|
|
191
|
+
recovery?.freshRunner !== true ||
|
|
192
|
+
sameRunner ||
|
|
193
|
+
recovery?.transientDurableStateFailures !== 1 ||
|
|
194
|
+
recovery?.recoveredAdapterOperations !== 1 ||
|
|
195
|
+
recovery?.alphaCandidateRebuilds !== 0 ||
|
|
196
|
+
recovery?.devMaterializationsBefore !== 1 ||
|
|
197
|
+
recovery?.devMaterializationsAfter !== 2 ||
|
|
198
|
+
recovery?.protectedDevMovement?.supersededAttempts !== 1 ||
|
|
199
|
+
unchangedDev ||
|
|
200
|
+
recovery?.protectedPrDelay?.status !== "pr-pending" ||
|
|
201
|
+
recovery?.protectedPrDelay?.delayedStatus !== "pr-pending" ||
|
|
202
|
+
recovery?.protectedPrDelay?.unchangedControllerRoot !== true
|
|
203
|
+
) {
|
|
204
|
+
throw new Error(
|
|
205
|
+
"fresh-runner failure, Dev movement, or protected-PR delay proof is incomplete",
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function validateAdapterEvidence(adapter, semver, anchored) {
|
|
211
|
+
const operations = adapter?.operations || [];
|
|
212
|
+
operations.forEach(validateAdapterOperation);
|
|
213
|
+
const expectedKeys = [...semver.attempts, ...anchored.attempts]
|
|
214
|
+
.map((entry) => entry.operationKey)
|
|
215
|
+
.sort();
|
|
216
|
+
const observedKeys = operations.map((entry) => entry.operationKey).sort();
|
|
217
|
+
if (
|
|
218
|
+
adapter?.boundary !== "materializeNextDevelopmentTransition" ||
|
|
219
|
+
adapter?.contract !== NEXT_DEVELOPMENT_TRANSITION_CONTRACT ||
|
|
220
|
+
operations.length !== 3 ||
|
|
221
|
+
new Set(operations.map((entry) => entry.model)).size !== 2 ||
|
|
222
|
+
JSON.stringify(observedKeys) !== JSON.stringify(expectedKeys)
|
|
223
|
+
) {
|
|
224
|
+
throw new Error("real version-state adapter boundary proof is incomplete");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export class DurableStore {
|
|
229
|
+
constructor(records = [], { transientFailures = 0 } = {}) {
|
|
230
|
+
this.records = new Map(
|
|
231
|
+
records.map((record) => {
|
|
232
|
+
const current = validateNextDevelopmentController(record);
|
|
233
|
+
return [current.childKey, current];
|
|
234
|
+
}),
|
|
235
|
+
);
|
|
236
|
+
this.transientFailuresRemaining = transientFailures;
|
|
237
|
+
this.transientFailures = 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async readChild(key) {
|
|
241
|
+
return structuredClone(this.records.get(key) || null);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async createChild(key, record) {
|
|
245
|
+
if (!this.records.has(key)) this.records.set(key, structuredClone(record));
|
|
246
|
+
return { record: structuredClone(this.records.get(key)) };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async compareAndSwapChild(key, expectedRoot, record) {
|
|
250
|
+
if (this.transientFailuresRemaining > 0) {
|
|
251
|
+
this.transientFailuresRemaining -= 1;
|
|
252
|
+
this.transientFailures += 1;
|
|
253
|
+
throw new Error("injected transient durable-state write failure");
|
|
254
|
+
}
|
|
255
|
+
const current = this.records.get(key);
|
|
256
|
+
if (!current || current.controllerRoot !== expectedRoot) {
|
|
257
|
+
throw new Error("self-dogfood durable store compare-and-swap failed");
|
|
258
|
+
}
|
|
259
|
+
this.records.set(key, structuredClone(record));
|
|
260
|
+
return { record: structuredClone(record) };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
only() {
|
|
264
|
+
if (this.records.size !== 1) {
|
|
265
|
+
throw new Error("self-dogfood durable store expected exactly one child");
|
|
266
|
+
}
|
|
267
|
+
return structuredClone([...this.records.values()][0]);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export class VersionStateAdapterHarness {
|
|
272
|
+
constructor({
|
|
273
|
+
route,
|
|
274
|
+
operations = [],
|
|
275
|
+
devSha = fixtureSha("protected-dev:initial"),
|
|
276
|
+
movedDevSha = "",
|
|
277
|
+
moveAfterRecoveredOperation = false,
|
|
278
|
+
pullRequestReadsBeforeMerge = 0,
|
|
279
|
+
} = {}) {
|
|
280
|
+
this.operations = new Map(
|
|
281
|
+
operations.map((record) => {
|
|
282
|
+
const current = validateAdapterOperation(record);
|
|
283
|
+
return [current.operationKey, current];
|
|
284
|
+
}),
|
|
285
|
+
);
|
|
286
|
+
this.route = route ? structuredClone(route) : null;
|
|
287
|
+
this.devSha = devSha;
|
|
288
|
+
this.movedDevSha = movedDevSha;
|
|
289
|
+
this.moveAfterRecoveredOperation = moveAfterRecoveredOperation;
|
|
290
|
+
this.pullRequestReadsBeforeMerge = pullRequestReadsBeforeMerge;
|
|
291
|
+
this.materializations = operations.length;
|
|
292
|
+
this.recoveredOperations = 0;
|
|
293
|
+
this.pullRequestReads = 0;
|
|
294
|
+
this.pullRequest = null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async readProtectedDev() {
|
|
298
|
+
return { sha: this.devSha };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async materialize({
|
|
302
|
+
operationKey,
|
|
303
|
+
repository: targetRepository,
|
|
304
|
+
baseDevSha,
|
|
305
|
+
targetVersion,
|
|
306
|
+
anchor,
|
|
307
|
+
}) {
|
|
308
|
+
const existing = this.operations.get(operationKey);
|
|
309
|
+
if (existing) {
|
|
310
|
+
this.recoveredOperations += 1;
|
|
311
|
+
if (this.moveAfterRecoveredOperation && this.movedDevSha) {
|
|
312
|
+
this.devSha = this.movedDevSha;
|
|
313
|
+
this.moveAfterRecoveredOperation = false;
|
|
314
|
+
}
|
|
315
|
+
return structuredClone(existing.result);
|
|
316
|
+
}
|
|
317
|
+
const model = anchor ? "anchored" : "semver";
|
|
318
|
+
const route = this.route || {
|
|
319
|
+
repository: targetRepository,
|
|
320
|
+
runtimeSha:
|
|
321
|
+
model === "anchored"
|
|
322
|
+
? fixtureSha("anchored-runtime")
|
|
323
|
+
: fixtureSha("semver-runtime"),
|
|
324
|
+
contractRoot: nextDevelopmentRoot({ model, targetRepository }),
|
|
325
|
+
};
|
|
326
|
+
const alpha = completedAlpha({ ...route, model });
|
|
327
|
+
const cwd = adapterCheckout(model, alpha.version);
|
|
328
|
+
let materialized;
|
|
329
|
+
try {
|
|
330
|
+
materialized = materializeNextDevelopmentTransition({
|
|
331
|
+
cwd,
|
|
332
|
+
request: adapterRequest(
|
|
333
|
+
route,
|
|
334
|
+
model,
|
|
335
|
+
model === "anchored" ? { targetVersion, anchor } : undefined,
|
|
336
|
+
),
|
|
337
|
+
write: true,
|
|
338
|
+
});
|
|
339
|
+
} finally {
|
|
340
|
+
fs.rmSync(cwd, { recursive: true, force: true });
|
|
341
|
+
}
|
|
342
|
+
if (
|
|
343
|
+
materialized.state.status !== "materialized" ||
|
|
344
|
+
materialized.target.version !== targetVersion
|
|
345
|
+
) {
|
|
346
|
+
throw new Error(
|
|
347
|
+
"real version-state adapter did not materialize the target",
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
const result = {
|
|
351
|
+
baseDevSha,
|
|
352
|
+
commitSha: fixtureSha(
|
|
353
|
+
`prepared:${operationKey}:${materialized.materialization.materializationRoot}`,
|
|
354
|
+
),
|
|
355
|
+
treeSha: fixtureSha(
|
|
356
|
+
`prepared-tree:${operationKey}:${materialized.materialization.materializationRoot}`,
|
|
357
|
+
),
|
|
358
|
+
sourceRoots: materialized.materialization.paths.map((entry) => ({
|
|
359
|
+
path: entry.path,
|
|
360
|
+
beforeRoot: entry.beforeRoot,
|
|
361
|
+
afterRoot: entry.afterRoot,
|
|
362
|
+
version: targetVersion,
|
|
363
|
+
})),
|
|
364
|
+
derivedRoots: [],
|
|
365
|
+
changedPaths: materialized.materialization.paths
|
|
366
|
+
.filter((entry) => entry.changed)
|
|
367
|
+
.map((entry) => entry.path)
|
|
368
|
+
.sort(),
|
|
369
|
+
lifecycleEvidenceRoot: materialized.materialization.materializationRoot,
|
|
370
|
+
};
|
|
371
|
+
const record = {
|
|
372
|
+
operationKey,
|
|
373
|
+
model,
|
|
374
|
+
adapterContract: materialized.contract,
|
|
375
|
+
idempotencyKey: materialized.idempotencyKey,
|
|
376
|
+
materializationRoot: materialized.materialization.materializationRoot,
|
|
377
|
+
result,
|
|
378
|
+
};
|
|
379
|
+
record.operationRoot = nextDevelopmentRoot(record);
|
|
380
|
+
this.operations.set(operationKey, validateAdapterOperation(record));
|
|
381
|
+
this.materializations += 1;
|
|
382
|
+
return structuredClone(result);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async ensurePullRequest({ baseBranch, headSha }) {
|
|
386
|
+
this.pullRequest = {
|
|
387
|
+
number: 91,
|
|
388
|
+
url: "https://github.com/kungfu-systems/buildchain/pull/91",
|
|
389
|
+
baseBranch,
|
|
390
|
+
headSha,
|
|
391
|
+
status: "open",
|
|
392
|
+
evidenceRoot: nextDevelopmentRoot({
|
|
393
|
+
baseBranch,
|
|
394
|
+
headSha,
|
|
395
|
+
pullRequest: 91,
|
|
396
|
+
}),
|
|
397
|
+
};
|
|
398
|
+
return structuredClone(this.pullRequest);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async readPullRequest({ number }) {
|
|
402
|
+
if (!this.pullRequest)
|
|
403
|
+
throw new Error(`self-dogfood pull request ${number} was not restored`);
|
|
404
|
+
this.pullRequestReads += 1;
|
|
405
|
+
if (this.pullRequestReads > this.pullRequestReadsBeforeMerge) {
|
|
406
|
+
this.pullRequest.status = "merged";
|
|
407
|
+
this.devSha = fixtureSha(`protected-merge:${this.pullRequest.headSha}`);
|
|
408
|
+
}
|
|
409
|
+
return structuredClone(this.pullRequest);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async readProtectedVersionState({
|
|
413
|
+
sourcePaths,
|
|
414
|
+
derivedPaths,
|
|
415
|
+
targetVersion,
|
|
416
|
+
preparedCommitSha,
|
|
417
|
+
}) {
|
|
418
|
+
const operation = [...this.operations.values()].find(
|
|
419
|
+
(entry) => entry.result.commitSha === preparedCommitSha,
|
|
420
|
+
);
|
|
421
|
+
if (!operation)
|
|
422
|
+
throw new Error("protected readback has no rooted adapter operation");
|
|
423
|
+
const sourceRoots = Object.fromEntries(
|
|
424
|
+
operation.result.sourceRoots.map((entry) => [
|
|
425
|
+
entry.path,
|
|
426
|
+
entry.afterRoot,
|
|
427
|
+
]),
|
|
428
|
+
);
|
|
429
|
+
const derivedRoots = Object.fromEntries(
|
|
430
|
+
operation.result.derivedRoots.map((entry) => [entry.path, entry.root]),
|
|
431
|
+
);
|
|
432
|
+
const body = {
|
|
433
|
+
branch: PROTECTED_DEV_BRANCH,
|
|
434
|
+
devSha: this.devSha,
|
|
435
|
+
preparedCommitSha,
|
|
436
|
+
targetVersion,
|
|
437
|
+
versionRoots: sourcePaths.map((filePath) => ({
|
|
438
|
+
path: filePath,
|
|
439
|
+
root: sourceRoots[filePath],
|
|
440
|
+
})),
|
|
441
|
+
derivedRoots: derivedPaths.map((filePath) => ({
|
|
442
|
+
path: filePath,
|
|
443
|
+
root: derivedRoots[filePath],
|
|
444
|
+
})),
|
|
445
|
+
};
|
|
446
|
+
return {
|
|
447
|
+
devSha: this.devSha,
|
|
448
|
+
treeSha: fixtureSha(`protected-readback-tree:${preparedCommitSha}`),
|
|
449
|
+
version: targetVersion,
|
|
450
|
+
containsPreparedCommit: true,
|
|
451
|
+
versionRoots: body.versionRoots,
|
|
452
|
+
derivedRoots: body.derivedRoots,
|
|
453
|
+
evidenceRoot: nextDevelopmentRoot(body),
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
operationRecords() {
|
|
458
|
+
return [...this.operations.values()].map((entry) => structuredClone(entry));
|
|
459
|
+
}
|
|
460
|
+
}
|