Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and refuses to create a commit from any tree that has not passed.
</p>

<p align="center">
Built for coding agents: one verb, JSON on every command, stable exit codes, and a drop-in skill.<br>
An open source primitive from <a href="https://reachpad.dev">reachpad</a> · Apache-2.0 · Linux and macOS
</p>

Expand Down Expand Up @@ -210,15 +211,35 @@ freely with a running watcher.

### For coding agents

The agent-facing contract is one verb and one exit-code table:
An agent is the primary user. The whole interface an agent needs is one
verb and one exit-code table:

```sh
greentree gate --json -m "<message>"
greentree gate --json -m "<message>" # verify, then commit the tree; idempotent
```

[docs/AGENTS.md](docs/AGENTS.md) has the copy-paste project-instructions
Every command takes `--json` (exactly one object on stdout) and returns a
stable exit code, so an agent branches on the code, never on parsed text:
`0` committed, `10` a check failed (output at `.log_tail`), `11` not
verified yet (run `test` first), `12` unsnapshotable, `13` locked. No
prose to interpret, no re-running "to be sure" — a cache hit proves the
content already passed.

**Drop-in skill.** [`skills/greentree/`](skills/greentree/SKILL.md) is a
Claude Code skill that teaches an agent the workflow and the exit codes.
Copy it into a project so the agent loads it automatically:

```sh
cp -r skills/greentree .claude/skills/
```

The skill tells the agent to land changes with `greentree gate` instead of
raw `git commit`/`git push`, how to react to each exit code, and not to
re-run cached verdicts.

**Enforce it.** [docs/AGENTS.md](docs/AGENTS.md) has a project-instructions
snippet and a Claude Code hook that blocks `git commit`/`git push` so the
gate is the only door.
gate is the only door, even if the agent forgets.

### The gate before GitHub

Expand Down
72 changes: 72 additions & 0 deletions skills/greentree/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
name: greentree
description: Land code changes through greentree instead of raw git. Use in any repo that has a greentree.yaml or the greentree binary on PATH, whenever about to commit, push, run tests, or land a change, or when asked "is it green" / "did it pass". greentree verifies the working tree, caches the result by content, and only lets a verified tree become a commit.
---

# greentree: verify the tree before it becomes a commit

greentree runs this repo's checks against the current working tree, caches
the verdict by content (tree hash), and creates the commit from the exact
tree that passed. Use it instead of running tests by hand and then
`git commit`/`git push`.

Applies when the repo has a `greentree.yaml`, or `greentree` is on PATH and
`greentree status` succeeds.

## Landing a change: one command

```sh
greentree gate --json -m "<commit message>"
```

`gate` runs the required checks (instant when this exact content already
passed), then commits the verified tree. It is idempotent: running it twice
in a row is a no-op. This replaces `git add`/`git commit`/`git push`.

Add `--push` to also push. Without it, `gate` commits locally only.

## Branch on the exit code, never on the text

| exit | meaning | do this |
|---|---|---|
| 0 | verified and committed | done |
| 10 | a check failed | read `.log_tail` in the JSON, fix the code, run `gate` again |
| 11 | tree not verified yet | run `greentree test --json`, then `gate` |
| 12 | repo mid-merge/rebase, or a dirty submodule | resolve it, then `gate` |
| 13 | another greentree run is in progress | wait a moment, then retry |
| 14 | config error | read `.error`; fix `greentree.yaml` |
| 15 | publish/push failed | read `.error` (e.g. remote rejected, no token) |

## While iterating

- `greentree test --json` runs the checks without committing. `.ok` is the
answer. Per-check detail is in `.results`.
- `greentree status --json` answers "would a commit succeed right now?"
without running anything new. Read `.publishable`.

## Rules that keep it correct

- Do not run `git commit` or `git push` directly in a greentree repo. The
commit must be built from the tested tree; `gate` does that. Bypassing it
produces an unverified commit.
- A cache hit is as authoritative as a fresh run. The tree hash proves the
content is identical, so do NOT re-run "to be sure" — that is wasted work.
- Do not edit files while `gate` or `test` is running. If the tree changes
mid-run the verdict is `cancelled` (it binds to no tree) and you will
just rerun it. Make your edits, then run the check.
- When a check fails (exit 10), the output is in the JSON: `.log` is the
full log file path, `.log_tail` is the last lines. Read it before
guessing at a fix.

## Gate before GitHub

The published commit is built from the exact verified tree, so it needs no
re-check. If the repo enforces a `greentree/<check>` required status, a
plain `git push` can be made mergeable by attesting the pushed commit:

```sh
git push
greentree attest --json # posts greentree/<check> on HEAD if verified
```

`gate --push` does the commit, push, and status in one step.