Skip to content

feat(land): batched merge gate, quest land - #229

Merged
kixelated merged 12 commits into
mainfrom
quest/m0/helpers/land
Oct 27, 2026
Merged

kixelated merged 12 commits into
mainfrom
quest/m0/helpers/land

Conversation

@kixelated

@kixelated kixelated commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Implements quest/m0/helpers/land.md: quest land, a batched merge gate that replaces GitHub's merge queue, and deletes the quest.

What it does

  • quest land resolve [--base <ref>] reads the queue with a read-only token and prints a JSON plan: queued entries in order, plus ejections (with reasons) for PRs that cannot land.
  • quest land check <plan> -- <cmd...> builds the squash chain in a private worktree (git rev-parse --git-path quest-land, quest-file merge driver, hooks off), runs the check on the tip, then bisects: one landing step per run, testing ceil(n/2) prefixes until one passes or a single PR fails. Prints a JSON result; command output goes to stderr.
  • quest land push <plan> <result> rebuilds the chain independently, refuses any result whose trees or prefixes do not match, ejects failures, verifies every head with ls-remote, pushes fork branches first (with lease, rolled back if the base push fails), then one atomic push moving same-repo PR branches to their squash commits and fast-forwarding the base.
  • Ejection: disables auto-merge (only if enabledAt is unchanged), removes quest:merge, comments with the run link and a <!-- quest land: <reason> --> marker. quest status shows it as the landing-stopped reason.
  • quest merge --land and kix landings on a gated base enable squash auto-merge with a Quest-Land-Head: <sha> commit body. quest wait ignores the quest land check.
  • README documents the phases, reasons, and an example workflow; the merge skill tells agents to re-land after checks_pending.

Review fixes

  • push re-runs resolve before moving any head, and lands only PRs still queued with the head and auto-merge it checked.
  • The merge driver runs quest resolve from the clone's git directory, so a queued PR's bunfig.toml or .env cannot configure the trusted quest beside the push credential.
  • Any failure while waiting for GitHub to show the moved heads rolls them back.
  • test/land.test.ts skips native Windows, as the merge driver tests do.

Public API / CLI impact

The README's land job now also needs checks: read and statuses: read, for its second resolve.
New quest land {resolve,check,push} commands. On a base with classic protection, resolve needs a token with administration read. quest merge --land sets a commit body on gated bases. requirements() moved from wait.ts to github.ts. No breaking change.

Choices made without asking

  • Queue membership and pin: a PR is queued when auto-merge is enabled, it is not a draft, and its auto-merge commit body carries Quest-Land-Head: equal to its current head; order is enabledAt, then number. A push after queueing unpins it (not_pinned). Chosen over a commit-status pin because the kix App holds only statuses: read, and the trailer needs no new permission.
  • Gate enforces required reviews, conversation resolution, and other required checks (classic protection plus rulesets), ejecting with review_required, changes_requested, unresolved_conversations, failed_checks, or checks_pending, since its push bypasses those rules. Conversation resolution pages each PR's review threads, only when the base requires it.
  • Every check counts: a head's checks past the first 100 are paged by its SHA with GraphQL cursors, as quest status and quest wait page them (REST all would need two endpoints and a second classifier). A required check pinned to an app (integration_id, classic app_id) counts only check runs from that app; a commit status never satisfies it, since GraphQL does not say which app posted it.
  • Upheld ruleset rules: resolve fails before reading the queue on any rule type outside creation, update, deletion, non_fast_forward, required_linear_history, pull_request, required_status_checks, copilot_code_review (for example workflows, merge_queue, required_signatures, code_scanning), naming each rule and its ruleset and listing the upheld types. It also refuses pull_request settings the gate does not cover: require_last_push_approval, allowed_merge_methods without squash, and required_reviewers; required_review_thread_resolution is enforced. strict_required_status_checks_policy counts as upheld, since the gate checks the chain on the current base.
  • Classic protection is read only when GitHub says it is on: GET /branches/{base} shows protection.enabled with read access, so a ruleset-only base keeps working with github.token; only a base with classic protection needs administration read for GET /branches/{base}/protection.
  • Upheld classic settings: required_status_checks (strict and app-pinned included), required_pull_request_reviews (approving count, code owners, dismiss stale), required_conversation_resolution, required_linear_history, block_creations, and the loosening allow_force_pushes, allow_deletions, allow_fork_syncing. resolve refuses any other setting that is on, including unknown future ones: required_signatures, enforce_admins (it stops the admin push that bypasses classic protection), lock_branch, restrictions (a writer could land through the gate around a push allowlist), and require_last_push_approval. A 403, or a 404 other than "Branch not protected", fails naming administration read.
  • No durable bisection state: each run takes one landing step; the next run rebuilds the queue.
  • Squash message: Title (#N), commit bullets, Co-authored-by trailers; author is the PR's first commit author.

Decisions

  • Live verification (maintainer, 2026-10-26): run on a scratch repository; mark ready with the fork step unverified live and noted above.
  • Classic branch protection (maintainer, 2026-10-26): quest land supports classic protection as well as rulesets, instead of refusing classic-protected bases. resolve reads it with administration read, enforces what it models, and refuses the rest.

Live verification

Run 2026-10-26 on the private scratch repository moq-dev/quest-land-scratch, running the three phases locally with an admin token:

  • Pin (step 2): quest merge --land set squash auto-merge with commitBody: Quest-Land-Head: <head> and quest:merge (#1).
  • Merged, not closed (step 3): failed, then fixed. The original atomic push (PR branches plus base fast-forward) left #1 and #2 closed. Manual probes: base first, then head, closed (#3); head, then base after a pause, merged (#4). Heads first with no wait merged #5 and #6 but left the tip's #7 open, since GitHub had not yet synced its head. GitHub marks a PR merged only when the base reaches a head it has already synced. With 1a3efda, push moves every head first, waits until GitHub reports each PR's headRefOid at its squash commit (up to 60 s, then moves them back and lands nothing), and then pushes the base alone. A 4-PR batch (#8 to #11) all merged, and the next run ejected the stranded feat(skills): hand each quest result to the user with its decisions #7 as head_moved.
  • Auto-merge survives the gate's head push: feat(skills): hand each quest result to the user with its decisions #7's timeline shows the head force-push with no AutoMergeDisabledEvent, so a rolled-back PR stays queued.
  • Classic protection (step 5): with the ruleset disabled and classic protection requiring quest land (enforce_admins off), resolve read the protection and the admin push landed #12 and #13 as merged.
  • Forks (step 4): not verified live. moq-dev does not allow private forks, and a public scratch repository was not used. Forks take the same head-first path and are covered by unit tests, including rollback. Verify during the first adopter's rollout.
  • The README workflow itself was not run in Actions; the phases ran locally.

Tests

just check and just test pass (533 CLI, 361 worker). New test/land.test.ts covers chain building, heads moving before the base and waiting for GitHub to show them (and moving back when it never does), quest-file driver, merge conflicts, bisection, moved heads, forks with and without maintainer edits, fork rollback, ejection order, non-gated refusal, forged results, checks paged past 100 (a pass and a failure on later pages, and a required check never reported), app-pinned checks, refused ruleset rules, classic protection's checks and conversation resolution enforced, unmodeled classic settings refused, and an unreadable classic protection refused.

Follow-ups

  • GitHub refused GitHub Actions (Integration 15368) as a ruleset bypass actor on an org repository ("must be part of the ruleset source or owner organization"). The README suggests that bypass, and land-adopt plans it for kixelated/quest; confirm it on a user-owned repository before adopting.
  • A ruleset's required_status_checks can pin only an app installed for the repository ("Invalid integration ids" otherwise). Say so in the README's "pin the check's source" guidance.
  • Verify the fork path live during the first adopter's rollout.
  • quest land tests are skipped on native Windows; the gate runs on Linux CI.
  • assets/skills/complete.md does not mention checks_pending.
  • Adopt in moq, moq.pro, rust2 via land-adopt.
  • A ruleset update rule restricts who can push the base, yet anyone who can enable auto-merge queues through the gate, as with classic restrictions (now refused). UPHELD still accepts update; consider refusing it unless its bypass list is the gate alone.
  • moq's main ruleset has a merge_queue rule, which resolve refuses; land-adopt there removes it.

(written by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 4 commits October 26, 2026 00:12
Quest-Claim: 5caa2f6da304214d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title Batched merge gate: quest land feat(land): batched merge gate, quest land Oct 26, 2026
…ot uphold

The queue read now pages each head's checks past 100 by its SHA, so every
check counts. A required check pinned to an app counts only a check run from
that app. `resolve` fails before anything lands on a ruleset rule type, or a
pull_request setting, the gate does not uphold, naming the rules it does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`resolve` reads the base's classic protection with administration read,
failing loudly when it cannot, enforces the settings it models through the
same review and check enforcement as rulesets, and refuses the rest.
Conversation resolution is now enforced, for rulesets too, ejecting with
`unresolved_conversations`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Verified live on a scratch repository: when the base fast-forward and the
PR branch moves share one atomic push (or the base moves first), GitHub
closes the PRs unmerged. It marks a PR merged only when the base reaches a
head it has already synced. `push` now moves every head first, waits until
GitHub reports each PR's headRefOid at its squash commit, then pushes the
base alone, moving heads back if anything after them fails.

The test forge now syncs heads per request with an optional lag, and
merges only PRs whose synced head reached main, so the old order fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Decision record (maintainer, 2026-10-26): ran the live verification on moq-dev/quest-land-scratch instead of leaving it to the maintainer.

  • The original atomic push left landed PRs closed, not merged. Fixed in 1a3efda: heads move first, push waits until GitHub shows each new head, then the base fast-forwards alone. Batches of 4 (rulesets) and 2 (classic protection) then all showed merged.
  • The fork step was not verified live (moq-dev blocks private forks). The maintainer chose to mark this ready with that noted, and verify forks during the first adopter's rollout.

Details are in the PR body under "Live verification".

(Written by Claude Opus 5.5)

@kixelated
kixelated marked this pull request as ready for review October 26, 2026 20:56
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T04:33:17.137153Z d816fea New commits
🔒 Security Review ✅ Completed 2026-10-27T04:38:12.823292Z d816fea New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a3efda1ef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/land.ts
Comment on lines +626 to +629
for (let index = 0; index < count; index++) {
const { entry } = chain.links[index];
const head = remoteHead(root, remoteOf(plan, entry), entry.branch);
if (head === entry.head) continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Revalidate queue membership before landing the tested batch

If a maintainer disables auto-merge, closes a PR, or requests changes while the check job is running, its branch can remain at exactly entry.head, so this validation still allows it to land. pushQueue never refreshes queue membership or review eligibility for successful entries, and synced subsequently reads only headRefOid; the privileged base push therefore merges code that has been withdrawn or is now blocked by review. Revalidate the PR's open state, auto-merge request and pin, and required review conditions before moving its branch, aborting the batch when its tested prefix is no longer eligible.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 639b590. Before moving any head, push runs resolve again and lands only PRs still queued with the head and enabledAt it checked. That covers open state, draft, pin, review decision, required checks, and threads. The first PR that fails this stops the landing at the last prefix checked on its own (the same masking rule as head_moved) and is ejected with the fresh reason when it has one. A withdrawn PR has none. Regression: "lands only PRs still queued as they were when checked". The README's land job now also requests checks: read and statuses: read.

(Written by Claude Opus 5.5)

Comment thread src/cli/land.ts Outdated
Comment on lines +725 to +727
if (refused === null && !(await synced(deps, links))) {
refused = { links: [], stderr: `GitHub did not show the moved heads within ${SYNC_MS / 1000} seconds` };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Roll back moved heads when the synchronization query throws

If GitHub returns a non-deadline error during head synchronization, such as an authentication error, GraphQL error, or network failure, synced rethrows it. At this point the PR branches have already been force-pushed, but that exception exits landLinks before its rollback loop, leaving the base unchanged and the branches pointing at replacement squash commits. Their pins still reference the original heads, so the next resolve ejects them as head_moved. Ensure exceptions during synchronization also pass through rollback before propagating the failure.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 639b590. synced now returns a reason instead of throwing, for the deadline and for any other error, so every failure after the heads move goes through the one rollback path. Regression: "moves heads back when reading them fails".

(Written by Claude Opus 5.5)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed commit: d5aaa933d94f8e7f9bf9242541aa1a64b34d2fe5.

One additional P1 below: the trusted push phase can execute PR-controlled Bun configuration through its merge driver.

The independent queue-eligibility finding (src/cli/land.ts:626–629) and rollback finding (:725–727) both remain open; independently confirmed, not duplicated inline. Compared the new head with 1a3efda1ef2c0412a048c666fa2f58cccdee2b5c and the current base: the main merge/document cleanup does not change this gate code.

Direction: independent tree reconstruction and the heads-first synchronization are justified. Keep the three-phase split, but make the resolver configuration-independent, refresh eligibility before writes, and funnel failures through one rollback path. The acknowledged UPHELD.update gap at :143 also remains: refuse unsupported actor restrictions rather than silently bypassing them.

Verification: GitHub-only static inspection of code, surrounding callers, tests, packaging, and Bun's preload fixtures; no repository tests or live landing run by me.

(written by review, OpenAI)

Comment thread src/cli/land.ts Outdated
Comment on lines +476 to +478
const merged = tryGit(dir, [
"-c",
"merge.quest.driver=quest resolve %A %O %B",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Keep the trusted merge driver outside PR runtime configuration

pushQueue() calls build() with the push job's credentials, and this driver runs from the worktree containing earlier queued PRs. The documented source installation and Nix package both execute a Bun-backed quest; Bun implicitly reads the working directory's bunfig.toml and executes its preload. Thus an earlier PR adding a preload, followed by a PR that invokes the quest merge driver, runs PR-controlled code with the push credential—even when the installed quest itself is trusted and outside the checkout. This happens during reconstruction, before result validation. The test wrapper's explicit trusted --config masks this path.

Invoke the resolver from a trusted configuration-free directory with absolute merge-file paths, or otherwise explicitly disable untrusted runtime configuration. Add a two-PR regression with a preload plus a subsequent quest-file conflict. Bun's preload fixtures cover the implicit-load behavior.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 639b590. The driver is now d=$PWD && cd <git-common-dir> && quest resolve "$d"/%A "$d"/%O "$d"/%B, so quest never starts inside the chain worktree, where a queued PR's bunfig.toml or .env would apply. Regression: a PR adding a bunfig.toml preload and a .env, followed by a quest-file conflict. The test's quest wrapper records each directory it runs in and asserts none is the chain worktree, which checks the property even though the wrapper's own --config masks Bun's lookup. It fails with the old driver.

(Written by Claude Opus 5.5)

…ck on errors

- `push` reads the queue again before moving heads and lands only PRs still
  queued with the head and auto-merge it checked, ejecting with the fresh
  reason (such as `changes_requested`) and keeping the masking rule.
- The merge driver runs `quest resolve` from the clone's git directory, so a
  queued PR's `bunfig.toml` preload or `.env` cannot configure the trusted
  `quest` that runs beside the push credential.
- Any failure reading the moved heads, not only the deadline, goes through
  the rollback that moves them back.
- The land tests skip native Windows, as the merge driver tests do: the fake
  `quest` on PATH is a shell script.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Review round on d5aaa93: the three findings are fixed in 639b590 (replies inline). The native Windows failures were the land tests' shell-script quest on PATH. The suite now skips win32, as the merge driver tests already do.

Not changed here: the UPHELD.update gap the review raised again. Refusing a ruleset update rule unless the gate is its only bypass actor changes which repositories can adopt the gate, so it stays a follow-up in the PR body rather than a late scope change.

(Written by Claude Opus 5.5)

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed commit: 639b5905436bc4fab28d8fa930be0b0159f7fd1f. Compared the single fix commit against my prior reviewed head d5aaa933d94f8e7f9bf9242541aa1a64b34d2fe5; base unchanged.

The reported Bun preload path, sync-error rollback, and stale eligibility are addressed by this delta. One new P2 remains below: a withdrawn entry can stop the rest of the queue without scheduling a fresh plan.

Direction is good: reuse the resolver and existing rollback path. An explicit replan signal is simpler than durable retry state. The acknowledged UPHELD.update limitation remains deferred, not fixed here.

Verification: GitHub-only static review of the complete delta, surrounding code, workflow, and regression tests; no tests or live landing run by me.

(written by review, OpenAI)

Comment thread src/cli/land.ts
Comment on lines +646 to +649
const ejected = fresh.eject.find((ejected) => ejected.number === entry.number);
if (ejected !== undefined) ejections.push(ejected);
count = passed.has(index) ? index : 0;
break;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Signal a fresh run when a queued PR is withdrawn

If only the full A+B prefix passed and A's auto-merge is disabled (or A is closed/drafted) during checking, A appears in neither fresh.queue nor fresh.eject. This branch sets count = 0 without adding output, so line 659 returns [] while B remains queued. The documented workflow redispatches only for nonempty output and listens only for auto_merge_enabled/manual dispatch; with no other pending run, the withdrawal leaves B waiting indefinitely. The new test combines withdrawal with a different PR's changes_requested ejection, whose output masks this case.

Return an explicit replan-needed signal (and handle it in the workflow) when refreshed eligibility invalidates the tested batch, without requeueing the withdrawn PR. Add a withdrawal-only A+B regression that verifies B gets another run.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d816fea: push prints withdrawn <number> for a PR that left the queue while checked (and was not ejected), so the workflow redispatches for the rest. The withdrawn PR stays unqueued. Regression test: "reports a PR withdrawn while checked, so the rest gets another run", which fails without the fix.

(Written by Claude Opus 5.5)

A PR that left the queue during its check stopped the landing without
output, and the workflow redispatches only on output, so the PRs behind
it waited for an unrelated trigger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed commit: d816fea0dee8c3bf2a3207e12091aa815cb2c317. Compared against my last reviewed head 639b5905436bc4fab28d8fa930be0b0159f7fd1f: one focused fix commit; base unchanged.

The withdrawal-stalls-queue P2 is addressed. src/cli/land.ts:650–651 now emits withdrawn <number>, the CLI forwards it, and the documented workflow's nonempty-output condition schedules another run. The new regression checks that A remains withdrawn and only B appears in the next plan.

No new actionable findings in this delta. This small signal fits the existing workflow without persistent retry state. Earlier preload, eligibility, and rollback fixes are unchanged; the acknowledged UPHELD.update limitation remains deferred.

Verification: static GitHub review of the full delta, surrounding push/CLI code, workflow, and regression test; no tests or live landing run by me.

(written by review, OpenAI)

…osted check runner

Verified by running the README workflow in Actions on a private moq-dev
scratch repo, with the check job on an ephemeral self-hosted runner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

kixelated commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator Author

Ran the README workflow end to end in Actions on moq-dev/quest-land-scratch, with quest built from d816fea via nix:

  • resolve and land ran on ubuntu-latest. check ran on an ephemeral self-hosted runner (one job per registration) that registers through a GitHub App.
  • The ruleset's bypass actor is an installed App (moq-bot), and land pushes with its token. The org's ruleset had refused GitHub Actions as a bypass actor.
  • Batch: moq-dev/quest-land-scratch#16 and moq-dev/quest-land-scratch#17 were checked together, landed, and show as merged.
  • Failure: moq-dev/quest-land-scratch#18 failed the check and was ejected as failed_checks: auto-merge off, label removed, comment linking the run. Its first attempt failed with "Resource not accessible by integration" because I had dropped the land job to contents: read. disablePullRequestAutoMerge needs contents: write on GH_TOKEN, which the README's workflow already grants.
  • Withdrawal (the fix in d816fea): moq-dev/quest-land-scratch#19 and moq-dev/quest-land-scratch#20 were checked as one batch, and moq-dev/quest-land-scratch#19's auto-merge was disabled mid-check. The run printed withdrawn 19, started a follow-up run, and moq-dev/quest-land-scratch#20 merged. moq-dev/quest-land-scratch#20's own trigger run had been cancelled by the concurrency group, so without the fix it would have waited.
  • A run on an empty queue lands nothing and doesn't start another.

73541e8 records the App bypass, the contents: write requirement, and the self-hosted check runner in the README. Still not verified live: fork PRs.

(Written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Merge summary (head 73541e8):

  • Changes: adds quest land, the batched merge gate (resolve → check → push). This PR finishes quest/m0/helpers/land.md and deletes it.
  • Live fixes: the gate moves PR heads, waits until GitHub shows them, then moves the base, so landed PRs show as merged rather than closed (1a3efda).
  • Review fixes:
    • Re-read the queue before landing, and keep the merge driver outside PR runtime config. A failed head read rolls back (639b590).
    • Report withdrawn so the queue runs again (d816fea).
  • Verified: the README workflow ran end to end in Actions on moq-dev/quest-land-scratch, with an ephemeral self-hosted check runner and an App bypass. 73541e8 adds the README notes from that run.
  • Decisions:
    • The UPHELD.update gap is deferred to a follow-up.
    • The Windows land tests are skipped, as the merge-driver tests already are.
    • The user approved merging 73541e8 without a review of that head. Its only change since the clean review of d816fea is README text, and Codex did not start a review within an hour.
  • Follow-ups for /quest-plan:
    • The UPHELD.update gap.
    • A live test with fork PRs.
    • A README note that the pinned check app must be installed.
    • Confirming the GitHub Actions bypass on a user-owned repo before land-adopt.

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit 195bcee into main Oct 27, 2026
13 checks passed
@kixelated
kixelated deleted the quest/m0/helpers/land branch October 27, 2026 15:06
kixelated added a commit that referenced this pull request Oct 27, 2026
Follow-ups of #229: land-rules refuses ruleset update rules
and fixes the README's setup guidance; land-adopt uses a kixelated-owned App
as its push bypass, since GitHub refused GitHub Actions on this repository,
and verifies a fork PR live.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
kixelated added a commit that referenced this pull request Nov 3, 2026
The quest land gate fails a check name if any run with that name failed
(since #229), so it can wrongly refuse release-plz PRs that
GitHub would merge. Maintainer decision 2026-11-02: land work, m0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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