@kungfu-tech/buildchain 4.0.1-alpha.5 → 4.0.1-alpha.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/actions/release-tail/README.md +8 -0
- package/actions/v4-release-candidate-promote/README.md +3 -0
- package/architecture/agent-change-map.md +1015 -0
- package/architecture/ci-lane-change-budget.json +256 -0
- package/architecture/decisions/0004-v4-declarative-release-tail.md +47 -0
- package/architecture/internal-capabilities.json +220 -15
- package/architecture/maintainability-debt.json +3229 -0
- package/architecture/maintainability-policy.json +489 -132
- package/architecture/release-tail-contract-inventory.json +5 -2
- package/architecture/v3-core-mechanism-inventory.json +4 -3
- package/architecture/v3-v4-capability-closure.md +59 -0
- package/architecture/v3-v4-live-capability-inventory.json +102404 -0
- package/architecture/v4-adopter-delivery-parity.json +32 -5
- package/architecture/v4-cross-platform-adopter-qualification.json +52 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +3 -3
- package/architecture/v4-floating-consumer-policy.json +6 -0
- package/architecture/v4-publication-rehearsal-parity.json +84 -0
- package/architecture/v4-runtime-semantic-closure.json +710 -0
- package/bin/buildchain.mjs +7 -1
- package/bin/internal/adopter-delivery-cli.mjs +70 -0
- package/bin/internal/command-registry.mjs +1 -0
- package/bin/internal/trust-release-release-handlers.mjs +5 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
- package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
- package/contracts/v4-publication-qualification-v1.schema.json +51 -0
- package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/agent-index.json +4 -0
- package/dist/site/artifact-schemas.json +8 -0
- package/dist/site/buildchain-contract.json +125 -34
- package/dist/site/buildchain-site.json +308 -33
- package/dist/site/capability-registry.json +13 -12
- package/dist/site/cli-registry.json +57 -2
- package/dist/site/controller-registry.json +41 -5
- package/dist/site/kfd-claims.json +438 -24
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +22 -7
- package/dist/site/node-api-registry.json +12843 -9314
- package/dist/site/page-registry.json +288 -21
- package/dist/site/public-surface-audit.json +281 -21
- package/dist/site/publication-authority-registry.json +66 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +20 -0
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
- package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
- package/dist/site/schemas/v4-publication-qualification-v1.schema.json +51 -0
- package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/site-manifest.json +19 -11
- package/dist/site/workflow-registry.json +195 -17
- package/docs/MAP.md +4 -2
- package/docs/cli-reference.md +70 -2
- package/docs/cli.md +8 -0
- package/docs/dev-delivery-warrant.md +34 -10
- package/docs/node-api-reference.md +524 -279
- package/docs/publish-transaction.md +11 -4
- package/docs/release-candidate.md +7 -0
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/v4-adopter-delivery.md +152 -0
- package/docs/v4-declarative-release-tail-migration.md +43 -0
- package/docs/v4-publication-rehearsal.md +130 -0
- package/package.json +24 -4
- package/packages/core/adopter-delivery-passport.js +295 -0
- package/packages/core/adopter-delivery-vectors.js +158 -0
- package/packages/core/adopter-delivery-vectors.json +77 -0
- package/packages/core/buildchain-agent-manuals.js +1 -0
- package/packages/core/buildchain-config.js +84 -0
- package/packages/core/buildchain-contract.js +31 -1
- package/packages/core/buildchain-delivery-bootstrap.js +240 -0
- package/packages/core/buildchain-delivery-infrastructure.js +164 -0
- package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
- package/packages/core/buildchain-publication-authority.js +2 -0
- package/packages/core/ci-lane-change-budget.js +247 -0
- package/packages/core/dev-delivery-native-proof.js +43 -27
- package/packages/core/dev-delivery-warrant-settlement.js +239 -3
- package/packages/core/dev-delivery-warrant-state.js +46 -10
- package/packages/core/dev-delivery-warrant.js +29 -7
- package/packages/core/github-governance-authority.js +5 -4
- package/packages/core/index.js +6 -0
- package/packages/core/legacy-kfd-adopter-driver.js +167 -0
- package/packages/core/paper-scaffold-content.js +401 -0
- package/packages/core/paper.js +12 -378
- package/packages/core/publication-control-plane-audit.js +135 -111
- package/packages/core/publication-rehearsal-projection.js +104 -0
- package/packages/core/publication-rehearsal-runtime.js +190 -0
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-candidate-recovery.js +71 -15
- package/packages/core/release-candidate.js +2 -2
- package/packages/core/release-passport.js +95 -170
- package/packages/core/v4-adopter-delivery-parity.js +3 -3
- package/packages/core/v4-adopter-delivery.js +359 -0
- package/packages/core/v4-cross-platform-adopter-qualification.js +343 -0
- package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
- package/packages/core/v4-floating-consumer-evidence.js +18 -4
- package/packages/core/v4-floating-consumer-policy.js +139 -35
- package/packages/core/v4-floating-consumer-release-passport.js +154 -8
- package/packages/core/v4-publication-qualification.js +222 -0
- package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
- package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
- package/packages/core/v4-publication-rehearsal.js +481 -0
- package/packages/core/workflow-yaml-contract.js +24 -2
- package/scripts/artifact-signing-controller-core.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +17 -17
- package/scripts/buildchain-cli-help.mjs +13 -0
- package/scripts/check-ci-lane-change-budget.mjs +100 -0
- package/scripts/check-internal-architecture.mjs +23 -3
- package/scripts/check-inventory.mjs +24 -6
- package/scripts/check-maintainability.mjs +145 -4
- package/scripts/check-project-cut-merge-queue-admission.mjs +87 -0
- package/scripts/check-v3-v4-capability-inventory.mjs +574 -0
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +30 -4
- package/scripts/check-v4-runtime-semantic-closure.mjs +266 -0
- package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
- package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
- package/scripts/dev-delivery-warrant-options.mjs +27 -11
- package/scripts/dev-delivery-warrant-store.mjs +10 -6
- package/scripts/dev-delivery-warrant.mjs +50 -6
- package/scripts/dev-pr-auto-merge.mjs +25 -11
- package/scripts/dev-pr-delivery-warrant.mjs +45 -0
- package/scripts/dev-pr-prequeue-guard.mjs +0 -18
- package/scripts/dev-qualification-patrol.mjs +9 -9
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/generate-agent-change-map.mjs +118 -0
- package/scripts/generate-channel-promotion-workflow.mjs +2 -2
- package/scripts/generate-release-candidate-passport.mjs +177 -0
- package/scripts/generate-site-bundle.mjs +36 -0
- package/scripts/maintainability-domain-metrics.mjs +237 -0
- package/scripts/maintainability-governance.mjs +473 -0
- package/scripts/maintainability-metrics.mjs +28 -6
- package/scripts/project-cut-merge-queue-admission.mjs +394 -0
- package/scripts/promotion-channel-router.mjs +4 -4
- package/scripts/promotion-routing-evidence.mjs +35 -6
- package/scripts/release-candidate-anchor-provenance.mjs +99 -0
- package/scripts/release-candidate-resolver.mjs +44 -0
- package/scripts/release-line-policy.mjs +25 -15
- package/scripts/release-tail.mjs +68 -3
- package/scripts/resume-from-candidate-run.mjs +595 -65
- package/scripts/run-lifecycle-core.mjs +341 -376
- package/scripts/site-capability-metadata.mjs +21 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v3-v4-capability-catalog.mjs +439 -0
- package/scripts/v4-consumer-policy.mjs +4 -0
- package/scripts/v4-cross-platform-adopter-qualification.mjs +228 -0
- package/scripts/v4-declarative-promotion-admission.mjs +42 -0
- package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
|
@@ -5,11 +5,183 @@ import { execFileSync } from "node:child_process";
|
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import { createReleaseCandidatePassport, validateReleaseCandidatePassport } from "../packages/core/release-candidate.js";
|
|
7
7
|
import { scanV4FloatingConsumerPolicy, v4ConsumerPolicyScannerRoot } from "../packages/core/v4-floating-consumer-policy.js";
|
|
8
|
+
import { v4ContentRoot } from "../packages/core/v4-canonical-contracts.js";
|
|
9
|
+
import { V4_STAGE_CAPSULE_CONTRACT, V4_STAGE_CAPSULE_IDENTITY_CONTRACT, v4StageCapsuleIdentityRoot, v4StageCapsuleRoot, validateV4StageCapsule } from "../packages/core/v4-stage-capsule.js";
|
|
10
|
+
import { v4RuntimeResumeDocumentRoot } from "../packages/core/v4-runtime-ref-resume-authority.js";
|
|
11
|
+
import { createV4PublicationQualificationReceipt } from "../packages/core/v4-publication-qualification.js";
|
|
8
12
|
import { writeGitHubOutputs } from "./build-contract-core.mjs";
|
|
9
13
|
|
|
10
14
|
const env = (name, fallback = "") => process.env[name] || fallback;
|
|
11
15
|
const readJsonFile = (filePath) => JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
12
16
|
|
|
17
|
+
export function createReleaseCandidateStageCapsules({
|
|
18
|
+
passport,
|
|
19
|
+
buildSummary,
|
|
20
|
+
coordinates,
|
|
21
|
+
}) {
|
|
22
|
+
if (!passport.consumerPolicy?.receiptRoot) return undefined;
|
|
23
|
+
if (
|
|
24
|
+
coordinates.repository !== passport.repository ||
|
|
25
|
+
String(coordinates.runId) !== String(passport.workflow.runId) ||
|
|
26
|
+
String(coordinates.runAttempt) !== String(passport.workflow.runAttempt) ||
|
|
27
|
+
coordinates.sourceSha !== passport.source.headSha
|
|
28
|
+
) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
"Stage Capsule coordinate set does not bind the candidate run",
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
const coordinateByPlatform = new Map(
|
|
34
|
+
coordinates.artifacts.map((entry) => [entry.platformId, entry]),
|
|
35
|
+
);
|
|
36
|
+
const qualificationArtifacts = passport.platformMatrix.map((platform) => {
|
|
37
|
+
const coordinate = coordinateByPlatform.get(platform.platformId);
|
|
38
|
+
if (!coordinate || coordinate.name !== platform.artifactName) throw new Error(`Stage Capsule coordinate missing for ${platform.platformId}`);
|
|
39
|
+
const manifest = readJsonFile(platform.manifestPath);
|
|
40
|
+
return { role: platform.artifactName, platform: platform.platformId, artifactRoot: coordinate.digest, manifestRoot: v4ContentRoot("stage-capsule-artifact-manifest", manifest) };
|
|
41
|
+
});
|
|
42
|
+
const qualificationExpiresAt = coordinates.artifacts.map((entry) => entry.expiresAt).sort((left, right) => Date.parse(left) - Date.parse(right))[0];
|
|
43
|
+
const qualificationReceipt = createV4PublicationQualificationReceipt({
|
|
44
|
+
repository: passport.repository, candidateRoot: `sha256:${passport.candidateHash}`,
|
|
45
|
+
sourceSha: passport.source.headSha, sourceRoot: v4ContentRoot("candidate-identity", passport.source),
|
|
46
|
+
policyDigest: passport.consumerPolicy.receiptRoot, artifacts: qualificationArtifacts,
|
|
47
|
+
issuedAt: passport.createdAt || new Date(Date.parse(qualificationExpiresAt) - 86_400_000).toISOString(), expiresAt: qualificationExpiresAt,
|
|
48
|
+
});
|
|
49
|
+
const qualificationRoot = qualificationReceipt.receiptRoot;
|
|
50
|
+
const entries = passport.platformMatrix
|
|
51
|
+
.map((platform) => {
|
|
52
|
+
const coordinate = coordinateByPlatform.get(platform.platformId);
|
|
53
|
+
if (!coordinate || coordinate.name !== platform.artifactName) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Stage Capsule coordinate missing for ${platform.platformId}`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
const manifest = readJsonFile(platform.manifestPath);
|
|
59
|
+
const identity = {
|
|
60
|
+
schema: V4_STAGE_CAPSULE_IDENTITY_CONTRACT,
|
|
61
|
+
sourceRoot: v4ContentRoot("candidate-identity", passport.source),
|
|
62
|
+
platform: platform.platformId,
|
|
63
|
+
platformRoot: v4ContentRoot("candidate-identity", manifest.platform),
|
|
64
|
+
stage: "verify",
|
|
65
|
+
toolchainRoots: [],
|
|
66
|
+
runtimeRoot: v4ContentRoot("candidate-identity", {
|
|
67
|
+
sha: passport.buildchain.sha,
|
|
68
|
+
}),
|
|
69
|
+
policyRoot: passport.consumerPolicy.receiptRoot,
|
|
70
|
+
declaredInputs: [
|
|
71
|
+
{
|
|
72
|
+
name: "release-candidate",
|
|
73
|
+
root: `sha256:${passport.candidateHash}`,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
transformationRoot: v4ContentRoot("candidate-identity", {
|
|
77
|
+
lifecycle: manifest.lifecycle,
|
|
78
|
+
summary: platform.summary,
|
|
79
|
+
}),
|
|
80
|
+
outputManifestRoot: v4ContentRoot(
|
|
81
|
+
"stage-capsule-artifact-manifest",
|
|
82
|
+
{ artifact: coordinate, manifest },
|
|
83
|
+
),
|
|
84
|
+
qualificationRoot,
|
|
85
|
+
observationRoots: [
|
|
86
|
+
{
|
|
87
|
+
name: "provider-coordinate",
|
|
88
|
+
root: v4ContentRoot("provider-readback-sample", coordinate),
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
const capsule = {
|
|
93
|
+
schema: V4_STAGE_CAPSULE_CONTRACT,
|
|
94
|
+
writerAuthority: "typescript-v3",
|
|
95
|
+
rustAuthority: "validation-only",
|
|
96
|
+
identity,
|
|
97
|
+
identityRoot: v4StageCapsuleIdentityRoot(identity),
|
|
98
|
+
retentionPromise: {
|
|
99
|
+
class: "github-artifact",
|
|
100
|
+
retainUntil: coordinate.expiresAt,
|
|
101
|
+
},
|
|
102
|
+
capsuleRoot: `sha256:${"0".repeat(64)}`,
|
|
103
|
+
};
|
|
104
|
+
capsule.capsuleRoot = v4StageCapsuleRoot(capsule);
|
|
105
|
+
validateV4StageCapsule(capsule);
|
|
106
|
+
return {
|
|
107
|
+
platform: platform.platformId,
|
|
108
|
+
artifactName: coordinate.name,
|
|
109
|
+
artifactDigest: coordinate.digest,
|
|
110
|
+
publicationArtifact: qualificationArtifacts.find(
|
|
111
|
+
({ platform: platformId }) => platformId === platform.platformId,
|
|
112
|
+
),
|
|
113
|
+
artifact: coordinate,
|
|
114
|
+
capsule,
|
|
115
|
+
};
|
|
116
|
+
})
|
|
117
|
+
.sort((left, right) => left.platform.localeCompare(right.platform));
|
|
118
|
+
const body = {
|
|
119
|
+
schemaVersion: 1,
|
|
120
|
+
contract: "kungfu-buildchain-v4-release-candidate-stage-capsules/v1",
|
|
121
|
+
status: "sealed",
|
|
122
|
+
repository: passport.repository,
|
|
123
|
+
buildAttempt: {
|
|
124
|
+
id: `github-run:${passport.workflow.runId}:attempt:${passport.workflow.runAttempt}`,
|
|
125
|
+
runtimeSha: passport.buildchain.sha,
|
|
126
|
+
},
|
|
127
|
+
source: {
|
|
128
|
+
sha: passport.source.headSha,
|
|
129
|
+
treeSha: passport.source.treeHash,
|
|
130
|
+
},
|
|
131
|
+
consumerPolicyReceiptRoot: passport.consumerPolicy.receiptRoot,
|
|
132
|
+
publicationQualificationRoot: qualificationRoot,
|
|
133
|
+
publicationQualificationReceipt: qualificationReceipt,
|
|
134
|
+
capsules: entries,
|
|
135
|
+
};
|
|
136
|
+
return { ...body, root: v4RuntimeResumeDocumentRoot(body) };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function writeReleaseCandidateStageCapsules({
|
|
140
|
+
passport,
|
|
141
|
+
buildSummary,
|
|
142
|
+
outputPath,
|
|
143
|
+
}) {
|
|
144
|
+
const coordinatesPath = path.resolve(
|
|
145
|
+
env(
|
|
146
|
+
"BUILDCHAIN_ARTIFACT_COORDINATES_PATH",
|
|
147
|
+
".buildchain/artifacts/artifact-coordinates.json",
|
|
148
|
+
),
|
|
149
|
+
);
|
|
150
|
+
if (!fs.existsSync(coordinatesPath)) {
|
|
151
|
+
if (passport.consumerPolicy?.receiptRoot) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
"v4 release candidate Stage Capsules require exact artifact coordinates",
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return { path: "", root: "" };
|
|
157
|
+
}
|
|
158
|
+
const stageCapsules = createReleaseCandidateStageCapsules({
|
|
159
|
+
passport,
|
|
160
|
+
buildSummary,
|
|
161
|
+
coordinates: readJsonFile(coordinatesPath),
|
|
162
|
+
});
|
|
163
|
+
if (!stageCapsules) return { path: "", root: "" };
|
|
164
|
+
const stageCapsulesPath = path.join(
|
|
165
|
+
path.dirname(outputPath),
|
|
166
|
+
"release-candidate-stage-capsules.json",
|
|
167
|
+
);
|
|
168
|
+
fs.writeFileSync(
|
|
169
|
+
stageCapsulesPath,
|
|
170
|
+
`${JSON.stringify(stageCapsules, null, 2)}\n`,
|
|
171
|
+
);
|
|
172
|
+
const qualificationPath = path.join(path.dirname(outputPath), "release-candidate-publication-qualification.json");
|
|
173
|
+
fs.writeFileSync(qualificationPath, `${JSON.stringify(stageCapsules.publicationQualificationReceipt, null, 2)}\n`);
|
|
174
|
+
return {
|
|
175
|
+
path: path
|
|
176
|
+
.relative(process.cwd(), stageCapsulesPath)
|
|
177
|
+
.split(path.sep)
|
|
178
|
+
.join("/"),
|
|
179
|
+
root: stageCapsules.root,
|
|
180
|
+
qualificationPath: path.relative(process.cwd(), qualificationPath).split(path.sep).join("/"),
|
|
181
|
+
qualificationRoot: stageCapsules.publicationQualificationRoot,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
13
185
|
export function resolveLegacyConsumerPolicyReceipt(options = {}) {
|
|
14
186
|
if (options.json) return { receipt: JSON.parse(options.json), receiptRoot: options.receiptRoot };
|
|
15
187
|
if (options.repository !== "kungfu-systems/buildchain" || options.targetChannel !== "alpha" || options.workflowShellRef !== "v4-alpha" || options.runtimeOverride !== true) return undefined;
|
|
@@ -80,6 +252,7 @@ export function generateReleaseCandidatePassportCli() {
|
|
|
80
252
|
if (!validation.ok) throw new Error(`release candidate passport invalid: ${validation.errors.join("; ")}`);
|
|
81
253
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
82
254
|
fs.writeFileSync(outputPath, `${JSON.stringify(passport, null, 2)}\n`);
|
|
255
|
+
const stageCapsules = writeReleaseCandidateStageCapsules({ passport, buildSummary, outputPath });
|
|
83
256
|
writeGitHubOutputs({
|
|
84
257
|
"release-candidate-passport-path": path.relative(process.cwd(), outputPath).split(path.sep).join("/"),
|
|
85
258
|
"release-candidate-passport-json": JSON.stringify({
|
|
@@ -94,6 +267,10 @@ export function generateReleaseCandidatePassportCli() {
|
|
|
94
267
|
consumerPolicy: passport.consumerPolicy,
|
|
95
268
|
controllerReceipts: passport.controllerReceipts || [],
|
|
96
269
|
}),
|
|
270
|
+
"release-candidate-stage-capsules-path": stageCapsules.path,
|
|
271
|
+
"release-candidate-stage-capsules-root": stageCapsules.root,
|
|
272
|
+
"release-candidate-publication-qualification-path": stageCapsules.qualificationPath,
|
|
273
|
+
"release-candidate-publication-qualification-root": stageCapsules.qualificationRoot,
|
|
97
274
|
});
|
|
98
275
|
return passport;
|
|
99
276
|
}
|
|
@@ -54,6 +54,28 @@ const V4_COMPATIBILITY_FACTS_SCHEMA =
|
|
|
54
54
|
"schemas/v4-compatibility-facts-v1.schema.json";
|
|
55
55
|
const V4_COMPATIBILITY_FACTS_SCHEMA_SOURCE =
|
|
56
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 V4_PUBLICATION_QUALIFICATION_SCHEMA =
|
|
70
|
+
"schemas/v4-publication-qualification-v1.schema.json";
|
|
71
|
+
const V4_PUBLICATION_QUALIFICATION_SCHEMA_SOURCE =
|
|
72
|
+
"contracts/v4-publication-qualification-v1.schema.json";
|
|
73
|
+
const PUBLICATION_REHEARSAL_SITE_SCHEMAS = [
|
|
74
|
+
V4_PUBLICATION_REHEARSAL_SCHEMA,
|
|
75
|
+
V4_ADOPTER_DELIVERY_SCHEMA,
|
|
76
|
+
RELEASE_TAIL_CAPABILITIES_SCHEMA,
|
|
77
|
+
V4_PUBLICATION_QUALIFICATION_SCHEMA,
|
|
78
|
+
];
|
|
57
79
|
const root = path.resolve(import.meta.dirname, "..");
|
|
58
80
|
const outputDir = path.join(root, "dist", "site");
|
|
59
81
|
const requireFromHere = createRequire(import.meta.url);
|
|
@@ -84,6 +106,10 @@ function stableJson(value) {
|
|
|
84
106
|
function siteFileBytes(name, value) {
|
|
85
107
|
if (name === DEV_DELIVERY_AUTHORITY_SCHEMA) return readText(DEV_DELIVERY_AUTHORITY_SCHEMA_SOURCE);
|
|
86
108
|
if (name === V4_COMPATIBILITY_FACTS_SCHEMA) return readText(V4_COMPATIBILITY_FACTS_SCHEMA_SOURCE);
|
|
109
|
+
if (name === V4_PUBLICATION_REHEARSAL_SCHEMA) return readText(V4_PUBLICATION_REHEARSAL_SCHEMA_SOURCE);
|
|
110
|
+
if (name === V4_PUBLICATION_QUALIFICATION_SCHEMA) return readText(V4_PUBLICATION_QUALIFICATION_SCHEMA_SOURCE);
|
|
111
|
+
if (name === V4_ADOPTER_DELIVERY_SCHEMA) return readText(V4_ADOPTER_DELIVERY_SCHEMA_SOURCE);
|
|
112
|
+
if (name === RELEASE_TAIL_CAPABILITIES_SCHEMA) return readText(RELEASE_TAIL_CAPABILITIES_SCHEMA_SOURCE);
|
|
87
113
|
return stableJson(value);
|
|
88
114
|
}
|
|
89
115
|
|
|
@@ -396,6 +422,7 @@ const manualMetaById = new Map(Object.entries({
|
|
|
396
422
|
"observed-evidence-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 140 },
|
|
397
423
|
"engineering-housekeeper": { capabilityGroup: "governance-versioning", audience: ["maintainer", "consumer", "agent"], maturity: "preview", order: 145 },
|
|
398
424
|
"reusable-build-surface": { capabilityGroup: "reusable-build", audience: ["consumer", "release-operator"], maturity: "stable", order: 200 },
|
|
425
|
+
"v4-adopter-delivery": { capabilityGroup: "reusable-build", audience: ["consumer", "developer", "agent"], maturity: "preview", order: 205 },
|
|
399
426
|
"lifecycle-protocol": { capabilityGroup: "reusable-build", audience: ["consumer", "developer"], maturity: "stable", order: 210 },
|
|
400
427
|
"runtime-train-validation": { capabilityGroup: "governance-versioning", audience: ["maintainer", "consumer"], maturity: "stable", order: 220 },
|
|
401
428
|
"kfd-support": { capabilityGroup: "kfd-trust", audience: ["agent", "maintainer"], maturity: "stable", order: 300 },
|
|
@@ -956,6 +983,7 @@ function buildSiteBundle() {
|
|
|
956
983
|
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
957
984
|
"schemas/dev-delivery-authority-v2.schema.json",
|
|
958
985
|
"schemas/v4-compatibility-facts-v1.schema.json",
|
|
986
|
+
...PUBLICATION_REHEARSAL_SITE_SCHEMAS,
|
|
959
987
|
"kfd-support.json",
|
|
960
988
|
"artifact-evidence.json",
|
|
961
989
|
"product-mechanism.json",
|
|
@@ -992,6 +1020,7 @@ function buildSiteBundle() {
|
|
|
992
1020
|
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
993
1021
|
"schemas/dev-delivery-authority-v2.schema.json",
|
|
994
1022
|
"schemas/v4-compatibility-facts-v1.schema.json",
|
|
1023
|
+
...PUBLICATION_REHEARSAL_SITE_SCHEMAS,
|
|
995
1024
|
"buildchain-contract.json",
|
|
996
1025
|
"kfd-claims.json",
|
|
997
1026
|
"product-mechanism.json",
|
|
@@ -1124,6 +1153,7 @@ function buildSiteBundle() {
|
|
|
1124
1153
|
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
1125
1154
|
"schemas/dev-delivery-authority-v2.schema.json",
|
|
1126
1155
|
"schemas/v4-compatibility-facts-v1.schema.json",
|
|
1156
|
+
...PUBLICATION_REHEARSAL_SITE_SCHEMAS,
|
|
1127
1157
|
"buildchain-contract.json",
|
|
1128
1158
|
"kfd-upstream-aggregate.json",
|
|
1129
1159
|
"kfd-claims.json",
|
|
@@ -1304,6 +1334,12 @@ function buildSiteBundle() {
|
|
|
1304
1334
|
[V4_COMPATIBILITY_FACTS_SCHEMA]: readJson(
|
|
1305
1335
|
V4_COMPATIBILITY_FACTS_SCHEMA_SOURCE,
|
|
1306
1336
|
),
|
|
1337
|
+
...Object.fromEntries(
|
|
1338
|
+
PUBLICATION_REHEARSAL_SITE_SCHEMAS.map((name) => [
|
|
1339
|
+
name,
|
|
1340
|
+
JSON.parse(siteFileBytes(name)),
|
|
1341
|
+
]),
|
|
1342
|
+
),
|
|
1307
1343
|
"badge-endpoint-registry.json": badgeEndpointRegistry,
|
|
1308
1344
|
"publication-registry.json": publicationRegistry,
|
|
1309
1345
|
"buildchain-contract.json": createBuildchainContractWorld({ root, controllerRegistry }),
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { parseWorkflowDocument } from "../packages/core/workflow-yaml-contract.js";
|
|
2
|
+
|
|
3
|
+
function lineCount(source) {
|
|
4
|
+
const text = String(source || "");
|
|
5
|
+
if (!text) return 0;
|
|
6
|
+
const lines = text.split(/\r?\n/u);
|
|
7
|
+
return lines.at(-1) === "" ? lines.length - 1 : lines.length;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function rustTokens(source) {
|
|
11
|
+
const tokens = [];
|
|
12
|
+
let index = 0;
|
|
13
|
+
let line = 1;
|
|
14
|
+
const advance = () => {
|
|
15
|
+
if (source[index] === "\n") line += 1;
|
|
16
|
+
index += 1;
|
|
17
|
+
};
|
|
18
|
+
while (index < source.length) {
|
|
19
|
+
if (/\s/u.test(source[index])) {
|
|
20
|
+
advance();
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (source.startsWith("//", index)) {
|
|
24
|
+
while (index < source.length && source[index] !== "\n") advance();
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (source.startsWith("/*", index)) {
|
|
28
|
+
let depth = 1;
|
|
29
|
+
advance();
|
|
30
|
+
advance();
|
|
31
|
+
while (index < source.length && depth > 0) {
|
|
32
|
+
if (source.startsWith("/*", index)) {
|
|
33
|
+
depth += 1;
|
|
34
|
+
advance();
|
|
35
|
+
advance();
|
|
36
|
+
} else if (source.startsWith("*/", index)) {
|
|
37
|
+
depth -= 1;
|
|
38
|
+
advance();
|
|
39
|
+
advance();
|
|
40
|
+
} else {
|
|
41
|
+
advance();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const raw = source.slice(index).match(/^(?:br|r)(#+)?"/u);
|
|
47
|
+
if (raw) {
|
|
48
|
+
const suffix = `"${raw[1] || ""}`;
|
|
49
|
+
for (let count = 0; count < raw[0].length; count += 1) advance();
|
|
50
|
+
while (index < source.length && !source.startsWith(suffix, index)) {
|
|
51
|
+
advance();
|
|
52
|
+
}
|
|
53
|
+
for (let count = 0; count < suffix.length; count += 1) advance();
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (source[index] === '"' || source.startsWith('b"', index)) {
|
|
57
|
+
if (source[index] === "b") advance();
|
|
58
|
+
advance();
|
|
59
|
+
while (index < source.length) {
|
|
60
|
+
if (source[index] === "\\") {
|
|
61
|
+
advance();
|
|
62
|
+
if (index < source.length) advance();
|
|
63
|
+
} else if (source[index] === '"') {
|
|
64
|
+
advance();
|
|
65
|
+
break;
|
|
66
|
+
} else {
|
|
67
|
+
advance();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const character = source.slice(index).match(/^'(?:\\.|[^'\\\n])'/u);
|
|
73
|
+
if (character) {
|
|
74
|
+
for (let count = 0; count < character[0].length; count += 1) advance();
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const identifier = source.slice(index).match(/^[A-Za-z_][A-Za-z0-9_]*/u);
|
|
78
|
+
if (identifier) {
|
|
79
|
+
tokens.push({ value: identifier[0], line });
|
|
80
|
+
index += identifier[0].length;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const operator = [
|
|
84
|
+
"=>",
|
|
85
|
+
"&&",
|
|
86
|
+
"||",
|
|
87
|
+
"::",
|
|
88
|
+
"->",
|
|
89
|
+
"<=",
|
|
90
|
+
">=",
|
|
91
|
+
"==",
|
|
92
|
+
"!=",
|
|
93
|
+
].find((candidate) => source.startsWith(candidate, index));
|
|
94
|
+
tokens.push({ value: operator || source[index], line });
|
|
95
|
+
index += (operator || source[index]).length;
|
|
96
|
+
}
|
|
97
|
+
return tokens;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function rustFunctions(tokens) {
|
|
101
|
+
const functions = [];
|
|
102
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
103
|
+
if (tokens[index].value !== "fn") continue;
|
|
104
|
+
const name =
|
|
105
|
+
tokens[index + 1]?.value || `<anonymous@${tokens[index].line}>`;
|
|
106
|
+
let body = index + 2;
|
|
107
|
+
while (body < tokens.length && !["{", ";"].includes(tokens[body].value)) {
|
|
108
|
+
body += 1;
|
|
109
|
+
}
|
|
110
|
+
if (tokens[body]?.value !== "{") continue;
|
|
111
|
+
let depth = 1;
|
|
112
|
+
let end = body + 1;
|
|
113
|
+
while (end < tokens.length && depth > 0) {
|
|
114
|
+
if (tokens[end].value === "{") depth += 1;
|
|
115
|
+
if (tokens[end].value === "}") depth -= 1;
|
|
116
|
+
end += 1;
|
|
117
|
+
}
|
|
118
|
+
if (depth !== 0) continue;
|
|
119
|
+
functions.push({ name, token: index, body, end: end - 1 });
|
|
120
|
+
}
|
|
121
|
+
return functions;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isRustDecision(tokens, index) {
|
|
125
|
+
const value = tokens[index].value;
|
|
126
|
+
if (["if", "for", "while", "loop", "?", "=>"].includes(value)) return true;
|
|
127
|
+
if (!["&&", "||"].includes(value)) return false;
|
|
128
|
+
return !["=", "(", "{", "[", ",", ";", "return", "move"].includes(
|
|
129
|
+
tokens[index - 1]?.value,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function analyzeRust(_file, source) {
|
|
134
|
+
const tokens = rustTokens(source);
|
|
135
|
+
const parsed = rustFunctions(tokens);
|
|
136
|
+
const byStart = new Map(parsed.map((entry) => [entry.token, entry]));
|
|
137
|
+
const functions = parsed.map((entry) => {
|
|
138
|
+
let complexity = 1;
|
|
139
|
+
for (let index = entry.body + 1; index < entry.end; index += 1) {
|
|
140
|
+
const nested = byStart.get(index);
|
|
141
|
+
if (nested && nested.end <= entry.end) {
|
|
142
|
+
index = nested.end;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (isRustDecision(tokens, index)) complexity += 1;
|
|
146
|
+
}
|
|
147
|
+
const start = tokens[entry.token].line;
|
|
148
|
+
const end = tokens[entry.end].line;
|
|
149
|
+
return {
|
|
150
|
+
name: entry.name,
|
|
151
|
+
start,
|
|
152
|
+
end,
|
|
153
|
+
lines: end - start + 1,
|
|
154
|
+
complexity,
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
return {
|
|
158
|
+
lines: lineCount(source),
|
|
159
|
+
complexity: functions.reduce((total, entry) => total + entry.complexity, 0),
|
|
160
|
+
functions,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function semanticYamlLines(source) {
|
|
165
|
+
const result = [];
|
|
166
|
+
let blockIndent = null;
|
|
167
|
+
for (const [line, raw] of String(source || "")
|
|
168
|
+
.split(/\r?\n/u)
|
|
169
|
+
.entries()) {
|
|
170
|
+
const indent = raw.match(/^(\s*)/u)?.[1].length || 0;
|
|
171
|
+
if (blockIndent !== null) {
|
|
172
|
+
if (!raw.trim() || indent > blockIndent) continue;
|
|
173
|
+
blockIndent = null;
|
|
174
|
+
}
|
|
175
|
+
const text = raw.trim();
|
|
176
|
+
if (!text || text.startsWith("#")) continue;
|
|
177
|
+
result.push({ line: line + 1, indent, text });
|
|
178
|
+
if (/:\s*[>|][+-]?\s*(?:#.*)?$/u.test(raw)) blockIndent = indent;
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function analyzeWorkflow(_file, source) {
|
|
184
|
+
const lines = semanticYamlLines(source);
|
|
185
|
+
const document = parseWorkflowDocument(source);
|
|
186
|
+
const jobsEntry = lines.find(
|
|
187
|
+
(entry) => entry.indent === 0 && entry.text === "jobs:",
|
|
188
|
+
);
|
|
189
|
+
const jobs = [];
|
|
190
|
+
if (jobsEntry) {
|
|
191
|
+
const candidates = lines.filter(
|
|
192
|
+
(entry) => entry.line > jobsEntry.line && entry.indent > jobsEntry.indent,
|
|
193
|
+
);
|
|
194
|
+
const jobIndent = Math.min(...candidates.map((entry) => entry.indent));
|
|
195
|
+
for (const entry of candidates) {
|
|
196
|
+
if (
|
|
197
|
+
entry.indent === jobIndent &&
|
|
198
|
+
/^[A-Za-z0-9_.-]+:\s*(?:#.*)?$/u.test(entry.text)
|
|
199
|
+
) {
|
|
200
|
+
jobs.push(entry);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const stepsByJob = jobs.map((job, jobIndex) => {
|
|
205
|
+
const endLine = jobs[jobIndex + 1]?.line || Number.POSITIVE_INFINITY;
|
|
206
|
+
const stepRoot = lines.find(
|
|
207
|
+
(entry) =>
|
|
208
|
+
entry.line > job.line &&
|
|
209
|
+
entry.line < endLine &&
|
|
210
|
+
entry.indent > job.indent &&
|
|
211
|
+
entry.text === "steps:",
|
|
212
|
+
);
|
|
213
|
+
if (!stepRoot) return 0;
|
|
214
|
+
const stepLines = lines.filter(
|
|
215
|
+
(entry) =>
|
|
216
|
+
entry.line > stepRoot.line &&
|
|
217
|
+
entry.line < endLine &&
|
|
218
|
+
entry.indent > stepRoot.indent,
|
|
219
|
+
);
|
|
220
|
+
const stepIndent = Math.min(...stepLines.map((entry) => entry.indent));
|
|
221
|
+
return stepLines.filter(
|
|
222
|
+
(entry) => entry.indent === stepIndent && entry.text.startsWith("- "),
|
|
223
|
+
).length;
|
|
224
|
+
});
|
|
225
|
+
const decisions = lines.filter((entry) =>
|
|
226
|
+
/^(?:-\s+)?(?:if|matrix):/u.test(entry.text),
|
|
227
|
+
).length;
|
|
228
|
+
return {
|
|
229
|
+
lines: lineCount(source),
|
|
230
|
+
jobs: document.jobs.length,
|
|
231
|
+
steps: stepsByJob.reduce((total, count) => total + count, 0),
|
|
232
|
+
maxStepsPerJob: Math.max(0, ...stepsByJob),
|
|
233
|
+
decisions,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export { analyzeRust, analyzeWorkflow };
|