@kungfu-tech/buildchain 4.0.2-alpha.9 → 4.0.2

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.
Files changed (157) hide show
  1. package/AGENTS.md +10 -1
  2. package/CONTRIBUTING.md +24 -0
  3. package/README.md +2 -2
  4. package/actions/promote-buildchain-ref/README.md +1 -1
  5. package/actions/v4-release-candidate-promote/README.md +28 -0
  6. package/architecture/agent-change-map.md +64 -7
  7. package/architecture/ci-lane-change-budget.json +366 -0
  8. package/architecture/internal-capabilities.json +53 -8
  9. package/architecture/maintainability-debt.json +147 -93
  10. package/architecture/maintainability-policy.json +32 -28
  11. package/architecture/release-tail-contract-inventory.json +6 -6
  12. package/architecture/v4-architecture-constitution.md +23 -20
  13. package/architecture/v4-cross-platform-adopter-qualification.json +1 -1
  14. package/architecture/v4-delivery-authority-parity.json +2 -2
  15. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +1 -1
  16. package/architecture/v4-floating-consumer-policy.json +2 -2
  17. package/architecture/v4-next-development-parity.json +5 -5
  18. package/architecture/v4-partial-mutation-recovery-qualification.json +4 -1
  19. package/architecture/v4-platform-stage-checkpoints.json +1 -1
  20. package/architecture/v4-provider-operation-journal-contract.json +5 -2
  21. package/architecture/v4-publication-facade-patches.json +64 -0
  22. package/architecture/v4-release-activation-shadow-domain.json +5 -2
  23. package/architecture/v4-release-topology.json +74 -39
  24. package/architecture/v4-rust-wasm-production-authority.json +58 -0
  25. package/architecture/v4-stable-publication-fence.json +5 -2
  26. package/architecture/v4-stage-capsule-qualification.json +2 -2
  27. package/architecture/v4-tail-reseal-parity.json +8 -6
  28. package/architecture/v4-universal-workflow-bootstrap.json +171 -0
  29. package/architecture/v4-universal-workflow-fault-campaign.json +72 -0
  30. package/architecture/v4-universal-workflow-train-admission.json +40 -0
  31. package/architecture/workflow-taxonomy.json +1451 -0
  32. package/bin/buildchain.mjs +7 -7
  33. package/contracts/buildchain-v2-residuals-v1.json +57 -48
  34. package/contracts/dev-delivery-authority-v2.schema.json +16 -2
  35. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  36. package/contracts/v4-canonical-contracts-v1.schema.json +1 -0
  37. package/dist/site/buildchain-contract.json +93 -48
  38. package/dist/site/buildchain-site.json +222 -69
  39. package/dist/site/capability-registry.json +4 -4
  40. package/dist/site/controller-registry.json +50 -13
  41. package/dist/site/kfd-claims.json +2585 -1000
  42. package/dist/site/kfd-upstream-aggregate.json +1 -1
  43. package/dist/site/manual-registry.json +13 -13
  44. package/dist/site/node-api-registry.json +120 -115
  45. package/dist/site/page-registry.json +204 -51
  46. package/dist/site/public-surface-audit.json +3205 -1219
  47. package/dist/site/publication-authority-registry.json +1124 -362
  48. package/dist/site/publication-registry.json +4 -4
  49. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +16 -2
  50. package/dist/site/site-manifest.json +17 -17
  51. package/dist/site/workflow-registry.json +5061 -1659
  52. package/docs/MAP.md +1 -0
  53. package/docs/auditable-demo.md +1 -1
  54. package/docs/cli.md +21 -8
  55. package/docs/dev-delivery-warrant.md +1 -1
  56. package/docs/engineering-housekeeper.md +3 -3
  57. package/docs/kfd-support.md +26 -2
  58. package/docs/next-development-transition.md +2 -2
  59. package/docs/node-api-reference.md +190 -190
  60. package/docs/publication-authority.md +2 -2
  61. package/docs/release-candidate.md +3 -3
  62. package/docs/release-flow.md +67 -19
  63. package/docs/release-governance.md +8 -8
  64. package/docs/release-passport.md +27 -2
  65. package/docs/reusable-build-surface.md +16 -0
  66. package/docs/v4-adopter-delivery.md +2 -2
  67. package/docs/v4-production-release.md +71 -8
  68. package/docs/v4-rust-wasm-production-authority.md +58 -0
  69. package/docs/v4-stage-capsule.md +2 -2
  70. package/docs/v4-tail-reseal.md +1 -1
  71. package/docs/workflow-catalog.md +154 -0
  72. package/docs/workflow-path-migration.md +109 -0
  73. package/package.json +10 -6
  74. package/packages/core/artifact-verification-envelope.js +5 -5
  75. package/packages/core/buildchain-contract.js +1 -1
  76. package/packages/core/buildchain-kfd-claims.js +2 -2
  77. package/packages/core/buildchain-publication-authority.js +42 -26
  78. package/packages/core/buildchain-v4-domain.wasm +0 -0
  79. package/packages/core/ci-lane-change-budget.js +14 -1
  80. package/packages/core/controller-evidence.js +4 -1
  81. package/packages/core/dev-delivery-candidate-identity.js +40 -6
  82. package/packages/core/dev-delivery-warrant-state.js +3 -4
  83. package/packages/core/dev-delivery-warrant.js +12 -2
  84. package/packages/core/github-governance-authority.js +4 -4
  85. package/packages/core/kfd-adopter-manifest.js +46 -47
  86. package/packages/core/next-development-projection.js +2 -2
  87. package/packages/core/paper-agent-entry.js +30 -4
  88. package/packages/core/paper-fleet.js +1 -1
  89. package/packages/core/paper-runtime-channels.js +348 -0
  90. package/packages/core/paper-scaffold-content.js +10 -0
  91. package/packages/core/paper.js +37 -71
  92. package/packages/core/release-line-dry-run.js +1 -1
  93. package/packages/core/release-passport-contract.js +5 -5
  94. package/packages/core/release-passport.js +5 -4
  95. package/packages/core/release-tail-provider-plane.js +68 -1048
  96. package/packages/core/v4-canonical-contracts.js +10 -75
  97. package/packages/core/v4-domain-wasm-artifact.js +7 -0
  98. package/packages/core/v4-domain-wasm.js +216 -0
  99. package/packages/core/v4-floating-consumer-evidence.js +1 -1
  100. package/packages/core/v4-floating-consumer-policy.js +1 -1
  101. package/packages/core/v4-partial-mutation-recovery-qualification.js +9 -386
  102. package/packages/core/v4-product-publication.js +19 -349
  103. package/packages/core/v4-protected-publication-source.js +19 -21
  104. package/packages/core/v4-provider-operation-journal.js +18 -487
  105. package/packages/core/v4-provider-readback-idempotency.js +12 -97
  106. package/packages/core/v4-release-activation-shadow.js +13 -500
  107. package/packages/core/v4-release-invocation.js +28 -389
  108. package/packages/core/v4-stable-publication-fence.js +13 -342
  109. package/packages/core/v4-universal-workflow-bootstrap.js +586 -0
  110. package/scripts/binary-publication-evidence.mjs +135 -0
  111. package/scripts/build-contract-core.mjs +1 -14
  112. package/scripts/build-v4-domain-wasm.mjs +189 -0
  113. package/scripts/check-action-bundles.mjs +12 -2
  114. package/scripts/check-ci-lane-change-budget.mjs +4 -1
  115. package/scripts/check-core-mechanism-inventory.mjs +4 -3
  116. package/scripts/check-inventory.mjs +35 -44
  117. package/scripts/check-maintainability.mjs +16 -113
  118. package/scripts/check-v4-floating-consumer-policy-contract.mjs +2 -2
  119. package/scripts/check-v4-public-dogfood-contract.mjs +22 -9
  120. package/scripts/check-v4-release-topology.mjs +90 -33
  121. package/scripts/check-v4-runtime-semantic-closure.mjs +4 -1
  122. package/scripts/check-v4-universal-workflow-bootstrap.mjs +266 -0
  123. package/scripts/check-workflow-taxonomy.mjs +6 -0
  124. package/scripts/copy-v4-domain-wasm.mjs +14 -0
  125. package/scripts/dev-delivery-authority-command-adapters.mjs +1 -1
  126. package/scripts/dev-delivery-authority.mjs +8 -8
  127. package/scripts/dev-delivery-source-proof-reuse.mjs +2 -1
  128. package/scripts/dev-delivery-warrant-options.mjs +14 -9
  129. package/scripts/dev-delivery-warrant-store.mjs +73 -7
  130. package/scripts/dev-delivery-warrant-transition.mjs +109 -0
  131. package/scripts/dev-delivery-warrant.mjs +76 -99
  132. package/scripts/dev-pr-auto-merge.mjs +15 -15
  133. package/scripts/generate-channel-build-workflow.mjs +7 -2
  134. package/scripts/generate-channel-promotion-workflow.mjs +7 -5
  135. package/scripts/generate-site-bundle.mjs +11 -11
  136. package/scripts/generate-v4-universal-workflow-facades.mjs +458 -0
  137. package/scripts/generate-workflow-taxonomy.mjs +25 -0
  138. package/scripts/github-output.mjs +31 -0
  139. package/scripts/maintainability-metrics.mjs +16 -5
  140. package/scripts/maintainability-public-surface.mjs +115 -0
  141. package/scripts/next-development-review.mjs +307 -0
  142. package/scripts/paper-work-fleet-cli.mjs +2 -0
  143. package/scripts/release-asset-client.mjs +91 -0
  144. package/scripts/release-candidate-resolver.mjs +9 -9
  145. package/scripts/source-verification-evidence.mjs +344 -0
  146. package/scripts/stable-candidate-qualification.mjs +1 -1
  147. package/scripts/universal-facade-maintainability.mjs +82 -0
  148. package/scripts/v4-product-publication-intent.mjs +13 -6
  149. package/scripts/v4-publication-settlement.mjs +211 -0
  150. package/scripts/v4-universal-workflow-backflow.mjs +212 -0
  151. package/scripts/v4-universal-workflow-engine.mjs +618 -0
  152. package/scripts/v4-universal-workflow-self-dogfood.mjs +205 -0
  153. package/scripts/verify-version-state-delta.mjs +173 -0
  154. package/scripts/workflow-taxonomy.mjs +507 -0
  155. package/templates/native-dev-delivery.yml +4 -7
  156. package/templates/universal-buildchain-bootstrap-recovery.yml +282 -0
  157. package/templates/universal-buildchain-bootstrap.yml +28 -0
@@ -1,88 +1,17 @@
1
- import { v4ContentRoot, validateV4Root } from "./v4-canonical-contracts.js";
2
- import {
3
- RELEASE_TAIL_DECLARATION_CONTRACT,
4
- RELEASE_TAIL_EFFECT_SCHEMA,
5
- RELEASE_TAIL_OBSERVATION_SCHEMA,
6
- RELEASE_TAIL_RECEIPT_SCHEMA,
7
- RELEASE_TAIL_TRANSACTION_POLICY,
8
- } from "./release-tail-provider-plane.js";
9
- import { RELEASE_TAIL_PRODUCT_CAPABILITIES } from "./release-tail-product-capabilities.js";
1
+ import { invokeV4DomainWasm } from "./v4-domain-wasm.js";
10
2
 
11
3
  export const V4_PRODUCT_PUBLICATION_INTENT_CONTRACT =
12
4
  "kungfu-buildchain-v4-product-publication-intent/v1";
13
5
  export const V4_PRODUCT_PUBLICATION_PLAN_CONTRACT =
14
6
  "kungfu-buildchain-v4-product-publication-plan/v1";
15
7
 
16
- const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-alpha\.(\d+))?$/u;
17
- const SHA = /^[0-9a-f]{40}$/u;
18
- const ROOT = /^sha256:[0-9a-f]{64}$/u;
19
- const REPOSITORY = /^[^/\s]+\/[^/\s]+$/u;
20
-
21
- function fail(message) {
22
- throw new Error(`invalid v4 product publication: ${message}`);
23
- }
24
-
25
- function parseVersion(value, label) {
26
- const normalized = String(value || "").trim();
27
- const match = normalized.match(VERSION);
28
- if (!match) fail(`${label} must be an exact stable or alpha version`);
29
- return {
30
- value: normalized,
31
- major: Number(match[1]),
32
- minor: Number(match[2]),
33
- patch: Number(match[3]),
34
- alpha: match[4] === undefined ? null : Number(match[4]),
35
- };
36
- }
37
-
38
- function channelName(value) {
39
- if (value === "alpha") return "alpha";
40
- if (["release", "stable", "major"].includes(value)) return "stable";
41
- fail(`unsupported channel '${value}'`);
42
- }
43
-
44
- function assertLane({ channel, targetRef, version }) {
45
- const expression =
46
- channel === "alpha"
47
- ? /^alpha\/v(\d+)\/v(\d+)\.(\d+)$/u
48
- : /^release\/v(\d+)\/v(\d+)\.(\d+)$/u;
49
- const match = String(targetRef || "").match(expression);
50
- if (!match) fail(`target ref '${targetRef}' is not a ${channel} lane`);
51
- if (
52
- Number(match[1]) !== version.major ||
53
- Number(match[2]) !== version.major ||
54
- Number(match[3]) !== version.minor
55
- )
56
- fail("target ref does not match the publication version line");
57
- }
58
-
59
- function observedAlphaNumber(version, expected) {
60
- const parsed = parseVersion(version, "observed version");
61
- return parsed.major === expected.major &&
62
- parsed.minor === expected.minor &&
63
- parsed.patch === expected.patch
64
- ? parsed.alpha
65
- : null;
66
- }
67
-
68
- function requiredString(value, label) {
69
- const normalized = String(value || "").trim();
70
- if (!normalized) fail(`${label} must be a non-empty string`);
71
- return normalized;
72
- }
73
-
74
- function requiredRoot(value, label) {
75
- const normalized = requiredString(value, label).toLowerCase();
76
- if (!ROOT.test(normalized)) fail(`${label} must be a sha256 content root`);
77
- return normalized;
78
- }
79
-
80
8
  export function selectV4ProductPublicationIntent({
81
9
  channel,
82
10
  targetRef,
83
11
  sourceSha,
84
12
  sourceTimestamp,
85
13
  repository,
14
+ artifactKind = "npm",
86
15
  packageName,
87
16
  distTag,
88
17
  sealedBundleRoot,
@@ -91,102 +20,21 @@ export function selectV4ProductPublicationIntent({
91
20
  recoveredVersion = "",
92
21
  observedVersions = [],
93
22
  }) {
94
- const normalizedChannel = channelName(channel);
95
- if (!SHA.test(String(sourceSha || "")))
96
- fail("sourceSha must be an exact Git SHA");
97
- if (!REPOSITORY.test(String(repository || "")))
98
- fail("repository must be owner/repo");
99
- const normalizedTimestamp = requiredString(
100
- sourceTimestamp,
101
- "sourceTimestamp",
102
- );
103
- if (Number.isNaN(Date.parse(normalizedTimestamp)))
104
- fail("sourceTimestamp must be an ISO timestamp");
105
- const normalizedPackageName = requiredString(packageName, "packageName");
106
- const normalizedDistTag = requiredString(distTag, "distTag");
107
- if (!/^[a-z0-9][a-z0-9._-]*$/u.test(normalizedDistTag))
108
- fail("distTag must be an npm dist-tag");
109
- if (!Array.isArray(observedVersions))
110
- fail("observedVersions must be an array");
111
- const candidate = parseVersion(candidateVersion, "candidateVersion");
112
- assertLane({ channel: normalizedChannel, targetRef, version: candidate });
113
- const observed = [...new Set(observedVersions.map(String))].sort();
114
- let version;
115
- let mode = "fresh";
116
- if (String(recoveredVersion || "").trim()) {
117
- const recovered = parseVersion(recoveredVersion, "recoveredVersion");
118
- if (
119
- recovered.major !== candidate.major ||
120
- recovered.minor !== candidate.minor ||
121
- recovered.patch !== candidate.patch ||
122
- (normalizedChannel === "alpha" && recovered.alpha === null) ||
123
- (normalizedChannel === "stable" && recovered.alpha !== null)
124
- )
125
- fail("recoveredVersion is incompatible with the candidate release line");
126
- version = recovered.value;
127
- mode = "resume";
128
- } else if (normalizedChannel === "alpha") {
129
- if (candidate.alpha === null)
130
- fail("fresh alpha publication requires an alpha candidate version");
131
- const highest = observed.reduce(
132
- (current, entry) =>
133
- Math.max(current, observedAlphaNumber(entry, candidate) ?? -1),
134
- candidate.alpha,
135
- );
136
- version = `${candidate.major}.${candidate.minor}.${candidate.patch}-alpha.${highest + 1}`;
137
- } else {
138
- version = `${candidate.major}.${candidate.minor}.${candidate.patch}`;
139
- }
140
- const intent = {
141
- schema: V4_PRODUCT_PUBLICATION_INTENT_CONTRACT,
142
- mode,
143
- channel: normalizedChannel,
23
+ return invokeV4DomainWasm("product-publication-intent", {
24
+ channel,
144
25
  targetRef,
145
26
  sourceSha,
146
- sourceTimestamp: new Date(normalizedTimestamp).toISOString(),
27
+ sourceTimestamp,
147
28
  repository,
148
- packageName: normalizedPackageName,
149
- distTag: normalizedDistTag,
150
- sealedBundleRoot: requiredRoot(sealedBundleRoot, "sealedBundleRoot"),
151
- requiredArtifactsRoot: requiredRoot(
152
- requiredArtifactsRoot,
153
- "requiredArtifactsRoot",
154
- ),
155
- candidateVersion: candidate.value,
156
- version,
157
- exactTag: `v${version}`,
158
- observedVersions: observed,
159
- };
160
- return {
161
- ...intent,
162
- intentRoot: v4ContentRoot("v4-product-publication-intent", intent),
163
- };
164
- }
165
-
166
- function alphaReferences(intent, version) {
167
- const line = `v${version.major}.${version.minor}`;
168
- return [
169
- { ref: `refs/tags/${intent.exactTag}`, target: "source" },
170
- { ref: `refs/heads/${intent.targetRef}`, target: "version-state" },
171
- {
172
- ref: `refs/heads/dev/v${version.major}/${line}`,
173
- target: "version-state",
174
- },
175
- { ref: `refs/tags/${line}-alpha`, target: "version-state" },
176
- { ref: `refs/tags/v${version.major}-alpha`, target: "version-state" },
177
- ];
178
- }
179
-
180
- function stableReferences(intent, version) {
181
- return [
182
- { ref: `refs/tags/${intent.exactTag}`, target: "source" },
183
- { ref: `refs/heads/${intent.targetRef}`, target: "version-state" },
184
- {
185
- ref: `refs/tags/v${version.major}.${version.minor}`,
186
- target: "version-state",
187
- },
188
- { ref: `refs/tags/v${version.major}`, target: "version-state" },
189
- ];
29
+ artifactKind,
30
+ ...(packageName === undefined ? {} : { packageName }),
31
+ ...(distTag === undefined ? {} : { distTag }),
32
+ ...(sealedBundleRoot === undefined ? {} : { sealedBundleRoot }),
33
+ requiredArtifactsRoot,
34
+ candidateVersion,
35
+ recoveredVersion,
36
+ observedVersions,
37
+ });
190
38
  }
191
39
 
192
40
  export function createV4ProductPublicationPlan({
@@ -194,194 +42,16 @@ export function createV4ProductPublicationPlan({
194
42
  invocationRoot,
195
43
  transactionRoot,
196
44
  }) {
197
- validateV4Root(invocationRoot, "$/productPlan/invocationRoot");
198
- validateV4Root(transactionRoot, "$/productPlan/transactionRoot");
199
- const selected = selectV4ProductPublicationIntent({
200
- channel: intent.channel,
201
- targetRef: intent.targetRef,
202
- sourceSha: intent.sourceSha,
203
- sourceTimestamp: intent.sourceTimestamp,
204
- repository: intent.repository,
205
- packageName: intent.packageName,
206
- distTag: intent.distTag,
207
- sealedBundleRoot: intent.sealedBundleRoot,
208
- requiredArtifactsRoot: intent.requiredArtifactsRoot,
209
- candidateVersion: intent.candidateVersion,
210
- recoveredVersion: intent.mode === "resume" ? intent.version : "",
211
- observedVersions: intent.observedVersions,
212
- });
213
- if (selected.intentRoot !== intent.intentRoot)
214
- fail("intentRoot does not match the canonical publication intent");
215
- const parsed = parseVersion(intent.version, "intent.version");
216
- const references =
217
- intent.channel === "alpha"
218
- ? alphaReferences(intent, parsed)
219
- : stableReferences(intent, parsed);
220
- const stateRef = `refs/heads/buildchain/v4-product-state/${intent.sourceSha}-${intent.version.replaceAll(".", "-")}`;
221
- const operations = [
222
- {
223
- id: "product.version-state.materialize",
224
- adapter: "github-version-state",
225
- authority: "contents-write",
226
- target: {
227
- repository: intent.repository,
228
- version: intent.version,
229
- sourceSha: intent.sourceSha,
230
- sourceTimestamp: intent.sourceTimestamp,
231
- stateRef,
232
- },
233
- },
234
- {
235
- id: "product.package.publish",
236
- adapter: "npm-trusted-publishing",
237
- authority: "oidc-provider-mutation",
238
- target: {
239
- packageName: intent.packageName,
240
- version: intent.version,
241
- distTag: intent.distTag,
242
- sealedBundleRoot: intent.sealedBundleRoot,
243
- requiredArtifactsRoot: intent.requiredArtifactsRoot,
244
- },
245
- },
246
- {
247
- id: "product.release-refs.converge",
248
- adapter: "github-release-refs",
249
- authority: "contents-write",
250
- target: {
251
- repository: intent.repository,
252
- sourceSha: intent.sourceSha,
253
- stateRef,
254
- references,
255
- },
256
- },
257
- ].map((operation) => ({
258
- ...operation,
259
- operationRoot: v4ContentRoot("v4-product-publication-operation", operation),
260
- }));
261
- const plan = {
262
- schema: V4_PRODUCT_PUBLICATION_PLAN_CONTRACT,
263
- intentRoot: intent.intentRoot,
45
+ return invokeV4DomainWasm("product-publication-plan", {
46
+ intent,
264
47
  invocationRoot,
265
48
  transactionRoot,
266
- operationOrder: operations.map(({ id }) => id),
267
- operations,
268
- };
269
- return {
270
- ...plan,
271
- planRoot: v4ContentRoot("v4-product-publication-plan", plan),
272
- };
273
- }
274
-
275
- const PRODUCT_CAPABILITIES = Object.fromEntries(
276
- RELEASE_TAIL_PRODUCT_CAPABILITIES.map((capability) => [
277
- capability.id,
278
- {
279
- ...capability,
280
- destinationKind:
281
- capability.id === "product.package.publish"
282
- ? "npm-package"
283
- : capability.adapter,
284
- },
285
- ]),
286
- );
287
-
288
- function exactTagPattern(tag) {
289
- return `^${tag.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}$`;
49
+ });
290
50
  }
291
51
 
292
52
  export function createV4ProductPublicationDeclaration({ intent, plan }) {
293
- const rebuilt = createV4ProductPublicationPlan({
53
+ return invokeV4DomainWasm("product-publication-declaration", {
294
54
  intent,
295
- invocationRoot: plan.invocationRoot,
296
- transactionRoot: plan.transactionRoot,
55
+ plan,
297
56
  });
298
- if (rebuilt.planRoot !== plan.planRoot)
299
- fail("planRoot does not match the canonical publication plan");
300
- const operationById = new Map(
301
- plan.operations.map((operation) => [operation.id, operation]),
302
- );
303
- return {
304
- contract: RELEASE_TAIL_DECLARATION_CONTRACT,
305
- schemaVersion: 1,
306
- transactionPolicy: RELEASE_TAIL_TRANSACTION_POLICY,
307
- subject: {
308
- repository: intent.repository,
309
- sourceSha: intent.sourceSha,
310
- version: intent.version,
311
- tag: intent.exactTag,
312
- channel: intent.channel,
313
- },
314
- capabilities: plan.operationOrder.map((id) => {
315
- const operation = operationById.get(id);
316
- const descriptor = PRODUCT_CAPABILITIES[id];
317
- if (!operation || !descriptor)
318
- fail(`unsupported product publication operation '${id}'`);
319
- return {
320
- id,
321
- executor: "provider-adapter",
322
- adapter: operation.adapter,
323
- artifactRoles: [
324
- { role: "publication-intent", root: intent.intentRoot },
325
- ...(id === "product.package.publish"
326
- ? [
327
- { role: "sealed-bundle", root: intent.sealedBundleRoot },
328
- {
329
- role: "required-artifacts",
330
- root: intent.requiredArtifactsRoot,
331
- },
332
- ]
333
- : []),
334
- ],
335
- destination: {
336
- kind: descriptor.destinationKind,
337
- locator: `${operation.adapter}:${operation.operationRoot}`,
338
- },
339
- channelPolicy: {
340
- channel: intent.channel,
341
- tagPattern: exactTagPattern(intent.exactTag),
342
- authorityMove:
343
- id === "product.package.publish" ? "none" : "verified-ref",
344
- },
345
- activationPolicy: { mode: "none", environment: "none" },
346
- readbackPredicates: [
347
- {
348
- id: `${id}.target-root`,
349
- kind: "exact-root",
350
- expected: operation.operationRoot,
351
- },
352
- ],
353
- effect: {
354
- schema: RELEASE_TAIL_EFFECT_SCHEMA,
355
- kind: descriptor.effectKind,
356
- },
357
- observation: {
358
- schema: RELEASE_TAIL_OBSERVATION_SCHEMA,
359
- kind: descriptor.observationKind,
360
- },
361
- receipt: {
362
- schema: RELEASE_TAIL_RECEIPT_SCHEMA,
363
- kind: descriptor.receiptKind,
364
- },
365
- operationIdentity: {
366
- transactionRoot: plan.transactionRoot,
367
- capabilityId: id,
368
- subjectRoot: intent.intentRoot,
369
- targetRoot: operation.operationRoot,
370
- attemptKey: `${plan.planRoot}:${id}`,
371
- },
372
- idempotency: {
373
- scope: "subject-target",
374
- duplicate: "readback-before-retry",
375
- },
376
- retry: {
377
- class: "provider-transient",
378
- localAttempts: 1,
379
- exhausted: "blocked",
380
- },
381
- evidenceRequirements: [
382
- "provider readback must match the rooted publication operation",
383
- ],
384
- };
385
- }),
386
- };
387
57
  }
@@ -4,10 +4,8 @@ const SHA_PATTERN = /^[0-9a-f]{40}$/u;
4
4
 
5
5
  function exactSha(value, label) {
6
6
  const normalized = String(value || "").toLowerCase();
7
- if (!SHA_PATTERN.test(normalized)) {
8
- throw new Error(`${label} must be an exact Git SHA`);
9
- }
10
- return normalized;
7
+ if (SHA_PATTERN.test(normalized)) return normalized;
8
+ throw new Error(`${label} must be an exact Git SHA`);
11
9
  }
12
10
 
13
11
  function exactParents(value, label) {
@@ -26,16 +24,14 @@ export function bindV4ProtectedPublicationSource({
26
24
  if (!/^[^/\s]+\/[^/\s]+$/u.test(String(repository || ""))) {
27
25
  throw new Error("protected publication source requires owner/repository");
28
26
  }
29
- const protectedSource = {
30
- sha: exactSha(protectedCommit?.sha, "protected source SHA"),
31
- tree: exactSha(protectedCommit?.tree, "protected source tree"),
32
- parents: exactParents(protectedCommit?.parents, "protected source parents"),
33
- };
34
- const candidateSource = {
35
- sha: exactSha(candidateCommit?.sha, "candidate source SHA"),
36
- tree: exactSha(candidateCommit?.tree, "candidate source tree"),
37
- parents: exactParents(candidateCommit?.parents, "candidate source parents"),
38
- };
27
+ const [protectedSource, candidateSource] = [
28
+ ["protected", protectedCommit],
29
+ ["candidate", candidateCommit],
30
+ ].map(([kind, commit]) => ({
31
+ sha: exactSha(commit?.sha, `${kind} source SHA`),
32
+ tree: exactSha(commit?.tree, `${kind} source tree`),
33
+ parents: exactParents(commit?.parents, `${kind} source parents`),
34
+ }));
39
35
  if (protectedSource.tree !== candidateSource.tree) {
40
36
  throw new Error(
41
37
  "protected publication source tree does not match the qualified candidate tree",
@@ -45,13 +41,13 @@ export function bindV4ProtectedPublicationSource({
45
41
  let mode = "exact-commit";
46
42
  let pullRequestNumber = null;
47
43
  if (protectedSource.sha !== candidateSource.sha) {
48
- mode = "merge-equivalent";
49
44
  const sameParents =
50
- protectedSource.parents.length === candidateSource.parents.length &&
51
- protectedSource.parents.every(
52
- (parent, index) => parent === candidateSource.parents[index],
53
- );
54
- if (!sameParents) {
45
+ protectedSource.parents.join(",") === candidateSource.parents.join(",");
46
+ const squashParents =
47
+ protectedSource.parents.length === 1 &&
48
+ candidateSource.parents.length === 2 &&
49
+ protectedSource.parents[0] === candidateSource.parents[0];
50
+ if (!sameParents && !squashParents) {
55
51
  throw new Error(
56
52
  "protected publication source is not parent-equivalent to the qualified merge candidate",
57
53
  );
@@ -70,12 +66,14 @@ export function bindV4ProtectedPublicationSource({
70
66
  pullRequestNumber <= 0 ||
71
67
  pullRequest?.merged !== true ||
72
68
  pullRequestMerge !== protectedSource.sha ||
73
- !candidateSource.parents.includes(pullRequestHead)
69
+ !candidateSource.parents.includes(pullRequestHead) ||
70
+ (squashParents && candidateSource.parents[1] !== pullRequestHead)
74
71
  ) {
75
72
  throw new Error(
76
73
  "protected publication source does not match the merged pull request and qualified merge candidate",
77
74
  );
78
75
  }
76
+ mode = sameParents ? "merge-equivalent" : "squash-equivalent";
79
77
  }
80
78
 
81
79
  const binding = {