@kungfu-tech/buildchain 4.0.1-alpha.2 → 4.0.1-alpha.3

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 (75) hide show
  1. package/architecture/decisions/0003-two-phase-delivery-warrant.md +152 -0
  2. package/architecture/internal-capabilities.json +94 -0
  3. package/architecture/maintainability-policy.json +75 -8
  4. package/architecture/v3-core-mechanism-inventory.json +2 -0
  5. package/architecture/v4-delivery-authority-parity.json +250 -0
  6. package/architecture/v4-delivery-warrant-shadow-fixtures.json +29 -6
  7. package/bin/buildchain.mjs +9 -1
  8. package/contracts/dev-delivery-authority-v2.schema.json +662 -0
  9. package/dist/site/agent-index.json +1 -0
  10. package/dist/site/artifact-schemas.json +2 -0
  11. package/dist/site/buildchain-contract.json +89 -6
  12. package/dist/site/buildchain-site.json +169 -18
  13. package/dist/site/capability-registry.json +2 -2
  14. package/dist/site/cli-registry.json +46 -2
  15. package/dist/site/kfd-claims.json +43 -9
  16. package/dist/site/kfd-upstream-aggregate.json +1 -1
  17. package/dist/site/manual-registry.json +3 -3
  18. package/dist/site/node-api-registry.json +1694 -165
  19. package/dist/site/page-registry.json +162 -11
  20. package/dist/site/public-surface-audit.json +367 -7
  21. package/dist/site/publication-registry.json +4 -4
  22. package/dist/site/release-provenance.json +2 -0
  23. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +662 -0
  24. package/dist/site/site-manifest.json +7 -7
  25. package/dist/site/workflow-registry.json +22 -6
  26. package/docs/MAP.md +1 -0
  27. package/docs/cli-reference.md +211 -13
  28. package/docs/dev-delivery-qualification-landing-adr.md +251 -0
  29. package/docs/dev-delivery-warrant.md +326 -38
  30. package/docs/node-api-reference.md +113 -53
  31. package/package.json +5 -1
  32. package/packages/core/buildchain-contract.js +3 -2
  33. package/packages/core/dev-delivery-authority-candidate.js +270 -0
  34. package/packages/core/dev-delivery-authority-evidence.js +146 -0
  35. package/packages/core/dev-delivery-authority-landing.js +461 -0
  36. package/packages/core/dev-delivery-authority-observation.js +48 -0
  37. package/packages/core/dev-delivery-authority-qualification.js +591 -0
  38. package/packages/core/dev-delivery-authority-settlement.js +213 -0
  39. package/packages/core/dev-delivery-authority-state.js +583 -0
  40. package/packages/core/dev-delivery-candidate-identity.js +13 -0
  41. package/packages/core/dev-delivery-contract-surface.js +76 -0
  42. package/packages/core/dev-delivery-execution-failure.js +133 -0
  43. package/packages/core/dev-delivery-execution-transfer.js +572 -0
  44. package/packages/core/dev-delivery-landing-admission-core.js +119 -0
  45. package/packages/core/dev-delivery-landing-readback.js +598 -0
  46. package/packages/core/dev-delivery-landing-terminal-evidence.js +271 -0
  47. package/packages/core/dev-delivery-landing-testing-port.js +6 -0
  48. package/packages/core/dev-delivery-native-execution.js +110 -0
  49. package/packages/core/dev-delivery-native-proof.js +546 -0
  50. package/packages/core/dev-delivery-process-boundary.js +551 -0
  51. package/packages/core/dev-delivery-provider-attempt.js +127 -0
  52. package/packages/core/dev-delivery-provider-heartbeat.js +370 -0
  53. package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
  54. package/packages/core/dev-delivery-warrant-qualification.js +145 -0
  55. package/packages/core/dev-delivery-warrant-settlement.js +237 -36
  56. package/packages/core/dev-delivery-warrant-state.js +565 -0
  57. package/packages/core/dev-delivery-warrant.js +320 -368
  58. package/scripts/buildchain-cli-help.mjs +11 -2
  59. package/scripts/dev-delivery-authority-command-adapters.mjs +206 -0
  60. package/scripts/dev-delivery-authority-provider.mjs +28 -0
  61. package/scripts/dev-delivery-authority.mjs +490 -0
  62. package/scripts/dev-delivery-native-run.mjs +177 -0
  63. package/scripts/dev-delivery-process-boundary.mjs +260 -0
  64. package/scripts/dev-delivery-proof.mjs +67 -2
  65. package/scripts/dev-delivery-provider-heartbeat.mjs +215 -0
  66. package/scripts/dev-delivery-two-phase-resume.mjs +345 -0
  67. package/scripts/dev-delivery-two-phase.mjs +573 -0
  68. package/scripts/dev-delivery-warrant-options.mjs +266 -0
  69. package/scripts/dev-delivery-warrant-store.mjs +227 -0
  70. package/scripts/dev-delivery-warrant.mjs +227 -193
  71. package/scripts/dev-pr-delivery-warrant.mjs +10 -0
  72. package/scripts/generate-site-bundle.mjs +26 -4
  73. package/scripts/npm-publish-transaction.mjs +2 -2
  74. package/scripts/site-capability-metadata.mjs +12 -0
  75. package/templates/native-dev-delivery.yml +141 -0
@@ -0,0 +1,345 @@
1
+ import { execFileSync } from "node:child_process";
2
+
3
+ import {
4
+ createNativeCommandContract,
5
+ createNativeProofReuseDecision,
6
+ createNativeQualificationProof,
7
+ } from "../packages/core/dev-delivery-warrant.js";
8
+
9
+ const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/u;
10
+
11
+ function exactLocalSha(value, label) {
12
+ const normalized = String(value || "")
13
+ .trim()
14
+ .toLowerCase();
15
+ if (!/^[0-9a-f]{40}$/u.test(normalized))
16
+ throw new Error(`${label} must be an exact lowercase Git SHA`);
17
+ return normalized;
18
+ }
19
+
20
+ export class LocalTwoPhaseClient {
21
+ constructor({ candidateDirectory } = {}) {
22
+ this.candidateDirectory = candidateDirectory;
23
+ }
24
+
25
+ git(args, options = {}) {
26
+ return execFileSync("git", ["-C", this.candidateDirectory, ...args], {
27
+ encoding: "utf8",
28
+ ...options,
29
+ }).trim();
30
+ }
31
+
32
+ async baseSha(branch) {
33
+ return exactLocalSha(
34
+ this.git(["rev-parse", `refs/remotes/origin/${branch}`]),
35
+ "protected base SHA",
36
+ );
37
+ }
38
+
39
+ async exactPullRequestHead(_pullRequestNumber, expectedHead) {
40
+ const observed = exactLocalSha(this.git(["rev-parse", "HEAD"]), "PR head");
41
+ if (observed !== expectedHead)
42
+ throw new Error(
43
+ `semantic source head changed: ${observed} != ${expectedHead}`,
44
+ );
45
+ return observed;
46
+ }
47
+
48
+ async baseDelta(previousBase, currentBase) {
49
+ if (previousBase === currentBase)
50
+ return {
51
+ graphKnown: true,
52
+ attributionComplete: true,
53
+ changedPaths: [],
54
+ renames: [],
55
+ };
56
+ try {
57
+ this.git(["merge-base", "--is-ancestor", previousBase, currentBase]);
58
+ } catch {
59
+ return {
60
+ graphKnown: false,
61
+ attributionComplete: false,
62
+ changedPaths: [],
63
+ renames: [],
64
+ };
65
+ }
66
+ const entries = this.git([
67
+ "diff",
68
+ "--name-status",
69
+ "-M",
70
+ previousBase,
71
+ currentBase,
72
+ ])
73
+ .split("\n")
74
+ .filter(Boolean)
75
+ .map((line) => line.split("\t"));
76
+ const renames = entries
77
+ .filter(([status]) => status.startsWith("R"))
78
+ .map(([, from, to]) => ({ from, to }));
79
+ return {
80
+ graphKnown: true,
81
+ attributionComplete: renames.every(({ from, to }) => from && to),
82
+ changedPaths: [
83
+ ...new Set(entries.flatMap(([, ...paths]) => paths).filter(Boolean)),
84
+ ].sort(),
85
+ renames,
86
+ };
87
+ }
88
+ }
89
+
90
+ export class GitHubTwoPhaseClient {
91
+ constructor({
92
+ repository,
93
+ token,
94
+ apiUrl = "https://api.github.com",
95
+ fetchImpl = globalThis.fetch,
96
+ } = {}) {
97
+ if (!token) throw new Error("GITHUB_TOKEN is required");
98
+ this.repository = repository;
99
+ this.token = token;
100
+ this.apiUrl = apiUrl.replace(/\/+$/u, "");
101
+ this.fetch = fetchImpl;
102
+ }
103
+
104
+ async request(requestPath, { method = "GET", body } = {}) {
105
+ const response = await this.fetch(`${this.apiUrl}${requestPath}`, {
106
+ method,
107
+ headers: {
108
+ accept: "application/vnd.github+json",
109
+ authorization: `Bearer ${this.token}`,
110
+ "content-type": "application/json",
111
+ "x-github-api-version": "2022-11-28",
112
+ },
113
+ body: body === undefined ? undefined : JSON.stringify(body),
114
+ });
115
+ const raw = await response.text();
116
+ const data = raw ? JSON.parse(raw) : null;
117
+ if (!response.ok) throw new Error(data?.message || `${requestPath} failed`);
118
+ return data;
119
+ }
120
+
121
+ async baseSha(branch) {
122
+ const data = await this.request(
123
+ `/repos/${this.repository}/git/ref/heads/${branch
124
+ .split("/")
125
+ .map(encodeURIComponent)
126
+ .join("/")}`,
127
+ );
128
+ return exactLocalSha(data?.object?.sha, "protected base SHA");
129
+ }
130
+
131
+ async exactPullRequestHead(pullRequestNumber, expectedHead) {
132
+ const data = await this.request(
133
+ `/repos/${this.repository}/pulls/${pullRequestNumber}`,
134
+ );
135
+ const observed = exactLocalSha(data?.head?.sha, "observed PR head");
136
+ if (observed !== expectedHead)
137
+ throw new Error(
138
+ `semantic source head changed: ${observed} != ${expectedHead}`,
139
+ );
140
+ return observed;
141
+ }
142
+
143
+ async baseDelta(previousBase, currentBase) {
144
+ if (previousBase === currentBase)
145
+ return {
146
+ graphKnown: true,
147
+ attributionComplete: true,
148
+ changedPaths: [],
149
+ renames: [],
150
+ };
151
+ const data = await this.request(
152
+ `/repos/${this.repository}/compare/${previousBase}...${currentBase}`,
153
+ );
154
+ return attributedGitHubBaseDelta(data, previousBase);
155
+ }
156
+
157
+ async wake(eventType, candidate) {
158
+ await this.request(`/repos/${this.repository}/dispatches`, {
159
+ method: "POST",
160
+ body: { event_type: eventType, client_payload: { candidate } },
161
+ });
162
+ }
163
+ }
164
+
165
+ export function attributedGitHubBaseDelta(data, previousBase) {
166
+ const files = Array.isArray(data?.files) ? data.files : [];
167
+ const graphKnown =
168
+ data?.status === "ahead" &&
169
+ data?.merge_base_commit?.sha === previousBase &&
170
+ files.length < 300;
171
+ const renames = files
172
+ .filter((entry) => entry.status === "renamed")
173
+ .map((entry) => ({
174
+ from: String(entry.previous_filename || ""),
175
+ to: String(entry.filename || ""),
176
+ }));
177
+ const attributionComplete =
178
+ graphKnown && renames.every((entry) => entry.from && entry.to);
179
+ return {
180
+ graphKnown,
181
+ attributionComplete,
182
+ changedPaths: attributionComplete
183
+ ? [
184
+ ...new Set(
185
+ files
186
+ .flatMap((entry) => [
187
+ String(entry.filename || ""),
188
+ String(entry.previous_filename || ""),
189
+ ])
190
+ .filter(Boolean),
191
+ ),
192
+ ].sort()
193
+ : [],
194
+ renames: attributionComplete ? renames : [],
195
+ };
196
+ }
197
+
198
+ export async function replayQualifiedNativeWarrant({
199
+ warrant,
200
+ pullRequestNumber,
201
+ expectedHead,
202
+ exactPullRequestHead,
203
+ }) {
204
+ if (warrant.phase !== "qualified") return null;
205
+ if (
206
+ !ROOT_PATTERN.test(warrant.nativeProofRoot || "") ||
207
+ !ROOT_PATTERN.test(warrant.nativeProofReuseRoot || "") ||
208
+ !ROOT_PATTERN.test(warrant.qualificationReceiptRoot || "")
209
+ ) {
210
+ throw new Error(
211
+ "qualified Warrant replay is missing rooted native or qualification evidence",
212
+ );
213
+ }
214
+ await exactPullRequestHead(pullRequestNumber, expectedHead);
215
+ return {
216
+ schema: "kungfu.buildchain.two-phase-delivery-result/v1",
217
+ ok: true,
218
+ outcome: "already-qualified-warrant",
219
+ nativeAttempts: 0,
220
+ nativeProofRoot: warrant.nativeProofRoot,
221
+ nativeReuseDecisionRoot: warrant.nativeProofReuseRoot,
222
+ qualificationReceiptRoot: warrant.qualificationReceiptRoot,
223
+ landingAuthority: false,
224
+ qualifiedWarrant: warrant,
225
+ };
226
+ }
227
+
228
+ export async function classifyNativeProofAgainstCurrent(
229
+ proof,
230
+ options,
231
+ client,
232
+ ) {
233
+ const currentBase = await client.baseSha(options.branch);
234
+ const delta = await client.baseDelta(proof.qualifiedBase, currentBase);
235
+ const current = {
236
+ sourceHead: options.expectedHead,
237
+ sourceIdentityRoot: options.sourceIdentityRoot,
238
+ sourcePatchRoot: options.sourcePatchRoot,
239
+ planRoot: options.planRoot,
240
+ closureRoot: options.closureRoot,
241
+ dependencyRoot: options.dependencyRoot,
242
+ toolchainRoot: options.toolchainRoot,
243
+ environmentRoot: options.environmentRoot,
244
+ nativeCommandRoot: options.nativeCommandRoot,
245
+ currentBase,
246
+ graphKnown: delta.graphKnown,
247
+ attributionComplete: delta.attributionComplete,
248
+ changedPaths: delta.changedPaths,
249
+ renames: delta.renames,
250
+ };
251
+ return {
252
+ current,
253
+ decision: createNativeProofReuseDecision({ proof, current }),
254
+ };
255
+ }
256
+
257
+ export async function runNativeQualificationAttempt({
258
+ options,
259
+ warrant,
260
+ attempt,
261
+ client,
262
+ runCommand,
263
+ runNative,
264
+ composeCandidate,
265
+ writeEvidence,
266
+ }) {
267
+ await client.exactPullRequestHead(
268
+ options.pullRequestNumber,
269
+ options.expectedHead,
270
+ );
271
+ const qualifiedBase = await client.baseSha(options.branch);
272
+ composeCandidate(
273
+ options.candidateDirectory,
274
+ options.expectedHead,
275
+ qualifiedBase,
276
+ );
277
+ const commandContract = createNativeCommandContract(options.nativeCommand);
278
+ if (
279
+ commandContract.commandRoot !==
280
+ warrant.nativeCommandContract?.commandRoot ||
281
+ commandContract.commandRoot !== options.nativeCommandRoot
282
+ ) {
283
+ throw new Error(
284
+ "native command does not match the authorized Warrant contract",
285
+ );
286
+ }
287
+ const nativeExecutionReceipt = await runNative({
288
+ command: options.nativeCommand,
289
+ cwd: options.candidateDirectory,
290
+ intervalMs: options.heartbeatSeconds * 1000,
291
+ executionBinding: {
292
+ repository: options.repository,
293
+ protectedBase: options.branch,
294
+ sourceHead: options.expectedHead,
295
+ qualifiedBase,
296
+ nativeCommandRoot: options.nativeCommandRoot,
297
+ toolchainRoot: options.toolchainRoot,
298
+ environmentRoot: options.environmentRoot,
299
+ },
300
+ heartbeat: async () => {
301
+ await runCommand({
302
+ command: "heartbeat",
303
+ repository: options.repository,
304
+ branch: options.branch,
305
+ fencingToken: warrant.fencingToken,
306
+ leaseGeneration: warrant.generation,
307
+ leaseSeconds: options.leaseSeconds,
308
+ execute: true,
309
+ token: options.token,
310
+ apiUrl: options.apiUrl,
311
+ });
312
+ },
313
+ });
314
+ writeEvidence(
315
+ `native-heartbeat-attempt-${attempt}.json`,
316
+ nativeExecutionReceipt,
317
+ );
318
+ await client.exactPullRequestHead(
319
+ options.pullRequestNumber,
320
+ options.expectedHead,
321
+ );
322
+ const proof = createNativeQualificationProof({
323
+ repository: options.repository,
324
+ protectedBase: options.branch,
325
+ sourceIdentityRoot: options.sourceIdentityRoot,
326
+ sourcePatchRoot: options.sourcePatchRoot,
327
+ planRoot: options.planRoot,
328
+ closureRoot: options.closureRoot,
329
+ dependencyRoot: options.dependencyRoot,
330
+ toolchainRoot: options.toolchainRoot,
331
+ environmentRoot: options.environmentRoot,
332
+ sourceHead: options.expectedHead,
333
+ qualifiedBase,
334
+ nativeCommandRoot: options.nativeCommandRoot,
335
+ nativeExecutionReceipt,
336
+ affectedPaths: options.affectedPaths,
337
+ shardEvidenceRoots: [
338
+ ...options.shardEvidenceRoots,
339
+ nativeExecutionReceipt.receiptRoot,
340
+ ],
341
+ qualifiedAt: new Date().toISOString(),
342
+ });
343
+ writeEvidence("native-proof.json", proof);
344
+ return proof;
345
+ }