grove
at @2
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 ──> accepted ──> 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- **accepted:** the owner approved a specific revision. grove lands it straight away; this
104 state is visible only while landing runs, or if landing failed (see Landing).
105- **landed:** its steps are on main.
106- **declined:** the owner said no. **withdrawn:** the proposer gave up.
107
108Declined and withdrawn changes can be reopened as draft. Nothing is ever deleted.
109
110### Revisions and steps
111
112A **revision** is one complete version of the work, immutable once created:
113
114| field | |
115|---|---|
116| `number` | r1, r2, … within the change |
117| `base` | the main point it was built on |
118| `steps` | ordered list; each step builds on the one before |
119| `note` | what changed since the previous revision, optional |
120| `created_by` | agent-for-person |
121
122A **step** within a revision is a snapshot plus a message. Each step has a stable id inside
123the change, so the same step can be followed across revisions ("step 2 in r1, r2 and r4"),
124even as it's edited, reordered, split or dropped. Revising one step means creating a new
125revision; later steps carry forward, rebuilt on top of it if needed.
126
127This keeps Sapling's useful property, that amending is free and nothing is lost, without
128tracking each amend as its own object. The diff between two revisions (or between two
129versions of one step) shows exactly what the agent did in response to feedback.
130
131### Evidence
132
133Evidence is attached to a revision. The agent decides what to attach.
134
135| field | |
136|---|---|
137| `title` | short label |
138| `type` | a media type (`text/markdown`, `image/png`, `application/json`, `text/uri-list`, …) |
139| `content` | a blob, or a URL for something running elsewhere (a preview) |
140| `step` | optional: the step it's about |
141| `created_by` | agent-for-person |
142
143grove stores evidence as given and renders what it knows how to: markdown, images, video,
144logs, tables, links to previews. Anything else downloads.
145
146### Discussion
147
148A change's discussion is one timeline of **messages** and **events**.
149
150A message has an author (person, or agent-for-person), a markdown body, optional attachments
151(stored like evidence), an optional reply-to, and an optional anchor: a revision, step, file
152or line it's about. Messages can be edited; every version is kept.
153
154Events are written by grove: state changes, new revisions, evidence added, landing results.
155
156Steering is a message. The owner (or their agent) writes in the discussion; the building
157agent reads it and responds with a new revision, a reply, or both. grove doesn't interpret
158the request.
159
160## Landing
161
162When the owner accepts revision R of change C:
163
1641. If `R.base` is the current tip of main, each step becomes the next point, in order.
1652. Otherwise grove rebuilds the steps on the current tip with a three-way merge of each file
166 (base snapshot, step snapshot, current tip).
1673. If every file merges cleanly, the rebuilt steps land as above, and the change records
168 which revision landed and what it was rebased onto.
1694. If anything conflicts, nothing lands. The change returns to **proposed** with an event
170 listing the conflicting files, and the agent makes a new revision on the current tip.
171
172Landing is one database transaction: either all of a change's steps land or none do. Points
173are only ever appended.
174
175## Agents' workflow
176
177From an agent's side, through the CLI or the API:
178
1791. `checkout owner/project`: files at `@main` in a working directory, with `.grove/`
180 holding local state.
1812. Edit files. `step "message"` records the working directory as a new step; `amend` replaces
182 the current step; `stack` shows the local steps.
1833. `propose` (or `revise` for an existing change) uploads the local steps as a new revision,
184 sending only blobs the server doesn't have.
1854. `attach` adds evidence. `say` posts to the discussion. `watch` streams the discussion so
186 the agent can respond to steering.
1875. `sync` brings the working directory up to the new tip and rebuilds local steps on it.
188
189The MCP server exposes the same operations to agents that speak MCP.
190
191## Storage
192
193grove has no database. Two kinds of files hold everything:
194
195- **Objects** (blobs and trees) live under `objects/<first 2 hex>/<rest of id>`. An object is
196 written to a temporary file, flushed to disk, renamed into place, and the directory flushed,
197 before any record refers to it. Objects are never rewritten.
198- **The log** holds every other fact as an append-only sequence of records: person and agent
199 created, project created, change opened, revision added, message posted, state changed,
200 point landed. Each record is `length, checksum, payload` (payload is JSON; checksum is the
201 first 8 bytes of its BLAKE3). A write is acknowledged only after the record is flushed to
202 disk. On start, grove replays the log into memory and answers every query from memory.
203- **Torn writes.** If the process dies mid-append, the last record fails its length or
204 checksum. On start, grove cuts the file back to the last whole record. Damage anywhere
205 but the tail stops grove with an error instead of guessing.
206- **One writer.** The store takes an exclusive file lock while open.
207- **Backups** are a copy of the log and the objects directory, both append-only, so backups
208 are incremental. An integrity check confirms every object a record refers to exists and
209 hashes to its id; it runs after every backup and on demand.
210- **Fast start.** When the log grows large, grove writes a checkpoint (the in-memory state at
211 record N, checksummed) and replays only the records after it. The log itself is kept whole.
212
213## Moving existing projects in
214
215Each repository's current history on Mononoke is read commit by commit, oldest first, and
216written as points on main with the original messages, authors and times. Each imported point
217records that it came from an import and the original commit hash. Open diffs on the old system
218become changes in proposed state with one revision each. Nothing on the old system is removed
219until the import of every project has been checked against it.
220
221## Open decisions
222
2231. **Shared ownership.** Today there are orgs. Should a project be owned by a group of
224 people, or by one person with co-owners?
2252. **Agents acting for someone else.** Should a person be able to lend their agent to
226 another project's owner (an agent working *for* the owner, run by someone else)?
2273. **Who runs previews.** Evidence can link to a running preview. Does grove host previews
228 itself (as Pages does today), or only link to what the agent runs?
2294. **Notifications.** How you hear that something needs your decision: web only, email,
230 or through your own agent.
2315. **Size limits.** A maximum file and revision size, so an agent can't fill the disk by mistake.