@kungfu-tech/buildchain 2.14.18-alpha.1 → 2.14.18-alpha.11

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 (43) hide show
  1. package/README.md +1 -0
  2. package/actions/promote-buildchain-ref/README.md +33 -30
  3. package/bin/buildchain.mjs +16 -4
  4. package/dist/site/buildchain-contract.json +86 -31
  5. package/dist/site/buildchain-site.json +88 -27
  6. package/dist/site/capability-registry.json +6 -5
  7. package/dist/site/cli-registry.json +12 -0
  8. package/dist/site/controller-registry.json +50 -6
  9. package/dist/site/kfd-claims.json +110 -21
  10. package/dist/site/kfd-upstream-aggregate.json +1 -1
  11. package/dist/site/manual-registry.json +21 -6
  12. package/dist/site/node-api-registry.json +19 -6
  13. package/dist/site/page-registry.json +70 -17
  14. package/dist/site/public-surface-audit.json +130 -23
  15. package/dist/site/publication-authority-registry.json +21 -1
  16. package/dist/site/publication-registry.json +4 -4
  17. package/dist/site/release-provenance.json +1 -0
  18. package/dist/site/site-manifest.json +18 -10
  19. package/dist/site/workflow-registry.json +62 -16
  20. package/docs/MAP.md +3 -1
  21. package/docs/github-governance-authority.md +267 -0
  22. package/docs/lifecycle-protocol.md +10 -10
  23. package/docs/publish-transaction.md +73 -4
  24. package/docs/release-governance.md +38 -24
  25. package/docs/reusable-build-surface.md +29 -11
  26. package/docs/web-surface-deployments.md +46 -0
  27. package/package.json +2 -1
  28. package/packages/core/buildchain-kfd-claims.js +2 -0
  29. package/packages/core/buildchain-publication-authority.js +1 -0
  30. package/packages/core/controller-evidence.js +1 -1
  31. package/packages/core/github-governance-authority.js +1312 -0
  32. package/packages/core/index.js +22 -0
  33. package/packages/core/publication-control-plane-audit.js +4 -1
  34. package/scripts/audit-github-governance.mjs +474 -0
  35. package/scripts/audit-publication-control-plane.mjs +41 -5
  36. package/scripts/check-inventory.mjs +8 -0
  37. package/scripts/generate-channel-promotion-workflow.mjs +3 -0
  38. package/scripts/generate-site-bundle.mjs +5 -0
  39. package/scripts/installer-publication.mjs +351 -0
  40. package/scripts/publication-commit-evidence.mjs +158 -0
  41. package/scripts/reconcile-github-governance.mjs +549 -0
  42. package/scripts/release-candidate-resolver.mjs +53 -0
  43. package/scripts/web-surface-core.mjs +127 -14
@@ -0,0 +1,1312 @@
1
+ import crypto from "node:crypto";
2
+
3
+ export const GITHUB_GOVERNANCE_AUTHORITY_CONTRACT =
4
+ "kungfu-buildchain-github-governance-authority";
5
+ export const GITHUB_GOVERNANCE_RECEIPT_CONTRACT =
6
+ "kungfu-buildchain-github-governance-receipt";
7
+ export const GITHUB_GOVERNANCE_ROLLOUT_CONTRACT =
8
+ "kungfu-buildchain-github-governance-rollout-plan";
9
+ export const GITHUB_GOVERNANCE_RULESET_ROLLOUT_CONTRACT =
10
+ "kungfu-buildchain-github-governance-ruleset-rollout-plan";
11
+
12
+ const SHA256 = /^sha256:[0-9a-f]{64}$/;
13
+ const GITHUB_ACTIONS_APP_ID = 15368;
14
+ const GITHUB_ACTIONS_BYPASS_ACTOR = Object.freeze({
15
+ actorType: "Integration",
16
+ actorId: GITHUB_ACTIONS_APP_ID,
17
+ bypassMode: "always",
18
+ });
19
+ const check = (context, appId = GITHUB_ACTIONS_APP_ID) => ({
20
+ context,
21
+ appId,
22
+ });
23
+ const target = (
24
+ targetRef,
25
+ requiredCheckBindings,
26
+ strictRequiredChecks,
27
+ allowedBypassActors = /^(dev|alpha|release)\//.test(targetRef)
28
+ ? [GITHUB_ACTIONS_BYPASS_ACTOR]
29
+ : [],
30
+ ) => ({
31
+ targetRef,
32
+ requiredCheckBindings,
33
+ strictRequiredChecks,
34
+ allowedBypassActors,
35
+ });
36
+ const PUBLIC_REPOSITORY_TARGETS = Object.freeze({
37
+ "agent-hub-demo": [
38
+ target("dev/v0/v0.2", [check("check / check")], true),
39
+ target("alpha/v0/v0.2", [check("check / check")], false),
40
+ target("release/v0/v0.2", [check("check / check")], false),
41
+ ],
42
+ "build-images": [
43
+ target("dev/v1/v1.1", [check("check")], true),
44
+ target("alpha/v1/v1.1", [check("check")], true),
45
+ target("release/v1/v1.1", [check("check")], true),
46
+ target("publish-gate/major", [check("check")], true),
47
+ ],
48
+ buildchain: [
49
+ target("dev/v2/v2.14", [check("check")], false),
50
+ target("alpha/v2/v2.14", [check("check"), check("verify")], false),
51
+ target("release/v2/v2.14", [check("check")], true),
52
+ target("publish-gate/major", [check("check")], true),
53
+ ],
54
+ "homebrew-tap": [
55
+ target("main", [check("check / Finalize channel router controller evidence")], false),
56
+ ],
57
+ kfd: [
58
+ target("dev/v1/v1.0", [check("check / check")], true),
59
+ target("alpha/v1/v1.0", [check("check / check")], true),
60
+ ],
61
+ kungfu: [
62
+ target("dev/v4/v4.0", [check("affected-native / linux")], false),
63
+ target("alpha/v4/v4.0", [
64
+ check("build", null),
65
+ check("signoff"),
66
+ check("validate"),
67
+ ], true),
68
+ ],
69
+ libnode: [
70
+ target("dev/v22/v22.22", [check("build")], true),
71
+ target("alpha/v22/v22.22", [check("build")], true),
72
+ target(
73
+ "release/v22/v22.22",
74
+ [check("build / Build with resolved channel / Summarize build contract")],
75
+ true,
76
+ ),
77
+ ],
78
+ "paper-episodes-to-primitives": [
79
+ target("main", [check("governance")], false),
80
+ target("dev/v0/v0.1", [check("check / check")], true),
81
+ target("alpha/v0/v0.1", [check("check / check")], true),
82
+ ],
83
+ "paper-kfd-foundation-real-world-agent-work": [
84
+ target("main", [check("check / check")], false),
85
+ target("dev/v0/v0.1", [check("check / check")], true),
86
+ target("alpha/v0/v0.1", [check("check / check")], true),
87
+ ],
88
+ "paper-kungfu-product-white-paper": [
89
+ target("main", [check("check / check")], false),
90
+ target("dev/v0/v0.1", [check("check / check")], true),
91
+ target("alpha/v0/v0.1", [check("check / check")], true),
92
+ ],
93
+ "paper-observer-declared-timelines": [
94
+ target("main", [check("check / check")], false),
95
+ target("dev/v0/v0.1", [check("check / check")], true),
96
+ target("alpha/v0/v0.1", [check("check / check")], true),
97
+ ],
98
+ "site-kungfu-tech": [
99
+ target("main", [check("web-surface / Record web-surface controller receipt")], false),
100
+ ],
101
+ "site-libkungfu-dev": [
102
+ target("main", [check("web-surface / Record web-surface controller receipt")], false),
103
+ ],
104
+ });
105
+ const PUBLIC_REPOSITORIES = Object.freeze(Object.keys(PUBLIC_REPOSITORY_TARGETS).sort());
106
+ const PRIVATE_REPOSITORY_IDENTITIES = Object.freeze([
107
+ "sha256:581823ab841e1d9a9025c92d0c47b164c6aaa1ea22112fdbc8d73bd2c862a05f",
108
+ "sha256:b7255e01d10000eb3a2786456b4c558178675ccb6cf28a6e39a74dbfe918df35",
109
+ "sha256:f41bd767e9c4a0ab429ca2a2f456d29ddefab0e996d75000ba36334689eb177e",
110
+ ]);
111
+ const PROTECTED_AUTHORITY_PATHS = Object.freeze([
112
+ ".github/CODEOWNERS",
113
+ ".github/workflows/.publication-authority.yml",
114
+ ".github/workflows/.release-candidate-promote.yml",
115
+ ".github/workflows/buildchain-ref-promotion.yml",
116
+ ".github/workflows/github-governance-audit.yml",
117
+ ".github/workflows/paper-release-sealed.yml",
118
+ ".github/workflows/paper-release.yml",
119
+ ".github/workflows/release-line-bootstrap.yml",
120
+ ".github/workflows/release-candidate-promote.yml",
121
+ "actions/promote-buildchain-ref/action.yml",
122
+ "actions/promote-buildchain-ref/dist/index.js",
123
+ "actions/promote-buildchain-ref/index.js",
124
+ "actions/promote-buildchain-ref/lib.js",
125
+ "packages/core/github-governance-authority.js",
126
+ "packages/core/buildchain-publication-authority.js",
127
+ "scripts/audit-github-governance.mjs",
128
+ "scripts/reconcile-github-governance.mjs",
129
+ ]);
130
+ const REQUIRED_FACTS = Object.freeze([
131
+ "api-evidence",
132
+ "repository-admission",
133
+ "target-ref-admission",
134
+ "plan-capability",
135
+ "codeowners-source",
136
+ "codeowners-authority",
137
+ "native-protection",
138
+ "independent-review",
139
+ "fresh-review",
140
+ "administrator-enforcement",
141
+ "bypass-policy",
142
+ "conversation-resolution",
143
+ "required-checks",
144
+ "strict-required-checks",
145
+ "ref-integrity",
146
+ "development-least-privilege",
147
+ "review-authority",
148
+ ]);
149
+
150
+ function stableJson(value) {
151
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
152
+ if (value && typeof value === "object") {
153
+ return `{${Object.keys(value)
154
+ .sort()
155
+ .map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
156
+ .join(",")}}`;
157
+ }
158
+ return JSON.stringify(value);
159
+ }
160
+
161
+ export function githubGovernanceDigest(value) {
162
+ return `sha256:${crypto.createHash("sha256").update(stableJson(value)).digest("hex")}`;
163
+ }
164
+
165
+ function requiredString(value, label) {
166
+ const normalized = String(value || "").trim();
167
+ if (!normalized) throw new Error(`${label} is required`);
168
+ return normalized;
169
+ }
170
+
171
+ function requiredRoot(value, label) {
172
+ const normalized = requiredString(value, label);
173
+ if (!SHA256.test(normalized)) throw new Error(`${label} must be a sha256 root`);
174
+ return normalized;
175
+ }
176
+
177
+ function normalizedRef(value) {
178
+ return requiredString(value, "target ref").replace(/^refs\/heads\//, "");
179
+ }
180
+
181
+ function normalizeLogin(value) {
182
+ return requiredString(value, "GitHub login").replace(/^@/, "").toLowerCase();
183
+ }
184
+
185
+ function normalizeRulePath(value) {
186
+ return String(value || "").trim().replace(/^\/+/, "");
187
+ }
188
+
189
+ function matchesRef(pattern, branch, defaultBranch) {
190
+ const normalized = String(pattern || "");
191
+ const ref = `refs/heads/${branch}`;
192
+ if (normalized === "~DEFAULT_BRANCH") return branch === defaultBranch;
193
+ if (normalized === branch || normalized === ref) return true;
194
+ if (!normalized.includes("*")) return false;
195
+ const expression = new RegExp(
196
+ `^${normalized
197
+ .split("*")
198
+ .map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
199
+ .join(".*")}$`,
200
+ );
201
+ return expression.test(ref) || expression.test(branch);
202
+ }
203
+
204
+ function applicableRulesets(rulesets, branch, defaultBranch) {
205
+ return (rulesets || []).filter((ruleset) => {
206
+ if (ruleset?.enforcement !== "active" || ruleset?.target !== "branch") return false;
207
+ const include = ruleset.conditions?.ref_name?.include || [];
208
+ const exclude = ruleset.conditions?.ref_name?.exclude || [];
209
+ return include.some((pattern) => matchesRef(pattern, branch, defaultBranch)) &&
210
+ !exclude.some((pattern) => matchesRef(pattern, branch, defaultBranch));
211
+ });
212
+ }
213
+
214
+ function ruleParameters(rulesets, type) {
215
+ return rulesets
216
+ .flatMap((ruleset) => ruleset.rules || [])
217
+ .filter((rule) => rule?.type === type)
218
+ .map((rule) => rule.parameters || {});
219
+ }
220
+
221
+ function normalizeCheckBinding(entry) {
222
+ const context = String(
223
+ typeof entry === "string" ? entry : entry?.context || "",
224
+ ).trim();
225
+ if (!context) return null;
226
+ const providerAppId = typeof entry === "string"
227
+ ? null
228
+ : entry?.app_id ?? entry?.integration_id ?? null;
229
+ return {
230
+ context,
231
+ appId: Number.isInteger(providerAppId) && providerAppId > 0
232
+ ? providerAppId
233
+ : null,
234
+ };
235
+ }
236
+
237
+ function uniqueCheckBindings(bindings) {
238
+ return bindings
239
+ .filter(Boolean)
240
+ .filter((binding, index, values) =>
241
+ values.findIndex((candidate) =>
242
+ candidate.context === binding.context &&
243
+ candidate.appId === binding.appId) === index)
244
+ .sort((left, right) =>
245
+ `${left.context}:${left.appId ?? ""}`.localeCompare(
246
+ `${right.context}:${right.appId ?? ""}`,
247
+ ));
248
+ }
249
+
250
+ function uniqueBypassActors(actors) {
251
+ return (actors || [])
252
+ .map((actor) => ({
253
+ actorType: String(actor?.actorType || ""),
254
+ actorId: Number(actor?.actorId || 0),
255
+ bypassMode: String(actor?.bypassMode || ""),
256
+ }))
257
+ .filter((actor) =>
258
+ actor.actorType &&
259
+ Number.isInteger(actor.actorId) &&
260
+ actor.actorId > 0 &&
261
+ actor.bypassMode)
262
+ .filter((actor, index, values) =>
263
+ values.findIndex((candidate) =>
264
+ candidate.actorType === actor.actorType &&
265
+ candidate.actorId === actor.actorId &&
266
+ candidate.bypassMode === actor.bypassMode) === index)
267
+ .sort((left, right) =>
268
+ `${left.actorType}:${left.actorId}:${left.bypassMode}`.localeCompare(
269
+ `${right.actorType}:${right.actorId}:${right.bypassMode}`,
270
+ ));
271
+ }
272
+
273
+ function protectionCheckBindings(protection) {
274
+ const status = protection?.required_status_checks || {};
275
+ const checks = Array.isArray(status.checks) ? status.checks : [];
276
+ return uniqueCheckBindings(
277
+ (checks.length > 0 ? checks : status.contexts || [])
278
+ .map(normalizeCheckBinding),
279
+ );
280
+ }
281
+
282
+ function rulesetCheckBindings(rulesets) {
283
+ return uniqueCheckBindings(ruleParameters(rulesets, "required_status_checks")
284
+ .flatMap((parameters) => parameters.required_status_checks || [])
285
+ .map(normalizeCheckBinding));
286
+ }
287
+
288
+ function classicBypassActors(review = {}) {
289
+ const allowances = review.bypass_pull_request_allowances || {};
290
+ return [
291
+ ...(allowances.users || []).map((actor) => ({
292
+ actor_type: "User",
293
+ bypass_mode: "always",
294
+ actor_id: Number(actor?.id || 0),
295
+ })),
296
+ ...(allowances.teams || []).map((actor) => ({
297
+ actor_type: "Team",
298
+ bypass_mode: "always",
299
+ actor_id: Number(actor?.id || 0),
300
+ })),
301
+ ...(allowances.apps || []).map((actor) => ({
302
+ actor_type: "Integration",
303
+ bypass_mode: "always",
304
+ actor_id: Number(actor?.id || 0),
305
+ })),
306
+ ];
307
+ }
308
+
309
+ export function compileEffectiveGithubGovernancePolicy({
310
+ branch,
311
+ defaultBranch,
312
+ protectedBranch = false,
313
+ protection,
314
+ rulesets = [],
315
+ } = {}) {
316
+ const target = normalizedRef(branch);
317
+ const applicable = applicableRulesets(rulesets, target, defaultBranch);
318
+ const pullRequests = ruleParameters(applicable, "pull_request");
319
+ const classicReview = protection?.required_pull_request_reviews || {};
320
+ const requiredCheckBindings = uniqueCheckBindings([
321
+ ...protectionCheckBindings(protection),
322
+ ...rulesetCheckBindings(applicable),
323
+ ]);
324
+ const requiredChecks = [...new Set(
325
+ requiredCheckBindings.map((entry) => entry.context),
326
+ )].sort();
327
+ const bypassActors = [
328
+ ...classicBypassActors(classicReview),
329
+ ...applicable.flatMap((ruleset) => ruleset.bypass_actors || []),
330
+ ];
331
+ const classicProtected = Boolean(protection);
332
+ const rulesetPullRequest = pullRequests.length > 0;
333
+ const requiredApprovals = Math.max(
334
+ Number(classicReview.required_approving_review_count || 0),
335
+ ...pullRequests.map((policy) => Number(policy.required_approving_review_count || 0)),
336
+ );
337
+ const codeOwnerReviewRequired = classicReview.require_code_owner_reviews === true ||
338
+ pullRequests.some((policy) => policy.require_code_owner_review === true);
339
+ const dismissStaleReviews = classicReview.dismiss_stale_reviews === true ||
340
+ pullRequests.some((policy) => policy.dismiss_stale_reviews_on_push === true);
341
+ const requireLastPushApproval = classicReview.require_last_push_approval === true ||
342
+ pullRequests.some((policy) => policy.require_last_push_approval === true);
343
+ const conversationResolution = protection?.required_conversation_resolution?.enabled === true ||
344
+ pullRequests.some((policy) => policy.required_review_thread_resolution === true);
345
+ const enforceAdmins = classicProtected
346
+ ? protection?.enforce_admins?.enabled === true
347
+ : bypassActors.every((actor) => actor?.actor_type !== "OrganizationAdmin");
348
+ const allowForcePushes = classicProtected
349
+ ? protection?.allow_force_pushes?.enabled === true
350
+ : !applicable.some((ruleset) => (ruleset.rules || []).some((rule) => rule?.type === "non_fast_forward"));
351
+ const allowDeletions = classicProtected
352
+ ? protection?.allow_deletions?.enabled === true
353
+ : !applicable.some((ruleset) => (ruleset.rules || []).some((rule) => rule?.type === "deletion"));
354
+ return {
355
+ targetRef: target,
356
+ protected: protectedBranch === true || classicProtected || applicable.length > 0,
357
+ nativePullRequestRequired: classicReview.required_approving_review_count !== undefined ||
358
+ rulesetPullRequest,
359
+ requiredApprovals,
360
+ codeOwnerReviewRequired,
361
+ dismissStaleReviews,
362
+ requireLastPushApproval,
363
+ conversationResolution,
364
+ requiredChecks,
365
+ requiredCheckBindings,
366
+ strictRequiredChecks: protection?.required_status_checks?.strict === true ||
367
+ ruleParameters(applicable, "required_status_checks")
368
+ .some((policy) => policy.strict_required_status_checks_policy === true),
369
+ enforceAdmins,
370
+ bypassActors: bypassActors.map((actor) => ({
371
+ actorType: String(actor?.actor_type || ""),
372
+ bypassMode: String(actor?.bypass_mode || ""),
373
+ actorId: Number(actor?.actor_id || 0),
374
+ })),
375
+ allowForcePushes,
376
+ allowDeletions,
377
+ classicProtectionObserved: classicProtected,
378
+ applicableRulesetRoots: applicable.map((ruleset) => githubGovernanceDigest(ruleset)).sort(),
379
+ };
380
+ }
381
+
382
+ function codeownersPatternMatches(pattern, candidatePath) {
383
+ let normalized = normalizeRulePath(pattern);
384
+ if (!normalized || normalized.startsWith("!")) return false;
385
+ const candidate = normalizeRulePath(candidatePath);
386
+ if (!normalized.includes("/")) normalized = `**/${normalized}`;
387
+ if (normalized.endsWith("/")) normalized += "**";
388
+ const expression = normalized
389
+ .split("**")
390
+ .map((part) => part
391
+ .split("*")
392
+ .map((token) => token.replace(/[.+?^${}()|[\]\\]/g, "\\$&"))
393
+ .join("[^/]*"))
394
+ .join(".*");
395
+ return new RegExp(`^${expression}$`).test(candidate) ||
396
+ new RegExp(`^${expression.replace(/^\.\*\//, "(?:.*/)?")}$`).test(candidate);
397
+ }
398
+
399
+ export function parseCodeowners(source) {
400
+ const rules = [];
401
+ for (const [index, line] of String(source || "").split(/\r?\n/).entries()) {
402
+ const stripped = line.replace(/(^|[^\\])#.*/, "$1").trim();
403
+ if (!stripped) continue;
404
+ const [pattern, ...owners] = stripped.split(/\s+/);
405
+ if (!pattern || owners.length === 0) continue;
406
+ rules.push({
407
+ line: index + 1,
408
+ pattern,
409
+ owners: owners.map((owner) => owner.toLowerCase()),
410
+ });
411
+ }
412
+ return rules;
413
+ }
414
+
415
+ export function codeownersForPath(source, candidatePath) {
416
+ let owners = [];
417
+ for (const rule of parseCodeowners(source)) {
418
+ if (codeownersPatternMatches(rule.pattern, candidatePath)) owners = rule.owners;
419
+ }
420
+ return owners;
421
+ }
422
+
423
+ export function evaluateCodeownersAuthority({
424
+ source = "",
425
+ sourcePath = "",
426
+ reviewAuthority = "kungfu-origin",
427
+ protectedPaths = PROTECTED_AUTHORITY_PATHS,
428
+ } = {}) {
429
+ const authority = `@${normalizeLogin(reviewAuthority)}`;
430
+ const pathOwners = Object.fromEntries(
431
+ protectedPaths.map((candidatePath) => [
432
+ candidatePath,
433
+ codeownersForPath(source, candidatePath),
434
+ ]),
435
+ );
436
+ return {
437
+ exists: Boolean(String(source)),
438
+ sourcePath: normalizeRulePath(sourcePath),
439
+ sourceDigest: String(source) ? githubGovernanceDigest(String(source)) : "",
440
+ reviewAuthority: authority,
441
+ protectedPaths: [...protectedPaths],
442
+ pathOwners,
443
+ allProtectedPathsOwned: protectedPaths.every((candidatePath) =>
444
+ pathOwners[candidatePath].includes(authority)),
445
+ };
446
+ }
447
+
448
+ export function createBuildchainGithubGovernanceAuthority() {
449
+ const descriptor = {
450
+ schemaVersion: 1,
451
+ contract: GITHUB_GOVERNANCE_AUTHORITY_CONTRACT,
452
+ organization: "kungfu-systems",
453
+ authority: {
454
+ developmentIdentity: "dongkeren",
455
+ reviewIdentity: "kungfu-origin",
456
+ minimumIndependentApprovals: 1,
457
+ protectedAuthorityPaths: [...PROTECTED_AUTHORITY_PATHS],
458
+ },
459
+ trustedComputingBase: {
460
+ trusted: [
461
+ "GitHub service integrity and authenticated API state",
462
+ "retained organization-owner recovery custody",
463
+ "kungfu-origin review and governance identity",
464
+ "Buildchain verifier source and exact runtime identity",
465
+ "official publication identity configuration",
466
+ ],
467
+ nonClaims: [
468
+ "GitHub platform compromise is outside this authority guarantee",
469
+ "compromise of all retained owner and recovery anchors is outside this authority guarantee",
470
+ "a qualifying receipt is not a bearer credential and grants no GitHub permission",
471
+ ],
472
+ },
473
+ repositoryAdmission: {
474
+ publicRepositories: [...PUBLIC_REPOSITORIES],
475
+ publicAuthoritativeTargets: Object.fromEntries(
476
+ Object.entries(PUBLIC_REPOSITORY_TARGETS).map(([repository, targets]) => [
477
+ repository,
478
+ targets.map((entry) => ({
479
+ targetRef: entry.targetRef,
480
+ requiredCheckBindings: entry.requiredCheckBindings.map((binding) => ({
481
+ ...binding,
482
+ })),
483
+ strictRequiredChecks: entry.strictRequiredChecks,
484
+ allowedBypassActors: entry.allowedBypassActors.map((actor) => ({
485
+ ...actor,
486
+ })),
487
+ })),
488
+ ]),
489
+ ),
490
+ privateRepositoryIdentities: PRIVATE_REPOSITORY_IDENTITIES.map((identityRoot) => ({
491
+ identityRoot,
492
+ targetPolicy: "default-and-current-version-line",
493
+ requiredCheckPolicies: {},
494
+ })),
495
+ privateRepositoryPolicy: "non-authoritative-until-plan-capability-qualifies",
496
+ unknownRepositoryPolicy: "non-authoritative-until-explicit-admission",
497
+ baseline: {
498
+ observedOn: "2026-07-24",
499
+ repositoryCount: 16,
500
+ publicCount: 13,
501
+ privateCount: 3,
502
+ authoritativePublicTargetCount: Object.values(PUBLIC_REPOSITORY_TARGETS)
503
+ .reduce((count, targets) => count + targets.length, 0),
504
+ },
505
+ },
506
+ planCapability: {
507
+ publicRepositories: ["free", "team", "enterprise"],
508
+ privateRepositories: ["team", "enterprise"],
509
+ organizationRulesets: ["team", "enterprise"],
510
+ },
511
+ effectivePolicy: {
512
+ codeOwnerReviewRequired: true,
513
+ minimumIndependentApprovals: 1,
514
+ freshApprovalAfterLatestPush: true,
515
+ administratorEnforcement: true,
516
+ allowedBypassActors: "target-bound-official-integrations-only",
517
+ conversationResolution: true,
518
+ requiredChecks: "descriptor-bound-exact-context-and-app-identity",
519
+ strictRequiredChecks: "descriptor-bound",
520
+ forcePush: false,
521
+ deletion: false,
522
+ nativeEnforcementRequired: true,
523
+ },
524
+ breakGlass: {
525
+ default: "disabled",
526
+ requirements: [
527
+ "separately authenticated",
528
+ "reason-bound",
529
+ "time-bounded",
530
+ "independently receipted",
531
+ "mandatory restoration and root comparison",
532
+ ],
533
+ },
534
+ };
535
+ return {
536
+ ...descriptor,
537
+ policyRoot: githubGovernanceDigest(descriptor),
538
+ };
539
+ }
540
+
541
+ export function githubRepositoryIdentityRoot({
542
+ provider = "github",
543
+ providerRepositoryId,
544
+ } = {}) {
545
+ return githubGovernanceDigest({
546
+ provider: requiredString(provider, "repository identity provider"),
547
+ providerRepositoryId: requiredString(
548
+ providerRepositoryId,
549
+ "provider repository id",
550
+ ),
551
+ });
552
+ }
553
+
554
+ function privateRepositoryPolicy(descriptor, identityRoot) {
555
+ return descriptor.repositoryAdmission.privateRepositoryIdentities
556
+ .find((entry) => entry.identityRoot === identityRoot);
557
+ }
558
+
559
+ function repositoryAdmission(descriptor, repository) {
560
+ const [owner, name] = requiredString(repository.fullName, "repository full name").split("/");
561
+ if (owner !== descriptor.organization) return "non-authoritative-foreign-owner";
562
+ if (repository.visibility === "private") {
563
+ return privateRepositoryPolicy(descriptor, repository.identityRoot)
564
+ ? "admitted-private"
565
+ : "non-authoritative-explicit-admission-required";
566
+ }
567
+ return descriptor.repositoryAdmission.publicRepositories.includes(name)
568
+ ? "admitted-public"
569
+ : "non-authoritative-explicit-admission-required";
570
+ }
571
+
572
+ function activeVersionLineTargets(defaultBranch) {
573
+ const normalized = normalizedRef(defaultBranch);
574
+ const match = normalized.match(/^dev\/(v\d+)\/(v\d+\.\d+)$/);
575
+ if (!match) return [normalized];
576
+ return [
577
+ normalized,
578
+ `alpha/${match[1]}/${match[2]}`,
579
+ `release/${match[1]}/${match[2]}`,
580
+ ];
581
+ }
582
+
583
+ function publicTargetPolicy(descriptor, repository, targetRef) {
584
+ const name = requiredString(repository.fullName, "repository full name").split("/")[1];
585
+ return (descriptor.repositoryAdmission.publicAuthoritativeTargets[name] || [])
586
+ .find((entry) => entry.targetRef === normalizedRef(targetRef));
587
+ }
588
+
589
+ function privateTargetPolicy(descriptor, repository, targetRef) {
590
+ const policy = privateRepositoryPolicy(descriptor, repository.identityRoot);
591
+ if (!policy) return null;
592
+ const normalized = normalizedRef(targetRef);
593
+ if (!activeVersionLineTargets(repository.defaultBranch || normalized).includes(normalized)) {
594
+ return null;
595
+ }
596
+ return {
597
+ targetRef: normalized,
598
+ ...(policy.requiredCheckPolicies?.[normalized] || {}),
599
+ };
600
+ }
601
+
602
+ function targetPolicy(descriptor, repository, targetRef) {
603
+ return repository.visibility === "private"
604
+ ? privateTargetPolicy(descriptor, repository, targetRef)
605
+ : publicTargetPolicy(descriptor, repository, targetRef);
606
+ }
607
+
608
+ export function resolveGithubGovernanceTargetPolicy({
609
+ descriptor = BUILDCHAIN_GITHUB_GOVERNANCE_AUTHORITY,
610
+ repository,
611
+ targetRef,
612
+ } = {}) {
613
+ const fullName = requiredString(repository, "repository");
614
+ const [owner] = fullName.split("/");
615
+ if (owner !== descriptor.organization) {
616
+ throw new Error("repository is outside the governance authority organization");
617
+ }
618
+ const policy = publicTargetPolicy(descriptor, {
619
+ fullName,
620
+ visibility: "public",
621
+ }, targetRef);
622
+ if (!policy) {
623
+ throw new Error("target ref is not admitted by the governance authority");
624
+ }
625
+ return {
626
+ ...structuredClone(policy),
627
+ requiredApprovals: descriptor.authority.minimumIndependentApprovals,
628
+ };
629
+ }
630
+
631
+ export function resolveGithubGovernanceTargetRefs({
632
+ descriptor = BUILDCHAIN_GITHUB_GOVERNANCE_AUTHORITY,
633
+ repository,
634
+ availableRefs = [],
635
+ requestedTargetRef = "",
636
+ } = {}) {
637
+ if (requestedTargetRef) return [normalizedRef(requestedTargetRef)];
638
+ const defaultBranch = normalizedRef(repository.defaultBranch);
639
+ if (repository.visibility === "private") {
640
+ if (!privateRepositoryPolicy(descriptor, repository.identityRoot)) {
641
+ return [defaultBranch];
642
+ }
643
+ const available = new Set(availableRefs.map(normalizedRef));
644
+ return activeVersionLineTargets(defaultBranch)
645
+ .filter((ref) => ref === defaultBranch || available.has(ref));
646
+ }
647
+ const name = requiredString(repository.fullName, "repository full name").split("/")[1];
648
+ const admitted = descriptor.repositoryAdmission.publicAuthoritativeTargets[name] || [];
649
+ return [...new Set([
650
+ ...admitted.map((entry) => entry.targetRef),
651
+ defaultBranch,
652
+ ])].sort();
653
+ }
654
+
655
+ function planCapability(descriptor, repository, planName) {
656
+ const normalizedPlan = String(planName || "").toLowerCase();
657
+ const allowed = repository.visibility === "private"
658
+ ? descriptor.planCapability.privateRepositories
659
+ : descriptor.planCapability.publicRepositories;
660
+ return {
661
+ plan: normalizedPlan || "unknown",
662
+ requiredCapability: repository.visibility === "private"
663
+ ? "private-repository-native-protection"
664
+ : "public-repository-native-protection",
665
+ qualifying: allowed.includes(normalizedPlan),
666
+ };
667
+ }
668
+
669
+ function fact(id, pass, observed) {
670
+ return {
671
+ id,
672
+ status: pass ? "pass" : "fail",
673
+ evidenceRoot: githubGovernanceDigest({ id, observed }),
674
+ };
675
+ }
676
+
677
+ function sanitizedRepositoryIdentity(repository) {
678
+ if (repository.visibility === "private") {
679
+ return {
680
+ visibility: "private",
681
+ repository: null,
682
+ repositoryIdentityRoot: requiredRoot(
683
+ repository.identityRoot || githubGovernanceDigest(repository.fullName),
684
+ "private repository identity root",
685
+ ),
686
+ };
687
+ }
688
+ return {
689
+ visibility: "public",
690
+ repository: requiredString(repository.fullName, "repository full name"),
691
+ repositoryIdentityRoot: githubGovernanceDigest(repository.fullName),
692
+ };
693
+ }
694
+
695
+ export function evaluateGithubGovernanceSnapshot({
696
+ descriptor = createBuildchainGithubGovernanceAuthority(),
697
+ repository,
698
+ targetRef,
699
+ organizationPlan,
700
+ codeowners,
701
+ effectivePolicy,
702
+ memberships,
703
+ apiEvidence = {},
704
+ observedAt,
705
+ expiresAt,
706
+ verifier = {},
707
+ } = {}) {
708
+ const { policyRoot, ...descriptorCore } = descriptor;
709
+ if (policyRoot !== githubGovernanceDigest(descriptorCore)) {
710
+ throw new Error("GitHub governance authority policy root mismatch");
711
+ }
712
+ const identity = sanitizedRepositoryIdentity(repository || {});
713
+ const admission = repositoryAdmission(descriptor, repository || {});
714
+ const admittedTargetPolicy = targetPolicy(descriptor, repository || {}, targetRef);
715
+ const capability = planCapability(descriptor, repository || {}, organizationPlan);
716
+ const policy = effectivePolicy || {};
717
+ const ownership = codeowners || {};
718
+ const developmentLogin = normalizeLogin(descriptor.authority.developmentIdentity);
719
+ const reviewLogin = normalizeLogin(descriptor.authority.reviewIdentity);
720
+ const development = memberships?.[developmentLogin] || {};
721
+ const review = memberships?.[reviewLogin] || {};
722
+ const apiPass = apiEvidence.complete === true &&
723
+ apiEvidence.readable === true &&
724
+ apiEvidence.ambiguous !== true;
725
+ const admitted = admission === "admitted-public" ||
726
+ admission === "admitted-private";
727
+ const observedCheckBindings = uniqueCheckBindings(policy.requiredCheckBindings || []);
728
+ const observedCheckBindingRoot = githubGovernanceDigest(observedCheckBindings);
729
+ const expectedCheckBindingRoot = admittedTargetPolicy?.requiredCheckBindingRoot ||
730
+ (admittedTargetPolicy?.requiredCheckBindings
731
+ ? githubGovernanceDigest(uniqueCheckBindings(
732
+ admittedTargetPolicy.requiredCheckBindings.map((entry) => ({
733
+ context: entry.context,
734
+ appId: entry.appId,
735
+ })),
736
+ ))
737
+ : "");
738
+ const exactRequiredChecks = observedCheckBindings.length > 0 &&
739
+ SHA256.test(expectedCheckBindingRoot) &&
740
+ observedCheckBindingRoot === expectedCheckBindingRoot;
741
+ const strictRequiredChecks = typeof admittedTargetPolicy?.strictRequiredChecks === "boolean" &&
742
+ policy.strictRequiredChecks === admittedTargetPolicy.strictRequiredChecks;
743
+ const observedBypassActors = uniqueBypassActors(policy.bypassActors);
744
+ const allowedBypassActors = uniqueBypassActors(
745
+ admittedTargetPolicy?.allowedBypassActors,
746
+ );
747
+ const unapprovedBypassActors = observedBypassActors.filter((actor) =>
748
+ !allowedBypassActors.some((allowed) =>
749
+ allowed.actorType === actor.actorType &&
750
+ allowed.actorId === actor.actorId &&
751
+ allowed.bypassMode === actor.bypassMode));
752
+ const facts = [
753
+ fact("api-evidence", apiPass, apiEvidence),
754
+ fact("repository-admission", admitted, { admission, visibility: repository?.visibility }),
755
+ fact("target-ref-admission", Boolean(admittedTargetPolicy), {
756
+ targetAdmission: admittedTargetPolicy
757
+ ? "admitted-authoritative-target"
758
+ : "non-authoritative-target",
759
+ targetRef: normalizedRef(targetRef),
760
+ }),
761
+ fact("plan-capability", capability.qualifying, capability),
762
+ fact("codeowners-source", ownership.exists === true && SHA256.test(ownership.sourceDigest || ""), {
763
+ exists: ownership.exists,
764
+ sourcePath: ownership.sourcePath,
765
+ sourceDigest: ownership.sourceDigest,
766
+ }),
767
+ fact("codeowners-authority", ownership.allProtectedPathsOwned === true, {
768
+ reviewAuthority: ownership.reviewAuthority,
769
+ protectedPaths: ownership.protectedPaths,
770
+ pathOwners: ownership.pathOwners,
771
+ }),
772
+ fact("native-protection", policy.protected === true && policy.nativePullRequestRequired === true, {
773
+ protected: policy.protected,
774
+ nativePullRequestRequired: policy.nativePullRequestRequired,
775
+ }),
776
+ fact("independent-review",
777
+ policy.codeOwnerReviewRequired === true &&
778
+ Number(policy.requiredApprovals || 0) >= descriptor.authority.minimumIndependentApprovals,
779
+ {
780
+ codeOwnerReviewRequired: policy.codeOwnerReviewRequired,
781
+ requiredApprovals: policy.requiredApprovals,
782
+ }),
783
+ fact("fresh-review",
784
+ policy.dismissStaleReviews === true || policy.requireLastPushApproval === true,
785
+ {
786
+ dismissStaleReviews: policy.dismissStaleReviews,
787
+ requireLastPushApproval: policy.requireLastPushApproval,
788
+ }),
789
+ fact("administrator-enforcement", policy.enforceAdmins === true, {
790
+ enforceAdmins: policy.enforceAdmins,
791
+ }),
792
+ fact("bypass-policy", unapprovedBypassActors.length === 0, {
793
+ bypassActors: observedBypassActors,
794
+ allowedBypassActors,
795
+ unapprovedBypassActors,
796
+ }),
797
+ fact("conversation-resolution", policy.conversationResolution === true, {
798
+ conversationResolution: policy.conversationResolution,
799
+ }),
800
+ fact("required-checks", exactRequiredChecks, {
801
+ requiredChecks: policy.requiredChecks || [],
802
+ requiredCheckBindings: observedCheckBindings,
803
+ observedCheckBindingRoot,
804
+ expectedCheckBindingRoot,
805
+ }),
806
+ fact("strict-required-checks", strictRequiredChecks, {
807
+ observed: policy.strictRequiredChecks === true,
808
+ expected: admittedTargetPolicy?.strictRequiredChecks,
809
+ }),
810
+ fact("ref-integrity", policy.allowForcePushes === false && policy.allowDeletions === false, {
811
+ allowForcePushes: policy.allowForcePushes,
812
+ allowDeletions: policy.allowDeletions,
813
+ }),
814
+ fact("development-least-privilege",
815
+ development.state === "active" && !["admin", "maintain"].includes(development.role),
816
+ { state: development.state, roleClass: development.role }),
817
+ fact("review-authority",
818
+ review.state === "active" && ["admin", "maintain"].includes(review.role),
819
+ { state: review.state, roleClass: review.role }),
820
+ ];
821
+ const qualifying = REQUIRED_FACTS.every((id) =>
822
+ facts.some((entry) => entry.id === id && entry.status === "pass"));
823
+ const core = {
824
+ schemaVersion: 1,
825
+ contract: GITHUB_GOVERNANCE_RECEIPT_CONTRACT,
826
+ status: qualifying ? "qualifying" : "non-qualifying",
827
+ qualifying,
828
+ organization: descriptor.organization,
829
+ ...identity,
830
+ targetRef: normalizedRef(targetRef),
831
+ policyRoot: descriptor.policyRoot,
832
+ codeownersDigest: ownership.sourceDigest || "",
833
+ effectiveRuleRoots: [
834
+ ...(policy.applicableRulesetRoots || []),
835
+ ...(policy.classicProtectionObserved
836
+ ? [githubGovernanceDigest({
837
+ targetRef: policy.targetRef,
838
+ classicProtectionObserved: true,
839
+ nativePullRequestRequired: policy.nativePullRequestRequired,
840
+ requiredApprovals: policy.requiredApprovals,
841
+ codeOwnerReviewRequired: policy.codeOwnerReviewRequired,
842
+ dismissStaleReviews: policy.dismissStaleReviews,
843
+ requireLastPushApproval: policy.requireLastPushApproval,
844
+ conversationResolution: policy.conversationResolution,
845
+ requiredCheckBindings: observedCheckBindings,
846
+ strictRequiredChecks: policy.strictRequiredChecks,
847
+ enforceAdmins: policy.enforceAdmins,
848
+ bypassActors: policy.bypassActors,
849
+ allowForcePushes: policy.allowForcePushes,
850
+ allowDeletions: policy.allowDeletions,
851
+ })]
852
+ : []),
853
+ ].sort(),
854
+ membershipAuthorityRoot: githubGovernanceDigest({
855
+ development: { state: development.state || "", roleClass: development.role || "" },
856
+ review: { state: review.state || "", roleClass: review.role || "" },
857
+ }),
858
+ requiredChecks: [...(policy.requiredChecks || [])].sort(),
859
+ requiredCheckBindings: observedCheckBindings,
860
+ requiredCheckBindingRoot: observedCheckBindingRoot,
861
+ expectedRequiredCheckBindingRoot: expectedCheckBindingRoot,
862
+ targetAdmission: admittedTargetPolicy
863
+ ? "admitted-authoritative-target"
864
+ : "non-authoritative-target",
865
+ admission,
866
+ planCapability: capability,
867
+ apiEvidence: {
868
+ status: apiPass ? "complete" : "non-qualifying",
869
+ evidenceRoot: githubGovernanceDigest(apiEvidence),
870
+ },
871
+ verifier: {
872
+ runtime: requiredString(verifier.runtime, "verifier runtime"),
873
+ sourceRevision: requiredString(verifier.sourceRevision, "verifier source revision"),
874
+ identityRoot: requiredRoot(verifier.identityRoot, "verifier identity root"),
875
+ },
876
+ observedAt: requiredString(observedAt, "observedAt"),
877
+ expiresAt: requiredString(expiresAt, "expiresAt"),
878
+ facts,
879
+ failureIds: facts.filter((entry) => entry.status === "fail").map((entry) => entry.id),
880
+ nonClaims: [...descriptor.trustedComputingBase.nonClaims],
881
+ };
882
+ return { ...core, receiptRoot: githubGovernanceDigest(core) };
883
+ }
884
+
885
+ export function verifyGithubGovernanceReceipt(receipt, {
886
+ expectedOrganization,
887
+ expectedRepository,
888
+ expectedRepositoryIdentityRoot,
889
+ expectedTargetRef,
890
+ expectedPolicyRoot,
891
+ expectedVerifierSourceRevision,
892
+ now = new Date().toISOString(),
893
+ } = {}) {
894
+ if (receipt?.contract !== GITHUB_GOVERNANCE_RECEIPT_CONTRACT) {
895
+ throw new Error("GitHub governance receipt contract mismatch");
896
+ }
897
+ const { receiptRoot, ...core } = receipt;
898
+ if (receiptRoot !== githubGovernanceDigest(core)) {
899
+ throw new Error("GitHub governance receipt root mismatch");
900
+ }
901
+ if (receipt.qualifying !== true || receipt.status !== "qualifying" || receipt.failureIds?.length) {
902
+ throw new Error("GitHub governance receipt is non-qualifying");
903
+ }
904
+ if (expectedOrganization && receipt.organization !== expectedOrganization) {
905
+ throw new Error("GitHub governance receipt organization mismatch");
906
+ }
907
+ if (expectedRepository && receipt.repository !== expectedRepository) {
908
+ throw new Error("GitHub governance receipt repository mismatch");
909
+ }
910
+ if (expectedRepositoryIdentityRoot &&
911
+ receipt.repositoryIdentityRoot !== expectedRepositoryIdentityRoot) {
912
+ throw new Error("GitHub governance receipt repository identity mismatch");
913
+ }
914
+ if (expectedTargetRef && receipt.targetRef !== normalizedRef(expectedTargetRef)) {
915
+ throw new Error("GitHub governance receipt target ref mismatch");
916
+ }
917
+ if (expectedPolicyRoot && receipt.policyRoot !== expectedPolicyRoot) {
918
+ throw new Error("GitHub governance receipt policy root mismatch");
919
+ }
920
+ if (expectedVerifierSourceRevision &&
921
+ receipt.verifier?.sourceRevision !== expectedVerifierSourceRevision) {
922
+ throw new Error("GitHub governance receipt verifier source revision mismatch");
923
+ }
924
+ const observed = Date.parse(receipt.observedAt);
925
+ const expires = Date.parse(receipt.expiresAt);
926
+ const instant = Date.parse(now);
927
+ if (![observed, expires, instant].every(Number.isFinite) || observed > instant || expires <= instant) {
928
+ throw new Error("GitHub governance receipt freshness is invalid");
929
+ }
930
+ return receipt;
931
+ }
932
+
933
+ export function createGithubGovernanceRolloutPlan({
934
+ repository,
935
+ targetRef,
936
+ inventory,
937
+ rollbackSnapshot,
938
+ rollbackProtectionExists = true,
939
+ desiredProtection,
940
+ } = {}) {
941
+ const fullName = requiredString(repository, "repository");
942
+ const branch = normalizedRef(targetRef);
943
+ if (!inventory || !rollbackSnapshot) {
944
+ throw new Error("read-only inventory and frozen rollback snapshot are required");
945
+ }
946
+ const inventoryRoot = githubGovernanceDigest(inventory);
947
+ const rollbackSnapshotRoot = githubGovernanceDigest(rollbackSnapshot);
948
+ const requiredCheckBindings = (desiredProtection?.requiredCheckBindings ||
949
+ desiredProtection?.requiredChecks || []).map((entry) => {
950
+ const context = requiredString(
951
+ typeof entry === "string" ? entry : entry?.context,
952
+ "required check context",
953
+ );
954
+ const appId = typeof entry === "string" ? null : entry?.app_id;
955
+ if (appId !== null && (!Number.isInteger(appId) || appId <= 0)) {
956
+ throw new Error(`required check app_id must be a positive integer or null: ${context}`);
957
+ }
958
+ return { context, app_id: appId ?? null };
959
+ });
960
+ const body = {
961
+ required_status_checks: {
962
+ strict: desiredProtection?.strictRequiredChecks === true,
963
+ checks: requiredCheckBindings,
964
+ },
965
+ enforce_admins: true,
966
+ required_pull_request_reviews: {
967
+ dismiss_stale_reviews: true,
968
+ require_code_owner_reviews: true,
969
+ required_approving_review_count: Math.max(
970
+ 1,
971
+ Number(desiredProtection?.requiredApprovals || 1),
972
+ ),
973
+ require_last_push_approval: true,
974
+ dismissal_restrictions: {
975
+ users: [],
976
+ teams: [],
977
+ apps: [],
978
+ },
979
+ bypass_pull_request_allowances: {
980
+ users: [],
981
+ teams: [],
982
+ apps: [],
983
+ },
984
+ },
985
+ restrictions: null,
986
+ required_conversation_resolution: true,
987
+ allow_force_pushes: false,
988
+ allow_deletions: false,
989
+ };
990
+ const endpoint = `repos/${fullName}/branches/${encodeURIComponent(branch)}/protection`;
991
+ const core = {
992
+ schemaVersion: 1,
993
+ contract: GITHUB_GOVERNANCE_ROLLOUT_CONTRACT,
994
+ repository: fullName,
995
+ targetRef: branch,
996
+ inventoryRoot,
997
+ rollbackSnapshotRoot,
998
+ operations: [{ method: "PUT", endpoint, body }],
999
+ impact: [
1000
+ "require pull requests and a fresh Code Owner approval",
1001
+ "enforce required checks and resolved conversations for administrators",
1002
+ "deny force pushes and protected-branch deletion",
1003
+ ],
1004
+ expectedObservation: {
1005
+ codeOwnerReviewRequired: true,
1006
+ requiredApprovals: body.required_pull_request_reviews.required_approving_review_count,
1007
+ dismissStaleReviews: true,
1008
+ requireLastPushApproval: true,
1009
+ enforceAdmins: true,
1010
+ requiredChecks: body.required_status_checks.checks.map((entry) => entry.context),
1011
+ requiredCheckBindings: body.required_status_checks.checks,
1012
+ bypassActors: [],
1013
+ allowForcePushes: false,
1014
+ allowDeletions: false,
1015
+ },
1016
+ rollback: [{
1017
+ method: rollbackProtectionExists ? "PUT" : "DELETE",
1018
+ endpoint,
1019
+ body: rollbackProtectionExists ? rollbackSnapshot : null,
1020
+ preconditionRoot: rollbackSnapshotRoot,
1021
+ }],
1022
+ };
1023
+ return { ...core, planRoot: githubGovernanceDigest(core) };
1024
+ }
1025
+
1026
+ export function normalizeGithubBranchProtectionSnapshot(protection = {}) {
1027
+ const providerChecks = protection.required_status_checks?.checks || [];
1028
+ const checks = providerChecks.length > 0
1029
+ ? providerChecks.map((entry) => ({
1030
+ context: requiredString(entry?.context, "required check context"),
1031
+ app_id: entry?.app_id ?? null,
1032
+ }))
1033
+ : (protection.required_status_checks?.contexts || []).map((context) => ({
1034
+ context: requiredString(context, "required check context"),
1035
+ app_id: null,
1036
+ }));
1037
+ const uniqueChecks = [];
1038
+ for (const check of checks) {
1039
+ if (!uniqueChecks.some((entry) =>
1040
+ entry.context === check.context && entry.app_id === check.app_id)) {
1041
+ uniqueChecks.push(check);
1042
+ }
1043
+ }
1044
+ const actors = (value, key) => (value?.[key] || [])
1045
+ .map((entry) => String(entry?.login || entry?.slug || entry?.name || "").trim())
1046
+ .filter(Boolean)
1047
+ .sort();
1048
+ const restrictions = protection.restrictions;
1049
+ const review = protection.required_pull_request_reviews;
1050
+ const allowance = review?.bypass_pull_request_allowances;
1051
+ const dismissals = review?.dismissal_restrictions;
1052
+ return {
1053
+ required_status_checks: protection.required_status_checks
1054
+ ? {
1055
+ strict: protection.required_status_checks.strict === true,
1056
+ checks: uniqueChecks,
1057
+ }
1058
+ : null,
1059
+ enforce_admins: protection.enforce_admins?.enabled === true,
1060
+ required_pull_request_reviews: review
1061
+ ? {
1062
+ dismissal_restrictions: {
1063
+ users: actors(dismissals, "users"),
1064
+ teams: actors(dismissals, "teams"),
1065
+ apps: actors(dismissals, "apps"),
1066
+ },
1067
+ dismiss_stale_reviews: review.dismiss_stale_reviews === true,
1068
+ require_code_owner_reviews: review.require_code_owner_reviews === true,
1069
+ required_approving_review_count: Number(review.required_approving_review_count || 0),
1070
+ require_last_push_approval: review.require_last_push_approval === true,
1071
+ bypass_pull_request_allowances: {
1072
+ users: actors(allowance, "users"),
1073
+ teams: actors(allowance, "teams"),
1074
+ apps: actors(allowance, "apps"),
1075
+ },
1076
+ }
1077
+ : null,
1078
+ restrictions: restrictions
1079
+ ? {
1080
+ users: actors(restrictions, "users"),
1081
+ teams: actors(restrictions, "teams"),
1082
+ apps: actors(restrictions, "apps"),
1083
+ }
1084
+ : null,
1085
+ required_linear_history: protection.required_linear_history?.enabled === true,
1086
+ allow_force_pushes: protection.allow_force_pushes?.enabled === true,
1087
+ allow_deletions: protection.allow_deletions?.enabled === true,
1088
+ block_creations: protection.block_creations?.enabled === true,
1089
+ required_conversation_resolution:
1090
+ protection.required_conversation_resolution?.enabled === true,
1091
+ lock_branch: protection.lock_branch?.enabled === true,
1092
+ allow_fork_syncing: protection.allow_fork_syncing?.enabled === true,
1093
+ };
1094
+ }
1095
+
1096
+ export function normalizeGithubRulesetSnapshot(ruleset = {}) {
1097
+ return {
1098
+ name: requiredString(ruleset.name, "ruleset name"),
1099
+ target: requiredString(ruleset.target, "ruleset target"),
1100
+ enforcement: requiredString(ruleset.enforcement, "ruleset enforcement"),
1101
+ bypass_actors: (ruleset.bypass_actors || []).map((actor) => ({
1102
+ actor_id: Number(actor?.actor_id || 0),
1103
+ actor_type: requiredString(actor?.actor_type, "ruleset bypass actor type"),
1104
+ bypass_mode: requiredString(actor?.bypass_mode, "ruleset bypass mode"),
1105
+ })),
1106
+ conditions: structuredClone(ruleset.conditions || {}),
1107
+ rules: structuredClone(ruleset.rules || []),
1108
+ };
1109
+ }
1110
+
1111
+ export function createGithubRulesetBypassRolloutPlan({
1112
+ repository,
1113
+ rulesetId,
1114
+ inventory,
1115
+ rollbackSnapshot,
1116
+ } = {}) {
1117
+ const fullName = requiredString(repository, "repository");
1118
+ const id = Number(rulesetId);
1119
+ if (!Number.isInteger(id) || id <= 0) {
1120
+ throw new Error("ruleset id must be a positive integer");
1121
+ }
1122
+ if (!inventory || !rollbackSnapshot) {
1123
+ throw new Error("read-only inventory and frozen rollback snapshot are required");
1124
+ }
1125
+ const before = normalizeGithubRulesetSnapshot(rollbackSnapshot);
1126
+ const desired = { ...before, bypass_actors: [] };
1127
+ const endpoint = `repos/${fullName}/rulesets/${id}`;
1128
+ const core = {
1129
+ schemaVersion: 1,
1130
+ contract: GITHUB_GOVERNANCE_RULESET_ROLLOUT_CONTRACT,
1131
+ repository: fullName,
1132
+ rulesetId: id,
1133
+ inventoryRoot: githubGovernanceDigest(inventory),
1134
+ rollbackSnapshotRoot: githubGovernanceDigest(before),
1135
+ operations: [{ method: "PUT", endpoint, body: desired }],
1136
+ impact: [
1137
+ "remove every user, team, and App bypass actor from one exact repository ruleset",
1138
+ "preserve the ruleset name, target, enforcement, conditions, and rules",
1139
+ ],
1140
+ expectedObservation: {
1141
+ rulesetRoot: githubGovernanceDigest(desired),
1142
+ bypassActors: [],
1143
+ },
1144
+ rollback: [{
1145
+ method: "PUT",
1146
+ endpoint,
1147
+ body: before,
1148
+ preconditionRoot: githubGovernanceDigest(before),
1149
+ }],
1150
+ };
1151
+ return { ...core, planRoot: githubGovernanceDigest(core) };
1152
+ }
1153
+
1154
+ function providerRulesetCheckBindings(bindings) {
1155
+ return (bindings || []).map((entry) => {
1156
+ const context = requiredString(entry?.context, "required check context");
1157
+ const appId = entry?.app_id ?? entry?.appId ?? null;
1158
+ if (appId !== null && (!Number.isInteger(appId) || appId <= 0)) {
1159
+ throw new Error(`required check app id must be a positive integer or null: ${context}`);
1160
+ }
1161
+ return {
1162
+ context,
1163
+ ...(appId === null ? {} : { integration_id: appId }),
1164
+ };
1165
+ });
1166
+ }
1167
+
1168
+ function providerRulesetBypassActors(actors) {
1169
+ return (actors || []).map((actor) => {
1170
+ const actorId = Number(actor?.actor_id ?? actor?.actorId ?? 0);
1171
+ const actorType = requiredString(
1172
+ actor?.actor_type ?? actor?.actorType,
1173
+ "ruleset bypass actor type",
1174
+ );
1175
+ const bypassMode = requiredString(
1176
+ actor?.bypass_mode ?? actor?.bypassMode,
1177
+ "ruleset bypass mode",
1178
+ );
1179
+ if (!Number.isInteger(actorId) || actorId <= 0) {
1180
+ throw new Error("ruleset bypass actor id must be a positive integer");
1181
+ }
1182
+ return {
1183
+ actor_id: actorId,
1184
+ actor_type: actorType,
1185
+ bypass_mode: bypassMode,
1186
+ };
1187
+ });
1188
+ }
1189
+
1190
+ export function createGithubRulesetGovernanceRolloutPlan({
1191
+ repository,
1192
+ targetRef,
1193
+ rulesetId,
1194
+ inventory,
1195
+ rollbackSnapshot,
1196
+ desiredProtection,
1197
+ } = {}) {
1198
+ const fullName = requiredString(repository, "repository");
1199
+ const branch = normalizedRef(targetRef);
1200
+ const id = Number(rulesetId);
1201
+ if (!Number.isInteger(id) || id <= 0) {
1202
+ throw new Error("ruleset id must be a positive integer");
1203
+ }
1204
+ if (!inventory || !rollbackSnapshot) {
1205
+ throw new Error("read-only inventory and frozen rollback snapshot are required");
1206
+ }
1207
+ const before = normalizeGithubRulesetSnapshot(rollbackSnapshot);
1208
+ const exactInclude = before.conditions?.ref_name?.include || [];
1209
+ const exactExclude = before.conditions?.ref_name?.exclude || [];
1210
+ if (
1211
+ exactInclude.length !== 1 ||
1212
+ exactInclude[0] !== `refs/heads/${branch}` ||
1213
+ exactExclude.length !== 0
1214
+ ) {
1215
+ throw new Error("ruleset policy rollout requires one exact target branch condition");
1216
+ }
1217
+ const requiredStatusChecks = providerRulesetCheckBindings(
1218
+ desiredProtection?.requiredCheckBindings,
1219
+ );
1220
+ if (requiredStatusChecks.length === 0) {
1221
+ throw new Error("ruleset policy rollout requires descriptor-bound status checks");
1222
+ }
1223
+ const requiredApprovals = Number(desiredProtection?.requiredApprovals);
1224
+ if (!Number.isInteger(requiredApprovals) || requiredApprovals < 1) {
1225
+ throw new Error("ruleset policy rollout requires a positive approval count");
1226
+ }
1227
+ const pullRequestRules = before.rules.filter((rule) => rule.type === "pull_request");
1228
+ const statusCheckRules = before.rules.filter(
1229
+ (rule) => rule.type === "required_status_checks",
1230
+ );
1231
+ if (pullRequestRules.length > 1 || statusCheckRules.length > 1) {
1232
+ throw new Error("ruleset policy rollout rejects duplicate managed rule types");
1233
+ }
1234
+ const desiredPullRequestRule = {
1235
+ type: "pull_request",
1236
+ parameters: {
1237
+ allowed_merge_methods: ["merge", "squash", "rebase"],
1238
+ dismissal_restriction: {
1239
+ allowed_actors: [],
1240
+ enabled: false,
1241
+ },
1242
+ required_reviewers: [],
1243
+ ...(pullRequestRules[0]?.parameters || {}),
1244
+ dismiss_stale_reviews_on_push: true,
1245
+ require_code_owner_review: true,
1246
+ require_last_push_approval: true,
1247
+ required_approving_review_count: requiredApprovals,
1248
+ required_review_thread_resolution: true,
1249
+ },
1250
+ };
1251
+ const desiredStatusCheckRule = {
1252
+ type: "required_status_checks",
1253
+ parameters: {
1254
+ do_not_enforce_on_create: false,
1255
+ ...(statusCheckRules[0]?.parameters || {}),
1256
+ required_status_checks: requiredStatusChecks,
1257
+ strict_required_status_checks_policy:
1258
+ desiredProtection?.strictRequiredChecks === true,
1259
+ },
1260
+ };
1261
+ const desiredRules = before.rules.map((rule) => {
1262
+ if (rule.type === "pull_request") return desiredPullRequestRule;
1263
+ if (rule.type === "required_status_checks") return desiredStatusCheckRule;
1264
+ return structuredClone(rule);
1265
+ });
1266
+ if (pullRequestRules.length === 0) desiredRules.push(desiredPullRequestRule);
1267
+ if (statusCheckRules.length === 0) desiredRules.push(desiredStatusCheckRule);
1268
+ const desired = {
1269
+ ...before,
1270
+ bypass_actors: providerRulesetBypassActors(
1271
+ desiredProtection?.rulesetBypassActors,
1272
+ ),
1273
+ rules: desiredRules,
1274
+ };
1275
+ const endpoint = `repos/${fullName}/rulesets/${id}`;
1276
+ const core = {
1277
+ schemaVersion: 1,
1278
+ contract: GITHUB_GOVERNANCE_RULESET_ROLLOUT_CONTRACT,
1279
+ repository: fullName,
1280
+ targetRef: branch,
1281
+ rulesetId: id,
1282
+ inventoryRoot: githubGovernanceDigest(inventory),
1283
+ rollbackSnapshotRoot: githubGovernanceDigest(before),
1284
+ operations: [{ method: "PUT", endpoint, body: desired }],
1285
+ impact: [
1286
+ "replace ruleset bypass actors with the exact provider-admitted desired set",
1287
+ "require fresh Code Owner review and resolved review threads",
1288
+ "bind required status checks and strictness to the authoritative target descriptor",
1289
+ "preserve unrelated ruleset rules and exact target conditions",
1290
+ ],
1291
+ expectedObservation: {
1292
+ rulesetRoot: githubGovernanceDigest(desired),
1293
+ bypassActors: desired.bypass_actors,
1294
+ requiredCheckBindings: requiredStatusChecks,
1295
+ strictRequiredChecks:
1296
+ desiredProtection?.strictRequiredChecks === true,
1297
+ requiredApprovals,
1298
+ },
1299
+ rollback: [{
1300
+ method: "PUT",
1301
+ endpoint,
1302
+ body: before,
1303
+ preconditionRoot: githubGovernanceDigest(before),
1304
+ }],
1305
+ };
1306
+ return { ...core, planRoot: githubGovernanceDigest(core) };
1307
+ }
1308
+
1309
+ export const BUILDCHAIN_GITHUB_GOVERNANCE_AUTHORITY =
1310
+ Object.freeze(createBuildchainGithubGovernanceAuthority());
1311
+ export const BUILDCHAIN_GITHUB_GOVERNANCE_PROTECTED_PATHS =
1312
+ PROTECTED_AUTHORITY_PATHS;