test: git-remote coverage (2/4) — real-hook P0 tests, cross-machine fetch, explain fix#1637
test: git-remote coverage (2/4) — real-hook P0 tests, cross-machine fetch, explain fix#1637Soph wants to merge 7 commits into
Conversation
Exercise the git-invoked pre-push hook end to end for both checkpoint backends, closing the "real hook is never asserted" blind spot from the git-remote test plan. - A1: extend the seed test to assert the user branch itself arrives (the user push is unaffected by the checkpoint sync). - A2: a push carrying only a non-checkpointed commit is a fast no-op (RemoteCheckpointState digest unchanged). - A3: `git push --delete` (zero-sha stdin) and a tag-only push run through the real hook without crashing and exit 0. - A4: checkpoints routed to a genuinely unreachable target (a missing repo on the in-process HTTPS server) degrade gracefully; the user push still lands the feature branch. Local-path origins can't express this (URL derivation falls back to origin), hence the HTTPS setup. - A6 (git-refs slice): a real push drains the per-checkpoint push queue; the v1 alternates rebase scenario has no git-refs equivalent. Adds GitPushArgsWithHooks, GitTag, PushQueueRefs, and SetGitConfig helpers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq Entire-Checkpoint: 0d5ed27f5bb0
Cover the clone → fetch → read paths for both checkpoint backends. - C1 (git-branch, HTTPS): exercise the production fetchMetadataBranchIfMissing path — a clone that lacks the local v1 branch fetches it during pre-push settings resolution, no manual git fetch. Runs over HTTPS because local-path origins can't derive a checkpoint URL. - C2 (git-refs): a clone with no checkpoint refs reads a checkpoint; the on-demand RefFetcher fetches EXACTLY that ref, leaving an unrelated checkpoint's ref absent until it too is read. - C3 (git-refs): unreachable remote + locally-missing ref. Documents a KNOWN BUG (regression class 7bbdad0): explain's git-refs prefix-match remote fallback (explain_export.go:216-227) discards the FetchCheckpointRef error and reports "checkpoint not found", masking an unreachable remote as an absent checkpoint. The test self-heals: it skips while the masking is present and asserts once fixed. No production change. - C4 (both, HTTPS): reading a checkpoint from a fresh authenticated clone auto-fetches its data with the token (v1 branch on miss for git-branch, the exact per-checkpoint ref for git-refs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq Entire-Checkpoint: 5d532318e7a7
The remote clone tests pushed checkpoints explicitly via PushCheckpointRefs right after the user push, which would mask a broken pre-push hook. Replace those calls with AssertCheckpointsOnRemote so a plain `git push` must sync checkpoints through the installed hook end to end (A5/G1 in spirit). No prompt text changed, so the vogon canary parser is unaffected. Verified green under both E2E_CHECKPOINT_STORE values. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq Entire-Checkpoint: cfbc41e5de4b
Root-caused the git-refs KNOWN BUG skip: it was a harness artifact, not a backend divergence. The test staged the session's file changes after the stop hook, so the "no files modified during session" gate skipped condensation on BOTH backends. git-branch still passed only because the assertion (v1 branch exists) was satisfied by the checkpoint inherited from origin via the plain clone; git-refs correctly reported no local checkpoint (per-checkpoint refs are not fetched by a default clone). Stage before stop like a real agent session (and like the createCheckpointedCommit helper), drop the skip, and assert a NEW checkpoint ID was condensed so inherited clone data can never satisfy the check again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq Entire-Checkpoint: 8429f8830bfd
matchCheckpointPrefixWithRemoteFallback discarded the FetchCheckpointRef
error in its git-refs branch, so an unreachable remote was reported
identically to a genuinely absent checkpoint ("checkpoint not found") —
re-masking exactly the error class 7bbdad0 fixed at the store layer.
The fallback now returns the fetch error and callers surface it as
"checkpoint <id> not found locally; fetching from remote failed: ...".
A repo with no resolvable checkpoint source skips the fetch entirely
(local absence is authoritative there), the commit-ref prefetch path
deliberately keeps ignoring it (the downstream store read surfaces real
failures with correct absent-vs-error semantics), and the v1-branch
fallback keeps its historical fail-soft behavior for now.
Converts the self-healing KNOWN BUG skip in
TestGitRefsClone_UnreachableRemoteMissingRefSurfacesRealError into hard
regression assertions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq
Entire-Checkpoint: ca56eea65fe0
There was a problem hiding this comment.
Pull request overview
This PR (part 2/4 of the #1636 stack) expands integration + e2e coverage around git-remote checkpoint behavior—especially “real git push through installed pre-push hook” flows and cross-machine clone/read fetch paths—and fixes a production bug where checkpoint explain could incorrectly mask an unreachable-remote fetch failure as “checkpoint not found”.
Changes:
- Adds integration tests for real-hook pushes (sync, no-op, delete/tag shapes, unreachable checkpoint_remote degradation, git-refs push-queue drain) and HTTPS clone/read auto-fetch flows.
- Adds git-refs clone read coverage to ensure the on-demand
RefFetcherfetches exactly the needed checkpoint ref and surfaces real remote fetch errors. - Updates explain/prefix-match remote fallback to propagate git-refs fetch errors (instead of reporting “not found”), and updates e2e clone tests to assert hook-driven remote checkpoint sync.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| e2e/tests/resume_remote_test.go | Switches clone flow to assert hook-driven remote checkpoint sync instead of explicitly pushing checkpoint refs. |
| e2e/tests/explain_test.go | Same as above for explain-from-clone e2e coverage. |
| cmd/entire/cli/integration_test/testenv.go | Adds integration test helpers (git config/tag, push queue peek, push-with-hooks args). |
| cmd/entire/cli/integration_test/remote_operations_test.go | Fixes graceful-degradation test ordering and strengthens assertion to require a new checkpoint. |
| cmd/entire/cli/integration_test/refs_fetch_test.go | New C2/C3 git-refs clone/read tests for exact-ref fetch and unreachable-remote error surfacing. |
| cmd/entire/cli/integration_test/real_hook_push_test.go | Adds A2/A3/A4/A6 integration coverage around real pre-push hook behavior. |
| cmd/entire/cli/integration_test/http_remote_test.go | Adds HTTPS cross-machine clone → fetch/read tests (C1/C4). |
| cmd/entire/cli/git_operations.go | Updates FetchCheckpointRef contract comment to require surfacing fetch failures. |
| cmd/entire/cli/explain.go | Propagates remote fetch failures from the prefix-match fallback instead of returning not-found. |
| cmd/entire/cli/explain_export.go | Threads fallback errors through export + commit-ref prefetch behavior; updates fallback helper signature. |
| cmd/entire/cli/attribution.go | Ensures attribution refresh path sees remote fallback errors (instead of treating as not-found). |
| queuePath := filepath.Join(env.RepoDir, ".git", "entire-checkpoint-push-queue.jsonl") | ||
| data, err := os.ReadFile(queuePath) //nolint:gosec // G304: path built from test env, not user input |
There was a problem hiding this comment.
Fixed in a9b0334 — PushQueueRefs now resolves the common dir via git rev-parse --path-format=absolute --git-common-dir, matching production (checkpoint/pushqueue.go), so it's correct from linked worktrees.
| // GitPushArgsWithHooks installs the pre-push hook and runs `git push <args>` | ||
| // (WITHOUT --no-verify) so the real hook fires exactly as git runs it, then | ||
| // returns the combined output and any error instead of failing the test. Use it | ||
| // for push shapes GitPushWithHooks can't express — a `--delete` (zero-sha stdin), | ||
| // a tag-only push, or an expected graceful-degradation exit code. |
There was a problem hiding this comment.
Reworded in a9b0334. One nuance: the wrapper forwarding only the remote name is not a fidelity gap — it matches the production hook template (strategy/hooks.go runs entire hooks git pre-push "$1"), and stdin is inherited via exec. The docs now state that explicitly instead of the blanket "exactly as git runs it".
- PushQueueRefs: resolve the git common dir via rev-parse like production (checkpoint/pushqueue.go) instead of hard-coding RepoDir/.git, so the helper is correct from linked worktrees - clarify InstallRealPrePushHook/GitPushArgsWithHooks docs: the wrapper forwards only the remote name, matching the production hook template Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq Entire-Checkpoint: 283495b6edfd
https://entire.io/gh/entireio/cli/trails/758
Part 2 of the stack on #1636. The P0 test groups from the plan, plus two fixes they surfaced.
Tests
fetchMetadataBranchIfMissingexercised for real over HTTPS (C1 — previous clone tests substituted a rawgit fetch), git-refs on-demandRefFetcherfetches exactly the needed ref and nothing else (C2), unreachable-remote reads surface the real fetch error (C3), authenticated HTTPS clone auto-fetch under both backends (C4).resume_remote_test.go/explain_test.goclone tests now rely on the installed hook (AssertCheckpointsOnRemote) instead of explicitPushCheckpointRefs— a broken hook can no longer pass the e2e suite.Fixes
fix: surface git-refs checkpoint fetch failures in explain fallback:matchCheckpointPrefixWithRemoteFallbackdiscarded theFetchCheckpointReferror, reporting an unreachable remote identically to an absent checkpoint — re-masking the error class 7bbdad0 fixed at the store layer. The fallback now propagates the error (repos with no resolvable checkpoint source keep the clean not-found; the commit-ref prefetch stays best-effort; the v1-branch path keeps its historical fail-soft behavior). C3 asserts it hard.Verification
mise run fmt && mise run lintclean per commitmise run test:integrationgreen (430 tests, 0 skips at tip)mise run test:e2e:canarygreen under bothE2E_CHECKPOINT_STOREvalues🤖 Generated with Claude Code
https://claude.ai/code/session_012yi3hHGAGepwfrfjPETjGq
Note
Medium Risk
Changes user-visible error handling on checkpoint explain and attribution when remotes are unreachable; behavior is more correct but could surprise scripts that only matched on “not found”.
Overview
Adds broad integration and e2e coverage for checkpoint sync and cross-machine reads: real
git pushthrough the installed pre-push hook (sync, no-op pushes, delete/tag shapes, unreachablecheckpoint_remote, git-refs push-queue drain), HTTPS clone flows (fetchMetadataBranchIfMissing, tokenized auto-fetch, per-refRefFetcherprecision), plus e2e tests that assert checkpoints land on the remote via the hook instead of explicitPushCheckpointRefs.Fixes misleading “checkpoint not found” when the remote fetch actually failed (regression 7bbdad0):
matchCheckpointPrefixWithRemoteFallbacknow returns a thirderrorvalue; git-refsFetchCheckpointReffailures and post-fetch re-list errors propagate throughcheckpoint explain, JSON export, and attribution remote enrichment. Repos with no resolvable remote still get a clean local not-found; the v1 metadata-branch path keeps prior fail-soft behavior.Test harness / correctness tweaks:
TestEnvhelpers for config, tags, push-queue peek, and hook pushes with arbitrary args; graceful-degradation clone test stages files before stop and requires a new checkpoint ID (fixes a false positive from inherited v1 metadata).Reviewed by Cursor Bugbot for commit 52f48e2. Configure here.