AgentSkillsCN

git-practices

分支、提交与拉取请求的 Git 规范。每当您创建分支、撰写提交信息,或发起 PR 时,均可加载此技能。无论是提交、分支、拉取请求,还是合并操作,皆可触发此技能。

SKILL.md
--- frontmatter
name: git-practices
description: Git conventions for branches, commits, and pull requests. Load this skill whenever creating branches, writing commit messages, or opening PRs. Trigger on any git workflow operation — commits, branches, pull requests, or merge operations.

Git Practices

These are the conventions for all git operations in this project. Every branch, commit, and pull request follows these rules without exception.

Branch Naming

Format: <type>/<ticket-id>

Types:

  • feat — New feature or capability
  • fix — Bug fix
  • refactor — Code restructuring without behavior change
  • chore — Maintenance, tooling, config, dependencies

Examples:

code
feat/CTR-12
fix/CTR-45
refactor/CTR-78
chore/CTR-99

Rules:

  • Ticket ID matches the project tracker (JIRA, Linear, etc.)
  • Lowercase everything
  • No descriptions in branch name — the ticket ID is the reference
  • One branch per ticket — don't combine work from multiple tickets

Commit Messages

Format:

code
<type>(<scope>): <short message> [<ticket-id>]

<description>

Line 1 (subject):

  • type — Same as branch types, plus: docs (documentation), test (tests only)
  • scope — One word indicating the module or layer affected (e.g., auth, api, database, ui, config)
  • short message — Imperative mood, lowercase, no period, under 50 characters after the scope
  • ticket-id — In square brackets at the end

Line 2: Blank line (always)

Line 3+ (description):

  • What was done and why, in plain sentences
  • Reference related files or decisions if helpful
  • Keep it concise — enough context for someone reading git log, not a full essay

Examples:

code
feat(document): implement search by text [CTR-12]

Added full-text search to the document module using the existing
search index. Follows the pattern established in the user search
feature.
code
fix(auth): prevent token refresh race condition [CTR-45]

Two concurrent requests could both trigger a token refresh,
causing one to fail with an invalid token error. Added a mutex
to ensure only one refresh happens at a time.
code
refactor(api): extract validation middleware [CTR-78]

Moved request validation logic from individual route handlers
into a shared middleware. Reduces duplication across 12 endpoints.
code
chore(config): update dependencies to latest stable [CTR-99]

Bumped all non-major dependencies. No breaking changes detected.
code
test(billing): add edge cases for prorated charges [CTR-33]

Covers: mid-cycle upgrade, downgrade on last day, zero-usage
month, and negative balance scenarios.
code
docs(api): update endpoint documentation for v2 routes [CTR-55]

Reflects the new response format introduced in the API v2 migration.

Rules:

  • Subject line is mandatory, description is mandatory for anything beyond trivial changes
  • No co-author or AI attribution lines
  • One logical change per commit — if you have to use "and" in the subject, consider splitting
  • The type in the commit matches the branch type (a feat/ branch should have feat() commits, with occasional test() or docs() commits for supporting work)

Pull Requests

Title format: Same as commit subject line:

code
<type>(<scope>): <short message> [<ticket-id>]

Body format:

markdown
## Summary
[2-4 sentences: what this PR does and why. Written for a reviewer
who hasn't read the ticket — they should understand the change
from this summary alone.]

## Changes
- [Concrete change 1 — what file/module and what was done]
- [Concrete change 2]
- [Concrete change 3]

## Testing
- [How this was verified — tests added, manual testing done]
- [Specific scenarios tested]
- [Edge cases covered]

## Ticket
[TICKET-ID](link to ticket if available)

Examples:

Title: feat(document): implement search by text [CTR-12]

Body:

markdown
## Summary
Adds full-text search to the document module, allowing users to
search documents by content rather than just title. Uses the existing
search index infrastructure established in user search.

## Changes
- Added search endpoint `GET /api/documents/search` with query parameter
- Created `DocumentSearchService` following the `UserSearchService` pattern
- Added search result highlighting in the response
- Added 8 unit tests covering: exact match, partial match, no results,
  special characters, pagination, and sorting

## Testing
- Unit tests pass: `npm run test -- documents`
- Manual testing: verified search returns expected results with 500+ documents
- Tested edge cases: empty query, very long query, special characters

## Ticket
[CTR-12](https://tracker.example.com/CTR-12)

Rules:

  • PR title follows the exact same format as commit messages
  • Summary explains the WHY, changes list the WHAT
  • Testing section is mandatory — no PRs without describing how it was verified
  • One ticket per PR — don't bundle unrelated work
  • Link the ticket if possible
  • No screenshots unless the change is visual (then they're encouraged)

Tool

Pull requests are created with the GitHub CLI (gh):

bash
gh pr create --title "<title>" --body "<body>"

For drafts:

bash
gh pr create --draft --title "<title>" --body "<body>"

Determining the Type

When in doubt about the commit/branch type:

If the change...Type
Adds a new user-facing capabilityfeat
Fixes something that was brokenfix
Changes code structure without changing behaviorrefactor
Updates build, CI, deps, config, toolingchore
Only adds or updates documentationdocs
Only adds or updates teststest

If a commit includes both a feature and its tests, use feat — the tests support the feature. If a commit is purely adding missing tests for existing code, use test.

Determining the Scope

The scope should be a single word identifying the affected module or layer. Use names that match your project's directory structure or domain concepts:

Examples of good scopes:

  • auth, api, database, ui, config, billing, search, notifications
  • user, document, task, project, team

Avoid:

  • Generic scopes: app, code, misc, stuff
  • Multi-word scopes: user-profile (use user or profile)
  • File names as scopes: userService (use user)

Git Worktrees

We use git worktrees instead of switching branches. Each branch lives in its own directory, allowing parallel work across multiple features.

Folder convention:

code
my-repo/                    ← main branch (cloned from GitHub)
my-repo-worktrees/          ← worktree directory (sibling, auto-created)
  feat/CTR-12/              ← one worktree per branch
  fix/CTR-45/

Creating a worktree:

bash
git wt feat/CTR-12

This uses the global alias git wt which:

  • Creates the worktree at ../{repo}-worktrees/{branch}
  • Reuses the branch if it already exists, or creates it with -b

Removing a worktree:

bash
git wtr feat/CTR-12

This uses the global alias git wtr which removes the worktree from ../{repo}-worktrees/{branch}.

Listing worktrees:

bash
git worktree list

Rules:

  • Always create worktrees from the main repo directory (not from inside another worktree)
  • Branch name follows the Branch Naming convention above: <type>/<ticket-id>
  • One worktree per ticket — matches one-branch-per-ticket rule
  • Remove worktrees after the PR is merged to keep the directory clean
  • The main repo directory always stays on the default branch (main/master) — never switch branches there

Backlog Lock

When working with worktrees, multiple sessions may access the backlog simultaneously. A lockfile prevents two worktrees from picking up the same item.

File: docs/backlog.lock

Format:

yaml
# Managed by /next and /pr commands — do not edit manually
locks:
  - item: "S-003"
    feature: "FEAT-007"
    branch: "feat/CTR-12"
    worktree: "../my-repo-worktrees/feat/CTR-12"
    started: "2026-02-12T14:30:00"

Rules:

  • /next creates a lock entry when picking up a backlog item
  • /pr removes the lock entry after the PR is created
  • /worktree --remove checks for stale locks and warns
  • Before picking an item, /next checks the lockfile — if the item is locked, it skips to the next
  • The lockfile is committed to the repo so all worktrees can see it
  • If a lock is stale (branch no longer exists, worktree removed), it can be cleaned up