Standpat How it works Install

Install and try Standpat

Version 0.15.0 is a preview. The install takes 5 minutes and the walk 20.

Get it: the download

In your own terminal. This unpacks to a folder standpat-0.15.0/ and moves it to ~/standpat, the path every command on this page assumes:

cd ~
curl -LO https://www.standpat.ai/standpat-0.15.0.zip
unzip standpat-0.15.0.zip
mv standpat-0.15.0 ~/standpat
cd ~/standpat

If ~/standpat already exists, move it aside first, or leave the folder where it unpacked and change ~/standpat in each path below. Then go on at Install, skipping its git clone line.

Download standpat-0.15.0.zip

Or, for invited testers: the repository

If you were invited to the private repository, clone it with the first line under Install instead. You need the invitation first.

What Standpat is

Standpat is a brake for coding agents, run as a hook by the agent app. Every chat starts free, and typing brake in a chat turns the brake on there. From then on it holds any change to the lines you named until you type a word to let it through, and it keeps a log of the decisions you typed.

Before you install

Install changes three things, and only these:

Before it writes, install proves that taking its own lines back out of the new settings file gives your file back byte for byte, so nothing of yours is removed or changed; the one byte it adds to your part is a comma after the item its block follows. uninstall takes the entries out again under the same proof. --system asks for your password through sudo once, to copy the hook into a folder owned by root, so the agent, which runs as you, cannot change the gate it has to pass.

You need git and Python 3.9 or later. On a Mac the hook runs on the Command Line Tools' Python (xcode-select --install adds it if it is missing); on Linux, on /usr/bin/python3. If your settings file already has a status line of your own, install keeps yours and adds none; then run python3 ~/standpat/standpat/standpat.py status ~/standpat-play in a terminal wherever this page mentions the status line.

Install

In your own terminal, not inside an agent session (from inside one, install refuses and changes nothing). The first line takes the address of the repository you were invited to; skip it if you used the download and are already in ~/standpat.

git clone https://github.com/CHRSRCH/standpat.git ~/standpat
cd ~/standpat
[ -f ~/.claude/settings.json ] && cp ~/.claude/settings.json ~/settings.before-standpat.json
python3 standpat/standpat.py install --system

The cp keeps a copy of your settings file for the check below; with no ~/.claude/settings.json it does nothing, and install creates one. Every command on this page assumes Standpat is at ~/standpat; if you put it elsewhere, change that part of each path.

It worked when install prints a line starting installed: 8 entries in, followed by the hook's path (the folder name after Standpat/ is the version) and the interpreter it chose. diff ~/settings.before-standpat.json ~/.claude/settings.json then shows one block of added lines. The status line under the agent's prompt shows only in a repository with a handbrake file (step 1 below); elsewhere it stays blank.

If install refuses with one line about the file's layout, the repository's README explains it under "Install": --reformat clears it. If it says no interpreter qualifies, it has changed nothing.

A 20-minute walk

The card's words (brake, goal, stamp, park, free and the rest) go to Standpat and never to the agent; redirect is the one whose words reach it. Anything else you type is a message for the agent. The replies below are copied from a rehearsal; your times, version and session ids will differ. The status line starts Standpat ·.

1. A throwaway repository 2 minutes

In your terminal:

mkdir ~/standpat-play && cd ~/standpat-play && git init -q
printf 'print("hello")\n' > app.py && printf '# Notes\n' > notes.md
git add . && git commit -qm start
python3 ~/standpat/standpat/standpat.py init ~/standpat-play

init writes .standpat/handbrake listing nothing yet, and prints Chats in this repository start free and hold nothing until you type `brake` in one. among its lines.

2. A chat starts free 2 minutes

cd ~/standpat-play, then start your agent there. The status line reads Standpat · handbrake off · handbrake lists nothing: write a line in it, then type brake. Ask the agent What does app.py do?: it answers, and Standpat says nothing. Edits pass too.

3. A handbrake naming one file 2 minutes

In your terminal:

cat > ~/standpat-play/.standpat/handbrake <<'EOF'
{
  "standpat_format": 1,
  "project": "Play",
  "must_not_change": [
    {"id": "app", "label": "The app", "matcher": "path", "pattern": "app.py", "class": "other"}
  ],
  "done": "",
  "who_may_widen": ["owner"]
}
EOF
python3 ~/standpat/standpat/standpat.py check ~/standpat-play

check prints Standpat: the handbrake at ~/standpat-play/.standpat/handbrake is valid, version <version>. and the words to type. Note the version (eight characters).

4. brake, then goal demo, then handbrake accept <version> 2 minutes

In the chat. The replies:

Standpat: the handbrake is on in this chat from the agent's next action; type `free` to turn it off. Type `goal <name>` to say what this chat is for, then `handbrake accept <version>` to accept what must not change, then your message for the agent. Nothing was sent to the agent.
Standpat: goal demo is open. Type `handbrake accept <version>` to accept the handbrake, then your message for the agent. Nothing was sent to the agent.
Standpat: handbrake version <version> accepted. Type your message for the agent. Nothing was sent to the agent.

The accept also prints the card of every word (the same as python3 ~/standpat/standpat/standpat.py card). The status line: Standpat · demo · moves 0 of 3 today · 0 of 240 min this sitting · nothing held · load 4 ■□□□□.

5. The hold 2 minutes

Ask the agent Change app.py so it prints hello, world. Its edit is not run, and the agent's output shows:

Standpat: hold 1, held for a stamp: Edit on app.py.
Lines it crosses: The app.
- print("hello")
+ print("hello, world")
Cost: 1 handbrake move. Handbrake moves today so far: 0 of 3.
Type `stamp` to allow exactly this, once, or `park` to stop.

The status line says type stamp.

6. stamp 1 minute

The reply:

Standpat: stamped Edit on app.py, held at <time>, crossing The app. The action is allowed for 30 minutes. Send the agent any message and it will run it.

Send Go ahead. and the agent makes the edit; the status line says moves 1 of 3 today and nothing held.

7. Slices and the review stop 4 minutes

Type slice add docs notes.md says what app.py prints, then slice add usage notes.md has a usage line. The replies:

Standpat: slice docs is 1 of 1 and is open now. Type `slices` to see the list. Nothing was sent to the agent.
Standpat: slice usage is 2 of 2 and waits its turn. Type `slices` to see the list. Nothing was sent to the agent.

Ask Do the docs slice. Standpat tells the agent to end its reply with the line Slice done: docs when it believes the slice is done. When it does, the status line reads slice 1 of 2: docs waits for review · review waits: type accept or redirect, and any further change the agent tries is not run. Type slices to see the review card:

Standpat review: slice 1 of 2, docs.
Why: the agent says the slice is done
Done when: notes.md says what app.py prints
Changed: notes.md
Shell commands run: 0
Stamped: nothing
Refused: nothing
Type `accept` to close the slice, or `redirect <what instead>` to send the agent another way.

accept replies Standpat: slice docs is done, and slice usage is open now. Type your message for the agent. Nothing was sent to the agent. Then ask Do the usage slice., and when it says Slice done: usage, type redirect put the usage line at the top instead. Those words go to the agent, with Standpat's note that the slice stays open until its done line holds; the status line goes back to slice 2 of 2: usage.

8. reserve 25 1 minute

The reply, with no usage reading:

Standpat: your reserve is 25% of the five-hour window, kept until you change it. Standpat has no usage reading, so it cannot say what jobs have left. Type `reserve <n>` to change it. Nothing was sent to the agent.

The status line ends yours 25% · usage unknown. The usage reading comes only on a Pro or Max account, and only after the chat's first reply; with it, the status line ends like used 62% · yours 25% · jobs 13% left and the reply says Used now 62%, so jobs have 13% left until 09:29. with your own figures.

9. park, and the return card 2 minutes

The reply: Standpat: parked; anything held for a stamp was not run, and this sitting used 1 handbrake move. When you are back, send your next message as usual and the card comes with it. Nothing was sent to the agent. The status line says parked since <time>. Send I'm back.: the message goes through, and the agent is told to show you the card first. It runs from Goal: demo through Agent may not: The app and the budget to Done while parked: nothing and Refused while parked: nothing.

10. The garage 1 minute

In your terminal:

cd ~/standpat-play && python3 ~/standpat/standpat/standpat.py garage

One page: Chats, Goal, Slices (1. docs: done at <time>, 2. usage: running), Decisions this sitting (each word you typed with its weight, the load, and The agent's claims: 2), Tokens and Jobs.

11. free 1 minute

The reply:

Standpat: the handbrake is off in this chat, so Standpat holds nothing in it. Type `brake` to turn it on again. Nothing was sent to the agent.

The status line reads handbrake off: type brake to turn it on, and edits to app.py pass again.

Uninstall

In your own terminal:

python3 ~/standpat/standpat/standpat.py uninstall

It prints uninstalled: 8 entries removed from your settings file, then settings: Standpat's status line removed. and settings: restored byte for byte.; cmp ~/settings.before-standpat.json ~/.claude/settings.json then prints nothing if you changed nothing else in the file. What stays: ~/.standpat with your user log, each repository's .standpat/ with its handbrake and log, and the hook copy in /Library/Application Support/Standpat/, which sudo rm -r "/Library/Application Support/Standpat" removes.

Feedback

Testers give feedback only through the issue form "Tester feedback" on the repository they were invited to (Issues, New issue); not by email. What helps most: the steps you took, in order; what you expected; and Standpat's exact words, copied from the reply or the status line. For the version, give the folder name from the install line and, from a clone, the output of git -C ~/standpat log -1 --format=%h.

Known limits

  1. Linux is untried. No one has run Standpat on Linux yet, and --system puts the hook in /Library/Application Support/Standpat/ there too, as on a Mac.
  2. What the always-on guard cannot see. In a free chat, deleting by name a folder that holds Standpat's state (rm -rf ~/standpat-play) is not refused. A second agent session started by a script in another language, or by a command built at run time, is not caught.
  3. Merge reviews. With "review": {"merge": true} in a handbrake, env git merge, command git merge, /usr/bin/git merge and sh -c 'git merge ...' are not recognised as merges and run with no review stop. The handbrake's lines still hold.
  4. The garage during a park prints the park twice on its Goal line (demo, parked since 07:29; parked since 07:29).