Skip to content

Concurrent git-refs checkpoint writes can silently lose completed data! #1917

Description

@ChetanReddyC

What happened?

The persistent git-refs checkpoint backend has a read-modify-write race.

When two independent writers update the same checkpoint, both can read the same
checkpoint tip, build different child commits, and successfully update the
checkpoint ref. Because the final ref update is unconditional and does not
verify the expected old commit, the later writer can silently hide the earlier
writer's mutation.

I reproduced this deterministically on the official stable release v0.9.0
and on the latest fetched public main. Both ref writes returned success, but
the first writer's summary disappeared while the second writer's attribution
remained visible.

This can silently lose checkpoint data such as summaries, transcripts,
attribution, assets, or attached sessions even though the writers report
success.

Steps to reproduce

I added the following local regression test to
cmd/entire/cli/checkpoint/refs_store_test.go:

TestGitRefsStore_ForcedConcurrentBackfillsLoseOneMutation

The test performs this deterministic interleaving:

  1. Create a checkpoint.
  2. Read its current parent once.
  3. Build a summary mutation from that parent.
  4. Build an attribution mutation from the same parent.
  5. Write the summary commit successfully.
  6. Write the attribution commit successfully afterward.
  7. Read the checkpoint.
  8. Observe that attribution remains but the summary is absent.

Run:

go test ./cmd/entire/cli/checkpoint -run '^TestGitRefsStore_ForcedConcurrentBackfillsLoseOneMutation$' -count=1 -v

The test passes because it successfully observes the lost-update behavior; it
does not mean the defect is fixed.

Entire CLI version

Entire CLI v0.9.0

OS and architecture

Windows amd64 (build 26200.8875)

Agent

Codex

Terminal

PowerShell 5.1 through the Codex desktop integrated shell

Logs / debug output

=== RUN   TestGitRefsStore_ForcedConcurrentBackfillsLoseOneMutation
--- PASS: TestGitRefsStore_ForcedConcurrentBackfillsLoseOneMutation (1.88s)
PASS
ok   github.com/entireio/cli/cmd/entire/cli/checkpoint   5.479s

Additional context

Validated against official stable release v0.9.0:

8b77ad43132d18f7958825c9dcd26544ab8f5d92

Also validated against the latest fetched public main revision:

dd2e87a02ef853b32a85e7094e1944fe320739a3

Environment details:

  • Stable test Go version: 1.26.4
  • Latest-main test Go version: 1.26.5
  • Git: 2.46.0.windows.1
  • Shell: PowerShell 5.1.26100.8875
  • Validation worktree: detached worktree at the exact revision above
  • Production source changes: none; only the local regression test was added

The likely cause is that gitRefsStore.setRef advances the checkpoint ref with
an unconditional SetReference. The read/build/set sequence has no
checkpoint-scoped lock and does not use an expected-old-value compare-and-swap.
Two writers can therefore build valid commits from the same parent while the
last ref update silently makes the first mutation unreachable through normal
checkpoint reads.

Expected behavior: both independent mutations should remain reachable, or one
writer should receive an explicit conflict and retry or fail clearly.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions