Atomic Issues and PRs
Publish a change-set as atomic GitHub objects: one issue/PR per logical change, never bundled. The layer above atomic-commit-and-push — it opens the PRs (and optionally issues) that skill never touches.
Phase 0 — Preflight & canonical-repo resolution
Run gh auth status. If unauthenticated, stop and ask the user to run gh auth login.
Resolve the canonical (upstream) slug explicitly before any permission check — gh repo view's
default inspects the current repo, which is the fork in a fork clone.
- Read remotes:
git remote -v. Pick the contribution target:upstreamif present, elseorigin. - Detect a fork relationship:
gh repo view <slug> --json nameWithOwner,parent,defaultBranchRef. A non-nullparentmeans<slug>is itself a fork, so the canonical slug isparent.nameWithOwner. - Query permission on the canonical slug:
gh repo view <canonical-slug> --json viewerPermission.viewerPermission∈ {ADMIN, MAINTAIN, WRITE} ⇒ direct mode; otherwise ⇒ fork mode. - Record the canonical default base branch from
defaultBranchRef.
Phase 1 — Decompose & commit atomically
Group working-tree changes into atomic units by mechanism/file boundary — one concern per unit. Never bundle unrelated changes.
Commit the whole set into N atomic commits first, running the repo-native type-checker and linter before each commit. This is the patch-isolation mechanism: each unit becomes one self-contained commit, so per-unit branches come from cherry-pick — never from re-staging a dirty tree, which would let later units swallow earlier diffs. Present the unit→commit list to the user.
Phase 2 — Choose objects (ask the user)
Ask what to create per unit:
- PR-only — one branch + one PR per unit.
- Issue + linked PR — a tracking issue per unit, plus a PR whose body says
Closes #N.
One answer applies to the run; honor a per-unit override if the user volunteers one.
Phase 3 — Resolve push URL (once)
Push by URL, never by remote name — this avoids undefined targets (a canonical slug from parent
has no local remote) and remote-name collisions / local-config mutation. With gh authenticated, the
git credential helper authorizes HTTPS pushes.
- Direct mode: push URL =
https://github.com/<canonical-slug>. - Fork mode: fork owner = authenticated login (
gh api user --jq .login); fork slug =<login>/<repo>. If the fork does not exist, create it (gh repo fork <canonical-slug> --clone=false) — ask the user first, it is a write action. Push URL =https://github.com/<fork-slug>.
Phase 4 — Per-unit publish loop
Default contract: units are independent — each branches off the canonical base and merges alone.
Determine dependency order in Phase 1; if no unit depends on another, every PR bases on <default>.
For each unit, in dependency order:
git branch <branch> <parent-ref>thengit cherry-pick <unit-commit>onto it.- Independent unit:
<parent-ref>=<canonical-default-base>; PR bases on<default>. - Dependent unit (direct mode only):
<parent-ref>= the prerequisite unit's already-pushed branch; PR bases on that branch (a stacked PR). Cherry-pick only this unit's commit — the prerequisite is already present via the parent branch, so no prerequisite is lost. - Dependent unit in fork mode: cross-fork stacking can't be expressed (a fork PR's base must be a
branch in the canonical repo, not the fork). Stop and ask the user to land the prerequisite PR
first, then re-run for the dependent unit — do not silently flatten it onto
<default>.
- Independent unit:
git push <push-url> <branch>:refs/heads/<branch>(same form both modes — only the URL differs).- If issues were chosen:
gh issue create --repo <canonical-slug> --title "<summary>" --body-file <tmp>→ capture#N. gh pr create --repo <canonical-slug>— direct mode:--base <parent-ref> --head <branch>; fork mode:--base <default> --head <fork-owner>:<branch>. IncludeCloses #Nin the body when an issue was filed.- Emit the issue/PR URLs; move to the next unit.
Constraints (hard)
- Never
--forceor--force-with-leasewithout explicit user authorization. - Never push directly to protected branches (
main,master,release/*) — branches only. - One GitHub object per logical change; bundling is refused.
- Creating a fork is a write action — ask the user before
gh repo fork.