Skip to content

finding(platform): the GitHub MCP update_pull_request tool un-drafts a PR when you pass only reviewers — it published a governed-surface draft into a merge queue #12200

Description

@os-warren

Filed by the domain:devx @ objectui execution seat (objectstack-ai/objectui#5748), PM session session_012CZgmFFzqA9cX8tBMhvpFe, 2026-08-25 R30. Filed here rather than in objectui because the fix is a line in the pm-dispatch skill's platform fact table (.claude/skills/pm-dispatch/references/platform-readings.md), which lives in this repo — the incident was in objectui, the repair is not.

Unassigned. Grading and domain:* are the triage seat's; on the routing judgement above it looks like domain:skills (the fact table is under .claude/skills/**).

The reading

Calling the GitHub MCP tool update_pull_request with onlyreviewersalso sets draft: false. The tool sends the draft field whether or not the caller supplies it, so a call whose visible intent is "request a review" silently publishes a draft pull request.

Measured, not inferred — the same PR, twenty minutes apart, with one call between:

timereadingsource
~12:05Z"draft": true, requested_reviewers: []pull_request_read on objectui#6183
~12:11Zupdate_pull_request(pullNumber: 6183, reviewers: ["os-zhuang"])no draft argument passed
12:45Z"draft": false, requested_reviewers: ["os-zhuang"]list_pull_requests on the same head ref

Nothing else touched that PR in the window (it had no commits since 2026-08-24T23:07Z).

Why it is worth a card and not a note

⚠️It defeats the governed-surface rule by making the safe action the dangerous one. The pm-dispatch ACCEPT path requires, for a governed-surface PR: leave it draft, ⛔ never ready / enqueue / auto-merge, and request review from os-zhuang so it is visible in a human's queue. Those two instructions are given together — and performing the second, through this tool, breaks the first.

In this instance the PR (objectui#6183, an AGENTS.md change) went ready and entered objectui's merge queue, where it was found third in a five-PR chain. It was converted back to draft and did not land — origin/main was verified not to contain its commit. Caught by re-reading the queue refs for an unrelated reason, ⛔ not by any check: no gate fires on "a governed file is about to merge without a human", because the entire defence is seat discipline.

The window between "ready" and "merged" on a repo with an active merge queue is minutes.

Dispositions

  1. Fact-table line (cheapest, and the reason this is filed here): record in references/platform-readings.md that update_pull_request un-drafts, and that any call against a draft PR must pass draft: true explicitly. Same register as the existing "list_issueslabels is a UNION, not an intersection" entry.
  2. Skill text: the ACCEPT-path fork instructs seats to request review on a draft. That instruction is unsafe as written with this tool, so it should carry the draft: true requirement inline where it is given — a fact-table line alone is not read at the moment of the call.
  3. Consider whether requesting a reviewer needs this tool at all. A dedicated review-request path would not carry the draft field; worth checking what else is available before hardening around the general-purpose updater.

⚠️ The same hazard applies to title, body, labels and any other single-field update_pull_request call on a draft — the reviewers case is just the one that has now happened. Whoever takes this should state the rule over the whole tool, not over one argument.

Related

  • objectui#6183 — the affected PR; the incident is recorded on it in full.
  • objectui#6069 — its card.
  • The 2026-08-18 maintainer ruling establishing the governed surface (docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md) as draft-only with human merge, cross-repo.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions