| 1 | # grove core model |
| 2 | |
| 3 | The objects grove stores, the rules they obey, and the operations people and agents perform. |
| 4 | Read with [VISION.md](../VISION.md). Built in Rust with no dependencies beyond the standard library. |
| 5 | |
| 6 | ## Overview |
| 7 | |
| 8 | ``` |
| 9 | person ── 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 | |
| 17 | A project's main line is a numbered sequence of snapshots. Work happens in changes. A change |
| 18 | collects revisions of a stack of steps; the owner lands one revision, and its steps become the |
| 19 | next 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 |
| 27 | recorded as *agent A for person P*. A person can revoke an agent, which kills its tokens and |
| 28 | leaves its history intact. |
| 29 | |
| 30 | People can also act directly, from the web. Then the actor is just the person. |
| 31 | |
| 32 | ## Projects |
| 33 | |
| 34 | A project has an owner (a person), a name unique within the owner, a description, and a |
| 35 | visibility: |
| 36 | |
| 37 | - **public:** anyone can read it, open changes and join discussions. |
| 38 | - **private:** only the owner and people they invite. |
| 39 | |
| 40 | Only the owner (and people they make co-owners) can land, decline, or change settings, and |
| 41 | only in person: these are decisions, so an agent acting for the owner can't make them. |
| 42 | |
| 43 | ## Content |
| 44 | |
| 45 | Content 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 | |
| 54 | Names are UTF-8 and may not contain `/` or be `.` or `..`. Binary and text files are the same |
| 55 | to storage; diffs are computed for text and shown as size changes otherwise. |
| 56 | |
| 57 | Ignored paths come from a `.groveignore` file in the project, same syntax as the usual ignore |
| 58 | files. The CLI applies it; the server stores whatever it's given. |
| 59 | |
| 60 | ## Main |
| 61 | |
| 62 | Main 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 | |
| 73 | There are no branches and no merges on main. Work in progress lives in changes. Main only |
| 74 | ever grows; a mistake is undone by landing a change that reverts it. |
| 75 | |
| 76 | Agents and the web refer to versions as `owner/project@n`, and `@main` for the latest. |
| 77 | |
| 78 | ## Changes |
| 79 | |
| 80 | A 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 | ``` |
| 95 | idea ──> 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 | |
| 106 | Declined and withdrawn changes can be reopened. Nothing is ever deleted. |
| 107 | |
| 108 | ### Revisions and steps |
| 109 | |
| 110 | A **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 | |
| 120 | A **step** within a revision is a snapshot plus a message. Each step has a stable id inside |
| 121 | the change, so the same step can be followed across revisions ("step 2 in r1, r2 and r4"), |
| 122 | even as it's edited, reordered, split or dropped. Revising one step means creating a new |
| 123 | revision; later steps carry forward, rebuilt on top of it if needed. |
| 124 | |
| 125 | This keeps Sapling's useful property, that amending is free and nothing is lost, without |
| 126 | tracking each amend as its own object. The diff between two revisions (or between two |
| 127 | versions of one step) shows exactly what the agent did in response to feedback. |
| 128 | |
| 129 | ### Evidence |
| 130 | |
| 131 | Evidence 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 | |
| 141 | grove stores evidence as given and renders what it knows how to: markdown, images, video, |
| 142 | logs, tables, links to previews. Anything else downloads. |
| 143 | |
| 144 | ### Discussion |
| 145 | |
| 146 | A change's discussion is one timeline of **messages** and **events**. |
| 147 | |
| 148 | A 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 |
| 150 | or line it's about. Messages can be edited; every version is kept. |
| 151 | |
| 152 | Events are written by grove: state changes, new revisions, evidence added, landing results. |
| 153 | |
| 154 | Steering is a message. The owner (or their agent) writes in the discussion; the building |
| 155 | agent reads it and responds with a new revision, a reply, or both. grove doesn't interpret |
| 156 | the request. |
| 157 | |
| 158 | ## Landing |
| 159 | |
| 160 | grove never merges. A change lands only on the point it was built on. |
| 161 | |
| 162 | 1. 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. |
| 164 | 2. 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 | |
| 168 | Agents rewrite files; they don't need a merge algorithm to do it, and nothing in grove's |
| 169 | history is ever a mix nobody wrote. |
| 170 | |
| 171 | ## Agents' workflow |
| 172 | |
| 173 | From an agent's side, through the CLI or the API: |
| 174 | |
| 175 | 1. `checkout owner/project`: files at `@main` in a working directory, with `.grove/` |
| 176 | holding local state. |
| 177 | 2. Edit files. `step "message"` records the working directory as a new step; `amend` replaces |
| 178 | the current step; `stack` shows the local steps. |
| 179 | 3. `propose` (or `revise` for an existing change) uploads the local steps as a new revision, |
| 180 | sending only blobs the server doesn't have. |
| 181 | 4. `attach` adds evidence. `say` posts to the discussion. `watch` streams the discussion so |
| 182 | the agent can respond to steering. |
| 183 | 5. `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 | |
| 186 | The MCP server exposes the same operations to agents that speak MCP. |
| 187 | |
| 188 | ## Storage |
| 189 | |
| 190 | grove 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 | |
| 212 | Each repository's current history on Mononoke is read commit by commit, oldest first, and |
| 213 | written as points on main with the original messages, authors and times. Each imported point |
| 214 | records that it came from an import and the original commit hash. Open diffs on the old system |
| 215 | become changes in proposed state with one revision each. Nothing on the old system is removed |
| 216 | until the import of every project has been checked against it. |
| 217 | |
| 218 | ## Open decisions |
| 219 | |
| 220 | 1. **Shared ownership.** Today there are orgs. Should a project be owned by a group of |
| 221 | people, or by one person with co-owners? |
| 222 | 2. **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)? |
| 224 | 3. **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? |
| 226 | 4. **Notifications.** How you hear that something needs your decision: web only, email, |
| 227 | or through your own agent. |
| 228 | 5. **Size limits.** A maximum file and revision size, so an agent can't fill the disk by mistake. |