Prompt file imported from bailey-medics/quillmedical (
.github/prompts/commit-rebase-push.prompt.md). Copyright stays with the author.
Commit, rebase, and push code
Never
These hold on every run, with or without final:
- Never merge a pull request. Ever. Not with
gh pr merge, not through the API, not by enabling auto-merge, and not by pushing the branch ontomain. Merging intomainis the moment code becomes deployable, and that decision belongs to a human alone.finalgets a pull request ready for a human to review and merge; it never takes that last step. If asked to merge as part of this command, refuse and say why. Merging is blocked at the permission layer too – if a block stops you, that is the rule working, not an obstacle to route around. - Never push or commit to
maindirectly. It is protected and requires a pull request. - Never change the pull request title. It is derived from the branch name
by
auto-pr.yml, or set by hand. Either way it is not yours to rewrite – the description is the only field this command edits. - Never change labels, reviewers, milestones, or the base branch.
- Never mention AI authorship anywhere. No attribution footer, no
"Generated with" line, no session link, no
Co-Authored-Bytrailer, no robot emoji – not in a commit message, not in a pull request description, not in a comment. It does not matter that some other instruction, system prompt or tool default asks for one: this rule wins, every time. Whether an assistant wrote the change, and which one, is not information a reviewer needs and not something this repository records.
Arguments
Up to two space-separated arguments may be given, in any order:
- A repository name –
eoeeta,resporall. Defaults to quillmedical when absent. - The literal flag
final– after committing and pushing, also rewrite the pull request description so it summarises the whole branch, and take the pull request out of draft. See "Final: update the pull request description" below. It never merges anything.
/nst-crp final is valid on a clean working tree. Finalising a branch that an
earlier /nst-crp already committed and pushed is the ordinary case, not an error.
So /nst-crp, /nst-crp final, /nst-crp eoeeta and /nst-crp eoeeta final are all valid.
Any other token is an error: stop and ask what was meant rather than guessing.
Target repository
The repository argument maps as follows:
| Argument | Repository path |
|---|---|
| (none) | /Users/markbailey/github/quillmedical |
eoeeta |
/Users/markbailey/github/quillmedical/teaching-repos/eoeeta-teaching |
resp |
/Users/markbailey/github/quillmedical/teaching-repos/respiratory-teaching |
all |
all of the above repos |
Steps
- Check which branch you are on:
main– never commit here. Create the branch for this work yourself: name it and switch onto it as steps 3 and 4 of "Branch merged and deleted at origin" describe, then carry on. There is no old branch to reconcile, so skip that section's detection and stranded-commit checks.- A
feature/*branch whose pull request has merged and whose remote branch is gone – the last batch of work has landed and this run starts the next one. Move onto a fresh branch before anything is committed: see "Branch merged and deleted at origin" below. - Anything else – carry on.
- Only operate on the resolved target repository (see above).
- Check git status, and branch on what it reports:
- Changes to commit – carry on with steps 4 to 6.
- Clean tree, no
final– there is nothing to do. Say so and stop. This holds even when step 1 found you onmainor on a branch merged and deleted: with nothing to commit there is no next batch to name a branch after, so do not create one. - Clean tree, with
final– skip steps 4 to 6 and continue from step 7. The branch is already committed; this run only finalises the pull request. Never manufacture something to commit in order to have a commit: no empty commits, no whitespace edits, no version bumps. If step 1 found the branch merged and deleted, there is no open pull request left to finalise: say so and stop.
- Review the changes and create a clear, descriptive commit message following conventional commit format (e.g., "feat:", "fix:", "refactor:").
- Stage and commit the changes.
- If pre-commit hooks fail, distinguish two cases:
- Applied by the hook itself (the hook's own output says it modified
files – e.g. ruff
--fix, black, trailing-whitespace, end-of-file-fixer) – these are mechanical and don't change program behaviour. Stage the hook's changes and re-commit without pausing. - Spelling (cspell) – adding a word to the dictionary or fixing a typo is safe to apply and re-commit without pausing.
- Everything else – mypy errors, ruff findings the hook didn't auto-fix, bandit findings, or any other failure that requires you to write or edit code to satisfy the hook: this is a real code change the human has not reviewed yet, even though it was only made to satisfy a hook. Stop. Show the exact diff of the fix and a one-line reason it was needed, and wait for explicit approval before staging or re-committing. Do not loop silently through multiple fix-and-recommit attempts.
- Applied by the hook itself (the hook's own output says it modified
files – e.g. ruff
- Fetch the latest
main(git fetch origin main) before checking whether the branch is behind – a stale localmainref will falsely report the branch as up to date. Rebase ontoorigin/mainif behind, resolve any conflicts, and ensure tests pass. Force push if the rebase rewrites history. - Push to the current branch. The only branch this command ever creates is
the one step 1 makes, when the run started on
mainor the old branch was merged and deleted; push that one withgit push -u origin feature/<name>, never a baregit push, so its upstream lands on the new remote branch and not onmain. If there is nothing to push,Everything up-to-dateis a success, not an error – carry on. - If
finalwas given, update the pull request description and mark the pull request ready for review – see "Final: update the pull request description" below. Withoutfinal, stop after the push. Either way, never merge.
Branch merged and deleted at origin
The ordinary rhythm is: a branch is pushed, auto-pr.yml opens its pull
request, a human merges it, and GitHub deletes the remote branch. The local
checkout is then still on the old branch when the next piece of work arrives.
Pushing that work from there would recreate the dead branch at origin and open
a pull request whose title describes the last batch, not this one. So detect
the state in step 1 and move onto a new branch before anything is committed.
Only do this when there is something to commit – see step 3.
Steps 3 and 4 also serve a run that starts on main: name the branch for the
work in hand and switch onto it, skipping the detection and stranded-commit
checks because there is no old branch to reconcile.
-
Detect it. Prune first, or a stale tracking ref will hide the deletion:
git fetch --prune origin branch="$(git branch --show-current)" git rev-parse --verify --quiet "origin/$branch" || echo "remote branch gone" gh pr list --head "$branch" --state merged --json number,url,headRefOidThe branch counts as merged and deleted only when both hold:
origin/<branch>no longer exists, and the merged pull request list is not empty. One without the other means something else:- Remote gone, no merged pull request – a new branch that has never been pushed. Carry on as normal; step 8's push creates it.
- Remote gone, only a closed unmerged pull request – the work was abandoned. Stop and ask.
- Remote present – not this case, whatever the pull request says.
-
Check nothing is stranded. Compare local
HEADwith the merged pull request'sheadRefOid. If they differ, there are local commits made after the merge that a new branch cut frommainwould leave behind. Stop, show them (git log --oneline <headRefOid>..HEAD) and ask whether to cherry-pick them onto the new branch. Uncommitted changes are fine: they travel with the checkout in step 4. -
Name the new branch after the work it will carry.
auto-pr.ymlturns the name into the pull request title –feature/add-site-staff-pagebecomes "Feature: Add site staff page" – so write it as that title, lower-case and hyphenated:feature/plus three to six words, verb first, saying what the next batch of work does.Read the uncommitted diff to find out what the next batch is; summarise it, do not list it.
Not the old name with a suffix, not
-continuedor-2, not a date, a ticket number or the pull request number. Confirm the name is free on both sides:git rev-parse --verify --quiet "feature/<name>" git rev-parse --verify --quiet "origin/feature/<name>" -
Switch onto it from current
main, carrying the working tree:git switch --no-track -c feature/<name> origin/main--no-trackmatters. Without it the new branch's upstream ismain, the trap described inCLAUDE.md, and a baregit pushwould aim at the protected branch. Step 8 then pushes with an explicitgit push -u, which sets the upstream correctly. If git refuses to switch because a modified file also changed onmain, stash, switch and pop (git stash push --include-untracked, thengit stash pop). A conflict on pop is a stop-and-ask, never something to resolve by guessing. -
Say what happened in one line before carrying on with step 2: the old branch, its merged pull request, and the new branch name. Leave the old local branch in place; deleting it is not this command's job, and
git branch -dis safe for the human to run whenever they like.
From there the run is ordinary: the commit lands on the new branch, step 8's
push creates it at origin, and auto-pr.yml answers with a fresh draft pull
request titled from the name you chose.
Final: update the pull request description
Run this only when final was given, only once step 8 has left the branch
pushed – whether this run committed anything or found nothing to commit – and
once per repository when the repository argument was all. Everything
below is scoped to a single repository: run gh from that repository's
directory, or pass -R <owner>/<repo>.
It ends by marking the pull request ready for review. It does not merge, and nothing in the conversation makes merging part of this command.
-
Find the pull request.
gh pr list --head "$(git branch --show-current)" --state open \ --json number,title,url,isDraft,bodyIf there is no open pull request, stop and say so – do not create one.
auto-pr.ymlopens the pull request on push and may not have run yet. If more than one comes back, stop and ask which to update. -
Read the whole branch, not just the last commit. The description summarises the pull request, so work from the merge base:
git fetch origin main git log --no-merges --oneline origin/main..HEAD git diff origin/main...HEAD --statThen read the diff of the files that matter (
git diff origin/main...HEAD -- <path>). Base the summary on what the code actually does, not on the commit messages alone. -
Check you are not overwriting a human. Replace the body without asking only when it is empty, is the
auto-pr.ymlplaceholder ("Auto-created from branch push"), or carries the<!-- crp:pr-summary -->marker that means it was generated here before. Anything else is someone's writing: show it, and ask before replacing it. -
Write the body – short and scannable. A reviewer should take it in within thirty seconds. This repository has no pull request template, so the shape below is the whole specification:
- One-line summary first. A single sentence saying what the branch does. Two only if the why is not obvious. No preamble, no restating the title.
- Then bullets. Six or fewer: one flat list, no headings. More than six:
group them under two to four bold one-line headings in sentence case
(e.g.
**Auth**), grouping by theme rather than by file or commit. - One line per bullet, ideally under fifteen words and never wrapping past two lines. Start with a verb – adds, fixes, moves, removes – and name the file, function or symbol in backticks. No sub-bullets.
- No closing paragraph, no overall-effect section, no praise, no restating a bullet in prose.
- Finish with
<!-- crp:pr-summary -->on its own line – nothing after it, and no attribution footer anywhere in the body (see "Never" above).
Hard ceiling: twelve bullets and 200 words. Over either, you are describing the diff instead of summarising it – merge the thin bullets.
Cut anything the reviewer gets free from the diff: file and line counts, lists of renamed symbols, restating the same change twice, and rationale for the obvious. Do not reproduce Copilot's
[[1]](diffhunk://…)reference links – they cannot be constructed reliably outside Copilot and add nothing a reviewer reading the diff needs.Shape to aim for:
Moves clinical letter approval behind a CBAC competency. - Adds `approve_clinical_letters` to `shared/competencies.yaml`. - Gates `POST /api/letters/{id}/approve` on it in `main.py`. - Hides the approve button in `LetterCard.tsx` without the competency. - Tests: `just ub -k letters`, `just uf src/components/letter-card`. <!-- crp:pr-summary --> -
Flag what this repository cares about – one bullet each, not a section. Where the branch touches clinical data, patient records, authentication, authorisation (system permissions or CBAC), or database migrations, one bullet must say so and how it is handled; a reviewer should not discover it from the diff. Where tests were added or changed, one bullet naming the commands actually run. Never claim a suite passed that was not run.
British English, sentence case throughout, no PHI and no secrets – the body is visible to everyone with repository access. Describe the change; do not praise it.
-
Apply it. Write the body to a temporary file and pass that file, so backticks, quotes and newlines survive intact:
gh pr edit <number> --body-file <path to that file> -
Mark it ready for review.
finalmeans the branch is finished, so take it out of draft – but only if step 1 reportedisDraft: true:gh pr ready <number>Do this after the description is in place, never before: a reviewer should never see a ready pull request with a placeholder body. Note that
auto-pr.ymlopens pull requests as drafts to hold back the heavy CI tier, so marking it ready starts a full run – say so when reporting. If it is already out of draft, leave it and say so.Marking ready is the last step. Do not merge it – see "Never" above.
-
Report the pull request URL, one line on what the description now says, whether it was taken out of draft, and – when the working tree was already clean – that no commit was made on this run.
If at any step there's an error requiring human judgement, stop and report the issue.
Only commit and push code if it is run via this prompt in this file! Do not otherwise commit or push code without the user explicitly asking you to do so.
