Create PR
Preconditions
- Be on a feature branch (not
main).
- Have a working
gh CLI auth session (gh auth status).
- Know whether
make e2e is acceptable to run right now (it is destructive in this repo).
- Decide whether you want to rebase/merge on top of the latest
origin/main before opening the PR.
1) Verify branch and repo state
- Get current branch:
git branch --show-current
- Stop if on
main:
- If branch is
main, create/switch to a new branch before continuing.
- Sync refs and understand delta vs
main:
git fetch origin
git log --oneline --decorate origin/main..HEAD
git diff --stat origin/main...HEAD
- (Optional but recommended) Rebase/merge onto latest
origin/main:
- Prefer
git rebase origin/main for linear history, unless the repo/process prefers merges.
- If you already pushed and rebased, you will need
git push --force-with-lease (do not use plain --force).
- Confirm what will be included:
git status --porcelain
git diff
2) Decide which checks to run (based on relevant file changes)
- Compute changed files vs
origin/main:
git fetch origin
changed=$(git diff --name-only origin/main...HEAD)
- Decide what’s “relevant”:
- Go-relevant (run coverage/lint/backend tests): any changes to
*.go, go.mod, go.sum, cmd/, internal/, migrations/
- JS/asset-relevant (run frontend tests): any changes under
web/ or web/static/js/, or JS/CSS/HTML files
- E2E-relevant (consider running Playwright): any changes under
web/, tests/e2e/, playwright.config.js, or backend HTTP surfaces (cmd/, internal/handlers/, internal/middleware/, internal/services/, migrations/)
- Optional override:
- If the user wants the “full” safety net regardless of change type, run all checks anyway.
3) Run validations (only when relevant)
- If Go-relevant: ensure coverage does not decrease vs
origin/main:
- Compute baseline coverage on
origin/main (recommended: temporary worktree so you don’t lose state):
tmp=$(mktemp -d)
git worktree add --detach "$tmp" origin/main
base_cov=$((cd "$tmp" && make coverage) | tail -n 1 | rg -o '[0-9]+(\\.[0-9]+)?%' | tr -d '%')
git worktree remove --force "$tmp"
head_cov=$(make coverage | tail -n 1 | rg -o '[0-9]+(\\.[0-9]+)?%' | tr -d '%')
BASE_COV="$base_cov" HEAD_COV="$head_cov" python3 - <<'PY'
import os
base = float(os.environ["BASE_COV"])
head = float(os.environ["HEAD_COV"])
print(f"origin/main: {base:.2f}%")
print(f"HEAD: {head:.2f}%")
if head + 1e-9 < base:
raise SystemExit("Coverage decreased. Add/improve tests before proceeding.")
PY
- If Go-relevant: run lint:
- Run tests based on relevance:
- If Go-relevant and JS/asset-relevant:
make test
- If Go-relevant only:
make test-backend
- If JS/asset-relevant only:
make test-frontend
- If E2E-relevant: confirm with the user, then run e2e:
- Explain that
make e2e is destructive (resets volumes/reseeds) and can take a while.
- Run:
make e2e
If any validation step fails:
- Fix failures automatically (update code/config/tests as needed).
- Re-run the failed command until it passes.
- After fixes, restart this validation section from the beginning (coverage/lint/tests/e2e as applicable) before proceeding to staging/commit/PR.
4) Stage changes safely (no secrets, no troubleshooting images)
- Enumerate changed/untracked files:
- Identify files to explicitly exclude from commit:
- Any secrets/credentials (tokens, private keys, API keys).
- Local env/config/log artifacts (examples:
.env, .stripe-listen.log, coverage.out, playwright-report/, test-results/), unless there is an explicit reason to commit them.
- Any image added only for debugging/troubleshooting (common extensions:
.png, .jpg, .jpeg, .gif, .webp), unless the change intentionally adds product assets.
- Stage the intended change set:
- Prefer staging explicitly by file path(s) you intend to include.
- For careful staging, use
git add -p to avoid bundling drive-by changes.
- If staging everything for convenience (
git add -A), immediately unstage excluded files (git restore --staged <path>), and/or delete unintended untracked artifacts.
- Review staged changes carefully:
git diff --cached --stat
git diff --cached
- Confirm nothing important was accidentally left out:
git status --porcelain should show either a fully staged change set (ready to commit) or only intentionally untracked/ignored artifacts.
- Do a quick secret sanity check before committing:
- Search staged diff for common secret markers (examples):
git diff --cached | rg -n '(BEGIN (RSA|EC|OPENSSH) PRIVATE KEY|PRIVATE KEY-----|STRIPE_SECRET_KEY|AWS_SECRET_ACCESS_KEY|GH_TOKEN|xox[baprs]-)'
- If any match is found, do not commit; remove/redact and rotate secrets if needed.
5) Commit with a detailed message
Create a commit message that is detailed enough for review and future archaeology.
- Choose a clear title (imperative, ≤ 72 chars).
- In the body, include:
- Why: what problem this change solves
- What: key changes grouped by area (backend/frontend/db)
- Risk: any tricky parts or migration notes
- Tests: explicitly list which checks ran (and outcomes), and which were skipped (and why)
Commit using a multi-line message, for example:
git commit -m "<title>" \
-m "Why: ..." \
-m "What: ..." \
-m "Tests: (ran) ...; (skipped) ... (reason: ...)"
6) Push branch
- Push and set upstream:
- If you rebased after pushing:
- Push safely:
git push --force-with-lease
7) Create or update PR with a reviewer-ready description
- Determine branch name:
branch=$(git branch --show-current)
- If an open PR already exists for this branch, prefer updating it:
gh pr view --head "$branch" (if this fails, create a new PR)
- Update title/body when needed:
gh pr edit --title "<title>" --body-file - <<'EOF' ... EOF
- Create PR targeting
main (adjust base if requested):
gh pr create --base main --head "$branch" --title "<title>" --body-file - <<'EOF'
## Summary
- ...
## Changes
- ...
## How to review
1. ...
2. ...
## How to test
- (Ran) ...
- (Skipped) ... (reason: ...)
## Notes / risks
- ...
EOF
- (Optional) Add reviewers/assignees/labels if the user provides them:
gh pr edit --add-reviewer user1,user2
gh pr edit --add-assignee user1
gh pr edit --add-label "bug","enhancement"
- Provide the PR URL and (optionally) open it in a browser:
- After opening, watch CI status (optional but convenient):
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: create-pr-233description: Create a GitHub pull request from the current non-main branch in this repo by running lint/tests/e2e, staging only safe files (never secrets or troubleshooting images), committing with a detailed message, pushing, and opening/updating a PR via the gh CLI with a reviewer-ready description. Use for requests like "/create-pr", "create a PR", "push and open a PR", or "prep this branch for review". Use when this capability is needed.4---56# Create PR78## Preconditions910- Be on a feature branch (not `main`).11- Have a working `gh` CLI auth session (`gh auth status`).12- Know whether `make e2e` is acceptable to run right now (it is destructive in this repo).13- Decide whether you want to rebase/merge on top of the latest `origin/main` before opening the PR.1415## 1) Verify branch and repo state16171. Get current branch:18 - `git branch --show-current`192. Stop if on `main`:20 - If branch is `main`, create/switch to a new branch before continuing.213. Sync refs and understand delta vs `main`:22 - `git fetch origin`23 - `git log --oneline --decorate origin/main..HEAD`24 - `git diff --stat origin/main...HEAD`254. (Optional but recommended) Rebase/merge onto latest `origin/main`:26 - Prefer `git rebase origin/main` for linear history, unless the repo/process prefers merges.27 - If you already pushed and rebased, you will need `git push --force-with-lease` (do not use plain `--force`).285. Confirm what will be included:29 - `git status --porcelain`30 - `git diff`3132## 2) Decide which checks to run (based on relevant file changes)33341. Compute changed files vs `origin/main`:35 - `git fetch origin`36 - `changed=$(git diff --name-only origin/main...HEAD)`372. Decide what’s “relevant”:38 - **Go-relevant** (run coverage/lint/backend tests): any changes to `*.go`, `go.mod`, `go.sum`, `cmd/`, `internal/`, `migrations/`39 - **JS/asset-relevant** (run frontend tests): any changes under `web/` or `web/static/js/`, or JS/CSS/HTML files40 - **E2E-relevant** (consider running Playwright): any changes under `web/`, `tests/e2e/`, `playwright.config.js`, or backend HTTP surfaces (`cmd/`, `internal/handlers/`, `internal/middleware/`, `internal/services/`, `migrations/`)413. Optional override:42 - If the user wants the “full” safety net regardless of change type, run all checks anyway.4344## 3) Run validations (only when relevant)45461. If Go-relevant: ensure coverage does not decrease vs `origin/main`:47 - Compute baseline coverage on `origin/main` (recommended: temporary worktree so you don’t lose state):4849```bash50tmp=$(mktemp -d)51git worktree add --detach "$tmp" origin/main52base_cov=$((cd "$tmp" && make coverage) | tail -n 1 | rg -o '[0-9]+(\\.[0-9]+)?%' | tr -d '%')53git worktree remove --force "$tmp"54head_cov=$(make coverage | tail -n 1 | rg -o '[0-9]+(\\.[0-9]+)?%' | tr -d '%')55BASE_COV="$base_cov" HEAD_COV="$head_cov" python3 - <<'PY'56import os57base = float(os.environ["BASE_COV"])58head = float(os.environ["HEAD_COV"])59print(f"origin/main: {base:.2f}%")60print(f"HEAD: {head:.2f}%")61if head + 1e-9 < base:62 raise SystemExit("Coverage decreased. Add/improve tests before proceeding.")63PY64```65662. If Go-relevant: run lint:67 - `make lint`683. Run tests based on relevance:69 - If Go-relevant **and** JS/asset-relevant: `make test`70 - If Go-relevant only: `make test-backend`71 - If JS/asset-relevant only: `make test-frontend`724. If E2E-relevant: confirm with the user, then run e2e:73 - Explain that `make e2e` is destructive (resets volumes/reseeds) and can take a while.74 - Run: `make e2e`7576If any validation step fails:77- Fix failures automatically (update code/config/tests as needed).78- Re-run the failed command until it passes.79- After fixes, restart this validation section from the beginning (coverage/lint/tests/e2e as applicable) before proceeding to staging/commit/PR.8081## 4) Stage changes safely (no secrets, no troubleshooting images)82831. Enumerate changed/untracked files:84 - `git status --porcelain`852. Identify files to explicitly exclude from commit:86 - Any secrets/credentials (tokens, private keys, API keys).87 - Local env/config/log artifacts (examples: `.env`, `.stripe-listen.log`, `coverage.out`, `playwright-report/`, `test-results/`), unless there is an explicit reason to commit them.88 - Any image added only for debugging/troubleshooting (common extensions: `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`), unless the change intentionally adds product assets.893. Stage the intended change set:90 - Prefer staging explicitly by file path(s) you intend to include.91 - For careful staging, use `git add -p` to avoid bundling drive-by changes.92 - If staging everything for convenience (`git add -A`), immediately unstage excluded files (`git restore --staged <path>`), and/or delete unintended untracked artifacts.934. Review staged changes carefully:94 - `git diff --cached --stat`95 - `git diff --cached`965. Confirm nothing important was accidentally left out:97 - `git status --porcelain` should show either a fully staged change set (ready to commit) or only intentionally untracked/ignored artifacts.986. Do a quick secret sanity check before committing:99 - Search staged diff for common secret markers (examples): `git diff --cached | rg -n '(BEGIN (RSA|EC|OPENSSH) PRIVATE KEY|PRIVATE KEY-----|STRIPE_SECRET_KEY|AWS_SECRET_ACCESS_KEY|GH_TOKEN|xox[baprs]-)'`100 - If any match is found, do not commit; remove/redact and rotate secrets if needed.101102## 5) Commit with a detailed message103104Create a commit message that is detailed enough for review and future archaeology.1051061. Choose a clear title (imperative, ≤ 72 chars).1072. In the body, include:108 - Why: what problem this change solves109 - What: key changes grouped by area (backend/frontend/db)110 - Risk: any tricky parts or migration notes111 - Tests: explicitly list which checks ran (and outcomes), and which were skipped (and why)112113Commit using a multi-line message, for example:114115```bash116git commit -m "<title>" \117 -m "Why: ..." \118 -m "What: ..." \119 -m "Tests: (ran) ...; (skipped) ... (reason: ...)"120```121122## 6) Push branch123124- Push and set upstream:125 - `git push -u origin HEAD`126- If you rebased after pushing:127 - Push safely: `git push --force-with-lease`128129## 7) Create or update PR with a reviewer-ready description1301311. Determine branch name:132 - `branch=$(git branch --show-current)`1332. If an open PR already exists for this branch, prefer updating it:134 - `gh pr view --head "$branch"` (if this fails, create a new PR)135 - Update title/body when needed: `gh pr edit --title "<title>" --body-file - <<'EOF' ... EOF`1363. Create PR targeting `main` (adjust base if requested):137138```bash139gh pr create --base main --head "$branch" --title "<title>" --body-file - <<'EOF'140## Summary141- ...142143## Changes144- ...145146## How to review1471. ...1482. ...149150## How to test151- (Ran) ...152- (Skipped) ... (reason: ...)153154## Notes / risks155- ...156EOF157```1581594. (Optional) Add reviewers/assignees/labels if the user provides them:160 - `gh pr edit --add-reviewer user1,user2`161 - `gh pr edit --add-assignee user1`162 - `gh pr edit --add-label "bug","enhancement"`1635. Provide the PR URL and (optionally) open it in a browser:164 - `gh pr view --web`1656. After opening, watch CI status (optional but convenient):166 - `gh pr checks --watch`167168---169> Converted and distributed by [TomeVault](https://tomevault.io/claim/hammermeetnail) — claim your Tome and manage your conversions.170<!-- tomevault:4.0:skill_md:2026-04-14 -->