cd /news/developer-tools/two-coding-agents-editing-the-same-i… · home topics developer-tools article
[ARTICLE · art-73475] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

Two coding agents editing the same issue, no merge conflict. Here is how git refs make that work

A developer built `grite`, an issue tracker that lives inside a git repository as an append-only event log with deterministic CRDT merging, eliminating merge conflicts when multiple AI coding agents edit the same issue. The system stores events in a git ref (`refs/grite/wal`) and uses a materialized view via sled for fast queries, enabling conflict-free multi-agent editing without a server or database.

read5 min views1 publishedJul 25, 2026

Run two AI coding agents on the same repo and the first thing that breaks is not

the code. It is coordination. Agent A starts refactoring auth. Agent B, running in

parallel, has no idea and starts the same thing. Neither remembers what it did last

session, because each one boots fresh with an empty context window. The usual fixes

are worse than the problem: a state file in the repo pollutes every diff and

conflicts on merge, and an external issue tracker means API tokens, rate limits,

and a hard dependency on the network for something that should be local.

So I built grite

: an issue tracker that lives inside your git repository as an

append-only event log, with deterministic CRDT merging so two writers never

conflict. No server. No database. No merge conflicts. Just git.

Grite does not store issues as files in your working tree. It stores them as an

append-only write-ahead log inside a git ref, refs/grite/wal

. Every action, a

create, a comment, a label change, is one immutable CBOR-encoded event appended to

that log. Your working tree stays completely clean. The only tracked file grite

ever writes is AGENTS.md

, and that is on purpose, so agents discover the tool

automatically.

Because the state lives in a git ref, it travels with your code. It branches when

you branch. It merges when you merge. It syncs when you git push

. If you can push

to a remote, you can sync issues. There is no new account, no new infrastructure,

no new protocol to learn.

Three layers, cleanly separated.

The git WAL is the source of truth. Events are appended as CBOR chunks, each

identified by a content-addressed EventId

that is a BLAKE2b hash of the event

body. Content addressing is what makes the log tamper-evident: change one byte of

an event and its ID no longer matches, which breaks the chain. Signing is optional

Ed25519 per event, so you can prove which actor created what.

The materialized view is a sled

embedded key-value store that projects the

event log into current issue state. This is the cache you actually query. It

rebuilds from the WAL on startup and then updates incrementally. Deleting it is

safe, it just replays the log. The projection uses CRDT semantics, Last-Write-Wins

for scalar fields and commutative-set semantics for things like labels. That is the

whole trick behind conflict-free multi-agent editing: two agents edit the same

issue on two machines, both changes survive the merge, and the result is identical

regardless of merge order.

The CLI and optional daemon are the interface. The daemon keeps the sled view

warm and serializes writes while allowing parallel reads, but it is optional for

correctness. The CLI works standalone and auto-spawns the daemon only when it helps.

The numbers that matter when an agent is firing hundreds of queries per session:

Operation Time
Issue create ~5ms (single event append)
Issue list, 1,000 issues ~10ms (sled view query)
Full rebuild, 10,000 events ~500ms (replay entire WAL)
Snapshot rebuild ~50ms (jump to snapshot, replay delta)

The full WAL rebuild is O(n), which is why periodic snapshots exist, they cut a

cold rebuild after a clone from replaying everything down to replaying a delta.

Each actor gets its own isolated sled database, so one agent's heavy query load

never blocks another's.

The human path looks like a normal tracker, just with git

semantics underneath:

cd your-project
grite init                       # creates AGENTS.md for agent discoverability

grite issue create --title "Fix race in WAL append" \
  --body "Intermittent failure under high concurrency."

grite issue list
grite issue update <id> --label bug --label concurrency
grite issue link <issue-a> blocks <issue-b>

grite sync                       # git fetch + CRDT merge, then push

The agent path is where the design pays off. An agent claims a lock so a second

agent picks different work, then records what it learned as a durable memory issue

that the next session can read:

grite sync --pull
grite issue list --label "agent:todo" --json
grite issue update <id> --label "agent:in-progress"
grite lock acquire src/parser.rs --ttl 1800

grite issue create --title "Parser edge case: empty structs" \
  --body "The parser fails on struct {}; need to handle this." \
  --label memory

grite issue close <id>
grite sync --push

Every command supports --json

, so agents parse output instead of scraping it.

The distributed lock has a TTL lease, so a crashed agent does not hold a resource

forever. Context extraction is tree-sitter powered across 10 languages (Rust,

Python, TypeScript, JavaScript, Go, Java, C, C++, Ruby, Elixir), so an agent can

create an issue with real symbol context instead of a vague note.

Honesty is the whole point, so here is where grite is the wrong tool.

It is terminal-and-git native. There is no hosted web UI, no kanban board, no

@-mention notifications, no email digest. If your team lives in a browser-based

tracker with dashboards and non-technical stakeholders, grite will feel primitive.

It is built for people and agents who already live in the terminal.

It is repo-local by design. Cross-repo issues, an org-wide backlog spanning

dozens of services, or a public bug tracker where external users file reports, none

of that is what grite is for. Your issues are scoped to the repo they describe.

The CRDT merge is deterministic, not smart. Last-Write-Wins means if two agents set

conflicting titles, one wins by timestamp. That is correct and conflict-free, but it

is not human judgment. For fields where you actually want a human to reconcile two

edits, an automatic merge is not what you want.

And it needs git 2.38+. The daemon is optional but the git dependency is not.

Code, the data model, and the hashing test vectors are here:

https://github.com/neul-labs/grite

If you are running more than one coding agent against a single repo, I want to know

how you are coordinating them today. Kick the tyres, issues welcome.

── more in #developer-tools 4 stories · sorted by recency
── more on @grite 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/two-coding-agents-ed…] indexed:0 read:5min 2026-07-25 ·