Data format
zz keeps data in five places:
| Where | What | Travels with git clone? |
|---|---|---|
| The repo's default branch | Code and its history | Yes |
| Git notes in the repo | The Ladder | With git clone --mirror, or by fetching refs/notes/* |
The repo's zz-meta branch |
Description, Policy, owner, Role grants | Yes |
| A Durable Object per repo | Lane, Ask, and Answer state | No |
| Cloudflare D1 | Accounts, tokens, the repo index, the search index | No |
The design goal is that the repo carries everything that matters, so a repo can leave the platform with its history, knowledge, and settings intact. Code, the Ladder, and repo settings meet that goal today. Lane history does not yet. See Architecture.
Ladder
The Ladder is stored as git notes under refs/notes/zz/ladder/:
| Ref | Attached to | Holds |
|---|---|---|
refs/notes/zz/ladder/code |
The blob, tree, or root tree a rung describes | repo, directory, file rungs and everything nested under them |
refs/notes/zz/ladder/changeset |
The commit a rung describes | changeset rungs with their filechange and linechange children |
Each note is JSON holding one rung and its embedded children:
1{
2 "path": "hello.js",
3 "kind": "file",
4 "summary": "Exports greet(name), which returns a greeting string for the given name.",
5 "sources": [
6 { "kind": "commit", "ref": "3f9a2c1e...", "title": "Add greet()", "author": "you@example.com[claude]", "at": "2026-10-13T18:22:04Z" }
7 ],
8 "children": []
9}
A child can also be a reference to another note, {"ref": "..."}, instead of an embedded rung. Ladders built locally with zz ladder build add more refs for releases, discussion threads, contributors, and dependencies, which hang off the repo's root commit.
Standard git tools can read the Ladder:
git fetch origin 'refs/notes/*:refs/notes/*'
git notes --ref=zz/ladder/changeset show <commit>
zz-meta branch
Repo settings live on an orphan branch named zz-meta, separate from the code's history so that settings changes don't add commits to it. Clone it with git clone -b zz-meta <url>. It holds two files.
meta.json
1{
2 "description": "A place to try zz",
3 "policy": { "require_changeset_review": true, "visibility": "public" },
4 "owner": "you@example.com",
5 "shard_of": "you@example.com/hello",
6 "created_at": "2026-10-13T18:00:00Z"
7}
shard_of appears only on a shard and names its immediate parent. visibility is public or private. A public repo can be read by anyone and written only by Maintainers and above.
roles.json
1{
2 "grants": [
3 { "principal": "you@example.com", "role": "owner", "granted_by": "system", "at": "2026-10-13T18:00:00Z" },
4 { "principal": "you@example.com[claude]", "role": "contributor", "granted_by": "you@example.com", "at": "2026-10-13T18:05:00Z" }
5 ]
6}
A principal is an Agent's username. Roles are owner, maintainer, contributor, and viewer. An Agent with no grant on a public repo is treated as a viewer. See Governance.
Lanes
Each repo has one Durable Object holding its Lanes in SQLite, one row per Lane with the Lane stored as JSON:
1{
2 "id": "_repo",
3 "repo": "repo-3c9e1a7b",
4 "origin": null,
5 "name": "you@example.com/hello",
6 "objective": "the you@example.com/hello repository",
7 "status": "active",
8 "scope": ["**"],
9 "trigger": { "kind": "manual", "proposed_by": "you@example.com" },
10 "coordinators": ["you@example.com"],
11 "maintainers": ["you@example.com"],
12 "history": [
13 { "id": "ask-1a2b3c4d5e", "type": "ASK", "kind": "work", "from": "you@example.com",
14 "payload": { "description": "Add hello.js exporting a greet(name) function" }, "at": "2026-10-13T18:10:00Z" },
15 { "id": "ask-5e6f7a8b9c", "type": "ASK", "kind": "claim_request", "from": "you@example.com[claude]",
16 "payload": { "target": "ask-1a2b3c4d5e" }, "at": "2026-10-13T18:11:00Z" },
17 { "id": "answer-0d1e2f3a4b", "type": "ANSWER", "ask": "ask-5e6f7a8b9c", "from": "you@example.com",
18 "action": "grant", "payload": { "grant": "grant-ask-5e6f7a8b9c" }, "at": "2026-10-13T18:11:00Z" }
19 ],
20 "related_lanes": []
21}
History entries are one of:
type |
Fields |
|---|---|
ASK |
id, kind, from, payload, depends_on, priority, at |
ANSWER |
id, ask (the Ask's id), from, action (grant, deny, complete), payload, at |
INSIGHT |
kind (norm or trap), text, from, at |
repo holds the server's internal storage name for the repo. A related_lanes entry is {repo, lane, origin, status}, where origin is {repo, lane}.
Planned. Exporting Lane state into the repo, likely onto the zz-meta branch, so a clone carries its coordination history too.
Accounts and the index
D1 holds what belongs to no single repo:
- Agents and identities. One row per Agent, human or managed, keyed by username, plus which login provider (Cloudflare or Google) each human signed in with.
- Tokens and sessions. Hashed bearer tokens, web sessions, and pending
zz loginhandshakes. - The repo index. Every repo's slug, owner, description, visibility, shard parent, and a cache of its Role grants, for search and listing. The
zz-metabranch is the source of truth for anything cached here. - Tracking. The repos each Agent has chosen to watch.
- The Ladder search index. A full-text index of rung summaries across all repos, which
ladder_searchand the explore page query. It can be rebuilt from the Ladders withladder_reindex.
Local mode
With --local, zz keeps everything on your machine:
| Path | Contents |
|---|---|
~/.zz/repos/<owner>/<repo>/ |
A git clone. Its Ladder lives in the clone's git notes. |
~/.zz/state/<owner>/<repo>/.zz/zz.db |
Lane state in SQLite, in the format shown above. |
Shard names keep their brackets everywhere except on disk, where repo[task] becomes repo__task.
The original design for a committed .zz/ directory with decision records, votes, trust scores, and a fuller policy.yaml is in the Archive.