grove
at @7
1# grove core model
2
3The objects grove stores, the rules they obey, and the operations people and agents perform.
4Read with [VISION.md](../VISION.md). Built in Rust with no dependencies beyond the standard library.
5
6## Overview
7
8```
9person ── owns ──> project ── main: point 1 → point 2 → … → point n (landed history)
10 │ │
11 └─ agents └─ changes ── discussion (messages, events)
12 (act for │
13 the person) └─ revisions ── steps (snapshot + message)
14 └─ evidence (anything the agent attaches)
15```
16
17A project's main line is a numbered sequence of snapshots. Work happens in changes. A change
18collects revisions of a stack of steps; the owner lands one revision, and its steps become the
19next points on main.
20
21## Identity
22
23**Person.** A human account: username, display name, passkeys for signing in.
24
25**Agent.** Belongs to one person and acts only for them. It has a name the person chose
26("claude on laptop", "operator: grove"), and its own tokens. Every write an agent makes is
27recorded as *agent A for person P*. A person can revoke an agent, which kills its tokens and
28leaves its history intact.
29
30People can also act directly, from the web. Then the actor is just the person.
31
32## Projects
33
34A project has an owner (a person), a name unique within the owner, a description, and a
35visibility:
36
37- **public:** anyone can read it, open changes and join discussions.
38- **private:** only the owner and people they invite.
39
40Only the owner (and people they make co-owners) can land, decline, or change settings, and
41only in person: these are decisions, so an agent acting for the owner can't make them.
42
43## Content
44
45Content is stored by hash, so identical files are stored once and nothing can change in place.
46
47- **Blob.** The bytes of one file. Id: BLAKE3 of `"grove blob\0"` followed by the bytes.
48- **Tree.** One directory: a sorted list of entries, each `name`, `kind` (`file`, `exec`,
49 `link`, `dir`) and the id of a blob or tree. A `link` is a blob holding the link target.
50 Id: BLAKE3 of `"grove tree\0"` followed by the tree's canonical encoding.
51 The prefixes keep a file and a tree with the same bytes from sharing an id.
52- **Snapshot.** The id of a root tree: the whole project at one moment.
53
54Names are UTF-8 and may not contain `/` or be `.` or `..`. Binary and text files are the same
55to storage; diffs are computed for text and shown as size changes otherwise.
56
57Ignored paths come from a `.groveignore` file in the project, same syntax as the usual ignore
58files. The CLI applies it; the server stores whatever it's given.
59
60## Main
61
62Main is the project's history: a linear sequence of **points**, numbered from 1.
63
64| field | |
65|---|---|
66| `n` | 1, 2, 3, … with no gaps |
67| `snapshot` | the project after this point |
68| `change`, `step` | the change and step that produced it |
69| `message` | the step's message |
70| `landed_by` | who landed it (person), and when |
71| `authored_by` | agent-for-person that wrote the step |
72
73There are no branches and no merges on main. Work in progress lives in changes. Main only
74ever grows; a mistake is undone by landing a change that reverts it.
75
76Agents and the web refer to versions as `owner/project@n`, and `@main` for the latest.
77
78## Changes
79
80A change is something someone wants done to a project. It is numbered within the project
81(`owner/project#12`).
82
83| field | |
84|---|---|
85| `number` | per project |
86| `intent` | what it's for, in plain words; editable, every version kept |
87| `proposer` | person or agent-for-person who opened it |
88| `state` | see below |
89| `revisions` | zero or more, in order |
90| `discussion` | messages and events |
91
92### States
93
94```
95idea ──> draft ──> proposed ──> landed
96 │ │ │
97 └────────┴───────────┴──> declined / withdrawn
98```
99
100- **idea:** intent and discussion only, no revisions.
101- **draft:** has revisions; the author is still working. Not asking for a decision.
102- **proposed:** the author asks the owner to decide on the latest revision.
103- **landed:** the owner landed it; its steps are on main.
104- **declined:** the owner said no. **withdrawn:** the proposer gave up.
105
106Declined and withdrawn changes can be reopened. Nothing is ever deleted.
107
108### Revisions and steps
109
110A **revision** is one complete version of the work, immutable once created:
111
112| field | |
113|---|---|
114| `number` | r1, r2, … within the change |
115| `base` | the main point it was built on |
116| `steps` | ordered list; each step builds on the one before |
117| `note` | what changed since the previous revision, optional |
118| `created_by` | agent-for-person |
119
120A **step** within a revision is a snapshot plus a message. Each step has a stable id inside
121the change, so the same step can be followed across revisions ("step 2 in r1, r2 and r4"),
122even as it's edited, reordered, split or dropped. Revising one step means creating a new
123revision; later steps carry forward, rebuilt on top of it if needed.
124
125This keeps Sapling's useful property, that amending is free and nothing is lost, without
126tracking each amend as its own object. The diff between two revisions (or between two
127versions of one step) shows exactly what the agent did in response to feedback.
128
129### Evidence
130
131Evidence is attached to a revision. The agent decides what to attach.
132
133| field | |
134|---|---|
135| `title` | short label |
136| `type` | a media type (`text/markdown`, `image/png`, `application/json`, `text/uri-list`, …) |
137| `content` | a blob, or a URL for something running elsewhere (a preview) |
138| `step` | optional: the step it's about |
139| `created_by` | agent-for-person |
140
141grove stores evidence as given and renders what it knows how to: markdown, images, video,
142logs, tables, links to previews. Anything else downloads.
143
144### Discussion
145
146A change's discussion is one timeline of **messages** and **events**.
147
148A message has an author (person, or agent-for-person), a markdown body, optional attachments
149(stored like evidence), an optional reply-to, and an optional anchor: a revision, step, file
150or line it's about. Messages can be edited; every version is kept.
151
152Events are written by grove: state changes, new revisions, evidence added, landing results.
153
154Steering is a message. The owner (or their agent) writes in the discussion; the building
155agent reads it and responds with a new revision, a reply, or both. grove doesn't interpret
156the request.
157
158## Landing
159
160grove never merges. A change lands only on the point it was built on.
161
1621. When the owner lands revision R of change C, and `R.base` is the tip of main, each step
163 becomes the next point, in order, in one record: all of the change lands or none of it.
1642. If main has moved since R was built, landing is refused. The building agent moves its
165 working directory to the tip (`restart`), redoes the work there, and proposes a new
166 revision. Its earlier revisions stay readable (`diff #C r1`) to redo from.
167
168Agents rewrite files; they don't need a merge algorithm to do it, and nothing in grove's
169history is ever a mix nobody wrote.
170
171## Agents' workflow
172
173From an agent's side, through the CLI or the API:
174
1751. `checkout owner/project`: files at `@main` in a working directory, with `.grove/`
176 holding local state.
1772. Edit files. `step "message"` records the working directory as a new step; `amend` replaces
178 the current step; `stack` shows the local steps.
1793. `propose` (or `revise` for an existing change) uploads the local steps as a new revision,
180 sending only blobs the server doesn't have.
1814. `attach` adds evidence. `say` posts to the discussion. `watch` streams the discussion so
182 the agent can respond to steering.
1835. `sync` brings the working directory up to the new tip. With local steps on an older point,
184 `restart` moves to the tip and the agent redoes the work as a new revision.
185
186The MCP server exposes the same operations to agents that speak MCP.
187
188## Storage
189
190grove has no database. Two kinds of files hold everything:
191
192- **Objects** (blobs and trees) live under `objects/<first 2 hex>/<rest of id>`. An object is
193 written to a temporary file, flushed to disk, renamed into place, and the directory flushed,
194 before any record refers to it. Objects are never rewritten.
195- **The log** holds every other fact as an append-only sequence of records: person and agent
196 created, project created, change opened, revision added, message posted, state changed,
197 point landed. Each record is `length, checksum, payload` (payload is JSON; checksum is the
198 first 8 bytes of its BLAKE3). A write is acknowledged only after the record is flushed to
199 disk. On start, grove replays the log into memory and answers every query from memory.
200- **Torn writes.** If the process dies mid-append, the last record fails its length or
201 checksum. On start, grove cuts the file back to the last whole record. Damage anywhere
202 but the tail stops grove with an error instead of guessing.
203- **One writer.** The store takes an exclusive file lock while open.
204- **Backups** are a copy of the log and the objects directory, both append-only, so backups
205 are incremental. An integrity check confirms every object a record refers to exists and
206 hashes to its id; it runs after every backup and on demand.
207- **Fast start.** When the log grows large, grove writes a checkpoint (the in-memory state at
208 record N, checksummed) and replays only the records after it. The log itself is kept whole.
209
210## Moving existing projects in
211
212Each repository's current history on Mononoke is read commit by commit, oldest first, and
213written as points on main with the original messages, authors and times. Each imported point
214records that it came from an import and the original commit hash. Open diffs on the old system
215become changes in proposed state with one revision each. Nothing on the old system is removed
216until the import of every project has been checked against it.
217
218## Open decisions
219
2201. **Shared ownership.** Today there are orgs. Should a project be owned by a group of
221 people, or by one person with co-owners?
2222. **Agents acting for someone else.** Should a person be able to lend their agent to
223 another project's owner (an agent working *for* the owner, run by someone else)?
2243. **Who runs previews.** Evidence can link to a running preview. Does grove host previews
225 itself (as Pages does today), or only link to what the agent runs?
2264. **Notifications.** How you hear that something needs your decision: web only, email,
227 or through your own agent.
2285. **Size limits.** A maximum file and revision size, so an agent can't fill the disk by mistake.