@kungfu-tech/buildchain 2.14.1-alpha.1 → 2.14.1

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-publication-release-registry",
4
- "generatedAt": "2026-07-17T00:51:57.848Z",
5
- "publishedAt": "2026-07-17T00:51:57.848Z",
4
+ "generatedAt": "2026-07-17T00:55:24.253Z",
5
+ "publishedAt": "2026-07-17T00:55:24.253Z",
6
6
  "reproducible": true,
7
7
  "timestampPolicy": "ci-injected",
8
8
  "deterministicInputs": [
@@ -19,7 +19,7 @@
19
19
  "declared Buildchain surface manifest contract"
20
20
  ],
21
21
  "sourceDateEpoch": "0",
22
- "sourceRevision": "5073d72855cd9136112a1866398f072ac3a065bb",
22
+ "sourceRevision": "d962181c3023e587488a5276b10307007cc5a13f",
23
23
  "timestampPolicyDetails": {
24
24
  "contract": "kungfu-buildchain-surface-timestamp-policy",
25
25
  "timestampFields": [
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "package": {
34
34
  "name": "@kungfu-tech/buildchain",
35
- "version": "2.14.1-alpha.1",
35
+ "version": "2.14.1",
36
36
  "versionSource": "package.json#version"
37
37
  },
38
38
  "sourceKind": "package-site-bundle",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-site-manifest",
4
- "generatedAt": "2026-07-17T00:51:57.848Z",
5
- "publishedAt": "2026-07-17T00:51:57.848Z",
4
+ "generatedAt": "2026-07-17T00:55:24.253Z",
5
+ "publishedAt": "2026-07-17T00:55:24.253Z",
6
6
  "reproducible": true,
7
7
  "timestampPolicy": "ci-injected",
8
8
  "deterministicInputs": [
@@ -19,7 +19,7 @@
19
19
  "declared Buildchain surface manifest contract"
20
20
  ],
21
21
  "sourceDateEpoch": "0",
22
- "sourceRevision": "5073d72855cd9136112a1866398f072ac3a065bb",
22
+ "sourceRevision": "d962181c3023e587488a5276b10307007cc5a13f",
23
23
  "timestampPolicyDetails": {
24
24
  "contract": "kungfu-buildchain-surface-timestamp-policy",
25
25
  "timestampFields": [
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "package": {
39
39
  "name": "@kungfu-tech/buildchain",
40
- "version": "2.14.1-alpha.1",
40
+ "version": "2.14.1",
41
41
  "versionSource": "package.json#version"
42
42
  },
43
43
  "entrypoint": "buildchain-site.json",
@@ -237,7 +237,7 @@
237
237
  "path": "docs/release-governance.md",
238
238
  "plane": "why",
239
239
  "exists": true,
240
- "digest": "sha256:5e0e6ef6720b51bb156da54390850688ef9a538908afb22484011364102e48eb"
240
+ "digest": "sha256:5cf4bf4332119531f6aaa1c123d7f64e6af4a61611c4e4a33d40bd9fb46448cf"
241
241
  },
242
242
  {
243
243
  "id": "release-flow",
@@ -879,7 +879,6 @@
879
879
  "cadence",
880
880
  "capabilities",
881
881
  "dry-run",
882
- "landing-mode",
883
882
  "max-actions",
884
883
  "merge-method",
885
884
  "mode",
@@ -889,7 +888,7 @@
889
888
  "same-repository-only",
890
889
  "target-branch"
891
890
  ],
892
- "inputCount": 16,
891
+ "inputCount": 15,
893
892
  "secrets": [
894
893
  "github-token"
895
894
  ],
@@ -1002,7 +1001,6 @@
1002
1001
  "buildchain-ref",
1003
1002
  "buildchain-repository",
1004
1003
  "dry-run",
1005
- "landing-mode",
1006
1004
  "max-merges",
1007
1005
  "merge-method",
1008
1006
  "ready-label",
@@ -1011,20 +1009,18 @@
1011
1009
  "same-repository-only",
1012
1010
  "target-branch"
1013
1011
  ],
1014
- "inputCount": 13,
1012
+ "inputCount": 12,
1015
1013
  "secrets": [
1016
1014
  "github-token"
1017
1015
  ],
1018
1016
  "secretCount": 1,
1019
1017
  "outputs": [
1020
- "action-count",
1021
- "enqueued-count",
1022
1018
  "evaluated-count",
1023
1019
  "final-base-sha",
1024
1020
  "merged-count",
1025
1021
  "skipped-count"
1026
1022
  ],
1027
- "outputCount": 6,
1023
+ "outputCount": 4,
1028
1024
  "surface": "dev-governance",
1029
1025
  "capabilityGroup": "governance-versioning",
1030
1026
  "status": "active"
@@ -1166,7 +1162,6 @@
1166
1162
  "buildchain-ref",
1167
1163
  "buildchain-repository",
1168
1164
  "dry-run",
1169
- "landing-mode",
1170
1165
  "max-actions",
1171
1166
  "merge-method",
1172
1167
  "ready-label",
@@ -1175,7 +1170,7 @@
1175
1170
  "same-repository-only",
1176
1171
  "target-branch"
1177
1172
  ],
1178
- "inputCount": 13,
1173
+ "inputCount": 12,
1179
1174
  "secrets": [
1180
1175
  "github-token"
1181
1176
  ],
@@ -435,21 +435,9 @@ The workflow defaults are conservative. A PR is skipped unless it targets the
435
435
  configured dev line, is not a draft, has the ready label, has no block label,
436
436
  comes from the same repository, uses an allowed work-branch prefix, has a
437
437
  current approval, is mergeable, and has the configured required checks passing.
438
- `landing-mode: auto` reads the target branch's native merge-queue state. When a
439
- queue exists, Buildchain never calls the direct merge endpoint: it admits at
440
- most one PR against the observed target-branch SHA and immutable PR head, then
441
- calls GraphQL `enqueuePullRequest` with `expectedHeadOid`. GitHub's
442
- `merge_group` checks remain the final authority for the projected merge.
443
-
444
- The admission receipt records the expected and observed base/head SHAs, policy
445
- checks, decision, reason, and active predecessor. Buildchain re-reads the base,
446
- head, mergeability, and native queue immediately before enqueueing. Base or
447
- head drift fails closed, an active queue entry blocks admission, and a rejected
448
- ready predecessor leaves its PR open while later PRs receive
449
- `blocked-by-predecessor`. Workflow concurrency serializes Buildchain-owned
450
- admission runs; GitHub still owns the atomic queue and protected-ref update.
451
- Repositories may explicitly select `landing-mode: direct` only when the target
452
- branch has no native queue. Queue presence always disables the direct path.
438
+ After each merge, the next PR is re-evaluated before it can move the protected
439
+ dev branch. This prevents one merge from silently making the next candidate
440
+ stale or conflicting.
453
441
 
454
442
  The canonical consumer required check context is `check / check`, matching the
455
443
  reusable workflow call plus its `check` job. Buildchain's own `Verify` workflow
@@ -509,7 +497,6 @@ jobs:
509
497
  ready-label: ready
510
498
  block-labels: blocked,do-not-merge
511
499
  max-merges: 1
512
- landing-mode: auto
513
500
  dry-run: ${{ inputs.dry-run || false }}
514
501
  ```
515
502
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungfu-tech/buildchain",
3
- "version": "2.14.1-alpha.1",
3
+ "version": "2.14.1",
4
4
  "private": false,
5
5
  "description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
6
6
  "repository": "https://github.com/kungfu-systems/buildchain",
@@ -91,7 +91,6 @@ export function normalizePatrolOptions(options = {}) {
91
91
  sameRepositoryOnly: options.sameRepositoryOnly ?? process.env.BUILDCHAIN_PATROL_SAME_REPOSITORY_ONLY,
92
92
  maxActions: intOption(options.maxActions ?? process.env.BUILDCHAIN_PATROL_MAX_ACTIONS, 1),
93
93
  mergeMethod: String(options.mergeMethod || process.env.BUILDCHAIN_PATROL_MERGE_METHOD || "merge").trim(),
94
- landingMode: String(options.landingMode || process.env.BUILDCHAIN_PATROL_LANDING_MODE || "auto").trim(),
95
94
  dryRun: boolOption(options.dryRun ?? process.env.BUILDCHAIN_PATROL_DRY_RUN, true),
96
95
  outputPath: String(options.outputPath || process.env.BUILDCHAIN_PATROL_OUTPUT_PATH || DEFAULT_OUTPUT_PATH),
97
96
  };
@@ -168,7 +167,6 @@ export async function runBuildchainPatrol(optionsInput = {}, clientInput) {
168
167
  sameRepositoryOnly: options.sameRepositoryOnly,
169
168
  maxMerges: options.maxActions,
170
169
  mergeMethod: options.mergeMethod,
171
- landingMode: options.landingMode,
172
170
  dryRun: options.dryRun,
173
171
  outputPath: path.join(path.dirname(options.outputPath), "dev-pr-auto-merge.json"),
174
172
  },
@@ -180,7 +178,7 @@ export async function runBuildchainPatrol(optionsInput = {}, clientInput) {
180
178
  result: mergeResult,
181
179
  });
182
180
  result.summary.evaluatedCount += mergeResult.evaluated.length;
183
- result.summary.actionCount += mergeResult.actions.length;
181
+ result.summary.actionCount += mergeResult.merged.length;
184
182
  result.summary.skippedCount += mergeResult.skipped.length;
185
183
  }
186
184
 
@@ -7,15 +7,6 @@ const DEFAULT_ALLOWED_HEAD_PREFIXES = ["feature/", "fix/", "chore/", "docs/", "c
7
7
  const DEFAULT_REQUIRED_CHECKS = ["check"];
8
8
  const SUCCESS_STATES = new Set(["success"]);
9
9
  const SUCCESS_CONCLUSIONS = new Set(["success", "neutral", "skipped"]);
10
- const VALID_LANDING_MODES = new Set(["auto", "direct", "queue"]);
11
- const STATIC_SKIP_REASONS = new Set([
12
- "draft",
13
- "fork-or-cross-repository-head",
14
- "head-prefix-not-allowed",
15
- "missing-ready-label",
16
- "blocked-label",
17
- ]);
18
- const ADMISSION_CONTRACT = "kungfu-buildchain-dev-merge-queue-admission";
19
10
 
20
11
  function splitList(value, fallback = []) {
21
12
  if (Array.isArray(value)) return value.map((entry) => String(entry).trim()).filter(Boolean);
@@ -39,14 +30,6 @@ function intOption(value, fallback) {
39
30
  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : fallback;
40
31
  }
41
32
 
42
- function choiceOption(value, valid, fallback, field) {
43
- const normalized = String(value || fallback).trim().toLowerCase();
44
- if (!valid.has(normalized)) {
45
- throw new Error(`${field} must be one of ${[...valid].join(", ")}, got: ${value || "<empty>"}`);
46
- }
47
- return normalized;
48
- }
49
-
50
33
  function normalizeRepo(value) {
51
34
  const text = String(value || "").trim();
52
35
  const match = text.match(/^([^/\s]+)\/([^/\s]+)$/);
@@ -66,7 +49,6 @@ function normalizeOptions(options = {}) {
66
49
  sameRepositoryOnly: boolOption(options.sameRepositoryOnly, true),
67
50
  maxMerges: intOption(options.maxMerges, 1),
68
51
  mergeMethod: String(options.mergeMethod || "merge").trim(),
69
- landingMode: choiceOption(options.landingMode, VALID_LANDING_MODES, "auto", "landing mode"),
70
52
  dryRun: boolOption(options.dryRun, true),
71
53
  pollMergeableAttempts: intOption(options.pollMergeableAttempts, 3),
72
54
  pollMergeableDelayMs: intOption(options.pollMergeableDelayMs, 1000),
@@ -183,12 +165,6 @@ export async function evaluatePullRequest(pr, options, client) {
183
165
  attempts: options.pollMergeableAttempts,
184
166
  delayMs: options.pollMergeableDelayMs,
185
167
  });
186
- if (detailed.base?.ref && detailed.base.ref !== options.targetBranch) {
187
- return skip("base-branch-drift", {
188
- expectedBaseRef: options.targetBranch,
189
- observedBaseRef: detailed.base.ref,
190
- });
191
- }
192
168
  if (!mergeableAccepted(detailed)) {
193
169
  return skip("not-mergeable", {
194
170
  mergeable: detailed.mergeable,
@@ -196,25 +172,19 @@ export async function evaluatePullRequest(pr, options, client) {
196
172
  });
197
173
  }
198
174
 
199
- const approval = { required: options.requireApproval, passed: true };
200
175
  if (options.requireApproval) {
201
176
  const reviews = await client.listReviews(pr.number);
202
- approval.passed = hasRequiredApproval(reviews);
203
- if (!approval.passed) return skip("missing-approval", { approval });
177
+ if (!hasRequiredApproval(reviews)) return skip("missing-approval");
204
178
  }
205
179
 
206
- const observedHeadSha = detailed.head?.sha || pr.head?.sha || "";
207
- const checks = await client.listCommitChecks(observedHeadSha);
180
+ const checks = await client.listCommitChecks(pr.head?.sha);
208
181
  const checkSummary = summarizeChecks(checks, options.requiredChecks);
209
- if (!checkSummary.passed) return skip("required-checks-not-passing", { checks: checkSummary, approval });
182
+ if (!checkSummary.passed) return skip("required-checks-not-passing", { checks: checkSummary });
210
183
 
211
184
  return {
212
185
  action: options.dryRun ? "would-merge" : "merge",
213
186
  reason: options.dryRun ? "dry-run" : "eligible",
214
187
  checks: checkSummary,
215
- approval,
216
- pullRequestId: detailed.node_id || pr.node_id || "",
217
- observedHeadSha,
218
188
  };
219
189
  }
220
190
 
@@ -332,178 +302,6 @@ export class GitHubClient {
332
302
  const { data } = await this.request("GET", `/repos/${this.repository.owner}/${this.repository.repo}/git/ref/${ref}`);
333
303
  return data.object?.sha || "";
334
304
  }
335
-
336
- async graphql(query, variables = {}) {
337
- const { data } = await this.request("POST", "/graphql", {
338
- body: { query, variables },
339
- });
340
- if (Array.isArray(data?.errors) && data.errors.length > 0) {
341
- const error = new Error(data.errors.map((entry) => entry.message).filter(Boolean).join("; ") || "GitHub GraphQL request failed");
342
- error.data = data;
343
- throw error;
344
- }
345
- return data?.data || {};
346
- }
347
-
348
- async getMergeQueueState(branch) {
349
- const data = await this.graphql(
350
- `query BuildchainMergeQueueState($owner: String!, $repo: String!, $branch: String!) {
351
- repository(owner: $owner, name: $repo) {
352
- mergeQueue(branch: $branch) {
353
- id
354
- entries(first: 100) {
355
- nodes {
356
- id
357
- position
358
- state
359
- baseCommit { oid }
360
- headCommit { oid }
361
- pullRequest { number headRefOid }
362
- }
363
- }
364
- }
365
- }
366
- }`,
367
- {
368
- owner: this.repository.owner,
369
- repo: this.repository.repo,
370
- branch,
371
- },
372
- );
373
- const queue = data.repository?.mergeQueue || null;
374
- return {
375
- enabled: Boolean(queue),
376
- id: queue?.id || "",
377
- entries: (queue?.entries?.nodes || []).map((entry) => ({
378
- id: entry.id || "",
379
- position: entry.position,
380
- state: entry.state || "",
381
- pullRequestNumber: entry.pullRequest?.number || null,
382
- pullRequestHeadSha: entry.pullRequest?.headRefOid || "",
383
- baseSha: entry.baseCommit?.oid || "",
384
- headSha: entry.headCommit?.oid || "",
385
- })),
386
- };
387
- }
388
-
389
- async enqueuePullRequest({ pullRequestId, expectedHeadOid }) {
390
- const data = await this.graphql(
391
- `mutation BuildchainEnqueuePullRequest($input: EnqueuePullRequestInput!) {
392
- enqueuePullRequest(input: $input) {
393
- mergeQueueEntry {
394
- id
395
- position
396
- state
397
- baseCommit { oid }
398
- headCommit { oid }
399
- pullRequest { number headRefOid }
400
- }
401
- }
402
- }`,
403
- {
404
- input: {
405
- pullRequestId,
406
- expectedHeadOid,
407
- },
408
- },
409
- );
410
- const entry = data.enqueuePullRequest?.mergeQueueEntry;
411
- if (!entry?.id) throw new Error("GitHub did not return a merge queue entry");
412
- return {
413
- id: entry.id,
414
- position: entry.position,
415
- state: entry.state || "",
416
- pullRequestNumber: entry.pullRequest?.number || null,
417
- pullRequestHeadSha: entry.pullRequest?.headRefOid || "",
418
- baseSha: entry.baseCommit?.oid || "",
419
- headSha: entry.headCommit?.oid || "",
420
- };
421
- }
422
- }
423
-
424
- function queuePredecessor(queueState, fallback = {}) {
425
- const entry = queueState?.entries?.[0];
426
- if (entry) {
427
- return {
428
- queueEntryId: entry.id,
429
- pullRequestNumber: entry.pullRequestNumber,
430
- headSha: entry.pullRequestHeadSha || entry.headSha || "",
431
- state: entry.state || "",
432
- };
433
- }
434
- return {
435
- queueEntryId: fallback.queueEntryId || "",
436
- pullRequestNumber: fallback.pullRequestNumber || null,
437
- headSha: fallback.headSha || "",
438
- state: fallback.state || "",
439
- };
440
- }
441
-
442
- function admissionReceipt({
443
- options,
444
- pr,
445
- expectedBaseSha,
446
- observedBaseSha,
447
- expectedHeadSha,
448
- observedHeadSha,
449
- decision,
450
- reason,
451
- checks,
452
- approval,
453
- predecessor,
454
- } = {}) {
455
- return {
456
- schemaVersion: 1,
457
- contract: ADMISSION_CONTRACT,
458
- repository: options.repository.fullName,
459
- targetBranch: options.targetBranch,
460
- pullRequestNumber: pr.number,
461
- expectedBaseSha: expectedBaseSha || "",
462
- observedBaseSha: observedBaseSha || "",
463
- expectedHeadSha: expectedHeadSha || "",
464
- observedHeadSha: observedHeadSha || "",
465
- approvalRequired: options.requireApproval,
466
- approval: approval || { required: options.requireApproval, passed: false },
467
- checks: checks || { required: options.requiredChecks, entries: [], passed: false },
468
- decision,
469
- reason,
470
- predecessor: predecessor || null,
471
- finalSafetyBoundary: "github-merge-group",
472
- };
473
- }
474
-
475
- function evaluatedEntry(pr, decision) {
476
- return {
477
- number: pr.number,
478
- title: pr.title || "",
479
- headRef: pr.head?.ref || "",
480
- headSha: pr.head?.sha || "",
481
- action: decision.action,
482
- reason: decision.reason,
483
- checks: decision.checks,
484
- };
485
- }
486
-
487
- function blockRemainingPullRequests(result, pullRequests, startIndex, options, expectedBaseSha, predecessor) {
488
- for (const pr of pullRequests.slice(startIndex)) {
489
- const entry = evaluatedEntry(pr, {
490
- action: "skip",
491
- reason: "blocked-by-predecessor",
492
- });
493
- entry.admissionReceipt = admissionReceipt({
494
- options,
495
- pr,
496
- expectedBaseSha,
497
- observedBaseSha: expectedBaseSha,
498
- expectedHeadSha: pr.head?.sha || "",
499
- observedHeadSha: pr.head?.sha || "",
500
- decision: "blocked",
501
- reason: "blocked-by-predecessor",
502
- predecessor,
503
- });
504
- result.evaluated.push(entry);
505
- result.skipped.push(entry);
506
- }
507
305
  }
508
306
 
509
307
  export async function runDevPrAutoMerge(optionsInput = {}, clientInput) {
@@ -515,51 +313,21 @@ export async function runDevPrAutoMerge(optionsInput = {}, clientInput) {
515
313
  apiUrl: optionsInput.apiUrl || process.env.GITHUB_API_URL || "https://api.github.com",
516
314
  });
517
315
 
518
- const [pullRequests, initialBaseSha, initialQueueState] = await Promise.all([
519
- client.listPullRequests(options.targetBranch),
520
- client.getBranchSha(options.targetBranch).catch(() => ""),
521
- client.getMergeQueueState(options.targetBranch),
522
- ]);
523
- const landingMode = initialQueueState.enabled ? "queue" : options.landingMode === "queue" ? "queue" : "direct";
524
- const orderedPullRequests = landingMode === "queue"
525
- ? [...pullRequests].sort((left, right) => Number(left.number) - Number(right.number))
526
- : pullRequests;
316
+ const pullRequests = await client.listPullRequests(options.targetBranch);
527
317
  const result = {
528
318
  schemaVersion: 1,
529
319
  contract: "kungfu-buildchain-dev-pr-auto-merge",
530
320
  repository: options.repository.fullName,
531
321
  targetBranch: options.targetBranch,
532
- requestedLandingMode: options.landingMode,
533
- landingMode,
534
322
  dryRun: options.dryRun,
535
323
  maxMerges: options.maxMerges,
536
324
  evaluated: [],
537
- actions: [],
538
325
  merged: [],
539
- enqueued: [],
540
326
  skipped: [],
541
- initialBaseSha,
542
- finalBaseSha: initialBaseSha,
543
- mergeQueue: initialQueueState,
327
+ finalBaseSha: "",
544
328
  };
545
329
 
546
- if (landingMode === "queue" && !initialQueueState.enabled) {
547
- blockRemainingPullRequests(result, orderedPullRequests, 0, options, initialBaseSha, null);
548
- for (const entry of result.evaluated) {
549
- entry.reason = "merge-queue-not-enabled";
550
- entry.admissionReceipt.reason = "merge-queue-not-enabled";
551
- entry.admissionReceipt.decision = "rejected";
552
- }
553
- return result;
554
- }
555
-
556
- if (landingMode === "queue" && initialQueueState.entries.length > 0) {
557
- blockRemainingPullRequests(result, orderedPullRequests, 0, options, initialBaseSha, queuePredecessor(initialQueueState));
558
- return result;
559
- }
560
-
561
- for (let index = 0; index < orderedPullRequests.length; index += 1) {
562
- const pr = orderedPullRequests[index];
330
+ for (const pr of pullRequests) {
563
331
  if (result.merged.length >= options.maxMerges) {
564
332
  const entry = { number: pr.number, title: pr.title || "", action: "skip", reason: "max-merges-reached" };
565
333
  result.evaluated.push(entry);
@@ -568,134 +336,29 @@ export async function runDevPrAutoMerge(optionsInput = {}, clientInput) {
568
336
  }
569
337
 
570
338
  const decision = await evaluatePullRequest(pr, options, client);
571
- const entry = evaluatedEntry(pr, decision);
339
+ const entry = {
340
+ number: pr.number,
341
+ title: pr.title || "",
342
+ headRef: pr.head?.ref || "",
343
+ headSha: pr.head?.sha || "",
344
+ action: decision.action,
345
+ reason: decision.reason,
346
+ checks: decision.checks,
347
+ };
572
348
  result.evaluated.push(entry);
573
349
 
574
- if (landingMode === "direct" && decision.action === "merge") {
350
+ if (decision.action === "merge") {
575
351
  const mergeResult = await client.mergePullRequest(pr.number, {
576
352
  method: options.mergeMethod,
577
353
  sha: pr.head?.sha,
578
354
  });
579
355
  const mergedEntry = { ...entry, mergeSha: mergeResult.sha || "" };
580
356
  result.merged.push(mergedEntry);
581
- result.actions.push(mergedEntry);
582
357
  result.evaluated[result.evaluated.length - 1] = mergedEntry;
583
- } else if (landingMode === "direct" && decision.action === "would-merge") {
358
+ } else if (decision.action === "would-merge") {
584
359
  result.merged.push(entry);
585
- result.actions.push(entry);
586
- } else if (landingMode === "direct" || STATIC_SKIP_REASONS.has(decision.reason)) {
587
- result.skipped.push(entry);
588
- } else if (decision.action === "skip") {
589
- entry.admissionReceipt = admissionReceipt({
590
- options,
591
- pr,
592
- expectedBaseSha: initialBaseSha,
593
- observedBaseSha: initialBaseSha,
594
- expectedHeadSha: decision.observedHeadSha || pr.head?.sha || "",
595
- observedHeadSha: decision.observedHeadSha || pr.head?.sha || "",
596
- decision: "rejected",
597
- reason: decision.reason,
598
- checks: decision.checks,
599
- approval: decision.approval,
600
- });
601
- result.skipped.push(entry);
602
- blockRemainingPullRequests(result, orderedPullRequests, index + 1, options, initialBaseSha, queuePredecessor(null, {
603
- pullRequestNumber: pr.number,
604
- headSha: decision.observedHeadSha || pr.head?.sha || "",
605
- state: "ADMISSION_REJECTED",
606
- }));
607
- break;
608
360
  } else {
609
- const expectedHeadSha = decision.observedHeadSha || pr.head?.sha || "";
610
- const [observedPullRequest, observedBaseSha, observedQueueState] = await Promise.all([
611
- client.getPullRequest(pr.number, {
612
- attempts: options.pollMergeableAttempts,
613
- delayMs: options.pollMergeableDelayMs,
614
- }),
615
- client.getBranchSha(options.targetBranch),
616
- client.getMergeQueueState(options.targetBranch),
617
- ]);
618
- const observedHeadSha = observedPullRequest.head?.sha || "";
619
- let admissionDecision = options.dryRun ? "planned" : "accepted";
620
- let admissionReason = options.dryRun ? "dry-run" : "eligible";
621
- let predecessor = null;
622
- if (observedQueueState.entries.length > 0) {
623
- admissionDecision = "blocked";
624
- admissionReason = "blocked-by-predecessor";
625
- predecessor = queuePredecessor(observedQueueState);
626
- } else if (observedBaseSha !== initialBaseSha) {
627
- admissionDecision = "rejected";
628
- admissionReason = "base-sha-drift";
629
- } else if (observedHeadSha !== expectedHeadSha) {
630
- admissionDecision = "rejected";
631
- admissionReason = "head-sha-drift";
632
- } else if (!mergeableAccepted(observedPullRequest)) {
633
- admissionDecision = "rejected";
634
- admissionReason = "not-mergeable-on-admission-recheck";
635
- }
636
-
637
- entry.action = admissionDecision === "planned" ? "would-enqueue" : admissionDecision === "accepted" ? "enqueue" : "skip";
638
- entry.reason = admissionReason;
639
- entry.headSha = expectedHeadSha;
640
- entry.admissionReceipt = admissionReceipt({
641
- options,
642
- pr,
643
- expectedBaseSha: initialBaseSha,
644
- observedBaseSha,
645
- expectedHeadSha,
646
- observedHeadSha,
647
- decision: admissionDecision,
648
- reason: admissionReason,
649
- checks: decision.checks,
650
- approval: decision.approval,
651
- predecessor,
652
- });
653
-
654
- if (admissionDecision === "planned") {
655
- result.actions.push(entry);
656
- } else if (admissionDecision === "accepted") {
657
- if (!decision.pullRequestId) {
658
- entry.action = "skip";
659
- entry.reason = "missing-pull-request-node-id";
660
- entry.admissionReceipt.decision = "rejected";
661
- entry.admissionReceipt.reason = entry.reason;
662
- result.skipped.push(entry);
663
- } else {
664
- try {
665
- const queueEntry = await client.enqueuePullRequest({
666
- pullRequestId: decision.pullRequestId,
667
- expectedHeadOid: expectedHeadSha,
668
- });
669
- entry.action = "enqueued";
670
- entry.reason = "enqueued-with-expected-head";
671
- entry.queueEntry = queueEntry;
672
- entry.admissionReceipt.reason = entry.reason;
673
- result.actions.push(entry);
674
- result.enqueued.push(entry);
675
- } catch (error) {
676
- entry.action = "skip";
677
- entry.reason = "enqueue-rejected";
678
- entry.enqueueError = {
679
- status: error.status || null,
680
- message: error.message || "GitHub rejected merge queue admission",
681
- };
682
- entry.admissionReceipt.decision = "rejected";
683
- entry.admissionReceipt.reason = entry.reason;
684
- result.skipped.push(entry);
685
- }
686
- }
687
- } else {
688
- result.skipped.push(entry);
689
- }
690
-
691
- const activePredecessor = queuePredecessor(null, {
692
- queueEntryId: entry.queueEntry?.id || predecessor?.queueEntryId || "",
693
- pullRequestNumber: pr.number,
694
- headSha: expectedHeadSha,
695
- state: entry.queueEntry?.state || (admissionDecision === "planned" ? "ADMISSION_PLANNED" : "ADMISSION_REJECTED"),
696
- });
697
- blockRemainingPullRequests(result, orderedPullRequests, index + 1, options, observedBaseSha || initialBaseSha, activePredecessor);
698
- break;
361
+ result.skipped.push(entry);
699
362
  }
700
363
  }
701
364
 
@@ -709,10 +372,9 @@ export function renderMarkdownSummary(result) {
709
372
  "",
710
373
  `Repository: \`${result.repository}\``,
711
374
  `Target branch: \`${result.targetBranch}\``,
712
- `Landing mode: \`${result.landingMode}\``,
713
- `Execution: \`${result.dryRun ? "dry-run" : "apply"}\``,
375
+ `Mode: \`${result.dryRun ? "dry-run" : "merge"}\``,
714
376
  `Evaluated PRs: ${result.evaluated.length}`,
715
- `Actions ${result.dryRun ? "planned" : "taken"}: ${result.actions.length}`,
377
+ `Eligible ${result.dryRun ? "dry-run" : "merged"} PRs: ${result.merged.length}`,
716
378
  "",
717
379
  "| PR | Action | Reason | Head |",
718
380
  "| --- | --- | --- | --- |",
@@ -748,7 +410,6 @@ async function main() {
748
410
  sameRepositoryOnly: process.env.BUILDCHAIN_DEV_PR_SAME_REPOSITORY_ONLY,
749
411
  maxMerges: process.env.BUILDCHAIN_DEV_PR_MAX_MERGES,
750
412
  mergeMethod: process.env.BUILDCHAIN_DEV_PR_MERGE_METHOD,
751
- landingMode: process.env.BUILDCHAIN_DEV_PR_LANDING_MODE,
752
413
  dryRun: process.env.BUILDCHAIN_DEV_PR_DRY_RUN,
753
414
  outputPath: process.env.BUILDCHAIN_DEV_PR_OUTPUT_PATH,
754
415
  });
@@ -761,8 +422,6 @@ async function main() {
761
422
  writeGitHubOutputs({
762
423
  "evaluated-count": result.evaluated.length,
763
424
  "merged-count": result.merged.length,
764
- "enqueued-count": result.enqueued.length,
765
- "action-count": result.actions.length,
766
425
  "skipped-count": result.skipped.length,
767
426
  "final-base-sha": result.finalBaseSha,
768
427
  "result-path": options.outputPath,