@kungfu-tech/buildchain 4.0.2-alpha.37 → 4.0.2-alpha.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/architecture/agent-change-map.md +2 -2
- package/architecture/internal-capabilities.json +2 -2
- package/architecture/maintainability-debt.json +8 -8
- package/architecture/maintainability-policy.json +3 -3
- package/architecture/v4-cross-platform-adopter-qualification.json +1 -1
- package/architecture/v4-floating-consumer-policy.json +1 -1
- package/architecture/v4-next-development-parity.json +2 -2
- package/architecture/v4-release-topology.json +0 -1
- package/architecture/v4-stage-capsule-qualification.json +1 -1
- package/architecture/v4-tail-reseal-parity.json +8 -6
- package/architecture/v4-universal-workflow-train-admission.json +1 -1
- package/architecture/workflow-taxonomy.json +18 -15
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/dist/site/buildchain-contract.json +6 -6
- package/dist/site/buildchain-site.json +67 -26
- package/dist/site/capability-registry.json +2 -2
- package/dist/site/kfd-claims.json +19 -73
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +5 -5
- package/dist/site/node-api-registry.json +30 -30
- package/dist/site/page-registry.json +58 -17
- package/dist/site/public-surface-audit.json +8 -67
- package/dist/site/publication-authority-registry.json +1 -16
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +9 -9
- package/dist/site/workflow-registry.json +5 -125
- package/docs/kfd-support.md +26 -2
- package/docs/next-development-transition.md +1 -1
- package/docs/node-api-reference.md +32 -32
- package/docs/release-passport.md +27 -2
- package/docs/v4-adopter-delivery.md +1 -1
- package/docs/v4-stage-capsule.md +1 -1
- package/docs/v4-tail-reseal.md +1 -1
- package/docs/workflow-catalog.md +5 -3
- package/docs/workflow-path-migration.md +109 -0
- package/package.json +1 -1
- package/packages/core/artifact-verification-envelope.js +5 -5
- package/packages/core/kfd-adopter-manifest.js +46 -47
- package/packages/core/next-development-projection.js +1 -1
- package/packages/core/release-passport-contract.js +5 -5
- package/packages/core/release-passport.js +5 -4
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +1 -1
- package/scripts/check-v4-public-dogfood-contract.mjs +4 -4
- package/scripts/check-v4-universal-workflow-bootstrap.mjs +3 -3
- package/scripts/generate-v4-universal-workflow-facades.mjs +4 -3
- package/scripts/v4-universal-workflow-engine.mjs +2 -1
- package/scripts/workflow-taxonomy.mjs +50 -11
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: draft
|
|
3
|
+
period: ongoing
|
|
4
|
+
theme: workflow-path-migration
|
|
5
|
+
doc_type: migration-guide
|
|
6
|
+
source_level: local-files
|
|
7
|
+
confidence: high
|
|
8
|
+
sensitivity: public
|
|
9
|
+
evidence_grade: B
|
|
10
|
+
review_state: unreviewed
|
|
11
|
+
last_reviewed: 2026-09-06
|
|
12
|
+
ai_provenance:
|
|
13
|
+
model_family: GPT-6
|
|
14
|
+
product: Codex
|
|
15
|
+
generated_at: 2026-09-05
|
|
16
|
+
visible_context: Exact GitHub channel refs and workflow files on 23 organization default branches
|
|
17
|
+
invisible_context_boundary: Other branches, private or unlisted consumers and future floating ref updates were not established by this audit
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Version-prefixed workflow path migration
|
|
21
|
+
|
|
22
|
+
This source change retires three filenames. Their reusable capabilities, inputs,
|
|
23
|
+
outputs, jobs and permission envelopes remain at the canonical paths below.
|
|
24
|
+
Keep the existing channel selector and dual contract-lock policy when migrating.
|
|
25
|
+
|
|
26
|
+
| Retired path | Canonical path |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| `v4-adopter-delivery.yml` | `public-build-adopter-qualification.yml` |
|
|
29
|
+
| `v4-stage-capsule-canary.yml` | `public-build-stage-capsule-canary.yml` |
|
|
30
|
+
| `v4-tail-reseal.yml` | `public-ops-tail-reseal.yml` |
|
|
31
|
+
|
|
32
|
+
All paths are beneath `.github/workflows/`. The taxonomy gate rejects version
|
|
33
|
+
segments in filenames and calls to version-prefixed Buildchain workflows. An
|
|
34
|
+
explicit retirement record keeps the original migration identity for historical
|
|
35
|
+
metrics and frozen facade source; generators resolve that identity to the
|
|
36
|
+
canonical implementation and cannot recreate the removed file.
|
|
37
|
+
|
|
38
|
+
## Development merge and channel rollout
|
|
39
|
+
|
|
40
|
+
The development change retires these filenames on `dev/v4/v4.0`, updates the
|
|
41
|
+
Buildchain-owned callers, and enforces the canonical paths through generators
|
|
42
|
+
and required checks. Its delivery boundary is the protected development merge.
|
|
43
|
+
External-repository migration and stable publication are separate follow-up
|
|
44
|
+
work and do not block this development merge.
|
|
45
|
+
|
|
46
|
+
The published channel boundary still requires the following rollout order:
|
|
47
|
+
|
|
48
|
+
1. Publish and verify the canonical entries on every affected channel while the
|
|
49
|
+
old entries remain available. The admission identity must match the actual
|
|
50
|
+
invoked canonical workflow, rather than its previous filename.
|
|
51
|
+
2. Migrate consumers on their existing floating channels, refresh locks through
|
|
52
|
+
the supported contract acceptance path if required, and verify real consumer
|
|
53
|
+
runs. Stable callers remain on stable; alpha callers remain on alpha.
|
|
54
|
+
3. Before publishing the retirement into a channel, recheck its live consumers
|
|
55
|
+
and resolve any remaining old-path calls. A protected development merge does
|
|
56
|
+
not by itself establish this channel readiness.
|
|
57
|
+
|
|
58
|
+
## Initial consumer readback
|
|
59
|
+
|
|
60
|
+
On 2026-09-05, all workflow files from 23 accessible `kungfu-systems` repository
|
|
61
|
+
_default branches_ were read at their returned Git revisions. Five calls were
|
|
62
|
+
found in three repositories. This is a bounded inventory, not evidence that
|
|
63
|
+
unknown or non-default-branch consumers do not exist.
|
|
64
|
+
|
|
65
|
+
| Repository and observed revision | Caller | Existing selector |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| Buildchain `d434a73d2d4fb010ee80fede2bf3ce872b1d04c8` | `self-build-adopter-dogfood.yml` | `v4-alpha` |
|
|
68
|
+
| Buildchain `d434a73d2d4fb010ee80fede2bf3ce872b1d04c8` | `self-build-public-consumer-dogfood.yml` | `v4-alpha` |
|
|
69
|
+
| agent-hub-demo `3f5de882b63464d70c50df0fd7ca418f487b794b` | `v4-adopter-delivery-qualification.yml` | `v4-alpha` |
|
|
70
|
+
| agent-hub-demo `3f5de882b63464d70c50df0fd7ca418f487b794b` | `v4-stage-capsule-canary.yml` | `v4` |
|
|
71
|
+
| taolu `6dcccdc3bd2c8b1801c7f6decea08ae7877aac2b` | `verify.yml` | `v4-alpha` |
|
|
72
|
+
|
|
73
|
+
The initial published channel readback was `v4` at
|
|
74
|
+
`da5e2db8384313e899eace87aea0a1a8e28c0aa8` and `v4-alpha` at
|
|
75
|
+
`dd87f43f7122b41c4ce7f1f6d1888521cc7f892f`. Neither contained the three canonical
|
|
76
|
+
filenames at that initial readback. The later evidence below records progress
|
|
77
|
+
without changing the scope or revisions of that initial inventory.
|
|
78
|
+
|
|
79
|
+
## Rollout evidence as of 2026-09-06
|
|
80
|
+
|
|
81
|
+
The canonical invocation-identity preparation was merged in
|
|
82
|
+
[Buildchain PR 3508](https://github.com/kungfu-systems/buildchain/pull/3508).
|
|
83
|
+
All three canonical paths are available in the immutable `v4.0.2-alpha.35`
|
|
84
|
+
release (`feab078f5d7c9927a3525154003e321700e8ca30`) and the later
|
|
85
|
+
`v4.0.2-alpha.37` release (`f5fa1a4a3e8d83eccee79c077e5f48417760c9df`).
|
|
86
|
+
The floating `v4-alpha` readback selects alpha.37.
|
|
87
|
+
|
|
88
|
+
[Taolu PR 30](https://github.com/kungfu-systems/taolu/pull/30) migrated its
|
|
89
|
+
Stage Capsule call on `@v4-alpha`, preserving the existing dual locks.
|
|
90
|
+
Its exact source `b3f4c12578d733ff44165ff1acafe131251b8907` passed
|
|
91
|
+
[consumer Verify](https://github.com/kungfu-systems/taolu/actions/runs/33975786413)
|
|
92
|
+
on all declared platforms and
|
|
93
|
+
[native delivery](https://github.com/kungfu-systems/taolu/actions/runs/34005576362).
|
|
94
|
+
The protected dev landing is `7e15123733110ad8ce484241934bbf0ef99fbb45`.
|
|
95
|
+
|
|
96
|
+
The stable `v4` readback remains
|
|
97
|
+
`da5e2db8384313e899eace87aea0a1a8e28c0aa8`, which does not contain the canonical
|
|
98
|
+
paths. Stable publication and the agent-hub-demo migration remain **pending**
|
|
99
|
+
outside this development change's completion boundary. The alpha.35
|
|
100
|
+
[Build Surface Fixture](https://github.com/kungfu-systems/buildchain/actions/runs/34005360217)
|
|
101
|
+
passed, but this individual result does not establish complete stable
|
|
102
|
+
qualification or authorize moving the stable channel.
|
|
103
|
+
|
|
104
|
+
## Recovery
|
|
105
|
+
|
|
106
|
+
Before channel retirement, revert consumer changes to the still-available old
|
|
107
|
+
paths if verification fails. After retirement, restore the exact previous
|
|
108
|
+
provider implementation through a reviewed corrective commit and normal
|
|
109
|
+
publication; do not rewrite immutable release tags or bypass admission checks.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "4.0.2-alpha.
|
|
3
|
+
"version": "4.0.2-alpha.39",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
|
|
6
6
|
"repository": "https://github.com/kungfu-systems/buildchain",
|
|
@@ -417,7 +417,7 @@ function kfdBindingBody({ manifest, manifestGate, legacyProjection, paths }) {
|
|
|
417
417
|
gatePath: paths.gate,
|
|
418
418
|
gateRoot: manifestGate.gateRoot,
|
|
419
419
|
},
|
|
420
|
-
source: jsonClone(manifestGate.source),
|
|
420
|
+
adopter: jsonClone(manifestGate.adopter), source: jsonClone(manifestGate.source),
|
|
421
421
|
standardPackage: jsonClone(manifestGate.standardPackage),
|
|
422
422
|
witness: {
|
|
423
423
|
decisionRoot: manifestGate.decisionWitness.root,
|
|
@@ -445,10 +445,10 @@ export function createKfdAdopterReleaseBinding({
|
|
|
445
445
|
manifestPath = "kfd-adopter-manifest.json",
|
|
446
446
|
gatePath = "kfd-adopter-manifest-gate.json",
|
|
447
447
|
legacyProjectionPath = "kfd-support.json",
|
|
448
|
-
expectedSourceSha = "",
|
|
448
|
+
expectedAdopterId = "kungfu-systems/buildchain", expectedSourceRepository = "", expectedSourceSha = "",
|
|
449
449
|
} = {}) {
|
|
450
450
|
const gate = validateKfdAdopterManifestGate(manifestGate, {
|
|
451
|
-
expectedSourceSha,
|
|
451
|
+
expectedAdopterId, expectedSourceRepository, expectedSourceSha,
|
|
452
452
|
checkedAt: manifestGate?.checkedAt,
|
|
453
453
|
});
|
|
454
454
|
const legacy = validateKfdLegacySupportMatrixProjection(legacyProjection, { manifest, manifestGate });
|
|
@@ -468,7 +468,7 @@ export function validateKfdAdopterReleaseBinding(binding, {
|
|
|
468
468
|
manifest,
|
|
469
469
|
manifestGate,
|
|
470
470
|
legacyProjection,
|
|
471
|
-
expectedSourceSha = "",
|
|
471
|
+
expectedAdopterId = "kungfu-systems/buildchain", expectedSourceRepository = "", expectedSourceSha = "",
|
|
472
472
|
} = {}) {
|
|
473
473
|
const issues = [];
|
|
474
474
|
if (!binding || binding.schemaVersion !== 1 || binding.contract !== KFD_ADOPTER_RELEASE_BINDING_CONTRACT) {
|
|
@@ -488,7 +488,7 @@ export function validateKfdAdopterReleaseBinding(binding, {
|
|
|
488
488
|
manifestPath: binding.authority?.manifestPath,
|
|
489
489
|
gatePath: binding.authority?.gatePath,
|
|
490
490
|
legacyProjectionPath: binding.legacyProjection?.path,
|
|
491
|
-
expectedSourceSha,
|
|
491
|
+
expectedAdopterId, expectedSourceRepository, expectedSourceSha,
|
|
492
492
|
});
|
|
493
493
|
if (stableJson(binding) !== stableJson(expected)) {
|
|
494
494
|
issues.push({ code: "adopter-release-binding-drift", path: "", message: "release binding differs from the exact manifest, gate, or legacy projection closure" });
|
|
@@ -21,7 +21,7 @@ export const KFD_LEGACY_SUPPORT_MATRIX_CONTRACT = "kungfu-kfd-support-matrix";
|
|
|
21
21
|
|
|
22
22
|
const BUILDCHAIN_ADOPTER_ID = "kungfu-systems/buildchain";
|
|
23
23
|
const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
24
|
-
const SHA_PATTERN = /^[0-9a-f]{40}
|
|
24
|
+
const SHA_PATTERN = /^[0-9a-f]{40}$/, SOURCE_REPOSITORY_PATTERN = /^[^@\s]+$/;
|
|
25
25
|
const REQUIRED_DECISIONS = Object.freeze(["KFD-1", "KFD-2", "KFD-3", "KFD-4", "KFD-5", "KFD-7"]);
|
|
26
26
|
const PRODUCT_GATE_STANDARDS = Object.freeze(["kfd-4", "kfd-5", "kfd-7"]);
|
|
27
27
|
const PRODUCT_GATE_SET = new Set(PRODUCT_GATE_STANDARDS);
|
|
@@ -77,29 +77,27 @@ function verifyPublishedManifest(manifest, packageOptions, issues) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function
|
|
81
|
-
const match = /^kungfu-systems\/buildchain@([0-9a-f]{40})$/.exec(text(manifest?.adopter?.artifact?.coordinate));
|
|
82
|
-
return match?.[1] || "";
|
|
83
|
-
}
|
|
80
|
+
function adopterExpectation({ expectedAdopterId = BUILDCHAIN_ADOPTER_ID, expectedSourceRepository = "" } = {}) { const adopterId = text(expectedAdopterId) || BUILDCHAIN_ADOPTER_ID; return { adopterId, sourceRepository: text(expectedSourceRepository) || adopterId }; }
|
|
84
81
|
|
|
85
|
-
function
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
82
|
+
function manifestSource(manifest) { const coordinate = text(manifest?.adopter?.artifact?.coordinate), match = /^([^@\s]+)@([0-9a-f]{40})$/.exec(coordinate); return { coordinate, repository: match?.[1] || "", sha: match?.[2] || "" }; }
|
|
83
|
+
|
|
84
|
+
function validateManifestIdentity(manifest, packageArtifactRoot, expectation, expectedSourceSha, issues) {
|
|
85
|
+
if (manifest?.adopter?.id !== expectation.adopterId) issues.push(issue("adopter-identity", "manifest.adopter.id", `manifest adopter must be ${expectation.adopterId}`));
|
|
89
86
|
const pinned = manifest?.kfdCut?.package;
|
|
90
87
|
if (pinned?.name !== kfdPackageJson.name
|
|
91
88
|
|| pinned?.version !== kfdPackageJson.version
|
|
92
89
|
|| pinned?.artifactRoot !== packageArtifactRoot) {
|
|
93
90
|
issues.push(issue("adopter-package-cut", "manifest.kfdCut.package", "manifest must bind the exact installed KFD package cut"));
|
|
94
91
|
}
|
|
95
|
-
const
|
|
96
|
-
if (manifest?.adopter?.artifact?.kind !== "git-commit" || !
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
const source = manifestSource(manifest);
|
|
93
|
+
if (!SOURCE_REPOSITORY_PATTERN.test(expectation.sourceRepository) || manifest?.adopter?.artifact?.kind !== "git-commit" || !source.sha || !ROOT_PATTERN.test(text(manifest?.adopter?.artifact?.root))) {
|
|
94
|
+
issues.push(issue("adopter-source", "manifest.adopter.artifact", "adopter authority must bind one exact repository, git commit, and artifact root"));
|
|
95
|
+
} else if (source.repository !== expectation.sourceRepository) {
|
|
96
|
+
issues.push(issue("adopter-source", "manifest.adopter.artifact.coordinate", `manifest source repository must be ${expectation.sourceRepository}`));
|
|
97
|
+
} else if (expectedSourceSha && source.sha !== expectedSourceSha) {
|
|
100
98
|
issues.push(issue("adopter-source", "manifest.adopter.artifact.coordinate", "manifest source does not match the requested release source"));
|
|
101
99
|
}
|
|
102
|
-
return
|
|
100
|
+
return source;
|
|
103
101
|
}
|
|
104
102
|
|
|
105
103
|
function validateRequiredDecisions(decisions, issues) {
|
|
@@ -131,7 +129,7 @@ function collectProductGates(gateResults, decisions, verificationCut, issues) {
|
|
|
131
129
|
for (const [index, gate] of gateResults.entries()) {
|
|
132
130
|
const validation = validateKfdProductGateResult(gate, verificationCut);
|
|
133
131
|
if (!validation.valid) {
|
|
134
|
-
issues.push(issue("adopter-gate-invalid", `gateResults[${index}]`, "
|
|
132
|
+
issues.push(issue("adopter-gate-invalid", `gateResults[${index}]`, "adopter product gate is invalid", {
|
|
135
133
|
gateIssues: validation.issues,
|
|
136
134
|
}));
|
|
137
135
|
}
|
|
@@ -141,15 +139,16 @@ function collectProductGates(gateResults, decisions, verificationCut, issues) {
|
|
|
141
139
|
if (gates.has(gate?.standard)) {
|
|
142
140
|
issues.push(issue("adopter-gate-duplicate", `gateResults[${index}].standard`, `${gate.standard} gate is duplicated`));
|
|
143
141
|
}
|
|
142
|
+
if (gate?.source?.repository !== verificationCut.expectedSourceRepository) issues.push(issue("adopter-gate-source", `gateResults[${index}].source.repository`, "product gate repository must match the adopter source repository"));
|
|
144
143
|
gates.set(gate?.standard, gate);
|
|
145
144
|
}
|
|
146
145
|
for (const standard of PRODUCT_GATE_STANDARDS) {
|
|
147
146
|
const gate = gates.get(standard);
|
|
148
147
|
const id = standard.toUpperCase();
|
|
149
148
|
if (!gate) {
|
|
150
|
-
issues.push(issue("adopter-gate-missing", `gateResults.${standard}`, `${id} requires its
|
|
149
|
+
issues.push(issue("adopter-gate-missing", `gateResults.${standard}`, `${id} requires its exact adopter product gate`));
|
|
151
150
|
} else if (!(decisions.get(id)?.verificationEvidence || []).some((entry) => entry?.root === gate.gateRoot)) {
|
|
152
|
-
issues.push(issue("adopter-gate-unbound", `manifest.decisions.${id}.verificationEvidence`, `${id} must bind the exact
|
|
151
|
+
issues.push(issue("adopter-gate-unbound", `manifest.decisions.${id}.verificationEvidence`, `${id} must bind the exact adopter gate root`));
|
|
153
152
|
}
|
|
154
153
|
}
|
|
155
154
|
return gates;
|
|
@@ -159,12 +158,12 @@ function projectedProductGates(gates) {
|
|
|
159
158
|
return PRODUCT_GATE_STANDARDS.map((standard) => {
|
|
160
159
|
const gate = gates.get(standard);
|
|
161
160
|
return gate
|
|
162
|
-
? { standard, sourceSha: gate.source.sha, gateRoot: gate.gateRoot, status: gate.status }
|
|
163
|
-
: { standard, sourceSha: "", gateRoot: "", status: "missing" };
|
|
161
|
+
? { standard, sourceRepository: gate.source.repository, sourceSha: gate.source.sha, gateRoot: gate.gateRoot, status: gate.status }
|
|
162
|
+
: { standard, sourceRepository: "", sourceSha: "", gateRoot: "", status: "missing" };
|
|
164
163
|
});
|
|
165
164
|
}
|
|
166
165
|
|
|
167
|
-
function createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot,
|
|
166
|
+
function createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, source, checkedAt, maxAgeSeconds, issues }) {
|
|
168
167
|
const gate = {
|
|
169
168
|
schemaVersion: 1,
|
|
170
169
|
contract: KFD_ADOPTER_MANIFEST_GATE_CONTRACT,
|
|
@@ -174,10 +173,8 @@ function createGateDocument({ manifest, bundle, gates, authorityPath, packageArt
|
|
|
174
173
|
contract: text(manifest?.contract),
|
|
175
174
|
manifestRoot: bundle?.roots?.manifestRoot || "",
|
|
176
175
|
},
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
artifactRoot: text(manifest?.adopter?.artifact?.root),
|
|
180
|
-
},
|
|
176
|
+
adopter: { id: text(manifest?.adopter?.id) },
|
|
177
|
+
source: { repository: source.repository, coordinate: source.coordinate, sha: source.sha, artifactRoot: text(manifest?.adopter?.artifact?.root) },
|
|
181
178
|
verificationCut: { checkedAt, maxAgeSeconds },
|
|
182
179
|
standardPackage: {
|
|
183
180
|
name: kfdPackageJson.name,
|
|
@@ -211,28 +208,28 @@ export function createKfdAdopterManifestGate({
|
|
|
211
208
|
packageArtifactRoot = "",
|
|
212
209
|
gateResults = [],
|
|
213
210
|
authorityPath = ".buildchain/kfd/adopter-manifest.json",
|
|
214
|
-
expectedSourceSha = "",
|
|
211
|
+
expectedAdopterId = BUILDCHAIN_ADOPTER_ID, expectedSourceRepository = "", expectedSourceSha = "",
|
|
215
212
|
checkedAt = new Date().toISOString(),
|
|
216
213
|
maxAgeSeconds = 86400,
|
|
217
214
|
} = {}) {
|
|
218
|
-
const issues = [];
|
|
215
|
+
const issues = [], expectation = adopterExpectation({ expectedAdopterId, expectedSourceRepository });
|
|
219
216
|
const bundle = verifyPublishedManifest(manifest, {
|
|
220
217
|
packageArtifactRoot,
|
|
221
218
|
verifiedAt: checkedAt,
|
|
222
219
|
maxAgeSeconds,
|
|
223
220
|
}, issues);
|
|
224
221
|
const decisions = new Map((manifest?.decisions || []).map((row) => [row.id, row]));
|
|
225
|
-
let
|
|
222
|
+
let source = manifestSource(manifest);
|
|
226
223
|
if (bundle) {
|
|
227
|
-
|
|
224
|
+
source = validateManifestIdentity(manifest, packageArtifactRoot, expectation, expectedSourceSha, issues);
|
|
228
225
|
validateRequiredDecisions(decisions, issues);
|
|
229
226
|
validateWarrantWitness(decisions, issues);
|
|
230
227
|
}
|
|
231
|
-
const gates = collectProductGates(gateResults, decisions, { expectedSourceSha:
|
|
232
|
-
return createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot,
|
|
228
|
+
const gates = collectProductGates(gateResults, decisions, { expectedSourceRepository: expectation.sourceRepository, expectedSourceSha: source.sha || expectedSourceSha, checkedAt }, issues);
|
|
229
|
+
return createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, source, checkedAt, maxAgeSeconds, issues });
|
|
233
230
|
}
|
|
234
231
|
|
|
235
|
-
function validateGateDocument(gate, issues) {
|
|
232
|
+
function validateGateDocument(gate, expectation, issues) {
|
|
236
233
|
const copy = structuredClone(gate);
|
|
237
234
|
const root = copy.gateRoot;
|
|
238
235
|
delete copy.gateRoot;
|
|
@@ -244,12 +241,13 @@ function validateGateDocument(gate, issues) {
|
|
|
244
241
|
|| !ROOT_PATTERN.test(text(gate?.authority?.manifestRoot))) {
|
|
245
242
|
issues.push(issue("adopter-gate-authority", "authority", "adopter gate must bind the standard manifest contract and root"));
|
|
246
243
|
}
|
|
244
|
+
if (gate?.adopter?.id !== expectation.adopterId) issues.push(issue("adopter-gate-identity", "adopter.id", `adopter gate must bind ${expectation.adopterId}`));
|
|
247
245
|
if (gate?.standardPackage?.name !== kfdPackageJson.name || gate?.standardPackage?.version !== kfdPackageJson.version
|
|
248
246
|
|| ![gate?.standardPackage?.artifactRoot, gate?.standardPackage?.registryRoot, gate?.standardPackage?.verifierSetRoot].every((value) => ROOT_PATTERN.test(text(value)))) {
|
|
249
247
|
issues.push(issue("adopter-gate-package", "standardPackage", "adopter gate uses stale KFD package metadata"));
|
|
250
248
|
}
|
|
251
|
-
if (!SHA_PATTERN.test(text(gate?.source?.sha)) || !ROOT_PATTERN.test(text(gate?.source?.artifactRoot))) {
|
|
252
|
-
issues.push(issue("adopter-gate-source", "source", "adopter gate must bind
|
|
249
|
+
if (gate?.source?.repository !== expectation.sourceRepository || gate?.source?.coordinate !== `${expectation.sourceRepository}@${gate?.source?.sha || ""}` || !SHA_PATTERN.test(text(gate?.source?.sha)) || !ROOT_PATTERN.test(text(gate?.source?.artifactRoot))) {
|
|
250
|
+
issues.push(issue("adopter-gate-source", "source", "adopter gate must bind the expected repository, exact source commit, coordinate, and artifact root"));
|
|
253
251
|
}
|
|
254
252
|
if (gate?.decisionWitness?.rootAlgorithm !== "sha256-buildchain-stable-json-v1"
|
|
255
253
|
|| !ROOT_PATTERN.test(text(gate?.decisionWitness?.root))
|
|
@@ -259,7 +257,7 @@ function validateGateDocument(gate, issues) {
|
|
|
259
257
|
}
|
|
260
258
|
}
|
|
261
259
|
|
|
262
|
-
function validateProjectedGates(gate, expectedSourceSha, issues) {
|
|
260
|
+
function validateProjectedGates(gate, expectation, expectedSourceSha, issues) {
|
|
263
261
|
const seenStandards = new Set();
|
|
264
262
|
for (const [index, productGate] of (gate.gateResults || []).entries()) {
|
|
265
263
|
if (!PRODUCT_GATE_SET.has(productGate?.standard) || productGate?.status !== "passed" || !ROOT_PATTERN.test(text(productGate?.gateRoot))) {
|
|
@@ -267,7 +265,7 @@ function validateProjectedGates(gate, expectedSourceSha, issues) {
|
|
|
267
265
|
}
|
|
268
266
|
if (seenStandards.has(productGate?.standard)) issues.push(issue("adopter-gate-result-set", `gateResults[${index}]`, "product gate standards must be unique"));
|
|
269
267
|
seenStandards.add(productGate?.standard);
|
|
270
|
-
if (productGate?.sourceSha !== gate?.source?.sha || (expectedSourceSha && productGate?.sourceSha !== expectedSourceSha)) {
|
|
268
|
+
if (productGate?.sourceRepository !== gate?.source?.repository || productGate?.sourceRepository !== expectation.sourceRepository || productGate?.sourceSha !== gate?.source?.sha || (expectedSourceSha && productGate?.sourceSha !== expectedSourceSha)) {
|
|
271
269
|
issues.push(issue("adopter-gate-source", `gateResults[${index}].sourceSha`, "product gate source must match the release source"));
|
|
272
270
|
}
|
|
273
271
|
}
|
|
@@ -277,15 +275,15 @@ function validateProjectedGates(gate, expectedSourceSha, issues) {
|
|
|
277
275
|
}
|
|
278
276
|
|
|
279
277
|
export function validateKfdAdopterManifestGate(gate, {
|
|
280
|
-
expectedSourceSha = "",
|
|
278
|
+
expectedAdopterId = BUILDCHAIN_ADOPTER_ID, expectedSourceRepository = "", expectedSourceSha = "",
|
|
281
279
|
checkedAt = gate?.checkedAt || new Date().toISOString(),
|
|
282
280
|
} = {}) {
|
|
283
|
-
const issues = [];
|
|
281
|
+
const issues = [], expectation = adopterExpectation({ expectedAdopterId, expectedSourceRepository });
|
|
284
282
|
if (!gate || gate.schemaVersion !== 1 || gate.contract !== KFD_ADOPTER_MANIFEST_GATE_CONTRACT) {
|
|
285
283
|
return { valid: false, issues: [issue("adopter-gate-contract", "", `gate must use ${KFD_ADOPTER_MANIFEST_GATE_CONTRACT} v1`)] };
|
|
286
284
|
}
|
|
287
|
-
validateGateDocument(gate, issues);
|
|
288
|
-
validateProjectedGates(gate, expectedSourceSha, issues);
|
|
285
|
+
validateGateDocument(gate, expectation, issues);
|
|
286
|
+
validateProjectedGates(gate, expectation, expectedSourceSha, issues);
|
|
289
287
|
if (gate.checkedAt !== checkedAt || gate?.verificationCut?.checkedAt !== checkedAt || !Number.isFinite(Date.parse(checkedAt))
|
|
290
288
|
|| !Number.isSafeInteger(gate?.verificationCut?.maxAgeSeconds) || gate.verificationCut.maxAgeSeconds < 0) {
|
|
291
289
|
issues.push(issue("adopter-gate-time", "verificationCut", "adopter gate verification cut does not match the requested cut"));
|
|
@@ -300,7 +298,7 @@ function legacyStatus(row) {
|
|
|
300
298
|
return row.state;
|
|
301
299
|
}
|
|
302
300
|
|
|
303
|
-
function legacyRow(row, gates) {
|
|
301
|
+
function legacyRow(row, gates, adopterId) {
|
|
304
302
|
const key = row.id.toLowerCase();
|
|
305
303
|
const productGate = gates.get(key);
|
|
306
304
|
return {
|
|
@@ -318,7 +316,7 @@ function legacyRow(row, gates) {
|
|
|
318
316
|
releaseQualification: { shippedSupport: false },
|
|
319
317
|
claimClass: "standard-adopter-manifest-projection",
|
|
320
318
|
knownLimitations: [...row.gaps],
|
|
321
|
-
owner:
|
|
319
|
+
owner: adopterId,
|
|
322
320
|
nextGate: "Release Passport artifact binding and independent release decision",
|
|
323
321
|
declaration: { state: row.state, usage: row.usage, root: kfdProductGateDigest(decisionWitnessRow(row)) },
|
|
324
322
|
};
|
|
@@ -326,7 +324,7 @@ function legacyRow(row, gates) {
|
|
|
326
324
|
|
|
327
325
|
export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate } = {}) {
|
|
328
326
|
const validation = validateKfdAdopterManifestGate(manifestGate, {
|
|
329
|
-
expectedSourceSha: manifestGate?.source?.sha,
|
|
327
|
+
expectedAdopterId: manifestGate?.adopter?.id, expectedSourceRepository: manifestGate?.source?.repository, expectedSourceSha: manifestGate?.source?.sha,
|
|
330
328
|
checkedAt: manifestGate?.checkedAt,
|
|
331
329
|
});
|
|
332
330
|
if (!validation.valid || manifest?.contract !== "kfd.adopter-conformance-manifest/v1") {
|
|
@@ -338,9 +336,9 @@ export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate
|
|
|
338
336
|
verifiedAt: manifestGate.verificationCut.checkedAt,
|
|
339
337
|
maxAgeSeconds: manifestGate.verificationCut.maxAgeSeconds,
|
|
340
338
|
}, authorityIssues);
|
|
341
|
-
const
|
|
339
|
+
const source = validateManifestIdentity(manifest, manifestGate.standardPackage.artifactRoot, { adopterId: manifestGate.adopter.id, sourceRepository: manifestGate.source.repository }, manifestGate.source.sha, authorityIssues);
|
|
342
340
|
const decisionRoot = kfdProductGateDigest(manifest.decisions.map(decisionWitnessRow));
|
|
343
|
-
if (!bundle ||
|
|
341
|
+
if (!bundle || source.sha !== manifestGate.source.sha || source.repository !== manifestGate.source.repository || source.coordinate !== manifestGate.source.coordinate || manifest.adopter.artifact.root !== manifestGate.source.artifactRoot
|
|
344
342
|
|| bundle.roots.manifestRoot !== manifestGate.authority.manifestRoot
|
|
345
343
|
|| bundle.roots.verificationReportRoot !== manifestGate.manifestVerificationReportRoot
|
|
346
344
|
|| bundle.bundleRoot !== manifestGate.manifestBundleRoot
|
|
@@ -364,7 +362,8 @@ export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate
|
|
|
364
362
|
path: manifestGate.authority.path,
|
|
365
363
|
contract: manifest.contract,
|
|
366
364
|
root: manifestGate.authority.manifestRoot,
|
|
367
|
-
gateRoot: manifestGate.gateRoot,
|
|
365
|
+
gateRoot: manifestGate.gateRoot, adopterId: manifestGate.adopter.id,
|
|
366
|
+
sourceRepository: manifestGate.source.repository, sourceSha: manifestGate.source.sha,
|
|
368
367
|
},
|
|
369
368
|
upstream: {
|
|
370
369
|
package: manifestGate.standardPackage.name,
|
|
@@ -374,7 +373,7 @@ export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate
|
|
|
374
373
|
verifierSetRoot: manifestGate.standardPackage.verifierSetRoot,
|
|
375
374
|
standardsSha256: standardsFileDigest(),
|
|
376
375
|
},
|
|
377
|
-
rows: manifest.decisions.map((row) => legacyRow(row, gates)),
|
|
376
|
+
rows: manifest.decisions.map((row) => legacyRow(row, gates, manifestGate.adopter.id)),
|
|
378
377
|
};
|
|
379
378
|
}
|
|
380
379
|
|
|
@@ -244,7 +244,7 @@ invalid.
|
|
|
244
244
|
## Public self-dogfood and adoption
|
|
245
245
|
|
|
246
246
|
\`.github/workflows/self-build-public-consumer-dogfood.yml\` remains the exact thin
|
|
247
|
-
consumer of the public \`
|
|
247
|
+
consumer of the public \`public-build-stage-capsule-canary.yml@v4-alpha\` contract. Its
|
|
248
248
|
declared \`lifecycle.verify\` runs \`pnpm run check\`, so the same public path
|
|
249
249
|
executes pure recovery tests that inject a transient durable-state write
|
|
250
250
|
failure, restore the adapter operation on a fresh runner, supersede stale work
|
|
@@ -405,7 +405,7 @@ export function prepareReleasePassportKfdSections({ kfd1, kfd2Claims, kfd3, kfdA
|
|
|
405
405
|
};
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], comparisonMatrix, sourceSha = "", checkedAt } = {}) {
|
|
408
|
+
export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], comparisonMatrix, expectedAdopterId = "kungfu-systems/buildchain", expectedSourceRepository = "", sourceSha = "", checkedAt } = {}) {
|
|
409
409
|
if (!manifest) {
|
|
410
410
|
if (comparisonMatrix || gateResults.length > 0) {
|
|
411
411
|
throw new Error("KFD support and product-gate inputs require --kfd-adopter-manifest-json; --kfd-support-matrix-json is comparison-only");
|
|
@@ -414,7 +414,7 @@ export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], c
|
|
|
414
414
|
}
|
|
415
415
|
const manifestGate = createKfdAdopterManifestGate({
|
|
416
416
|
manifest, packageArtifactRoot: installedKfdPackageArtifactRoot(), gateResults,
|
|
417
|
-
expectedSourceSha: sourceSha, checkedAt,
|
|
417
|
+
expectedAdopterId, expectedSourceRepository, expectedSourceSha: sourceSha, checkedAt,
|
|
418
418
|
});
|
|
419
419
|
const legacyProjection = createKfdLegacySupportMatrixProjection({ manifest, manifestGate });
|
|
420
420
|
if (comparisonMatrix) {
|
|
@@ -425,18 +425,18 @@ export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], c
|
|
|
425
425
|
}
|
|
426
426
|
return {
|
|
427
427
|
manifest, manifestGate, legacyProjection,
|
|
428
|
-
binding: createKfdAdopterReleaseBinding({ manifest, manifestGate, legacyProjection, expectedSourceSha: sourceSha }),
|
|
428
|
+
binding: createKfdAdopterReleaseBinding({ manifest, manifestGate, legacyProjection, expectedAdopterId, expectedSourceRepository, expectedSourceSha: sourceSha }),
|
|
429
429
|
};
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
-
export function validateKfdAdopterReleaseEvidence({ binding, artifactBinding, manifest, manifestGate, legacyProjection, passportLegacyProjection, expectedSourceSha = "" } = {}) {
|
|
432
|
+
export function validateKfdAdopterReleaseEvidence({ binding, artifactBinding, manifest, manifestGate, legacyProjection, passportLegacyProjection, expectedAdopterId = "kungfu-systems/buildchain", expectedSourceRepository = "", expectedSourceSha = "" } = {}) {
|
|
433
433
|
if (!binding) {
|
|
434
434
|
return passportLegacyProjection
|
|
435
435
|
? [{ code: "kfdSupport.authority", message: "legacy KFD support projection requires the standard adopter manifest binding" }]
|
|
436
436
|
: [];
|
|
437
437
|
}
|
|
438
438
|
const issues = validateKfdAdopterReleaseBinding(binding, {
|
|
439
|
-
manifest, manifestGate, legacyProjection, expectedSourceSha,
|
|
439
|
+
manifest, manifestGate, legacyProjection, expectedAdopterId, expectedSourceRepository, expectedSourceSha,
|
|
440
440
|
}).issues.map((entry) => ({ code: `kfdAdopter.${entry.path || entry.code}`, message: entry.message, details: entry }));
|
|
441
441
|
if (!artifactBinding || passportStableJson(artifactBinding) !== passportStableJson(binding)) {
|
|
442
442
|
issues.push({ code: "kfdAdopter.artifactEvidence", message: "release passport and artifact evidence must bind the same exact KFD adopter closure" });
|
|
@@ -1431,7 +1431,7 @@ export function createReleasePassport(options = {}) {
|
|
|
1431
1431
|
};
|
|
1432
1432
|
}
|
|
1433
1433
|
|
|
1434
|
-
function collectKfdAdopterReleaseInputs({ cwd, manifestJson, supportMatrixJson, productGateJsons, sourceSha, checkedAt }) {
|
|
1434
|
+
function collectKfdAdopterReleaseInputs({ cwd, manifestJson, supportMatrixJson, productGateJsons, repository, sourceSha, checkedAt }) {
|
|
1435
1435
|
const manifestMeta = parseJsonInputWithMeta(manifestJson, undefined, { cwd, label: "kfdAdopterManifestJson" });
|
|
1436
1436
|
const supportMatrixMeta = parseJsonInputWithMeta(supportMatrixJson, undefined, { cwd, label: "kfdSupportMatrixJson" });
|
|
1437
1437
|
const productGateMetas = (productGateJsons || [])
|
|
@@ -1442,7 +1442,7 @@ function collectKfdAdopterReleaseInputs({ cwd, manifestJson, supportMatrixJson,
|
|
|
1442
1442
|
manifest: manifestMeta.value,
|
|
1443
1443
|
gateResults: productGateMetas.map((meta) => meta.value),
|
|
1444
1444
|
comparisonMatrix: supportMatrixMeta.value,
|
|
1445
|
-
sourceSha,
|
|
1445
|
+
expectedAdopterId: repository || "kungfu-systems/buildchain", expectedSourceRepository: repository, sourceSha,
|
|
1446
1446
|
checkedAt,
|
|
1447
1447
|
});
|
|
1448
1448
|
}
|
|
@@ -1620,7 +1620,7 @@ export function collectGitHubReleasePassport({
|
|
|
1620
1620
|
const productMechanism = defaultProductMechanism({ repository, productName });
|
|
1621
1621
|
const kfdAdopterEvidence = collectKfdAdopterReleaseInputs({
|
|
1622
1622
|
cwd, manifestJson: kfdAdopterManifestJson, supportMatrixJson: kfdSupportMatrixJson,
|
|
1623
|
-
productGateJsons: kfdProductGateJsons, sourceSha, checkedAt: resolvedCheckedAt,
|
|
1623
|
+
productGateJsons: kfdProductGateJsons, repository, sourceSha, checkedAt: resolvedCheckedAt,
|
|
1624
1624
|
});
|
|
1625
1625
|
const {
|
|
1626
1626
|
manifest: kfdAdopterManifest,
|
|
@@ -2060,7 +2060,8 @@ function validateReleaseEvidenceContracts({
|
|
|
2060
2060
|
manifestGate: kfdAdopterManifestGate,
|
|
2061
2061
|
legacyProjection: kfdSupportEvidence,
|
|
2062
2062
|
passportLegacyProjection: passport?.kfdSupport,
|
|
2063
|
-
|
|
2063
|
+
expectedAdopterId: optionalString(passport?.product?.repository) || "kungfu-systems/buildchain",
|
|
2064
|
+
expectedSourceRepository: optionalString(passport?.product?.repository), expectedSourceSha: optionalString(passport?.release?.sourceSha),
|
|
2064
2065
|
})) {
|
|
2065
2066
|
issues.push(issue("error", entry.code, entry.message, entry.details));
|
|
2066
2067
|
}
|
|
@@ -148,7 +148,7 @@ export function checkV4FloatingConsumerPolicyContract() {
|
|
|
148
148
|
"Resolve controller identities",
|
|
149
149
|
"Install Buildchain runtime dependencies",
|
|
150
150
|
]);
|
|
151
|
-
const stageCanary = read(".github/workflows/
|
|
151
|
+
const stageCanary = read(".github/workflows/public-build-stage-capsule-canary.yml");
|
|
152
152
|
if (
|
|
153
153
|
!stageCanary.includes("consumer-admission:") ||
|
|
154
154
|
!stageCanary.includes("needs: consumer-admission")
|
|
@@ -10,7 +10,7 @@ const DEFAULT_ROOT = path.resolve(
|
|
|
10
10
|
"..",
|
|
11
11
|
);
|
|
12
12
|
const CALLER_PATH = ".github/workflows/self-build-public-consumer-dogfood.yml";
|
|
13
|
-
const REUSABLE_PATH = ".github/workflows/
|
|
13
|
+
const REUSABLE_PATH = ".github/workflows/public-build-stage-capsule-canary.yml";
|
|
14
14
|
export const V4_PUBLIC_DOGFOOD_ALPHA_REF = "v4-alpha";
|
|
15
15
|
const PRIVATE_CONSUMER = ["buildchain", "self", "dogfood"].join("-");
|
|
16
16
|
const PRIVATE_SHADOW = ["kungfu", "shadow"].join("-");
|
|
@@ -27,7 +27,7 @@ permissions:
|
|
|
27
27
|
|
|
28
28
|
jobs:
|
|
29
29
|
dogfood:
|
|
30
|
-
uses: kungfu-systems/buildchain/.github/workflows/
|
|
30
|
+
uses: kungfu-systems/buildchain/.github/workflows/public-build-stage-capsule-canary.yml@${validationRef}
|
|
31
31
|
with:
|
|
32
32
|
consumer: buildchain
|
|
33
33
|
node-version: "24"
|
|
@@ -114,7 +114,7 @@ function assertWorkflowInventory(root) {
|
|
|
114
114
|
fail(`${relative} invokes the private qualification script directly`);
|
|
115
115
|
if (
|
|
116
116
|
relative !== CALLER_PATH &&
|
|
117
|
-
text.includes(".github/workflows/
|
|
117
|
+
text.includes(".github/workflows/public-build-stage-capsule-canary.yml@")
|
|
118
118
|
)
|
|
119
119
|
fail(`${relative} creates a second v4 dogfood caller`);
|
|
120
120
|
if (relative !== REUSABLE_PATH && relative !== CALLER_PATH)
|
|
@@ -162,7 +162,7 @@ function assertArchitecture(root) {
|
|
|
162
162
|
!dogfood ||
|
|
163
163
|
dogfood.callerWorkflow !== CALLER_PATH ||
|
|
164
164
|
dogfood.reusableWorkflow !==
|
|
165
|
-
"kungfu-systems/buildchain/.github/workflows/
|
|
165
|
+
"kungfu-systems/buildchain/.github/workflows/public-build-stage-capsule-canary.yml" ||
|
|
166
166
|
dogfood.runtimeBinding !== "job.workflow_sha" ||
|
|
167
167
|
dogfood.consumerSourceBinding !== "github.sha" ||
|
|
168
168
|
JSON.stringify(dogfood.executableStages) !==
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { projectWorkflowIdentities } from "./workflow-taxonomy.mjs";
|
|
2
|
+
import { currentWorkflowPath, projectWorkflowIdentities } from "./workflow-taxonomy.mjs";
|
|
3
3
|
import crypto from "node:crypto";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
@@ -48,7 +48,7 @@ assert.deepEqual(
|
|
|
48
48
|
);
|
|
49
49
|
for (const relative of contract.inventoryWorkflows)
|
|
50
50
|
assert.ok(
|
|
51
|
-
fs.statSync(path.join(root, relative)).isFile(),
|
|
51
|
+
fs.statSync(path.join(root, currentWorkflowPath(root, relative))).isFile(),
|
|
52
52
|
`inventoried workflow is unavailable: ${relative}`,
|
|
53
53
|
);
|
|
54
54
|
assert.deepEqual(
|
|
@@ -87,7 +87,7 @@ for (const relative of contract.bootstrapGovernedWorkflows) {
|
|
|
87
87
|
contract.inventoryWorkflows.includes(relative),
|
|
88
88
|
`Bootstrap-governed workflow is outside the inventory: ${relative}`,
|
|
89
89
|
);
|
|
90
|
-
const source = fs.readFileSync(path.join(root, relative), "utf8");
|
|
90
|
+
const source = fs.readFileSync(path.join(root, currentWorkflowPath(root, relative)), "utf8");
|
|
91
91
|
assert.ok(
|
|
92
92
|
relative === contract.bootstrap.publicWorkflow ||
|
|
93
93
|
/uses:\s+(?:\.\/)?\.github\/workflows\/bootstrap\.yml/u.test(source) ||
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import crypto from "node:crypto";
|
|
4
|
-
import { writeWorkflowSource, rewriteRepositoryWorkflowPaths } from "./workflow-taxonomy.mjs";
|
|
4
|
+
import { currentWorkflowPath, writeWorkflowSource, rewriteRepositoryWorkflowPaths } from "./workflow-taxonomy.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { execFileSync } from "node:child_process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -426,14 +426,15 @@ function main() {
|
|
|
426
426
|
);
|
|
427
427
|
}
|
|
428
428
|
for (const relative of activeFacadePaths()) {
|
|
429
|
-
const
|
|
429
|
+
const current = currentWorkflowPath(root, relative);
|
|
430
|
+
const target = path.join(root, current);
|
|
430
431
|
const source = fresh
|
|
431
432
|
? frozenFacadeSource(relative)
|
|
432
433
|
: fs.readFileSync(target, "utf8");
|
|
433
434
|
if (check) verify(source, relative);
|
|
434
435
|
else
|
|
435
436
|
writeWorkflowSource(
|
|
436
|
-
root,
|
|
437
|
+
root, current,
|
|
437
438
|
migrateV4UniversalWorkflowFacade(source, relative),
|
|
438
439
|
);
|
|
439
440
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
+
import { currentWorkflowPath } from "./workflow-taxonomy.mjs";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { execFileSync } from "node:child_process";
|
|
6
7
|
import {
|
|
@@ -192,7 +193,7 @@ function executeBootstrapConformance(request, admission) {
|
|
|
192
193
|
fail("candidate Bootstrap conformance does not close execution semantics");
|
|
193
194
|
for (const relative of architecture.bootstrapGovernedWorkflows) {
|
|
194
195
|
const source = fs.readFileSync(
|
|
195
|
-
|
|
196
|
+
path.join(path.resolve(import.meta.dirname, ".."), currentWorkflowPath(path.resolve(import.meta.dirname, ".."), relative)),
|
|
196
197
|
"utf8",
|
|
197
198
|
);
|
|
198
199
|
if (
|