@kungfu-tech/buildchain 2.11.14-alpha.0 → 2.11.14-alpha.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.
- package/dist/site/buildchain-contract.json +25 -17
- package/dist/site/buildchain-site.json +26 -21
- package/dist/site/kfd-claims.json +5 -4
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +5 -5
- package/dist/site/node-api-registry.json +3 -3
- package/dist/site/page-registry.json +17 -12
- package/dist/site/public-surface-audit.json +4 -3
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +9 -9
- package/dist/site/workflow-registry.json +2 -1
- package/docs/lifecycle-protocol.md +4 -1
- package/docs/publication-artifacts.md +9 -0
- package/docs/release-flow.md +10 -1
- package/docs/release-governance.md +51 -3
- package/docs/reusable-build-surface.md +6 -2
- package/docs/web-surface-deployments.md +15 -1
- package/package.json +1 -1
- package/packages/core/buildchain-contract.js +9 -1
- package/packages/core/publication-artifact.js +56 -8
- package/packages/core/publication-package.js +5 -0
- package/packages/core/stable-release-gate.js +284 -0
- package/scripts/check-inventory.mjs +2 -0
- package/scripts/locked-source-checkout.mjs +43 -9
- package/scripts/publication-artifact.mjs +20 -2
- package/scripts/publication-registry-hydrate.mjs +216 -0
- package/scripts/run-lifecycle-core.mjs +7 -2
- package/scripts/stable-release-gate.mjs +295 -0
- package/scripts/web-surface-core.mjs +28 -0
|
@@ -412,6 +412,7 @@
|
|
|
412
412
|
"buildchain-ref",
|
|
413
413
|
"buildchain-repository",
|
|
414
414
|
"checkout-cache-fallback",
|
|
415
|
+
"checkout-cache-fetch-attempts",
|
|
415
416
|
"checkout-cache-mirror-url-template",
|
|
416
417
|
"checkout-cache-mode",
|
|
417
418
|
"checkout-cache-reference-repository-template",
|
|
@@ -442,7 +443,7 @@
|
|
|
442
443
|
"verify-command",
|
|
443
444
|
"working-directory"
|
|
444
445
|
],
|
|
445
|
-
"inputCount":
|
|
446
|
+
"inputCount": 49
|
|
446
447
|
},
|
|
447
448
|
{
|
|
448
449
|
"id": ".bump-minor-version",
|
|
@@ -2538,8 +2539,8 @@
|
|
|
2538
2539
|
"workflowRegistryPath": "dist/site/workflow-registry.json",
|
|
2539
2540
|
"pageRegistryPath": "dist/site/page-registry.json",
|
|
2540
2541
|
"cliRegistryDigest": "3c0729f5351eb82fe26625fa5aabf30903f45f68af2431e02860f85ada468ac9",
|
|
2541
|
-
"workflowRegistryDigest": "
|
|
2542
|
-
"pageRegistryDigest": "
|
|
2542
|
+
"workflowRegistryDigest": "7052aa984ab49647ce2c9f96669caa32f92da57d8b2e506ac172089bedd793cf",
|
|
2543
|
+
"pageRegistryDigest": "784424e0ecb730449947fc0a3aba0bfcff77c34d097d63d71a9d60d5db1383df"
|
|
2543
2544
|
},
|
|
2544
2545
|
"comparison": {
|
|
2545
2546
|
"missingCliRegistry": [],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-publication-release-registry",
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"publishedAt": "2026-07-
|
|
4
|
+
"generatedAt": "2026-07-10T13:03:42.866Z",
|
|
5
|
+
"publishedAt": "2026-07-10T13:03:42.866Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "d0be2b26d092ef5fa411d30888ea0ee3fa044c85",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"package": {
|
|
34
34
|
"name": "@kungfu-tech/buildchain",
|
|
35
|
-
"version": "2.11.14-alpha.
|
|
35
|
+
"version": "2.11.14-alpha.2",
|
|
36
36
|
"versionSource": "package.json#version"
|
|
37
37
|
},
|
|
38
38
|
"sourceKind": "package-site-bundle",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-site-manifest",
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"publishedAt": "2026-07-
|
|
4
|
+
"generatedAt": "2026-07-10T13:03:42.866Z",
|
|
5
|
+
"publishedAt": "2026-07-10T13:03:42.866Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "d0be2b26d092ef5fa411d30888ea0ee3fa044c85",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"package": {
|
|
39
39
|
"name": "@kungfu-tech/buildchain",
|
|
40
|
-
"version": "2.11.14-alpha.
|
|
40
|
+
"version": "2.11.14-alpha.2",
|
|
41
41
|
"versionSource": "package.json#version"
|
|
42
42
|
},
|
|
43
43
|
"entrypoint": "buildchain-site.json",
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
"path": "docs/lifecycle-protocol.md",
|
|
198
198
|
"plane": "use",
|
|
199
199
|
"exists": true,
|
|
200
|
-
"digest": "sha256:
|
|
200
|
+
"digest": "sha256:60c39693878258d275e9d47983b306faed35d6416297c14570429de73284604c"
|
|
201
201
|
},
|
|
202
202
|
{
|
|
203
203
|
"id": "reusable-build-surface",
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
"path": "docs/reusable-build-surface.md",
|
|
206
206
|
"plane": "use",
|
|
207
207
|
"exists": true,
|
|
208
|
-
"digest": "sha256:
|
|
208
|
+
"digest": "sha256:4f3699841c5a189d199db045d526b11f76ad0dff1464d6a63ca98ceadf1bb215"
|
|
209
209
|
},
|
|
210
210
|
{
|
|
211
211
|
"id": "publish-transaction",
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
"path": "docs/release-governance.md",
|
|
222
222
|
"plane": "why",
|
|
223
223
|
"exists": true,
|
|
224
|
-
"digest": "sha256:
|
|
224
|
+
"digest": "sha256:59858b6e02e42377b8a1f3d6d33ba50c5ffba476f7e1443fd61e03919046eff2"
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
"id": "release-flow",
|
|
@@ -229,7 +229,7 @@
|
|
|
229
229
|
"path": "docs/release-flow.md",
|
|
230
230
|
"plane": "verify",
|
|
231
231
|
"exists": true,
|
|
232
|
-
"digest": "sha256:
|
|
232
|
+
"digest": "sha256:5b855ab3863e197b2ab257536b1f3ff85c79bca1fe962c45089b3f4c5ab81189"
|
|
233
233
|
},
|
|
234
234
|
{
|
|
235
235
|
"id": "runtime-train-validation",
|
|
@@ -253,7 +253,7 @@
|
|
|
253
253
|
"path": "docs/web-surface-deployments.md",
|
|
254
254
|
"plane": "use",
|
|
255
255
|
"exists": true,
|
|
256
|
-
"digest": "sha256:
|
|
256
|
+
"digest": "sha256:f1e92ba87417dc9aac4f8cf080565f7f2c114b5007bb68b90f33692c73fdad88"
|
|
257
257
|
}
|
|
258
258
|
],
|
|
259
259
|
"facts": [
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"buildchain-ref",
|
|
27
27
|
"buildchain-repository",
|
|
28
28
|
"checkout-cache-fallback",
|
|
29
|
+
"checkout-cache-fetch-attempts",
|
|
29
30
|
"checkout-cache-mirror-url-template",
|
|
30
31
|
"checkout-cache-mode",
|
|
31
32
|
"checkout-cache-reference-repository-template",
|
|
@@ -56,7 +57,7 @@
|
|
|
56
57
|
"verify-command",
|
|
57
58
|
"working-directory"
|
|
58
59
|
],
|
|
59
|
-
"inputCount":
|
|
60
|
+
"inputCount": 49,
|
|
60
61
|
"surface": "reusable-workflow",
|
|
61
62
|
"capabilityGroup": "api-cli-reference",
|
|
62
63
|
"status": "active"
|
|
@@ -293,7 +293,10 @@ checked-out release material. Any code or undeclared file change still fails the
|
|
|
293
293
|
release tree gate.
|
|
294
294
|
|
|
295
295
|
The action input `verification-command` remains supported. When it is provided,
|
|
296
|
-
it overrides `lifecycle.verify` for that invocation.
|
|
296
|
+
it overrides `lifecycle.verify` for that invocation. The override inherits
|
|
297
|
+
`[lifecycle.env]` and `lifecycle.verify.env`; when the configured stage declares
|
|
298
|
+
`shell`, it also inherits that shell. If the stage has no explicit shell,
|
|
299
|
+
Buildchain preserves the platform-default shell for compatibility.
|
|
297
300
|
|
|
298
301
|
## Migration Preflight
|
|
299
302
|
|
|
@@ -150,6 +150,15 @@ The build-only workflow:
|
|
|
150
150
|
|
|
151
151
|
It does not publish npm packages, deploy web pages, or create GitHub Releases.
|
|
152
152
|
|
|
153
|
+
The paper release preset additionally hydrates every prior published package
|
|
154
|
+
registry from the npm registry before generating the current manifest. npm
|
|
155
|
+
package integrity authenticates each downloaded source; Buildchain verifies the
|
|
156
|
+
registry self-digest, merges immutable records, and fails if a cumulative
|
|
157
|
+
registry drops an accepted version or changes immutable route/artifact facts.
|
|
158
|
+
The synthesized package therefore carries complete history even on a clean
|
|
159
|
+
runner. Its cumulative registry and file SHA-256 values are bound into the paper
|
|
160
|
+
release build summary and release passport evidence.
|
|
161
|
+
|
|
153
162
|
## Paper Release Preset
|
|
154
163
|
|
|
155
164
|
Paper repositories that publish a versioned npm package should use the
|
package/docs/release-flow.md
CHANGED
|
@@ -12,6 +12,8 @@ flowchart TD
|
|
|
12
12
|
Review["Protected branch review"]
|
|
13
13
|
Merge["Merge PR into alpha or release"]
|
|
14
14
|
Promotion["Buildchain Ref Promotion"]
|
|
15
|
+
StableDecision{"Release channel?"}
|
|
16
|
+
StableGate["Stable only: exact-alpha canaries + soak + cooldown"]
|
|
15
17
|
Action["promote-buildchain-ref action"]
|
|
16
18
|
VersionState["Version-state commit"]
|
|
17
19
|
ExactTag["Exact tag"]
|
|
@@ -22,7 +24,10 @@ flowchart TD
|
|
|
22
24
|
Verify --> Review
|
|
23
25
|
Review --> Merge
|
|
24
26
|
Merge --> Promotion
|
|
25
|
-
Promotion -->
|
|
27
|
+
Promotion --> StableDecision
|
|
28
|
+
StableDecision -->|yes| StableGate
|
|
29
|
+
StableDecision -->|no: alpha| Action
|
|
30
|
+
StableGate --> Action
|
|
26
31
|
Action --> VersionState
|
|
27
32
|
Action --> ExactTag
|
|
28
33
|
Action --> FloatingRefs
|
|
@@ -33,6 +38,10 @@ flowchart TD
|
|
|
33
38
|
Buildchain treats the PR merge as release intent and the promotion action as the
|
|
34
39
|
only component allowed to turn that intent into release refs.
|
|
35
40
|
|
|
41
|
+
`StableGate` applies only to Buildchain's release channel. Alpha and train
|
|
42
|
+
iteration bypass it. See [Stable Release Throttle And Canary Gate](release-governance.md#stable-release-throttle-and-canary-gate)
|
|
43
|
+
for the versioned policy and evidence contract.
|
|
44
|
+
|
|
36
45
|
## Ref State
|
|
37
46
|
|
|
38
47
|
| Ref kind | Example | Mutability | Purpose |
|
|
@@ -206,6 +206,41 @@ The production channel and the test channel therefore intentionally diverge
|
|
|
206
206
|
after release: production stays on the release commit, while alpha/dev continue
|
|
207
207
|
at the next prerelease commit.
|
|
208
208
|
|
|
209
|
+
### Stable Release Throttle And Canary Gate
|
|
210
|
+
|
|
211
|
+
Buildchain's own stable channel has an additional pre-publication gate. It is
|
|
212
|
+
evaluated after the PR-stage release candidate has been resolved and before the
|
|
213
|
+
publish-gate ref, package registry, exact stable tag, or floating stable refs
|
|
214
|
+
are mutated. Train refs and alpha promotion do not execute this gate.
|
|
215
|
+
|
|
216
|
+
The policy is versioned in `.buildchain/stable-release-policy.json`. A stable
|
|
217
|
+
candidate is allowed only when all of these facts are true:
|
|
218
|
+
|
|
219
|
+
- the candidate resolves to an immutable exact alpha tag and its GitHub
|
|
220
|
+
prerelease timestamp;
|
|
221
|
+
- at least 24 hours have passed since the preceding stable patch on the same
|
|
222
|
+
minor line;
|
|
223
|
+
- the preceding stable-to-alpha comparison contains a product or public
|
|
224
|
+
contract path, not only version, test, evidence, or retrospective changes;
|
|
225
|
+
- the version-bound impact record has a non-empty summary and at least one
|
|
226
|
+
surface impact;
|
|
227
|
+
- the `Build Surface Fixture` release-candidate run succeeded;
|
|
228
|
+
- `site-libkungfu-dev` completed its no-apply `Buildchain Stable Canary` and an allowed
|
|
229
|
+
maintainer attested that successful run on the exact alpha SHA through the
|
|
230
|
+
`buildchain-canary/site-libkungfu-dev` commit-status context;
|
|
231
|
+
- at least one hour has elapsed after the last required canary completed.
|
|
232
|
+
|
|
233
|
+
The machine report is written to
|
|
234
|
+
`.buildchain/release-passport/stable-release-gate.json`. A passing report is
|
|
235
|
+
uploaded with the stable release passport. A blocked run writes the same report
|
|
236
|
+
before failing, so the missing or stale condition is inspectable without
|
|
237
|
+
opening a publication transaction.
|
|
238
|
+
|
|
239
|
+
The cooldown is a minimum interval, not an instruction to release every day.
|
|
240
|
+
Compatible work should still be batched until a stable release has a concrete
|
|
241
|
+
consumer need. Changing the interval, canary set, attestors, product path
|
|
242
|
+
boundary, or soak time is a reviewed policy change.
|
|
243
|
+
|
|
209
244
|
If release finalization is resumed after generated version-state bookkeeping was
|
|
210
245
|
partially applied, Buildchain applies the same recovery rule: the current
|
|
211
246
|
release head may be the generated commit, or a historical merge commit that
|
|
@@ -213,6 +248,13 @@ contains the recorded release material, existing exact tags and alpha/dev refs
|
|
|
213
248
|
are accepted when they match the transaction, and missing floating `vX.Y` or
|
|
214
249
|
`vX` tags are retried idempotently before completion.
|
|
215
250
|
|
|
251
|
+
Once the durable release transaction is `complete` and the exact/floating
|
|
252
|
+
stable refs have moved, next-alpha preparation is post-release bookkeeping. A
|
|
253
|
+
failure there records `deferred-post-release-bookkeeping` and
|
|
254
|
+
`next-anchor-required` instead of retroactively reporting the stable release as
|
|
255
|
+
failed. Generated next-alpha merges use the current dev tree as their base and
|
|
256
|
+
overlay only declared version-state paths, preserving concurrent dev changes.
|
|
257
|
+
|
|
216
258
|
## Major Gate Semantics
|
|
217
259
|
|
|
218
260
|
A major gate merge is:
|
|
@@ -276,8 +318,14 @@ After each merge, the next PR is re-evaluated before it can move the protected
|
|
|
276
318
|
dev branch. This prevents one merge from silently making the next candidate
|
|
277
319
|
stale or conflicting.
|
|
278
320
|
|
|
279
|
-
The required check
|
|
280
|
-
|
|
321
|
+
The canonical consumer required check context is `check / check`, matching the
|
|
322
|
+
reusable workflow call plus its `check` job. Buildchain's own `Verify` workflow
|
|
323
|
+
emits the repository-local context `check`, so Buildchain self-promotion,
|
|
324
|
+
release-line bootstrap, and dogfood patrol wrappers pass `check` explicitly.
|
|
325
|
+
Release governance preserves the exact context emitted by the repository and
|
|
326
|
+
records branch-protection policy before/after facts; it does not rewrite one
|
|
327
|
+
context shape into the other. Consumer repositories can keep their context
|
|
328
|
+
stable while changing the actual verification command declaratively in
|
|
281
329
|
`buildchain.toml`:
|
|
282
330
|
|
|
283
331
|
```toml
|
|
@@ -317,7 +365,7 @@ jobs:
|
|
|
317
365
|
statuses: read
|
|
318
366
|
with:
|
|
319
367
|
target-branch: dev/v2/v2.6
|
|
320
|
-
required-status-checks: check
|
|
368
|
+
required-status-checks: check / check
|
|
321
369
|
ready-label: ready
|
|
322
370
|
block-labels: blocked,do-not-merge
|
|
323
371
|
max-merges: 1
|
|
@@ -233,6 +233,7 @@ jobs:
|
|
|
233
233
|
checkout-cache-reference-repository-template: ${{ vars.BUILDCHAIN_CHECKOUT_CACHE_REFERENCE_REPOSITORY_TEMPLATE }}
|
|
234
234
|
checkout-cache-fallback: github
|
|
235
235
|
checkout-cache-timeout-seconds: 60
|
|
236
|
+
checkout-cache-fetch-attempts: 3
|
|
236
237
|
```
|
|
237
238
|
|
|
238
239
|
`checkout-cache-mode` accepts:
|
|
@@ -255,7 +256,10 @@ Do not read cache URLs or reference paths from PR-controlled files such as
|
|
|
255
256
|
`.buildchain/buildchain.toml`. These values are trusted workflow inputs or repo/org
|
|
256
257
|
variables. Buildchain does not pass GitHub credentials to cache mirrors or
|
|
257
258
|
reference repositories. If it must fall back to GitHub, the workflow token is
|
|
258
|
-
used only for the GitHub fetch path.
|
|
259
|
+
used only for the GitHub fetch path. Retryable timeout and transient network
|
|
260
|
+
failures use the bounded `checkout-cache-fetch-attempts` budget; permanent
|
|
261
|
+
failures stop immediately. Diagnostics record the configured budget and actual
|
|
262
|
+
GitHub fetch attempts before exact HEAD/tree verification.
|
|
259
263
|
|
|
260
264
|
Each platform diagnostics artifact includes `source-checkout.json` and embeds a
|
|
261
265
|
compact `sourceCheckout` summary in `diagnostics.json`: mode, transport,
|
|
@@ -592,7 +596,7 @@ jobs:
|
|
|
592
596
|
runner-preset: github-hosted
|
|
593
597
|
trusted-publishing: true
|
|
594
598
|
github-release: true
|
|
595
|
-
required-status-check: check
|
|
599
|
+
required-status-check: check / check
|
|
596
600
|
required-artifact-count: 3
|
|
597
601
|
publish-dist-tag: alpha
|
|
598
602
|
publish-package-set-order: platforms-first-main-last
|
|
@@ -351,6 +351,16 @@ The CLI emits GitHub outputs when `GITHUB_OUTPUT` is present:
|
|
|
351
351
|
- `web-surface-artifact-hash`
|
|
352
352
|
- `web-surface-manifest-json`
|
|
353
353
|
|
|
354
|
+
The reusable workflow resolves that same effective channel before running the
|
|
355
|
+
caller build or verify command. Both steps receive
|
|
356
|
+
`BUILDCHAIN_WEB_SURFACE_CHANNEL` (`preview`, `staging`, or `production`) and
|
|
357
|
+
`BUILDCHAIN_PREVIEW_ALIAS` for previews. Compatibility aliases
|
|
358
|
+
`BUILDCHAIN_SURFACE_CHANNEL` and `BUILDCHAIN_WEB_SURFACE_ALIAS` are also
|
|
359
|
+
provided. Callers should consume these variables instead of reconstructing the
|
|
360
|
+
release-intent state machine from raw GitHub events.
|
|
361
|
+
An unapproved manual canary resolves to `staging`; a trusted manual dispatch
|
|
362
|
+
with `production-approved=true` resolves to `production` through the same path.
|
|
363
|
+
|
|
354
364
|
## Explicit Apply
|
|
355
365
|
|
|
356
366
|
`deploy-apply` and `cleanup-apply` are explicit execution modes for the
|
|
@@ -452,7 +462,11 @@ HTTP check is visible from the consumer run.
|
|
|
452
462
|
|
|
453
463
|
It can also execute a previously saved deploy plan. In that mode Buildchain
|
|
454
464
|
recomputes the local artifact hash before running AWS commands and fails closed
|
|
455
|
-
if the artifact no longer matches the saved plan
|
|
465
|
+
if the artifact no longer matches the saved plan. Before any adapter operation,
|
|
466
|
+
it also checks channel-aware JSON manifests in the artifact: top-level
|
|
467
|
+
`canonicalHost` and declared `pages[].host` facts must belong to the plan's
|
|
468
|
+
surface hosts. A production plan therefore rejects staging/preview host facts
|
|
469
|
+
before AWS apply:
|
|
456
470
|
|
|
457
471
|
```bash
|
|
458
472
|
node scripts/web-surface.mjs \
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "2.11.14-alpha.
|
|
3
|
+
"version": "2.11.14-alpha.2",
|
|
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",
|
|
@@ -96,6 +96,10 @@ export function createBuildchainContractWorld({ root = process.cwd(), packageJso
|
|
|
96
96
|
"platforms-json",
|
|
97
97
|
"release-candidate",
|
|
98
98
|
"artifact-transfer-mode",
|
|
99
|
+
"checkout-cache-mode",
|
|
100
|
+
"checkout-cache-fallback",
|
|
101
|
+
"checkout-cache-timeout-seconds",
|
|
102
|
+
"checkout-cache-fetch-attempts",
|
|
99
103
|
"buildchain-contract-lock-path",
|
|
100
104
|
"buildchain-contract-drift-issue-mode",
|
|
101
105
|
],
|
|
@@ -104,6 +108,7 @@ export function createBuildchainContractWorld({ root = process.cwd(), packageJso
|
|
|
104
108
|
"publish source locks are verified before heavy build jobs",
|
|
105
109
|
"release-candidate builds do not publish registry artifacts",
|
|
106
110
|
"contract drift is checked before heavy build jobs for stable floating refs",
|
|
111
|
+
"retryable GitHub fallback fetches use a bounded attempt budget before exact source SHA and tree verification",
|
|
107
112
|
],
|
|
108
113
|
}),
|
|
109
114
|
surface(root, {
|
|
@@ -115,7 +120,7 @@ export function createBuildchainContractWorld({ root = process.cwd(), packageJso
|
|
|
115
120
|
requiredOutputs: ["promoted-sha", "built-source-sha", "release-candidate-artifact"],
|
|
116
121
|
breakingDefaults: {
|
|
117
122
|
promoteOnlyReleaseCandidate: true,
|
|
118
|
-
requiredStatusCheck: "check",
|
|
123
|
+
requiredStatusCheck: "check / check",
|
|
119
124
|
},
|
|
120
125
|
optionalInputs: [
|
|
121
126
|
"buildchain-ref",
|
|
@@ -177,6 +182,8 @@ export function createBuildchainContractWorld({ root = process.cwd(), packageJso
|
|
|
177
182
|
"contract drift is checked before caller build, web-surface render, deploy planning, and apply side effects",
|
|
178
183
|
"compatible contract drift continues and produces a consumer issue or summary",
|
|
179
184
|
"breaking contract drift fails closed before rendering, deployment, or release publication",
|
|
185
|
+
"effective channel and preview alias are injected before caller build and verify commands",
|
|
186
|
+
"channel-aware artifact host facts are checked against the deploy plan before adapter side effects",
|
|
180
187
|
],
|
|
181
188
|
}),
|
|
182
189
|
surface(root, {
|
|
@@ -189,6 +196,7 @@ export function createBuildchainContractWorld({ root = process.cwd(), packageJso
|
|
|
189
196
|
breakingDefaults: {
|
|
190
197
|
requireGovernance: false,
|
|
191
198
|
releasePassport: true,
|
|
199
|
+
requiredStatusCheck: "check / check",
|
|
192
200
|
},
|
|
193
201
|
optionalInputs: [
|
|
194
202
|
"publish-transaction",
|
|
@@ -477,7 +477,54 @@ function publicationArchiveRecord({ collected, manifestPath, passportPath, regis
|
|
|
477
477
|
};
|
|
478
478
|
}
|
|
479
479
|
|
|
480
|
-
function
|
|
480
|
+
function immutablePublicationRecord(record = {}) {
|
|
481
|
+
const { publishedAt: _publishedAt, latestObservedAt: _latestObservedAt, recordSha256: _recordSha256, ...immutable } = record;
|
|
482
|
+
return immutable;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function readVerifiedPublicationRegistry(registryPath) {
|
|
486
|
+
const registry = JSON.parse(fs.readFileSync(registryPath, "utf8"));
|
|
487
|
+
if (registry.contract !== PUBLICATION_ARTIFACT_REGISTRY_CONTRACT) {
|
|
488
|
+
throw new Error(`publication archive registry has unsupported contract: ${registry.contract || "(missing)"}`);
|
|
489
|
+
}
|
|
490
|
+
const expectedDigest = sha256Json({ ...registry, registrySha256: undefined });
|
|
491
|
+
if (registry.registrySha256 !== expectedDigest) {
|
|
492
|
+
throw new Error(`publication archive registry digest mismatch for ${registryPath}: expected ${expectedDigest}, got ${registry.registrySha256 || "(missing)"}`);
|
|
493
|
+
}
|
|
494
|
+
if (!Array.isArray(registry.versions)) throw new Error(`publication archive registry versions must be an array: ${registryPath}`);
|
|
495
|
+
return registry;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function mergePublicationRegistryVersions({ registries = [], publicationId }) {
|
|
499
|
+
const versions = new Map();
|
|
500
|
+
for (const registry of registries) {
|
|
501
|
+
if (registry.publication?.id !== publicationId) {
|
|
502
|
+
throw new Error(`publication archive registry id mismatch: expected ${publicationId}, got ${registry.publication?.id || "(missing)"}`);
|
|
503
|
+
}
|
|
504
|
+
if (registry.historyPolicy === "cumulative-authenticated-hydration") {
|
|
505
|
+
const declaredVersions = new Set(registry.versions.map((record) => String(record?.version || "")));
|
|
506
|
+
const missingVersions = [...versions.keys()].filter((version) => !declaredVersions.has(version));
|
|
507
|
+
if (missingVersions.length > 0) throw new Error(`cumulative publication archive registry dropped accepted versions: ${missingVersions.join(", ")}`);
|
|
508
|
+
}
|
|
509
|
+
for (const record of registry.versions) {
|
|
510
|
+
const version = String(record?.version || "").trim();
|
|
511
|
+
if (!version) throw new Error("publication archive registry record is missing version");
|
|
512
|
+
const existing = versions.get(version);
|
|
513
|
+
if (!existing) {
|
|
514
|
+
versions.set(version, record);
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (existing.immutableDigest !== record.immutableDigest || stableJson(immutablePublicationRecord(existing)) !== stableJson(immutablePublicationRecord(record))) {
|
|
518
|
+
throw new Error(`publication archive version ${version} changed across accepted registries: ${existing.immutableDigest || "(missing)"} != ${record.immutableDigest || "(missing)"}`);
|
|
519
|
+
}
|
|
520
|
+
const latestObservedAt = [existing.latestObservedAt, record.latestObservedAt].filter(Boolean).sort().at(-1);
|
|
521
|
+
versions.set(version, latestObservedAt ? { ...existing, latestObservedAt } : existing);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return [...versions.values()];
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function updatePublicationArchiveRegistry({ cwd, collected, manifestPath, passportPath, registryOutput, registryInputs = [], generatedAt }) {
|
|
481
528
|
const archive = collected.manifest.publication.archive;
|
|
482
529
|
if (!archive) {
|
|
483
530
|
return undefined;
|
|
@@ -503,17 +550,16 @@ function updatePublicationArchiveRegistry({ cwd, collected, manifestPath, passpo
|
|
|
503
550
|
siteConsumers: collected.manifest.publication.siteConsumers,
|
|
504
551
|
},
|
|
505
552
|
appendOnly: true,
|
|
553
|
+
historyPolicy: "cumulative-authenticated-hydration",
|
|
506
554
|
sameVersionRepublish: "fail-on-digest-change",
|
|
507
555
|
immutablePathPolicy: archive.immutablePathPolicy,
|
|
508
556
|
versions: [],
|
|
509
557
|
};
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
const versions = Array.isArray(registry.versions) ? registry.versions : [];
|
|
558
|
+
const inputPaths = [...registryInputs.map((entry) => path.resolve(cwd, entry)), ...(fs.existsSync(registryPath) ? [registryPath] : [])];
|
|
559
|
+
const versions = mergePublicationRegistryVersions({
|
|
560
|
+
registries: inputPaths.map(readVerifiedPublicationRegistry),
|
|
561
|
+
publicationId: archive.id,
|
|
562
|
+
});
|
|
517
563
|
const existingIndex = versions.findIndex((entry) => entry.version === record.version);
|
|
518
564
|
if (existingIndex !== -1) {
|
|
519
565
|
const existing = versions[existingIndex];
|
|
@@ -559,6 +605,7 @@ export function writePublicationArtifact({
|
|
|
559
605
|
output = "",
|
|
560
606
|
passportOutput = "",
|
|
561
607
|
registryOutput = "",
|
|
608
|
+
registryInputs = [],
|
|
562
609
|
sourceSha = "",
|
|
563
610
|
sourceBundle = true,
|
|
564
611
|
sourceBundlePath = "",
|
|
@@ -586,6 +633,7 @@ export function writePublicationArtifact({
|
|
|
586
633
|
manifestPath: path.resolve(cwd, manifestOutput),
|
|
587
634
|
passportPath: path.resolve(cwd, passportPath),
|
|
588
635
|
registryOutput,
|
|
636
|
+
registryInputs,
|
|
589
637
|
generatedAt,
|
|
590
638
|
});
|
|
591
639
|
return {
|
|
@@ -106,6 +106,7 @@ export function collectPublicationPackageFacts({
|
|
|
106
106
|
manifestPath: publication.manifestPath,
|
|
107
107
|
passportPath: ".buildchain/publication/publication-artifact-passport.json",
|
|
108
108
|
registryPath: publication.archive?.registryPath || "",
|
|
109
|
+
registryHydrationPath: publication.archive ? ".buildchain/publication/registry-hydration.json" : "",
|
|
109
110
|
sourceBundlePath: publication.sourceBundlePath,
|
|
110
111
|
artifactPaths: [publication.primaryArtifact, ...publication.artifactPaths].filter(Boolean),
|
|
111
112
|
metadataPaths: publication.metadataPaths,
|
|
@@ -131,6 +132,7 @@ export function preparePublicationNpmPackage({
|
|
|
131
132
|
facts.publication.manifestPath,
|
|
132
133
|
facts.publication.passportPath,
|
|
133
134
|
facts.publication.registryPath,
|
|
135
|
+
facts.publication.registryHydrationPath,
|
|
134
136
|
facts.publication.sourceBundlePath,
|
|
135
137
|
...facts.publication.artifactPaths,
|
|
136
138
|
...facts.publication.metadataPaths,
|
|
@@ -197,6 +199,9 @@ export function preparePublicationNpmPackage({
|
|
|
197
199
|
publicationManifest: facts.publication.manifestPath,
|
|
198
200
|
publicationPassport: facts.publication.passportPath,
|
|
199
201
|
publicationRegistry: facts.publication.registryPath || undefined,
|
|
202
|
+
publicationRegistryHydration: files.some((file) => file.path === facts.publication.registryHydrationPath)
|
|
203
|
+
? facts.publication.registryHydrationPath
|
|
204
|
+
: undefined,
|
|
200
205
|
primaryArtifact: facts.publication.primaryArtifact,
|
|
201
206
|
sourceBundle: facts.publication.sourceBundlePath,
|
|
202
207
|
siteConsumers: facts.publication.siteConsumers,
|