The about-page bundle pushes three commits, not one. Commit A is the AboutPhotoHero component and the ten WebP assets. Commit B is the /about page route. Commit C is the nav patch, and it only happens if the nav patch actually touched files. The transparent-photo bundle pushes two: one for the asset swap, one for the card-frame strip.
There is a reason to slice this way. When something breaks in preview, I want to bisect. If the nav patch wrecked the mobile sheet but the hero rendered fine, a single mega-commit forces me to revert everything. With Commit C standing alone, git revert is one SHA and the hero stays live.
It also makes the commit history readable as a log. Six months from now I’ll scroll back and see “feat(about): add AboutPhotoHero with cursor-driven 10-frame cross-fade” sitting next to “feat(about): add /about page route.” The order reflects the build order. The component existed before the route that consumed it.
There’s a small operational cost: ship.sh has to track which staging area is dirty and which isn’t. The script uses git diff —quiet against src/components/ as the gate for Commit C, because the nav patch is best-effort and sometimes finds nothing to patch. If no changes, no commit. The script warns and moves on.
The principle: commits are units of revert, not units of work. Slice them by what you’d want to undo separately, not by what you happened to write at the same time.