@kungfu-tech/buildchain 3.0.9-alpha.0 → 3.0.9-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/bin/buildchain.mjs +9 -1
- package/contracts/dev-delivery-authority-v2.schema.json +253 -0
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +16 -0
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +57 -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/next-development-request-v1.schema.json +66 -0
- package/contracts/next-development-transition-v1.schema.json +292 -0
- package/contracts/release-cut-v1.schema.json +1 -0
- package/contracts/release-train-transition-v1.schema.json +1 -0
- package/dist/site/agent-index.json +1 -0
- package/dist/site/artifact-schemas.json +2 -0
- package/dist/site/buildchain-contract.json +91 -32
- package/dist/site/buildchain-site.json +353 -45
- package/dist/site/capability-registry.json +7 -6
- package/dist/site/cli-registry.json +41 -2
- package/dist/site/controller-registry.json +8 -4
- package/dist/site/kfd-claims.json +193 -9
- package/dist/site/kfd-upstream-aggregate.json +9 -9
- package/dist/site/manual-registry.json +26 -10
- package/dist/site/node-api-registry.json +8012 -4574
- package/dist/site/page-registry.json +331 -31
- package/dist/site/public-surface-audit.json +353 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-passport-check-manifest.json +1 -0
- package/dist/site/release-provenance.json +12 -0
- package/dist/site/schemas/dev-delivery-authority-v2.schema.json +442 -0
- package/dist/site/schemas/release-passport-v1.schema.json +3 -0
- package/dist/site/site-manifest.json +22 -14
- package/dist/site/workflow-registry.json +15 -7
- package/docs/MAP.md +5 -2
- package/docs/adopter-delivery-gate.md +106 -0
- package/docs/aws-us-elastic-runner-burst-plane.md +30 -3
- package/docs/cli-reference.md +200 -13
- package/docs/dev-alpha-candidate-patrol.md +8 -0
- package/docs/dev-delivery-qualification-landing-adr.md +137 -0
- package/docs/dev-delivery-warrant.md +164 -27
- package/docs/kfd-support.md +7 -2
- package/docs/next-development-transition.md +119 -0
- package/docs/node-api-reference.md +464 -277
- package/docs/release-governance.md +10 -7
- package/docs/release-passport.md +15 -2
- package/docs/release-train.md +46 -4
- package/docs/reusable-build-surface.md +26 -9
- package/docs/runtime-train-validation.md +6 -0
- package/docs/versioning.md +6 -2
- package/package.json +21 -6
- package/packages/core/README.md +25 -0
- package/packages/core/adopter-delivery-gate.js +576 -0
- package/packages/core/adopter-delivery-json.js +66 -0
- 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-verification-envelope.js +5 -5
- package/packages/core/buildchain-agent-manuals.js +1 -0
- package/packages/core/buildchain-channel-identity.js +118 -0
- package/packages/core/buildchain-compatibility-proof.js +54 -0
- package/packages/core/buildchain-contract.js +7 -0
- package/packages/core/buildchain-delivery-bootstrap.js +240 -0
- package/packages/core/buildchain-delivery-infrastructure.js +164 -0
- package/packages/core/channel-candidate.js +8 -0
- package/packages/core/channel-promotion-baseline.js +52 -0
- package/packages/core/controller-evidence.js +2 -1
- package/packages/core/dev-alpha-candidate-selection.js +10 -2
- package/packages/core/dev-delivery-authority.js +996 -0
- package/packages/core/dev-delivery-native-proof.js +418 -0
- package/packages/core/dev-delivery-warrant-settlement.js +67 -20
- package/packages/core/dev-delivery-warrant.js +141 -48
- package/packages/core/kfd-adopter-category-driver.js +167 -0
- package/packages/core/kfd-adopter-manifest.js +49 -50
- 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 +726 -0
- package/packages/core/next-development-projection.js +288 -0
- package/packages/core/next-development-transition.js +738 -0
- package/packages/core/paper-agent-entry.js +7 -4
- package/packages/core/paper.js +14 -7
- package/packages/core/release-blocker-priority.js +192 -0
- package/packages/core/release-passport-contract.js +10 -6
- package/packages/core/release-passport.js +29 -12
- package/packages/core/release-train.js +220 -0
- package/scripts/aws-macos-jit-controller-core.mjs +239 -4
- package/scripts/aws-macos-jit-controller.mjs +198 -2
- package/scripts/aws-macos-jit-instance-rehydrate.mjs +260 -0
- package/scripts/aws-macos-jit-source-rebind.mjs +235 -14
- package/scripts/aws-macos-jit.mjs +12 -0
- package/scripts/buildchain-channel-router.mjs +18 -2
- package/scripts/buildchain-cli-help.mjs +9 -2
- package/scripts/buildchain-contract-lock.mjs +11 -1
- package/scripts/check-internal-architecture.mjs +1 -0
- package/scripts/check-inventory.mjs +14 -3
- package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
- package/scripts/dev-delivery-authority.mjs +368 -0
- package/scripts/dev-delivery-native-run.mjs +235 -0
- package/scripts/dev-delivery-proof.mjs +64 -2
- package/scripts/dev-delivery-two-phase-resume.mjs +90 -0
- package/scripts/dev-delivery-two-phase.mjs +577 -0
- package/scripts/dev-delivery-warrant.mjs +47 -5
- package/scripts/dev-pr-delivery-warrant.mjs +19 -14
- package/scripts/generate-buildchain-kfd-witnesses.mjs +88 -94
- package/scripts/generate-channel-build-workflow.mjs +82 -31
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-site-bundle.mjs +8 -3
- package/scripts/init-repo.mjs +135 -62
- package/scripts/next-development-self-dogfood-harness.mjs +409 -0
- package/scripts/next-development-self-dogfood.mjs +532 -0
- package/scripts/next-development-transition.mjs +47 -0
- package/scripts/promotion-channel-router.mjs +5 -3
- package/scripts/release-candidate-tail-reseal.mjs +426 -0
- package/scripts/release-train-self-dogfood.mjs +579 -0
- package/scripts/runtime-ref-core.mjs +4 -17
- package/scripts/seal-artifact-signing-requests.mjs +7 -4
- package/scripts/site-capability-metadata.mjs +23 -1
- package/scripts/stable-candidate-qualification.mjs +7 -7
- package/scripts/workflow-call-contract.mjs +1 -1
- package/templates/native-dev-delivery.yml +86 -0
package/scripts/init-repo.mjs
CHANGED
|
@@ -3,21 +3,35 @@ import fs from "node:fs";
|
|
|
3
3
|
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
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
detectPackageManager,
|
|
8
|
+
assertPackageManager,
|
|
9
|
+
} from "../packages/core/package-manager.js";
|
|
7
10
|
import {
|
|
8
11
|
PUBLICATION_REHEARSAL_WORKFLOW_PATH,
|
|
9
12
|
appendPublicationRehearsalToml,
|
|
10
13
|
mergePublicationRehearsalAgentInstructions,
|
|
11
14
|
publicationRehearsalWorkflow,
|
|
12
15
|
} from "../packages/core/publication-rehearsal-projection.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
import {
|
|
17
|
+
appendNextDevelopmentToml,
|
|
18
|
+
mergeNextDevelopmentAgentInstructions,
|
|
19
|
+
nextDevelopmentWorkflowHeader,
|
|
20
|
+
} from "../packages/core/next-development-projection.js";
|
|
21
|
+
|
|
22
|
+
const BUILDCHAIN_WORKFLOW_REF =
|
|
23
|
+
"kungfu-systems/buildchain/.github/workflows/.build.yml@v3";
|
|
24
|
+
const DEFAULT_PUBLICATION_LATEX_IMAGE =
|
|
25
|
+
"ghcr.io/kungfu-systems/build-images/latex-pdf-builder";
|
|
26
|
+
const DEFAULT_PUBLICATION_LATEX_DIGEST =
|
|
27
|
+
"sha256:c20f3809e96836c1c78e97c76939d12f1de3fed0ea9b7c40c43332ec2ea480f8";
|
|
28
|
+
const DEFAULT_PUBLICATION_LATEX_COMMAND =
|
|
29
|
+
"latexmk -pdf -outdir=_build paper/main.tex";
|
|
18
30
|
|
|
19
31
|
function posixPath(value) {
|
|
20
|
-
return String(value || "")
|
|
32
|
+
return String(value || "")
|
|
33
|
+
.split(path.sep)
|
|
34
|
+
.join("/");
|
|
21
35
|
}
|
|
22
36
|
|
|
23
37
|
function repoName(cwd) {
|
|
@@ -282,34 +296,42 @@ command = "make check"
|
|
|
282
296
|
}
|
|
283
297
|
|
|
284
298
|
function infraContractDesiredJson(cwd) {
|
|
285
|
-
return `${JSON.stringify(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
299
|
+
return `${JSON.stringify(
|
|
300
|
+
{
|
|
301
|
+
service: repoName(cwd),
|
|
302
|
+
environment: "staging",
|
|
303
|
+
resources: [
|
|
304
|
+
{
|
|
305
|
+
kind: "static-site",
|
|
306
|
+
name: repoName(cwd),
|
|
307
|
+
desiredState: {
|
|
308
|
+
hostname: "staging.example.com",
|
|
309
|
+
accessControl: "managed-network",
|
|
310
|
+
owner: "platform",
|
|
311
|
+
},
|
|
296
312
|
},
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
313
|
+
],
|
|
314
|
+
},
|
|
315
|
+
null,
|
|
316
|
+
2,
|
|
317
|
+
)}\n`;
|
|
300
318
|
}
|
|
301
319
|
|
|
302
320
|
function infraContractOutputsJson(cwd) {
|
|
303
|
-
return `${JSON.stringify(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
321
|
+
return `${JSON.stringify(
|
|
322
|
+
{
|
|
323
|
+
service: repoName(cwd),
|
|
324
|
+
environment: "staging",
|
|
325
|
+
observedMode: "manual-observed",
|
|
326
|
+
outputs: {
|
|
327
|
+
hostname: "staging.example.com",
|
|
328
|
+
distributionId: "observed-distribution-id",
|
|
329
|
+
bucket: "observed-bucket-name",
|
|
330
|
+
},
|
|
311
331
|
},
|
|
312
|
-
|
|
332
|
+
null,
|
|
333
|
+
2,
|
|
334
|
+
)}\n`;
|
|
313
335
|
}
|
|
314
336
|
|
|
315
337
|
function workflowArtifactPaths(type) {
|
|
@@ -333,7 +355,7 @@ function workflowArtifactPaths(type) {
|
|
|
333
355
|
}
|
|
334
356
|
|
|
335
357
|
function publicationWorkflowYaml() {
|
|
336
|
-
return
|
|
358
|
+
return `${nextDevelopmentWorkflowHeader()}name: Build
|
|
337
359
|
|
|
338
360
|
on:
|
|
339
361
|
workflow_dispatch:
|
|
@@ -365,7 +387,7 @@ jobs:
|
|
|
365
387
|
}
|
|
366
388
|
|
|
367
389
|
function workflowYaml({ type, runnerPreset, artifactName }) {
|
|
368
|
-
return
|
|
390
|
+
return `${nextDevelopmentWorkflowHeader()}name: Build
|
|
369
391
|
|
|
370
392
|
on:
|
|
371
393
|
workflow_dispatch:
|
|
@@ -398,9 +420,17 @@ jobs:
|
|
|
398
420
|
`;
|
|
399
421
|
}
|
|
400
422
|
|
|
423
|
+
function devDeliveryWorkflowYaml() {
|
|
424
|
+
return fs.readFileSync(
|
|
425
|
+
new URL("../templates/native-dev-delivery.yml", import.meta.url),
|
|
426
|
+
"utf8",
|
|
427
|
+
);
|
|
428
|
+
}
|
|
401
429
|
function writeIfAllowed(filePath, content, { force }) {
|
|
402
430
|
if (fs.existsSync(filePath) && !force) {
|
|
403
|
-
throw new Error(
|
|
431
|
+
throw new Error(
|
|
432
|
+
`${posixPath(filePath)} already exists; pass --force to overwrite`,
|
|
433
|
+
);
|
|
404
434
|
}
|
|
405
435
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
406
436
|
fs.writeFileSync(filePath, content);
|
|
@@ -408,7 +438,9 @@ function writeIfAllowed(filePath, content, { force }) {
|
|
|
408
438
|
}
|
|
409
439
|
|
|
410
440
|
function writeManagedAgentEntry(filePath, current) {
|
|
411
|
-
const content =
|
|
441
|
+
const content = mergeNextDevelopmentAgentInstructions(
|
|
442
|
+
mergePublicationRehearsalAgentInstructions(current),
|
|
443
|
+
);
|
|
412
444
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
413
445
|
fs.writeFileSync(filePath, content);
|
|
414
446
|
return filePath;
|
|
@@ -424,27 +456,33 @@ export function initBuildchainRepo({
|
|
|
424
456
|
} = {}) {
|
|
425
457
|
const resolvedCwd = path.resolve(cwd);
|
|
426
458
|
const manager = detectOrDefaultPackageManager(resolvedCwd, packageManager);
|
|
427
|
-
const toml =
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
459
|
+
const toml = appendNextDevelopmentToml(
|
|
460
|
+
appendPublicationRehearsalToml(
|
|
461
|
+
(() => {
|
|
462
|
+
if (type === "package") {
|
|
463
|
+
return packageToml(resolvedCwd, manager);
|
|
464
|
+
}
|
|
465
|
+
if (type === "native") {
|
|
466
|
+
return nativeToml(resolvedCwd);
|
|
467
|
+
}
|
|
468
|
+
if (type === "web-surface") {
|
|
469
|
+
return webSurfaceToml(resolvedCwd);
|
|
470
|
+
}
|
|
471
|
+
if (type === "infra-contract") {
|
|
472
|
+
return infraContractToml(resolvedCwd);
|
|
473
|
+
}
|
|
474
|
+
if (type === "publication-artifact") {
|
|
475
|
+
return publicationArtifactToml(resolvedCwd);
|
|
476
|
+
}
|
|
477
|
+
if (type === "anchored-package") {
|
|
478
|
+
return anchoredPackageToml(resolvedCwd, manager);
|
|
479
|
+
}
|
|
480
|
+
throw new Error(
|
|
481
|
+
"init --type must be one of package, native, web-surface, infra-contract, publication-artifact, or anchored-package",
|
|
482
|
+
);
|
|
483
|
+
})(),
|
|
484
|
+
),
|
|
485
|
+
);
|
|
448
486
|
|
|
449
487
|
const agentsPath = path.join(resolvedCwd, "AGENTS.md");
|
|
450
488
|
const currentAgents = fs.existsSync(agentsPath)
|
|
@@ -452,7 +490,9 @@ export function initBuildchainRepo({
|
|
|
452
490
|
: "";
|
|
453
491
|
|
|
454
492
|
const written = [
|
|
455
|
-
writeIfAllowed(path.join(resolvedCwd, BUILDCHAIN_CONFIG_PATH), toml, {
|
|
493
|
+
writeIfAllowed(path.join(resolvedCwd, BUILDCHAIN_CONFIG_PATH), toml, {
|
|
494
|
+
force,
|
|
495
|
+
}),
|
|
456
496
|
writeIfAllowed(
|
|
457
497
|
path.join(resolvedCwd, ".github", "workflows", "build.yml"),
|
|
458
498
|
type === "publication-artifact"
|
|
@@ -472,14 +512,41 @@ export function initBuildchainRepo({
|
|
|
472
512
|
writeManagedAgentEntry(agentsPath, currentAgents),
|
|
473
513
|
];
|
|
474
514
|
|
|
475
|
-
if (type === "
|
|
476
|
-
written.push(
|
|
515
|
+
if (type === "native") {
|
|
516
|
+
written.push(
|
|
517
|
+
writeIfAllowed(
|
|
518
|
+
path.join(resolvedCwd, ".github", "workflows", "dev-delivery.yml"),
|
|
519
|
+
devDeliveryWorkflowYaml(),
|
|
520
|
+
{ force },
|
|
521
|
+
),
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (
|
|
526
|
+
type === "anchored-package" &&
|
|
527
|
+
!fs.existsSync(path.join(resolvedCwd, "release.json"))
|
|
528
|
+
) {
|
|
529
|
+
written.push(
|
|
530
|
+
writeIfAllowed(
|
|
531
|
+
path.join(resolvedCwd, "release.json"),
|
|
532
|
+
'{\n "upstream": "",\n "version": "0.0.0"\n}\n',
|
|
533
|
+
{ force },
|
|
534
|
+
),
|
|
535
|
+
);
|
|
477
536
|
}
|
|
478
537
|
|
|
479
538
|
if (type === "infra-contract") {
|
|
480
539
|
written.push(
|
|
481
|
-
writeIfAllowed(
|
|
482
|
-
|
|
540
|
+
writeIfAllowed(
|
|
541
|
+
path.join(resolvedCwd, "infra", "desired.json"),
|
|
542
|
+
infraContractDesiredJson(resolvedCwd),
|
|
543
|
+
{ force },
|
|
544
|
+
),
|
|
545
|
+
writeIfAllowed(
|
|
546
|
+
path.join(resolvedCwd, "infra", "outputs.json"),
|
|
547
|
+
infraContractOutputsJson(resolvedCwd),
|
|
548
|
+
{ force },
|
|
549
|
+
),
|
|
483
550
|
);
|
|
484
551
|
}
|
|
485
552
|
|
|
@@ -489,7 +556,9 @@ export function initBuildchainRepo({
|
|
|
489
556
|
cwd: resolvedCwd,
|
|
490
557
|
packageManager: manager,
|
|
491
558
|
workflowRef: BUILDCHAIN_WORKFLOW_REF,
|
|
492
|
-
written: written.map((filePath) =>
|
|
559
|
+
written: written.map((filePath) =>
|
|
560
|
+
posixPath(path.relative(resolvedCwd, filePath)),
|
|
561
|
+
),
|
|
493
562
|
};
|
|
494
563
|
}
|
|
495
564
|
|
|
@@ -501,7 +570,11 @@ function readArg(name, fallback = "") {
|
|
|
501
570
|
return process.argv[index + 1] || "";
|
|
502
571
|
}
|
|
503
572
|
|
|
504
|
-
if (
|
|
573
|
+
if (
|
|
574
|
+
!process.env.BUILDCHAIN_EMBEDDED_ENTRYPOINT &&
|
|
575
|
+
process.argv[1] &&
|
|
576
|
+
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
577
|
+
) {
|
|
505
578
|
try {
|
|
506
579
|
const result = initBuildchainRepo({
|
|
507
580
|
cwd: readArg("cwd", process.cwd()),
|
|
@@ -0,0 +1,409 @@
|
|
|
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/v3/v3.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)) throw new Error(`${label} must be owner/repo`);
|
|
52
|
+
return normalized;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function fixtureSha(label) {
|
|
56
|
+
return nextDevelopmentRoot({ label }).slice("sha256:".length, 40 + 7);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function completedAlpha({ runtimeSha, contractRoot, model }) {
|
|
60
|
+
const version = model === "anchored" ? "22.0.0-alpha.1" : "3.0.9-alpha.1";
|
|
61
|
+
return {
|
|
62
|
+
outcome: "succeeded",
|
|
63
|
+
version,
|
|
64
|
+
exactTag: `v${version}`,
|
|
65
|
+
releaseSha: runtimeSha,
|
|
66
|
+
treeSha: fixtureSha(`completed-alpha-tree:${model}`),
|
|
67
|
+
publicationRoot: nextDevelopmentRoot({ runtimeSha, contractRoot, model }),
|
|
68
|
+
completedAt: "2026-08-11T01:00:00.000Z",
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function adapterCheckout(model, version) {
|
|
73
|
+
const cwd = fs.mkdtempSync(
|
|
74
|
+
path.join(os.tmpdir(), `buildchain-next-development-self-dogfood-${model}-`),
|
|
75
|
+
);
|
|
76
|
+
fs.mkdirSync(path.join(cwd, ".buildchain"), { recursive: true });
|
|
77
|
+
fs.writeFileSync(
|
|
78
|
+
path.join(cwd, ".buildchain", "buildchain.toml"),
|
|
79
|
+
`schema = 1
|
|
80
|
+
|
|
81
|
+
[version]
|
|
82
|
+
required = true
|
|
83
|
+
strategy = "${model === "anchored" ? "anchored" : "semver"}"
|
|
84
|
+
next = "${model === "anchored" ? "manual" : "auto"}"
|
|
85
|
+
${model === "anchored" ? 'manifest = "release.json"\n' : ""}
|
|
86
|
+
[[version.files]]
|
|
87
|
+
type = "json"
|
|
88
|
+
path = "package.json"
|
|
89
|
+
key = "version"
|
|
90
|
+
`,
|
|
91
|
+
);
|
|
92
|
+
fs.writeFileSync(
|
|
93
|
+
path.join(cwd, "package.json"),
|
|
94
|
+
`${JSON.stringify({ name: "self-dogfood-fixture", version }, null, 2)}\n`,
|
|
95
|
+
);
|
|
96
|
+
if (model === "anchored") fs.writeFileSync(path.join(cwd, "release.json"), ANCHOR_BYTES);
|
|
97
|
+
return cwd;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function anchorInput() {
|
|
101
|
+
return {
|
|
102
|
+
targetVersion: "22.1.0",
|
|
103
|
+
anchor: {
|
|
104
|
+
manifestPath: "release.json",
|
|
105
|
+
manifestRoot: `sha256:${crypto.createHash("sha256").update(ANCHOR_BYTES).digest("hex")}`,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function adapterRequest(route, model, reviewedInput) {
|
|
111
|
+
return {
|
|
112
|
+
contract: NEXT_DEVELOPMENT_REQUEST_CONTRACT,
|
|
113
|
+
repository: route.repository,
|
|
114
|
+
completedAlpha: completedAlpha({ ...route, model }),
|
|
115
|
+
recordedAt: RECORDED_AT,
|
|
116
|
+
...(reviewedInput || {}),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function adapterTransition(route, model) {
|
|
121
|
+
const alpha = completedAlpha({ ...route, model });
|
|
122
|
+
const cwd = adapterCheckout(model, alpha.version);
|
|
123
|
+
try {
|
|
124
|
+
return materializeNextDevelopmentTransition({
|
|
125
|
+
cwd,
|
|
126
|
+
request: adapterRequest(route, model),
|
|
127
|
+
});
|
|
128
|
+
} finally {
|
|
129
|
+
fs.rmSync(cwd, { recursive: true, force: true });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function operationBody(record) {
|
|
134
|
+
const body = structuredClone(record);
|
|
135
|
+
delete body.operationRoot;
|
|
136
|
+
return body;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function validateAdapterOperation(record) {
|
|
140
|
+
exactRoot(record?.operationKey, "adapter operation key");
|
|
141
|
+
exactRoot(record?.idempotencyKey, "adapter idempotency key");
|
|
142
|
+
exactRoot(record?.materializationRoot, "adapter materialization root");
|
|
143
|
+
if (
|
|
144
|
+
record?.adapterContract !== NEXT_DEVELOPMENT_TRANSITION_CONTRACT ||
|
|
145
|
+
!["semver", "anchored"].includes(record?.model)
|
|
146
|
+
) {
|
|
147
|
+
throw new Error("real version-state adapter operation contract mismatch");
|
|
148
|
+
}
|
|
149
|
+
if (record.operationRoot !== nextDevelopmentRoot(operationBody(record))) {
|
|
150
|
+
throw new Error("real version-state adapter operation root drifted");
|
|
151
|
+
}
|
|
152
|
+
return structuredClone(record);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function validateSelfDogfoodRoute(route) {
|
|
156
|
+
exactSha(route?.runtimeSha, "route.runtimeSha");
|
|
157
|
+
const targetRepository = repository(route?.repository, "route.repository");
|
|
158
|
+
if (
|
|
159
|
+
targetRepository !== "kungfu-systems/buildchain" ||
|
|
160
|
+
route.runtimeRef !== "v3-alpha" ||
|
|
161
|
+
route.publicWorkflow !==
|
|
162
|
+
`${targetRepository}/.github/workflows/build.yml@v3-alpha` ||
|
|
163
|
+
route.protectedDevBranch !== PROTECTED_DEV_BRANCH ||
|
|
164
|
+
JSON.stringify(route.hostedReadbackPaths) !==
|
|
165
|
+
JSON.stringify(HOSTED_READBACK_PATHS)
|
|
166
|
+
) {
|
|
167
|
+
throw new Error("next-development evidence public route drifted");
|
|
168
|
+
}
|
|
169
|
+
return targetRepository;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function validateRecoveryEvidence(recovery) {
|
|
173
|
+
const sameRunner =
|
|
174
|
+
required(recovery?.faultRunnerId, "faultRunnerId") ===
|
|
175
|
+
required(recovery?.resumeRunnerId, "resumeRunnerId");
|
|
176
|
+
const unchangedDev =
|
|
177
|
+
exactSha(recovery?.protectedDevMovement?.initialSha, "protected Dev initial SHA") ===
|
|
178
|
+
exactSha(recovery?.protectedDevMovement?.movedSha, "protected Dev moved SHA");
|
|
179
|
+
if (
|
|
180
|
+
recovery?.freshRunner !== true ||
|
|
181
|
+
sameRunner ||
|
|
182
|
+
recovery?.transientDurableStateFailures !== 1 ||
|
|
183
|
+
recovery?.recoveredAdapterOperations !== 1 ||
|
|
184
|
+
recovery?.alphaCandidateRebuilds !== 0 ||
|
|
185
|
+
recovery?.devMaterializationsBefore !== 1 ||
|
|
186
|
+
recovery?.devMaterializationsAfter !== 2 ||
|
|
187
|
+
recovery?.protectedDevMovement?.supersededAttempts !== 1 ||
|
|
188
|
+
unchangedDev ||
|
|
189
|
+
recovery?.protectedPrDelay?.status !== "pr-pending" ||
|
|
190
|
+
recovery?.protectedPrDelay?.delayedStatus !== "pr-pending" ||
|
|
191
|
+
recovery?.protectedPrDelay?.unchangedControllerRoot !== true
|
|
192
|
+
) {
|
|
193
|
+
throw new Error("fresh-runner failure, Dev movement, or protected-PR delay proof is incomplete");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function validateAdapterEvidence(adapter, semver, anchored) {
|
|
198
|
+
const operations = adapter?.operations || [];
|
|
199
|
+
operations.forEach(validateAdapterOperation);
|
|
200
|
+
const expectedKeys = [...semver.attempts, ...anchored.attempts]
|
|
201
|
+
.map((entry) => entry.operationKey)
|
|
202
|
+
.sort();
|
|
203
|
+
const observedKeys = operations.map((entry) => entry.operationKey).sort();
|
|
204
|
+
if (
|
|
205
|
+
adapter?.boundary !== "materializeNextDevelopmentTransition" ||
|
|
206
|
+
adapter?.contract !== NEXT_DEVELOPMENT_TRANSITION_CONTRACT ||
|
|
207
|
+
operations.length !== 3 ||
|
|
208
|
+
new Set(operations.map((entry) => entry.model)).size !== 2 ||
|
|
209
|
+
JSON.stringify(observedKeys) !== JSON.stringify(expectedKeys)
|
|
210
|
+
) {
|
|
211
|
+
throw new Error("real version-state adapter boundary proof is incomplete");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export class DurableStore {
|
|
216
|
+
constructor(records = [], { transientFailures = 0 } = {}) {
|
|
217
|
+
this.records = new Map(
|
|
218
|
+
records.map((record) => {
|
|
219
|
+
const current = validateNextDevelopmentController(record);
|
|
220
|
+
return [current.childKey, current];
|
|
221
|
+
}),
|
|
222
|
+
);
|
|
223
|
+
this.transientFailuresRemaining = transientFailures;
|
|
224
|
+
this.transientFailures = 0;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async readChild(key) {
|
|
228
|
+
return structuredClone(this.records.get(key) || null);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async createChild(key, record) {
|
|
232
|
+
if (!this.records.has(key)) this.records.set(key, structuredClone(record));
|
|
233
|
+
return { record: structuredClone(this.records.get(key)) };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async compareAndSwapChild(key, expectedRoot, record) {
|
|
237
|
+
if (this.transientFailuresRemaining > 0) {
|
|
238
|
+
this.transientFailuresRemaining -= 1;
|
|
239
|
+
this.transientFailures += 1;
|
|
240
|
+
throw new Error("injected transient durable-state write failure");
|
|
241
|
+
}
|
|
242
|
+
const current = this.records.get(key);
|
|
243
|
+
if (!current || current.controllerRoot !== expectedRoot) {
|
|
244
|
+
throw new Error("self-dogfood durable store compare-and-swap failed");
|
|
245
|
+
}
|
|
246
|
+
this.records.set(key, structuredClone(record));
|
|
247
|
+
return { record: structuredClone(record) };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
only() {
|
|
251
|
+
if (this.records.size !== 1) {
|
|
252
|
+
throw new Error("self-dogfood durable store expected exactly one child");
|
|
253
|
+
}
|
|
254
|
+
return structuredClone([...this.records.values()][0]);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export class VersionStateAdapterHarness {
|
|
259
|
+
constructor({
|
|
260
|
+
route,
|
|
261
|
+
operations = [],
|
|
262
|
+
devSha = fixtureSha("protected-dev:initial"),
|
|
263
|
+
movedDevSha = "",
|
|
264
|
+
moveAfterRecoveredOperation = false,
|
|
265
|
+
pullRequestReadsBeforeMerge = 0,
|
|
266
|
+
} = {}) {
|
|
267
|
+
this.operations = new Map(
|
|
268
|
+
operations.map((record) => {
|
|
269
|
+
const current = validateAdapterOperation(record);
|
|
270
|
+
return [current.operationKey, current];
|
|
271
|
+
}),
|
|
272
|
+
);
|
|
273
|
+
this.route = route ? structuredClone(route) : null;
|
|
274
|
+
this.devSha = devSha;
|
|
275
|
+
this.movedDevSha = movedDevSha;
|
|
276
|
+
this.moveAfterRecoveredOperation = moveAfterRecoveredOperation;
|
|
277
|
+
this.pullRequestReadsBeforeMerge = pullRequestReadsBeforeMerge;
|
|
278
|
+
this.materializations = operations.length;
|
|
279
|
+
this.recoveredOperations = 0;
|
|
280
|
+
this.pullRequestReads = 0;
|
|
281
|
+
this.pullRequest = null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async readProtectedDev() {
|
|
285
|
+
return { sha: this.devSha };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async materialize({ operationKey, repository: targetRepository, baseDevSha, targetVersion, anchor }) {
|
|
289
|
+
const existing = this.operations.get(operationKey);
|
|
290
|
+
if (existing) {
|
|
291
|
+
this.recoveredOperations += 1;
|
|
292
|
+
if (this.moveAfterRecoveredOperation && this.movedDevSha) {
|
|
293
|
+
this.devSha = this.movedDevSha;
|
|
294
|
+
this.moveAfterRecoveredOperation = false;
|
|
295
|
+
}
|
|
296
|
+
return structuredClone(existing.result);
|
|
297
|
+
}
|
|
298
|
+
const model = anchor ? "anchored" : "semver";
|
|
299
|
+
const route = this.route || {
|
|
300
|
+
repository: targetRepository,
|
|
301
|
+
runtimeSha: model === "anchored" ? fixtureSha("anchored-runtime") : fixtureSha("semver-runtime"),
|
|
302
|
+
contractRoot: nextDevelopmentRoot({ model, targetRepository }),
|
|
303
|
+
};
|
|
304
|
+
const alpha = completedAlpha({ ...route, model });
|
|
305
|
+
const cwd = adapterCheckout(model, alpha.version);
|
|
306
|
+
let materialized;
|
|
307
|
+
try {
|
|
308
|
+
materialized = materializeNextDevelopmentTransition({
|
|
309
|
+
cwd,
|
|
310
|
+
request: adapterRequest(
|
|
311
|
+
route,
|
|
312
|
+
model,
|
|
313
|
+
model === "anchored" ? { targetVersion, anchor } : undefined,
|
|
314
|
+
),
|
|
315
|
+
write: true,
|
|
316
|
+
});
|
|
317
|
+
} finally {
|
|
318
|
+
fs.rmSync(cwd, { recursive: true, force: true });
|
|
319
|
+
}
|
|
320
|
+
if (materialized.state.status !== "materialized" || materialized.target.version !== targetVersion) {
|
|
321
|
+
throw new Error("real version-state adapter did not materialize the target");
|
|
322
|
+
}
|
|
323
|
+
const result = {
|
|
324
|
+
baseDevSha,
|
|
325
|
+
commitSha: fixtureSha(`prepared:${operationKey}:${materialized.materialization.materializationRoot}`),
|
|
326
|
+
treeSha: fixtureSha(`prepared-tree:${operationKey}:${materialized.materialization.materializationRoot}`),
|
|
327
|
+
sourceRoots: materialized.materialization.paths.map((entry) => ({
|
|
328
|
+
path: entry.path,
|
|
329
|
+
beforeRoot: entry.beforeRoot,
|
|
330
|
+
afterRoot: entry.afterRoot,
|
|
331
|
+
version: targetVersion,
|
|
332
|
+
})),
|
|
333
|
+
derivedRoots: [],
|
|
334
|
+
changedPaths: materialized.materialization.paths
|
|
335
|
+
.filter((entry) => entry.changed)
|
|
336
|
+
.map((entry) => entry.path)
|
|
337
|
+
.sort(),
|
|
338
|
+
lifecycleEvidenceRoot: materialized.materialization.materializationRoot,
|
|
339
|
+
};
|
|
340
|
+
const record = {
|
|
341
|
+
operationKey,
|
|
342
|
+
model,
|
|
343
|
+
adapterContract: materialized.contract,
|
|
344
|
+
idempotencyKey: materialized.idempotencyKey,
|
|
345
|
+
materializationRoot: materialized.materialization.materializationRoot,
|
|
346
|
+
result,
|
|
347
|
+
};
|
|
348
|
+
record.operationRoot = nextDevelopmentRoot(record);
|
|
349
|
+
this.operations.set(operationKey, validateAdapterOperation(record));
|
|
350
|
+
this.materializations += 1;
|
|
351
|
+
return structuredClone(result);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async ensurePullRequest({ baseBranch, headSha }) {
|
|
355
|
+
this.pullRequest = {
|
|
356
|
+
number: 91,
|
|
357
|
+
url: "https://github.com/kungfu-systems/buildchain/pull/91",
|
|
358
|
+
baseBranch,
|
|
359
|
+
headSha,
|
|
360
|
+
status: "open",
|
|
361
|
+
evidenceRoot: nextDevelopmentRoot({ baseBranch, headSha, pullRequest: 91 }),
|
|
362
|
+
};
|
|
363
|
+
return structuredClone(this.pullRequest);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async readPullRequest({ number }) {
|
|
367
|
+
if (!this.pullRequest) throw new Error(`self-dogfood pull request ${number} was not restored`);
|
|
368
|
+
this.pullRequestReads += 1;
|
|
369
|
+
if (this.pullRequestReads > this.pullRequestReadsBeforeMerge) {
|
|
370
|
+
this.pullRequest.status = "merged";
|
|
371
|
+
this.devSha = fixtureSha(`protected-merge:${this.pullRequest.headSha}`);
|
|
372
|
+
}
|
|
373
|
+
return structuredClone(this.pullRequest);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async readProtectedVersionState({ sourcePaths, derivedPaths, targetVersion, preparedCommitSha }) {
|
|
377
|
+
const operation = [...this.operations.values()].find(
|
|
378
|
+
(entry) => entry.result.commitSha === preparedCommitSha,
|
|
379
|
+
);
|
|
380
|
+
if (!operation) throw new Error("protected readback has no rooted adapter operation");
|
|
381
|
+
const sourceRoots = Object.fromEntries(
|
|
382
|
+
operation.result.sourceRoots.map((entry) => [entry.path, entry.afterRoot]),
|
|
383
|
+
);
|
|
384
|
+
const derivedRoots = Object.fromEntries(
|
|
385
|
+
operation.result.derivedRoots.map((entry) => [entry.path, entry.root]),
|
|
386
|
+
);
|
|
387
|
+
const body = {
|
|
388
|
+
branch: PROTECTED_DEV_BRANCH,
|
|
389
|
+
devSha: this.devSha,
|
|
390
|
+
preparedCommitSha,
|
|
391
|
+
targetVersion,
|
|
392
|
+
versionRoots: sourcePaths.map((filePath) => ({ path: filePath, root: sourceRoots[filePath] })),
|
|
393
|
+
derivedRoots: derivedPaths.map((filePath) => ({ path: filePath, root: derivedRoots[filePath] })),
|
|
394
|
+
};
|
|
395
|
+
return {
|
|
396
|
+
devSha: this.devSha,
|
|
397
|
+
treeSha: fixtureSha(`protected-readback-tree:${preparedCommitSha}`),
|
|
398
|
+
version: targetVersion,
|
|
399
|
+
containsPreparedCommit: true,
|
|
400
|
+
versionRoots: body.versionRoots,
|
|
401
|
+
derivedRoots: body.derivedRoots,
|
|
402
|
+
evidenceRoot: nextDevelopmentRoot(body),
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
operationRecords() {
|
|
407
|
+
return [...this.operations.values()].map((entry) => structuredClone(entry));
|
|
408
|
+
}
|
|
409
|
+
}
|