From 976a02d50fce7b3842b38c23f17a38d2e01b25ff Mon Sep 17 00:00:00 2001 From: Rizel Date: Mon, 30 Mar 2026 06:55:03 -0400 Subject: [PATCH] Fix checkpoint_remote silently falling back to origin on owner mismatch When checkpoint_remote was configured but the origin remote had a different owner, fork detection silently skipped the checkpoint remote and pushed to origin instead. Users had no indication their config was being ignored. Now respects the user's explicit checkpoint_remote configuration regardless of owner mismatch, and prints visible stderr warnings when fallback occurs due to URL derivation or parse failures. Fixes #800 Co-Authored-By: Claude Opus 4.6 (1M context) Entire-Checkpoint: 328e759b573a --- cmd/entire/cli/strategy/checkpoint_remote.go | 17 ++++++++++++----- .../cli/strategy/checkpoint_remote_test.go | 7 ++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cmd/entire/cli/strategy/checkpoint_remote.go b/cmd/entire/cli/strategy/checkpoint_remote.go index c2837b4f8..ac4a36577 100644 --- a/cmd/entire/cli/strategy/checkpoint_remote.go +++ b/cmd/entire/cli/strategy/checkpoint_remote.go @@ -76,9 +76,11 @@ func resolvePushSettings(ctx context.Context, pushRemoteName string) pushSetting return ps } - // Get the push remote URL for protocol detection and fork detection + // Get the push remote URL for protocol detection pushRemoteURL, err := getRemoteURL(ctx, pushRemoteName) if err != nil { + fmt.Fprintf(os.Stderr, "[entire] Warning: checkpoint_remote is configured (%s) but could not get push remote URL: %v\n", config.Repo, err) + fmt.Fprintln(os.Stderr, "[entire] Checkpoints will be pushed to origin instead. Check your git remote configuration.") logging.Debug(ctx, "checkpoint-remote: could not get push remote URL, skipping", slog.String("remote", pushRemoteName), slog.String("error", err.Error()), @@ -86,9 +88,11 @@ func resolvePushSettings(ctx context.Context, pushRemoteName string) pushSetting return ps } - // Parse the push remote URL once for both fork detection and URL derivation + // Parse the push remote URL for protocol detection and URL derivation pushInfo, err := parseGitRemoteURL(pushRemoteURL) if err != nil { + fmt.Fprintf(os.Stderr, "[entire] Warning: checkpoint_remote is configured (%s) but could not parse push remote URL: %v\n", config.Repo, err) + fmt.Fprintln(os.Stderr, "[entire] Checkpoints will be pushed to origin instead. Check your git remote configuration.") logging.Warn(ctx, "checkpoint-remote: could not parse push remote URL", slog.String("remote", pushRemoteName), slog.String("error", err.Error()), @@ -96,19 +100,22 @@ func resolvePushSettings(ctx context.Context, pushRemoteName string) pushSetting return ps } - // Fork detection: compare owners + // Log when push remote owner differs from checkpoint remote owner. + // Previously this skipped checkpoint_remote entirely (fork detection), + // but the user explicitly configured a checkpoint remote — respect that choice. checkpointOwner := config.Owner() if pushInfo.owner != "" && checkpointOwner != "" && !strings.EqualFold(pushInfo.owner, checkpointOwner) { - logging.Debug(ctx, "checkpoint-remote: push remote owner differs from checkpoint remote owner, skipping (fork detected)", + logging.Debug(ctx, "checkpoint-remote: push remote owner differs from checkpoint remote owner, proceeding with configured checkpoint remote", slog.String("push_owner", pushInfo.owner), slog.String("checkpoint_owner", checkpointOwner), ) - return ps } // Derive checkpoint URL using same protocol as push remote checkpointURL, err := deriveCheckpointURLFromInfo(pushInfo, config) if err != nil { + fmt.Fprintf(os.Stderr, "[entire] Warning: checkpoint_remote is configured (%s) but could not derive URL from push remote: %v\n", config.Repo, err) + fmt.Fprintln(os.Stderr, "[entire] Checkpoints will be pushed to origin instead. Check your checkpoint_remote settings.") logging.Warn(ctx, "checkpoint-remote: could not derive URL from push remote", slog.String("remote", pushRemoteName), slog.String("repo", config.Repo), diff --git a/cmd/entire/cli/strategy/checkpoint_remote_test.go b/cmd/entire/cli/strategy/checkpoint_remote_test.go index 5bb2b6e46..3af092620 100644 --- a/cmd/entire/cli/strategy/checkpoint_remote_test.go +++ b/cmd/entire/cli/strategy/checkpoint_remote_test.go @@ -490,9 +490,10 @@ func TestResolvePushSettings_ForkDetection(t *testing.T) { t.Chdir(localDir) ps := resolvePushSettings(ctx, "origin") - // Should fall back to origin since fork detected (alice != org) - assert.False(t, ps.hasCheckpointURL()) - assert.Equal(t, "origin", ps.pushTarget()) + // Should proceed with checkpoint remote even when owners differ — + // the user explicitly configured it, respect that choice. + assert.True(t, ps.hasCheckpointURL()) + assert.Equal(t, "git@github.com:org/checkpoints.git", ps.pushTarget()) assert.False(t, ps.pushDisabled) }