|
| 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