# Commit message style

Freeform **scope-prefix** messages. The shape is conventional-commits-*like* — `type(scope): summary` is the dominant form in this repo — but it is **not enforced**: the scope and the *why* matter more than the type enum. Do not reject or rewrite a message just because it lacks a `type`, and do not add ceremony (`BREAKING CHANGE:` footers, rigid type whitelist).

## The pattern

```
<scope-prefix>: <imperative summary>

<optional body: WHY this change, not what — the diff shows what>
```

- **Scope prefix** — the area(s) touched. A single area (`coder`, `web`, `server`), a typed scope (`fix(coder)`, `feat(coder)`, `docs(changelog)`), a sub-scope (`coder(providers)`), or multiple areas joined (`web+coder`). Pick whatever names the blast radius honestly.
- **Imperative summary** — "strip dcp tags", not "stripped" / "strips". One line, no trailing period needed.
- **Body** — only when the *why* isn't obvious from the summary. Explain the reason, the failure it fixes, or the constraint it satisfies. Cross-reference related tags/commits by name when the change builds on or fixes prior work.
- **No emojis.** Anywhere — summary, body, or trailers.

## Real examples (from this repo's log)

```
fix(coder): strip dcp-message-id tags split across stream chunks
feat(coder): per-session SSE subscriptions (P1.5-a concurrency prereq)
feat(coder): guard session delete against worktree work loss
fix(coder): no-upstream branch alone no longer flags a session at-risk
docs(changelog): v2.6.2-delete-guard-and-sse
chore(coder): untrack live coder-providers.json, ship example
```

And the freeform multi-area / sub-scope forms the house style also allows:

```
web+coder: per-session SSE
coder(providers): fix empty picker
```

## Why-not-just-what

A summary that restates the diff (`fix: change variable name`) wastes the message. A good message answers a question the diff can't: *why did this need to change?* Example — the bare summary `fix(coder): no-upstream branch alone no longer flags a session at-risk` is fine, but its body earns its keep:

> Session worktree branches never get an upstream, so the original rule flagged
> every worktree-backed session as at-risk on delete — even pristine ones.

That sentence is the part a future reader (or `git blame`) actually needs.
