Skip to content

Commit 5a72c37

Browse files
authored
Merge pull request #162 from remenoscodes/docs/comparisons-beads
docs: add git-native-issue vs Beads comparison
2 parents f342335 + 715ea81 commit 5a72c37

4 files changed

Lines changed: 1060 additions & 0 deletions

File tree

docs/comparisons/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Comparisons
2+
3+
Detailed technical comparisons of git-native-issue against other distributed
4+
issue tracking tools.
5+
6+
| Tool | Approach | Document |
7+
|------|----------|----------|
8+
| [Beads (bd)](https://github.com/gastownhall/beads) | Dolt SQL database | [beads.md](beads.md) |
9+
10+
*More comparisons coming: git-bug, Fossil, Bugs Everywhere, git-appraise.*

docs/comparisons/beads.md

Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
# git-native-issue vs Beads (bd)
2+
3+
> Last verified against Beads v1.0.2 (2026-04-20)
4+
5+
[Beads](https://github.com/gastownhall/beads) is a Go-based distributed
6+
graph issue tracker powered by [Dolt](https://github.com/dolthub/dolt), a
7+
version-controlled SQL database. It was designed primarily for AI agent
8+
workflows: dependency graphs, ready queues, and JSON output for machine
9+
consumption.
10+
11+
This document compares its approach to git-native-issue's Git-native model.
12+
13+
## At a Glance
14+
15+
| Dimension | git-native-issue | Beads (bd) |
16+
|-----------|-----------------|------------|
17+
| **Storage** | Git commits + refs + trailers | Dolt (version-controlled SQL) |
18+
| **Language** | POSIX Shell | Go |
19+
| **Codebase** | ~7K lines, 22 scripts | ~314K lines, 1,061 files |
20+
| **Dependencies** | Git (+ optional `gh`, `jq` for bridges) | 212 Go modules, Dolt engine |
21+
| **Install size** | 0 (shell scripts on PATH) | ~41-44MB compressed binary |
22+
| **Merge strategy** | Git's own merge primitives | Dolt cell-level merge |
23+
| **Formal spec** | [ISSUE-FORMAT.md](../../ISSUE-FORMAT.md) (33KB) | None (implementation is the spec) |
24+
| **License** | GPL-2.0 | MIT |
25+
| **Version** | v1.4.0 | v1.0.2 |
26+
| **Repo** | [remenoscodes/git-native-issue](https://github.com/remenoscodes/git-native-issue) | [gastownhall/beads](https://github.com/gastownhall/beads) |
27+
28+
Both projects solve distributed issue tracking without a central server.
29+
The difference is architectural: git-native-issue treats Git as the
30+
database -- issues are commits, identity is refs, metadata is trailers,
31+
merge is Git merge. Beads brings a separate database (Dolt) alongside Git.
32+
git-native-issue does this with zero dependencies beyond Git itself, and
33+
produces a formal specification that any tool can implement.
34+
35+
---
36+
37+
## What They Have in Common
38+
39+
Before diving into differences, it is worth noting the shared ground.
40+
Both projects reject centralized trackers (GitHub Issues, Jira, Linear) as
41+
single points of failure. Both use hash-based IDs for collision-free
42+
distributed work -- git-native-issue uses UUID v4, Beads uses content-hash
43+
in base36 (e.g. `bd-a3f8e9`). Both sync via push/pull rather than webhooks
44+
or polling. Both bridge to GitHub, GitLab, Gitea, and Azure DevOps.
45+
46+
The shared DNA ends at the storage layer. Everything that follows stems from
47+
a single architectural fork.
48+
49+
---
50+
51+
## The Fundamental Fork
52+
53+
Two competing theses:
54+
55+
**Beads' thesis**: "Issues need a real database. Git is the wrong
56+
abstraction for queries and graphs." Beads ships Dolt -- a version-controlled
57+
SQL database with cell-level merge, native branching, and built-in sync --
58+
as its storage engine. Issues live in SQL tables with indexes, foreign keys,
59+
and joins.
60+
61+
**git-native-issue's thesis**: "Git already IS a distributed, append-only,
62+
content-addressable database. Issues are just commits."
63+
64+
git-native-issue uses only Git primitives that have existed for 20 years:
65+
commits as events, refs as identity, trailers as structured metadata, and
66+
Git's own merge machinery for conflict resolution. No new storage engine.
67+
No new protocol. No new binary.
68+
69+
The rest of this document presents evidence for the Git-native thesis --
70+
while being honest about where the tradeoffs favor Beads.
71+
72+
---
73+
74+
## Storage: Commits + Refs + Trailers vs Dolt SQL
75+
76+
### git-native-issue
77+
78+
```
79+
refs/issues/<uuid> --> commit chain (append-only)
80+
|-- root commit: title (subject) + metadata (trailers)
81+
|-- comment commits: body text
82+
|-- state-change commits: State: trailer
83+
All point to empty tree (4b825dc...)
84+
```
85+
86+
- **Human-readable**: `git log refs/issues/*` shows your issues
87+
- **Tamper-evident**: every commit is SHA-addressed; changing a byte changes
88+
the hash
89+
- **Zero overhead**: no files in the working tree, no database files, no
90+
`.beads/` directory
91+
- **Works everywhere**: bare repos, CI environments, embedded systems --
92+
anywhere Git runs
93+
94+
Every piece of issue data is a standard Git object. `git clone` copies your
95+
issues. `git push` shares them. `git fsck` validates them.
96+
97+
### Beads
98+
99+
```
100+
.beads/embeddeddolt/ --> Dolt database
101+
|-- issues table (id, title, status, priority, type, ...)
102+
|-- dependencies table (graph edges)
103+
|-- comments, events, labels, wisps, ...
104+
|-- refs/dolt/data (Dolt's own ref namespace)
105+
```
106+
107+
- **Full SQL power**: indexes, joins, foreign keys, ad-hoc queries
108+
- **Cell-level versioning**: every field change is tracked independently
109+
- **Large footprint**: Go binary with embedded Dolt engine (~41-44MB
110+
compressed)
111+
- **Requires Dolt**: either embedded (in-process, single-writer) or server
112+
mode (external `dolt sql-server`)
113+
114+
Beads' storage is a parallel system alongside Git. It has Git-like
115+
properties (branching, merge, history), but it is not Git. Your issues
116+
live in a separate database. They do not travel with `git clone`.
117+
118+
---
119+
120+
## Merge: Git's Own Primitives vs an External Engine
121+
122+
### How git-native-issue merges
123+
124+
git-native-issue does not invent merge algorithms. It composes Git's
125+
existing primitives:
126+
127+
- **Comments**: union of commit chains. When two branches diverge, merging
128+
produces a commit with two parents -- both chains are preserved. This is
129+
how Git merge works by default.
130+
- **Labels**: three-way set merge via `git merge-base` + `comm` + `sort`.
131+
Additions from both sides are kept. Removals from both sides are honored.
132+
On tie, additions beat removals (bias toward keeping data). These are
133+
standard UNIX tools from the 1970s.
134+
- **Scalar fields** (assignee, priority, milestone): last-writer-wins via
135+
`%(authordate)` -- Git's own author timestamp. Tiebreaker:
136+
lexicographically greater SHA.
137+
- **Merge commits**: `git commit-tree` with two parents and resolved
138+
metadata as trailers. Standard Git plumbing.
139+
140+
The [ISSUE-FORMAT.md](../../ISSUE-FORMAT.md) spec contribution is policy
141+
decisions over these primitives -- which strategy applies to which field
142+
type -- not new algorithms. The strategies themselves are textbook
143+
distributed systems patterns implemented with Git commands.
144+
145+
### How Beads merges
146+
147+
Beads delegates merge entirely to Dolt's cell-level merge engine. Dolt
148+
performs a three-way merge at the SQL cell level. This is effective, but it
149+
is a black box: users must trust Dolt's merge semantics, which are not
150+
formally specified and may change between versions.
151+
152+
git-native-issue's merge behavior is specified in a
153+
[formal document](../../ISSUE-FORMAT.md) that any implementation can follow.
154+
Beads' merge behavior is whatever Dolt does.
155+
156+
---
157+
158+
## The Dependency Graph Question
159+
160+
Beads' strongest selling point is its dependency graph: `bd dep add/remove/tree`,
161+
`bd ready` (list unblocked tasks), and cycle detection. This is genuinely
162+
useful for AI agent workflows where agents need to ask "what can I work on
163+
next?"
164+
165+
**Our argument: this is a data modeling problem, not a storage engine
166+
problem.**
167+
168+
Git trailers model the same graph:
169+
170+
```
171+
Blocks: a7f3b2c
172+
Blocked-By: c4e5f6d
173+
Parent: b8c9d0e
174+
Related: f1a2b3c
175+
```
176+
177+
These are append-only (a new commit adds a trailer), merge-safe (union
178+
semantics -- same as comments), and queryable via Git plumbing.
179+
180+
### The GitHub Issues analogy
181+
182+
GitHub Issues has zero built-in hierarchy or dependency primitives. Yet
183+
millions of teams model epics, stories, tasks, and blockers using nothing
184+
but labels, mentions, and markdown checklists. git-native-issue's trailers
185+
are more structured than GitHub's freeform markdown -- they are typed,
186+
machine-parseable, and formally specified.
187+
188+
### Query cost
189+
190+
To be honest about the tradeoff: querying trailers requires iterating issue
191+
refs -- `git for-each-ref refs/issues/` to collect tips, then inspecting
192+
each tip commit's trailers. This is O(n) over all issues, not an indexed
193+
lookup like Dolt's SQL.
194+
195+
For most repositories (hundreds of issues), this is fast -- the same pattern
196+
`git issue ls` already uses for filtering by label or priority. For
197+
thousands of issues, it is slower than SQL but still tractable. The tradeoff
198+
is: no new dependency for queries that are fast enough at realistic scale.
199+
200+
### Cycle detection
201+
202+
Detecting cycles in a trailer-based dependency graph requires a full DFS
203+
traversal across all `Blocks:`/`Blocked-By:` relationships. This is
204+
non-trivial but well-understood -- a standard graph algorithm. We do not
205+
pretend this is a one-liner.
206+
207+
### A ready queue
208+
209+
A ready queue is: "open issues where no unresolved `Blocked-By` target
210+
exists." This requires collecting all open issues' `Blocked-By` trailers,
211+
checking which targets are still open, and filtering. Not trivial, but the
212+
same algorithmic complexity as any graph-based ready queue -- including
213+
Beads'.
214+
215+
### The bottom line
216+
217+
Dependency tracking is a **feature gap** in git-native-issue today, not an
218+
**architectural limitation**. Closing it requires new trailers in the spec
219+
plus approximately five new commands (`dep add`, `dep remove`, `dep list`,
220+
`dep tree`, `ready`) with cycle detection and validation. Realistic
221+
estimate: 400-600 lines of shell. This is meaningful work, but proportional
222+
-- git-native-issue's existing commands average 100-150 lines each.
223+
224+
Beads built a 314K-line Go application with 212 dependencies and a SQL
225+
database to solve a problem that structured metadata and smart queries can
226+
handle.
227+
228+
---
229+
230+
## Agent Integration
231+
232+
Beads was designed agent-first: JSON output everywhere, atomic `--claim`
233+
(sets assignee + in_progress in one operation), `bd ready` (unblocked
234+
tasks), a built-in Anthropic SDK, and an MCP server on PyPI.
235+
236+
git-native-issue approaches agent integration differently -- via plugins
237+
rather than baking agent concerns into the core tool. The
238+
[claude-git-native-issue](https://github.com/remenoscodes/claude-git-native-issue)
239+
plugin enables Claude Code to create, list, update, and close issues
240+
autonomously.
241+
242+
To be clear: Beads is human-usable. It has a full CLI and TUI. The
243+
argument is not that Beads sacrificed human usability, but that
244+
agent-specific concerns -- molecules, gates, formulas, built-in LLM SDKs
245+
-- do not need to live in the issue tracker's core.
246+
247+
Trailers are structured data. Agents consume them naturally. A `--json`
248+
output flag is a small addition to git-native-issue that closes the
249+
machine-readability gap without pulling in the Anthropic SDK as a
250+
dependency of the issue tracker.
251+
252+
Agent orchestration belongs in orchestration tools. Issue tracking belongs
253+
in the issue tracker.
254+
255+
---
256+
257+
## What Beads Has That We Don't (And Whether It Matters)
258+
259+
### Worth considering
260+
261+
Features that close real gaps with small additions:
262+
263+
- **`--json` output**: machine-readable output for agent consumption.
264+
A natural addition to `git issue ls` and `git issue show`.
265+
- **Dependency trailers**: `Blocks:`, `Blocked-By:`, `Parent:`, `Related:`.
266+
New trailers in the ISSUE-FORMAT.md spec, queryable with existing Git
267+
plumbing.
268+
- **`git issue ready`**: list unblocked tasks. A new command built on
269+
dependency trailers.
270+
- **`git issue dep tree`**: visualize the dependency graph. A new command
271+
that reads `Blocks:`/`Blocked-By:` trailers and renders a tree.
272+
273+
### Interesting but out of scope
274+
275+
Features that solve real problems but belong in separate tools:
276+
277+
- **Molecules, formulas, gates**: workflow orchestration primitives (compound
278+
tasks, YAML recipes, async coordination). These are useful but they are
279+
orchestration, not issue tracking.
280+
- **Hierarchical IDs** (`bd-a3f8.1.1`): convenient shorthand for sub-tasks,
281+
but solvable with `Parent:` trailers without changing the ID scheme.
282+
- **Multi-repo federation**: interesting for enterprise deployments,
283+
premature for v1.x of a tool focused on getting the primitives right.
284+
- **Contributor/maintainer roles**: auto-detection of repo access level.
285+
Solves a Beads-specific problem (keeping planning databases out of PRs).
286+
287+
### Unnecessary complexity
288+
289+
Features that add weight without clear benefit for issue tracking:
290+
291+
- **Wisps** (ephemeral TTL messages): adds complexity to an issue tracker
292+
with unclear benefit. While append-only systems can support TTL (e.g.
293+
Kafka retention), it is unclear why an issue tracker needs ephemeral
294+
messages.
295+
- **Compaction/memory decay**: rewriting closed issue history to save AI
296+
context windows. This trades auditability for a concern that belongs in
297+
the agent, not the data store.
298+
- **OpenTelemetry**: structured observability for a CLI issue tracker. The
299+
overhead of tracing infrastructure outweighs the diagnostic value for a
300+
tool that runs in milliseconds.
301+
- **212 Go module dependencies**: a large dependency tree for functionality
302+
that POSIX shell scripts handle with zero external dependencies.
303+
304+
---
305+
306+
## Feature Matrix
307+
308+
| Category | Feature | git-native-issue | Beads (bd) |
309+
|----------|---------|-----------------|------------|
310+
| **Core** | Create issues |||
311+
| | List/filter issues |||
312+
| | Show issue details |||
313+
| | Edit issue metadata |||
314+
| | Change state (open/close) |||
315+
| | Search issues |||
316+
| | Add comments |||
317+
| **Dependencies** | Add/remove dependencies | planned ||
318+
| | Dependency tree visualization | planned ||
319+
| | Ready queue (unblocked tasks) | planned ||
320+
| | Cycle detection | planned ||
321+
| | Epics / sub-tasks | planned (via `Parent:` trailer) | ✓ (hierarchical IDs) |
322+
| **Collaboration** | Assign issues |||
323+
| | Atomic claim (assign + start) | -- ||
324+
| | Comment threading | -- ||
325+
| **Sync** | GitHub | ✓ (import/export/sync) ||
326+
| | GitLab | ✓ (import/export/sync) ||
327+
| | Gitea / Forgejo | ✓ (import/export) ||
328+
| | Azure DevOps | ✓ (import/export) ||
329+
| | Jira | -- ||
330+
| | Linear | -- ||
331+
| | DoltHub | N/A ||
332+
| **Agent support** | JSON output | planned ||
333+
| | MCP server | -- ||
334+
| | Claude plugin | ✓ (via plugin) | ✓ (built-in SDK) |
335+
| | Ready queue | planned ||
336+
| **Maintenance** | Data integrity validation | ✓ (`git issue fsck`) | ✓ (`bd doctor`) |
337+
| | Garbage collection | N/A (Git GC) | ✓ (`bd gc`) |
338+
| | Compaction | N/A -- by design | ✓ (`bd compact`) |
339+
| **Data properties** | Tamper-evident (SHA-addressed) || -- |
340+
| | Human-readable (`git log`) || -- |
341+
| | Formal interop spec | ✓ (ISSUE-FORMAT.md) | -- |
342+
| | Works in bare repos || -- |
343+
| | Zero dependencies beyond Git || -- |
344+
| | Travels with `git clone` || -- |
345+
| | SQL ad-hoc queries | -- ||
346+
| | Cell-level field versioning | -- ||
347+
348+
Legend: `` = available, `planned` = on roadmap, `--` = not available,
349+
`N/A` = not applicable by design.
350+
351+
---
352+
353+
## When to Use Which
354+
355+
**Use git-native-issue when:**
356+
357+
- You already use Git and want zero new dependencies
358+
- Issues should travel with your code (`git clone` copies your issues)
359+
- You value a formal spec that other tools can implement
360+
- Human readability matters (`git log` shows your issues)
361+
- You work in constrained environments (CI, bare repos, air-gapped systems)
362+
- You want issue data that is tamper-evident by construction
363+
364+
**Consider Beads when:**
365+
366+
- Your primary users are AI agents, not humans
367+
- You need complex SQL queries over issue data
368+
- Your team already uses Dolt
369+
- You need Jira or Linear sync specifically
370+
- You need dependency graph queries at scale (thousands of issues)
371+
372+
Both are honest tools built by people who care about distributed issue
373+
tracking. They differ on a single architectural question: whether Git is
374+
enough. We believe it is.

0 commit comments

Comments
 (0)