@link-assistant/hive-mind 2.15.0 → 2.15.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.
@@ -159,12 +159,21 @@ export async function startWorkSession({ isContinueMode, prNumber, argv, log, fo
159
159
  }
160
160
 
161
161
  export async function endWorkSession({ isContinueMode, prNumber, argv, log, formatAligned, $, logsAttached = false }) {
162
- // Post end work session comment and convert PR back to ready if in continue mode.
162
+ // Post end work session comment and convert PR back to ready for review.
163
+ //
163
164
  // Issue #2123: the ready conversion mirrors startWorkSession's draft conversion, so it must
164
165
  // run for every continue-mode session, not only for --watch/--auto-continue ones.
165
- if (isContinueMode && prNumber) {
166
+ //
167
+ // Issue #2182: the `isContinueMode` gate is deliberately gone from the ready conversion.
168
+ // A session that created the pull request itself (isContinueMode === false) can still leave
169
+ // it in draft — either because the AI opened it as a draft, or because an auto-restart
170
+ // iteration converted it — and hive-mind, not the AI, owns putting it back. In the reported
171
+ // run isContinueMode was false for the whole process, so this function did nothing and the
172
+ // PR stayed a draft while --auto-merge retried the merge 2692 times over 4d 12h.
173
+ // Session *comments* stay gated: they are about --watch/--auto-continue reporting, not state.
174
+ if (prNumber) {
166
175
  const workEndTime = new Date();
167
- const shouldPostSessionComment = argv.watch || argv.autoContinue;
176
+ const shouldPostSessionComment = isContinueMode && (argv.watch || argv.autoContinue);
168
177
  await log(`\n${formatAligned('🏁', 'Ending work session:', workEndTime.toISOString())}`);
169
178
 
170
179
  // Only post end comment if logs were NOT already attached
@@ -194,7 +203,9 @@ export async function endWorkSession({ isContinueMode, prNumber, argv, log, form
194
203
  await log(formatAligned('ℹ️', 'Skipping:', 'End comment (logs already attached with session end message)', 2));
195
204
  }
196
205
 
197
- // Convert PR back to ready for review (issue #2123: shared implementation)
206
+ // Convert PR back to ready for review (issue #2123: shared implementation).
207
+ // This is the invariant of the whole draft/ready state machine: a finished working
208
+ // session never leaves a pull request in draft (issue #2182).
198
209
  const { reportError } = await import('./sentry.lib.mjs');
199
210
  await ensurePullRequestIsReady({
200
211
  owner: global.owner,
@@ -460,7 +460,9 @@ export class MergeQueueProcessor {
460
460
  }
461
461
  const waitForReadyResult = await this.waitForPRReady(item, mergeableCheck);
462
462
  if (!waitForReadyResult.success) {
463
- if (waitForReadyResult.status === 'cancelled' || waitForReadyResult.status === 'conflict') {
463
+ // Issue #2182: 'draft' joins cancel/conflict as a skip, not a failure —
464
+ // the pull request author has to mark it ready for review first.
465
+ if (waitForReadyResult.status === 'cancelled' || waitForReadyResult.status === 'conflict' || waitForReadyResult.status === 'draft') {
464
466
  item.status = MergeItemStatus.SKIPPED;
465
467
  item.error = waitForReadyResult.error;
466
468
  this.stats.skipped++;
@@ -45,6 +45,13 @@ export async function waitForPRReady(processor, item, initialCheck, options) {
45
45
  return { success: true, status: 'ready', error: null };
46
46
  }
47
47
 
48
+ // Issue #2182: a draft pull request never becomes mergeable on its own —
49
+ // waiting for the full CI timeout only delays the queue. Skip it right away
50
+ // with the real reason instead of a generic "did not become mergeable".
51
+ if (latestCheck?.isDraft) {
52
+ return { success: false, status: 'draft', error: `PR #${item.pr.number} is a draft — mark it ready for review before merging` };
53
+ }
54
+
48
55
  if (latestCheck?.reason === conflictSkipReason) {
49
56
  return { success: false, status: 'conflict', error: conflictSkipReason };
50
57
  }