Repository navigation
Land picked PRs through merge jobs - #209
Conversation
Quest-Claim: 32e768096e37aab7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge jobs hold their PR with quest:merge, publish conflict resolutions as merge commits, land through auto-merge bound to the published head, and release the label when the job ends or the landing ends unmerged. The runner gains a wait tool, and the inbox offers merge on unclean PRs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
Security findingsAdvisory findings (1)ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5326944d4f
ℹ️ 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".
- Give Claude's kix MCP server a tool timeout past WAIT_LIMIT, so a merge job's `wait` outlasts the 60s HTTP first-response limit. - Refuse landing while the project is paused, like delivery. - Check a merge commit's workflows against the head it updates instead of refusing merge-relative changes that only keep the PR's own workflow edits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the overlap with planning (#205) and agent activity (#215), keeping every feature: - Renumber 0018_merge_jobs.sql to 0019, after 0018_plan_request.sql, and carry its new idea and context columns through the jobs rebuild. - createJob inserts both the plan request and continues a pull request's head for merge jobs as well as iterate. - The runner's context tool covers plan requests and merge jobs; deliver records both a landing and suggested follow-ups. - Agent cards name merge jobs "Merging". - Drop the finished activity, planning, and merge-job quests from the kix epic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3101b7ebf9
ℹ️ 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".
Take main's shared adapter environment and stop grace for the Claude adapter, keeping the kix MCP tool timeout for wait; keep both the wait and opencode runner tests; drop the finished opencode quest from the kix epic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A stopped attempt whose runner never reports a result is cancelled by a lease expiring in dispatch, or by a status that omits it. Neither path ran the merge hold release, so quest:merge stayed on the pull request until its next webhook. Both now return the merge jobs they cancelled while holding a pull request, and the runner hub releases those holds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dbe5471fe8
ℹ️ 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".
Main's publication tests now run against the shared GitHub fake in cloudflare/dev/github.ts (#213). Port what merge jobs need into it: pull request labels and the timeline that added them, the hold query, the auto-merge and merge mutations, comparing a commit with main, and a commit's .github/workflows tree. The merge job publication tests now drive that fake, and pulled jobs' read tokens keep contents: read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A runner status could dispatch a new merge job while kix was still adding its quest:merge label, so the attempt could run, or even end, before the job recorded its hold. Dispatch now skips merge jobs without a hold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kix reads only the latest 50 reviews and 100 comments, so past those a new entry replaces an old one and counts stay the same. wait now fingerprints the entries themselves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep main's merged flag on the fake's pull requests (#217) for both its REST merge and the merge mutation, and document both meanings of a listed upstream branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cb95a27025
ℹ️ 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".
A stop that landed while kix was adding quest:merge cancelled a job with no hold, so its release did nothing, and the hold recorded afterwards kept the label on. store.held now records a hold only on a queued job, and the pick releases the label at once when the job was stopped first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Design checkpoint after four review rounds: the maintainer chose to continue for one more round. The approach stays as it is. Each recent finding was a missed path where a merge job's hold should be released, not a flaw in the approach. This round fixes the remaining race, where a stop arrives while the pick is taking the pull request (4f42425). It also merges main (#208). If Codex finds nothing new on the new head, the PR lands as a squash merge. (Written by Claude Opus 5.5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 23d2022524
ℹ️ 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".
A publication's changes carry no submodules, and a merge commit's tree builds on the merged-in commit. A pull request's own submodule bump then fell out of the merge, and the published commit reverted it. The runner now lists those submodules with a merge publication, and kix refuses it on a decision instead of publishing the revert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep #212's sandbox alongside merge jobs: both imports in the Claude adapter and the attempt, main's systemPrompt with quest merge among the skipped hold commands and the CONFLICTS instruction after it, the fake Claude logging the sandbox settings and the kix tool timeout, and both launch assertions. Take main's sandbox quest line; the merge-job quest stays deleted. Drop an unused binding from the submodule snapshot test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Landed 6213cb4 as a squash merge (the repository allows only squash merges). What changed during /quest-merge:
Maintainer decisions:
Checks on 6213cb4:
Follow-ups to plan:
(Written by Claude Opus 5.5) |
There was a problem hiding this comment.
🛡️ Codex Security Review · Automatically triggered
Here are some automated security review suggestions for this pull request.
Reviewed commit: 6213cb40b7
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
| const land: Land | undefined = job.workflow === "merge" ? (method ? { method } : {}) : undefined; | ||
| const followups = followupsOf(args.followups); | ||
| const published = await this.#publish( | ||
| typeof args.message === "string" && args.message.trim() ? args.message : subject(text), | ||
| pull, | ||
| { pull, land }, |
There was a problem hiding this comment.
🛡️ Codex Security Review · Automatically triggered
Security: Require approval after the merge agent changes the head
When a maintainer starts a merge job on an upstream/service-fork PR, the agent consumes attacker-controlled PR discussion and head contents, including repository instruction files. This new path turns any deliver call into land without showing the resulting diff to the maintainer. A lower-privileged writer or commenter can therefore use prompt injection to make the App merge post-click changes the maintainer never reviewed. Hold/head checks validate those new changes rather than stopping them, and GitHub rules help only when they require an effective fresh review. Require explicit approval of the final head/diff before landing.
Useful? React with 👍 / 👎.
Picking merge on a pull request that is not clean now starts a
mergejob on one of the maintainer's runners. The job runs the merge skill's loop (fix conflicts, failing checks, and review findings), then lands the PR through GitHub. It holds the PR with the samequest:mergelabel that localquest mergesessions use. This finishes and deletesquest/m0/kix/merge-job.md.Changes
Workflow.
mergejoinsplan,start,iterate, andbootstrap. Migration0019_merge_jobs.sqlrebuildsjobsfor the new CHECK value (keepingideaandcontextfrom0018_plan_request.sql) and adds aholdcolumn. Dependent rows are kept, which I checked against a populated database with foreign keys on. Likeiterate, a merge job works on the PR's own head, is untrusted, and is refused on drafts.Hold (label contract). When a merge job is created, the bot adds
quest:mergeand stores the timeline event ID that added it as the job'shold. The pick is refused while anotherquest:*label holds the PR, and the refused job is cancelled. When two sessions label at the same moment, the earlier label wins, as inquest merge. A spentquest:merge(a landing that already ended unmerged) is removed first.Release. The bot removes its own label when:
pull_requestwebhook arrives and GitHub shows the landing ended unmerged.Either way GitHub is reread first; the webhook body is never trusted. The PR then shows as an idle decision item again (
src/jobs/landing.ts).Landing. A runner publication can now carry
land: { method? }. kix checks that the project is enabled, the published head, that the job still holds its label, and that the PR is open. It then enables auto-merge bound to that head, which enters the merge queue when there is one. If GitHub would refuse auto-merge because nothing is left to wait for, kix merges right away. The method is the one the job names, or else the first the repository allows. A refusal, a moved head, or a removed label pauses the job on a publication decision.Merge commits. Conflicts need a real merge commit, so a publication can name
merge: <sha>. kix accepts it only if the PR's upstream base branch already contains that commit. It then writes a two-parent commit, with changes applied relative to the merged commit's tree. On the runner, the workspace detects the agent'sgit mergeand sends only what differs from the merged commit. The workflows refusal compares the merge commit's.github/workflowstree with the head it updates, so a PR's own workflow edits do not block it. This also fixes conflict resolution foriteratejobs: before, publishing a resolved merge as a single-parent commit left GitHub showing the conflict again.Runner.
quest merge --landmapped todeliverandquest waitmapped to a newwaittool.waitrereads the PR's context through kix every minute. It returns when the state, head, overall checks, reviews, comments, or threads change, or after at most 55 minutes. The Claude adapter sets the kix MCP server's tool timeout past that, since HTTP tool calls otherwise time out after 60s.delivertakes an optional merge method.git merge, never by rebasing.Inbox. On an open, non-draft PR that is not clean, the merge button starts a merge job. A clean PR keeps the direct merge bound to its exact head. Observations now record a PR's
quest:*label, and the inbox lists such a PR as in progress with no actions.Docs.
cloudflare/README.mdand the new "Merge jobs" section ofcloudflare/GITHUB.md.Choices made without asking
quest:mergeis treated as released and removed, matching whatquest mergedoes when it takes a PR.iterateormerge). A job's own fork branch starts from the default branch, so it has nothing to merge in.queston the runner. Ifquestis not installed in the job's environment, the merge prompt asks the agent to review the quest files the PR changes against the quest guide itself, in place of thequest check --basestep.Validation
just checkandjust testpass locally.waitloop.Limitations
quest mergealready uses.pull_requestevent on that PR, not right away.cloudflare/dev/github.ts, from kixelated/quest#213) now models labels, the hold query, both landing mutations, compare, and workflow trees, and the merge job publication tests run against it. It has no merge queue.Suggested follow-ups
.github/workflows.src/cli/hold.tsand the Worker instead of keeping two copies.wait.🤖 Generated with Claude Code
(Written by Claude Opus 5.5)