github-hygiene
Skill1 starsChanged 45 days agoMITView on GitHub
Use when opening or updating a pull request in a repository you maintain, merging PRs, closing issues, reconciling acceptance criteria, decomposing large work into sub-issues, auditing an issue after a merge, or cleaning up branches at the end of a session — before running gh pr create, gh pr merge, gh issue close, or any merge step.
- Deletes or force-pushes
- Commits and pushes
In this repository14 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- docs/claude.mdCLAUDE.md
- github-contributingSkill
- github-for-ado-usersSkill
- github-for-gitlab-usersSkill
- github-issue-firstSkill
- github-projectsSkill
- github-pr-reviewSkill
- github-releasesSkill
- github-repo-bootstrapSkill
- github-repo-configureSkill
- github-repo-reviewSkill
- github-security-responseSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
21 rules. About 0 are common, 12 are distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈12
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Distinctive: line 17
Every change follows one path, and each link is enforced by the tool rather than by memory:
- Distinctive: line 38
Before approving a merge that would close an issue:
- Distinctive: line 40
Read every closing issue body and identify its acceptance criteria.
- Very specific: line 44
Update satisfied checkboxes with a newline-preserving body file: gh issue edit <N> --body-file <file>.
- Very specific: line 47
If any in-scope criterion is unmet or unevaluated, keep it unchecked and use Refs #N in the PR.
- Very specific: line 49
Only when every in-scope criterion passes, change Refs #N to Closes #N and proceed with the ordinary merge gate.
- Distinctive: line 52
Do steps 2 to 5 when you open the PR, not after the merge.
- Distinctive: line 61
If a criterion is no longer required, record the scope decision and rationale on the issue before merge.
- Very specific: line 97
Prefer gh issue develop <N> --name <branch> --base main --checkout to start work: it creates the branch and links it to the issue.
- Very specific: line 98
Commit style: conventional (fix:, feat:, ci:, test:, release:), subject ≤ 50 chars, body says why.
- Very specific: line 99
Wait for CI green on every matrix leg (e.g.
- Very specific: line 101
A closing keyword needs acceptance approval too: Do not merge a PR carrying Closes #N until the closure-gate procedure above passes for …
- Distinctive: line 113
Never re-run a red job hoping it turns green.
- Distinctive: line 126
Solo repo (default): no Projects boards — milestones + labels are the whole tracking system.
- Distinctive: line 135
Use native GitHub sub-issues whenever a single issue's work will span multiple PRs or sessions — not only when contributors need to pick up …
- Very specific: line 167
Keep a checklist in the parent issue's body alongside the native links — - [x] #84 -- batch name (16 files) -- done, PR #90 — since the …
- Distinctive: line 204
Sequence it: Do line-ending and ignore-rule fixes first so renames are not noisy.
- Distinctive: line 207
Make the restructure behaviour-preserving: Use git mv so history follows, keep the public surface identical, and keep tests green before …
- Distinctive: line 209
Update README and CI paths in the same pull request.
- Very specific: line 219
Delete merged local branches: git branch --merged main | grep -v main | xargs -r git branch -d.
- Distinctive: line 221
Run the closed-with-unchecked-boxes audit query (above, under "Acceptance criteria are closure gates") against any issue closed this …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
GitHub Hygiene
Conventions for PR flow, closure, and cleanup. Follow this instead of re-deriving from git history. Issue filing/triage is covered by the separate github-issue-first skill — file the issue first, then start work here. Cutting a release, tagging, branch protection, and milestones are github-releases — merge here, then ship there. Reviewing a PR before it reaches the merge decision is github-pr-review; multi-maintainer board setup is github-projects; a release that contains a security fix goes through github-security-response first.
This skill's PR/merge ceremony assumes a repo that warrants it. For a
clearly low-stakes repo (no CI, no branch protection, no code that gets
built/deployed/tested), see github-issue-first's "Scaling ceremony to
repo risk" section before applying the full flow below by default.
The traceability chain
Every change follows one path, and each link is enforced by the tool rather than by memory:
issue (#N, priority + category labels, milestone)
→ gh issue develop <N> # branch created AND linked to the issue
→ PR with "Refs #N" # links work without promising completion
→ acceptance criteria verified # each criterion has concrete evidence
→ change "Refs" to "Closes" # only now may merge close the issue
→ milestone # groups the release
→ auto-generated release notes # built from the merged PRsSkipping a link doesn't fail loudly — it just quietly returns traceability to human memory. gh issue develop is the one most often skipped and the one that buys the most.
Acceptance criteria are closure gates
Acceptance criteria belong to the issue's implementation work. A completed issue means every in-scope criterion was evaluated, supported by concrete evidence, and checked before closure. A green PR, merged code, elapsed time, sunk effort, or a release deadline does not substitute for that evaluation.
Before approving a merge that would close an issue:
- Read every closing issue body and identify its acceptance criteria.
- Map each criterion to evidence in the diff, tests, CI, documentation, or a reproducible verification result.
- Record a concise completion comment linking that evidence.
- Update satisfied checkboxes with a newline-preserving body file:
gh issue edit <N> --body-file <file>. Do not round-trip multiline Markdown through a PowerShell string array; it can flatten the issue body. - If any in-scope criterion is unmet or unevaluated, keep it unchecked and use
Refs #Nin the PR. The PR may merge, but do not assume the issue stays open. - Only when every in-scope criterion passes, change
Refs #NtoCloses #Nand proceed with the ordinary merge gate.
Do steps 2 to 5 when you open the PR, not after the merge. Tick a criterion at that point when you have seen its evidence hold: a diff plus a test you ran, or a passing CI run on the PR's head. Do not tick on the strength of the code alone, and do not wait for the merge for a criterion that already has evidence. For each criterion left unchecked, say so in the PR and offer the maintainer a linked follow-up issue or a recorded scope decision; file the issue or record the decision only when the maintainer agrees. The criterion stays unchecked until one of those happens.
If a criterion is no longer required, record the scope decision and rationale on the issue before merge. Mark it explicitly as removed or superseded; never check it as though it was delivered. Deferred work gets a linked follow-up issue and the original issue remains open unless its recorded scope is formally changed.
Refs #N avoids a PR-body closing keyword; it does not guarantee the issue
stays open when GitHub has a connected development branch. After every merge,
immediately audit the linked issue state and body. If it is closed while any
in-scope acceptance criterion is unchecked, unmet, or unevaluated, reopen it
immediately and record the reason. For work that spans the PR merge, either use
this audit-and-reopen flow or track the PR-scoped work in a child issue and leave
the release-spanning parent unconnected. Every PR-scoped criterion still needs
evidence before merge.
Closing as duplicate, invalid, or not planned is different from completed: use the appropriate state reason and a human-readable explanation; do not check criteria that were not delivered.
Audit for completed closures with unchecked task boxes:
gh issue list --state closed --limit 1000 --json number,title,body,stateReason \
--jq '.[] | select(.stateReason=="COMPLETED") | select(.body | test("(?m)^\\s*- \\[ \\]")) | "#\(.number)\t\(.title)"'PowerShell:
gh issue list --state closed --limit 1000 --json number,title,body,stateReason `
--jq '.[] | select(.stateReason=="COMPLETED") | select(.body | test("(?m)^\\s*- \\[ \\]")) | "#\(.number)\t\(.title)"'PR flow
- One branch per issue:
fix/…,feat/…,ci/…,test/…,docs/…,release/x.y.z. Branch from a freshgit pulled main — never commit on main. mainthroughout this skill means the repo's default branch. Look it up withgh repo view --json defaultBranchRef -q .defaultBranchRef.nameand substitute it where it differs.- Prefer
gh issue develop <N> --name <branch> --base main --checkoutto start work: it creates the branch and links it to the issue. Start the PR body withRefs #N; replace it withCloses #Nonly after the acceptance gate passes. When falling back togit checkout -b, add the sameRefs #Nlink manually. - Commit style: conventional (
fix:,feat:,ci:,test:,release:), subject ≤ 50 chars, body says why. PR body states what changed and how it was verified. - Wait for CI green on every matrix leg (e.g. windows + ubuntu) before merge —
gh pr checks <N> --watch. Never merge on a red or pending check, and never bypass required checks with an admin merge. - Merging needs the maintainer's explicit approval — ask once per batch ("merge these N when green?"). The maintainer sometimes merges from the GitHub UI mid-session: before acting on a PR,
git pulland re-checkgh pr view <N>state rather than assuming. - A closing keyword needs acceptance approval too. Do not merge a PR carrying
Closes #Nuntil the closure-gate procedure above passes for issue #N. If it has not passed, stop before merging, say which criterion has no evidence, and offer the two ways forward: record the evidence, or change the PR toRefs #Nand merge. "Merge it" or "merge when green" authorizes the merge, not an edit to the PR text, so do not rewriteClosestoRefsyourself and merge; let the maintainer choose. - Auto-merge is the maintainer's switch, not the agent's. GitHub's auto-merge merges a pull request the moment its required checks pass, so enabling it is the merge approval. Turn it on only when the maintainer has said to, only on a pull request whose acceptance criteria already have recorded evidence, and never on one carrying
Closes #Nunless every in-scope criterion is met: an auto-mergedClosescloses the issue with no chance to run the closure gate first. Otherwise leave it off, merge by hand after approval, and audit the issue afterwards. Check whether the repo allows it withgh api repos/{owner}/{repo} --jq .allow_auto_merge; enabling it on a pull request isgh pr merge <N> --autowith the repo's merge flag. - Merge with the repo's configured method. Check
gh repo view --json mergeCommitAllowed,squashMergeAllowed,rebaseMergeAllowed, use the matching flag (gh pr merge <N> --merge,--squash, or--rebase), and when several are allowed follow the repo's existing history. Ifgh repo view --json deleteBranchOnMergereports false, delete the merged remote branch yourself.
When CI goes red
Never re-run a red job hoping it turns green. Read the failure first:
gh run list --branch <branch> --limit 5
gh run view <run-id> --log-failed # only the failing steps- Genuine failure (test, lint, build): fix it on the same branch with its own commit; the fix rides the existing PR, no new issue needed — it is the same unit of work.
- Flake (network blip, runner timeout, race):
gh run rerun <run-id> --failed. If the same job flakes twice, that is a real defect in the test suite — file it (github-issue-first) rather than re-running a third time. - Infrastructure/config break (missing secret, expired token, action removed): file it as its own issue; it will hit every future PR, not just this one.
Solo vs multi-contributor tracking
- Solo repo (default): no Projects boards — milestones + labels are the whole tracking system; a board is unmaintained overhead.
- When the repo gains (or expects) additional contributors, upgrade the scaffolding — board setup, fields, automation and the rest of the multi-maintainer checklist live in the
github-projectsskill; use it rather than improvising a board here. The parts that belong to this skill either way:- A ruleset on main: require the CI status checks and at least one PR review; contributors never push to main. See
github-releases. - Native blocked-by/blocks relations plus
Depends on: #Nbody lines, so pick-up order is visible. - CODEOWNERS for review routing; CONTRIBUTING.md pointing at the conventions in this skill.
- Assign every issue at triage — unassigned means unowned.
- A ruleset on main: require the CI status checks and at least one PR review; contributors never push to main. See
Sub-issues for any large-scope issue, solo repo included
Use native GitHub sub-issues whenever a single issue's work will span multiple PRs or sessions — not only when contributors need to pick up children independently. In a solo repo, the reason isn't parallelization, it's resumability and blast-radius control: if a session ends mid-work, the next one reads the parent issue's sub-issue list and knows exactly which units are done, in-flight, or untouched, instead of re-deriving state from chat history or a half-finished diff. If a bad batch ships, it's isolated to one sub-issue and one PR, not tangled into a giant one-shot change.
Trigger: a mechanical or repetitive task decomposes naturally into batches (one per chapter, module, service, file group, etc.) and doing it all in one PR would be unreviewable or too risky to revert as a unit.
Mechanics:
# 1. File one issue per batch (same title/label/assignee conventions as
# github-issue-first), then link each as a native sub-issue of the parent:
id=$(gh api repos/{owner}/{repo}/issues/<child-number> --jq .id)
gh api -X POST repos/{owner}/{repo}/issues/<parent-number>/sub_issues -F sub_issue_id="$id"
# 2. Verify the link:
gh api repos/{owner}/{repo}/issues/<parent-number>/sub_issues --jq '.[].number'Gotcha: the endpoint wants the child's internal id (a large opaque
number), not its number (the small one everyone reads/types) — fetch it
first. And it must be sent as a typed field with -F (capital), not -f
(lowercase, which sends it as a string and gets rejected with Invalid
property /sub_issue_id: "..." is not of type integer).
Keep a checklist in the parent issue's body alongside the native links —
- [x] #84 -- batch name (16 files) -- done, PR #90 — since the native
sub-issue UI shows open/closed state but not which PR closed it; the
checklist is what a human skimming the issue actually reads. Update it each
time a batch merges. Edit it through a newline-preserving body file, not an
inline --body "..." string (same hazard as the closure-evidence step above):
gh issue view <parent> --json body --jq .body > parent-body.md
# edit parent-body.md: tick the finished child, add the PR number
gh issue edit <parent> --body-file parent-body.mdPowerShell:
gh issue view <parent> --json body --jq .body | Set-Content -Encoding utf8 parent-body.md
# edit parent-body.md: tick the finished child, add the PR number
gh issue edit <parent> --body-file parent-body.mdClose each child issue individually as its PR merges (Closes #<child> in
the PR body does this automatically); leave the parent open until every
child is closed.
Structure drift: suggest a restructure early
While doing ordinary work, notice when the layout is about to stop fitting the repository: one source file holding many unrelated units, a root cluttered with runner scripts, tests mixed with the distributable, or planned features that will each add more to the same place. Surface it before the next feature lands, not after — a restructure is cheapest while the repository is small and has few open pull requests and consumers' paths to keep working.
- File it first (
github-issue-first) as adecision-neededissue that records the proposed layout and states what must stay unchanged (exports, public paths, behaviour). Do not fold it into a feature pull request. - Sequence it. Do line-ending and ignore-rule fixes first so renames are not noisy; then the restructure; then the feature work that touches the same paths. Note the order on the affected issues.
- Make the restructure behaviour-preserving. Use
git mvso history follows, keep the public surface identical, and keep tests green before and after. Update README and CI paths in the same pull request. - Check ignore rules with
git check-ignore -v <path>for every new path; blanket rules such as*.ymlor*.jsoncan silently hide new files. - The recommended starting layouts and the checks live in
github-repo-bootstrap("Starting layout") andgithub-repo-review("The structure baseline"). Do not recommend folders a small repository does not need.
Cleanup checklist (end of session / after release)
git checkout main && git pull && git fetch --prune— local branch listings lie until pruned; verify remote state withgh api repos/{owner}/{repo}/branchesbefore reporting leftover branches.- Delete merged local branches:
git branch --merged main | grep -v main | xargs -r git branch -d. - Working tree clean, everything pushed, no open PRs left unmentioned.
- Run the closed-with-unchecked-boxes audit query (above, under "Acceptance
criteria are closure gates") against any issue closed this session — the
PR template's checkbox catches it at merge time for one PR, but this
catches anything closed a different way (bulk triage, manual
gh issue close) that skipped the reminder.
Common mistakes
| Mistake | Fix |
Trusting git branch -r for remote state | git fetch --prune first, or query the API |
| Merging own PR without asking | Ask once per batch; check whether the maintainer already merged it |
| Treating merged PR or green CI as proof every criterion passed | Evaluate each criterion, record evidence, and keep the issue open until all in-scope criteria pass |
| Checking a removed or deferred criterion as delivered | Record the scope decision; link follow-up work; never falsify completion |
Letting Closes #N auto-close an unevaluated issue | Use Refs #N until the acceptance gate passes; reopen immediately if it closes early |
| Committing on main before branching | Branch first; if it happens: branch from the commit, then git reset --hard origin/main on main |
gh api -f sub_issue_id=<id> rejected with "not of type integer" | Use -F (capital), not -f — sub_issue_id must be sent as a typed integer, not a string |
Linking a sub-issue by its number instead of its id | The sub-issues endpoint wants the internal id — gh api repos/{owner}/{repo}/issues/<number> --jq .id first |
| Adding another feature to an overgrown single file or flat root | Raise a restructure issue first and sequence it before the feature work; see "Structure drift" |
| Re-running a red CI job without reading the log | gh run view <id> --log-failed first; rerun only a diagnosed flake |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

