Conventional Commits, short-lived branches, review-gated merges. Agents never commit or push — the user does.
- Conventional Commits config:
commitlint.config.mjs - Branch protection:
mainis reviewed; no direct push. - Repo host: GitHub (
bymaxone/nest-auth-example).
Before opening or closing a piece of work, naming a branch, writing a commit message, preparing a PR.
Never run git commit or git push. Prepare the commands, output them for the user, and wait for explicit approval before any state change. The user reviews each change and commits manually.
If an agent is asked to "commit" or "push" directly:
- Confirm the user explicitly authorized it (not just implied).
- Still prefer to surface the intended command for one-shot approval.
- Never bypass hooks (
--no-verify,--no-gpg-sign) unless the user has said so in durable instructions.
This is the single most load-bearing rule in this doc. Do not relax.
<type>/<short-slug>
Types mirror commit types:
feat/…— new feature (feat/mfa-recovery-codes-modal)fix/…— bug fix (fix/login-double-submit)refactor/…,docs/…,chore/…,test/…,perf/…,ci/…
Rules:
- Lower-case, kebab-case slug.
- Include the scope when it matters (
feat/web/invite-form,fix/api/rate-limit-header). - No personal prefixes (
max/…) — anyone should be able to pick up the branch. - Under ~60 chars total.
Conventional Commits format:
<type>(<scope>): <subject>
<optional body>
<optional footer>
type:feat|fix|docs|chore|refactor|test|perf|ci|build.scope: workspace or area —api,web,infra,docs,deps,release,auth,prisma,redis, ….subject: lower-case, imperative mood, no trailing period, ≤ 72 chars.body: wrap at 100; explain why. Link issues (Refs #123,Closes #123).footer:BREAKING CHANGE: …when relevant;Co-Authored-By: …for pairing.
Examples:
feat(web): add MFA recovery codes modal
Surfaces the library's recovery codes exactly once, gated behind the
confirmation step. Adds a print-friendly layout and copy-to-clipboard.
Closes #42
fix(api): restore X-Request-Id propagation on 4xx responses
The exception filter swallowed the header; re-inject it before returning.
Regression introduced in #31.
Rules:
- Small commits, one idea each. Ten files change because a refactor renamed something → one commit. Ten features → ten commits (or PRs).
- Never commit generated files unless the repo explicitly includes them (
prisma/migrations, shadcn-copied components). - Never commit secrets.
.env, private keys, credentials..gitignore+ human review both enforce. - Amending is fine on an unpublished branch; avoid once pushed. Create a follow-up commit instead.
- Never skip hooks with
--no-verify. Fix the hook failure.
- Title mirrors the top commit subject (
feat(web): add MFA recovery codes modal). - Description uses the template:
## Summary
- Bullet 1
- Bullet 2
## Test plan
- [ ] Ran `pnpm lint && pnpm typecheck && pnpm test`
- [ ] Exercised the happy path in the browser
- [ ] Verified the regression case in docs/FEATURES.md §N
## Screenshots / recordings
<attach>
## Risks & follow-ups
- …- Target: < 400 lines of diff, under 8 files.
- If larger: split. Chain PRs with
Depends on #Nin the description when the split cannot be independent. - Reference PRs (a whole feature demonstrated end to end) may exceed the limit; justify in the description.
- Must go green before review is requested. Don't burn reviewer time on a red branch.
- Re-run flaky jobs once. If it flakes twice, investigate — do not merge on a flake.
See pr-review.md for reviewer expectations.
- Squash and merge is the default — keeps
mainlinear and each PR one commit. - Preserve the Conventional Commits subject on the squash.
- Rebase before merge if
mainhas moved; do not use the "merge commit" button.
- Branch from
mainwithfix/<short-slug>. - Minimum diff; no opportunistic refactors.
- Two approvals encouraged for auth-path fixes.
- Tag
v<x.y.z>after merge if the project is publishing releases.
- Use
git revert <sha>— never force-push over the commit. - Revert commit keeps the Conventional Commits format:
revert: <original subject>. - Document the incident in the revert PR body; link follow-up issue.
Not wired yet. When releases start:
- Semver tags
v1.0.0,v1.1.0, … docs/RELEASES.mdnotes the@bymax-one/nest-authversion pinned.- GitHub Release body mirrors CHANGELOG entry.
- Never force-push
mainor any shared branch. - Force-push on a personal, not-yet-reviewed branch is fine (
git push --force-with-lease). --force-with-leasealways — never--force. Prevents overwriting a teammate's push you didn't know about.
- Never add
.env, credentials, private keys. Pre-commit should block; human also blocks. - Large binary assets (
> 1 MBimage, recordings) — discuss before committing. Consider an external host or Git LFS. - If a secret slipped in:
git filter-repo/ BFG, then rotate the secret. Removing it from history is not sufficient.
| Action | Allowed for agent? |
|---|---|
| Propose a commit message | ✅ |
Run git status / git diff |
✅ |
Run git log, git show |
✅ |
Run git add |
Only when the user has authorized for the current task |
Run git commit |
❌ (user does this) |
Run git push |
❌ (user does this) |
Run git reset --hard |
❌ without explicit OK |
Run git rebase -i |
❌ — requires interactive input |
--no-verify, --no-gpg-sign |
❌ unless explicit user request |
- Merging your own PR — review gate exists for a reason.
- Force-pushing a shared branch to "clean up commits" — destroys reviewers' anchors.
- Amending a pushed commit — bypasses review, breaks others' local branches.
- Long-lived feature branches — merge hell. Rebase daily or split the work.
- Committing generated files — Prisma client,
.next/,dist/. feat: wipcommits landing onmain— squash merge should catch, but don't let them through in the first place.- Agent running
git commitautomatically — top of this doc; re-read.
- Conventional Commits: https://www.conventionalcommits.org
commitlint.config.mjsin this repo- GitHub flow: https://docs.github.com/get-started/using-github/github-flow
- pr-review.md
- agent-handoff.md