@kungfu-tech/buildchain 4.0.1-alpha.8 → 4.0.1
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/v4-release-candidate-promote/README.md +3 -0
- package/architecture/agent-change-map.md +1015 -0
- package/architecture/ci-lane-change-budget.json +70 -1
- package/architecture/decisions/0004-v4-declarative-release-tail.md +47 -0
- package/architecture/internal-capabilities.json +156 -11
- package/architecture/maintainability-debt.json +3229 -0
- package/architecture/maintainability-policy.json +480 -129
- 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-cross-platform-adopter-qualification.json +52 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +3 -3
- package/architecture/v4-runtime-semantic-closure.json +710 -0
- package/bin/internal/trust-release-release-handlers.mjs +5 -0
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/contracts/v4-publication-qualification-v1.schema.json +51 -0
- package/dist/site/agent-index.json +1 -0
- package/dist/site/artifact-schemas.json +2 -0
- package/dist/site/buildchain-contract.json +123 -32
- package/dist/site/buildchain-site.json +96 -28
- package/dist/site/capability-registry.json +6 -6
- package/dist/site/cli-registry.json +5 -2
- package/dist/site/controller-registry.json +41 -5
- package/dist/site/kfd-claims.json +170 -21
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +6 -6
- package/dist/site/node-api-registry.json +1474 -215
- package/dist/site/page-registry.json +85 -17
- package/dist/site/public-surface-audit.json +85 -20
- package/dist/site/publication-authority-registry.json +28 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +5 -0
- package/dist/site/schemas/v4-publication-qualification-v1.schema.json +51 -0
- package/dist/site/site-manifest.json +10 -10
- package/dist/site/workflow-registry.json +94 -18
- package/docs/MAP.md +1 -1
- package/docs/cli-reference.md +4 -4
- package/docs/dev-delivery-warrant.md +12 -9
- package/docs/node-api-reference.md +291 -206
- package/docs/publish-transaction.md +11 -4
- package/docs/release-candidate.md +7 -0
- package/docs/v4-adopter-delivery.md +23 -4
- package/docs/v4-declarative-release-tail-migration.md +43 -0
- package/package.json +8 -3
- package/packages/core/adopter-delivery-passport.js +36 -0
- package/packages/core/buildchain-contract.js +31 -1
- package/packages/core/buildchain-publication-authority.js +1 -0
- package/packages/core/dev-delivery-warrant-settlement.js +239 -3
- package/packages/core/dev-delivery-warrant-state.js +15 -2
- package/packages/core/dev-delivery-warrant.js +21 -4
- package/packages/core/github-governance-authority.js +4 -4
- package/packages/core/index.js +1 -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/release-candidate-recovery.js +6 -5
- package/packages/core/release-passport.js +82 -184
- package/packages/core/v4-cross-platform-adopter-qualification.js +343 -0
- package/packages/core/v4-floating-consumer-policy.js +3 -2
- package/packages/core/v4-floating-consumer-release-passport.js +134 -15
- package/packages/core/v4-publication-qualification.js +222 -0
- package/scripts/artifact-signing-controller-core.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +17 -17
- package/scripts/buildchain-cli-help.mjs +3 -0
- package/scripts/check-internal-architecture.mjs +23 -3
- package/scripts/check-inventory.mjs +23 -5
- 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 +10 -3
- package/scripts/check-v4-runtime-semantic-closure.mjs +266 -0
- package/scripts/dev-delivery-warrant-options.mjs +27 -11
- package/scripts/dev-delivery-warrant.mjs +45 -5
- package/scripts/dev-pr-auto-merge.mjs +25 -11
- package/scripts/dev-pr-delivery-warrant.mjs +44 -0
- package/scripts/dev-pr-prequeue-guard.mjs +0 -18
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/generate-agent-change-map.mjs +118 -0
- package/scripts/generate-release-candidate-passport.mjs +26 -1
- package/scripts/generate-site-bundle.mjs +6 -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-routing-evidence.mjs +35 -6
- package/scripts/release-candidate-resolver.mjs +44 -0
- package/scripts/release-line-policy.mjs +25 -15
- package/scripts/release-tail.mjs +19 -2
- package/scripts/resume-from-candidate-run.mjs +106 -103
- package/scripts/run-lifecycle-core.mjs +341 -376
- package/scripts/site-capability-metadata.mjs +4 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v3-v4-capability-catalog.mjs +439 -0
- package/scripts/v4-cross-platform-adopter-qualification.mjs +228 -0
- package/scripts/v4-declarative-promotion-admission.mjs +42 -0
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import {
|
|
8
|
+
collectHistoryRows,
|
|
9
|
+
collectRevisionCatalog,
|
|
10
|
+
countsBy,
|
|
11
|
+
git,
|
|
12
|
+
sha256,
|
|
13
|
+
} from "./v3-v4-capability-catalog.mjs";
|
|
14
|
+
|
|
15
|
+
export const V3_V4_CAPABILITY_INVENTORY_CONTRACT =
|
|
16
|
+
"kungfu-buildchain-v3-v4-live-capability-inventory";
|
|
17
|
+
export const V3_V4_CAPABILITY_CUTS = Object.freeze({
|
|
18
|
+
priorFamilyV3: "6b96bdad8d9f8ccf9275f27d9370a226a9c78465",
|
|
19
|
+
liveV3: "8493bf140a7f567e76aff3119f3d39ff026afc84",
|
|
20
|
+
liveV4: "11b4ab21405cdffcc9d6dc10cd947618d9d1bf9c",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const INVENTORY_PATH = "architecture/v3-v4-live-capability-inventory.json";
|
|
24
|
+
const ALLOWED_DISPOSITIONS = new Set([
|
|
25
|
+
"v4-native",
|
|
26
|
+
"compatibility-adapter",
|
|
27
|
+
"executable-migration",
|
|
28
|
+
"owned-missing",
|
|
29
|
+
]);
|
|
30
|
+
const ASSIGNMENTS = Object.freeze({
|
|
31
|
+
public: "2026-08-26-buildchain-v4-public-surface-compatibility-closure",
|
|
32
|
+
runtime: "2026-08-26-buildchain-v4-runtime-semantic-closure",
|
|
33
|
+
adopter: "2026-08-26-buildchain-v4-cross-platform-adopter-qualification",
|
|
34
|
+
stable: "2026-08-26-buildchain-v4-stable-channel-capability-closure",
|
|
35
|
+
retirement: "2026-08-26-buildchain-v4-v3-retirement-reconciliation",
|
|
36
|
+
});
|
|
37
|
+
const REQUIRED_CATEGORIES = Object.freeze([
|
|
38
|
+
"action",
|
|
39
|
+
"action-input",
|
|
40
|
+
"cli-command",
|
|
41
|
+
"cli-option",
|
|
42
|
+
"config-contract",
|
|
43
|
+
"documented-module",
|
|
44
|
+
"generated-contract",
|
|
45
|
+
"history-change",
|
|
46
|
+
"node-export",
|
|
47
|
+
"node-symbol",
|
|
48
|
+
"observable-evidence-contract",
|
|
49
|
+
"platform-branch",
|
|
50
|
+
"release-delivery-recovery",
|
|
51
|
+
"source-schema",
|
|
52
|
+
"workflow",
|
|
53
|
+
"workflow-input",
|
|
54
|
+
"workflow-output",
|
|
55
|
+
"workflow-secret",
|
|
56
|
+
]);
|
|
57
|
+
const MIGRATION_ROUTES = new Map([
|
|
58
|
+
[
|
|
59
|
+
"source-schema:contracts/publication-rehearsal-capsule-v1.schema.json",
|
|
60
|
+
"source-schema:contracts/v4-publication-rehearsal-capsule-v1.schema.json",
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
"generated-contract:dist/site/schemas/publication-rehearsal-capsule-v1.schema.json",
|
|
64
|
+
"generated-contract:dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json",
|
|
65
|
+
],
|
|
66
|
+
[
|
|
67
|
+
"generated-contract:dist/site/schemas/release-tail-provider-bindings-v1.schema.json",
|
|
68
|
+
"source-schema:contracts/release-tail-provider-bindings-v1.schema.json",
|
|
69
|
+
],
|
|
70
|
+
]);
|
|
71
|
+
const HISTORY_MIGRATION_PATHS = new Map([
|
|
72
|
+
[
|
|
73
|
+
"actions/promote-buildchain-ref/internal/alpha-publication-recovery.js",
|
|
74
|
+
"actions/promote-buildchain-ref/internal/promote-alpha-channel.js",
|
|
75
|
+
],
|
|
76
|
+
["scripts/release-candidate-tail-reseal.mjs", "scripts/v4-tail-reseal.mjs"],
|
|
77
|
+
[
|
|
78
|
+
"tests/release-candidate-tail-reseal.test.mjs",
|
|
79
|
+
"tests/v4-tail-reseal-parity.test.mjs",
|
|
80
|
+
],
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
function capabilityCutAvailable(root, revision) {
|
|
84
|
+
try {
|
|
85
|
+
execFileSync("git", ["cat-file", "-e", `${revision}^{commit}`], {
|
|
86
|
+
cwd: root,
|
|
87
|
+
stdio: "ignore",
|
|
88
|
+
});
|
|
89
|
+
return true;
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function assertCapabilityCutAncestor({
|
|
96
|
+
root = process.cwd(),
|
|
97
|
+
revision,
|
|
98
|
+
descendant = "HEAD",
|
|
99
|
+
label = "capability cut",
|
|
100
|
+
} = {}) {
|
|
101
|
+
try {
|
|
102
|
+
execFileSync("git", ["merge-base", "--is-ancestor", revision, descendant], {
|
|
103
|
+
cwd: root,
|
|
104
|
+
stdio: "ignore",
|
|
105
|
+
});
|
|
106
|
+
} catch {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`${label} ${revision} must be an ancestor of ${descendant}; regenerate the cut after rebasing instead of relying on a retained local object`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function repositoryIsShallow(root) {
|
|
114
|
+
return (
|
|
115
|
+
execFileSync("git", ["rev-parse", "--is-shallow-repository"], {
|
|
116
|
+
cwd: root,
|
|
117
|
+
encoding: "utf8",
|
|
118
|
+
}).trim() === "true"
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function ensureCapabilityCutAncestor({
|
|
123
|
+
root = process.cwd(),
|
|
124
|
+
revision,
|
|
125
|
+
descendant = "HEAD",
|
|
126
|
+
label = "capability cut",
|
|
127
|
+
} = {}) {
|
|
128
|
+
try {
|
|
129
|
+
assertCapabilityCutAncestor({ root, revision, descendant, label });
|
|
130
|
+
return;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if (!repositoryIsShallow(root)) throw error;
|
|
133
|
+
}
|
|
134
|
+
const descendantCommit = execFileSync(
|
|
135
|
+
"git",
|
|
136
|
+
["rev-parse", `${descendant}^{commit}`],
|
|
137
|
+
{ cwd: root, encoding: "utf8" },
|
|
138
|
+
).trim();
|
|
139
|
+
try {
|
|
140
|
+
execFileSync(
|
|
141
|
+
"git",
|
|
142
|
+
["fetch", "--no-tags", "--depth=128", "origin", descendantCommit],
|
|
143
|
+
{ cwd: root, stdio: "ignore" },
|
|
144
|
+
);
|
|
145
|
+
} catch {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`${label} ${revision} ancestry could not be hydrated from ${descendantCommit} through a bounded origin fetch`,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
assertCapabilityCutAncestor({ root, revision, descendant, label });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function ensureCapabilityCutsAvailable(root) {
|
|
154
|
+
const revisions = Object.values(V3_V4_CAPABILITY_CUTS);
|
|
155
|
+
const missing = revisions.filter(
|
|
156
|
+
(revision) => !capabilityCutAvailable(root, revision),
|
|
157
|
+
);
|
|
158
|
+
if (missing.length) {
|
|
159
|
+
execFileSync(
|
|
160
|
+
"git",
|
|
161
|
+
["fetch", "--no-tags", "--depth=128", "origin", ...missing],
|
|
162
|
+
{ cwd: root, stdio: "ignore" },
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
const unavailable = revisions.filter(
|
|
166
|
+
(revision) => !capabilityCutAvailable(root, revision),
|
|
167
|
+
);
|
|
168
|
+
if (unavailable.length) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`v3/v4 capability cuts are unavailable after a bounded origin fetch: ${unavailable.join(", ")}`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
ensureCapabilityCutAncestor({
|
|
174
|
+
root,
|
|
175
|
+
revision: V3_V4_CAPABILITY_CUTS.liveV4,
|
|
176
|
+
label: "live v4 capability cut",
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
const RUNTIME_SYMBOLS = Object.freeze([
|
|
180
|
+
"createPublicationRehearsalCapsule",
|
|
181
|
+
"executePublicationRehearsal",
|
|
182
|
+
"normalizePublicationRehearsalCapsule",
|
|
183
|
+
"PUBLICATION_REHEARSAL_CAPSULE_CONTRACT",
|
|
184
|
+
"PUBLICATION_REHEARSAL_COMMAND",
|
|
185
|
+
"PUBLICATION_REHEARSAL_DIAGNOSTIC_CONTRACT",
|
|
186
|
+
"PUBLICATION_REHEARSAL_EVIDENCE_CONTRACT",
|
|
187
|
+
"publicationRehearsalBindingRoot",
|
|
188
|
+
"publicationRehearsalDiagnostic",
|
|
189
|
+
"PublicationRehearsalError",
|
|
190
|
+
"RELEASE_LOCAL_CONSTRUCTIBILITY_ADR",
|
|
191
|
+
"RELEASE_LOCAL_CONSTRUCTIBILITY_INVARIANT",
|
|
192
|
+
"resolvePublicationRehearsalFile",
|
|
193
|
+
"verifyPublicationRehearsalCapsule",
|
|
194
|
+
]);
|
|
195
|
+
const PROJECTION_SYMBOLS = Object.freeze([
|
|
196
|
+
"appendPublicationRehearsalToml",
|
|
197
|
+
"assertPublicationRehearsalConfig",
|
|
198
|
+
"mergePublicationRehearsalAgentInstructions",
|
|
199
|
+
"projectPublicationRehearsalToml",
|
|
200
|
+
"PUBLICATION_REHEARSAL_AGENT_SECTION_END",
|
|
201
|
+
"PUBLICATION_REHEARSAL_AGENT_SECTION_START",
|
|
202
|
+
"PUBLICATION_REHEARSAL_WORKFLOW_PATH",
|
|
203
|
+
"publicationRehearsalAgentInstructions",
|
|
204
|
+
"publicationRehearsalToml",
|
|
205
|
+
"publicationRehearsalWorkflow",
|
|
206
|
+
]);
|
|
207
|
+
const PUBLIC_SURFACE_CLOSURE_IDS = new Set([
|
|
208
|
+
"documented-module:./publication-rehearsal-projection",
|
|
209
|
+
"documented-module:./publication-rehearsal-runtime",
|
|
210
|
+
"node-export:./publication-rehearsal-projection",
|
|
211
|
+
"node-export:./publication-rehearsal-runtime",
|
|
212
|
+
"node-symbol:./release-passport#releasePassportKfdAdopterSourceSha",
|
|
213
|
+
...RUNTIME_SYMBOLS.map(
|
|
214
|
+
(name) => `node-symbol:./publication-rehearsal-runtime#${name}`,
|
|
215
|
+
),
|
|
216
|
+
...PROJECTION_SYMBOLS.map(
|
|
217
|
+
(name) => `node-symbol:./publication-rehearsal-projection#${name}`,
|
|
218
|
+
),
|
|
219
|
+
"cli-option:collect-github-release#--adopter-delivery-json",
|
|
220
|
+
"cli-option:release-tail#--capsule-root",
|
|
221
|
+
"cli-option:release-tail#--environment-json",
|
|
222
|
+
"workflow-input:.build#buildchain-contract-expected-channel",
|
|
223
|
+
"workflow-input:.build#buildchain-contract-expected-major",
|
|
224
|
+
"workflow-input:.release-candidate-promote#release-passport-adopter-delivery-json",
|
|
225
|
+
"workflow-input:.release-candidate-promote#release-passport-kfd-adopter-manifest-gate-json",
|
|
226
|
+
"workflow-input:.web-surface#buildchain-contract-expected-channel",
|
|
227
|
+
"workflow-input:.web-surface#buildchain-contract-expected-major",
|
|
228
|
+
"workflow-input:dev-pr-auto-merge#defer-landing",
|
|
229
|
+
"workflow-input:release-candidate-promote#release-passport-adopter-delivery-json",
|
|
230
|
+
"workflow-input:release-candidate-promote#release-passport-kfd-adopter-manifest-gate-json",
|
|
231
|
+
"workflow-input:release-tail#capsule-contract",
|
|
232
|
+
"workflow-input:release-tail#capsule-path",
|
|
233
|
+
"workflow-input:release-tail#capsule-root",
|
|
234
|
+
"workflow-input:release-tail#evidence-path",
|
|
235
|
+
"workflow-output:release-tail#binding-root",
|
|
236
|
+
"workflow-output:release-tail#evidence-root",
|
|
237
|
+
"action-input:promote-buildchain-ref#plan-before-target-advance",
|
|
238
|
+
"action-input:promote-buildchain-ref#release-passport-adopter-delivery-json",
|
|
239
|
+
"action-input:promote-buildchain-ref#release-passport-kfd-adopter-manifest-gate-json",
|
|
240
|
+
"action-input:release-tail#capsule",
|
|
241
|
+
"action-input:release-tail#capsule-contract",
|
|
242
|
+
"action-input:release-tail#capsule-root",
|
|
243
|
+
"action-input:release-tail#evidence-path",
|
|
244
|
+
]);
|
|
245
|
+
|
|
246
|
+
function stableJson(value) {
|
|
247
|
+
if (Array.isArray(value)) return value.map(stableJson);
|
|
248
|
+
if (!value || typeof value !== "object") return value;
|
|
249
|
+
return Object.fromEntries(
|
|
250
|
+
Object.entries(value)
|
|
251
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
252
|
+
.map(([key, entry]) => [key, stableJson(entry)]),
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function canonicalInventoryJson(value) {
|
|
257
|
+
return `${JSON.stringify(stableJson(value), null, 2)}\n`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function ownerForCategory(category, identity) {
|
|
261
|
+
const publicCategories = new Set([
|
|
262
|
+
"action",
|
|
263
|
+
"action-input",
|
|
264
|
+
"cli-command",
|
|
265
|
+
"cli-option",
|
|
266
|
+
"documented-module",
|
|
267
|
+
"node-export",
|
|
268
|
+
"node-symbol",
|
|
269
|
+
"workflow",
|
|
270
|
+
"workflow-input",
|
|
271
|
+
"workflow-output",
|
|
272
|
+
"workflow-secret",
|
|
273
|
+
]);
|
|
274
|
+
if (publicCategories.has(category)) return ASSIGNMENTS.public;
|
|
275
|
+
if (category === "platform-branch") return ASSIGNMENTS.adopter;
|
|
276
|
+
if (["generated-contract", "observable-evidence-contract"].includes(category))
|
|
277
|
+
return ASSIGNMENTS.stable;
|
|
278
|
+
if (category === "history-change") return ASSIGNMENTS.retirement;
|
|
279
|
+
if (category === "release-delivery-recovery") return ASSIGNMENTS.runtime;
|
|
280
|
+
if (
|
|
281
|
+
category === "source-schema" &&
|
|
282
|
+
/release|publication|passport/u.test(identity)
|
|
283
|
+
)
|
|
284
|
+
return ASSIGNMENTS.stable;
|
|
285
|
+
return ASSIGNMENTS.runtime;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function rowFromCatalogEntry(source, targetCatalog) {
|
|
289
|
+
const ownerAssignment = ownerForCategory(source.category, source.identity);
|
|
290
|
+
const direct = targetCatalog.get(source.id);
|
|
291
|
+
const migratedId = MIGRATION_ROUTES.get(source.id);
|
|
292
|
+
const migrated = migratedId ? targetCatalog.get(migratedId) : undefined;
|
|
293
|
+
let disposition;
|
|
294
|
+
let v4Route = null;
|
|
295
|
+
let residual = null;
|
|
296
|
+
if (direct) {
|
|
297
|
+
disposition = "v4-native";
|
|
298
|
+
v4Route = { capabilityId: direct.id, evidence: direct.evidence };
|
|
299
|
+
} else if (migrated) {
|
|
300
|
+
disposition = "executable-migration";
|
|
301
|
+
v4Route = { capabilityId: migrated.id, evidence: migrated.evidence };
|
|
302
|
+
} else if (PUBLIC_SURFACE_CLOSURE_IDS.has(source.id)) {
|
|
303
|
+
disposition = "owned-missing";
|
|
304
|
+
residual = {
|
|
305
|
+
ownerAssignment,
|
|
306
|
+
requiredOutcome:
|
|
307
|
+
"add an executable v4 compatibility route or an explicit source-bound migration",
|
|
308
|
+
};
|
|
309
|
+
} else {
|
|
310
|
+
throw new Error(`unclassified live v3 capability: ${source.id}`);
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
id: source.id,
|
|
314
|
+
category: source.category,
|
|
315
|
+
disposition,
|
|
316
|
+
ownerAssignment,
|
|
317
|
+
sourceEvidence: source.evidence,
|
|
318
|
+
v4Route,
|
|
319
|
+
residual,
|
|
320
|
+
positiveProbe:
|
|
321
|
+
disposition === "owned-missing"
|
|
322
|
+
? "residual identity and owner match the closed expected-residual set"
|
|
323
|
+
: "the exact v4 cut contains the declared route evidence",
|
|
324
|
+
negativeProbe:
|
|
325
|
+
disposition === "owned-missing"
|
|
326
|
+
? "removing the owner or changing the residual identity fails validation"
|
|
327
|
+
: "removing or substituting the declared v4 route fails validation",
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function sourceCut(root, commit, role) {
|
|
332
|
+
return {
|
|
333
|
+
branch: "origin/dev/v3/v3.0",
|
|
334
|
+
commit,
|
|
335
|
+
tree: git(root, ["rev-parse", `${commit}^{tree}`]),
|
|
336
|
+
role,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export function buildV3V4CapabilityInventory({ root = process.cwd() } = {}) {
|
|
341
|
+
ensureCapabilityCutsAvailable(root);
|
|
342
|
+
git(root, [
|
|
343
|
+
"merge-base",
|
|
344
|
+
"--is-ancestor",
|
|
345
|
+
V3_V4_CAPABILITY_CUTS.priorFamilyV3,
|
|
346
|
+
V3_V4_CAPABILITY_CUTS.liveV3,
|
|
347
|
+
]);
|
|
348
|
+
const v3 = collectRevisionCatalog({
|
|
349
|
+
root,
|
|
350
|
+
revision: V3_V4_CAPABILITY_CUTS.liveV3,
|
|
351
|
+
liveV3Revision: V3_V4_CAPABILITY_CUTS.liveV3,
|
|
352
|
+
});
|
|
353
|
+
const v4 = collectRevisionCatalog({
|
|
354
|
+
root,
|
|
355
|
+
revision: V3_V4_CAPABILITY_CUTS.liveV4,
|
|
356
|
+
liveV3Revision: V3_V4_CAPABILITY_CUTS.liveV3,
|
|
357
|
+
});
|
|
358
|
+
const rows = [
|
|
359
|
+
...[...v3.catalog.values()].map((entry) =>
|
|
360
|
+
rowFromCatalogEntry(entry, v4.catalog),
|
|
361
|
+
),
|
|
362
|
+
...collectHistoryRows({
|
|
363
|
+
root,
|
|
364
|
+
priorRevision: V3_V4_CAPABILITY_CUTS.priorFamilyV3,
|
|
365
|
+
liveV3Revision: V3_V4_CAPABILITY_CUTS.liveV3,
|
|
366
|
+
v4Paths: v4.paths,
|
|
367
|
+
migrationPaths: HISTORY_MIGRATION_PATHS,
|
|
368
|
+
ownerAssignment: ASSIGNMENTS.retirement,
|
|
369
|
+
}),
|
|
370
|
+
].sort((left, right) => left.id.localeCompare(right.id));
|
|
371
|
+
const changedPaths = rows.filter((row) => row.category === "history-change");
|
|
372
|
+
const prior = sourceCut(
|
|
373
|
+
root,
|
|
374
|
+
V3_V4_CAPABILITY_CUTS.priorFamilyV3,
|
|
375
|
+
"previous-absorption-family-capture",
|
|
376
|
+
);
|
|
377
|
+
const liveV3 = sourceCut(
|
|
378
|
+
root,
|
|
379
|
+
V3_V4_CAPABILITY_CUTS.liveV3,
|
|
380
|
+
"execution-time-protected-read-only-reference",
|
|
381
|
+
);
|
|
382
|
+
const liveV4 = sourceCut(
|
|
383
|
+
root,
|
|
384
|
+
V3_V4_CAPABILITY_CUTS.liveV4,
|
|
385
|
+
"execution-time-protected-target",
|
|
386
|
+
);
|
|
387
|
+
liveV4.branch = "origin/dev/v4/v4.0";
|
|
388
|
+
const inventory = {
|
|
389
|
+
schemaVersion: 1,
|
|
390
|
+
contract: V3_V4_CAPABILITY_INVENTORY_CONTRACT,
|
|
391
|
+
sourceCuts: { priorFamilyV3: prior, liveV3, liveV4 },
|
|
392
|
+
extraction: {
|
|
393
|
+
mode: "exact-git-object-closed-world",
|
|
394
|
+
extractor: "scripts/check-v3-v4-capability-inventory.mjs",
|
|
395
|
+
categories: REQUIRED_CATEGORIES,
|
|
396
|
+
sources: [
|
|
397
|
+
"package.json#exports",
|
|
398
|
+
"dist/site/node-api-registry.json",
|
|
399
|
+
"dist/site/cli-registry.json",
|
|
400
|
+
"dist/site/workflow-registry.json",
|
|
401
|
+
"contracts/**/*.schema.json",
|
|
402
|
+
".buildchain/*.{json,toml}",
|
|
403
|
+
"dist/site/schemas/*.schema.json",
|
|
404
|
+
"architecture/v3-core-mechanism-inventory.json",
|
|
405
|
+
"architecture/v4-runtime-semantic-closure.json",
|
|
406
|
+
".github/workflows/*.{yml,yaml}",
|
|
407
|
+
"git diff <prior-family-v3>..<live-v3>",
|
|
408
|
+
],
|
|
409
|
+
unknownPolicy: "fail-closed",
|
|
410
|
+
residualPolicy: "exact-identity-allowlist-with-required-owner",
|
|
411
|
+
},
|
|
412
|
+
reverseHistory: {
|
|
413
|
+
fromCommit: V3_V4_CAPABILITY_CUTS.priorFamilyV3,
|
|
414
|
+
toCommit: V3_V4_CAPABILITY_CUTS.liveV3,
|
|
415
|
+
commitCount: Number(
|
|
416
|
+
git(root, [
|
|
417
|
+
"rev-list",
|
|
418
|
+
"--count",
|
|
419
|
+
`${V3_V4_CAPABILITY_CUTS.priorFamilyV3}..${V3_V4_CAPABILITY_CUTS.liveV3}`,
|
|
420
|
+
]),
|
|
421
|
+
),
|
|
422
|
+
changedPathCount: changedPaths.length,
|
|
423
|
+
changedPathSetRoot: sha256(
|
|
424
|
+
changedPaths.map((row) => row.sourceEvidence.path).join("\n"),
|
|
425
|
+
),
|
|
426
|
+
unclassifiedPathCount: 0,
|
|
427
|
+
},
|
|
428
|
+
summary: {
|
|
429
|
+
capabilityCount: rows.length,
|
|
430
|
+
categoryCounts: countsBy(rows, "category"),
|
|
431
|
+
dispositionCounts: countsBy(rows, "disposition"),
|
|
432
|
+
residualCount: rows.filter((row) => row.disposition === "owned-missing")
|
|
433
|
+
.length,
|
|
434
|
+
unknownCount: 0,
|
|
435
|
+
unownedCount: 0,
|
|
436
|
+
},
|
|
437
|
+
capabilities: rows,
|
|
438
|
+
};
|
|
439
|
+
assertV3V4CapabilityInventory(inventory);
|
|
440
|
+
return inventory;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function validateRoute(row, issues) {
|
|
444
|
+
if (row.disposition === "owned-missing") {
|
|
445
|
+
if (!PUBLIC_SURFACE_CLOSURE_IDS.has(row.id))
|
|
446
|
+
issues.push(`${row.id}: residual is not in the exact expected set`);
|
|
447
|
+
if (!row?.residual?.ownerAssignment || row.v4Route)
|
|
448
|
+
issues.push(`${row.id}: owned residual binding is invalid`);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (
|
|
452
|
+
!row?.v4Route?.capabilityId ||
|
|
453
|
+
!row?.v4Route?.evidence?.path ||
|
|
454
|
+
row.residual
|
|
455
|
+
) {
|
|
456
|
+
issues.push(
|
|
457
|
+
`${row.id}: executable v4 route is missing or conflicts with a residual`,
|
|
458
|
+
);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const historyPath =
|
|
462
|
+
row.category === "history-change" ? row.sourceEvidence?.path : "";
|
|
463
|
+
const expectedRouteId = historyPath
|
|
464
|
+
? `path:${HISTORY_MIGRATION_PATHS.get(historyPath) || historyPath}`
|
|
465
|
+
: MIGRATION_ROUTES.get(row.id) || row.id;
|
|
466
|
+
if (row.v4Route.capabilityId !== expectedRouteId)
|
|
467
|
+
issues.push(
|
|
468
|
+
`${row.id}: v4 route identity does not match the exact direct or migration policy`,
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function validateRow(row, ids, issues) {
|
|
473
|
+
if (!row?.id || ids.has(row.id))
|
|
474
|
+
issues.push(
|
|
475
|
+
`capability id is missing or duplicated: ${row?.id || "<empty>"}`,
|
|
476
|
+
);
|
|
477
|
+
ids.add(row?.id);
|
|
478
|
+
if (!REQUIRED_CATEGORIES.includes(row?.category))
|
|
479
|
+
issues.push(`${row?.id}: category is unknown`);
|
|
480
|
+
if (!ALLOWED_DISPOSITIONS.has(row?.disposition))
|
|
481
|
+
issues.push(`${row?.id}: disposition is unknown`);
|
|
482
|
+
if (!Object.values(ASSIGNMENTS).includes(row?.ownerAssignment))
|
|
483
|
+
issues.push(`${row?.id}: owner Assignment is missing or unknown`);
|
|
484
|
+
if (!row?.sourceEvidence?.path || !row?.sourceEvidence?.selector)
|
|
485
|
+
issues.push(`${row?.id}: source evidence is incomplete`);
|
|
486
|
+
if (!row?.positiveProbe || !row?.negativeProbe)
|
|
487
|
+
issues.push(`${row?.id}: positive and negative probes are required`);
|
|
488
|
+
validateRoute(row, issues);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function validateCoverage(inventory, rows, issues) {
|
|
492
|
+
for (const category of REQUIRED_CATEGORIES) {
|
|
493
|
+
if (!rows.some((row) => row.category === category))
|
|
494
|
+
issues.push(`required category has zero coverage: ${category}`);
|
|
495
|
+
}
|
|
496
|
+
const actualResiduals = new Set(
|
|
497
|
+
rows
|
|
498
|
+
.filter((row) => row.disposition === "owned-missing")
|
|
499
|
+
.map((row) => row.id),
|
|
500
|
+
);
|
|
501
|
+
for (const id of PUBLIC_SURFACE_CLOSURE_IDS) {
|
|
502
|
+
const row = rows.find((entry) => entry.id === id);
|
|
503
|
+
if (!row) issues.push(`public-surface closure identity is absent: ${id}`);
|
|
504
|
+
else if (actualResiduals.has(id))
|
|
505
|
+
issues.push(`public-surface capability remains unresolved: ${id}`);
|
|
506
|
+
}
|
|
507
|
+
if (
|
|
508
|
+
inventory?.reverseHistory?.fromCommit !==
|
|
509
|
+
V3_V4_CAPABILITY_CUTS.priorFamilyV3 ||
|
|
510
|
+
inventory?.reverseHistory?.toCommit !== V3_V4_CAPABILITY_CUTS.liveV3 ||
|
|
511
|
+
inventory?.reverseHistory?.unclassifiedPathCount !== 0
|
|
512
|
+
) {
|
|
513
|
+
issues.push(
|
|
514
|
+
"reverse history does not close the exact prior-family-to-live-v3 range",
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
if (
|
|
518
|
+
inventory?.summary?.unknownCount !== 0 ||
|
|
519
|
+
inventory?.summary?.unownedCount !== 0
|
|
520
|
+
) {
|
|
521
|
+
issues.push("inventory summary contains unknown or unowned capabilities");
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export function assertV3V4CapabilityInventory(inventory) {
|
|
526
|
+
const issues = [];
|
|
527
|
+
if (
|
|
528
|
+
inventory?.schemaVersion !== 1 ||
|
|
529
|
+
inventory?.contract !== V3_V4_CAPABILITY_INVENTORY_CONTRACT
|
|
530
|
+
) {
|
|
531
|
+
issues.push("inventory contract or schemaVersion is invalid");
|
|
532
|
+
}
|
|
533
|
+
const rows = Array.isArray(inventory?.capabilities)
|
|
534
|
+
? inventory.capabilities
|
|
535
|
+
: [];
|
|
536
|
+
const ids = new Set();
|
|
537
|
+
for (const row of rows) validateRow(row, ids, issues);
|
|
538
|
+
validateCoverage(inventory, rows, issues);
|
|
539
|
+
if (issues.length)
|
|
540
|
+
throw new Error(
|
|
541
|
+
`v3-to-v4 capability inventory failed:\n- ${issues.join("\n- ")}`,
|
|
542
|
+
);
|
|
543
|
+
return inventory;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function main() {
|
|
547
|
+
const root = process.cwd();
|
|
548
|
+
const inventory = buildV3V4CapabilityInventory({ root });
|
|
549
|
+
const expected = canonicalInventoryJson(inventory);
|
|
550
|
+
if (process.argv.includes("--write")) {
|
|
551
|
+
fs.writeFileSync(path.join(root, INVENTORY_PATH), expected);
|
|
552
|
+
console.log(
|
|
553
|
+
`wrote ${INVENTORY_PATH}: ${inventory.summary.capabilityCount} capabilities`,
|
|
554
|
+
);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
const actual = fs.readFileSync(path.join(root, INVENTORY_PATH), "utf8");
|
|
558
|
+
if (actual !== expected)
|
|
559
|
+
throw new Error(
|
|
560
|
+
`${INVENTORY_PATH} is stale; run node scripts/check-v3-v4-capability-inventory.mjs --write`,
|
|
561
|
+
);
|
|
562
|
+
console.log(
|
|
563
|
+
`v3-to-v4 capability inventory passed: ${inventory.summary.capabilityCount} capabilities, ` +
|
|
564
|
+
`${inventory.reverseHistory.changedPathCount} reverse-history paths, ` +
|
|
565
|
+
`${inventory.summary.residualCount} owned residuals, 0 unknown, 0 unowned`,
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (
|
|
570
|
+
process.argv[1] &&
|
|
571
|
+
import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href
|
|
572
|
+
) {
|
|
573
|
+
main();
|
|
574
|
+
}
|
|
@@ -126,10 +126,17 @@ export function checkV4FloatingConsumerPolicyContract() {
|
|
|
126
126
|
"Enforce v4 floating consumer policy",
|
|
127
127
|
"Validate consumer package manager contract",
|
|
128
128
|
]);
|
|
129
|
+
const buildWorkflow = read(".github/workflows/.build.yml");
|
|
129
130
|
if (
|
|
130
|
-
!
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
!buildWorkflow.includes("Normalize v3 expected identity aliases") ||
|
|
132
|
+
!buildWorkflow.includes(
|
|
133
|
+
"buildchain-contract-expected-channel conflicts with buildchain-expected-channel",
|
|
134
|
+
) ||
|
|
135
|
+
!buildWorkflow.includes(
|
|
136
|
+
"BUILDCHAIN_EXPECTED_INVOCATION_CHANNEL: ${{ steps.expected-identity.outputs.expected-channel }}",
|
|
137
|
+
) ||
|
|
138
|
+
buildWorkflow.indexOf("Normalize v3 expected identity aliases") >
|
|
139
|
+
buildWorkflow.indexOf("Enforce v4 floating consumer policy")
|
|
133
140
|
)
|
|
134
141
|
fail("channel builds must disambiguate dual-channel caller invocations");
|
|
135
142
|
assertTrustGatedJobs(read(".github/workflows/.build.yml"), [
|