@kungfu-tech/buildchain 3.0.9-alpha.9 → 4.0.0-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.
Files changed (262) hide show
  1. package/AGENTS.md +39 -0
  2. package/README.md +17 -13
  3. package/actions/promote-buildchain-ref/README.md +0 -10
  4. package/actions/release-tail/README.md +6 -10
  5. package/architecture/internal-capabilities.json +502 -0
  6. package/architecture/maintainability-baseline.json +59 -0
  7. package/architecture/maintainability-policy.json +1384 -0
  8. package/architecture/release-tail-contract-inventory.json +587 -0
  9. package/architecture/v3-core-mechanism-inventory.json +1043 -0
  10. package/architecture/v3-core-mechanism-inventory.md +67 -0
  11. package/architecture/v4-adopter-delivery-parity.json +47 -0
  12. package/architecture/v4-architecture-constitution.md +210 -0
  13. package/architecture/v4-bootstrap-authority.json +56 -0
  14. package/architecture/v4-canonical-contract-fixtures.json +105 -0
  15. package/architecture/v4-capability-state-machine-manifest.json +480 -0
  16. package/architecture/v4-capability-state-machine-manifest.schema.json +202 -0
  17. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +830 -0
  18. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.md +223 -0
  19. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.schema.json +318 -0
  20. package/architecture/v4-delivery-warrant-shadow-fixtures.json +227 -0
  21. package/architecture/v4-delivery-warrant-shadow-fixtures.schema.json +67 -0
  22. package/architecture/v4-exception-ledger.json +5 -0
  23. package/architecture/v4-exception-ledger.schema.json +56 -0
  24. package/architecture/v4-partial-mutation-recovery-qualification.json +65 -0
  25. package/architecture/v4-platform-stage-checkpoints.json +97 -0
  26. package/architecture/v4-provider-operation-journal-contract.json +113 -0
  27. package/architecture/v4-release-activation-shadow-domain.json +61 -0
  28. package/architecture/v4-release-train-parity.json +68 -0
  29. package/architecture/v4-rust-libnode-bridge-evaluation.json +107 -0
  30. package/architecture/v4-rust-libnode-bridge-spike.md +85 -0
  31. package/architecture/v4-stable-publication-fence.json +66 -0
  32. package/architecture/v4-stage-capsule-contract.json +90 -0
  33. package/architecture/v4-stage-capsule-qualification.json +99 -0
  34. package/architecture/v4-stage-capsule-resume-planner.json +52 -0
  35. package/architecture/v4-stage-capsule-store-contract.json +72 -0
  36. package/bin/buildchain.mjs +15 -10
  37. package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
  38. package/contracts/buildchain-v2-residuals-v1.json +2 -2
  39. package/contracts/fixtures/v4-delivery-warrant-trace-v1/golden.json +158 -0
  40. package/contracts/fixtures/v4-delivery-warrant-trace-v1/property.json +380 -0
  41. package/contracts/fixtures/v4-delivery-warrant-trace-v1/replay.json +163 -0
  42. package/contracts/fixtures/v4-partial-mutation-recovery-v1/shared.json +155 -0
  43. package/contracts/fixtures/v4-provider-operation-journal-v1/shared.json +457 -0
  44. package/contracts/fixtures/v4-provider-readback-idempotency-v1/shared.json +682 -0
  45. package/contracts/fixtures/v4-release-activation-shadow-v1/shared.json +53 -0
  46. package/contracts/fixtures/v4-stable-publication-fence-v1/shared.json +56 -0
  47. package/contracts/fixtures/v4-stage-capsule-resume-v1/late-platform-failure.json +176 -0
  48. package/contracts/fixtures/v4-stage-capsule-store-v1/shared.json +132 -0
  49. package/contracts/fixtures/v4-stage-capsule-v1/shared.json +111 -0
  50. package/contracts/v4-canonical-contracts-v1.schema.json +182 -0
  51. package/contracts/v4-delivery-warrant-semantic-diff-report-v1.schema.json +284 -0
  52. package/contracts/v4-delivery-warrant-trace-v1.schema.json +215 -0
  53. package/contracts/v4-host-contract-v1.schema.json +135 -0
  54. package/contracts/v4-partial-mutation-recovery-v1.schema.json +168 -0
  55. package/contracts/v4-provider-operation-journal-v1.schema.json +381 -0
  56. package/contracts/v4-release-activation-shadow-v1.schema.json +186 -0
  57. package/contracts/v4-stable-publication-fence-v1.schema.json +125 -0
  58. package/contracts/v4-stage-capsule-resume-v1.schema.json +225 -0
  59. package/contracts/v4-stage-capsule-store-v1.schema.json +154 -0
  60. package/contracts/v4-stage-capsule-v1.schema.json +178 -0
  61. package/crates/buildchain-v4-bridge/Cargo.lock +213 -0
  62. package/crates/buildchain-v4-bridge/Cargo.toml +12 -0
  63. package/crates/buildchain-v4-bridge/src/main.rs +495 -0
  64. package/dist/site/agent-index.json +0 -4
  65. package/dist/site/artifact-schemas.json +0 -8
  66. package/dist/site/buildchain-contract.json +103 -397
  67. package/dist/site/buildchain-site.json +302 -386
  68. package/dist/site/capability-registry.json +9 -11
  69. package/dist/site/cli-registry.json +8 -53
  70. package/dist/site/controller-registry.json +48 -8
  71. package/dist/site/kfd-claims.json +129 -167
  72. package/dist/site/kfd-upstream-aggregate.json +9 -9
  73. package/dist/site/manual-registry.json +18 -49
  74. package/dist/site/node-api-registry.json +4345 -7345
  75. package/dist/site/page-registry.json +278 -346
  76. package/dist/site/public-surface-audit.json +85 -404
  77. package/dist/site/publication-authority-registry.json +11 -1
  78. package/dist/site/publication-registry.json +4 -4
  79. package/dist/site/release-provenance.json +3 -9
  80. package/dist/site/schemas/kfd-support-projection-v1.schema.json +106 -0
  81. package/dist/site/site-manifest.json +22 -38
  82. package/dist/site/workflow-registry.json +78 -35
  83. package/docs/MAP.md +8 -5
  84. package/docs/auditable-demo.md +2 -2
  85. package/docs/aws-us-elastic-runner-burst-plane.md +7 -59
  86. package/docs/cli-reference.md +15 -213
  87. package/docs/cli.md +14 -14
  88. package/docs/dev-alpha-candidate-patrol.md +3 -11
  89. package/docs/dev-delivery-warrant.md +29 -194
  90. package/docs/engineering-housekeeper.md +12 -4
  91. package/docs/getting-started.md +1 -1
  92. package/docs/install.md +5 -5
  93. package/docs/kfd-support.md +2 -7
  94. package/docs/lifecycle-protocol.md +2 -4
  95. package/docs/node-api-reference.md +414 -554
  96. package/docs/release-flow.md +31 -31
  97. package/docs/release-governance.md +16 -28
  98. package/docs/release-passport.md +2 -15
  99. package/docs/release-tail-contract.md +13 -6
  100. package/docs/release-tail-provider-plane.md +13 -19
  101. package/docs/release-train.md +4 -4
  102. package/docs/reusable-build-surface.md +14 -27
  103. package/docs/site-bundle-contract.md +1 -5
  104. package/docs/stable-candidate-patrol.md +7 -7
  105. package/docs/v4-canonical-contracts.md +92 -0
  106. package/docs/v4-delivery-warrant-read-candidate.md +73 -0
  107. package/docs/v4-delivery-warrant-semantic-diff.md +75 -0
  108. package/docs/v4-production-release.md +63 -0
  109. package/docs/v4-stage-capsule.md +193 -0
  110. package/docs/versioning.md +3 -5
  111. package/package.json +19 -19
  112. package/packages/core/adopter-delivery-gate.js +576 -0
  113. package/packages/core/adopter-delivery-json.js +66 -0
  114. package/packages/core/artifact-signing.js +0 -61
  115. package/packages/core/artifact-verification-envelope.js +5 -5
  116. package/packages/core/build-facts.js +12 -4
  117. package/packages/core/buildchain-agent-manuals.js +0 -2
  118. package/packages/core/buildchain-channel-identity.js +3 -2
  119. package/packages/core/buildchain-config.js +4 -69
  120. package/packages/core/buildchain-contract.js +232 -46
  121. package/packages/core/buildchain-kfd-claims.js +1 -1
  122. package/packages/core/channel-candidate.js +0 -8
  123. package/packages/core/channel-promotion-baseline.js +0 -52
  124. package/packages/core/controller-evidence.js +3 -4
  125. package/packages/core/dev-alpha-candidate-selection.js +2 -10
  126. package/packages/core/dev-delivery-warrant-cancellation.js +0 -1
  127. package/packages/core/dev-delivery-warrant-settlement.js +20 -67
  128. package/packages/core/dev-delivery-warrant.js +50 -138
  129. package/packages/core/diagnostics.js +3 -8
  130. package/packages/core/github-governance-authority.js +3 -1
  131. package/packages/core/kfd-adopter-category-driver.js +167 -0
  132. package/packages/core/kfd-adopter-manifest.js +47 -46
  133. package/packages/core/kfd-gate.js +15 -45
  134. package/packages/core/paper-agent-entry.js +7 -16
  135. package/packages/core/paper-fleet.js +2 -2
  136. package/packages/core/paper-repository.js +2 -3
  137. package/packages/core/paper-scaffold-content.js +2 -23
  138. package/packages/core/paper.js +11 -43
  139. package/packages/core/publication-reproducibility.js +4 -4
  140. package/packages/core/release-line-bootstrap.js +21 -8
  141. package/packages/core/release-passport-contract.js +5 -5
  142. package/packages/core/release-passport.js +20 -131
  143. package/packages/core/self-dogfood-version.js +48 -35
  144. package/packages/core/spawn-command.js +232 -8
  145. package/packages/core/v4-adopter-delivery-parity.js +219 -0
  146. package/packages/core/v4-canonical-contracts.js +284 -0
  147. package/packages/core/v4-delivery-warrant-fixture-runner.js +371 -0
  148. package/packages/core/v4-delivery-warrant-read-candidate.js +279 -0
  149. package/packages/core/v4-delivery-warrant-semantic-diff-gate.js +352 -0
  150. package/packages/core/v4-delivery-warrant-shadow-adapter.js +447 -0
  151. package/packages/core/v4-partial-mutation-recovery-qualification.js +395 -0
  152. package/packages/core/v4-platform-stage-checkpoints.js +471 -0
  153. package/packages/core/v4-provider-operation-journal.js +531 -0
  154. package/packages/core/v4-provider-readback-idempotency.js +394 -0
  155. package/packages/core/v4-release-activation-shadow.js +519 -0
  156. package/packages/core/v4-stable-publication-fence.js +357 -0
  157. package/packages/core/v4-stage-capsule-local-store.js +426 -0
  158. package/packages/core/v4-stage-capsule-qualification-campaign.js +512 -0
  159. package/packages/core/v4-stage-capsule-qualification.js +456 -0
  160. package/packages/core/v4-stage-capsule-resume-planner.js +397 -0
  161. package/packages/core/v4-stage-capsule-store.js +401 -0
  162. package/packages/core/v4-stage-capsule.js +319 -0
  163. package/scripts/assemble-self-publication-admission.mjs +1 -1
  164. package/scripts/audit-publication-control-plane.mjs +5 -5
  165. package/scripts/auditable-demo-bundle-verification.mjs +3 -2
  166. package/scripts/auditable-demo-platform.mjs +2 -2
  167. package/scripts/auditable-demo-renditions.mjs +1 -1
  168. package/scripts/auditable-demo.mjs +2 -2
  169. package/scripts/aws-macos-jit-controller-core.mjs +13 -346
  170. package/scripts/aws-macos-jit-controller-runtime.mjs +2 -210
  171. package/scripts/aws-macos-jit-controller.mjs +37 -275
  172. package/scripts/aws-macos-jit-core.mjs +3 -41
  173. package/scripts/aws-macos-jit.mjs +0 -12
  174. package/scripts/aws-windows-jit-campaign.mjs +2 -2
  175. package/scripts/aws-windows-jit-controller-core.mjs +1 -1
  176. package/scripts/aws-windows-jit-controller.mjs +3 -3
  177. package/scripts/build-contract-core.mjs +3 -8
  178. package/scripts/build-standalone-binary.mjs +3 -14
  179. package/scripts/buildchain-channel-router.mjs +4 -0
  180. package/scripts/buildchain-cli-help.mjs +7 -16
  181. package/scripts/buildchain-contract-lock.mjs +0 -6
  182. package/scripts/check-action-bundles.mjs +3 -4
  183. package/scripts/check-internal-architecture.mjs +7 -6
  184. package/scripts/check-inventory.mjs +30 -25
  185. package/scripts/check-v4-public-dogfood-contract.mjs +270 -0
  186. package/scripts/create-release-bundle.mjs +8 -4
  187. package/scripts/dev-alpha-candidate-patrol.mjs +1 -22
  188. package/scripts/dev-delivery-proof.mjs +2 -64
  189. package/scripts/dev-delivery-warrant.mjs +61 -86
  190. package/scripts/dev-pr-auto-merge.mjs +4 -30
  191. package/scripts/dev-pr-delivery-warrant.mjs +0 -55
  192. package/scripts/dispatch-artifact-signing-authority.mjs +7 -4
  193. package/scripts/generate-buildchain-kfd-witnesses.mjs +94 -88
  194. package/scripts/generate-channel-build-workflow.mjs +54 -24
  195. package/scripts/generate-channel-promotion-workflow.mjs +15 -6
  196. package/scripts/generate-site-bundle.mjs +8 -27
  197. package/scripts/git-fetch-process-tree.mjs +34 -1
  198. package/scripts/init-repo.mjs +64 -159
  199. package/scripts/inspect-artifact-signing-requests.mjs +0 -6
  200. package/scripts/locked-source-checkout.mjs +3 -4
  201. package/scripts/maintainability-metrics.mjs +8 -2
  202. package/scripts/npm-publish-dry-run.mjs +2 -2
  203. package/scripts/npm-publish-transaction.mjs +2 -2
  204. package/scripts/publication-commit-evidence.mjs +23 -69
  205. package/scripts/release-candidate-resolver.mjs +11 -17
  206. package/scripts/release-tail.mjs +3 -49
  207. package/scripts/resume-from-candidate-run.mjs +9 -123
  208. package/scripts/run-lifecycle-core.mjs +12 -13
  209. package/scripts/seal-artifact-signing-requests.mjs +17 -21
  210. package/scripts/site-capability-metadata.mjs +3 -20
  211. package/scripts/stable-candidate-qualification.mjs +10 -10
  212. package/scripts/v4-architecture.mjs +12 -2
  213. package/scripts/v4-bridge-bootstrap.mjs +141 -0
  214. package/scripts/v4-bridge-evidence.mjs +141 -0
  215. package/scripts/v4-host-adapter.mjs +182 -0
  216. package/scripts/v4-platform-stage-checkpoint-rehearsal.mjs +207 -0
  217. package/scripts/v4-stage-capsule-qualification.mjs +528 -0
  218. package/scripts/v4-stage-capsule-resume-rehearsal.mjs +57 -0
  219. package/scripts/v4-warrant-shadow-plan.mjs +601 -0
  220. package/scripts/verify-golden-path.mjs +5 -5
  221. package/scripts/web-surface-cloudfront-rewrite.mjs +15 -15
  222. package/scripts/web-surface-core.mjs +2 -8
  223. package/scripts/workflow-call-contract.mjs +5 -184
  224. package/contracts/dev-delivery-authority-v2.schema.json +0 -253
  225. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +0 -16
  226. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +0 -57
  227. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +0 -47
  228. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +0 -47
  229. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +0 -40
  230. package/contracts/next-development-request-v1.schema.json +0 -66
  231. package/contracts/next-development-transition-v1.schema.json +0 -292
  232. package/contracts/publication-rehearsal-capsule-v1.schema.json +0 -173
  233. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +0 -442
  234. package/dist/site/schemas/publication-rehearsal-capsule-v1.schema.json +0 -269
  235. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +0 -353
  236. package/dist/site/schemas/release-tail-provider-bindings-v1.schema.json +0 -94
  237. package/docs/dev-delivery-qualification-landing-adr.md +0 -137
  238. package/docs/next-development-transition.md +0 -119
  239. package/docs/publication-rehearsal.md +0 -94
  240. package/packages/core/buildchain-compatibility-proof.js +0 -631
  241. package/packages/core/dev-delivery-authority.js +0 -996
  242. package/packages/core/dev-delivery-native-proof.js +0 -418
  243. package/packages/core/dev-delivery-warrant-shadow.js +0 -502
  244. package/packages/core/next-development-candidate-reservation.js +0 -186
  245. package/packages/core/next-development-controller.js +0 -726
  246. package/packages/core/next-development-projection.js +0 -288
  247. package/packages/core/next-development-transition.js +0 -738
  248. package/packages/core/publication-rehearsal-projection.js +0 -173
  249. package/packages/core/publication-rehearsal-runtime.js +0 -921
  250. package/scripts/aws-macos-jit-instance-rehydrate.mjs +0 -260
  251. package/scripts/aws-macos-jit-source-rebind.mjs +0 -724
  252. package/scripts/dev-delivery-authority.mjs +0 -368
  253. package/scripts/dev-delivery-native-run.mjs +0 -235
  254. package/scripts/dev-delivery-two-phase-resume.mjs +0 -90
  255. package/scripts/dev-delivery-two-phase.mjs +0 -577
  256. package/scripts/generate-next-development-guidance.mjs +0 -49
  257. package/scripts/materialize-self-release-candidate-version.mjs +0 -137
  258. package/scripts/next-development-self-dogfood-harness.mjs +0 -409
  259. package/scripts/next-development-self-dogfood.mjs +0 -532
  260. package/scripts/next-development-transition.mjs +0 -47
  261. package/scripts/release-candidate-tail-reseal.mjs +0 -426
  262. package/templates/native-dev-delivery.yml +0 -86
@@ -1,502 +0,0 @@
1
- import {
2
- devDeliveryClone as clone,
3
- devDeliveryContentRoot,
4
- devDeliveryExactRoot as exactRoot,
5
- devDeliveryExactSha as exactSha,
6
- devDeliveryPositiveInteger as positiveInteger,
7
- devDeliveryText as text,
8
- devDeliveryTimestamp as timestamp,
9
- } from "./dev-delivery-common.js";
10
- import {
11
- normalizeDevDeliveryQueue,
12
- rankDevDeliveryCandidates,
13
- } from "./dev-delivery-warrant.js";
14
-
15
- export const DEV_DELIVERY_WARRANT_SHADOW_OBSERVATION_SCHEMA =
16
- "kungfu.buildchain.dev-delivery-warrant-shadow-observation/v1";
17
- export const DEV_DELIVERY_WARRANT_SHADOW_PLAN_SCHEMA =
18
- "kungfu.buildchain.dev-delivery-warrant-shadow-plan/v1";
19
- export const DEV_DELIVERY_WARRANT_SHADOW_QUALIFICATION_SCHEMA =
20
- "kungfu.buildchain.dev-delivery-warrant-shadow-qualification/v1";
21
-
22
- const EVIDENCE_STATES = Object.freeze({
23
- projectCut: "qualified",
24
- approval: "approved",
25
- requiredChecks: "passed",
26
- status: "ready",
27
- });
28
-
29
- function nonNegativeInteger(value, label, fallback = 0) {
30
- const parsed = Number(value ?? fallback);
31
- if (!Number.isInteger(parsed) || parsed < 0) {
32
- throw new Error(`${label} must be a non-negative integer`);
33
- }
34
- return parsed;
35
- }
36
-
37
- function boolean(value, label) {
38
- if (typeof value !== "boolean") throw new Error(`${label} must be boolean`);
39
- return value;
40
- }
41
-
42
- function evidenceBinding(input, label) {
43
- const binding = input || {};
44
- return {
45
- candidateId: exactRoot(binding.candidateId, `${label}.candidateId`),
46
- sourceHead: exactSha(binding.sourceHead, `${label}.sourceHead`),
47
- baseHead: exactSha(binding.baseHead, `${label}.baseHead`),
48
- root: exactRoot(binding.root, `${label}.root`),
49
- state: text(binding.state),
50
- };
51
- }
52
-
53
- function laneBinding(input) {
54
- const binding = input || {};
55
- return {
56
- candidateId: exactRoot(binding.candidateId, "binding.candidateId"),
57
- sourceHead: exactSha(binding.sourceHead, "binding.sourceHead"),
58
- baseHead: exactSha(binding.baseHead, "binding.baseHead"),
59
- queueGeneration: nonNegativeInteger(
60
- binding.queueGeneration,
61
- "binding.queueGeneration",
62
- ),
63
- queueStateRoot: exactRoot(binding.queueStateRoot, "binding.queueStateRoot"),
64
- projectedBaseRoot: exactRoot(
65
- binding.projectedBaseRoot,
66
- "binding.projectedBaseRoot",
67
- ),
68
- warrantFencingToken: binding.warrantFencingToken
69
- ? exactRoot(binding.warrantFencingToken, "binding.warrantFencingToken")
70
- : null,
71
- warrantGeneration:
72
- binding.warrantGeneration == null
73
- ? null
74
- : positiveInteger(
75
- binding.warrantGeneration,
76
- "binding.warrantGeneration",
77
- ),
78
- projectCut: evidenceBinding(binding.projectCut, "binding.projectCut"),
79
- approval: evidenceBinding(binding.approval, "binding.approval"),
80
- requiredChecks: evidenceBinding(
81
- binding.requiredChecks,
82
- "binding.requiredChecks",
83
- ),
84
- status: evidenceBinding(binding.status, "binding.status"),
85
- lease: {
86
- ...evidenceBinding(binding.lease, "binding.lease"),
87
- state: text(binding.lease?.state),
88
- },
89
- conflictKeys: [
90
- ...new Set((binding.conflictKeys || []).map((entry) => text(entry))),
91
- ]
92
- .filter(Boolean)
93
- .sort(),
94
- expectedEligible:
95
- binding.expectedEligible == null
96
- ? null
97
- : boolean(binding.expectedEligible, "binding.expectedEligible"),
98
- };
99
- }
100
-
101
- function normalizeObservation(input) {
102
- const observation = clone(input || {});
103
- if (observation.schema !== DEV_DELIVERY_WARRANT_SHADOW_OBSERVATION_SCHEMA) {
104
- throw new Error(
105
- `shadow observation must use ${DEV_DELIVERY_WARRANT_SHADOW_OBSERVATION_SCHEMA}`,
106
- );
107
- }
108
- const queue = normalizeDevDeliveryQueue(observation.queue);
109
- const normalized = {
110
- schema: observation.schema,
111
- observationId: text(observation.observationId),
112
- kind: text(observation.kind || "replay"),
113
- observedAt: timestamp(observation.observedAt, "observation.observedAt"),
114
- protectedBaseHead: exactSha(
115
- observation.protectedBaseHead,
116
- "observation.protectedBaseHead",
117
- ),
118
- projectedBaseRoot: exactRoot(
119
- observation.projectedBaseRoot,
120
- "observation.projectedBaseRoot",
121
- ),
122
- queue,
123
- nativeQueue: {
124
- occupied: boolean(
125
- observation.nativeQueue?.occupied,
126
- "observation.nativeQueue.occupied",
127
- ),
128
- entryCount: nonNegativeInteger(
129
- observation.nativeQueue?.entryCount,
130
- "observation.nativeQueue.entryCount",
131
- ),
132
- root: exactRoot(
133
- observation.nativeQueue?.root,
134
- "observation.nativeQueue.root",
135
- ),
136
- },
137
- candidateBindings: (observation.candidateBindings || []).map(laneBinding),
138
- metrics: {
139
- baselineQueueWaitSeconds: nonNegativeInteger(
140
- observation.metrics?.baselineQueueWaitSeconds,
141
- "observation.metrics.baselineQueueWaitSeconds",
142
- ),
143
- shadowQueueWaitSeconds: nonNegativeInteger(
144
- observation.metrics?.shadowQueueWaitSeconds,
145
- "observation.metrics.shadowQueueWaitSeconds",
146
- ),
147
- additionalCheckSeconds: nonNegativeInteger(
148
- observation.metrics?.additionalCheckSeconds,
149
- "observation.metrics.additionalCheckSeconds",
150
- ),
151
- additionalRunnerSeconds: nonNegativeInteger(
152
- observation.metrics?.additionalRunnerSeconds,
153
- "observation.metrics.additionalRunnerSeconds",
154
- ),
155
- ambiguous: boolean(
156
- observation.metrics?.ambiguous ?? false,
157
- "observation.metrics.ambiguous",
158
- ),
159
- },
160
- };
161
- if (!normalized.observationId) {
162
- throw new Error("observation.observationId is required");
163
- }
164
- const bindingIds = normalized.candidateBindings.map(
165
- (binding) => binding.candidateId,
166
- );
167
- if (new Set(bindingIds).size !== bindingIds.length) {
168
- throw new Error("shadow observation contains duplicate candidate bindings");
169
- }
170
- return normalized;
171
- }
172
-
173
- function productionOrder(queue, now) {
174
- const active = queue.activeWarrant
175
- ? queue.candidates.find(
176
- (candidate) =>
177
- candidate.candidateId === queue.activeWarrant.candidateId,
178
- )
179
- : null;
180
- const queued = rankDevDeliveryCandidates(queue, { now }).map(
181
- (entry) => entry.candidate,
182
- );
183
- return active ? [active, ...queued] : queued;
184
- }
185
-
186
- function bindingReasons({ binding, candidate, observation }) {
187
- const reasons = [];
188
- const expect = (condition, reason) => {
189
- if (!condition) reasons.push(reason);
190
- };
191
- expect(
192
- binding.candidateId === candidate.candidateId,
193
- "candidate-id-mismatch",
194
- );
195
- expect(binding.sourceHead === candidate.sourceHead, "stale-source-head");
196
- expect(binding.baseHead === observation.protectedBaseHead, "stale-base-head");
197
- expect(
198
- binding.queueGeneration === observation.queue.generation,
199
- "stale-queue-generation",
200
- );
201
- expect(
202
- binding.queueStateRoot === observation.queue.stateRoot,
203
- "stale-queue-state-root",
204
- );
205
- expect(
206
- binding.projectedBaseRoot === observation.projectedBaseRoot,
207
- "incompatible-projected-base",
208
- );
209
- for (const [name, acceptedState] of Object.entries(EVIDENCE_STATES)) {
210
- const evidence = binding[name];
211
- expect(
212
- evidence.candidateId === candidate.candidateId,
213
- `${name}-candidate-mismatch`,
214
- );
215
- expect(
216
- evidence.sourceHead === candidate.sourceHead,
217
- `${name}-head-mismatch`,
218
- );
219
- expect(
220
- evidence.baseHead === observation.protectedBaseHead,
221
- `${name}-base-mismatch`,
222
- );
223
- expect(evidence.state === acceptedState, `${name}-not-${acceptedState}`);
224
- }
225
- expect(
226
- binding.lease.candidateId === candidate.candidateId,
227
- "lease-candidate-mismatch",
228
- );
229
- expect(
230
- binding.lease.sourceHead === candidate.sourceHead,
231
- "lease-head-mismatch",
232
- );
233
- expect(
234
- binding.lease.baseHead === observation.protectedBaseHead,
235
- "lease-base-mismatch",
236
- );
237
- const active = observation.queue.activeWarrant;
238
- if (active?.candidateId === candidate.candidateId) {
239
- expect(binding.lease.state === "active", "active-lease-not-bound");
240
- expect(
241
- binding.warrantFencingToken === active.fencingToken,
242
- "stale-warrant-fence",
243
- );
244
- expect(
245
- binding.warrantGeneration === active.generation,
246
- "stale-warrant-generation",
247
- );
248
- } else {
249
- expect(
250
- binding.lease.state === "available",
251
- "queued-lane-lease-unavailable",
252
- );
253
- expect(binding.warrantFencingToken === null, "queued-lane-has-live-fence");
254
- expect(
255
- binding.warrantGeneration === null,
256
- "queued-lane-has-live-generation",
257
- );
258
- }
259
- return reasons;
260
- }
261
-
262
- function aliasedLaneIndexes(lanes) {
263
- const indexes = new Set();
264
- const roots = new Map();
265
- const fields = [
266
- "projectCut",
267
- "approval",
268
- "requiredChecks",
269
- "status",
270
- "lease",
271
- ];
272
- lanes.forEach((lane, index) => {
273
- if (!lane.binding) return;
274
- for (const field of fields) {
275
- const key = `${field}:${lane.binding[field].root}`;
276
- const prior = roots.get(key);
277
- if (prior != null) {
278
- indexes.add(prior);
279
- indexes.add(index);
280
- } else roots.set(key, index);
281
- }
282
- });
283
- return indexes;
284
- }
285
-
286
- function conflictingLaneIndexes(lanes) {
287
- const indexes = new Set();
288
- for (let left = 0; left < lanes.length; left += 1) {
289
- for (let right = left + 1; right < lanes.length; right += 1) {
290
- if (!lanes[left].binding || !lanes[right].binding) continue;
291
- const rightKeys = new Set(lanes[right].binding.conflictKeys);
292
- if (lanes[left].binding.conflictKeys.some((key) => rightKeys.has(key))) {
293
- indexes.add(left);
294
- indexes.add(right);
295
- }
296
- }
297
- }
298
- return indexes;
299
- }
300
-
301
- export function planDevDeliveryWarrantShadow(
302
- input,
303
- { maxConcurrency = 2 } = {},
304
- ) {
305
- const requested = positiveInteger(maxConcurrency, "maxConcurrency");
306
- if (requested > 2) throw new Error("shadow maxConcurrency cannot exceed 2");
307
- const observation = normalizeObservation(input);
308
- const order = productionOrder(observation.queue, observation.observedAt);
309
- const candidates = order.slice(0, requested);
310
- const bindings = new Map(
311
- observation.candidateBindings.map((binding) => [
312
- binding.candidateId,
313
- binding,
314
- ]),
315
- );
316
- const lanes = candidates.map((candidate) => {
317
- const binding = bindings.get(candidate.candidateId);
318
- const reasonCodes = binding
319
- ? bindingReasons({ binding, candidate, observation })
320
- : ["candidate-binding-missing"];
321
- return { candidate, binding, reasonCodes };
322
- });
323
- if (observation.nativeQueue.occupied) {
324
- for (const lane of lanes) lane.reasonCodes.push("native-queue-occupied");
325
- }
326
- for (const index of aliasedLaneIndexes(lanes)) {
327
- lanes[index].reasonCodes.push("cross-lane-binding-alias");
328
- }
329
- for (const index of conflictingLaneIndexes(lanes)) {
330
- lanes[index].reasonCodes.push("cross-lane-conflict");
331
- }
332
- const plannedLanes = lanes.map((lane) => {
333
- const reasonCodes = [...new Set(lane.reasonCodes)].sort();
334
- const laneBody = {
335
- schema: "kungfu.buildchain.dev-delivery-warrant-shadow-lane/v1",
336
- candidateId: lane.candidate.candidateId,
337
- pullRequestNumber: lane.candidate.pullRequestNumber,
338
- sourceHead: lane.candidate.sourceHead,
339
- baseHead: observation.protectedBaseHead,
340
- queueStateRoot: observation.queue.stateRoot,
341
- bindingRoot: lane.binding ? devDeliveryContentRoot(lane.binding) : null,
342
- accepted: reasonCodes.length === 0,
343
- reasonCodes,
344
- effects: [],
345
- productionAuthority: false,
346
- };
347
- return {
348
- ...laneBody,
349
- laneId: devDeliveryContentRoot({
350
- schema: laneBody.schema,
351
- candidateId: laneBody.candidateId,
352
- sourceHead: laneBody.sourceHead,
353
- baseHead: laneBody.baseHead,
354
- queueStateRoot: laneBody.queueStateRoot,
355
- }),
356
- };
357
- });
358
- const productionCandidateId = order[0]?.candidateId || null;
359
- const body = {
360
- schema: DEV_DELIVERY_WARRANT_SHADOW_PLAN_SCHEMA,
361
- observationId: observation.observationId,
362
- observationRoot: devDeliveryContentRoot(observation),
363
- observedAt: observation.observedAt,
364
- maxConcurrency: requested,
365
- productionCandidateId,
366
- singleFlightParity: {
367
- evaluated: requested === 1,
368
- candidateIdentityMatches:
369
- requested === 1 &&
370
- (plannedLanes[0]?.candidateId || null) === productionCandidateId,
371
- },
372
- lanes: plannedLanes,
373
- acceptedLaneCount: plannedLanes.filter((lane) => lane.accepted).length,
374
- rejectedLaneCount: plannedLanes.filter((lane) => !lane.accepted).length,
375
- deferredCandidateIds: order
376
- .slice(requested)
377
- .map((candidate) => candidate.candidateId),
378
- metrics: observation.metrics,
379
- decision:
380
- plannedLanes.some((lane) => lane.accepted) &&
381
- !observation.nativeQueue.occupied
382
- ? "qualified-shadow-only"
383
- : "hold",
384
- effects: [],
385
- mutationAllowed: false,
386
- productionAuthority: "unchanged-single-flight",
387
- rolloutAuthorized: false,
388
- };
389
- return { ...body, planRoot: devDeliveryContentRoot(body) };
390
- }
391
-
392
- function thresholds(input = {}) {
393
- return {
394
- minObservationCount: positiveInteger(
395
- input.minObservationCount,
396
- "thresholds.minObservationCount",
397
- 8,
398
- ),
399
- minEligibleOverlapCount: positiveInteger(
400
- input.minEligibleOverlapCount,
401
- "thresholds.minEligibleOverlapCount",
402
- 2,
403
- ),
404
- minProjectedQueueWaitBenefitSeconds: nonNegativeInteger(
405
- input.minProjectedQueueWaitBenefitSeconds,
406
- "thresholds.minProjectedQueueWaitBenefitSeconds",
407
- 300,
408
- ),
409
- maxAdditionalRunnerSeconds: nonNegativeInteger(
410
- input.maxAdditionalRunnerSeconds,
411
- "thresholds.maxAdditionalRunnerSeconds",
412
- 7200,
413
- ),
414
- maxAmbiguityCount: nonNegativeInteger(
415
- input.maxAmbiguityCount,
416
- "thresholds.maxAmbiguityCount",
417
- 0,
418
- ),
419
- maxFalsePositiveCount: nonNegativeInteger(
420
- input.maxFalsePositiveCount,
421
- "thresholds.maxFalsePositiveCount",
422
- 0,
423
- ),
424
- };
425
- }
426
-
427
- export function qualifyDevDeliveryWarrantShadow(input = {}) {
428
- const observations = Array.isArray(input.observations)
429
- ? input.observations
430
- : [];
431
- const policy = thresholds(input.thresholds);
432
- const plans = observations.map((observation) =>
433
- planDevDeliveryWarrantShadow(observation, { maxConcurrency: 2 }),
434
- );
435
- const metrics = plans.reduce(
436
- (summary, plan) => {
437
- if (plan.lanes.filter((lane) => lane.accepted).length === 2) {
438
- summary.eligibleOverlapCount += 1;
439
- }
440
- summary.projectedQueueWaitBenefitSeconds += Math.max(
441
- 0,
442
- plan.metrics.baselineQueueWaitSeconds -
443
- plan.metrics.shadowQueueWaitSeconds,
444
- );
445
- summary.additionalCheckSeconds += plan.metrics.additionalCheckSeconds;
446
- summary.additionalRunnerSeconds += plan.metrics.additionalRunnerSeconds;
447
- if (plan.metrics.ambiguous) summary.ambiguityCount += 1;
448
- const observation = normalizeObservation(
449
- observations[summary.observationCount],
450
- );
451
- const expected = new Map(
452
- observation.candidateBindings.map((binding) => [
453
- binding.candidateId,
454
- binding.expectedEligible,
455
- ]),
456
- );
457
- summary.falsePositiveCount += plan.lanes.filter(
458
- (lane) => lane.accepted && expected.get(lane.candidateId) === false,
459
- ).length;
460
- summary.observationCount += 1;
461
- return summary;
462
- },
463
- {
464
- observationCount: 0,
465
- eligibleOverlapCount: 0,
466
- projectedQueueWaitBenefitSeconds: 0,
467
- additionalCheckSeconds: 0,
468
- additionalRunnerSeconds: 0,
469
- ambiguityCount: 0,
470
- falsePositiveCount: 0,
471
- },
472
- );
473
- const checks = {
474
- observationCount: metrics.observationCount >= policy.minObservationCount,
475
- eligibleOverlap:
476
- metrics.eligibleOverlapCount >= policy.minEligibleOverlapCount,
477
- projectedQueueWaitBenefit:
478
- metrics.projectedQueueWaitBenefitSeconds >=
479
- policy.minProjectedQueueWaitBenefitSeconds,
480
- runnerCost:
481
- metrics.additionalRunnerSeconds <= policy.maxAdditionalRunnerSeconds,
482
- ambiguity: metrics.ambiguityCount <= policy.maxAmbiguityCount,
483
- falsePositives: metrics.falsePositiveCount <= policy.maxFalsePositiveCount,
484
- };
485
- const decision = Object.values(checks).every(Boolean) ? "proceed" : "hold";
486
- const body = {
487
- schema: DEV_DELIVERY_WARRANT_SHADOW_QUALIFICATION_SCHEMA,
488
- decision,
489
- reasonCodes: Object.entries(checks)
490
- .filter(([, passed]) => !passed)
491
- .map(([name]) => `threshold-${name}-not-met`),
492
- thresholds: policy,
493
- metrics,
494
- checks,
495
- planRoots: plans.map((plan) => plan.planRoot),
496
- effects: [],
497
- mutationAllowed: false,
498
- productionAuthority: "unchanged-single-flight",
499
- rolloutAuthorized: false,
500
- };
501
- return { ...body, qualificationRoot: devDeliveryContentRoot(body), plans };
502
- }
@@ -1,186 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
-
3
- import crypto from "node:crypto";
4
-
5
- const SHA = /^[0-9a-f]{40}$/u;
6
- const ROOT = /^sha256:[0-9a-f]{64}$/u;
7
-
8
- function text(value = "") {
9
- return String(value ?? "").trim();
10
- }
11
-
12
- function exactSha(value, label) {
13
- const normalized = text(value).toLowerCase();
14
- if (!SHA.test(normalized)) {
15
- throw new Error(`${label} must be an exact 40-character SHA`);
16
- }
17
- return normalized;
18
- }
19
-
20
- function exactRoot(value, label) {
21
- const normalized = text(value).toLowerCase();
22
- if (!ROOT.test(normalized)) {
23
- throw new Error(`${label} must be an exact sha256 content root`);
24
- }
25
- return normalized;
26
- }
27
-
28
- function canonical(value) {
29
- if (Array.isArray(value)) return value.map(canonical);
30
- if (value && typeof value === "object") {
31
- return Object.fromEntries(
32
- Object.entries(value)
33
- .sort(([left], [right]) => left.localeCompare(right))
34
- .map(([key, item]) => [key, canonical(item)]),
35
- );
36
- }
37
- return value;
38
- }
39
-
40
- function evidenceRoot(value) {
41
- return `sha256:${crypto
42
- .createHash("sha256")
43
- .update(JSON.stringify(canonical(value)))
44
- .digest("hex")}`;
45
- }
46
-
47
- export function nextDevelopmentPreparedVersion(message) {
48
- const title = text(message).split("\n", 1)[0];
49
- return (
50
- title.match(
51
- /^(?:chore\(release\): prepare|Prepare)\s+v?([0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?)$/u,
52
- )?.[1] || ""
53
- );
54
- }
55
-
56
- function preparation(entry) {
57
- if (!entry || typeof entry === "string") return undefined;
58
- const targetVersion = nextDevelopmentPreparedVersion(entry.message);
59
- if (!targetVersion) return undefined;
60
- return {
61
- sha: exactSha(entry.sha, "sourceHistory.sha"),
62
- targetVersion,
63
- };
64
- }
65
-
66
- export function nextDevelopmentPatrolContext(sourceHistory) {
67
- const preparations = sourceHistory
68
- .map((entry, index) => ({ index, preparation: preparation(entry) }))
69
- .filter((entry) => entry.preparation);
70
- const preparationShas = new Set(
71
- preparations.map(({ preparation: item }) => item.sha),
72
- );
73
- const integrationShas = sourceHistory
74
- .filter(
75
- (entry) =>
76
- entry &&
77
- typeof entry !== "string" &&
78
- Array.isArray(entry.parents) &&
79
- entry.parents.length === 2 &&
80
- entry.parents.some((parent) => preparationShas.has(parent)),
81
- )
82
- .map((entry) => exactSha(entry.sha, "sourceHistory.sha"));
83
- return {
84
- preparations,
85
- ignoredSourceShas: [...preparationShas, ...integrationShas],
86
- };
87
- }
88
-
89
- export function normalizeNextDevelopmentVersionReservation(input, expected) {
90
- const status = text(input?.status);
91
- if (!["current", "absent", "stale"].includes(status)) {
92
- throw new Error("next-development version reservation status is invalid");
93
- }
94
- const normalized = {
95
- status,
96
- reservationSha: exactSha(
97
- input?.reservationSha,
98
- "versionReservation.reservationSha",
99
- ),
100
- candidateSha: exactSha(
101
- input?.candidateSha,
102
- "versionReservation.candidateSha",
103
- ),
104
- targetVersion: text(input?.targetVersion),
105
- paths: [...new Set((input?.paths || []).map(text))].sort(),
106
- evidenceRoot: exactRoot(
107
- input?.evidenceRoot,
108
- "versionReservation.evidenceRoot",
109
- ),
110
- };
111
- if (
112
- normalized.reservationSha !== expected.reservationSha ||
113
- normalized.candidateSha !== expected.candidateSha ||
114
- normalized.targetVersion !== expected.targetVersion
115
- ) {
116
- throw new Error("next-development version reservation identity drifted");
117
- }
118
- if (normalized.paths.length === 0 || normalized.paths.some((path) => !path)) {
119
- throw new Error("next-development version reservation paths are required");
120
- }
121
- return normalized;
122
- }
123
-
124
- function encodeRepositoryPath(value) {
125
- return value.split("/").map(encodeURIComponent).join("/");
126
- }
127
-
128
- export async function readGitHubNextDevelopmentVersionReservation({
129
- api,
130
- owner,
131
- repo,
132
- reservationSha,
133
- candidateSha,
134
- targetVersion,
135
- }) {
136
- const commit = await api(
137
- `/repos/${owner}/${repo}/commits/${encodeURIComponent(reservationSha)}`,
138
- );
139
- if (
140
- text(commit.sha) !== reservationSha ||
141
- nextDevelopmentPreparedVersion(commit.commit?.message) !== targetVersion
142
- ) {
143
- throw new Error(
144
- "next-development preparation commit identity or version drifted",
145
- );
146
- }
147
- const paths = [
148
- ...new Set((commit.files || []).map((file) => text(file.filename))),
149
- ]
150
- .filter(Boolean)
151
- .sort();
152
- if (paths.length === 0) {
153
- throw new Error(
154
- "next-development preparation commit has no version-state paths",
155
- );
156
- }
157
- const observations = [];
158
- let status = "current";
159
- for (const filePath of paths) {
160
- const endpoint = `/repos/${owner}/${repo}/contents/${encodeRepositoryPath(filePath)}`;
161
- const prepared = await api(
162
- `${endpoint}?ref=${encodeURIComponent(reservationSha)}`,
163
- { allow404: true },
164
- );
165
- const candidate = await api(
166
- `${endpoint}?ref=${encodeURIComponent(candidateSha)}`,
167
- { allow404: true },
168
- );
169
- const preparedBlob =
170
- text(prepared?.type) === "file" ? text(prepared?.sha) : "";
171
- const candidateBlob =
172
- text(candidate?.type) === "file" ? text(candidate?.sha) : "";
173
- if (!candidateBlob) status = "absent";
174
- else if (!preparedBlob || candidateBlob !== preparedBlob) status = "stale";
175
- observations.push({ path: filePath, preparedBlob, candidateBlob });
176
- }
177
- const body = {
178
- status,
179
- reservationSha,
180
- candidateSha,
181
- targetVersion,
182
- paths,
183
- observations,
184
- };
185
- return { ...body, evidenceRoot: evidenceRoot(body) };
186
- }