AI-native git workflow tooling — component-scoped commits, ordered by dependency, with semantic conflict resolution.
gait — a homophone for gate, a mashup of git and AI, and a literal description of how code should move through the repository.
AI coding sessions break traditional git workflows. A single session produces a diff spanning multiple directories simultaneously — semantically coherent but physically wide. Left unconstrained, both humans and AI assistants revert to git add . && git commit: a single blob commit that is impossible to review, impossible to resolve when it collides with another session's changes, and a violation of the principle that commits should be atomic units of change.
gait makes the right commit behaviour the path of least resistance for both humans and AI coding assistants.
gait reads a gait.toml file at the repo root that defines the architectural structure of the repository:
- Layers — the horizontal technical stack (e.g.
proto,contracts,api,frontend). Defines commit ordering: lower-order layers are committed first because higher layers depend on them. - Features — vertical business slices (e.g.
auth,billing,workflow). Defines semantic context for AI.
Every file in the repo belongs to exactly one layer and zero or one feature. gait uses this map to:
- Enforce component boundaries at commit time (pre-commit hook) — skeleton layers (cross-boundary contracts) must be committed separately from implementation changes.
- Decompose a wide diff into an ordered commit plan —
gait decomposeproposes the right sequence of commits. - Stage by feature or layer —
gait add <feature>orgait add --layer <layer>stages exactly the right files. - Structure session-end workflows —
gait session-endruns save → decompose → push. - Provide an audited escape hatch —
gait-override commit --reason "..."bypasses the guard and logs the reason.
gait's skeleton = true flag is a direct operationalization of the skeleton
architecture pattern described by Patrick Farry in
Working with Code Assistants: the Skeleton Architecture
(InfoQ, Feb 2026).
The core insight: a codebase has two fundamentally different kinds of code.
| Skeleton | Tissue | |
|---|---|---|
| What it is | Cross-boundary contracts: API schemas, DB migrations, proto definitions, abstract base classes, build config | Vertical feature slices: the concrete business logic that implements those contracts |
| Who owns it | Human architect — reviewed carefully, changed rarely | AI coding assistant — generated freely within the boundaries the skeleton sets |
| What breaks if it drifts | Every other layer that depends on the contract | Only the feature being changed |
| Review standard | Separate PR, peer approval | Normal feature review |
Farry's summary: "Vertical slices give the AI focus, the skeleton gives the human control."
How gait maps to this:
- Features (
[[features]]) are the vertical slices — they scope each AI coding session to a coherent unit of business logic. - Skeleton layers (
skeleton = true) are the skeleton — they mark the cross-boundary contracts that the AI should not silently modify.
When an AI session touches both a skeleton layer and tissue layers, gait decompose splits them into separate, ordered commits. The pre-commit hook blocks mixed commits outright. This transforms the architectural governance from a code-review suggestion into a physical constraint baked into the git workflow.
pip install gaitThen, in your repo root, run:
gait installThis installs:
- A shell function that intercepts
git add/commit/pushand prompts you to usegaitinstead - A pre-commit hook that enforces skeleton layer separation
- Create a
gait.tomlat your repo root (seegait.toml.example). - Run
gait install. - Use
gait statusto see your working tree organised by feature × layer.
$ gait status
feature layer files
─────────────── ─────────── ──────────────────────────────
auth backend src/auth/jwt.py +74 -12
auth api api/src/auth/handler.py +42 -0
auth frontend web/src/features/auth/LoginPage.tsx +88 -3
billing contracts api/schemas/invoice.json +15 -2 [skeleton ⚠]
billing backend src/billing/invoice.py +33 -0
The [skeleton ⚠] flag tells you the billing/contracts change must ship in its own
commit — gait will block a mixed commit at the pre-commit hook.
| Command | Description |
|---|---|
gait status |
Show changed files by feature × layer |
gait add <feature> |
Stage a feature's files |
gait add --layer <layer> |
Stage a layer's files |
gait commit -m <msg> |
Commit with skeleton guard |
gait commit-all |
Stage + commit every decompose group in order |
gait decompose |
Propose ordered commit plan |
gait checkpoint |
Save a local snapshot before a major AI transformation |
gait save |
Push all changes to shadow wip branch |
gait sync |
Pull trunk into current branch (rebase, no push) |
gait push |
Rebase on trunk and push |
gait session-start |
Pull trunk, assert clean tree |
gait session-end |
save → decompose instructions → push |
gait reconcile <A> <B> |
Build AI-assisted conflict workspace |
gait install |
Install shell functions + pre-commit hook |
gait-override commit --reason <reason> |
Bypass skeleton guard (audited) |
See gait.toml.example for a fully annotated template.
The example below shows a realistic fullstack SaaS service. Three skeleton layers
(proto, contracts, config) form the cross-cutting contracts that must be
committed separately and reviewed carefully. Everything else is implementation.
[gait]
version = "1"
repo = "my-service"
trunk = "main"
release = "main"
tag_pattern = "v*"
# ── Skeleton: gRPC definitions ────────────────────────────────────────────────
[[layers]]
name = "proto"
order = 1
skeleton = true
paths = ["proto/", "api/proto/"]
# ── Skeleton: auto-generated stubs (derived from proto, do not edit) ──────────
[[layers]]
name = "generated"
order = 2
generated = true
paths = ["src/generated/", "client/src/generated/"]
# ── Skeleton: API schema + DB migrations ─────────────────────────────────────
[[layers]]
name = "contracts"
order = 3
skeleton = true
paths = ["api/openapi.yaml", "api/schemas/", "db/migrations/"]
# ── Skeleton: build graph config ─────────────────────────────────────────────
[[layers]]
name = "config"
order = 4
skeleton = true
paths = [
"pyproject.toml", "Cargo.toml", "Cargo.lock",
"package.json", "package-lock.json",
"docker-compose.yml", "Justfile",
]
# ── Implementation layers (ordered by dependency) ─────────────────────────────
[[layers]]
name = "backend"
order = 5
paths = ["src/"]
[[layers]]
name = "db-seed"
order = 6
paths = ["db/seed/", "db/fixtures/"]
[[layers]]
name = "api"
order = 7
paths = ["api/src/"]
[[layers]]
name = "clients"
order = 8
paths = ["client/", "sdk/"]
[[layers]]
name = "frontend"
order = 9
paths = ["web/src/"]
[[layers]]
name = "tests"
order = 10
paths = ["tests/", "e2e/"]
[[layers]]
name = "docs"
order = 11
paths = ["docs/", "README.md", "CLAUDE.md"]
# ── Features ─────────────────────────────────────────────────────────────────
[[features]]
name = "auth"
paths = [
"src/auth/", "api/src/auth/",
"web/src/features/auth/", "api/schemas/auth",
]
[[features]]
name = "billing"
paths = [
"src/billing/", "api/src/billing/",
"web/src/features/billing/", "db/migrations/billing_",
]
[[features]]
name = "notifications"
paths = ["src/notifications/", "api/src/notifications/", "web/src/features/notifications/"]
[[features]]
name = "admin"
paths = ["src/admin/", "api/src/admin/", "web/src/features/admin/"]When you run gait decompose on a wide AI diff, gait uses this map to propose
commits in dependency order — skeleton changes first, then implementation layers,
each labelled with its feature:
$ gait decompose
Proposed commit order:
1 [proto] auth proto/auth.proto +24 -0 ← skeleton, review carefully
2 [contracts] auth api/schemas/auth_token.json +12 ← skeleton, review carefully
3 [backend] auth src/auth/jwt.py src/auth/oidc.py +180 -5
4 [api] auth api/src/auth/handler.py +95 -2
5 [frontend] auth web/src/features/auth/LoginPage.tsx +210 -8
6 [tests] auth tests/test_auth.py +66 -0
- Python 3.11+
- git
- No external Python dependencies — uses only the standard library
MIT — see LICENSE.
