Skip to main content

statutor

Typed project-ledger framework for agentic repos. A statutor is one who enacts (agent noun of statuere) — which is the thesis: repo instruction files are a state machine of typed registers, each with a mutation policy and exactly one writer, enforced by hooks and git, not by prose.

Plane Files Policy Enforced by
Constitution AGENTS.md (+ CLAUDE.md = @AGENTS.md) hard cap 200 lines hook + git floor
State HANDOFF.md overwrite-only, ≤ 40 lines, required sections hook + git floor
State TASKS.md stable T-NNNN ids; mutable checkbox/detail/order hook + git floor + doctor
Log DECISIONS.md append-only, insertions only, supersede-never-edit hook + git floor
Plan ROADMAP.md, plans/ → plans/archive/ (frozen) archive immutable hook + git floor

Plus a bash guard on every harness: shell writes to governed files (>>, sed -i, tee, ...) are denied — the editor tools are the audited path. Automatic adapters stay silent outside a repository explicitly marked by .statutor.yaml. A complete quoted heredoc used as git commit -F - message data is not scanned as shell code; its opener and all actual command lines remain guarded. No hand-maintained CHANGELOG.md: git log + conventional commits.

The policies are ordered by reversibility and the guardrails follow that order: a HANDOFF overwrite is reversible, a TASKS checkbox or detail edit is reversible with history, a DECISIONS append is irreversible by policy, and an archived plan is frozen (only git mv INTO plans/archive/ is allowed). Text an agent reads or quotes — a note, a heredoc, a tool result — informs its proposal but never authorizes a mutation: only the declared policy under the committed HEAD snapshot gates a write, in-loop and at the git floor alike.

State task identities are durable: an existing ID cannot disappear or be renamed, while its checkbox, detail, and position may change. New IDs advance beyond the committed maximum. v0.4 intentionally has no pruning operation; completed entries remain until a separately specified identity-preserving archive exists (D-0016).

Kernel / adapter architecture

core/statutor_core.py     single-file kernel: validate() + embedded templates
                      modes: hook | check | staged | init | trust approve
                      (fail-open hooks; staged floor fails closed)
core/statutor_doctor.py   drift linter (stale stamps, budgets, unarchived plans)
core/statutor_global.py   global-layer roots, schemas, hashes, CAS, backups
core/statutor_global_cli.py  opt-in global instruction lifecycle and CLI
core/statutor_skills.py   portable Agent Skill validation and projections
hooks/stop_doctor.py  Claude Code Stop hook: runs statutor-doctor after each
                      turn and surfaces its WARN/ERROR lines as
                      additionalContext — non-blocking, silent when the
                      ledger is clean or the cwd isn't a statutor ledger at all
pyproject.toml        pipx install → `statutor`, `statutor-doctor` on PATH
Adapter Mechanism Coverage
Claude Code (repo root is the plugin) PreToolUse Write|Edit|Bash|apply_patch → statutor hook; Stop → hooks/stop_doctor.py full in-loop + drift surfacing
Google Antigravity (adapters/antigravity/) PreToolUse (run_command|write_to_file|replace_file_content) → statutor hook; Stop → statutor hook --stop full in-loop + drift surfacing
OpenCode (adapters/opencode/statutor.ts) tool.execute.before → statutor check --if-ledger in-loop (write/edit/bash)¹
Codex CLI (adapters/codex/) PreToolUse (Claude-compatible protocol) → statutor hook bash guard + apply_patch²
git (adapters/git/, .pre-commit-hooks.yaml) statutor staged in local pre-commit and CI staged-index backstop
native (crates/statutor/) statutor-staged, conformance-gated ≡ Python local staged floor without Python
Custom harnesses (statutor check, or import validate) embed in your own tool dispatch full in-loop

¹ in-loop for write/edit/bash/apply_patch; the kernel parses apply_patch envelopes (T-0011), with two partial-diff blind spots: required sections on an Update File and server-namespaced MCP tool ids — the git floor covers both. Subagent tool calls DO fire plugin hooks (verified opencode v1.18.21, 2026-08-21); the opposite claim (sst/opencode#5894) was a misdiagnosis, stale-closed 2026-04-15. ² Codex hooks are on by default since rust-v0.124.0 (2026-04-23) — the old [features].codex_hooks flag is a deprecated legacy alias, and hooks need a one-time trust approval (/hooks). Codex sends edits as tool_name apply_patch + {"command": "<envelope>"}, which guard_apply_patch() parses (frozen/delete/ append-only/cap checks; see adapters/codex/). Residual gaps: MCP tools and Update-File section checks — so the git floor remains mandatory there.

Worker provenance (v0.5, statutor machine/statutor worker): automatic hooks on every host prove an attempt only and record nothing by themselves — the hook path stays fail-open. Ingress is executor- or custom-run (begin → record --event attempt → attributed rewrite → complete --session); --event mutation under an automatic harness is denied. worker capabilities reports proven surfaces and gaps per host (verified against Claude Code 2.1.258, Codex CLI 0.152.1, OpenCode 1.18.20 baselines); worker compare <ref> explains sibling handoffs read-only. The machine-local registry is a bounded ring (50 sessions per project, 20 leases per worktree), not an audit trail; durable provenance is the attributed HANDOFF lineage in Git history, which lives as long as the repository does.

Install

pipx install statutor                # or: pip install -e .
statutor init .                      # scaffold any repo, any harness
statutor init . --type none          # governed files only; env: STATUTOR_INIT_TYPE

# Claude Code (this repo doubles as the plugin):
/plugin marketplace add https://github.com/hoohugokim/statutor
/plugin install statutor@hoo-plugins --scope project
# then: /statutor-init  /handoff  /decide  /statutor-doctor

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/hoohugokim/statutor
    rev: v0.6.0
    hooks: [{id: statutor}]

init never mandates a layout (D-0024). It selects a profile — --type NAME, else STATUTOR_INIT_TYPE, else the first marker found in the order below, else min — and adds only empty conventional directories on top of the governed files, plans/archive/, notes/, and the CLAUDE.md bridge. Present paths are skipped, never overwritten, adopted, or followed; no profile writes policy, constitution content, or ecosystem files, and the min fallback prints and creates exactly what the pre-v0.6 init did.

Profile Adds Selected by
python tests/ pyproject.toml, setup.py, setup.cfg
rust tests/ Cargo.toml
node test/ package.json
docs docs/ mkdocs.yml, _quarto.yml, an existing docs/
min nothing further fallback when no marker matches, or explicitly
none nothing, not even plans/archive/ or notes/ --type none or STATUTOR_INIT_TYPE=none only

Per-repo policy lives in .statutor.yaml. In-loop checks use the committed HEAD snapshot; the git floor judges the transaction under both HEAD and the candidate index snapshot, so an unstaged or co-staged weakening cannot disable existing rules. The format is a strict, zero-dependency YAML subset shared by Python and Rust; malformed or unsupported committed/candidate policy denies. Before the marker's first commit, automatic hooks use embedded defaults. An edited worktree policy does not change hook behavior until it passes trust approval and becomes HEAD in a separate commit.

After bootstrap, changing .statutor.yaml or Statutor's exact CLAUDE.md → @AGENTS.md bridge requires an exact-tree Git-local receipt:

git add .statutor.yaml
statutor trust approve . --decision D-0015 --reason "why this changes trust"
statutor staged .

Approval displays the reserved diff and all staged paths, then requires the complete candidate tree ID. The mode-0600 receipt expires on any HEAD or index change and is never committed.

Release gate

With Python build/pytest, Cargo, and npm available, stage the candidate and run python scripts/release_gate.py. The gate tests the exact Git index, audits all package payloads, builds the Python sdist and wheel in scratch space, installs the wheel into an isolated target, and smoke-tests both console scripts. A tagged release additionally passes --tag vX.Y.Z --dist-dir dist; verified artifacts are copied only after every check succeeds.

The v0.4 global layer also has a separate, opt-in current-host probe: python scripts/global_e2e.py --json. It pins the tested Claude, Codex, and OpenCode versions, rehomes every host under a temporary profile, performs no model or network request, and proves native discovery where an offline surface exists, modified-target refusal, and exact uninstall recovery. It is not part of the hermetic release gate and never mutates the caller's real home.

Portable user layer (v0.4)

The global layer is opt-in and separate from project ledgers. init creates only human-owned canonical sources and versioned state; it does not change any host file. plan, status, and --json are read-only. apply owns only whole generated files it created, while adopt HOST backs up an existing regular file and imports its bytes verbatim into that host's canonical overlay.

statutor global init
statutor global plan --json
statutor global apply --host codex
statutor global status --json
statutor global doctor --json
statutor global adopt claude
statutor global uninstall --host codex

statutor global skill import ./my-skill
statutor global skill plan --json
statutor global skill apply
statutor global skill status --json
statutor global skill sync
statutor global skill uninstall

Every mutation prints its resolved plan first. Existing unmanaged targets are conflicts, managed hand edits are never overwritten or removed, and uninstall restores the original backup. --home, --config-root, and --state-root provide explicit isolated roots for tests and alternate profiles. Codex overrides and OpenCode's Claude fallback are reported as precedence warnings; Statutor never rewrites them.

Imported skills are copied to a new human-owned canonical source, then projected as complete trees to $HOME/.agents/skills and Claude's personal skills root. Core SKILL.md metadata and the complete tree are validated; host-specific frontmatter is preserved. Identical unmanaged trees require explicit --adopt-identical, differing or hand-edited trees are conflicts, and names in a foreign .agents/.skill-lock.json remain entirely foreign. OpenCode discovers the portable and Claude projections, so Statutor reports whether those duplicates are identical instead of creating a third native OpenCode copy.

Unified global status is a fast, read-only inventory of effective instruction precedence, ownership, disabled/foreign skills, duplicate names, legacy/native/ admin/plugin roots, and catalog size. global doctor performs the deeper full- tree audit: managed drift and receipt topology, generated headers, unsafe links, active divergent duplicates, Codex's configured instruction cap, and Statutor's 32 KiB skill-description diagnostic budget (reported explicitly as a Statutor budget, not a host limit). Status exits zero when inventory completes; doctor exits one when the audit contains errors. Neither command invokes a host binary or performs a network request.

Provenance

Framework doctrine distilled from: the AGENTS.md open standard https://agents.md/, Claude Code memory & hooks docs https://code.claude.com/docs/en/memory, https://code.claude.com/docs/en/hooks, Anthropic on long-running agent harnesses https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents and context engineering https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents, MADR https://adr.github.io/madr/, Keep a Changelog https://keepachangelog.com/, Conventional Commits https://www.conventionalcommits.org/, pre-commit https://pre-commit.com/.

Metadata

Release files for statutor 0.7.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for statutor 0.7.1
File Size Uploaded Provenance Actions
statutor-0.7.1.tar.gz 64.7 kB With provenance Details for statutor-0.7.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for statutor 0.7.1
File Interpreter ABI Platform Actions
statutor-0.7.1-py3-none-any.whl Python 3 none any Details for statutor-0.7.1-py3-none-any.whl

Total release size: 133.6 kB

Release files / statutor-0.7.1.tar.gz

Download URL statutor-0.7.1.tar.gz
Size 64.7 kB
Tags Source
SHA-256 checksum
How to use checksums
e8d909f3eee86b4a77e219e582a352f6cb831a9583370fee46e0179b4a907ec8
BLAKE2b-256 checksum
How to use checksums
26cb2907c28c353dbec818460d4d16bac05113bd9f81fae202ec0030c6986786
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Release files / statutor-0.7.1-py3-none-any.whl

Download URL statutor-0.7.1-py3-none-any.whl
Size 69.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b1ef293c0931c40458ab7e5e2cdf72ebca99793e6d75aa8365135248d948c730
BLAKE2b-256 checksum
How to use checksums
0eb2e8a2b6b9a83d00ecefc38575d5d3e0a50c0e1c774220d3ca34063d6528dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Security for statutor 0.7.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for statutor-0.7.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page