Skip to content

Fetch missing source notes from the HTTP backend before the git refs fetch#1975

Open
scouredimage wants to merge 3 commits into
git-ai-project:mainfrom
scouredimage:http-backend-missing-source-notes
Open

Fetch missing source notes from the HTTP backend before the git refs fetch#1975
scouredimage wants to merge 3 commits into
git-ai-project:mainfrom
scouredimage:http-backend-missing-source-notes

Conversation

@scouredimage

@scouredimage scouredimage commented Jul 24, 2026

Copy link
Copy Markdown

Problem

When a rewrite (rebase/restack/cherry-pick) needs source notes that are not in the local notes-db cache, fetch_missing_notes_for_commits has exactly one recovery path: a git refs fetch of refs/notes/ai from each remote. On the HTTP notes backend that is the wrong tool twice over:

  1. the server — not refs/notes/ai — is the source of truth, so the refs fetch often has nothing to offer even when the backend holds every note;
  2. the fetch needs working SSH auth in the daemon's environment, which is not a given (the daemon's child git fetch may not see the user's SSH agent).

Observed at scale in a large deployment: dozens of restacked PRs permanently lost AI attribution because translation aborted with

failed to fetch authorship notes for source commits [...]: Git CLI (... fetch ... +refs/notes/ai:refs/notes/ai-remote/origin) failed with exit code 128: [email protected]: Permission denied (publickey).

…while the HTTP backend held every single source note. #1887/#1888 made the presence check backend-aware (cached sources skip the network), but sources missing from the local cache still funnel into the SSH-only fetch.

Fix

  • New notes_api::warm_cache_for_commits(shas): batch-fetches specific SHAs from the HTTP backend into notes-db as already-synced rows. Shares the fetch/cache core (fetch_and_cache_notes) and the cached-SHA filter with warm_cache_for_remote, whose behavior and logging are unchanged.
  • fetch_missing_notes_for_commits: when the HTTP backend is enabled, pull the missing sources from it first; if that covers everything, done — no refs fetch, no SSH. Otherwise fall through to the existing refs fetch and the existing still-missing error contract (unchanged for the git-notes backend).

Constant git cost: zero new git spawns — the backend fetch is one HTTP call per 100 SHAs and a notes-db write.

Tests

  • fetch_missing_notes_pulls_sources_from_http_backend_when_refs_fetch_fails — repo with a remote whose fetch always fails (stand-in for broken SSH auth), note available only on a mock backend server: previously returned the stranding error, now succeeds and caches the note as synced. Red without the fix (verified by disabling the new path), green with it.
  • fetch_missing_notes_still_errors_when_backend_lacks_note — when neither the backend nor any remote has the note, the loud failure with the missing SHAs is preserved.
  • Existing warm_cache_for_remote_* and notes_api cache tests pass unchanged.

Related: #1961 replays rebases that completed while no daemon was observing; its replay funnels through this same fetch, so the two fixes compose — #1961 finds the missed rewrite, this PR lets its note translation succeed without SSH.

🤖 Generated with Claude Code

When a rewrite needs source notes that are not in the local notes-db
cache, fetch_missing_notes_for_commits only tried a git refs fetch of
refs/notes/ai from each remote. On the HTTP notes backend that fetch is
the wrong tool: the server — not refs — is the source of truth, and the
fetch requires SSH auth that is not available in every daemon
environment. In production this stranded authorship notes at scale:
restacks failed translation with 'failed to fetch authorship notes for
source commits [...]: Permission denied (publickey)' even though the
backend held every source note.

Add warm_cache_for_commits, which batch-fetches specific SHAs from the
HTTP backend into notes-db as synced rows (sharing the fetch/cache core
with warm_cache_for_remote), and call it for the missing sources before
falling back to the refs fetch. The refs fetch and the missing-note
error contract are unchanged for the git-notes backend.

Co-Authored-By: Claude Fable 5 <[email protected]>
@scouredimage
scouredimage marked this pull request as ready for review July 24, 2026 21:57

@devin-ai-integration devin-ai-integration Bot 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.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@scouredimage

Copy link
Copy Markdown
Author

One clarification that strengthens the motivation here: on an HTTP-backend deployment the git refs fetch in fetch_missing_notes_for_commits can never actually find notes — push_authorship_notes early-returns for the HTTP backend, so refs/notes/ai on the remotes is empty by construction. The current behavior is therefore strictly lose-lose: with working SSH auth the fetch "succeeds", finds nothing, and the shift silently proceeds without the missing sources; with broken SSH auth it errors and aborts the translation outright. (The backend=git_notes field in fetch_authorship_notes's log line is hardcoded, which makes this path easy to misread as backend-dispatched — it runs for HTTP-backend deployments too.) This PR makes the rewrite path consult the store that actually holds the notes.

@devin-ai-integration devin-ai-integration Bot 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.

Devin Review found 1 new potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment thread src/git/notes_api.rs Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants