@kungfu-tech/buildchain 3.0.5-alpha.8 → 3.0.6-alpha.0
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/contracts/auditable-demo-scenario-v1.schema.json +11 -0
- package/dist/site/buildchain-contract.json +42 -22
- package/dist/site/buildchain-site.json +73 -27
- package/dist/site/capability-registry.json +2 -2
- package/dist/site/cli-registry.json +5 -3
- package/dist/site/controller-registry.json +19 -3
- package/dist/site/kfd-claims.json +58 -14
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +7 -6
- package/dist/site/node-api-registry.json +335 -8
- package/dist/site/page-registry.json +63 -17
- package/dist/site/public-surface-audit.json +94 -11
- package/dist/site/publication-authority-registry.json +21 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +10 -10
- package/dist/site/workflow-registry.json +53 -4
- package/docs/MAP.md +4 -1
- package/docs/auditable-demo.md +19 -3
- package/docs/cli-reference.md +15 -4
- package/docs/dev-alpha-candidate-patrol.md +14 -5
- package/docs/dev-qualification-patrol.md +108 -0
- package/docs/node-api-reference.md +42 -9
- package/docs/release-propagation.md +93 -0
- package/docs/reusable-build-surface.md +44 -12
- package/docs/stable-candidate-patrol.md +5 -2
- package/package.json +1 -1
- package/packages/core/buildchain-publication-authority.js +1 -0
- package/packages/core/release-propagation-agent-entry.js +185 -0
- package/packages/core/release-propagation-push.js +293 -0
- package/packages/core/release-propagation-stage-evidence.js +36 -0
- package/packages/core/release-propagation-work.js +1 -1
- package/packages/core/release-propagation.js +15 -0
- package/scripts/artifact-signing-controller-core.mjs +525 -0
- package/scripts/artifact-signing-controller.mjs +302 -0
- package/scripts/artifact-signing-delegation.mjs +51 -3
- package/scripts/auditable-demo-platform.mjs +59 -1
- package/scripts/auditable-demo-transport-smoke.mjs +176 -0
- package/scripts/build-standalone-binary.mjs +3 -1
- package/scripts/buildchain-cli-help.mjs +1 -1
- package/scripts/check-inventory.mjs +1 -0
- package/scripts/compiler-cache-evidence.mjs +90 -7
- package/scripts/dev-alpha-candidate-patrol.mjs +80 -5
- package/scripts/dev-pr-auto-merge.mjs +4 -4
- package/scripts/dev-qualification-patrol.mjs +594 -0
- package/scripts/dispatch-artifact-signing-authority.mjs +312 -61
- package/scripts/generate-site-bundle.mjs +3 -0
- package/scripts/inspect-artifact-signing-requests.mjs +12 -0
- package/scripts/release-propagation.mjs +73 -0
- package/scripts/run-lifecycle-core.mjs +25 -0
- package/scripts/site-capability-metadata.mjs +1 -1
- package/scripts/stable-candidate-patrol.mjs +30 -4
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: active
|
|
3
|
+
period: ongoing
|
|
4
|
+
theme: buildchain-release-propagation
|
|
5
|
+
doc_type: technical-reference
|
|
6
|
+
source_level: local-files
|
|
7
|
+
confidence: high
|
|
8
|
+
sensitivity: public
|
|
9
|
+
evidence_grade: A
|
|
10
|
+
review_state: unreviewed
|
|
11
|
+
last_reviewed: 2026-08-03
|
|
12
|
+
ai_provenance:
|
|
13
|
+
model_family: GPT-5
|
|
14
|
+
product: Codex
|
|
15
|
+
generated_at: 2026-08-03
|
|
16
|
+
invisible_context: not asserted
|
|
17
|
+
---
|
|
18
|
+
|
|
1
19
|
# Release Propagation
|
|
2
20
|
|
|
3
21
|
Release propagation lets a finalized upstream release open a downstream update
|
|
@@ -205,6 +223,53 @@ The receipt keeps four machine states separate:
|
|
|
205
223
|
|
|
206
224
|
Package publication or alpha completion never implies either visibility state.
|
|
207
225
|
|
|
226
|
+
## Unified Site agent entry
|
|
227
|
+
|
|
228
|
+
An Agent begins every Site upstream update through one policy-reporting entry:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
buildchain release-propagation entry plan \
|
|
232
|
+
--source-id <paper|kfd|buildchain|kungfu-core> \
|
|
233
|
+
--channel <alpha|release> \
|
|
234
|
+
--json
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The entry does not blur content and code release policies:
|
|
238
|
+
|
|
239
|
+
| Upstream | Trigger policy | Entry result |
|
|
240
|
+
| ----------- | ------------------------- | -------------------------------------------------- |
|
|
241
|
+
| Paper | automatic release handoff | requires or resumes the exact captured Work |
|
|
242
|
+
| KFD | automatic release handoff | requires or resumes the exact captured Work |
|
|
243
|
+
| Buildchain | explicit Site intent | resolves an exact published package before capture |
|
|
244
|
+
| Kungfu Core | explicit Site intent | resolves an exact published package before capture |
|
|
245
|
+
|
|
246
|
+
Paper and KFD releases keep automatic capture-only handoff. Buildchain and
|
|
247
|
+
Kungfu Core releases remain inert until a downstream Agent receives explicit
|
|
248
|
+
Site-update intent. In all four cases, the entry reports the selected policy,
|
|
249
|
+
exact release coordinate when one has been admitted, Work root and recovery
|
|
250
|
+
cursor when one exists, and one machine-readable next action. A GitHub PR is a
|
|
251
|
+
delivery stage inside that Work; it is not the handoff unit.
|
|
252
|
+
|
|
253
|
+
For an automatic handoff, bind the exact artifact rather than resolving a
|
|
254
|
+
floating tag again:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
buildchain release-propagation entry plan \
|
|
258
|
+
--source-id kfd \
|
|
259
|
+
--handoff-work work.json \
|
|
260
|
+
--json
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The deterministic recovery table is available without repository mutation:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
buildchain release-propagation entry fault-matrix --json
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
It classifies current and duplicate Work as successful no-ops, supersession as
|
|
270
|
+
an explicit decision boundary, stale base/expected-old and operational failures
|
|
271
|
+
as retryable, and package/schema disagreement as a hard safety gate.
|
|
272
|
+
|
|
208
273
|
## Agent-native work envelope
|
|
209
274
|
|
|
210
275
|
Setting `agent-work-mode: capture-only` makes a finalized release emit a
|
|
@@ -372,6 +437,34 @@ buildchain release-propagation work repair ... --output successor.json --json
|
|
|
372
437
|
buildchain release-propagation work complete ... --output successor.json --json
|
|
373
438
|
```
|
|
374
439
|
|
|
440
|
+
The `push-branch` stage has an executable, fail-closed entrypoint:
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
buildchain release-propagation work push-plan \
|
|
444
|
+
--work work.json \
|
|
445
|
+
--expected-work-root sha256:<64-hex> \
|
|
446
|
+
--cwd downstream-worktree \
|
|
447
|
+
--remote origin \
|
|
448
|
+
--json
|
|
449
|
+
|
|
450
|
+
buildchain release-propagation work push-branch \
|
|
451
|
+
--work work.json \
|
|
452
|
+
--expected-work-root sha256:<64-hex> \
|
|
453
|
+
--cwd downstream-worktree \
|
|
454
|
+
--remote origin \
|
|
455
|
+
--execute \
|
|
456
|
+
--json
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
The executor checks the exact GitHub repository, current managed branch,
|
|
460
|
+
captured downstream base, and expected-old Work root. It pushes only the current
|
|
461
|
+
commit with `HEAD:refs/heads/<managed-branch>`, never a bare branch or wildcard,
|
|
462
|
+
never a force option, then reads that exact remote ref back. Its typed branch
|
|
463
|
+
reconciliation evidence records the argv, source SHA, destination ref, prior
|
|
464
|
+
remote SHA, observed remote SHA, and whether a mutation occurred. A wrong
|
|
465
|
+
repository, unrelated branch, stale base, concurrent writer, or non-fast-forward
|
|
466
|
+
target fails closed without mutating another branch.
|
|
467
|
+
|
|
375
468
|
Known operational races (`stale-branch`, `expected-old-mismatch`,
|
|
376
469
|
`lockfile-drift`, `failed-check`, `interrupted-execution`, and `ci-delay`) return
|
|
377
470
|
a retryable repair action. Semantic ambiguity, missing credentials, policy
|
|
@@ -8,11 +8,11 @@ confidence: high
|
|
|
8
8
|
sensitivity: public
|
|
9
9
|
evidence_grade: A
|
|
10
10
|
review_state: unreviewed
|
|
11
|
-
last_reviewed: 2026-
|
|
11
|
+
last_reviewed: 2026-08-03
|
|
12
12
|
ai_provenance:
|
|
13
13
|
model_family: GPT-5
|
|
14
14
|
product: Codex
|
|
15
|
-
generated_at: 2026-
|
|
15
|
+
generated_at: 2026-08-03
|
|
16
16
|
invisible_context: not asserted
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -105,6 +105,8 @@ jobs:
|
|
|
105
105
|
artifact-paths: |
|
|
106
106
|
dist
|
|
107
107
|
build/stage
|
|
108
|
+
pre-upload-transport-smoke-scenario-path: .buildchain/auditable-demo.json
|
|
109
|
+
pre-upload-transport-smoke-artifact-root: .
|
|
108
110
|
expected-artifacts-json: >-
|
|
109
111
|
{"minFiles":2,"requiredPaths":["dist/libnode.tar.gz","dist/checksums.txt"]}
|
|
110
112
|
process-summary-path: .buildchain/diagnostics/process-summary.json
|
|
@@ -113,6 +115,15 @@ jobs:
|
|
|
113
115
|
publish-source-ref: publish-gate/release/v22/v22.22/22.22.3-kf.0
|
|
114
116
|
```
|
|
115
117
|
|
|
118
|
+
For a standalone Linux binary demo, the optional pre-upload transport smoke
|
|
119
|
+
uses the same declarative scenario as the later capture workflow. Buildchain
|
|
120
|
+
copies the distribution containing the scenario metadata, strips file execute
|
|
121
|
+
bits to model GitHub Artifact transport, restores only the declared
|
|
122
|
+
digest-bound executable closure, and runs `transportSmoke` before either the
|
|
123
|
+
GitHub Artifact or S3 relay upload step. The smoke must be non-interactive and
|
|
124
|
+
is hard-capped at 60 seconds. Omitting the input preserves the ordinary build
|
|
125
|
+
surface; enabling it requires a scenario with `transportSmoke`.
|
|
126
|
+
|
|
116
127
|
`runner-preset` is the stable first-class surface for known runner fleets:
|
|
117
128
|
|
|
118
129
|
| Preset | Platforms |
|
|
@@ -520,7 +531,14 @@ the preparation step. Buildchain probes its version, runs `sccache
|
|
|
520
531
|
`compiler-cache-preparation.json`. The receipt binds the source commit/tree,
|
|
521
532
|
Buildchain runtime, platform, cache profile, and any declared dependency,
|
|
522
533
|
toolchain, or policy roots. It resets counters only; it does not delete cached
|
|
523
|
-
compiler outputs.
|
|
534
|
+
compiler outputs. The same preparation exports `RUSTC_WRAPPER`,
|
|
535
|
+
`CMAKE_C_COMPILER_LAUNCHER`, and `CMAKE_CXX_COMPILER_LAUNCHER` so Cargo and
|
|
536
|
+
CMake/Ninja compilation actually passes through the audited tool.
|
|
537
|
+
|
|
538
|
+
After the build lifecycle, Buildchain probes the reset counter set again. When
|
|
539
|
+
`compiler-cache-required` is true, the build fails closed if sccache observed
|
|
540
|
+
zero compiler requests or zero cacheable requests. This prevents an installed
|
|
541
|
+
but unbound sccache binary from being reported as an active compiler cache.
|
|
524
542
|
|
|
525
543
|
Final diagnostics admit sccache hit/miss outcomes as current-run evidence only
|
|
526
544
|
when that preparation receipt is present and valid. A bare `sccache
|
|
@@ -624,7 +642,8 @@ Every native and container build lane reads this declaration after the build
|
|
|
624
642
|
lifecycle and before verification. Buildchain binds the exact artifact bytes or directory tree to
|
|
625
643
|
the caller repository, source commit, source tree, immutable runtime, platform,
|
|
626
644
|
and requested signature semantics, then publishes a deterministic
|
|
627
|
-
`<artifact>-signing-request-<platform>-<source-sha>`
|
|
645
|
+
`<artifact>-signing-request-<platform>-<source-sha>-<run-id>-<run-attempt>`
|
|
646
|
+
request. No consumer
|
|
628
647
|
workflow step is required. The lifecycle runner automatically adds declarations
|
|
629
648
|
selected for the current platform to the `build` manifest scan, including
|
|
630
649
|
subjects outside the caller's ordinary `artifact-paths`; this extends the
|
|
@@ -677,14 +696,27 @@ Buildchain-owned signing authority is responsible for credential selection,
|
|
|
677
696
|
native signing, notarization where applicable, immutable result delivery, and a
|
|
678
697
|
receipt bound to the request digest, runtime SHA, output digest, and signature
|
|
679
698
|
evidence. Consumer repositories neither receive nor duplicate credential-island
|
|
680
|
-
material.
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
699
|
+
material. Each platform lane seals and uploads the unsigned request plus a
|
|
700
|
+
run-attempt-bound control request, completes functional verification, and exits.
|
|
701
|
+
It does not dispatch or poll the authority. A separate `ubuntu-24.04` controller
|
|
702
|
+
starts only after the build matrices complete, validates the exact source,
|
|
703
|
+
tree, runtime, request-set root, platform, run attempt, and correlation, then
|
|
704
|
+
dispatches and awaits the protected authority workflow. Its retained receipt
|
|
705
|
+
records two independent immutable identities: the consumer Buildchain runtime
|
|
706
|
+
SHA carried by the control request and the exact authority-ref commit resolved
|
|
707
|
+
immediately before dispatch. The former validates the request-producing
|
|
708
|
+
runtime; the latter must equal the authority workflow run's `head_sha` and is
|
|
709
|
+
retained with the exact authority run and result artifact. If the protected ref
|
|
710
|
+
moves between resolution and dispatch, settlement fails closed. Failure, timeout, or
|
|
711
|
+
cancellation produces a non-qualifying receipt and no finalization delegation.
|
|
712
|
+
|
|
713
|
+
A second GitHub-hosted finalization lane downloads the original control request,
|
|
714
|
+
controller receipt, and delegation, verifies their roots and coordinates agree,
|
|
715
|
+
then verifies the authority result against the sealed request, imports the exact
|
|
716
|
+
signed bytes, and recomputes the final manifest before replacing the
|
|
717
|
+
deterministic artifact. The signing result is never downloaded back to a
|
|
718
|
+
self-hosted native runner, so a macOS caller is released before credential-island
|
|
719
|
+
signing and notarization complete.
|
|
688
720
|
Platform manifests, KFD evidence, checksums, and Release Passport inputs
|
|
689
721
|
therefore observe the final signed artifact rather than the pre-signing build
|
|
690
722
|
output.
|
|
@@ -8,7 +8,7 @@ confidence: high
|
|
|
8
8
|
sensitivity: public
|
|
9
9
|
evidence_grade: A
|
|
10
10
|
review_state: self-reviewed
|
|
11
|
-
last_reviewed: 2026-
|
|
11
|
+
last_reviewed: 2026-08-03
|
|
12
12
|
ai_provenance:
|
|
13
13
|
model_family: GPT-5
|
|
14
14
|
product: Codex
|
|
@@ -141,7 +141,10 @@ bot, or human service-account token as `approval-token`. The approval identity
|
|
|
141
141
|
must differ from the promotion token identity that opens the PR. When no
|
|
142
142
|
`approval-token` is passed, `auto-approve: true` falls back to the caller
|
|
143
143
|
`github.token` and therefore requires GitHub Actions approval permission. The
|
|
144
|
-
generated PR may use auto-merge, but it never bypasses the target branch
|
|
144
|
+
generated PR may use auto-merge, but it never bypasses the target branch
|
|
145
|
+
checks. Callers can select `merge-method: merge`, `squash`, or `rebase`; the
|
|
146
|
+
default is `merge` for compatibility. Unsupported values fail closed before
|
|
147
|
+
GitHub is called.
|
|
145
148
|
|
|
146
149
|
Before enabling `auto-approve` and `auto-merge`, the caller repository must
|
|
147
150
|
provide one approval path and enable auto-merge:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.6-alpha.0",
|
|
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",
|
|
@@ -30,6 +30,7 @@ const DESCRIPTORS = Object.freeze([
|
|
|
30
30
|
[".github/workflows/buildchain-patrol.yml", "governance-write"],
|
|
31
31
|
[".github/workflows/buildchain-ref-promotion.yml", "governance-write"],
|
|
32
32
|
[".github/workflows/buildchain-stable-candidate-patrol.yml", "governance-write"],
|
|
33
|
+
[".github/workflows/dev-qualification-patrol.yml", "governance-write"],
|
|
33
34
|
[".github/workflows/dev-alpha-candidate-patrol.yml", "governance-write"],
|
|
34
35
|
[".github/workflows/dev-merge-queue-governance.yml", "governance-write"],
|
|
35
36
|
[".github/workflows/dev-pr-auto-merge.yml", "governance-write"],
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertPlainObject,
|
|
3
|
+
assertString,
|
|
4
|
+
normalizeChannel,
|
|
5
|
+
sha256Json,
|
|
6
|
+
} from "./release-propagation-common.js";
|
|
7
|
+
import { verifyReleasePropagationWork } from "./release-propagation-work.js";
|
|
8
|
+
|
|
9
|
+
export const SITE_UPSTREAM_AGENT_ENTRY_CONTRACT =
|
|
10
|
+
"kungfu-buildchain-site-upstream-agent-entry";
|
|
11
|
+
export const RELEASE_PROPAGATION_FAILURE_MATRIX_CONTRACT =
|
|
12
|
+
"kungfu-buildchain-release-propagation-failure-matrix";
|
|
13
|
+
|
|
14
|
+
const SOURCE_ALIASES = new Map([
|
|
15
|
+
["buildchain", "buildchain"],
|
|
16
|
+
["core", "kungfu-core"],
|
|
17
|
+
["kfd", "kfd"],
|
|
18
|
+
["kungfu", "kungfu-core"],
|
|
19
|
+
["kungfu-core", "kungfu-core"],
|
|
20
|
+
["paper", "paper"],
|
|
21
|
+
["papers", "paper"],
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const POLICIES = Object.freeze({
|
|
25
|
+
buildchain: Object.freeze({
|
|
26
|
+
mode: "downstream-manual",
|
|
27
|
+
trigger: "explicit-site-intent",
|
|
28
|
+
package: "@kungfu-tech/buildchain",
|
|
29
|
+
}),
|
|
30
|
+
"kungfu-core": Object.freeze({
|
|
31
|
+
mode: "downstream-manual",
|
|
32
|
+
trigger: "explicit-site-intent",
|
|
33
|
+
package: "@kungfu-tech/site",
|
|
34
|
+
}),
|
|
35
|
+
kfd: Object.freeze({
|
|
36
|
+
mode: "automatic-release-handoff",
|
|
37
|
+
trigger: "upstream-release-capture",
|
|
38
|
+
package: "@kungfu-tech/kfd",
|
|
39
|
+
}),
|
|
40
|
+
paper: Object.freeze({
|
|
41
|
+
mode: "automatic-release-handoff",
|
|
42
|
+
trigger: "upstream-release-capture",
|
|
43
|
+
packagePrefix: "@kungfu-tech/paper-",
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const FAILURE_ROWS = Object.freeze([
|
|
48
|
+
["current", "no-op", "none", "retain-current-cut"],
|
|
49
|
+
["duplicate-work", "no-op", "reuse", "resume-exact-work"],
|
|
50
|
+
["superseded-work", "stop", "needs-decision", "follow-declared-successor"],
|
|
51
|
+
["stale-downstream-base", "failure", "retry", "refresh-and-recapture"],
|
|
52
|
+
["stale-expected-old", "failure", "retry", "refresh-and-recapture"],
|
|
53
|
+
[
|
|
54
|
+
"package-schema-mismatch",
|
|
55
|
+
"failure",
|
|
56
|
+
"hard-safety-gate",
|
|
57
|
+
"repair-upstream-release",
|
|
58
|
+
],
|
|
59
|
+
["pull-request-conflict", "failure", "retry", "refresh-and-recapture"],
|
|
60
|
+
["verification-failure", "failure", "retry", "repair-same-work"],
|
|
61
|
+
["release-race", "failure", "retry", "read-authoritative-release-and-resume"],
|
|
62
|
+
["deployment-failure", "failure", "retry", "repair-same-work"],
|
|
63
|
+
["online-readback-failure", "failure", "retry", "repair-same-work"],
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
export const RELEASE_PROPAGATION_FAILURE_MATRIX = Object.freeze({
|
|
67
|
+
schemaVersion: 1,
|
|
68
|
+
contract: RELEASE_PROPAGATION_FAILURE_MATRIX_CONTRACT,
|
|
69
|
+
rows: FAILURE_ROWS.map(([condition, outcome, disposition, recovery]) => ({
|
|
70
|
+
condition,
|
|
71
|
+
outcome,
|
|
72
|
+
disposition,
|
|
73
|
+
recovery,
|
|
74
|
+
})),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export function normalizeSiteUpstreamIntent(value) {
|
|
78
|
+
const raw = assertString(value, "site upstream intent").toLowerCase();
|
|
79
|
+
if (raw.startsWith("@kungfu-tech/paper-")) return "paper";
|
|
80
|
+
const normalized = SOURCE_ALIASES.get(raw);
|
|
81
|
+
if (!normalized) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
"site upstream intent must identify paper, kfd, buildchain, or kungfu-core",
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return normalized;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function automaticHandoff(sourceId, policy, handoffWork) {
|
|
90
|
+
if (handoffWork === null || handoffWork === undefined) {
|
|
91
|
+
return {
|
|
92
|
+
status: "handoff-required",
|
|
93
|
+
exactRelease: null,
|
|
94
|
+
work: null,
|
|
95
|
+
nextAction: {
|
|
96
|
+
action: "consume-release-handoff",
|
|
97
|
+
command:
|
|
98
|
+
"buildchain release-propagation entry plan --source-id <source> --handoff-work <work.json> --json",
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const status = verifyReleasePropagationWork(
|
|
103
|
+
assertPlainObject(handoffWork, "automatic release handoff Work"),
|
|
104
|
+
);
|
|
105
|
+
const packageFact = status.work.upstream.release.package;
|
|
106
|
+
const packageMatches =
|
|
107
|
+
packageFact?.name === policy.package ||
|
|
108
|
+
(policy.packagePrefix &&
|
|
109
|
+
packageFact?.name?.startsWith(policy.packagePrefix));
|
|
110
|
+
if (!packageMatches) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`automatic ${sourceId} handoff package disagrees with the selected policy`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
status: status.lifecycle === "complete" ? "complete" : "handoff-ready",
|
|
117
|
+
exactRelease: {
|
|
118
|
+
repository: status.work.upstream.release.repository,
|
|
119
|
+
package: packageFact,
|
|
120
|
+
releaseRoot: status.work.upstream.releaseRoot,
|
|
121
|
+
},
|
|
122
|
+
work: {
|
|
123
|
+
workId: status.workId,
|
|
124
|
+
workRoot: status.contentRoot,
|
|
125
|
+
lifecycle: status.lifecycle,
|
|
126
|
+
currentStage: status.currentStage,
|
|
127
|
+
expectedBaseSha: status.work.downstream.expectedBaseSha,
|
|
128
|
+
branch: status.work.downstream.branch,
|
|
129
|
+
},
|
|
130
|
+
nextAction: status.nextAction,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function planSiteUpstreamAgentEntry({
|
|
135
|
+
sourceId: sourceInput,
|
|
136
|
+
channel = "",
|
|
137
|
+
handoffWork = null,
|
|
138
|
+
} = {}) {
|
|
139
|
+
const sourceId = normalizeSiteUpstreamIntent(sourceInput);
|
|
140
|
+
const policy = POLICIES[sourceId];
|
|
141
|
+
const normalizedChannel = channel
|
|
142
|
+
? normalizeChannel(channel, "site upstream channel")
|
|
143
|
+
: sourceId === "paper" || sourceId === "kfd"
|
|
144
|
+
? ""
|
|
145
|
+
: "release";
|
|
146
|
+
const route =
|
|
147
|
+
policy.mode === "automatic-release-handoff"
|
|
148
|
+
? automaticHandoff(sourceId, policy, handoffWork)
|
|
149
|
+
: {
|
|
150
|
+
status: "manual-resolution-required",
|
|
151
|
+
exactRelease: null,
|
|
152
|
+
work: null,
|
|
153
|
+
nextAction: {
|
|
154
|
+
action: "resolve-published-release",
|
|
155
|
+
command: `buildchain release-propagation pickup plan --config <config.json> --source-id ${sourceId} --channel ${normalizedChannel} --current-version <exact-version> --json`,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
const body = {
|
|
159
|
+
schemaVersion: 1,
|
|
160
|
+
contract: SITE_UPSTREAM_AGENT_ENTRY_CONTRACT,
|
|
161
|
+
sourceId,
|
|
162
|
+
channel: normalizedChannel,
|
|
163
|
+
policy,
|
|
164
|
+
automaticTrigger: policy.mode === "automatic-release-handoff",
|
|
165
|
+
...route,
|
|
166
|
+
faultMatrixRoot: sha256Json(RELEASE_PROPAGATION_FAILURE_MATRIX),
|
|
167
|
+
};
|
|
168
|
+
return { ...body, entryRoot: sha256Json(body) };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function classifyReleasePropagationCondition(condition) {
|
|
172
|
+
const normalized = assertString(condition, "release propagation condition");
|
|
173
|
+
const row = RELEASE_PROPAGATION_FAILURE_MATRIX.rows.find(
|
|
174
|
+
(entry) => entry.condition === normalized,
|
|
175
|
+
);
|
|
176
|
+
if (!row) {
|
|
177
|
+
throw new Error(`unsupported release propagation condition: ${normalized}`);
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
schemaVersion: 1,
|
|
181
|
+
contract: "kungfu-buildchain-release-propagation-condition",
|
|
182
|
+
...row,
|
|
183
|
+
matrixRoot: sha256Json(RELEASE_PROPAGATION_FAILURE_MATRIX),
|
|
184
|
+
};
|
|
185
|
+
}
|