Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/commit-messages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Every commit on a pull request follows the commit-craft format
# (tools/commit/check-commit-msg). PRs here are squash-merged and the squash
# body is the branch commits' messages, so they are checked, not only the
# title. Pull requests only: a direct push can carry older commits.
name: commit messages

on:
pull_request:

permissions:
contents: read

jobs:
check:
name: Check commit messages
runs-on: ubuntu-latest
steps:
# Pinned to a commit; the comment is the tag.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Check commit messages
env:
BASE: ${{ github.event.pull_request.base.sha }}
HEAD: ${{ github.event.pull_request.head.sha }}
run: tools/commit/check-commits "$BASE..$HEAD"
41 changes: 25 additions & 16 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,30 @@ jj log -r '<change-id>::' --limit 10

### Commit Message Format

```
Short imperative summary (50 chars or less)

Longer explanation of the change and why it was made. Focus on:
- What problem does this solve?
- What alternatives were considered?
- What did you learn?
- What tradeoffs were made?

Specific changes:
- Bullet points for key modifications
- File paths and what changed
- Configuration decisions

[Optional: Link to resources or documentation]
```
Every commit message follows the **commit-craft** skill
(`tools/agents/skills/commit-craft/SKILL.md`, installed at
`~/.agents/skills/commit-craft`). Read it before writing or changing any
message, in any harness, even when skills are not loaded for you
automatically. It defines the format, `<area>(<scope>): <summary>` plus a
body in a fixed order, and gives the body's wording to the ste100 skill. The
shape is enforced outside the skill, by the scripts in `tools/commit/`: run
`check-commit-msg` on a message before you set it, and push with `jj push`,
which checks the stack first (`tools/jujutsu/config`). A `git commit` in this
checkout runs the same check as a hook, and CI checks every commit on a pull
request. The jujutsu skill covers only the commands that set the message.
The same applies when compound-engineering's `ce-commit` makes the commit:
commit-craft is the project convention its step 3 defers to, so the body is
required even where ce-commit would leave it out.

Two additions for this repository:

- When a change came out of a mistake or a dead end, the body's "what and
how" paragraph also says what was learned. That is the story the history
is meant to tell.
- PRs here are squash-merged, so the PR title becomes the commit subject on
`main`. Write the title in the same subject format; GitHub appends ` (#N)`.
The squash body is every branch commit's message in turn (each subject as a
`* ` line), so branch commits follow the format too.

## Repository Structure

Expand Down Expand Up @@ -867,6 +875,7 @@ This document should evolve as patterns emerge. When you:
**Add it here** and commit with a message explaining what prompted the addition.

---
*Last updated: 2026-10-01 - Replaced the inline commit message format with a pointer to the commit-craft skill, and moved the enforcement out of the skill into configuration (a jj draft template and `jj push` alias, a git hook for this checkout, a CI job), because a skill only works when an agent loads it and the format must hold for every commit; the skill keeps what no check can judge and gives the body's wording to ste100*
*Last updated: 2026-09-29 - The scheduled flake update broke on `nix/build-skill` not existing: nix-skills was renamed to agents.nix, history rewritten and restructured. `lib/skills-sh.nix` now goes through upstream's public overlay, which the restructure made cheap (measured), instead of the internals it used to avoid a then-18s eval; supabase-postgres-best-practices is pinned by hand because the index dropped its repo*
*Last updated: 2026-09-21 - Ghost (#56): ghost.build is shutting down, so venari now runs a server for its OpenAPI contract from the alycda/ghost fork, with the CLI packaged from that fork and driven by an env-setting wrapper; recorded the two client-side assumptions (`tsdb` dbname, viper's empty-env handling) that the "no CLI patch needed" plan missed*
*Last updated: 2026-09-21 - Added "prebuilt binaries in a persisted `$HOME` are image-scoped state" to Tools Nix Can't Fully Manage, after a rustup toolchain in the devhome volume survived an image rebuild and left `cargo` erroring ENOENT for a loader that no longer existed — with the corollary that an activation step guarded on "is it installed" can never repair state that went bad in place (#80)*
Expand Down
37 changes: 26 additions & 11 deletions home-manager/modules/common.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ let
# Core packages shared with devShells (defined in lib/core-packages.nix)
# docker on OSX is installed by homebrew (Docker Desktop/Orbstack)
corePackages = import ../../lib/core-packages.nix pkgs;

# The commit message checks (tools/commit), on PATH for jj's `jj push`
# alias. One package, because check-commits looks for check-commit-msg
# next to itself.
commitChecks = pkgs.runCommandLocal "commit-checks" { } ''
mkdir -p $out/bin
cp ${../../tools/commit}/* $out/bin/
'';
in
{
imports = [
Expand Down Expand Up @@ -39,7 +47,7 @@ in
# Core packages across all profiles
# Note: helix is configured via ./tools/helix.nix (programs.helix)
# Note: claude-code CLI installed here (binary only; global rules managed by ./tools/claude-code.nix)
packages = corePackages ++ [ pkgs.claude-code pkgs.presenterm ];
packages = corePackages ++ [ pkgs.claude-code pkgs.presenterm commitChecks ];

# Set helix as default editor
sessionVariables = {
Expand Down Expand Up @@ -75,14 +83,21 @@ in
};
};

# Invocable cheatsheets as just's GLOBAL justfile: `just -g <recipe>` from
# anywhere (verified on just 1.58: -g recipes run with the invocation
# directory as cwd). The shim imports the store copy by absolute path, and
# `import?` leaves an optional hook so a profile or machine can layer its
# own recipes (e.g. the work profile's ditto-worktree recipes) by writing
# ~/.config/just/local.just instead of fighting over this file.
xdg.configFile."just/justfile".text = ''
import '${../../tools/just}/global.justfile'
import? '~/.config/just/local.just'
'';
xdg.configFile = {
# Invocable cheatsheets as just's GLOBAL justfile: `just -g <recipe>` from
# anywhere (verified on just 1.58: -g recipes run with the invocation
# directory as cwd). The shim imports the store copy by absolute path, and
# `import?` leaves an optional hook so a profile or machine can layer its
# own recipes (e.g. the work profile's ditto-worktree recipes) by writing
# ~/.config/just/local.just instead of fighting over this file.
"just/justfile".text = ''
import '${../../tools/just}/global.justfile'
import? '~/.config/just/local.just'
'';

# jj settings shared with the account without Nix: the commit message
# draft template and the `jj push` alias. conf.d, not config.toml, which
# `jj config set --user` writes.
"jj/conf.d/dotfiles.toml".source = ../../tools/jujutsu/config;
};
}
12 changes: 11 additions & 1 deletion home-manager/modules/git.nix
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,17 @@
# Settings that work without Nix (defaultBranch, pull/push behaviour,
# aliases) live in tools/git/config, which the mise account includes
# from ~/.gitconfig. Only what needs Nix or a secret stays below.
includes = [ { path = ../../tools/git/config; } ];
includes = [
{ path = ../../tools/git/config; }
]
# The commit-msg hook, in the dotfiles checkouts only (core.hooksPath
# replaces .git/hooks for the whole repo). tools/git/config has the same
# includeIf for the account without Nix, but its relative path finds
# nothing in the store copy, so the include is repeated here.
++ map (dir: {
path = ../../tools/git/dotfiles-repo.gitconfig;
condition = "gitdir:${dir}";
}) [ "~/dotfiles/" "/workspaces/dotfiles/" ];

settings = {
include.path = "${config.home.homeDirectory}/.local/share/agenix/git-config";
Expand Down
3 changes: 2 additions & 1 deletion home-manager/modules/tools/agent-skills.nix
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
# Repo skills:
# brag-doc - promo-packet impact entries from raw work notes
# cf-now - private file sharing from Cloudflare R2 (pre-signed URLs)
# commit-craft - commit-message craft + jj describe/push workflow
# commit-craft - what a commit message says; the shape is checked
# by tools/commit (jj push, git hook, CI)
# entity-level-git - Ataraxy Labs stack: sem (entity diff/blame/impact),
# weave (semantic merge driver), inspect (review triage)
# failure-doc - failures as deliberate-learning records
Expand Down
Loading
Loading