Skip to content

Barnett-Studios/commitward

commitward

CI Crates.io Downloads License

A deterministic, fail-open human-sign-off gate for high-stakes commits.

commitward matches a commit's diff against a checkpoint registry and blocks the commit (exit 2) only when a guarded change fires and no one has acknowledged it with a HITL-ACK: trailer in the commit message. It is fail-open by construction: a missing binary, a git error, an unreadable registry — anything that isn't a deliberate, unacknowledged fire — lets the commit through. The gate never blocks on its own failure.

It is not a linter and not a CI framework. It is one small, greppable primitive: approval, not correctness, for agentic commits where a human (or an orchestrator) must sign off on a narrow set of high-stakes changes.

Part of the Barnett Studios agentic-harness toolkit → cxpak · commitward · abproof · …

Install

As a git hook (most common):

# from your repo, install a commit-msg hook that runs commitward
./install-hook.sh

The hook is fail-open and disables with COMMITWARD_HITL=off. If your repo uses a global core.hooksPath, the installer warns and tells you how to target that directory instead.

As a CLI:

brew tap Barnett-Studios/tap && brew install commitward   # macOS/Linux
cargo install commitward                                   # any platform
commitward --base origin/main --format markdown

As a container image:

docker run --rm -v "$PWD:/repo" ghcr.io/barnett-studios/commitward \
  --base origin/main --format markdown

As a library crate (in-process, e.g. for another Rust tool):

[dependencies]
commitward = "0.1"
use commitward::{compile, detect, exit_class, load_checkpoints, merge};

The registry — checkpoints.yaml

A checkpoint fires on one of three modes: paths (regex over changed file paths), content (regex over added lines, with content_exempt_paths), or a code-driven semantic check. commitward ships a default global baseline; a repo adds or overrides checkpoints in .commitward/checkpoints.yaml (repo entries override global ones by name).

version: "1"
checkpoints:
  - name: agent-instructions-self-mod
    summary: "Editing agent-governing instructions"
    paths:
      - "(^|/)CLAUDE\\.md$"
      - "(^|/)rules/.*\\.md$"
  - name: destructive-ops
    summary: "A destructive shell command was added"
    content:
      - "\\brm\\s+-rf\\b"
    content_exempt_paths:
      - "(^|/)docs/"

Regex is the Rust regex crate: no look-around, no back-references.

Acknowledging a fire — HITL-ACK:

When a checkpoint fires, the commit is blocked (exit 2) until the message carries a matching trailer:

Refactor the gate registry

HITL-ACK: gate-self-mod reviewed with the owner

An acknowledged fire returns exit 1 (fired, but allowed to proceed) — distinct from exit 0 (nothing fired). Only exit 2 blocks.

Exit codes

Code Meaning
0 no checkpoint fired — or any fail-open path (missing git/registry, parse error)
1 at least one checkpoint fired and all fired checkpoints are acknowledged
2 at least one checkpoint fired and is unacknowledged — human sign-off required
64 usage error (bad flag / missing argument)

See CONTRACT.md for the full interface and the fail-open guarantee.

Programmatic use — the gate envelope subcommand

For consumption by another tool or from a container, commitward gate reads a JSON request on stdin and writes a {schema_version, status, body} response envelope on stdout — the diff and registries inlined, so it needs no git, no network, and no mounts:

$ echo '{"name_status":"M\tCLAUDE.md","commit_msg":"docs: x",
         "global_registry_yaml":"version: \"1\"\ncheckpoints:\n  - name: cm\n    summary: s\n    paths:\n      - CLAUDE\n"}' \
    | docker run --rm -i --network none ghcr.io/barnett-studios/commitward gate
{"schema_version":"1","status":"ok","body":{"fired":[{"name":"cm",...}],"unacked":["cm"],"exit_class":2}}

Request fields (all optional; evaluation fails open on absent inputs): diff, name_status (git diff --name-status), commit_msg, global_registry_yaml, repo_registry_yaml, base_repo_registry_yaml (for checkpoint-removed detection). The block decision is carried in body.exit_class (0 none fired · 1 fired-but-all-acked · 2 unacked fire), not the process exit code — the process exits 0 on any successful evaluation, so a consumer never mistakes a fired gate for a transport failure. A malformed request yields status:"error" + a non-zero exit. The native git-reading CLI above stays the path for commit-msg hooks and standalone use.

License

Licensed under either of MIT or Apache-2.0 at your option. Unless you explicitly state otherwise, any contribution you intentionally submit for inclusion in the work shall be dual-licensed as above, without any additional terms.


Built by Barnett Studios — part of the agentic-harness toolkit: cxpak · commitward · cascadr · abproof · cordon · slicr.

About

Deterministic, fail-open human-sign-off (HITL) gate for high-stakes agentic commits

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages