Skip to content

vc: enforce the commit message format in config, commit-craft over ste100 - #197

Closed
alycda wants to merge 7 commits into
mainfrom
claude/fervent-feynman-sfduqg-main
Closed

alycda wants to merge 7 commits into
mainfrom
claude/fervent-feynman-sfduqg-main

Conversation

@alycda

@alycda alycda commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Draft. The commits are a starting point for line review and are not expected to survive as-is.

Why

A skill only works when an agent loads it and follows it. Commit messages here come from Claude Code, Crush, Codex and from Alyssa, so the format has to be enforced by the commit path itself. The old commit-craft was Chris Beams' rules written for a person. Four files described the format, and they disagreed. The first rewrite in this PR then codified the repo's history, which has no convention.

What

Configuration (e86ed4b) enforces the format:

Where What runs
tools/commit/check-commit-msg The shape rules: subject <area>(<scope>): <summary>, lowercase, at most 72 characters, no period, not past tense; a blank line; a body, wrapped at 72.
tools/commit/check-commits check-commit-msg on every commit in a jj revset or a git range, merges skipped.
jj (tools/jujutsu/config, new on main) A new description opens with the format as JJ: lines. A jj push alias checks the stack that is not on trunk yet, then runs jj git push. jj runs no git hooks.
git (tools/git/hooks/commit-msg) A hook, in the dotfiles checkouts only (includeIf), because core.hooksPath replaces .git/hooks for the whole repo.
CI (commit-messages.yml) check-commits on every commit of a pull request. Squash bodies are made of those messages.

Wiring: home-manager installs the scripts as one package, links the jj file to ~/.config/jj/conf.d/dotfiles.toml, and repeats the includeIf in git.nix, because the store copy of tools/git/config cannot resolve a relative include. The account without Nix gets the scripts through mise's [env] and the jj file through bootstrap.sh.

The skill (88dda6b) keeps only what no check can judge. It is a layer over ste100, following the ste100-over-asd-ste100 pattern: commit bodies use ste100's STE-flavored mode. The rule "follow the repo's history", the history-derived examples and the skill's own language rules are removed. Kept from review: write as the author, and name commits by their subject, never by a jj change ID. The jujutsu skill pushes with jj push, and ste100, CLAUDE.md and agent-skills.nix are updated.

Facts from sem (a2b1431): sem diff --no-cosmetics supplies the changed entities and sem impact --tests the tests to name. Entity names go in verbatim. The entity-level-git skill's language gaps apply, and without sem the agent falls back to jj diff.

Beside ce-commit (a7f3d63): the compound-engineering plugin is enabled, and its ce-commit also triggers on "commit this". commit-craft is the project convention ce-commit's step 3 defers to: the body stays required, and in jj repos the jujutsu skill's commands replace ce-commit's git steps. Taken from ce-commit:

  • done/stop criteria;
  • a summary that names the outcome, not the files;
  • git commit -F msg -- <paths> on the git fallback, never git add -A;
  • an optional (U3) plan-unit suffix.

The push step now says to push only when asked, which matches the jujutsu skill.

The same change is on develop's line (#196), which also has tests/commit.bats in its CI.

Verification

  • Tested in a git-only sandbox, with jj 0.45.1, nix and sem 0.25.0 added.
  • develop's tests/commit.bats passes against this branch's files (15 tests). It covers each rule, the (U3) suffix, check-commits over git and jj, the jj template in a real editor run, jj push stopping before jj git push, and the hook with git's comment lines.
  • alyssa@dev-x86 evaluates, with GitHub inputs through git+https because the sandbox blocks the GitHub API. Its git config has both includeIf blocks, and the commit-checks package builds.
  • In a scratch HOME laid out like the account without Nix, a bad git commit fails in the checkout, and mise puts tools/commit on PATH.
  • The sem commands were checked in a scratch colocated jj repo, including Nix bindings coming out as entities.
  • statix, deadnix, shellcheck, actionlint, taplo and check-skill-frontmatter.sh pass. Every commit on this branch passes check-commits.
  • CI's check job evaluates the darwin configurations; I could not evaluate them locally.
  • ste100 is not installed in the sandbox, so no text here had a ste100 pass.
  • ce-commit was read, not run beside the skill.

Open

  • Your thread about hashes on rebase. The rule chosen: subject line by default, a hash only for commits on the default branch, the PR number for merged work.
  • The claude.ai account skill anthropic-skills:commit-craft is still the old version.
  • On develop, skills (including ste100 and entity-level-git) are not linked into $HOME.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Soievfadhy2k17VT4VecnT

claude added 2 commits October 1, 2026 21:01
Commit messages here come from Claude Code, Crush and Codex, and each
harness ships its own default for length and trailers. commit-craft was
Chris Beams' seven rules written for a person, with generic examples. It
triggered only when someone asked for a message, not when an agent
committed on its own, and its last step pushed straight to main.

This is the same skill as on develop (c42e3bf on the
claude/fervent-feynman-sfduqg branch), at main's path. It defines one
format, `<area>(<scope>): <summary>` with a required body in a fixed
order (why, what and how, verification, what is left), and a procedure
that ends with `jj describe --stdin`. scripts/check-message enforces the
mechanical rules: subject shape, case, length and tense, the blank line,
a body, and a 72-column wrap. An agent runs it before it sets a message,
so a model that knows the rules less well still produces the same shape.
The only change from develop is the line that says where the skill sits:
here, tools/agents/skills, deployed whole to ~/.agents/skills by
agent-skills.nix, whose comment now describes it.

Tested in a git-only sandbox (no nix, no jj): develop's bats suite for
the checker passes against this copy (10 tests), shellcheck 0.11 passes,
and check-skill-frontmatter.sh accepts the skill (492 bytes). main has
no bats or shellcheck job, so CI does not run the checker's tests here.
Not evaluated with nix; the .nix change is a comment.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Soievfadhy2k17VT4VecnT
Four places described a commit message, and they disagreed. CLAUDE.md
had a subject-plus-bulleted-body template. The jujutsu skill asked for
sentence case and conventional `feat:` prefixes. ste100 named "the seven
rules" and a 50-character subject as commit-craft's. commit-craft itself
was the fourth.

CLAUDE.md's Commit Message Format now points at commit-craft by path, so
an agent that only reads the always-loaded file (Codex reads it through
AGENTS.md) still finds the skill. It keeps two things specific to this
repository: a body says what was learned when the change came out of a
mistake, and because PRs are squash-merged, the PR title is the subject
on main and the branch commits make up its body, so both follow the
format. The jujutsu skill drops its format and keeps the commands: its
`-m` rule gains `jj describe --stdin < file` for messages with a body,
and the intent message from "describe first" is a subject line that the
full message replaces before a push. version-notes says why agents use
`--stdin` now. ste100's handoff names commit-craft's format and checker,
and says to run the checker again after a rewording pass.

Same change as 6bb1247 on develop's branch, applied to main's copies:
the jujutsu skill here is still the 0.44 one, so its edits were made by
hand rather than ported. Checked with rumdl, which main's CI does not
run: CLAUDE.md and the jujutsu skill gain three warnings that match
their existing style (bare code fences, the long Last updated line), and
ste100 stays clean. check-skill-frontmatter.sh passes for all skills.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Soievfadhy2k17VT4VecnT
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

⊕ Entity-level changes

.github/workflows/commit-messages.yml

Status Type Name
+ chunk (preamble)
+ property name
+ section on
+ section permissions
+ section jobs

CLAUDE.md

Status Type Name
Δ heading Commit Message Format
Δ heading Meta: Updating This Document

home-manager/modules/common.nix

Status Type Name
+ orphan module-level
+ binding commitChecks
Δ binding home
- orphan module-level
- binding xdg.configFile.just/justfile.text
+ binding xdg.configFile

home-manager/modules/git.nix

Status Type Name
Δ binding programs.git

home-manager/modules/tools/agent-skills.nix

Status Type Name
~ orphan module-level

tools/agents/skills/commit-craft/SKILL.md

Status Type Name
Δ preamble (preamble)
- heading The Seven Rules
- heading 1. Separate subject from body with a blank line
Δ heading Commit Craft
+ heading What is enforced, and where
- heading 2. Limit the subject line to 50 characters
+ heading Which convention wins
+ heading Procedure
- heading 3. Capitalize the subject line
- heading 4. Do not end the subject line with a period
+ heading Subject line
- heading 5. Use the imperative mood in the subject line
+ heading Body
- heading 6. Wrap the body at 72 characters
- heading 7. Use the body to explain what and why, not how
+ heading Facts
+ heading Language
- heading Workflow Integration
- heading For jujutsu users preparing to push to git
+ heading Trailers
+ heading Example
- heading Quick Reference
- heading Common Patterns
- heading Bug fixes
+ heading With compound-engineering's ce-commit
- heading New features
- heading Refactoring
+ heading Rewording existing commits
- heading Performance improvements
- heading Breaking changes
- heading Additional Guidelines
- heading Atomic commits
- heading References
- heading Conventional Commits (optional extension)
- heading When to Use This Skill

tools/agents/skills/jujutsu/SKILL.md

Status Type Name
+ heading Always pass the message: -m or --stdin
- heading Always use -m for messages
Δ heading Avoid interactive commands
Δ heading Describe first, then code
Δ heading Atomic commits
Δ heading Pushing
Δ heading Before pushing

tools/agents/skills/jujutsu/references/version-notes.md

Status Type Name
Δ heading Message via stdin

tools/agents/skills/ste100/SKILL.md

Status Type Name
Δ heading Handoffs

tools/commit/check-commit-msg

Status Type Name
+ orphan module-level
+ function problem
+ orphan module-level

tools/commit/check-commits

Status Type Name
+ orphan module-level
+ function report
+ orphan module-level

tools/git/config

Status Type Name
↻ Δ chunk lines 21-39 -> lines 21-40
+ chunk lines 41-49

tools/git/dotfiles-repo.gitconfig

Status Type Name
+ chunk lines 1-8

tools/git/hooks/commit-msg

Status Type Name
+ orphan module-level

tools/jujutsu/config

Status Type Name
+ chunk lines 1-20
+ chunk lines 21-35

tools/mise/bootstrap.sh

Status Type Name
Δ orphan module-level

tools/mise/config.toml

Status Type Name
Δ orphan module-level
+ section env

Summary: 32 added, 17 modified, 25 deleted, 1 renamed across 17 files (6 added orphans, 3 modified orphans, 1 deleted orphan)

functions and classes, not lines · sem

Comment thread tools/agents/skills/commit-craft/references/examples.md Outdated
Comment thread tools/agents/skills/commit-craft/references/examples.md Outdated
```

Copy: the first paragraph names the change that caused the problem by its
jj change ID (`vmp`) and gives the exact failure ("7 of its 8 tests

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use git... but this can change if rebase so IDK how to handle this...

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A hash only goes stale while a commit can still be rewritten. That gives a rule per case, which 4f1e34b adds to the skill:

  • Default reference: the subject line, in quotes. It survives a rebase, a squash and the move to GitHub, and git log --grep finds it.
  • A git hash: only for a commit already on the default branch, which isn't rewritten.
  • Merged work: prefer the PR number (#N). GitHub links it, and it still resolves after a squash.

On this repo's squash-merge flow, the branch commits are the ones whose hashes change. The subject is the only reference that stays valid for them. My own commit messages on this branch break this rule: they cite c42e3bf/6bb1247 on the develop branch. They'll be squashed, so I've left them.


Generated by Claude Code

Review on #197 found two faults in the first example and in the rules
behind it. The example named another commit by its jj change ID, which
GitHub cannot resolve, so the reference is lost once the commit is
pushed. It also ended with "to squash or rebase as you choose", an
instruction to Alyssa inside a commit that she is the author of. The
skill told agents to do both.

The skill now says to name another commit by its subject line in quotes.
A subject survives a rebase, a squash and the move to GitHub, and
`git log --grep` finds it. A git hash is allowed only for a commit
already on the default branch, and a PR number is preferred for merged
work. A new rule says to write as the author and to put notes for the
reviewer in the PR body. The "what is left" paragraph no longer offers
an instruction for the person who merges. The example is rewritten to
follow both rules, with a note that says what was removed and why.

Checked: the rewritten example passes check-message, the checker's bats
suite passes (10 tests), and check-skill-frontmatter.sh and rumdl pass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Soievfadhy2k17VT4VecnT
alycda pushed a commit that referenced this pull request Oct 1, 2026
Review on #197 found two faults in the first example and in the rules
behind it. The example named another commit by its jj change ID, which
GitHub cannot resolve, so the reference is lost once the commit is
pushed. It also ended with "to squash or rebase as you choose", an
instruction to Alyssa inside a commit that she is the author of. The
skill told agents to do both.

The skill now says to name another commit by its subject line in quotes.
A subject survives a rebase, a squash and the move to GitHub, and
`git log --grep` finds it. A git hash is allowed only for a commit
already on the default branch, and a PR number is preferred for merged
work. A new rule says to write as the author and to put notes for the
reviewer in the PR body. The "what is left" paragraph no longer offers
an instruction for the person who merges. The example is rewritten to
follow both rules, with a note that says what was removed and why.

Same change as on main's copy in #197. Checked: the rewritten example
passes check-message, the checker's bats suite passes (10 tests), and
check-skill-frontmatter.sh and rumdl pass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Soievfadhy2k17VT4VecnT
alycda pushed a commit that referenced this pull request Oct 1, 2026
The skill had copied its language rules and examples from this repo's
history. That history has no convention, so the skill made the noise a
rule. It also carried a checker that is now in tools/commit, and it
told agents to follow a repo's history when the history looked
consistent.

The skill now follows the ste100 pattern: it adds only what is
specific to commits, and it gives the wording to another skill. The
body is a ste100 "commit body" surface, in STE-flavored mode. When
ste100 is not installed, the agent says so and does not reconstruct
its rules. The skill keeps the subject format, the order of the body
paragraphs, the trailers, and two rules from the review of #197: write
as the author, and name another commit by its subject line, not by a
jj change ID. A table says which check runs where. The rule to copy a
repo's history is gone. references/examples.md is gone, and so are the
language rules. One example and two bad subjects stay in SKILL.md, and
the first bats test runs them.

The jujutsu skill's push section says to push with `jj push` in a repo
that uses this format. CLAUDE.md says where the checks run.

Tested: tests/commit.bats passes (14 tests), and rumdl passes on the
skill and CLAUDE.md. The jujutsu skill gains two long lines, in its
existing style. ste100 is not installed in this sandbox, so this
message and the skill's example were not given a ste100 pass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Soievfadhy2k17VT4VecnT
claude added 2 commits October 1, 2026 22:13
The commit message format was only in the commit-craft skill. A skill
works only when an agent loads it and follows it, and Alyssa writes
commits too. Configuration applies to every commit, whoever writes it.
This is the same change as "vc: check the commit message format in jj,
git and CI" on the develop line, fitted to how main installs config.

The checker moves out of the skill to tools/commit/check-commit-msg. A
new script, tools/commit/check-commits, runs it on each commit in a jj
revset or a git range and skips merges. home-manager installs both as
one package (common.nix). The account without Nix gets them on PATH
from mise's [env], from the checkout bootstrap.sh makes.

The checks run in four places:
- jj: tools/jujutsu/config is new on main. A new description opens
  with the format as `JJ:` lines, and a `jj push` alias runs
  `check-commits --jj` on the stack that is not on trunk yet before it
  runs `jj git push`. jj runs no git hooks, so the check happens before
  the push. home-manager links the file to
  ~/.config/jj/conf.d/dotfiles.toml; bootstrap.sh links the same path.
- git: a `commit-msg` hook in tools/git/hooks, for the dotfiles
  checkouts only, because `core.hooksPath` replaces .git/hooks for the
  whole repo. tools/git/config has the `includeIf` for the account
  without Nix, which includes that file from the checkout. home-manager
  copies it into the store alone, where the relative include finds
  nothing, so git.nix adds the same conditional include.
- CI: a new commit-messages workflow checks every commit on a pull
  request. PRs here are squash-merged and the squash body is made of the
  branch commits' messages, so all of them are checked.

Tested in a git-only sandbox, with jj 0.45.1 and nix added. The develop
line's tests/commit.bats, pointed at these files, passes (14 tests).
alyssa@dev-x86 evaluates, with the GitHub inputs fetched through
git+https (the sandbox blocks the GitHub API). Its git config has both
`includeIf` blocks, and the commit-checks package builds and passes
check-commits on this branch. In a scratch HOME laid out like the
account without Nix, a bad `git commit` fails in the checkout. mise
2026.8.6 puts tools/commit on PATH. statix, deadnix, shellcheck,
actionlint and taplo pass. The darwin configurations were not
evaluated, and main has no CI job for the bats suite.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Soievfadhy2k17VT4VecnT
The skill had copied its language rules and examples from this repo's
history. That history has no convention, so the skill made the noise a
rule. It also carried the checker that is now in tools/commit, and it
told agents to follow a repo's history when the history looked
consistent.

The skill now follows the ste100 pattern: it adds only what is
specific to commits, and it gives the wording to another skill. The
body is a ste100 "commit body" surface, in STE-flavored mode. When
ste100 is not installed, the agent says so and does not reconstruct
its rules. The skill keeps the subject format, the order of the body
paragraphs, the trailers, and two rules from the review of #197: write
as the author, and name another commit by its subject line, not by a
jj change ID. A table says which check runs where. The rule to copy a
repo's history is gone. references/examples.md is gone, and so are the
language rules. One example and two bad subjects stay in SKILL.md.
SKILL.md is the same file as on the develop line.

ste100's handoff names `check-commit-msg`. The jujutsu skill's push
section says to push with `jj push` in a repo that uses this format.
CLAUDE.md says where the checks run, and agent-skills.nix's comment
says what the skill now covers.

Tested: the develop line's tests/commit.bats passes against these files
(14 tests, including the skill's example and bad subjects), and
check-skill-frontmatter.sh accepts the skill (508 bytes). ste100 is
not installed in this sandbox, so this message and the skill's example
were not given a ste100 pass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Soievfadhy2k17VT4VecnT
@alycda alycda changed the title agent(skills): commit-craft for agents, one owner for the message format vc: enforce the commit message format in config, commit-craft over ste100 Oct 1, 2026
commit-craft told agents to read `jj diff --git` and work out what
changed. In a large or mixed diff that is where messages go wrong:
formatter churn sits next to the real change, and entity names get
paraphrased. The entity-level-git skill already has a tool for this.

sem now supplies the facts, ste100 the words, and commit-craft the
order. A new Facts section says to start with `sem diff --no-cosmetics`,
to name each entity as sem names it, to cover the cosmetic edits in one
sentence, and to use `sem impact <entity> --tests` to find the tests for
the verification paragraph. It points to entity-level-git for sem's
language gaps, and says to fall back to `jj diff` without sem and to say
so. The split step gains two signs of two commits from sem's output.

Tested with sem 0.25.0 (upstream's release binary, sha256 checked
against its checksums.txt) in a scratch colocated jj repo: `sem diff`
shows `@` against `@-`, `sem diff <commit>^ <commit>` shows one commit,
`--no-cosmetics` drops a whitespace-only edit, Nix bindings come out as
entities (`home.packages`), and `sem impact --tests` finds a test that
names the function. The develop line's tests/commit.bats still passes
(14 tests). Same change as on the develop line.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Soievfadhy2k17VT4VecnT
@alycda
alycda force-pushed the claude/fervent-feynman-sfduqg-main branch from 54ff9e3 to a2b1431 Compare October 1, 2026 22:16
The compound-engineering plugin is enabled on these machines, and its
ce-commit skill also triggers on "commit this". The two skills
disagreed. ce-commit adds a body only when the reason is not obvious,
and without CLAUDE.md in context it copies the recent log. The hook and
`jj push` then refuse its commits. ce-commit also had rules that
commit-craft lacked: when to stop, a subject that names the outcome, and
paths named on `git commit`.

commit-craft gains four of those rules: - Done and stop: done when each
change is a commit that passes   check-commit-msg and none of the task's
changes are left; stop when   there is nothing to commit, with no empty
commit. - The summary names the outcome, not the files, with a bad and a
good   example. - With git only, stage files by name and name them again
on   `git commit -F msg -- <paths>`, because a commit without paths
takes   everything already staged. Never `git add -A`. - A plan unit ID
from compound-engineering can end the subject, as in   `(U3)`, when the
commit implements one unit.

A new section says how the two skills work together. ce-commit's step 3
follows the project convention, which here is commit-craft, so the
format, the required body and the ste100 wording apply. The agent runs
check-commit-msg on ce-commit's message file, and in a jj repository it
uses the jujutsu skill's commands instead of ce-commit's
`git checkout -b` and staging. CLAUDE.md says the same in one sentence.

The push step said to push with `jj push` every time. That disagreed
with the jujutsu skill, which says never to push unless Alyssa asks. It
now says to push only when asked.

Tested: the develop line's tests/commit.bats passes against these files
(15 tests, one new for the plan unit suffix), check-skill-frontmatter.sh
accepts the skill, and rumdl passes on it. ce-commit was read at
compound-engineering-plugin main; its behavior beside this skill was not
run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Soievfadhy2k17VT4VecnT
@alycda alycda closed this Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants