merge-steward 0.9.3 → 0.9.5

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.
@@ -52,7 +52,7 @@ export async function reconcile(ctx) {
52
52
  });
53
53
  await cleanupSpec(ctx, entry);
54
54
  ctx.store.transition(entry.id, "preparing_head", {
55
- ...CLEAN_CI, ...CLEAN_SPEC,
55
+ ...CLEAN_CI, ...CLEAN_SPEC, retryAttempts: 0, lastFailedBaseSha: null,
56
56
  }, `stale dependency ${dep?.status ?? "removed"}`);
57
57
  continue;
58
58
  }
@@ -151,7 +151,7 @@ async function prepareEntry(ctx, entry, isHead, prevEntry) {
151
151
  // Gate: main CI must be green.
152
152
  if (ctx.ci.getMainStatus) {
153
153
  const mainStatus = await ctx.ci.getMainStatus(ctx.baseBranch);
154
- if (mainStatus === "fail") {
154
+ if (mainStatus !== "pass") {
155
155
  let mainChecks = [];
156
156
  try {
157
157
  mainChecks = await ctx.github.listChecksForRef(ref(ctx, ctx.baseBranch));
@@ -368,6 +368,15 @@ async function mergeHead(ctx, entry) {
368
368
  catch {
369
369
  // Can't verify — proceed and let push fail if needed.
370
370
  }
371
+ // Final main health check — don't push to a broken main.
372
+ if (ctx.ci.getMainStatus) {
373
+ const mainStatus = await ctx.ci.getMainStatus(ctx.baseBranch);
374
+ if (mainStatus !== "pass") {
375
+ emit(ctx, entry, "main_broken", { detail: "main unhealthy at merge time, re-preparing" });
376
+ ctx.store.transition(entry.id, "preparing_head", { ...CLEAN_CI, ...CLEAN_SPEC }, "main unhealthy at merge time");
377
+ return;
378
+ }
379
+ }
371
380
  // Push the spec branch to main (fast-forward).
372
381
  try {
373
382
  await ctx.git.push(entry.specBranch, false, ctx.baseBranch);
@@ -406,7 +415,7 @@ async function invalidateDownstream(ctx, allActive, afterIndex) {
406
415
  continue;
407
416
  emit(ctx, downstream, "invalidated", { detail: `base changed after position ${afterIndex}` });
408
417
  await cleanupSpec(ctx, downstream);
409
- ctx.store.transition(downstream.id, "preparing_head", { ...CLEAN_CI, ...CLEAN_SPEC }, "invalidated: base changed");
418
+ ctx.store.transition(downstream.id, "preparing_head", { ...CLEAN_CI, ...CLEAN_SPEC, retryAttempts: 0, lastFailedBaseSha: null }, "invalidated: base changed");
410
419
  }
411
420
  }
412
421
  async function cleanupSpec(ctx, entry) {
package/dist/service.js CHANGED
@@ -274,6 +274,7 @@ export class MergeStewardService {
274
274
  const entry = this.store.getEntryByPR(this.config.repoId, prNumber);
275
275
  if (entry) {
276
276
  this.store.transition(entry.id, "merged");
277
+ this.invalidateDownstreamOf(entry);
277
278
  this.logger.info({ prNumber, entryId: entry.id }, "External merge acknowledged");
278
279
  }
279
280
  }
@@ -290,9 +291,13 @@ export class MergeStewardService {
290
291
  continue;
291
292
  if (TERMINAL_STATUSES.includes(downstream.status))
292
293
  continue;
294
+ if (downstream.specBranch) {
295
+ this.specBuilder.deleteSpeculative(downstream.specBranch).catch(() => { });
296
+ }
293
297
  this.store.transition(downstream.id, "preparing_head", {
294
298
  ciRunId: null, ciRetries: 0,
295
299
  specBranch: null, specSha: null, specBasedOn: null,
300
+ retryAttempts: 0, lastFailedBaseSha: null,
296
301
  }, `invalidated: entry ${removedEntry.id.slice(0, 8)} dequeued`);
297
302
  invalidated++;
298
303
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "merge-steward",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "Serial merge queue for GitHub — rebase, CI-gate, and merge PRs one at a time",
5
5
  "type": "module",
6
6
  "repository": {