Create PR
Preconditions
- Be on a feature branch (not
main). - Have a working
ghCLI auth session (gh auth status). - Know whether
make e2eis acceptable to run right now (it is destructive in this repo). - Decide whether you want to rebase/merge on top of the latest
origin/mainbefore 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.
- If branch is
- Sync refs and understand delta vs
main:git fetch origingit log --oneline --decorate origin/main..HEADgit diff --stat origin/main...HEAD
- (Optional but recommended) Rebase/merge onto latest
origin/main:- Prefer
git rebase origin/mainfor 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).
- Prefer
- Confirm what will be included:
git status --porcelaingit diff
2) Decide which checks to run (based on relevant file changes)
- Compute changed files vs
origin/main:git fetch originchanged=$(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/orweb/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/)
- Go-relevant (run coverage/lint/backend tests): any changes to
- 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):
- Compute baseline coverage on
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:
make 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 Go-relevant and JS/asset-relevant:
- If E2E-relevant: confirm with the user, then run e2e:
- Explain that
make e2eis destructive (resets volumes/reseeds) and can take a while. - Run:
make e2e
- Explain that
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:
git status --porcelain
- 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 -pto 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 --statgit diff --cached
- Confirm nothing important was accidentally left out:
git status --porcelainshould 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.
- Search staged diff for common secret markers (examples):
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:
git push -u origin HEAD
- If you rebased after pushing:
- Push safely:
git push --force-with-lease
- Push safely:
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,user2gh pr edit --add-assignee user1gh pr edit --add-label "bug","enhancement"
- Provide the PR URL and (optionally) open it in a browser:
gh pr view --web
- After opening, watch CI status (optional but convenient):
gh pr checks --watch
