Promptheus/rules53 rule sets · CC0Promptheus hub ↗

Workflow · Git 2.55 · Conventional Commits 1.0.0 · commitlint 21 · Lefthook 2.1

Commits & Pull Requests

Small, atomic, conventional commits and PRs a reviewer can actually read.

gitcommitsconventional-commitspull-requestsreview

Updated 5 Jul 2026 · CC0

AGENTS.mdrepo root

You author the version-control history: Conventional Commits, atomic diffs, small single-purpose PRs. "Good" means a git log --oneline that reads like a changelog, every commit builds and passes tests on its own, and every PR is reviewable in one sitting.

Stack

  • Git 2.55 (git --version >= 2.55.0). Use native config-based hooks introduced in 2.54 — a hook is keyed by an arbitrary friendly name, with the trigger set separately: [hook "lint"] event = pre-commit; command = …, i.e. hook.<name>.command plus hook.<name>.event (the multi-valued event key, not the name, picks the moment). Or point core.hooksPath at a tracked dir. No wrapper needed for simple gates. Git 3.0 is on the horizon; keep off any removed-in-3.0 behavior (e.g. rely on git switch/git restore, not overloaded git checkout).
  • Conventional Commits 1.0.0 — the message grammar. Every commit conforms.
  • commitlint 21.2 (@commitlint/cli + @commitlint/config-conventional) — enforces the grammar in commit-msg.
  • Hook runner: prefer Lefthook 2.1 (single Go binary, parallel, one lefthook.yml) for JS/poly repos. For a zero-dependency Node repo, Husky 9.1 is acceptable. On a Git-2.54+ team, native core.hooksPath + a tracked .githooks/ dir replaces both — pick one and only one.
  • lint-staged 17 — run formatters/linters only on staged files in pre-commit.
  • GitHub CLI gh 2.96 — create/review PRs from the terminal (gh pr create, gh pr diff, gh pr checks).
  • Release automation (pick one, do not mix): release-please 17 or semantic-release 25 for fully commit-driven releases; @changesets/cli 2 for monorepos needing intent files. All three consume Conventional Commits.
  • Secret scanner: gitleaks 8.30 in pre-commit and CI. Use the current gitleaks git interface — detect/protect were deprecated and hidden in v8.19.

Project conventions

  • Config lives at repo root, tracked: commitlint.config.js (or .mjs), lefthook.yml (or .husky/), .gitleaks.toml, .gitignore, .gitattributes, .github/pull_request_template.md, CODEOWNERS.
  • Minimal commitlint.config.js:
    export default {
      extends: ['@commitlint/config-conventional'],
      rules: { 'scope-enum': [2, 'always', ['api', 'ui', 'auth', 'db', 'ci', 'deps']] },
    };
    
  • Minimal lefthook.yml:
    pre-commit:
      parallel: true
      commands:
        lint:    { glob: '*.{ts,tsx,js}', run: 'npx lint-staged' }
        secrets: { run: 'gitleaks git --pre-commit --staged --redact --no-banner' }
    commit-msg:
      commands:
        lint: { run: 'npx commitlint --edit {1}' }
    
  • .gitignore MUST cover .env* (except .env.example), node_modules/, dist/, build/, coverage/, *.log, .DS_Store, IDE dirs. Never negate-ignore a real secret file.
  • .gitattributes: set * text=auto eol=lf to normalize line endings; mark generated files linguist-generated and lockfiles merge=binary if churn is a problem.
  • Commit scopes are a small closed set (enforced by scope-enum), lowercase, matching your top-level modules — not arbitrary free text.

Commit format

Grammar: type(scope): summary, optional body, optional footer.

feat(auth): add refresh-token rotation

Access tokens now rotate on every refresh so a leaked token is
valid for at most 15 minutes. Old tokens are revoked server-side.

Closes #482
  • Types (only these): feat (new capability, → MINOR), fix (bug patch, → PATCH), refactor (behavior-preserving code change), perf (faster, no behavior change), style (whitespace/formatting only, no code-meaning change), docs, test, build (build system/deps), ci (pipeline config), chore (housekeeping, no src/test change). This is the config-conventional set; do not invent types (update, misc, wip are banned).
  • Scope: optional, one word from the enum, in parens. Omit rather than guess.
  • Summary: imperative mood ("add", not "added"/"adds"), lowercase after the colon, no trailing period, <= 72 chars total header. It completes the sentence "This commit will ...".
  • Body: optional, separated by a blank line, wrapped at 72 cols. Explain why and any non-obvious tradeoff — never restate the diff. The diff shows what.
  • Footer: BREAKING CHANGE: <desc> (or ! after type/scope, e.g. feat(api)!:) → MAJOR bump; issue refs Closes #123, Refs #45; Co-authored-by: for pairs. Per CC 1.0.0 a ! alone signals the break and the BREAKING CHANGE: footer MAY be omitted (the subject describes it). Team convention here: still write the footer to spell out the migration path.
  • One header line == one logical change. If your summary needs "and", split the commit.

Atomicity

  • One logical change per commit. A commit must build and pass tests in isolation — a bisect landing on it is never broken.
  • Stage selectively with git add -p (hunk-level) or git add <path>; never blind git add -A when you touched unrelated things.
  • Keep refactors mechanical and separate: never mix a refactor (rename/move) with a feat/fix in the same commit — reviewers can't tell what actually changed.
  • No wip, fixup here, misc, stuff, or "address review" as final messages. Iterate locally with git commit --fixup <sha>, then collapse before pushing:
    git commit --fixup=abc123
    git rebase -i --autosquash origin/main
    
  • Formatting-only churn goes in its own style/chore commit (or better, is auto-applied by the formatter hook so it never appears in a feature diff).
  • Lockfile/dependency bumps ride in a build(deps): commit, separate from feature code.

Branches

  • Never commit on main/master. Branch first: git switch -c feat/short-desc.
  • Name type/kebab-description, matching commit types: feat/user-avatar-upload, fix/null-cart-total, chore/bump-vitest. Optionally prefix an issue: feat/482-token-rotation.
  • Short-lived: rebase onto main daily (git fetch && git rebase origin/main) to keep the diff small and conflict-free. Prefer rebase over merge for feature branches — linear history bisects cleanly.
  • Delete the branch after merge (gh pr merge --squash --delete-branch).

Pull requests

  • Small and single-purpose: target < ~400 changed lines. If it's bigger, split into stacked PRs (base of PR 2 = branch of PR 1).
  • Open as draft while iterating; mark ready only when CI is green and you've self-reviewed.
  • Self-review the full diff before requesting review: gh pr diff — look for stray debug logs, commented code, unrelated reformatting, TODOs, and accidental file additions.
  • Description states: context/why, what changed, how tested (commands run + result), and screenshots for UI. Link the issue with Closes #.
    gh pr create --fill --draft \
      --title 'feat(auth): rotate refresh tokens' \
      --body 'Why: leaked tokens stayed valid 30d. How tested: `npm test`, manual refresh loop.'
    
  • No unrelated reformatting. If the formatter wants to touch lines you didn't change, that belongs in a separate chore(format): PR, not buried in a feature diff.
  • Squash-merge so main gets one Conventional Commit per PR — set the PR title to a valid conventional message (CI lints it). Merge commits and rebase-merge are fine only if every commit is already conventional and atomic.
  • Watch CI, don't merge red: gh pr checks --watch.

Hard rules

  • Never git push --force a shared branch (main, develop, anything others branch from). On your own feature branch use git push --force-with-lease only — it refuses if someone else pushed.
  • Never commit secrets: .env, API keys, tokens, private keys, *.pem, connection strings. gitleaks git --pre-commit --staged must pass before every commit. If a secret was committed, treat it as leaked: rotate it, then purge history (git filter-repo) — deleting the file in a new commit does not remove it.
  • Never commit build output or generated artifacts (dist/, build/, .next/, coverage/) — they belong in .gitignore, not history.
  • Never git commit --no-verify / -n to skip hooks, and never git rebase --skip past a failing gate. Fix the cause.
  • Never rewrite public history. rebase -i/--amend only on commits you have not pushed to a shared branch.
  • Never force a merge to bypass required checks. No --admin merge to dodge failing CI.
  • Sign commits: enable git config commit.gpgsign true with an SSH or GPG signing key so the "Verified" badge is real.

Testing

  • Enforce, don't hope. Hooks are the contract:
    • commit-msg: commitlint --edit rejects a malformed message before it exists.
    • pre-commit: lint-staged runs typecheck/lint/format + gitleaks on staged files only (fast, sub-second on small diffs).
    • pre-push: run the fast unit suite (vitest run --changed or equivalent) so you never push a red branch.
  • CI is the backstop, not the first line: it re-lints the commit/PR title, re-runs gitleaks, and runs the full test suite. Required status checks block merge.
  • After scaffolding hooks, verify they fire: attempt a bad commit (git commit -m "bad message") and a staged fake secret, and confirm both are rejected. A hook that silently no-ops is worse than none.
  • Test the release config on a scratch branch before trusting it on main: confirm a feat: bumps MINOR and fix: bumps PATCH in the dry-run output.

Security

  • gitleaks (or GitHub secret scanning + push protection) gates every commit and every push. Enable push protection on the remote so a secret can't reach GitHub even if a local hook is bypassed.
  • Commit signing enforced by branch protection (Require signed commits). Configure once: git config --global gpg.format ssh + git config --global user.signingkey <key>.
  • Branch protection on main: require PR review, passing status checks, linear history, signed commits, and no force-push / no deletion.
  • Pin GitHub Actions to a commit SHA, not a floating tag (uses: actions/checkout@<sha>), so a compromised tag can't inject code into your CI.
  • Review the diff, not just filenames, before staging — a copy-pasted config or fixture is the most common way a live credential slips in.
  • Least-privilege CI tokens: a permissions: block scoped per job; never a repo-wide write-all.

Do

  • Run git diff --staged and read it fully before every git commit.
  • Write the body when the why isn't obvious from the summary; skip it when the change is self-evident.
  • Rebase your feature branch onto main to resolve conflicts before opening/updating a PR.
  • Use git switch/git restore (Git 2.23+) over the overloaded git checkout.
  • Reference the issue in the footer (Closes #) so the tracker closes on merge.
  • Keep the PR description's "how tested" honest — paste the actual command and its result.
  • Use --force-with-lease (never bare --force) when you must rewrite your own branch.

Avoid

  • Vague/banned summaries: fix: bug, update code, misc changes, wip, final, asdf. Every summary names the actual change.
  • Past-tense or capitalized-with-period headers (Fixed the login.) — use imperative, lowercase, no period.
  • git add -A / git add . when your working tree has unrelated edits — stage hunks with add -p.
  • Mega-PRs mixing refactor + feature + formatting. Split them; reviewers can't verify a 2000-line diff.
  • Merge commits from main into your feature branch to "update" it (git merge main) — it pollutes history; rebase instead.
  • --no-verify to "just get it in". If the hook is wrong, fix the hook.
  • Skipping the body on a breaking change — every BREAKING CHANGE/! needs migration notes in the footer.
  • Committing lockfiles and feature code together, or bumping deps inside a feature commit — separate build(deps):.
  • Husky's deprecated pre-v9 shell shims (sourcing husky.sh) — v9 uses plain scripts in .husky/.

When you code

  • Propose small, reviewable commits with correct Conventional Commit messages. When a change grows, stop and split it into a commit sequence rather than one blob.
  • Before committing: run typecheck + lint + the relevant tests; stage only the files that belong to this logical change; read the staged diff.
  • Choose the type deliberately — is this feat, fix, or refactor? The type drives the version bump; getting it wrong ships a wrong release.
  • Before opening a PR: rebase on main, ensure green local checks, self-review gh pr diff, write a description with why + how-tested.
  • Ask before: force-pushing anything, rewriting shared history, changing the branch/PR strategy, merging with failing checks, or committing a file that looks generated or secret-bearing. When unsure whether a change is one commit or several, default to several.

Drop it in your repo

Save these rules as AGENTS.md, CLAUDE.md, .cursorrules, .windsurfrules or .github/copilot-instructions.md — your agent instantly codes to the same standard on Git 2.55 · Conventional Commits 1.0.0 · commitlint 21 · Lefthook 2.1.

Back to top ↑