Skip to content
Back to skills

Git Conventions

BSecurity

Manage checkout hygiene, branches, commits, and history. Load when starting or finishing checkout work, cleaning up worktrees or branches, creating or naming branches, grouping changes, or writing commits; pair with attribute-ai-commits.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
developmentgoshellbashtestinggitbackendsecurity

Works with

  • terminal
  • cli

Security analysis

B75/100
  • criticalAccesses sensitive system or user directories

Pro scans all 3 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add a-novel-kit/stack --skill git-conventions --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Git Conventions?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Git Conventions
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/a-novel-kit-git-conventions/badge)](https://www.skillsdirectory.com/skills/a-novel-kit-git-conventions)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: git-conventions
description: >
  Manage checkout hygiene, branches, commits, and history. Load when starting or finishing checkout
  work, cleaning up worktrees or branches, creating or naming branches, grouping changes, or writing
  commits; pair with attribute-ai-commits.
---

# Git Conventions

This skill governs workspace state, branch naming, and commit messages across all A-Novel backend
services. Every branch and commit produced by an agent follows these conventions exactly — they
drive automation (Renovate, CI tagging, changelogs) and signal intent to reviewers at a glance.

---

## Load the Git procedure for this operation

- Before starting or finishing checkout work, or cleaning anything up, read
  [workspace hygiene](references/workspace.md). Touch only what this session created, cleanup
  included, unless told otherwise; preserve other contributors' work and use an isolated checkout
  for sustained work.
- Before naming a branch, grouping changes, or writing, amending, or reviewing a commit message,
  read [commit conventions](references/commits.md).
  Use `attribute-ai-commits` for material AI contributions.
- For branch names and history changes, apply the rules below.
- For publication, PR descriptions, metadata, readiness, and final handoff, load
  [open-pull-request](../open-pull-request/SKILL.md); it owns those mechanics.

`develop-feature` owns stage timing. Existing authorization carries forward; neither a clean
tree nor a local commit alone authorizes publication, merging, or changes to another person's work.

## Branch Naming

```
<type>/<area>/<short-description>
```

- **type**: same vocabulary as commit types (`feat`, `fix`, `refactor`, `chore`, `ci`, `docs`)
- **area**: the layer or subsystem being changed — use the [commit scope](references/commits.md#scopes)
- **short-description**: kebab-case, 2–5 words, describes what the branch achieves

### Examples

```
feat/proto/add-key-revoke-rpc
feat/handlers/grpc-jwk-revoke
fix/dao/search-returns-deleted-keys
refactor/core/extract-key-rotation-logic
chore/skills/feature-workflow
docs/pkg/update-client-examples
```

Branch names are lowercase kebab-case only. No underscores and no slashes inside a segment. A working
branch carries no version number. Only the two kinds of long-lived line do:

- `release/vX.Y` holds a released minor and its backports.
- `v<N>` develops major N until it replaces `master`. The line it replaces keeps its own `v<N>` name,
  and the `retired-lines` ruleset freezes it once `master` releases a later major.

---

## Commit Workflow

```bash
# 1. Stage only the files for this logical unit
git add internal/dao/pg.jwkRevoke.go internal/dao/pg.jwkRevoke_test.go

# 2. Commit with a conventional message — a subject line, nothing more
git commit -m "feat(dao): add soft-delete repository for key revocation"

# Only when the body carries what the diff cannot (HEREDOC for multi-line):
git commit -m "$(cat <<'EOF'
fix(dao): lock the key row before revoking

Concurrent revokes both read the key as ACTIVE and wrote two audit
entries; SELECT ... FOR UPDATE is what serialises them.
EOF
)"
```

### Rules

- **A commit is its subject line.** Add a body only when it says something the subject and the
  diff cannot — see [Body](references/commits.md#body).
- **One signed commit per pull request.** Each signature costs the developer a key touch, and the
  squash merge lands one commit anyway. While you work, commit checkpoints unsigned
  (`git -c commit.gpgsign=false commit`); before the first push, squash them into one signed
  commit (`git reset --soft <base>`, then commit). Each later review round adds one commit. The
  branch is the logical unit: `implement-feature` cuts one per layer.
- **Stage explicit paths — never `git add -A` / `git add .`.** These checkouts keep sibling
  worktrees under an untracked `tmp/` (see [Before you start](references/workspace.md#before-you-start)). A blanket add
  stages each `tmp/wt-*` as an embedded-repo **gitlink** — a bogus submodule ref that rides your
  commit onto `master` if it slips through review. `git show --stat HEAD` betrays it as a
  `tmp/wt-… | 1 +` line. Name the files the logical unit touched; the `git add …` in this workflow
  is a list on purpose.
- **Generated files belong in the same commit as the change that required them.** Proto Go bindings
  (`internal/models/proto/gen/`) and mocks (`internal/handlers/mocks/`, `internal/core/mocks/`)
  never get their own commit — stage them with the `.proto` or interface change that required
  `pnpm generate:go`.
- **Never commit secrets.** .env files, APP_MASTER_KEY values, real credentials.
- **Never skip hooks** (`--no-verify`) unless explicitly asked.
- **Never amend a pushed commit.** Create a new commit instead.
- **Mid-rebase, resolve, `git add`, then `git rebase --continue`; never `git commit --amend`.**
  `HEAD` is then the previously replayed commit, so an amend folds the resolution into it and
  replaces its message. To undo it, `git reset --soft` to that commit from the reflog, commit the
  difference, and compare `HEAD^{tree}` with the amended tree to prove nothing else moved.
- **Push Conventional subjects only.** The `commit-messages` ruleset rejects any other commit, on
  every branch. Never push `fixup!`, `squash!`, `amend!` or work-in-progress commits: fold them in
  before the first push with `git rebase --autosquash <base>`, or write the follow-up as its own
  Conventional commit.
- **Update a branch by rebasing onto its base, never by merging the base in.** A merge writes a
  `Merge branch …` commit, which the ruleset rejects, and so does GitHub's default "Update branch".
  Its "Update with rebase" rewrites the commits unsigned, which the required-signatures rule rejects:
  the pull request can no longer enter the merge queue. Run `git rebase origin/master` and
  `git push --force-with-lease` instead, which signs them. A rebase replays the branch's commits
  unchanged, so it is not the amend the rule above forbids. A pull request needs no update to enter
  the queue, which tests it on the latest base.
- **Revert with git, not GitHub's Revert button**, whose `Revert "…"` subject the ruleset rejects:
  `git revert --no-commit <sha>`, then
  `git commit -m "revert: <reverted subject>" -m "This reverts commit <sha>."`.
- **Never push to `master`/`main` — not force-push, not a plain push — without explicit consent.**
  This is the one git action that is never safe by default. Most contributors lack the access, so
  the guardrail is already enforced for them; on an admin account it is _yours_ to hold, because you
  have the rights to bypass it. Publish feature branches at the stage authorized by
  `develop-feature`; see [Branch and PR freedom](#branch-and-pr-freedom).

---

## Git LFS

GitHub keeps every LFS object pushed to it. Deleting the file or rewriting history frees no
storage; only deleting the repository or a GitHub Support request does. Squash LFS iterations into
one commit before pushing, so only the final version uploads.

Check `git lfs version` before committing under a `filter=lfs` path. Without git-lfs, git ignores
the attribute and silently commits the file as an ordinary blob. A checkout without git-lfs's
pre-push hook pushes pointers whose content never reaches the remote. Run
`git lfs push origin <branch>` before `git push` there; it skips objects the remote already holds.

---

## Reuse the GitHub SSH Connection

The one goal: a chain of git operations must not ask a FIDO security key for presence again and
again. Every new SSH connection to GitHub needs a touch. So when you start a chain of network git
operations (push, fetch, pull, clone, `ls-remote`), reuse the shared connection if it is up, or open
it:

```bash
ssh -O check git@github.com 2>/dev/null || ssh -o ControlPersist=1h -fN git@github.com
```

Every operation in the chain rides that connection, which closes once it sits unused for an hour.
Do nothing else around the key: never probe, list, poll or wait on it or on the connection. A probe
opens the device and competes with ssh-keygen for it. When a connection or a signature fails, stop
and tell the developer.

That needs `~/.ssh/config` to multiplex github.com, which the developer guide's GitHub page sets up:
`ssh -G git@github.com` then prints a `controlpath` line. `a-novel core sync` checks for that same
connection and reuses it, so the two never clash. Without the config, ask the developer to add the
guide's block. Meanwhile, prefix each network git command with the socket the CLI opens itself:

```bash
GIT_SSH_COMMAND='ssh -o ControlMaster=auto -o ControlPath=~/.ssh/a-novel-%C -o ControlPersist=1h' git push
```

Prefix it rather than export it: an exported `GIT_SSH_COMMAND` makes the CLI skip its own sharing,
which keeps a sync under GitHub's limit of ten sessions per connection.

A signature shares nothing: every signed commit or tag asks for its own touch, and a rebase signs
every commit it replays again. So keep each pull request to one signed commit, as the rules above
say, and put every signing step in one script: the fetch, a rebase only when
[workspace hygiene](references/workspace.md#before-you-start) calls for one, the commit and the
push. Master then cannot move between your check and the signatures. Check the script first,
because one that fails after the developer approved it costs a second approval: run `bash -n` on
it, and its unsigned steps (the fetch, `git merge-tree`, `git add -n`) in your own shell. Give any
rebase in it `GIT_EDITOR=true`. Run it where the developer sees its terminal, and warn them before
it starts, because git hides ssh-keygen's "Confirm user presence" prompt until it fails. Once the
call returns, read the terminal with a wait of a few seconds at most. Commits nobody asked to sign
must not reach the key: scratch repositories and test suites that commit inherit `commit.gpgsign`,
so run them with `-c commit.gpgsign=false` or `GIT_CONFIG_GLOBAL=/dev/null`.

Git LFS's pre-push hook opens an SSH master of its own, so a plain push from a checkout that carries
it asks for a second touch. The stack and uikit carry it, as does any checkout where this succeeds:

```bash
grep -q lfs "$(git rev-parse --git-common-dir)/hooks/pre-push"
```

Turn LFS's multiplexing off in every such checkout, so LFS rides the shared connection:

```bash
git -c lfs.ssh.automultiplex=false push
```

If the push then stalls on LFS lock verification, add `-c lfs.locksverify=false`.

---

## Branch and PR Freedom

Follow [develop-feature](../develop-feature/SKILL.md): during local drafting, keep branches and
checkpoint commits local until the developer agrees on the direction, unless they explicitly
requested publication. A local commit is not itself a reason to push.

After that agreement, pushing a feature branch and opening a PR within the authorized scope need
no repeated permission. Use `open-pull-request` for the checks appropriate to the stage. Clear,
small tasks can take the shorter path defined by `develop-feature`.

PRs can receive review while their planning issues are still being discussed. Preserve review
history, keep scope and deferred coverage visible, and follow the normal history-rewrite rules.
Mark a PR ready as soon as anyone other than you should look at it, as `open-pull-request` defines;
issue scope approval, testing and cleanup gate the merge. Never treat a draft as exempt from
repository safeguards or push directly to `master`/`main` without explicit consent.

---

## Pull Request Description

Use [open-pull-request](../open-pull-request/SKILL.md) for the authoritative title, body,
issue linkage, validation, and handoff requirements. Keep the Conventional-Commits title under
70 characters, state breaking changes, and include only affected layers and meaningful verification.

Files in this skill

  • SKILL.md11.6 KB
  • references/commits.md6.1 KB
  • references/workspace.md8.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.