---
name: commit
description: Use when creating git commits in this repo. Enforces Conventional Commits format,
  lowercase-after-colon subject, test-pass pre-gate (pointing to /diagnose on failure),
  AskUserQuestion dropdown confirmation (Commit / Edit / Abort). Pre-commit hook runs
  the full quality gate; commit is blocked on unfixable failures. Never adds Co-Authored-By.
  Repo-local: also references the linked GitLab issue (Refs #N), inferred from context — the
  PRD or issue file the work implements carries the link; the branch name is only a fallback.
roles:
- developer
---

# Commit

Read the staged diff, run the test-pass pre-gate, draft a Conventional Commits message, link the GitLab issue, confirm via dropdown, commit.

This is the turbine repo-local commit skill — it supersedes the global one. Same rules, plus GitLab issue linking.

## Workflow

### 0. Locate staged changes

```bash
git status --short
git diff --cached --stat
git diff --cached
```

If nothing is staged but there are unstaged changes, ask via `AskUserQuestion`:

```yaml
question: "Nothing staged. Stage specific files, stage all, or abort?"
header: "Staging"
options:
  - label: "Stage specific files"
  - label: "Stage all (scoped paths only — never -A)"
  - label: "Abort"
```

**Never `git add -A` / `git add .`** — sweeps in `.env`, credentials, other agents' work. Stage by explicit path.

### 1. Test-pass pre-gate (MANDATORY)

```bash
uv run pytest
```

Read the actual summary. On failures, STOP — don't suggest committing anyway, don't suggest `--no-verify`:

```yaml
question: "Tests failing — commit blocked. How to proceed?"
header: "Test failure"
options:
  - label: "Investigate via /diagnose (Recommended)"
  - label: "Abort commit"
  - label: "Show me the failing tests"
```

### 2. Read the staged diff

Understand the _why_: feature, bug, refactor, what motivated it.

### 3. Draft the message

**Be concise. No verbose prose, no narration, no filler.** If the `writing-clearly-and-concisely` skill is available, load it and apply it — especially "omit needless words" and the AI-puffery list.

Conventional Commits format:

```
<type>(<optional-scope>): <summary>

<optional body>

<optional issue footer>
```

| Type       | Use when                         |
| ---------- | -------------------------------- |
| `feat`     | new feature                      |
| `fix`      | bug fix                          |
| `docs`     | documentation only               |
| `refactor` | refactor without behavior change |
| `test`     | adding/fixing tests              |
| `chore`    | build, config, deps              |
| `perf`     | performance improvement          |
| `ci`       | CI configuration changes         |

**Subject:** 20-72 chars after the prefix · lowercase after the colon (`feat: add X`, NOT `feat: Add X`) · imperative ("add", not "added") · no trailing period · concrete verb + object.

**Body (only when the subject can't carry it):** blank line after subject. Group entries under short headings — `Added`, `Changed`, `Fixed`, `Removed`, `Breaking` — one terse bullet per entry, imperative, no trailing periods. Skip headings when there's only one bullet.

### 3a. Link the GitLab issue (repo-local addition)

Find the issue this commit belongs to by **inferring it from context**, in this priority order. The branch name is the last resort, not the first — a branch can be misnamed, reused, or shared across several issues.

1. **The PRD or issue file this work implements.** When the staged changes deliver work from a PRD under `ai-docs/plans/<topic>/`, that PRD carries a back-reference line written by `/add-issue`, just under its title:

   ```
   **GitLab issue:** https://…/turbine-platform/turbine/-/issues/<iid>
   ```

   The `<iid>` is the trailing path segment — that's your `Refs #<iid>`. Issue files under `ai-docs/plans/<topic>/issues/NNN-*.md` may carry the same line. Match the PRD/issue to the diff by what the changed files actually do, not by guessing.

   ```bash
   # pull the iid from the PRD/issue file's "**GitLab issue:**" line
   grep -hoE 'issues/[0-9]+' <prd-or-issue-file> | grep -oE '[0-9]+' | head -1
   ```

2. **The conversation.** If you created the issue this session (e.g. via `/add-issue`) or the user named it, use that number directly.

3. **The branch name** (fallback only). GitLab's branch-per-issue convention prefixes the iid:

   ```bash
   branch=$(git branch --show-current)
   issue=$(printf '%s' "$branch" | grep -oE '^[0-9]+' || true)
   ```

   Treat this as a corroborating hint. If it disagrees with the PRD/context, trust the context and surface the mismatch in the confirmation preview.

Once you have the iid, add a reference footer as the **last line** of the body:

```
Refs #<iid>
```

Use `Refs`, never `Closes`/`Fixes` — a local commit must not close the issue. Closing is the MR's job (the `/release` skill puts `Closes #N` in the merge request, so the issue moves to Done only on merge). The `Refs #N` line just makes every commit show up in the issue's timeline.

If none of these surface an issue, don't silently skip — **ask the user whether there's a relevant issue** (work can map to a GitLab issue even when no PRD is involved). Add `Refs #<iid>` if they name one; commit without the footer only if they confirm there's none. Never invent a number; the dropdown preview is the user's chance to correct the one you inferred.

Example:

```
feat(orchestration): add pax cron runner

Added:
- pax scheduler adapter that starts a check run and blocks on failure

Refs #41
```

Never narrate session context, MR numbers, or future work.

### 4. Self-review

Subject names a concrete change? Body (if any) is bullets, not paragraphs? No puffery ("comprehensive", "robust", "various improvements")? `Refs #N` present when context (PRD/issue/branch) names an issue? No `Co-Authored-By` (forbidden)?

### 5. Confirm via dropdown

```yaml
question: "Commit with this message?"
header: "Commit"
options:
  - label: "Commit (Recommended)"
    preview: |
      <full message>
  - label: "Edit message"
  - label: "Abort"
```

### 6. Commit

```bash
git commit -m "$(cat <<'EOF'
<type>(<scope>): <summary>

<body>

Refs #<issue>
EOF
)"
```

HEREDOC keeps multi-line bodies formatted. Drop the `Refs` line when context yields no issue.

The pre-commit hook (`.claude/hooks/pre-commit-lint.sh`) auto-runs:

```
uv run ruff check --fix
uv run ruff format
uv run ty check
uv run pytest
uv run complexipy src/ -mx 10
uv run tach check
```

- **Pass** → commit created
- **Ruff autofixed** → hook stages fixes, commit captures them
- **Unfixable** → hook blocks; fix manually

### 7. On hook failure

Never `--no-verify`. Investigate:

- ruff → fix code (no `# noqa`)
- ty → fix types (no `# type: ignore`)
- pytest → `/diagnose`; 3 failed fixes = stop
- complexipy / tach → refactor, or ask before accepting a 10-15 complexity function

After fix: re-stage, create a NEW commit. The failed commit didn't happen — `--amend` is wrong here.

## Multi-Commit Sessions

For staged changes spanning unrelated concerns:

1. Unstage scoped paths: `git restore --staged <paths>` (never `git reset`)
2. Stage + commit one concern at a time
3. Use `AskUserQuestion` if the split is unclear.

## Forbidden

- `--no-verify`, `--no-gpg-sign`, any hook-bypass — not even "just this once"
- `git add -A` / `.` / `--all`
- `Co-Authored-By` lines
- `Closes`/`Fixes` keywords in a commit (closing belongs in the MR, via `/release`)
- `--amend` on a commit that failed the pre-commit hook
- force-push or destructive git (`reset --hard`, `clean -f`, `branch -D`) without explicit human-partner approval
- sentence-case / PascalCase / Start-Case after the colon
