Files
clide/.claude/skills/git-commit/SKILL.md
T
jpmschweitzerandClaude 7c2eae42f1
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m1s
fix theme picker integration test; one bake for build-time facts (T-116)
The test was awaiting services.commands.execute('theme.pick') whose
Future doesn't complete until the dialog is dismissed — deadlock.
Fire-and-forget around pumpAndSettle, then tap Cancel, then await
the original future. Also tear the widget tree down before
services.dispose() so listening widgets unsubscribe first.

Pre-existing layout overflow in the welcome _StatusLine surfaced
when running the test at narrower viewports. Switched to a whole-
row FittedBox(scaleDown) — uniform shrink on narrow screens, no-op
at standard widths.

User flagged the hardcoded 'clide 2.0.0-dev' string. Replaced with
one generated lib/src/build_info.g.dart (gitignored, regenerated
by `make gen-build-info` from pubspec.yaml + git short SHA + UTC
clock). The same target re-syncs assets/licenses.yaml self.version
in place — no second source. Every make build/run/test depends on
it implicitly. Welcome status line now reads `clideVersion`. Stale
fontSize literals in welcome_view swept to typography constants;
clideFontMeta=13, clideFontDialogTitle=16, clideFontWelcomeBanner=52
added to fill gaps in the scale.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-18 13:22:55 +02:00

10 KiB

name, description
name description
git-commit Git commit conventions for this repo — message style, changelog and version-bump rules, attribution trailer, and the safety rules that always apply. Use whenever the user says "commit", "commit this", "create a commit", "make a commit", "amend", or asks Claude to check work into git in any form.

Git commit rule — this repo

Follow these conventions whenever you create a commit in this repository. These are in addition to the standard Claude Code git-safety protocol (no --no-verify, no force-push to main, prefer new commits over --amend, etc.).

Message style

  • First line: imperative mood, ≤ 70 characters. Examples: add sidecar PTY scaffold, fix IPC reconnect after app reload, update CLI exit-code contract.
  • Body (optional): wrap at ~72 chars. Explain the why — the reason this change exists. The diff already shows the what; don't restate it in prose.
  • No emojis. Anywhere.
  • Don't prefix with types like feat: or fix: — this repo isn't Conventional Commits. (The Python-era clide under legacy/ used Conventional Commits; the Flutter rebuild at the repo root does not.)
  • Don't reference the current task or flow (for the v2.0 milestone, used by the canvas panel) — that context belongs in the PR description and rots as the repo evolves.
  • Naming: the project is clide. The Flutter desktop app lives at the repo root; the Go sidecar/CLI binary is clide. The supporter project is pql (referenced, not part of this repo). The archived Python implementation lives under legacy/.

Logically-separated commits

Default to one logical change per commit, even when a lot of work lands in the tree at once. When there's a pile of uncommitted or untracked files:

  1. Read git status + git diff first — never stage the whole tree blind.
  2. Group by concern, not by file location. Typical concerns to separate:
    • Bookkeeping.gitignore, editor/IDE config, lockfiles, go.sum / pubspec.lock updates.
    • DocumentationREADME.md, CLAUDE.md, ADRs under docs/ADRs/, design notes under docs/.
    • General-purpose tooling / skills — things that aren't project-specific (reusable skills, shared scripts).
    • Project-specific conventions — this repo's own rules.
    • Feature or subsystem — one cohesive change per commit; a sidecar change and an app change for the same feature can land together, but two unrelated features should split.
    • Layer changes — Flutter app, sidecar CLI, sidecar daemon, IPC server, pql wrapper, canvas driver, git panel — separate concerns; prefer separate commits when the changes are independent.
  3. Sequence the commits so each one is cleanly scoped, but don't obsess about whether each intermediate commit "works" — for scaffolding PRs it's fine if the full picture only snaps together at the end.
  4. Prefer many small focused commits over one large mixed one — a reviewer can read, revert, or cherry-pick a focused commit; they can't do any of those to a blob.
  5. Use git add <specific paths> — never git add -A or git add . when splitting, or you'll sweep in the next commit's work by accident.
  6. Verify between commits with git status and git log -1 to confirm the split landed as intended.

Corollary: if a commit's subject line needs the word "and" to be accurate, it probably should have been two commits.

Changelog discipline

This repo follows Keep a Changelog 1.1.0. Every user-visible commit must touch CHANGELOG.md under the ## [Unreleased] section, in the appropriate subsection:

  • Added — new features or capabilities.
  • Changed — changes to existing functionality.
  • Deprecated — soon-to-be-removed features.
  • Removed — now-removed features.
  • Fixed — bug fixes.
  • Security — vulnerability fixes.

Entries should be short imperative phrases that describe user-facing impact — not implementation detail. "Added sidecar PTY support for terminal pane" beats "Added internal/pty/session.go."

Be concise — this is the rule, not a suggestion

CHANGELOG entries must be one or two short sentences. Hard cap: 60 words per bullet (enforced by ci/changelog_gate.sh). Aim for 30 or under; if you can't say it in one line wrapped at ~75 columns, you're writing the wrong document.

The CHANGELOG is read by humans scanning for what changed between two versions. It is not the place for the rationale, the probe results, the implementation detail, the behavior-change deep dive, or the "see also" cross-references. Those belong in:

  • the commit message body — explain why, list evidence, name the trade-offs;
  • a D-record / decision document — durable architectural rationale;
  • the ticket / PR description — work-context and review notes.

Hard rules:

  • No multi-paragraph bullets. One paragraph max. If you reach for a blank line inside a bullet, stop and split or trim.
  • No "Behavior change:" / "Side benefit:" / "Note:" sub-headers inside a bullet. Those are essay structure; put them in the commit message.
  • No probe numbers, latency stats, or %-coverage deltas in entries. ("hit 95% target" is fine; "0 hangs in 300 spawns vs ~5% before" is commit-body material.)
  • No nested function/file lists inside parentheses. If you find yourself writing (foo, bar, baz, …) for more than 3 items, just say "several X" and trust the diff.
  • Don't restate the title in the body. A bullet is its own title.

Calibration — match the existing entries in CHANGELOG.md. Open it, look at five recent bullets, write to that length. If your draft is visibly bigger than its neighbors, trim until it isn't.

Good:

  • Mouse wheel scrolling in Claude pane — converts scroll events to PgUp/PgDown so TUI apps scroll naturally.

Bad (verbose; commit-body material leaked in):

  • PTY spawning switched from forkpty() to posix_openpt() + posix_spawn() (T-96). forkpty calls fork() underneath, which is unsafe in the multithreaded Dart VM: about 5% of spawns deadlocked in the child before execve because libc locks held by ghost-threads remained "locked forever" in the forked child. posix_spawn uses vfork (glibc/musl/macOS), keeping the parent suspended until execve completes — no Dart code runs in the child. Probed: zero hangs in 300 sequential spawns vs ~5% before. Behavior change: missing executable / missing workingDirectory now surface as a PtyException

Better:

  • PTY spawning uses posix_openpt + posix_spawn instead of forkpty — closes a ~5% deadlock window in the multithreaded Dart VM (T-96). Missing exe/cwd now throw PtyException at spawn time.

What skips the changelog: pure bookkeeping commits that have no user-visible effect (typo fix in internal comment, .gitignore tweak, lint config change, reformatting). When in doubt, add an entry — the harm of an extra line is zero.

When a commit spans multiple entries (e.g. a feature that adds one thing and fixes another), add a line under each applicable subsection rather than cramming both into one.

Cutting a release

Cutting a release is its own commit. In a single commit:

  1. Move all entries from ## [Unreleased] under a new heading ## [X.Y.Z] — YYYY-MM-DD.
  2. Leave an empty ## [Unreleased] section at the top with its subsection skeleton ready.
  3. Bump pubspec.yaml version: to X.Y.Z (drop the -dev suffix for the tag; re-add it on the next development commit if desired).
  4. Run make gen-build-info so assets/licenses.yaml self.version: re-syncs from pubspec (it's auto-rewritten by every build but commit the fresh state). Stage the resulting diff alongside step 3.
  5. Commit subject: release vX.Y.Z.

pubspec.yaml is the single source of truth for the version. Every make build/run/test target regenerates lib/src/build_info.g.dart (gitignored) and rewrites assets/licenses.yaml self.version: from it — so the Flutter app sees the current version everywhere without manual sync. Bumping pubspec.yaml and the changelog out of sync is the mistake this rule prevents.

Attribution trailer

Every commit ends with the Claude co-author line:

Co-Authored-By: Claude <noreply@anthropic.com>

The model-identifier variant produced by the Claude Code harness (e.g. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>) is also accepted — don't rewrite it if the harness emits that form.

HEREDOC discipline

Pass commit messages via HEREDOC so multi-line formatting survives:

git commit -m "$(cat <<'EOF'
short imperative summary

Optional longer body explaining why this change was needed,
wrapped at about 72 characters.

Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)"

Never pass multi-line messages via -m "line1\nline2" or multiple -m flags — Git's behavior differs between shells and quoting regimes and the trailer can end up in the wrong place.

What not to commit

  • .env and any *.env.local — see .gitignore.
  • .claude/settings.local.json — user-specific Claude Code settings, ignored.
  • Build artefacts: sidecar/bin/, sidecar/dist/, build/ (Flutter output), app/.dart_tool/ — gitignored.
  • SQLite index files (*.sqlite, *.sqlite-wal, *.sqlite-shm, *.db) — caches generated against local repos; must never land here. Gitignored defensively.
  • Coverage / test output (*.out, coverage.*, *.test) — gitignored.

Safety reminders (reinforced from the global Claude Code protocol)

  • Never --no-verify. If a pre-commit hook fails, fix the underlying issue and create a new commit.
  • Never --amend a commit unless the user explicitly asks. A failed-hook commit didn't land, so amending would overwrite the previous commit and lose work.
  • Never force-push to main or master. Warn the user if they ask.
  • When staging, prefer naming specific files over git add -A / git add . — those can sweep in secrets or unintended files.
  • git status before staging, git diff --staged before committing, git log -1 after committing to confirm.