# Git Wrapup

> Land verified working-tree changes in `@cyanheads/pixoo-toolkit` as a stack of logical commits, bump the version, author the CHANGELOG entry, and cut an annotated tag. Stops at "committed and tagged locally" — no push, no publish; `release-and-publish` picks up from there.

- Skill: `cyanheads/git-wrapup-4` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cyanheads/git-wrapup-4`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cyanheads/git-wrapup-4/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools, AI & ML
- Author: cyanheads (https://skillmd.com/u/cyanheads)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/cyanheads/git-wrapup-4

---


## When to use

Changes in the working tree are complete and verified, and they are ready to become a released version of the npm package. Typical triggers: a bug fix landed against a tracked issue, a feature is finished, a dependency refresh is done.

This is not a "save my work" skill. A commit here means the version ships.

## Pre-wrapup gates

Every item must be true before you touch a version number. If one is red, fix it first — starting wrapup on a broken tree burns the version number and forces an amend.

- [ ] **Changes exist** — uncommitted files, or commits since the last tag
- [ ] **Work is complete** — no half-finished units, no TODO placeholders
- [ ] **Code simplified** — for a diff past ~50 lines or 3+ source files, the `code-simplifier` pass has run
- [ ] **`bun run devcheck` passes** — typecheck, ESLint, Prettier, and the `CLAUDE.md`/`AGENTS.md` byte-identity check
- [ ] **`bun run test:all` passes** — clean rebuild plus the full Vitest suite
- [ ] **Regression test added** — a bug fix without a test that fails on the old code is not finished
- [ ] **Public API reviewed** — anything newly exported is in `src/index.ts`, and no exported signature changed without a matching version bump tier
- [ ] **Issues updated** — GitHub issues this work addresses have a comment saying what landed
- [ ] **Docs updated** — surgical edits to `README.md` and `CLAUDE.md`, not rewrites

## Steps

### 1. Review what is shipping

```bash
git status
git log v<latest-tag>..HEAD --oneline
git diff --stat
git diff
```

If the tree is clean and there are no commits since the last tag, halt — nothing to wrap up.

### 2. Pick the version

Read `version` from `package.json` and apply the bump. This package is pre-1.0 but is published and consumed, so treat the public surface as real.

| Bump | When |
|:---|:---|
| **patch** | Bug fixes, dependency updates, docs, packaging metadata |
| **minor** | New exported function, new option on an existing export, new script |
| **major** | Removed or renamed export, changed exported signature or return shape, behavior change a consumer must code around |

Default to **patch**. A change to an exported type — `PixooResult`, `Canvas`, `RGB` — is never a patch.

### 3. Bump the version everywhere

- `package.json` — `version`
- `README.md` — any version badge or install snippet that pins a version
- `CLAUDE.md` **and** `AGENTS.md` — only if they pin a version string; they must stay byte-identical

Catch stragglers (substitute the outgoing version):

```bash
rg -n --hidden -g '!node_modules' -g '!dist' -g '!.git' -g '!CHANGELOG.md' '0\.7\.7' .
```

Historical `CHANGELOG.md` entries are correct as-is — that is why it is excluded. Resolve every other hit.

### 4. Author the CHANGELOG entry

`CHANGELOG.md` is a single hand-maintained file in [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) order. Add the new entry directly under the header block, above the previous version.

```markdown
## [0.7.8] — 2026-08-13

### Fixed

- **canvas:** `drawRect()` now ... (#N).
```

Rules:

- **Never `[Unreleased]`.** Always a concrete version and an ISO date.
- Section order: Added / Changed / Deprecated / Removed / Fixed / Security. Include only sections with entries.
- Every bullet leads with a bold module symbol (`**canvas:**`, `**client:**`, `**svg-path:**`) matching the `src/` module it changed.
- One sentence per bullet, two at the absolute most. Name the exported function and what a consumer observes now. Backlink the issue as `(#N)`.
- **The diff is the source of truth.** Every concrete claim — function name, parameter, error type, threshold — is read out of the changed file, never recalled.
- Cut: mechanism walkthroughs, rationale defense, "X is unchanged" clauses, edge-case inventories.

### 5. Run the gate

The tree being committed must pass. Run both raw — do not filter the output:

```bash
bun run devcheck
bun run test:all
```

**If either fails, halt.** Fix, then re-run this step.

### 6. Commit — group by concern, release artifacts on top

Never `git add -A` into one blob. Stage each concern explicitly, commit, move on:

1. **The work** — one commit per concern. Two unrelated fixes are two commits. Work commits do not carry the version.
2. **The release commit** — last, on top: `package.json`, `CHANGELOG.md`, and any README/docs version strings.

```bash
git add <paths-for-this-concern>
git commit -m "<subject>"
# repeat per concern; version + changelog land last
```

**The file is the atomic boundary.** Never split one file's changes across commits. When a file serves two concerns, it ships whole in the commit of its dominant concern.

**Subjects** — Conventional Commits, scope = the `src/` module:

- Work: `fix(canvas): reject non-finite rect dimensions`, `feat(color): oklch parsing`, `docs: document discovery timeout`
- Release: `fix(canvas): 0.7.8 — reject non-finite rect dimensions`

When the change and its version bump are inseparable for a small patch, a single commit whose subject leads with the version is correct — that is the shape most of this repo's history takes.

**Every commit carries a body of one or two lines** — never subject-only, never a paragraph. One sentence of *why* or the load-bearing constraint:

```
fix(canvas): reject non-finite rect dimensions

A NaN width reached the fill loop and wrote nothing while reporting success.
```

Rules:

- Plain `-m` strings only — no heredoc, no command substitution
- No `Co-authored-by` or `Generated with` trailers
- No marketing adjectives ("comprehensive", "robust", "enhanced", "improved")
- **No closing keywords** (`Fixes #N`, `Closes #N`) — they close the issue on push, before the close-out comment recording what shipped. Use bare `(#N)` backlinks; closing is a deliberate later step.
- Each message stands alone in `git log` — no chat context, no "as discussed"

### 7. Cut an annotated tag

```bash
git tag -a v<version> --cleanup=whitespace -m "<subject>

- <notable change> (#N)
- <notable change> (#N)"
```

`--cleanup=whitespace` is load-bearing: the default `strip` mode deletes `#`-leading lines as comments, silently removing markdown headers from the tag body that `release-and-publish` renders as the GitHub Release.

Format — a headline digest, not a CHANGELOG mirror:

- **Subject omits the version.** GitHub prepends `v<VERSION>:` to the release title, so including it stutters.
- Flat bullets only — never `Added:` / `Fixed:` section headers.
- Complete at headline granularity: notable changes get a bullet, minor and internal items share one grouped bullet. The CHANGELOG carries depth; the tag carries existence.
- Deps get one line at most, naming only what earns it.
- Issue backlinks as `(#N)` — they render as links in the Release body.

### 8. Verify the end state

```bash
git log --oneline -8
git show v<version> --stat | head -20
git status
```

The tag must point at HEAD and the tree must be clean. If not, investigate before going further.

**Do not push.** This skill stops here.

## Constraints

- **Local only.** No `git push`, no remote operations. `release-and-publish` owns those.
- **Never `git stash`** — not for a quick check, not for a clean baseline, not for any reason.
- **Never destructive** — no `git reset --hard`, `git restore .`, `git clean -f`, `git checkout -- .`.
- Drive every git operation through Bash `git`.
- If `v<version>` already exists as a tag, **halt and report** the version string, the existing tag SHA, and the current HEAD SHA. Do not move or delete tags without explicit authorization.

## Checklist

- [ ] Diff reviewed end-to-end before the version bump
- [ ] Version bumped in `package.json` and every other file that pins it
- [ ] `CLAUDE.md` and `AGENTS.md` still byte-identical
- [ ] `CHANGELOG.md` entry added with a concrete version and date, bullets backlinked to issues
- [ ] `bun run devcheck` passes
- [ ] `bun run test:all` passes
- [ ] Work grouped into logical commits; version + changelog on top
- [ ] Every commit body is one or two lines
- [ ] Annotated tag `v<version>` created with `--cleanup=whitespace`
- [ ] Working tree clean, tag points at HEAD
- [ ] Nothing pushed

