Skip to content

pm-dispatch: cloud dev containers cannot reach the GitHub API — the skill must tell the PM to carry briefs inline and open PRs itself #8007

Description

@huangyiirene

Filed by the domain:metadata PM seat from a full shift of measured failures. ⛔ Not PM-edited — this seat's standing rule is that any card touching .claude/skills/pm-dispatch/** is filed and dispatched to claude-fable-5, never edited by the sitting PM. That rule holds even under an explicit instruction to update the skill; the instruction is satisfied through this card, not around it.

The headline finding — measured on 5 of 5 dispatches

Cloud dev sessions can git push but cannot reach the GitHub API. They cannot open a PR, post a comment, or file an issue. Confirmed root cause, from a dev session's own post_turn_summary:

org policy blocks GitHub API; need Claude GitHub App connected to objectstack-ai
needs_action: org admin: connect Claude GitHub App to objectstack-ai org

⚠️ This is an org-admin fix (connect the Claude GitHub App to objectstack-ai), and fixing it would make most of this card unnecessary. But the skill should be robust to it regardless, because the failure is silent and expensive: the dev finishes the work, cannot publish it, and parks in need_input. One dispatch sat idle 1h48m in that state before the PM noticed.

Measured cost this shift: 5 branches pushed, 0 PRs opened by devs, 4 PRs opened by the PM after the fact.

What the skill should say

  1. The dispatch brief must be carried IN FULL inside the dispatch prompt — not only as a GitHub issue comment. A brief that lives only on GitHub is unreachable to a session without GitHub read. Post the comment too, as the public record; but the prompt is the delivery mechanism. (One dispatch reported literally: "issue Product question: an uninstall with no organizationId deletes EVERY organization's rows for that package (measured 5 of 5, including a foreign org's) #7780 comment text unreadable; need GitHub access or paste §1–§6".)
  2. Every dispatch prompt should carry a publish-failure fallback: "if you cannot open the PR, do not retry in a loop and do not drop it — put the complete PR body, the complete <!-- os-dev-report -->, and the complete text of any issue you would have filed into your final message, and say which you managed to post."
  3. The PM opens the PR from the dev's own changeset. Worked reliably 4×. ⚠️ Read the changeset with git diff origin/main...FETCH_HEAD -- .changeset/ — ⛔ not git ls-tree .changeset/ | head -1, which returns the alphabetically-first pre-existing changeset and produced identical unrelated text for two different branches before it was caught.
  4. get_session status is not completion. review_ready / need_input mean the dev stopped, not that there is anything to review. Check the branch and the PR independently.

Second finding — probe controls must exist NOW

The skill's "pair every zero-hit with a positive control" rule needs a sharper clause: a merged branch is auto-deleted, so it is not a valid control. This seat used claude/issue-7728* as a branch-probe control, got an empty result, and would have read it as "the probe works, no branch yet" — but the control had been deleted on merge two hours earlier. Working forms: git ls-remote --heads origin 'claude/*' | wc -l, or a PR head known to be open right now.

Third finding — TEST_DEBT ratchet belongs in every gate list

Three PRs went to a patch round on the same gate in one shift. A card that adds a test file to a TEST_DEBT package must name pnpm check:type-check-debt in its gate list, because:

⛔ Two anti-patterns worth naming explicitly in the skill: never raise a ledger entry to admit a file added in the same PR (the "permission slip" #5278 refuses), and never run --lower on the informational lines of other packages (nine report lowerable entries; none are yours, and it balloons the diff).

⭐ Worth noting the gate earns its keep: on one PR the three "type errors" were two real API misusesregisterObject(schema) called without its required packageId — that vitest could never have caught.

Fourth finding — the mutually-exclusive-region exemption should be written down

A predecessor deferred one card three times, then measured the bottleneck (6 of 6 candidates colliding on protocol.ts), named the remedy, and correctly refused to adopt it unilaterally. The maintainer has since authorised it. The skill should record the protocol so the next seat does not have to rediscover it:

declare regions at claim time · merge main before opening the PR · merge again after each sibling lands · let the queue arbitrate, with a cap on concurrent editors per hot file.

And the reading that makes it safe: the rule guards against concurrent editors, not against a finished PR sitting in a merge queue. A card becomes dispatchable the moment the prior dev session is done, not when its PR lands.

Fifth — small but repeated

  • list_issues labels:[a,b] is OR, not AND. Probe with one unambiguous label and count pm:* client-side. A union totalCount reads exactly like a queue depth.
  • A card filed mid-round can land with no pm:* label and is then invisible to every sweep. Re-read a card's labels after filing. Happened 3× across two terms.
  • Re-read the lane from GitHub every round; never dispatch off the checkpoint's list. This seat's carried list was wrong twice — once omitting two pm:queue cards entirely, one of which had already been ruled and was sitting dispatchable.
  • Before treating a question as open, check whether the card already answers it. A "next open question" was carried for two rounds when the card's own measurement had answered it 75 minutes before it was first asked.

Dispatch constraints

Model: claude-fable-5 (mandatory for .claude/skills/pm-dispatch/**). ⚠️ Two PRs touched this skill today (#7946, #7971) — branch from current main and check for overlap before writing.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions