Skip to content

fix(land): re-pin a PR that is already auto-merging - #306

Merged
moq-bot[bot] merged 3 commits into
mainfrom
fix/land-repin
Oct 31, 2026
Merged

moq-bot[bot] merged 3 commits into
mainfrom
fix/land-repin

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Found during moq's cutover. Re-landing moq-dev/moq#7427 after a push left its pin on the old head: quest merge --land reported success, but the auto-merge commit body still named the previous SHA. GitHub keeps an existing auto-merge request, and its commit body, when asked to enable it again. The gate would then have ejected the PR as head_moved.

Change

A new enqueue() in land.ts is shared by quest merge --land and the fixer's requeue:

  • it reads the existing request;
  • it returns if the request already pins this head;
  • otherwise it disables the request first, then enables squash auto-merge pinned to the head.

An empty or missing commit body counts as no pin.

Checks

  • just check and just test pass (706 CLI and 426 Worker tests).
  • New tests:
    • re-pinning a PR already auto-merging on an older head (the fake now keeps an existing request, as GitHub does);
    • replacing a request with an empty commit body.

Review

  • Reviewer: Codex gpt-6.1-sol, medium, quest review --base origin/main, round 2
  • Head: f2cdd74
  • Findings: none. Round 1 found that an empty commit body threw; fixed.

🤖 Generated with Claude Code

(written by Claude Opus 5.5)

kixelated and others added 2 commits October 30, 2026 19:52
GitHub keeps an existing auto-merge request, and its commit body, when asked to
enable it again, so re-landing a queued PR after a push left the old head
pinned and the gate ejected it as head_moved. Queueing now disables a request
pinned to another head first; the fixer's requeue shares the same path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated enabled auto-merge (squash) October 31, 2026 03:00
@coderabbitai

coderabbitai Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ff51f95d-de46-4f83-afb3-0fbffd095629

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 46ac5b42-648e-4aa4-a37d-1f21db1a63b1
Commits

Reviewing files that changed from the base of the PR and between b1f0c40 and f2cdd74.

Files selected for processing: 4
  • src/cli/hold.ts
  • src/cli/land-fixer.ts
  • src/cli/land.ts
  • test/hold.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The landing flow now uses enqueue to reconcile a pull request’s auto-merge request with its current head SHA. If the existing request does not pin that head, enqueue disables it and enables squash auto-merge with the current head pinned. The gated landing path and land fixer both call enqueue. Tests cover existing requests with an older pinned head or an empty commit body.

Merge Risk: Low · previous review f2cdd

A concurrent auto-merge change could leave a PR unqueued until someone re-enqueues it. The narrow race is a bounded merge risk.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: re-pinning a pull request that is already auto-merging.
Description check Passed The description directly explains the stale auto-merge pin problem, the new enqueue flow, and the tests for the change.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 2 functions across 4 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@moq-bot
moq-bot Bot disabled auto-merge October 31, 2026 03:03
@moq-bot moq-bot Bot removed the quest:merge label Oct 31, 2026
@moq-bot

moq-bot Bot commented Oct 31, 2026

Copy link
Copy Markdown

quest land removed this pull request from the queue: the batch check failed (exit 1).

     �[33m�[2m✓�[22m�[39m a base push before merge 2 stops the batch without ejecting or reporting tree mismatch�[33m 1189�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m a tree mismatch logs CRITICAL, keeps landed PRs, and ejects only the unmerged batch suffix�[33m 1206�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m scopes each merge token to that PR's squash, preserving it through the queue token cache�[33m 1454�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m a lost merge response never re-merges or ejects the merged PR after restart�[33m 1502�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m a restart resolves only PRs left after successful API merges�[33m 2090�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m resumes all pending ejections after a restart during a failing batch�[33m 460�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m requests workflow write only when the selected batch touches workflows (false)�[33m 607�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m requests workflow write only when the selected batch touches workflows (true)�[33m 721�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m fails a batch whose check outlives the timeout�[33m 598�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m bounds a base conflict's comment however many paths conflict�[33m 651�[2mms�[22m�[39m
   �[32m✓�[39m asynchronous land fixer �[2m(36)�[22m
     �[33m�[2m✓�[22m�[39m requeues unchanged, suggests follow-up, and hands back a second ejection in the same pick�[33m 2300�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m collects a fixed conflict after a restart, pushes and pins the new Review head�[33m 1776�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back handback without requeueing�[33m 1282�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back protected file without requeueing�[33m 1283�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back shell.nix without requeueing�[33m 1392�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back default.nix without requeueing�[33m 1728�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back .gitmodules without requeueing�[33m 1440�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back fork without requeueing�[33m 860�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back crash without requeueing�[33m 546�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back invalid result without requeueing�[33m 447�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back symlink result without requeueing�[33m 455�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m times out a running fixer without waiting for it�[33m 503�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m passes no host credentials to wrapper or agent, including inherited Git configuration�[33m 559�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back a job after losing the enable-auto-merge response�[33m 583�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m preserves a refused-push handback across a failed comment and restart�[33m 888�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back deleted branch and keeps collection usable�[33m 624�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back gitfile and keeps collection usable�[33m 663�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back symlink and keeps collection usable�[33m 622�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back corrupt objects and keeps collection usable�[33m 1386�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m hands back oversized and keeps collection usable�[33m 1587�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m does not import private objects through alternates�[33m 999�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m ignores an alternate store when the fixed objects are self-contained�[33m 803�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m collects an already finished job after an extended daemon outage�[33m 528�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m only deduplicates the bot's own comments�[33m 1019�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m rereads the author's body immediately before recording the fix�[33m 1178�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m rejects mixing individually approved protected files�[33m 466�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m rejects new gitlinks anywhere in the tree�[33m 401�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m rebuilds pack indexes and strictly validates packs (corrupt pack: false)�[33m 1728�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m rebuilds pack indexes and strictly validates packs (corrupt pack: true)�[33m 2247�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m uses the recorded agent and leaves the author's attribution alone�[33m 2218�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m compares protected paths at the new head against the pin and current base�[33m 646�[2mms�[22m�[39m

�[31m⎯⎯⎯⎯⎯⎯⎯�[39m�[1m�[41m Failed Tests 2 �[49m�[22m�[31m⎯⎯⎯⎯⎯⎯⎯�[39m

�[41m�[1m FAIL �[22m�[49m test/agent.test.ts�[2m > �[22mstart runs claude with every launch setting, one resolved key, and no key in its output
�[31m�[1mAssertionError�[22m: expected { …(3) } to deeply equal { …(3) }�[39m

�[32m- Expected�[39m
�[31m+ Received�[39m

�[2m  {�[22m
�[2m    "ANTHROPIC_API_KEY": "resolved-second",�[22m
�[2m    "CODEX_HOME": "/tmp/nix-shell.H5SJVU/quest-test-3IrF0V/codex",�[22m
�[32m-   "QUEST_WORKTREE_HOME": undefined,�[39m
�[31m+   "QUEST_LAND_BASE": "b1f0c40e6733d2799ba35f47b88c2456b2bfc1b6",�[39m
�[2m  }�[22m

�[36m �[2m❯�[22m test/agent.test.ts:�[2m295:19�[22m�[39m
    �[90m293|�[39m  );
    �[90m294|�[39m  // Only its own key: no session credentials, harness markers, other k…
    �[90m295|�[39m  expect(call.env).toEqual({
    �[90m   |�[39m                   �[31m^�[39m
    �[90m296|�[39m   ANTHROPIC_API_KEY: "resolved-second",
    �[90m297|�[39m   CODEX_HOME: process.env.CODEX_HOME,

�[31m�[2m⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯�[22m�[39m

�[41m�[1m FAIL �[22m�[49m test/agent.test.ts�[2m > �[22mcodex runs with its key variable and reports its thread and last message
�[31m�[1mAssertionError�[22m: expected { …(4) } to deeply equal { CODEX_API_KEY: 'sk-literal', …(2) }�[39m

�[32m- Expected�[39m
�[31m+ Received�[39m

�[2m  {�[22m
�[2m    "CODEX_API_KEY": "sk-literal",�[22m
�[2m    "CODEX_HOME": "/tmp/nix-shell.H5SJVU/quest-test-bria1I/codex",�[22m
�[31m+   "QUEST_LAND_BASE": "b1f0c40e6733d2799ba35f47b88c2456b2bfc1b6",�[39m
�[2m    "QUEST_WORKTREE_HOME": "/tmp/nix-shell.H5SJVU/quest-test-bria1I/runtime/worktrees",�[22m
�[2m  }�[22m

�[36m �[2m❯�[22m test/agent.test.ts:�[2m385:27�[22m�[39m
    �[90m383|�[39m   "-",
    �[90m384|�[39m  ]);
    �[90m385|�[39m  expect(s.calls()[0].env).toEqual({
    �[90m   |�[39m                           �[31m^�[39m
    �[90m386|�[39m   CODEX_API_KEY: "sk-literal",
    �[90m387|�[39m   CODEX_HOME: process.env.CODEX_HOME,

�[31m�[2m⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯�[22m�[39m


�[2m Test Files �[22m �[1m�[31m1 failed�[39m�[22m�[2m | �[22m�[1m�[32m22 passed�[39m�[22m�[90m (23)�[39m
�[2m      Tests �[22m �[1m�[31m2 failed�[39m�[22m�[2m | �[22m�[1m�[32m705 passed�[39m�[22m�[2m | �[22m�[33m2 skipped�[39m�[90m (709)�[39m
�[2m   Start at �[22m 20:01:24
�[2m   Duration �[22m 101.46s�[2m (tests 74%, transform 17%, import 9%)�[22m

error: script "test" exited with code 1
error: recipe `_test` failed on line 33 with exit code 1
error: recipe `test` failed on line 30 with exit code 1

.

@moq-bot

moq-bot Bot commented Oct 31, 2026

Copy link
Copy Markdown

Codex land fixer: fixer disappeared before recording an exit status; run /quest-iterate. Recommendation: run /quest-iterate before picking it again.

(written by Codex)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant