handrail
Quality guidance for agent coding sessions.
Coding agents don't lack rules — they lack timing. The build command, the testing conventions, the "migrations are additive-only" norm: it's all written down somewhere the agent isn't looking at the moment it matters. Handrail puts your project's quality expectations into the agent's inner loop — at session start, the moment its edits make a norm relevant, and at wrap-up — as guidance it can hold onto, not walls it runs into.
A handrail is something you can hold onto. It is not a wall.
Handrail's job is guidance — not determinism
This is the north star, and it governs every feature decision: handrail guides, it does not enforce. It nudges while you edit, reminds at stop, and at most soft-blocks a single stop (waivable, self-decaying) — everything advisory, everything the agent can override. The delivery target is a working agent's attention at the right moment, nothing more.
Determinism — a pass/fail verdict that gates a merge or fails a build — is a different
tool's job: your CI, a pre-commit hook, a review pipeline. Handrail exposes runnable
gates; it does not own, brand, or bless the wall someone builds on top of them. If you want
enforcement, your CI runs handrail run <ids> and your workflow decides the pass/fail —
that determinism lives in your tool, not in a handrail feature.
The pull toward becoming a CI/enforcement engine is constant, because that's the familiar
quality-gate shape. Resisting it is the discipline: handrail is defined as much by what it
refuses to enforce as by what it delivers. When a proposed feature would let handrail
decide what fails rather than guide what to do next, that's the signal to stop — the
answer is a nudge, not a gate. (See CLAUDE.md for the development guardrails.)
How it works
Your project defines gates — small, handmade checks, one file + one command each, committed to your repo:
# .handrail/api-test-pairing.gate.yaml
expectation: New or changed HTTP surface ships with a test that exercises it.
kind: detector # detector | judge | verdict
semantics: waivable # advisory | waivable | blocking
cmd: bash .handrail/api-test-pairing.sh
paths: [backend/modules]
(YAML is the recommended authoring format; the same fields as .gate.json, which stays
accepted as the canonical, zero-dependency schema.)
The engine (handrail) runs them, records verdicts bound to content (a green
survives commits and unrelated edits; it goes stale only when in-scope content changes),
and exposes three read-only guidance commands — brief, delta, check — that print
plain text and stay silent when there's nothing to say.
Adapters wire those three commands into whatever hooks your agent harness has (Claude Code, Codex, plain git hooks, CI). The agent hears the expectation at the right moment; whether it follows is still its call — except for deterministic verdicts, which the stop-time check can demand (boundedly) before the session wraps up.
What makes it different
- Agent-native. Not a pre-commit runner, not a CI bot: the delivery target is the working agent's context, at three moments in its session.
- Attachment derives from touched files. No upfront configuration ceremony — as the agent's edits reach a gate's paths, that norm attaches to the session and speaks once.
- Three kinds, honestly separated. Deterministic verdicts (build, tests, patch coverage) can block. LLM judges (review gates) produce findings and never block. Cheap detectors ("controller touched, no test touched") nudge early and are waivable: fix it, or put your reason on the record.
- Every gate is handmade — and that's the point. There is no plugin registry. The
cookbook makes handmaking fast: state the norm, pick the rungs,
pick the semantics — and
handrail add <recipe>vendors a pattern's mechanics into your repo (a copy, never a dependency) with the norm left for you to state. - Cooperative trust model. Green means "verified here", not "proven". If you want an unforgeable tier, point your own CI at the same definitions — that enforcement is your CI's to own, not a handrail feature (see the north star above).
Layout
SPEC.md the contract: gate format, exit protocol, ledger, guidance, adapters
bin/handrail reference engine (bash + jq + git)
adapters/ claude-code · codex · git-hooks · github-action
cookbook/ METHOD.md + copyable gate patterns
examples/ runnable worked example (hello-handrail)
init/ `handrail init` scaffolding + the agent-assisted setup interview
tests/ engine test suite (plain bash)
Try it in 30 seconds
examples/hello-handrail/demo.sh
plays a full simulated agent session against a tiny example project: norms attaching as edits touch them, the bounded stop-time demand, the waive-vs-fix fork, and greens surviving a commit.
Install
Handrail is a single bash script — no build, no package (yet). Requires bash, git, and jq; plus yq or python3+PyYAML if you author gates in YAML (JSON gates need nothing extra). macOS and Linux; no Windows (use WSL).
git clone https://github.com/ChristianKohlberg/handrail.git
export PATH="$PWD/handrail/bin:$PATH" # add to your shell profile to persist
cd /path/to/your-repo
handrail init --claude-code # state dirs, config, Claude Code hook adapters
# then: handrail add (vendor a recipe) · handrail new <id> (from scratch)
# handrail validate · edit → handrail run --changed → green before wrapping up
init prints the exact next steps and, for Claude Code, the settings snippet to merge.
Status
Early but real: the spec (v0.1 draft), the reference engine (bin/handrail, tested by
tests/run.sh), the Claude Code / git-hooks adapters, five cookbook patterns, and the
runnable example. Deferred: replay/audit in the engine, verified Codex wiring,
action publishing. Read SPEC.md first — it's four pages and it's the whole
contract.
License
MIT