Commit
Planner Entry
Stage and commit changes in the primary conversation after the task-defined
implementation checks pass. Do not add broad post-commit /verify before push
by default. Preserve the hook receipt as normal commit evidence.
Create a git commit following this project's Conventional Commits convention. These messages are used by git-cliff (cliff.toml) to auto-generate changelogs and release notes. PRs are squash-merged, so the PR title becomes the commit on main — CI validates it via pr-title.yml.
Available skills
/pr-fixup — Use after the PR opens only for CI or actionable reviewer
findings.
Format
type: lowercase description
Allowed Types
| Type |
Use for |
In changelog? |
feat |
New features |
Yes (Features) |
fix |
Bug fixes |
Yes (Bug Fixes) |
perf |
Performance improvements |
Yes (Performance) |
refactor |
Code refactoring |
Yes (Refactoring) |
docs |
Documentation changes |
Yes (Documentation) |
chore |
Maintenance, deps, configs |
No |
ci |
CI/CD changes |
No |
test |
Test-only changes |
No |
Rules
- Subject must start with a lowercase letter
- Scope is optional:
feat(ui): add dialog is valid
- Include PR/issue number when relevant:
feat: add release notes (#295)
- Breaking changes: add
! after type: feat!: remove legacy API
- Keep the first line under 72 characters
- Body lines must be ≤100 characters (commitlint
body-max-line-length). Hard-wrap bullet points before committing; long URLs or prose lines that exceed 100 chars will fail the hook with body's lines must not be longer than 100 characters. If a HEREDOC body fails, re-wrap and create a new commit — do not amend.
Examples
feat: add release notes dialog
fix: flaky test in orchestrator (#292)
refactor: extract session handler into separate module
chore: update dependencies
ci: add PR title linting workflow
Steps
Track these steps with an internal todo/checklist and mark them complete as you go.
Do not create, update, or delete Kandev subtasks for this workflow unless the user
explicitly requests task tracking.
Understand changes: Run git status and git diff to understand all changes. Review recent commits with git log --oneline -10 to match project style.
Ensure pre-commit hooks are wired up. This must work in worktrees too, where .git/ is a file (not a directory) and the real hooks path is shared with the main repo via core.hooksPath. Use git rev-parse --git-path so the check resolves correctly regardless:
# Is the framework on PATH?
pre-commit --version >/dev/null 2>&1 && echo "INSTALLED" || echo "NOT_INSTALLED"
# Is the hook actually wired into git's hook system?
PRE_COMMIT_HOOK_PATH=$(git rev-parse --git-path hooks/pre-commit)
COMMIT_MSG_HOOK_PATH=$(git rev-parse --git-path hooks/commit-msg)
test -f "$PRE_COMMIT_HOOK_PATH" && grep -q "pre-commit" "$PRE_COMMIT_HOOK_PATH" \
&& test -f "$COMMIT_MSG_HOOK_PATH" && grep -q "pre-commit" "$COMMIT_MSG_HOOK_PATH" \
&& echo "ACTIVE" || echo "INACTIVE"
If NOT_INSTALLED, tell the user once: "⚠️ pre-commit is not on PATH. Install it with pip install pre-commit so format/lint runs on every commit." Then continue (don't block).
If installed but INACTIVE, install it yourself — the project ships .pre-commit-config.yaml and make doctor is a no-op-on-already-installed wrapper around the same command:
pre-commit install -t pre-commit -t commit-msg --overwrite
Mention that you wired it up. Subsequent commits will run hooks automatically.
If both checks pass, no output needed.
Before the first commit, ensure the worktree can run the commit-msg
dependency. If apps/node_modules/.bin/commitlint is absent, install it
from apps/ before committing:
cd apps && pnpm install --frozen-lockfile
Retry the normal commit after the install; never bypass the hook.
After merging or rebasing the base branch, if apps/package.json or
apps/pnpm-lock.yaml changed and a hook reports
ERR_MODULE_NOT_FOUND even though apps/node_modules/.bin/commitlint
exists, refresh the workspace dependencies from apps/:
cd apps && pnpm install --frozen-lockfile
Retry the normal commit without bypassing hooks.
Why this matters: a missing hook lets lint regressions slip past local commits and only surface in CI (e.g. funlen / cognitive complexity on backend Go code). The hook catches them in <1s at commit time. See Makefile's doctor target for the idempotent install command.
Capture the parent SHA and preserve hook evidence:
git rev-parse HEAD
Never use --no-verify, SKIP, or another hook-bypass option or
environment variable. A bypassed commit is allowed only when the user
explicitly requests it, and its receipt must say bypass: true; it never
qualifies as a successful hook receipt.
Stage files: Stage relevant files (prefer specific files over git add -A).
Before reviewing or committing, inspect every ?? path from git status --short; git diff omits untracked files. Stage each relevant path or read
it explicitly so new tests and helpers are not missed.
- Splitting commits with new files: When introducing a brand-new file alongside the file that uses it, stage them together. The Go lint pre-commit hook stashes unstaged changes before linting but keeps untracked files in the working tree — so a new helper committed alone, while its (still-unstaged) caller sits in the working tree, lints as
unused and rejects the commit.
Commit: Write a commit message following the format above. If changes span multiple concerns, consider separate commits.
When MERGE_HEAD exists or a merge commit is being completed, use
git commit --no-edit so a non-interactive runner does not open an editor;
normal hooks still run. Confirm the merge commit exists and MERGE_HEAD is
gone before reporting success.
If a formatter changes files and prevents the commit, review and re-stage
those files, then create a new commit attempt; do not use --amend.
When editing harness files such as AGENTS.md, CLAUDE.md, or skills, run
the shared validation in
.agents/skills/harness-improvement/references/validation.md before
committing.
If a JSX layout-only edit touches an element containing an existing
hardcoded user-facing literal, i18n-new-code may classify that literal as
changed copy and fail. Localize it and add matching en/pseudo catalog
entries before retrying; verify with cd apps/web && pnpm run i18n:check and
the normal hook receipt.
If the commit command returns an exec_command session_id, preserve the
full result and poll that same session until it is terminal; never start a
second commit attempt while the first is live. Retain the exact temporary
log path from that single attempt for the hook receipt. Capture the normal
hook stream in a temporary log while committing and use that log to record
each hook ID and result. RTK can condense rtk git commit
output, so use rtk proxy git commit when preserving raw hook output. Do not
infer hook results from a condensed launcher summary. For example:
COMMIT_LOG="$(mktemp "${TMPDIR:-/tmp}/kandev-commit.XXXXXX.log")"
set -o pipefail
rtk proxy git commit -m "type(scope): description" 2>&1 | rtk proxy tee "$COMMIT_LOG" >/dev/null
Both the commit and tee stages must use raw-output mode when the log is
parsed; normal rtk git commit output may contain only ok <sha> and is not
hook evidence. Read the log to extract every hook ID/result and confirm the receipt still
says bypass: false, rather than printing the full stream again. Remove the
exact temporary file after copying the receipt into
the handoff:
unlink "$COMMIT_LOG"
If a timed-out hook leaves a commit or linter process running, inspect
git status, git log, and only that attempt's processes before retrying.
Wait for or stop the owned process tree first; do not mistake a transient
parallel golangci-lint is running message for a second commit failure.
If the commit exits nonzero, preserve and print the full or bounded log
before cleanup so hook diagnostics are not lost. Remove the temporary log
only after copying a successful hook receipt or recording the failed output.
When extracting receipt lines from a nested shell, use shell-safe single-
quoted awk or sed expressions; a double-quoted sed range containing
$ can be expanded by the outer shell and hide the receipt. Record active
hooks that report Skipped or no files to check as skipped; do not infer
their result from the launcher's summary, and retain the log until both the
pre-commit and commit-msg results are recorded.
If a hook fails only because another worktree is already running
golangci-lint (for example, parallel golangci-lint is running), wait for
that run to finish and retry the same commit. Do not bypass hooks or change
code for this transient lock; verify the retry has a normal hook receipt and
a clean worktree.
If a hook fails with ENOSPC, load /verify's Disk-constrained runners
guidance: inspect df, preserve managed caches, relocate only the affected
cache to an explicit persistent agent-owned path, and never bypass hooks or
blindly delete shared caches.
Return a hook receipt: After a successful commit, report:
parent_sha: <pre-commit HEAD>
commit_sha: <new HEAD>
pre_commit_hook: active|inactive
commit_msg_hook: active|inactive
hook_results: <hook-id=passed|skipped, ...>
bypass: false|true
commit_result: pass
worktree: clean|dirty
verify may use the receipt only when both hooks are active, bypass is
false, the commit succeeded, the current HEAD still equals commit_sha,
and the worktree is clean. The commit worker does not run verification.
1---2name: commit3description: Stage and commit changes using Conventional Commits. Use when there are dirty/staged files to commit, the user says "commit", or before pushing a PR.4---56# Commit78## Planner Entry910Stage and commit changes in the primary conversation after the task-defined11implementation checks pass. Do not add broad post-commit `/verify` before push12by default. Preserve the hook receipt as normal commit evidence.1314Create a git commit following this project's Conventional Commits convention. These messages are used by git-cliff (`cliff.toml`) to auto-generate changelogs and release notes. PRs are squash-merged, so the PR title becomes the commit on `main` — CI validates it via `pr-title.yml`.1516## Available skills1718- **`/pr-fixup`** — Use after the PR opens only for CI or actionable reviewer19 findings.2021## Format2223```24type: lowercase description25```2627## Allowed Types2829| Type | Use for | In changelog? |30|------|---------|---------------|31| `feat` | New features | Yes (Features) |32| `fix` | Bug fixes | Yes (Bug Fixes) |33| `perf` | Performance improvements | Yes (Performance) |34| `refactor` | Code refactoring | Yes (Refactoring) |35| `docs` | Documentation changes | Yes (Documentation) |36| `chore` | Maintenance, deps, configs | No |37| `ci` | CI/CD changes | No |38| `test` | Test-only changes | No |3940## Rules4142- Subject **must** start with a lowercase letter43- Scope is optional: `feat(ui): add dialog` is valid44- Include PR/issue number when relevant: `feat: add release notes (#295)`45- Breaking changes: add `!` after type: `feat!: remove legacy API`46- Keep the first line under 72 characters47- **Body lines must be ≤100 characters** (commitlint `body-max-line-length`). Hard-wrap bullet points before committing; long URLs or prose lines that exceed 100 chars will fail the hook with `body's lines must not be longer than 100 characters`. If a HEREDOC body fails, re-wrap and create a *new* commit — do not amend.4849## Examples5051```52feat: add release notes dialog53fix: flaky test in orchestrator (#292)54refactor: extract session handler into separate module55chore: update dependencies56ci: add PR title linting workflow57```5859## Steps6061Track these steps with an internal todo/checklist and mark them complete as you go.62Do not create, update, or delete Kandev subtasks for this workflow unless the user63explicitly requests task tracking.64651. **Understand changes:** Run `git status` and `git diff` to understand all changes. Review recent commits with `git log --oneline -10` to match project style.66672. **Ensure pre-commit hooks are wired up.** This must work in worktrees too, where `.git/` is a file (not a directory) and the real hooks path is shared with the main repo via `core.hooksPath`. Use `git rev-parse --git-path` so the check resolves correctly regardless:6869 ```bash70 # Is the framework on PATH?71 pre-commit --version >/dev/null 2>&1 && echo "INSTALLED" || echo "NOT_INSTALLED"7273 # Is the hook actually wired into git's hook system?74 PRE_COMMIT_HOOK_PATH=$(git rev-parse --git-path hooks/pre-commit)75 COMMIT_MSG_HOOK_PATH=$(git rev-parse --git-path hooks/commit-msg)76 test -f "$PRE_COMMIT_HOOK_PATH" && grep -q "pre-commit" "$PRE_COMMIT_HOOK_PATH" \77 && test -f "$COMMIT_MSG_HOOK_PATH" && grep -q "pre-commit" "$COMMIT_MSG_HOOK_PATH" \78 && echo "ACTIVE" || echo "INACTIVE"79 ```8081 - If **NOT_INSTALLED**, tell the user once: _"⚠️ pre-commit is not on PATH. Install it with `pip install pre-commit` so format/lint runs on every commit."_ Then continue (don't block).82 - If installed but **INACTIVE**, **install it yourself** — the project ships `.pre-commit-config.yaml` and `make doctor` is a no-op-on-already-installed wrapper around the same command:83 ```bash84 pre-commit install -t pre-commit -t commit-msg --overwrite85 ```86 Mention that you wired it up. Subsequent commits will run hooks automatically.87 - If both checks pass, no output needed.8889 - Before the first commit, ensure the worktree can run the commit-msg90 dependency. If `apps/node_modules/.bin/commitlint` is absent, install it91 from `apps/` before committing:92 ```bash93 cd apps && pnpm install --frozen-lockfile94 ```95 Retry the normal commit after the install; never bypass the hook.9697 - After merging or rebasing the base branch, if `apps/package.json` or98 `apps/pnpm-lock.yaml` changed and a hook reports99 `ERR_MODULE_NOT_FOUND` even though `apps/node_modules/.bin/commitlint`100 exists, refresh the workspace dependencies from `apps/`:101 ```bash102 cd apps && pnpm install --frozen-lockfile103 ```104 Retry the normal commit without bypassing hooks.105106 Why this matters: a missing hook lets lint regressions slip past local commits and only surface in CI (e.g. funlen / cognitive complexity on backend Go code). The hook catches them in <1s at commit time. See `Makefile`'s `doctor` target for the idempotent install command.1071083. **Capture the parent SHA and preserve hook evidence:**109 ```bash110 git rev-parse HEAD111 ```112 Never use `--no-verify`, `SKIP`, or another hook-bypass option or113 environment variable. A bypassed commit is allowed only when the user114 explicitly requests it, and its receipt must say `bypass: true`; it never115qualifies as a successful hook receipt.1161174. **Stage files:** Stage relevant files (prefer specific files over `git add -A`).118 Before reviewing or committing, inspect every `??` path from `git status119 --short`; `git diff` omits untracked files. Stage each relevant path or read120 it explicitly so new tests and helpers are not missed.121 - **Splitting commits with new files:** When introducing a brand-new file alongside the file that uses it, stage them together. The Go lint pre-commit hook stashes *unstaged* changes before linting but keeps *untracked* files in the working tree — so a new helper committed alone, while its (still-unstaged) caller sits in the working tree, lints as `unused` and rejects the commit.1221235. **Commit:** Write a commit message following the format above. If changes span multiple concerns, consider separate commits.124 When `MERGE_HEAD` exists or a merge commit is being completed, use125 `git commit --no-edit` so a non-interactive runner does not open an editor;126 normal hooks still run. Confirm the merge commit exists and `MERGE_HEAD` is127 gone before reporting success.128 If a formatter changes files and prevents the commit, review and re-stage129 those files, then create a new commit attempt; do not use `--amend`.130 When editing harness files such as `AGENTS.md`, `CLAUDE.md`, or skills, run131 the shared validation in132 `.agents/skills/harness-improvement/references/validation.md` before133 committing.134 If a JSX layout-only edit touches an element containing an existing135 hardcoded user-facing literal, `i18n-new-code` may classify that literal as136 changed copy and fail. Localize it and add matching `en`/`pseudo` catalog137 entries before retrying; verify with `cd apps/web && pnpm run i18n:check` and138 the normal hook receipt.139 If the commit command returns an `exec_command` `session_id`, preserve the140 full result and poll that same session until it is terminal; never start a141 second commit attempt while the first is live. Retain the exact temporary142 log path from that single attempt for the hook receipt. Capture the normal143 hook stream in a temporary log while committing and use that log to record144 each hook ID and result. RTK can condense `rtk git commit`145 output, so use `rtk proxy git commit` when preserving raw hook output. Do not146 infer hook results from a condensed launcher summary. For example:147 ```bash148 COMMIT_LOG="$(mktemp "${TMPDIR:-/tmp}/kandev-commit.XXXXXX.log")"149 set -o pipefail150 rtk proxy git commit -m "type(scope): description" 2>&1 | rtk proxy tee "$COMMIT_LOG" >/dev/null151 ```152 Both the commit and `tee` stages must use raw-output mode when the log is153 parsed; normal `rtk git commit` output may contain only `ok <sha>` and is not154 hook evidence. Read the log to extract every hook ID/result and confirm the receipt still155 says `bypass: false`, rather than printing the full stream again. Remove the156 exact temporary file after copying the receipt into157 the handoff:158 ```bash159 unlink "$COMMIT_LOG"160 ```161 If a timed-out hook leaves a commit or linter process running, inspect162 `git status`, `git log`, and only that attempt's processes before retrying.163 Wait for or stop the owned process tree first; do not mistake a transient164 `parallel golangci-lint is running` message for a second commit failure.165 If the commit exits nonzero, preserve and print the full or bounded log166 before cleanup so hook diagnostics are not lost. Remove the temporary log167 only after copying a successful hook receipt or recording the failed output.168 When extracting receipt lines from a nested shell, use shell-safe single-169 quoted `awk` or `sed` expressions; a double-quoted `sed` range containing170 `$` can be expanded by the outer shell and hide the receipt. Record active171 hooks that report `Skipped` or `no files to check` as `skipped`; do not infer172 their result from the launcher's summary, and retain the log until both the173 pre-commit and commit-msg results are recorded.174175 If a hook fails only because another worktree is already running176 golangci-lint (for example, `parallel golangci-lint is running`), wait for177 that run to finish and retry the same commit. Do not bypass hooks or change178 code for this transient lock; verify the retry has a normal hook receipt and179 a clean worktree.180181 If a hook fails with `ENOSPC`, load `/verify`'s Disk-constrained runners182 guidance: inspect `df`, preserve managed caches, relocate only the affected183 cache to an explicit persistent agent-owned path, and never bypass hooks or184 blindly delete shared caches.1851866. **Return a hook receipt:** After a successful commit, report:187 ```text188 parent_sha: <pre-commit HEAD>189 commit_sha: <new HEAD>190 pre_commit_hook: active|inactive191 commit_msg_hook: active|inactive192 hook_results: <hook-id=passed|skipped, ...>193 bypass: false|true194 commit_result: pass195 worktree: clean|dirty196 ```197 `verify` may use the receipt only when both hooks are active, bypass is198 false, the commit succeeded, the current `HEAD` still equals `commit_sha`,199 and the worktree is clean. The commit worker does not run verification.