GitHub Contribution Workflow
The gh-CLI contribution loop for an agent acting on a GitHub repo: branch →
commit → PR → CI → merge, plus issues, GitHub-side file ops, repo secrets, and
contribution-flow repo settings. Encodes conventions that keep an agent's
contributions reviewable and consistent. Tool-agnostic in spirit; concrete
commands are gh + git.
Native mechanism
Claude Code's Hooks can intercept and block a tool call before it runs — e.g. a PreToolUse hook matching Bash can deny a git push --force or a bare rm -rf. Hooks enforce a hard gate at the tool layer; the --no-verify rule and CLEAN-before-merge convention below are conventions this skill asks the agent to follow voluntarily where no hook exists to enforce them.
When to invoke
- Opening or merging a PR; opening or commenting on an issue.
- Creating or editing a file through GitHub (API / web flow) rather than a local clone.
- Setting a repo secret or configuring contribution-flow repo settings.
- Checking CI status before a merge; bumping a submodule pin.
- User says "open a PR / issue", "merge this", "set the secret", "configure the repo".
Scope — what this does NOT own (route to sibling)
- Verifying the diff matches the commit's claims before push/PR →
pr-diff-verification.
- Security repo settings (Secret Scanning, push protection, gitleaks,
.gitignore baseline) → apple-dev-skills:apple-public-repo-security.
- Parallel-session / submodule worktree conflicts →
subagent-conflict-detection.
- Distributing or installing skill plugins (marketplace, depth-1 rule) →
claude-skill-plugin-packaging.
- Pure local git with no GitHub surface → out of scope.
Intent → command
| Intent |
Command |
| Open a PR |
gh pr create --title "<conventional title>" --body "<body + 🤖 footer>" |
| Check CI before merge |
gh pr checks <n> --repo <o/r> --watch ; gh pr view <n> --json mergeStateStatus |
| Merge a PR |
gh pr merge <n> --squash --delete-branch (only when mergeStateStatus is CLEAN) |
| Open an issue |
gh issue create --title "<title>" --body "<body>" |
| Comment on an issue |
gh issue comment <n> --body "<text>" |
| Create a new file via GitHub |
gh api -X PUT repos/<o/r>/contents/<path> -f message=… -f content=$(base64) … (no local clone needed) |
| Edit an existing file via GitHub |
Same, plus -f sha=<current-blob-sha> (from a prior GET on the same path) — omitting it on an update fails with 422 |
| Set a repo secret |
gh secret set <NAME> --repo <o/r> (interactive paste; see Conventions) |
| List secrets |
gh secret list --repo <o/r> (names only; values are write-only) |
| Configure merge / branch protection |
gh api -X PATCH repos/<o/r> -F allow_squash_merge=true -F delete_branch_on_merge=true ; gh api -X PUT repos/<o/r>/branches/<b>/protection … |
Conventions
- Branch names use Conventional-Commits-style type prefixes (house convention, not a named spec):
feat/ fix/ chore/ docs/ ci/ refactor/ test/.
- PR titles follow Conventional Commits (some repos enforce this with a CI gate — e.g. a "Validate PR title" check; a non-conforming title fails the PR).
- Commit trailer: end commit messages with the agreed
Co-Authored-By: trailer. PR body: end with the 🤖 footer.
- Merge:
--squash --delete-branch. Never merge unless mergeStateStatus is CLEAN — gh pr checks itself never prints CLEAN; that's a gh pr view --json mergeStateStatus value, not a gh pr checks state. Use gh pr checks <n> --watch to wait for checks (exit 0 = all pass, exit 8 = still pending, any other non-zero = a check failed). BLOCKED covers more than pending checks — it also covers a failed required check, a missing review, or an unresolved conversation — so don't poll it indefinitely assuming it will clear on its own; also watch for UNSTABLE (a non-required check failing) and BEHIND (branch needs updating against base).
- Secrets:
gh secret set <NAME> without --body — a literal --body "$TOKEN" exposes the value in process args (ps) and in this harness's Bash-tool transcript, and if typed literally (not as $TOKEN) also lands in shell history. Use the interactive prompt or gh secret set NAME < file. Verify presence (not value) with gh secret list.
- Submodule pin bump: set the gitlink surgically with
git update-index --cacheinfo 160000,<commit-sha>,<submodule-path> — no submodule checkout needed (works in a fresh worktree). Confirm the target SHA is pushed/tag-reachable on the submodule's remote first (git ls-remote --tags <url> <tag>).
--no-verify: allowed ONLY for a commit with no code and no secrets (a submodule-pin bump, a .gitmodules/config-only change) when the repo's pre-commit gate is heavy and times out. git commit --no-verify skips both the pre-commit and commit-msg hooks (a commitlint-style message check is bypassed too — double-check the message format by hand); git push --no-verify separately skips pre-push. Never use either for code or content commits — those must pass the hooks.
- Shared repo / submodule: edit via an isolated worktree branched from
origin/main + PR, never in place — see subagent-conflict-detection.
Repo settings (contribution-flow only)
This skill owns the contribution-flow repo config: merge-button policy
(allow_squash_merge, delete_branch_on_merge), branch protection requiring CI,
required status checks, and labels. Security settings (Secret Scanning, push
protection) are owned by apple-dev-skills:apple-public-repo-security — set them there, not here.
Common Mistakes
- Merging on a non-CLEAN
mergeStateStatus — assuming BLOCKED only ever means pending checks and polling it forever, or force-merging past a real red check. Use gh pr checks <n> --watch plus mergeStateStatus; merge only on CLEAN.
gh secret set --body "$TOKEN" — exposes the value in process args and the tool transcript (and, if typed literally, shell history too). Use the interactive prompt or gh secret set NAME < file.
- Non-Conventional PR title — fails a repo's title-lint gate; the PR can't merge.
- Editing a shared repo / submodule in place — two writers clobber each other; use a worktree + PR.
--no-verify on a code/content commit — bypasses the gate that protects the repo. Reserve it for no-code/no-secret commits only.
- Hand-editing a submodule's checked-out files from the parent repo — bump the pin instead (
git update-index --cacheinfo), and land the submodule's own change via its own PR.
- Duplicating a sibling's job — re-doing diff verification, security settings, or worktree conflict checks here instead of routing to the owning skill.
Review Checklist
Related skills
pr-diff-verification — verify git show --stat --summary HEAD matches the commit's claims before push/PR.
apple-dev-skills:apple-public-repo-security — security repo settings + secret-leak prevention.
subagent-conflict-detection — worktree + PR flow for parallel sessions / submodules.
claude-skill-plugin-packaging — distributing/installing skill plugins.
1---2name: github-contribution-workflow3description: Author GitHub contributions with the gh CLI — open/merge PRs, open issues, create/edit files on GitHub, set repo secrets, configure contribution-flow repo settings. Use when running `gh pr create` / `gh pr merge` / `gh pr checks` / `gh issue create` / `gh secret set` / `gh api`, checking CI before merge, or bumping a submodule pin. Covers Conventional branch/PR-title conventions, Co-Authored-By trailer + 🤖 footer, squash+delete merge, CLEAN-before-merge, `gh secret set` without --body, `git update-index` submodule bumps, the --no-verify rule. Does NOT cover pure local git, diff-vs-commit verification (→ pr-diff-verification), security repo settings (→ apple-public-repo-security), worktree conflicts (→ subagent-conflict-detection), plugin distribution (→ claude-skill-plugin-packaging).4---56# GitHub Contribution Workflow78The `gh`-CLI contribution loop for an agent acting on a GitHub repo: branch →9commit → PR → CI → merge, plus issues, GitHub-side file ops, repo secrets, and10contribution-flow repo settings. Encodes conventions that keep an agent's11contributions reviewable and consistent. Tool-agnostic in spirit; concrete12commands are `gh` + `git`.1314## Native mechanism1516Claude Code's [Hooks](https://code.claude.com/docs/en/hooks) can intercept and block a tool call before it runs — e.g. a `PreToolUse` hook matching `Bash` can deny a `git push --force` or a bare `rm -rf`. Hooks enforce a hard gate at the tool layer; the `--no-verify` rule and CLEAN-before-merge convention below are conventions this skill asks the agent to follow voluntarily where no hook exists to enforce them.1718## When to invoke1920- Opening or merging a PR; opening or commenting on an issue.21- Creating or editing a file *through GitHub* (API / web flow) rather than a local clone.22- Setting a repo secret or configuring contribution-flow repo settings.23- Checking CI status before a merge; bumping a submodule pin.24- User says "open a PR / issue", "merge this", "set the secret", "configure the repo".2526## Scope — what this does NOT own (route to sibling)2728- **Verifying the diff matches the commit's claims** before push/PR → `pr-diff-verification`.29- **Security repo settings** (Secret Scanning, push protection, gitleaks, `.gitignore` baseline) → `apple-dev-skills:apple-public-repo-security`.30- **Parallel-session / submodule worktree conflicts** → `subagent-conflict-detection`.31- **Distributing or installing skill plugins** (marketplace, depth-1 rule) → `claude-skill-plugin-packaging`.32- **Pure local git** with no GitHub surface → out of scope.3334## Intent → command3536| Intent | Command |37|---|---|38| Open a PR | `gh pr create --title "<conventional title>" --body "<body + 🤖 footer>"` |39| Check CI before merge | `gh pr checks <n> --repo <o/r> --watch` ; `gh pr view <n> --json mergeStateStatus` |40| Merge a PR | `gh pr merge <n> --squash --delete-branch` (only when `mergeStateStatus` is `CLEAN`) |41| Open an issue | `gh issue create --title "<title>" --body "<body>"` |42| Comment on an issue | `gh issue comment <n> --body "<text>"` |43| Create a new file via GitHub | `gh api -X PUT repos/<o/r>/contents/<path> -f message=… -f content=$(base64) …` (no local clone needed) |44| Edit an existing file via GitHub | Same, plus `-f sha=<current-blob-sha>` (from a prior `GET` on the same path) — omitting it on an update fails with 422 |45| Set a repo secret | `gh secret set <NAME> --repo <o/r>` (interactive paste; see Conventions) |46| List secrets | `gh secret list --repo <o/r>` (names only; values are write-only) |47| Configure merge / branch protection | `gh api -X PATCH repos/<o/r> -F allow_squash_merge=true -F delete_branch_on_merge=true` ; `gh api -X PUT repos/<o/r>/branches/<b>/protection …` |4849## Conventions5051- **Branch names** use Conventional-Commits-style type prefixes (house convention, not a named spec): `feat/ fix/ chore/ docs/ ci/ refactor/ test/`.52- **PR titles** follow Conventional Commits (some repos enforce this with a CI gate — e.g. a "Validate PR title" check; a non-conforming title fails the PR).53- **Commit trailer**: end commit messages with the agreed `Co-Authored-By:` trailer. **PR body**: end with the 🤖 footer.54- **Merge**: `--squash --delete-branch`. **Never merge unless `mergeStateStatus` is `CLEAN`** — `gh pr checks` itself never prints `CLEAN`; that's a `gh pr view --json mergeStateStatus` value, not a `gh pr checks` state. Use `gh pr checks <n> --watch` to wait for checks (exit `0` = all pass, exit `8` = still pending, any other non-zero = a check failed). `BLOCKED` covers more than pending checks — it also covers a failed required check, a missing review, or an unresolved conversation — so don't poll it indefinitely assuming it will clear on its own; also watch for `UNSTABLE` (a non-required check failing) and `BEHIND` (branch needs updating against base).55- **Secrets**: `gh secret set <NAME>` **without `--body`** — a literal `--body "$TOKEN"` exposes the value in process args (`ps`) and in this harness's Bash-tool transcript, and if typed literally (not as `$TOKEN`) also lands in shell history. Use the interactive prompt or `gh secret set NAME < file`. Verify presence (not value) with `gh secret list`.56- **Submodule pin bump**: set the gitlink surgically with57 `git update-index --cacheinfo 160000,<commit-sha>,<submodule-path>` — no submodule checkout needed (works in a fresh worktree). Confirm the target SHA is pushed/tag-reachable on the submodule's remote first (`git ls-remote --tags <url> <tag>`).58- **`--no-verify`**: allowed ONLY for a commit with **no code and no secrets** (a submodule-pin bump, a `.gitmodules`/config-only change) when the repo's pre-commit gate is heavy and times out. `git commit --no-verify` skips both the pre-commit **and** commit-msg hooks (a commitlint-style message check is bypassed too — double-check the message format by hand); `git push --no-verify` separately skips pre-push. Never use either for code or content commits — those must pass the hooks.59- **Shared repo / submodule**: edit via an isolated worktree branched from `origin/main` + PR, never in place — see `subagent-conflict-detection`.6061## Repo settings (contribution-flow only)6263This skill owns the *contribution-flow* repo config: merge-button policy64(`allow_squash_merge`, `delete_branch_on_merge`), branch protection requiring CI,65required status checks, and labels. **Security settings (Secret Scanning, push66protection) are owned by `apple-dev-skills:apple-public-repo-security`** — set them there, not here.6768## Common Mistakes69701. **Merging on a non-CLEAN `mergeStateStatus`** — assuming `BLOCKED` only ever means pending checks and polling it forever, or force-merging past a real red check. Use `gh pr checks <n> --watch` plus `mergeStateStatus`; merge only on `CLEAN`.712. **`gh secret set --body "$TOKEN"`** — exposes the value in process args and the tool transcript (and, if typed literally, shell history too). Use the interactive prompt or `gh secret set NAME < file`.723. **Non-Conventional PR title** — fails a repo's title-lint gate; the PR can't merge.734. **Editing a shared repo / submodule in place** — two writers clobber each other; use a worktree + PR.745. **`--no-verify` on a code/content commit** — bypasses the gate that protects the repo. Reserve it for no-code/no-secret commits only.756. **Hand-editing a submodule's checked-out files from the parent repo** — bump the pin instead (`git update-index --cacheinfo`), and land the submodule's own change via its own PR.767. **Duplicating a sibling's job** — re-doing diff verification, security settings, or worktree conflict checks here instead of routing to the owning skill.7778## Review Checklist7980- [ ] Branch name uses a Conventional prefix; PR title is Conventional Commits.81- [ ] Commit carries the `Co-Authored-By:` trailer; PR body ends with the 🤖 footer.82- [ ] `mergeStateStatus` is `CLEAN` before merge (via `gh pr checks <n> --watch` + `gh pr view <n> --json mergeStateStatus`); merged with `--squash --delete-branch`.83- [ ] Any secret was set via interactive `gh secret set` (no `--body`); verified with `gh secret list`.84- [ ] A submodule bump used `git update-index --cacheinfo` against a remote-reachable SHA.85- [ ] `--no-verify` used only on a no-code/no-secret commit, with the reason stated.86- [ ] Shared-repo/submodule edits went through a worktree + PR.87- [ ] Nothing here duplicates a sibling skill's scope.8889## Related skills9091- `pr-diff-verification` — verify `git show --stat --summary HEAD` matches the commit's claims before push/PR.92- `apple-dev-skills:apple-public-repo-security` — security repo settings + secret-leak prevention.93- `subagent-conflict-detection` — worktree + PR flow for parallel sessions / submodules.94- `claude-skill-plugin-packaging` — distributing/installing skill plugins.