Skip to content

Latest commit

 

History

History
166 lines (135 loc) · 11.9 KB

File metadata and controls

166 lines (135 loc) · 11.9 KB

GitHub provider reference

The source-control skill bodies name operations (open a draft change request, read its checks, reply on a review thread). This file is how GitHub, the one forge the plugin supports today, does each of them, with the gh CLI. Read it before the first forge operation a skill performs. The decision that skill bodies stay forge-neutral and the provider detail lives here is ADR 0060.

This file is the index from an operation to its command; a skill's spoke is the authority on the full step around it. A spoke whose steps need more than a table row has a provider file of the same name under a directory named for its skill: pull-request/ holds the GitHub commands, scripts and gh facts behind the pull-request skill's create.md, ready-for-review.md, monitor.md, readiness.md, merge.md and review-venue.md, and each spoke links its steps there. The worktree skill's spokes need only the rows below. The babysit-prs and babysit-loop skills' spokes link their commands in babysit-prs/ and babysit-loop/.

A pull request is GitHub's change request; this file uses its terms.

Identity and repository

Operation GitHub command
The current login gh api user --jq .login
The current repository as owner/repo gh repo view --json nameWithOwner -q .nameWithOwner
The repository's default branch gh repo view --json defaultBranchRef -q .defaultBranchRef.name; in a sandboxed session, gh api "repos/{owner}/{repo}" --jq '.default_branch'
Read a tracked file from a repository's default branch gh api repos/<owner>/<repo>/contents/<path> (the content field is base64)

Resolve a pull request

Operation GitHub command
The current branch's PR number gh pr view --json number -q .number
The same, with the branch named (ambiguous or stale checkout) gh pr view "$(git branch --show-current)" --json number -q .number
State for the smart default gh pr view --json state,number,isDraft (non-zero exit: no PR)
The current branch's PR with author and head branch (babysit-prs single-PR scope) gh pr view --json number,url,author,headRefName
Number, URL and state for status gh pr view <N> --json number,url,state
Head commit gh pr view <N> --json headRefOid -q .headRefOid
Mergeability gh pr view <N> --json mergeable; CONFLICTING means GitHub runs no workflows until the base is integrated
The PR for a branch gh pr list --head <branch> --json number,title,state
Whether a branch's PR merged (worktree cleanup) gh pr list --state merged --head <branch>; a non-empty result means merged
PRs for worktree cross-reference (one call per worktree branch, all states) gh pr list --head <branch> --state all --json number,title,state,headRefName; a PR whose head is <branch> is a match, and a failed call marks that branch's PR lookup incomplete
Your own open PRs here gh pr list --state open --author "@me" --json number
Your own open PRs across an owner gh search prs --state open --author @me --owner <owner> --json repository

gh pr view with no number resolves by the checked-out branch. Resolve the number once at phase entry and pass it to every later gh call in that phase.

Change the pull request

Operation GitHub command
Open a draft PR gh pr create --draft (with --head <branch> on the PR-only entry), never --reviewer
Check out the PR head gh pr checkout <N>, or gh pr checkout <N> --detach when the branch is locked in another worktree. It is fork-safe: a fork's head branch is not fetchable from origin by name
Merge the base into the head gh pr update-branch <N> (pull-request ready-for-review.md)
Flip a draft ready gh pr ready <N>, run only inside /source-control:pull-request ready, never bare
Merge a PR gh pr merge <N> -R <owner>/<repo> with the PR number and repository written literally, plus the flags the repository needs: --squash, --auto on a merge-queue or auto-merge base, --admin only where a code-owner review or merge queue blocks an otherwise ready merge and the consumer's policy allows it; --match-head-commit <sha> is optional for an immediate merge and required with --auto. A refused merge is reported, never handed to the user or retried in another shape (pull-request/merge.md; pull-request merge.md §4.2.1 owns when to merge without asking). A babysit lane merges only through its merge-gate wrapper (babysit-prs safety.md)
Open a tracking issue gh issue create

Checks and CI logs

Operation GitHub command
List the PR's checks gh pr checks <N>; never --watch, which blocks the foreground
CI logs, last resort gh run view --log-failed, which truncates around 4 MB at the CLI display layer (cli/cli#11059, #10551, #7771); the fetch-logs action's annotation and archive paths return complete data

A workflow that exchanges an OIDC token fails when the PR modifies that workflow file: the file must match the default branch for the exchange to succeed. Classify that failure as informational.

Review surfaces

GitHub splits PR feedback across three surfaces, each paginated at 30 items per page, oldest first. Pass --paginate and per_page=100 on every read, or the newest item (usually your own reply) is missed.

Surface Read
Inline review-thread comments gh api --paginate "repos/<owner>/<repo>/pulls/<N>/comments?per_page=100"
Conversation (issue-level) comments, where AI-review summaries land gh api --paginate "repos/<owner>/<repo>/issues/<N>/comments?per_page=100"
Reviews (bodies of APPROVED, CHANGES_REQUESTED and COMMENTED reviews) gh api --paginate "repos/<owner>/<repo>/pulls/<N>/reviews?per_page=100"

Round scoping: inline comments carry original_commit_id, reviews carry commit_id, and conversation comments carry only created_at.

Operation GitHub command
React to a comment POST to the comment's /reactions endpoint (+1, -1, eyes); verify with a GET on the same endpoint. Review bodies have no reactions endpoint
Reply on an inline thread gh api repos/<owner>/<repo>/pulls/<N>/comments/<id>/replies -F body=@<body-file>
Reply on the conversation gh pr comment <N> --body-file <body-file>
Verify an inline reply the inline surface filtered with --jq '.[] | select(.in_reply_to_id == <original-id>)'
Verify a conversation reply (D5, before any fix) the conversation surface filtered with --jq '.[] | select(.user.login == "<posting-identity>") | .body'
Verify a follow-up reply citing the fix the conversation surface filtered with --jq '.[] | select((.body | contains("<sha>")) and .user.login == "<posting-identity>") | .body', never .[-1]
Tell a bot from a person REST user.type == "Bot"; GraphQL author.__typename == "Bot"
Resolve a review thread the GraphQL resolveReviewThread mutation, then confirm isResolved == true over GraphQL

Write a reply body to a file with the Write tool and pass the file: a reply often quotes reviewer text, and a quote, $(…) or backtick inside it can break out of an inline shell argument.

Branch rules

Operation GitHub command
Effective rules on a branch gh api repos/<owner>/<repo>/rules/branches/<branch>
Classic protection fallback gh api repos/<owner>/<repo>/branches/<branch>/protection
Whether head branches are deleted on merge the repository's delete_branch_on_merge field: gh api repos/{owner}/{repo} --jq '.delete_branch_on_merge'

Sandboxed sessions: GraphQL is unavailable

A cloud session behind the GitHub proxy is served no GraphQL operation: every GraphQL request gets HTTP 403, whatever token is set. Pointer: Configure cloud environments § GitHub proxy. As-of: 2026-10-11. Recheck trigger: that section changes its GraphQL entry, or a release note names the GitHub proxy. gh pr view --json, gh repo view --json, gh pr create, gh pr ready and gh pr merge all run over GraphQL. The 403 reads like an expired token or a missing scope and is neither: switch to the REST route the spoke names rather than re-authenticating. Probe it read-only:

gh api graphql -f query='query{viewer{login}}' --jq '.data.viewer.login'

A login means GraphQL is served. Under the restriction, the babysit engine re-sources the gh pr view bundle over REST (GET …/pulls/{n} plus the commit check-runs and combined-status endpoints), so discovery, classification, branch-rule and freshness checks keep working. Review-thread resolution has no REST equivalent and is not approximated: the merge gate reports threadResolutionProven: false and unresolvedThreadCount: null and holds the PR as readiness UNPROVEN. reviewDecision degrades the same way, because REST can prove CHANGES_REQUESTED but not an approval (GitHub folds CODEOWNERS and the required-reviewer count into that field). The same restriction is handled in the pull-request skill's create.md §2.4.0 and the work-item-tracker GitHub adapter.

Draft stage

A draft cannot merge until it is marked ready (changing the stage of a pull request, read 2026-10-11; recheck when GitHub changes the draft-stage rules).

API budget

Every gh call by every agent and session under one user shares that user's hourly budget: 5,000 REST requests and 5,000 GraphQL points. gh never retries or backs off. Every unattended loop follows this cadence:

  • Which bucket. GraphQL: gh pr view, gh pr list, gh pr checks (--watch re-queries every 10 s), gh pr create, edit, ready and merge, gh issue, gh api graphql. REST: gh run, gh api <path>.
  • Waiting. One watcher per PR or run, in the background, polling at 60 s or longer: gh run watch <id> --exit-status -i 60 (the default is every 3 s), or REST reads of repos/<owner>/<repo>/pulls/<n> and commits/<sha>/check-runs. Never loop gh pr view and never run gh pr checks --watch. 60 s is a floor per watcher, not a budget: with several watchers, poll every 2 s × watchers × requests per poll or slower, which keeps them near a third of the hourly REST budget (ten pull-request monitors at about seven requests a poll: 140 s, set as the monitor's INTERVAL).
  • GraphQL-only fields (isInMergeQueue, statusCheckRollup): read once, at the point you decide.
  • Writes. One at a time, at least 1 s apart, never in a retry loop. Secondary limits cap content-creating requests at 80 a minute and 500 an hour, and all requests at 100 concurrent.
  • Rate-limit errors. Stop at the first one; never retry in a loop or probe for recovery. Wait until x-ratelimit-reset, or retry-after when a secondary limit sends it, else at least one minute, then back off exponentially and give up after a few tries. gh api rate_limit does not show secondary limits and can count against them.

Pointer: best practices for using the REST API, REST rate limits, GraphQL rate limits. As-of: 2026-10-17. Recheck trigger: either rate-limits page changes a figure, or a gh release changes the run watch or pr checks --watch interval or adds retries.