entireio / entireio/cli

git-refs writes can return success without durable push bookkeeping

Open
#2,393 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Go
Stars
5.1k
Forks
475
Avg merge
1d 11h
Merged PRs (30d)
178

Description

What happened?

On the git-refs checkpoint backend, an ordinary persistent write updates the
checkpoint ref and then records that ref in
entire-checkpoint-push-queue.jsonl. If queue resolution, locking, opening, or
appending fails, enqueueForPush logs a warning but the write still returns
success.

I understand the documented intent: enqueue is best-effort so a locally
successful condensation is not failed, and a later write to the same checkpoint
can re-enqueue the ref. The residual problem is that the queue is also the sole
input to the normal git-refs unpushed count and publication path. If there is no
later write, the result is a durable false-success state:

  • the checkpoint ref and object exist only in the originating clone;
  • the write caller received success;
  • the normal unpushed count reports 0;
  • the normal queue publisher pushes 0 refs;
  • a fresh clone from the remote cannot recover the checkpoint ref or object.

The local ref remains readable and GC-protected, so this is not immediate local
deletion. It becomes unrecoverable through the normal remote path if that sole
clone is later lost or discarded.

Expected behavior

A successfully acknowledged checkpoint should not become indefinitely invisible
to both normal pending diagnostics and normal publication. Keeping condensation
fail-soft is reasonable, but the system should retain or reconstruct durable
knowledge that the local ref still needs publication, or explicitly surface a
partial/local-only outcome.

Actual behavior

The enqueue failure is warning-only. Once the transient obstruction clears,
queue-only status and publication see no pending checkpoint and perform no
reconciliation against local checkpoint refs.

Steps to reproduce

I reproduced this using a disposable work repository, a local bare remote, and a
fresh recovery clone:

  1. Configure checkpoints.primary.type as git-refs.
  2. Create the queue pathname as a directory to deterministically make only the
    JSONL queue open fail.
  3. Write a checkpoint through checkpoint.Open(...).Persistent.Write(...).
  4. Verify that the write returns nil and the checkpoint ref resolves locally.
  5. Remove the synthetic obstruction and call
    strategy.CountUnpushedCheckpoints(...); it returns 0.
  6. Call strategy.PushQueuedCheckpointRefs(...); it returns pushed=0 with
    pushing enabled.
  7. Verify the bare remote lacks the checkpoint ref, clone that remote, and
    verify the clone has neither the ref nor the checkpoint commit object.

Observed output:

WARN checkpoint: enqueue checkpoint ref for push failed ... open push queue: ... is a directory
write_success=true local_ref=refs/entire/checkpoints/f6/c1b2c3d4e5f6
count_unpushed=0 pushed=0 push_disabled=false
remote_ref_present=false recovery_ref_present=false recovery_object_present=false

The focused end-to-end regression passed while asserting this current behavior;
the control experiment also confirmed that the same write creates both the ref
and queue record when queue persistence is available.

Entire CLI version

v0.10.6

OS and architecture

Windows amd64

Agent

Codex

Terminal

PowerShell

Logs / debug output
=== RUN   TestGitRefsQueueFailure_LosesRemoteRecoverability
WARN checkpoint: enqueue checkpoint ref for push failed ref=refs/entire/checkpoints/f6/c1b2c3d4e5f6 error="open push queue: openat entire-checkpoint-push-queue.jsonl: is a directory"
write_success=true local_ref=refs/entire/checkpoints/f6/c1b2c3d4e5f6 checkpoint_commit=4589427309d230ba0e4bce32bbf4f17913fe7ac0
count_unpushed=0 pushed=0 push_disabled=false
remote_ref_present=false recovery_ref_present=false recovery_object_present=false
--- PASS: TestGitRefsQueueFailure_LosesRemoteRecoverability (1.99s)
Additional context

This is a correctness and checkpoint-durability report, not a security report.
The failure was deterministically injected; real-world trigger frequency is
unknown. Plausible queue persistence failures include filesystem permission,
capacity, locking, or I/O failures, and there is also a structural interruption
window after ref update and before enqueue, but those natural triggers were not
claimed as reproduced here.

The source test ran at 5f09e04de77d627984ff118eb87bd2c16cce8fbd
and was revalidated on current main
174dca012af81d3d6ebefc2d7cc17f50a5eb37bb. The ref-write, queue,
unpushed-count, publication, and architecture-document blobs are identical
between those revisions.

Related but distinct work:

  • #1925 fixes cancellation affecting post-ref bookkeeping.
  • #2008 / #2012 cover removal of a newer same-ref enqueue during an
    in-flight push.
  • #1773 / #1929 cover logged-out import and later synchronization.
  • #2327 adds explicit checkpoint migration/re-enqueue behavior.
  • #1917 / #1926 cover a concurrent same-checkpoint ref-update race before
    queue publication.

Potential solution directions include a durable pre-acknowledgement outbox,
surfacing a partial/local-only result, or reconciling local checkpoint refs
against queued/published state before status and publication. I have not assumed
which trade-off is preferred.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at checkpoint.Open(...).Persistent.Write(...), then trace enqueueForPush, strategy.CountUnpushedCheckpoints(...), and strategy.PushQueuedCheckpointRefs(...). Use TestGitRefsQueueFailure_LosesRemoteRecoverability and its control experiment as regression coverage. Done means an acknowledged checkpoint remains visible to pending diagnostics and publication, or explicitly reports a partial/local-only result after queue persistence fails.

Written by the indexing model from the issue text.

Assessment

Tech stack
git, go
Domain
backend, cli
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.