StayPut
StayPut — make sure your coding agent did the work where you told it to.
Deterministic integrity primitive for the Git locus an operator explicitly sealed.
StayPut is a host-neutral library and CLI. Hosts consume it with thin examples
under examples/. It is not an AI evaluator, sandbox, or workflow engine.
What StayPut checks
After an operator runs stayput save, stayput check verifies that the
delivered Git locus matches the sealed approval:
- same repository lineage (
repo_idfrom the sealed base commit) - same worktree (
worktree_key) - sealed
base_commitis an ancestor ofHEAD - every Git-visible delivered mutation is inside
allowed_paths - if
instruction_digestwas sealed, supplied instruction bytes digest to that value
What StayPut does not check
StayPut does not determine correctness, test honesty, intent, safety, unauthorized data, runtime behavior, or model-context accuracy.
allowed_paths is not a sandbox. instruction_digest=null means instruction
integrity is not verified.
Explicit save / approval
cwd is not approval. There is no SessionStart save, TOFU, or silent overwrite.
stayput save --allowed-path src --allowed-path tests
Optional instruction binding — freeze bytes, then seal those exact bytes:
stayput save --instruction-file PLAN.md --allowed-path src
--replace is required to overwrite an existing .stayput/approval.json.
Check
stayput check --json
Check never writes. It verifies record_digest before trusting the snapshot,
then compares the delivered Git locus. Git is the authority — not agent-reported
branch, worktree, or hook payload metadata.
allowed_paths
Literal repo-relative prefixes. Default ["."] means any Git-visible mutation
inside the approved repository. src/auth does not match src/auth_backup.
Globs and regex are not supported. A symlink whose resolved target leaves the
repository is PATH_OUTSIDE_ALLOWLIST.
Optional instruction binding
--instruction-file is read_bytes(). No whitespace, newline, or Unicode
normalization. A sealed digest with omitted bytes is INSTRUCTION_REQUIRED
(operational error), not MATCH and not drift.
Capture of prompt bytes is not approval. The human still runs stayput save.
Exit codes
| Exit | Meaning |
|---|---|
0 |
project / save success, or check MATCH |
1 |
operational ERROR (usage, missing approval, cannot prove) |
2 |
check MISMATCH |
CLI
stayput project [--cwd PATH] [--instruction-file FILE] [--allowed-path PATH ...] [--json]
stayput save [--cwd PATH] [--path FILE] [--instruction-file FILE] [--allowed-path PATH ...] [--replace] [--json]
stayput check [--cwd PATH] [--path FILE] [--instruction-file FILE] [--json]
Requires Python >=3.12,<3.13, salt-grain==0.1.0, and Git.
pip install -e '.[dev]'
Host examples
Same flow everywhere: operator save → agent work → hook/CI check.
| Host | Example |
|---|---|
| Claude Code | examples/claude-code/ |
| Cursor / Cursor Cloud | examples/cursor/ |
| OpenHands | examples/openhands/ |
| Ordinary CI | examples/ci/ |
CI needs full history (fetch-depth: 0). Shallow clones often fail closed.
See docs/usage.md and examples/README.md.
Snapshot (stayput.snapshot.v0.1)
| Field | Meaning |
|---|---|
schema_version |
stayput.snapshot.v0.1 |
instruction_digest |
SHA-256 of explicit instruction bytes, or null |
repo_id |
Sorted unique 40-hex roots of sealed-base ancestry |
worktree_key |
"" for the main worktree; otherwise a POSIX relative git-dir key |
base_commit |
40 lowercase hex |
allowed_paths |
Literal repo-relative prefixes; default ["."] |
Approval wrapper stayput.approval.v0.1: schema_version, snapshot,
record_digest. Default path: .stayput/approval.json.
Tests
python -m pytest -q
Windows
Windows Git/worktree CI is deferred. See docs/windows.md.
Release files for stayput 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| stayput-0.1.0.tar.gz | 41.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| stayput-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 67.9 kB
Release files / stayput-0.1.0.tar.gz
| Download URL | stayput-0.1.0.tar.gz |
|---|---|
| Size | 41.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
158f8a62095c2e0ae2d40339c3ac84d7a509a099c1a6b3df04a500d26e39bb5b
|
|
BLAKE2b-256 checksum How to use checksums |
65e4ca70caba18913ce29595cdb2e4e710c9591bc738a94a4352231748cbc596
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 15, 2026.
Transparency logRelease files / stayput-0.1.0-py3-none-any.whl
| Download URL | stayput-0.1.0-py3-none-any.whl |
|---|---|
| Size | 26.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
18b3ef3ebffa1bf311ecc0e5f05d0aa54d42f6009232854fb842f52182f65782
|
|
BLAKE2b-256 checksum How to use checksums |
fb79be2bc006aa542be532aea091a05e9f2598442ffb527d594250f708aa2010
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 15, 2026.
Transparency log