PR workflow
The `PR-A`..`PR-Z`..`PR-AA` letter convention, branch naming, and what to put in a PR title vs body.
PRs authored in this repo follow a strict letter prefix convention. The convention exists so the team and AI agents can refer to PRs unambiguously in conversation ("did PR-C ship?", "rebase PR-G on top of PR-F") without clicking through to GitHub.
Letter prefixes (Excel-column style)
PR-A → PR-B → ... → PR-Z → PR-AA → PR-AB → ... → PR-AZ → PR-BA → ...Each PR gets the next sequential letter in the team's series. To find the next one before opening a PR:
gh pr list --author @me --state all --limit 30 --json title -q '.[].title' \
| grep -oE '^PR-[A-Z]+'The latest letter +1 is yours. For a multi-PR series that's intentionally
ordered (PR-CA1 → PR-CA2 → PR-CB1 → ...), append a digit: PR-CA1, PR-CA2.
The base letter still advances the series; the digit groups related PRs.
PR title format
PR-X: <conventional commit summary>Examples (real PRs from recent history):
PR-BZ: fix(onboarding): keep custom goal chips to a single line
PR-CA1: feat(dev-docs): scaffold @todayai-labs/dev-docs Fumadocs site with deploy pipeline
PR-CA2: feat(dev-docs): migrate 6 canonical docs/*.md into the siteThe conventional-commit part follows the standard format —
type(scope): description. Common types: feat, fix, docs, refactor,
test, chore, ci. Scope is usually a package or domain (onboarding,
dev-docs, auth, admin).
Branch naming
pr-x/<topic>Examples: pr-bz/onboarding-goal-single-line, pr-ca/dev-docs-research,
pr-cb1/toolchain-chapters.
The branch name mirrors the PR letter in lowercase, slash, then the topic.
This lets git checkout tab-completion narrow by PR letter, and lets
origin/pr-cb* glob across a series.
PR body conventions
Each PR's title and body are the canonical record once the PR is squashed
into dev. The squash commit message inherits the PR title (and optionally
body), so be exhaustive.
For omnibus PRs that touch multiple themes, group commits by theme and list them individually in the body so reviewers can spot-check:
## Summary
Touches three subsystems.
### Auth (commits 1-3)
- Switch to host-only session cookie
- ...
### CI (commits 4-5)
- Add visual regression for dialog
- ...
### Docs (commit 6)
- Update local-development guideIf the PR depends on or is stacked on another PR, explicitly link it in the body:
Stacked on top of [PR-CA1 (#731)](https://github.com/todayai-labs/today-platform-web/pull/731).Test plan
PR bodies should include a "Test plan" section listing the gates that ran:
## Test plan
- [x] `pnpm typecheck` — green
- [x] `pnpm test` — 200 files / 2,095 tests, green
- [x] `pnpm --filter @todayai-labs/dev-docs build` — 23 routes generated
- [ ] CI Build / Typecheck / Lint / Format / Unit Tests / Visual Regression
- [ ] Manual review of the rendered docs on Vercel previewThe "Test plan" is the agent's contract that local gates ran before push, and the reviewer's checklist of what to look for. Unchecked items are expected to flip green during CI.
Merging
gh pr merge <n> --auto enables auto-merge — the PR enters the merge queue
once required checks pass. Do not pass --rebase or --squash — the
merge queue rejects strategy overrides with "merge strategy set by merge
queue."
See Merge queue for what happens after auto-merge fires.
Common pitfalls
- Pushing to a queued PR cancels auto-merge. If you push a fixup commit
after enabling auto-merge, run
gh pr merge <n> --autoagain to re-queue. - Branch naming with a different prefix. Branches like
fix-onboarding-bugskip the convention; they're harder to refer to in chat and break tooling that filters bypr-*. Use the letter convention even for hotfixes. - Forgetting the letter. If you open a PR without a letter prefix, edit the title before merging — the squash commit otherwise lands without one and the series is harder to track.
Parallel worktrees
Run multiple branches in parallel via `git worktree add`. What env files to mirror, the cookie-jar caveat, and the future per-worktree hostname plan.
Merge queue
How GitHub's merge queue interacts with `gh pr merge` on this repo. The `UNSTABLE` vs `CLEAN` vs `BLOCKED` states, what cancels auto-merge, and the speculative-branch double-run.