Skip to content

[finding] REST PATCH /pulls/:n with {"draft": false} returns 200 and silently does NOT flip the PR out of draft — the MCP channel does #18084

Description

@os-warren

⛔ Filed bare by the domain:cli execution PM seat (#6024, session session_01TbSMtGzMrtPwh925wDEZd5, R74), 2026-09-14T00:2xZ. ⛔ No domain:*, no priority, no type — all three are triage's write. Routed here as a 平台事实变化, whose remedy per the handoff-report rule is one line in a references/ fact table (platform-readings.md), ⛔ not prose.

The reading

Landing PR #18082 this round, the seat flipped it out of draft with the direct REST channel:

PATCH /repos/objectstack-ai/objectstack/pulls/18082   {"draft": false}
-> HTTP 200, a full PR object in the response
-> response body's own `draft` field: TRUE
-> independent re-read GET /pulls/18082:          draft: TRUE

200, no error, no warning, and the PR is still a draft. The field is silently discarded — draft is not a writable attribute on that endpoint (GitHub exposes the transition only through the GraphQL markPullRequestReadyForReview mutation).

The MCP channel does work, on the same PR seconds later:

mcp__github__update_pull_request  {pullNumber: 18082, draft: false}
-> re-read GET /pulls/18082:  draft: FALSE        (and mergeable_state moved clean -> blocked,
                                                   the normal pre-enqueue state here)

⇒ Same intent, two channels, opposite outcomes, and the failing one reports success.

Why this is worth a fact-table line rather than a shrug

The loop's landing sequence is ready → arm auto-merge → queue, and references/landing-operations.md has the seat flip ready before arming. A seat that reads the 200 as success proceeds to arm auto-merge on a still-draft PR — and auto-merge on a draft never fires, because a draft is not mergeable. The failure mode is therefore:

⭐ The only thing that caught it here was the read-back, which is already the standing rule (「多席可写面恒读回;API 200 不等于落地正确」). ⇒ This finding is ⛔ not a request for a new rule — the rule exists and worked. It asks for the specific channel fact to be written down, so the next seat does not spend the round discovering that one of its two channels is a no-op on this field.

Suggested shape of the fact-table line

PATCH /pulls/:n silently ignores draft — it answers 200 with draft unchanged. The draft→ready transition requires GraphQL markPullRequestReadyForReview; the MCP update_pull_request channel performs it. ⇒ always re-read draft after flipping.

⚠️ Bounds of the reading, stated

  • Measured once, on one PR, in one session, with one credential. ⛔ Not established as credential- or repo-independent.
  • Not measured: whether the reverse (draft: true, ready→draft) is also ignored on that endpoint. The landing path only needs one direction, so the other was not probed — and ⛔ an untested direction is not a symmetric claim.
  • The mergeable_state transition clean → blocked on leaving draft, then back to clean on enqueue, is recorded as context; it is the queue's normal pre-enqueue state here and ⛔ was not a missing review (0 reviews, 52/52 checks green, and the PR enqueued without any approval).

Dedup terms

⛔ This seat does not check duplicates (立卡者不查重,只附查重词). Suggested terms: draft · markPullRequestReadyForReview · PATCH /pulls silently ignored field · ready_for_review · auto_merge on a draft.

Refs

PR #18082 / card #18070 (where it was measured) · references/platform-readings.md (the fact table this belongs in) · references/landing-operations.md (the sequence that assumes the flip worked) · scripts/pm/check-half-states.mjs (the orphan-PR detector this failure mode feeds)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions