Skip to content

fix(checkpoint): fetch a locally-missing ref before declaring a backfill target absent#1824

Open
peyton-alt wants to merge 5 commits into
mainfrom
fix/refs-backfill-fetch-probe
Open

fix(checkpoint): fetch a locally-missing ref before declaring a backfill target absent#1824
peyton-alt wants to merge 5 commits into
mainfrom
fix/refs-backfill-fetch-probe

Conversation

@peyton-alt

@peyton-alt peyton-alt commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Follow-up to the #1811 review (addressed directly per discussion, no issue filed). Complete fix: store-layer semantics and production wiring.

Problem

The git-refs store's reads on-demand fetch a checkpoint ref that's missing locally; its backfill writes (summary, stop-time transcript finalize, attribution) probed with a local-only lookup. For a checkpoint written or migrated on another machine, a backfill declared it absent — landing on a stale fallback copy under kind routing (#1811), or failing — while reads served the refs copy, leaving the backfilled data permanently invisible.

Fix (three layers)

1. Store: backfills fetch-proberefBaseForBackfillresolveRefMaybeFetch fetches a locally-missing ref once when a fetcher is wired. Creates keep the local-only probe (their ref never exists yet). Zero cost when the ref exists locally (pinned).

2. Absence ≠ failure at the fetch layerremote.FetchCheckpointRef (moved from the cli package so strategy hooks can import it; thin aliases keep old call sites) now probes with ls-remote before fetching: a ref the remote genuinely lacks returns an error wrapping plumbing.ErrReferenceNotFound → classified as absence → safe fallthrough under kind routing. Transport failures on either leg surface as-is — never absence, so a dead network can't fork a write onto a stale copy. This was the blocker that made naive wiring unsafe (git fetch of a missing refspec fails like a network error).

3. Wiring with a hook-safe budgetremote.BoundedCheckpointRefFetcher(remote.WriteProbeFetchBudget) (15s per probe) wired at the three backfill write sites: explain --generate's write store, the stop-hook transcript finalize, and post-commit attribution. The git-refs store memoizes the first transport-level fetch failure per store instance, so a stop hook finalizing N checkpoints on a dead network pays the outage once, briefly. Remote absence is per-ref and never memoized.

Verification

  • TDD throughout: store-level fetch-restores-ref (all three backfill types, with read-back + parent-hash assertions), fetch-failure-aborts (NotErrorIs ErrCheckpointNotFound), absent-after-fetch, local-ref-never-fetches, creates-never-fetch, absence-classification chain-through, and failure-memo behavior (transport memoized once; absence never)
  • Remote-level fixtures against a local bare origin: missing ref → absence sentinel; present ref → fetched into the local ref; unreachable remote → hard error
  • Multi-agent review pass applied (comments re-anchored order-agnostically vs fix(checkpoint): kind-route backfill writes to the store holding the checkpoint #1811; FetchCheckpointRef's inverted contract comment fixed; ref-checkpoint-backend.md updated)
  • Full unit suite green (8180 tests), mise run fmt + mise run lint clean

Relationship to #1811

Independent; merges in either order (comments phrased order-agnostically). Reconciliation: whichever lands second drops #1811's "backfill absence probes are local-only" sentence from the kindRoutingStore Write doc comment.

🤖 Generated with Claude Code

…ill target absent

The git-refs store's backfill writes (summary, transcript finalize,
attribution) probed for the checkpoint ref with a local-only lookup, while
reads on-demand fetch a missing ref when a fetcher is configured. For a
checkpoint written or migrated on another machine that means the read path
sees it but a backfill declares it absent — under kind routing the write
then lands on a stale fallback copy (or errors), and refs-first reads never
surface it.

Backfills now resolve their base via resolveRefMaybeFetch: a ref missing
locally is fetched once before deciding. Absent after the fetch still
reports ErrCheckpointNotFound (legitimate fallthrough); a fetch FAILURE
surfaces as-is — transient unavailability must not read as absence, or the
router would fork the write. Creates keep the local-only probe: their ref
never exists yet, and fetch-on-create would add a doomed network round-trip
to every condensation and break offline writes.

Common-case cost is zero: the ref exists locally and no fetch fires.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@peyton-alt
peyton-alt requested a review from a team as a code owner July 21, 2026 19:21
Copilot AI review requested due to automatic review settings July 21, 2026 19:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a consistency gap in the git-refs checkpoint store: backfill writes (summary, transcript finalize, attribution) now resolve a checkpoint ref the same way reads do—by attempting a one-time on-demand fetch when the ref is missing locally—preventing kind-routing fallthrough from writing backfills to a stale backend copy.

Changes:

  • Introduces refBaseForBackfill (fetch-aware) and refactors shared tip/tree loading into refTip.
  • Switches backfill write paths to use refBaseForBackfill while keeping creates on the local-only refBase.
  • Adds unit tests covering fetch-on-backfill, fetch-failure behavior, genuine absence after fetch, and “creates never fetch”.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
cmd/entire/cli/checkpoint/refs_store.go Adds fetch-aware base resolution for backfills and updates backfill writers to avoid false “not found” when a ref is missing locally.
cmd/entire/cli/checkpoint/refs_store_test.go Adds targeted tests to pin backfill fetch behavior and failure/absence semantics.

Comment on lines 194 to 200
func (s *gitRefsStore) backfillTranscript(ctx context.Context, opts UpdateOptions) error {
if opts.CheckpointID.IsEmpty() {
return errors.New("invalid update options: checkpoint ID is required")
}

parentHash, existing, err := s.refBase(opts.CheckpointID)
parentHash, existing, err := s.refBaseForBackfill(ctx, opts.CheckpointID)
if err != nil {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 96013abbackfillTranscript now has the same ctx.Err() short-circuit as its two siblings, so a cancelled context never reaches the (potentially fetching) base resolution.

…ore layer

Review findings from the PR #1824 multi-agent pass:

- Tests: pin the zero-cost claim (backfill with a locally-present ref never
  fetches), verify the fetched-then-backfilled data reads back for all three
  request types, and assert the backfill commit parents on the fetched tip
  rather than orphaning over the checkpoint's history.
- Comments rephrased order-agnostically: the write-fallthrough they cited is
  PR #1811's unmerged behavior; they now state the absence-vs-failure
  contract instead of a phantom mechanism. refBase's doc names its other
  deliberate callers (migrate.go local import; attach's externally-guarded
  writeSession).
- FetchCheckpointRef's stale 'caller treats a fetch failure as not found'
  comment inverted the actual contract this PR depends on; it now documents
  failure-surfaces-never-absence, including the remote-lacks-ref caveat.
- ref-checkpoint-backend.md: the write shape and on-demand-fetch sections now
  describe the create/backfill probe split.

Scope note (review CRITICAL): no production write path currently wires a
RefFetcher, so this PR is store-layer semantics + plumbing; the wiring is a
follow-up gated on (1) FetchCheckpointRef distinguishing remote-lacks-ref
(absence) from transport failure — today wiring would break the
pre-migration-hex fallthrough — and (2) a hook-appropriate fetch timeout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@peyton-alt

Copy link
Copy Markdown
Contributor Author

Applied the review fix set in 2b4b736. Highlights:

  • Rescoped (review CRITICAL): the silent-failure agent found no production write path wires a RefFetcher, so the fetch branch was inert outside tests. Rather than wiring it hastily — which would break the pre-migration-hex fallthrough (git fetch of a ref the remote lacks fails like a network error, and the write path treats failure ≠ absence by design) and add multi-minute hook stalls — the PR is now honestly scoped as store-layer semantics, with the two wiring prerequisites named in the description.
  • Tests hardened (coverage review): local-ref-never-fetches pin (zero-cost claim), read-back verification for all three backfill types, parent-hash assertion (no orphaning over fetched history).
  • Comments re-anchored (comment review): the fix(checkpoint): kind-route backfill writes to the store holding the checkpoint #1811 write-fallthrough was cited as current fact; now phrased as the absence-vs-failure contract, order-agnostic. refBase's doc names the migrate.go and attach callers. FetchCheckpointRef's comment said callers treat fetch failure as not-found — the exact inversion of the contract; fixed.
  • Docs: ref-checkpoint-backend.md write-shape and on-demand-fetch sections updated for the probe split.

Field test (fresh local clone, ref absent locally): the ref was fetched and restored end-to-end — via the read store's fetcher, which is exactly the masking the review identified; the write-path wiring remains the named follow-up. Full suite green (8173 tests).

Completes the #1824 fix — previously the fetch-probing backfill base was
store-layer plumbing with no production caller wiring a RefFetcher. The two
prerequisites the review named are now in place:

- remote.FetchCheckpointRef (moved from the cli package so strategy hooks
  can wire it; cli keeps thin aliases) probes with ls-remote before
  fetching: a ref the remote genuinely lacks returns an error wrapping
  plumbing.ErrReferenceNotFound (absence — safe fallthrough under kind
  routing), while transport failures on either leg surface as-is. git fetch
  of a missing refspec fails indistinguishably from a network error, which
  is why wiring was unsafe before this discrimination existed.
- Write probes are budget-bounded (remote.WriteProbeFetchBudget, 15s via
  remote.BoundedCheckpointRefFetcher) and the git-refs store memoizes the
  first transport-level fetch failure per store, so a stop hook finalizing
  N checkpoints on a dead network pays the outage once, briefly. Remote
  absence is per-ref and never memoized.

Wired at the three backfill write sites: explain --generate's write store,
the stop-hook transcript finalize, and post-commit attribution. The
carry-forward and other creates stay unwired — creates never probe.

Tests: remote-level absence/present/transport-failure fixtures against a
local bare origin; store-level absence classification chain-through and
failure-memo behavior (transport memoized once, absence never).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@peyton-alt

Copy link
Copy Markdown
Contributor Author

Wiring follow-through in b33b3c2 — the review's CRITICAL ("fix is inert: no production write path wires a RefFetcher") is now resolved rather than rescoped around. Both named prerequisites were implemented: absence-vs-failure discrimination via an ls-remote probe in the relocated remote.FetchCheckpointRef (remote-lacks-ref → plumbing.ErrReferenceNotFound wrap → safe fallthrough; transport failure → hard error), and a hook-safe budget (15s bounded fetcher + per-store failure memo, so N checkpoints on a dead network cost one short stall). Wired at explain --generate, stop-hook transcript finalize, and post-commit attribution; creates remain unwired by design. PR description rewritten to match. 8180 tests green, fmt + lint clean.

peyton-alt and others added 2 commits July 21, 2026 13:54
Copilot review finding on #1824: backfillTranscript lacked the ctx.Err()
guard its two backfill siblings have, so a cancelled context could still
reach the (now potentially fetching) base resolution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Findings from the wiring commit's multi-agent re-review:

- False absence on a fallback target (HIGH): when a checkpoint_remote is
  configured but unresolvable (unknown provider + unmappable origin
  protocol), FetchURL falls back to origin — a remote that never hosts the
  configured checkpoint refs. The ls-remote probe treated its emptiness as
  absence, silently dropping backfills for checkpoints that exist on the
  real checkpoint remote. fetchURLAuthoritative now reports whether the
  resolved URL is authoritative for checkpoint refs; probe-emptiness on a
  non-authoritative fallback is a failure, never absence.
- SSH hygiene in hooks (HIGH): the two hook wirings now use
  remote.HookCheckpointRefFetcher, which marks the context non-interactive
  (BatchMode SSH) like the pre-push path — a passphrase-protected key can
  no longer prompt or hang inside post-commit/stop hooks.
- Actionable errors (HIGH): lsRemote folds git's redacted stderr into its
  error, and FetchCheckpointRef folds the fetch output — a bare 'exit
  status 128' left auth, DNS, and missing-repo failures indistinguishable
  in hook Warn logs.
- Caller cancellation is no longer memoized as a network failure (a Ctrl-C
  mid-operation said nothing about the remote); the memoized-skip message
  notes its cause may name a different ref; mirrors never receive a
  RefFetcher (best-effort write-only copies must not pay network probes);
  the git-branch backfillTranscript gains the ctx.Err() guard its siblings
  have (pre-existing gap surfaced by the same review).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@peyton-alt

Copy link
Copy Markdown
Contributor Author

Re-review of the wiring commit complete (code-reviewer + silent-failure agents, scoped to b33b3c2) — fixes applied in 96013ab and 4fdb8aa:

Code-reviewer: clean — probe semantics verified (same auth path as fetch, no tail-match over-matching, no prompt hang), memo concurrency correct, 15s budget correctly clamps the internal 2-minute timeout, layering sound.

Silent-failure agent found three HIGHs, all fixed:

  1. False absence on a fallback target: when a checkpoint_remote is configured but unresolvable, the probe ran against an origin fallback that never hosts checkpoint refs and read its emptiness as absence — silently dropping backfills. fetchURLAuthoritative now marks fallback URLs non-authoritative; probe-emptiness there is a failure, never absence (regression-tested with a bogus-provider fixture).
  2. SSH hygiene: hook wirings now use HookCheckpointRefFetcher (BatchMode SSH, matching the pre-push path) so a passphrase-protected key can't prompt or hang inside post-commit/stop hooks.
  3. Undebuggable errors: lsRemote and the fetch leg now fold git's redacted stderr/output into their errors — no more bare exit status 128 in hook Warn logs.

Also: caller cancellation is no longer memoized as a network verdict (tested), the memoized-skip message clarifies its cause may name a different ref, mirrors never receive a RefFetcher, and the git-branch backfillTranscript gained the ctx.Err() guard its siblings have (the true home of Copilot's finding — the refs-store one was fixed in 96013ab).

Field-tested in a fresh clone: present-on-remote ref resolves end-to-end in ~3s; absent-everywhere ULID returns a clean not-found in under a second. Full suite green (8182 tests), fmt + lint clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants