repo mirror: only diagnose suspended mirror on existing placement · Entire

repo mirror: only diagnose suspended mirror on existing placement

928dd85→main·

nodo·1mo ago·3 files·+29 added/-5 removed

A fresh create that races into invalid_target is propagation lag, not suspension — a mirror created moments ago cannot be suspended. Gate the suspended-mirror diagnosis on a non-fresh create so we don't misdirect users to a mirrors resume command that does nothing; let the raw error surface instead. The precondition is enforced inside explainSuspendedMirror so it can't be missed by a future caller.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

e5f7e2236cbbView transcript

[?
Update Auth Go After PR MergeClaude Code·Opus 4.8[1m]·2 steps](/content/gh/entireio/cli/session/01e22aeb-640d-4a2d-a1d0-24072431ca4c#timeline-e5f7e2236cbb/index.html)

Changes

3

155 unmodified lines

156
157
158
159
159
160
161
162

155 unmodified lines

return nil
 }
 if err := waitForMirrorClone(ctx, out, clusterHost, owner, repo, waitTimeout); err != nil {
 if handled, serr := explainSuspendedMirror(cmd.ErrOrStderr(), created.MirrorId, err); handled {
 if handled, serr := explainSuspendedMirror(cmd.ErrOrStderr(), created.MirrorId, created.Created, err); handled {
 cmd.SilenceUsage = true
 return serr
 }

Mcmd/entire/cli/repo_mirror.go+1/-1

138 unmodified lines

139
140
141
142
143
144
145
146
147
148
149
150
145
146
151
152
153
154
155

138 unmodified lines

// suspended mirrors behind invalid_target. Recovery is operator-side, so we
// name the exact resume command rather than leaking the raw OAuth error.
//
// freshCreate gates the diagnosis: a mirror created moments ago cannot be
// suspended (suspension only happens after upstream access is lost), so an
// invalid_target on a fresh create is propagation lag, not suspension.
// Diagnosing that as "suspended" would misdirect the user to a resume command
// that does nothing, so we decline (handled=false) and let the raw error surface.
//
// Returns handled=false for any other error so the caller surfaces it
// verbatim. When handled, the message is already written to w and the
// returned error is a SilentError so main.go won't reprint it.
func explainSuspendedMirror(w io.Writer, mirrorID string, err error) (bool, error) {
 if !errors.Is(err, auth.ErrRepoTargetUnknown) {
func explainSuspendedMirror(w io.Writer, mirrorID string, freshCreate bool, err error) (bool, error) {
 if freshCreate || !errors.Is(err, auth.ErrRepoTargetUnknown) {
 return false, nil
 }
 fmt.Fprintf(w,

Mcmd/entire/cli/repo_mirror_probe.go+8/-2

29 unmodified lines

30
31
32
33
33
34
35
36
10 unmodified lines

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
53
71
72
73
74

29 unmodified lines

// prove detection survives the wrapping chain.
 err := fmt.Errorf("authorize clone probe: %w", fmt.Errorf("repo-scoped token exchange: %w", auth.ErrRepoTargetUnknown))
 var buf bytes.Buffer
 handled, serr := explainSuspendedMirror(&buf, id, err)
 handled, serr := explainSuspendedMirror(&buf, id, false, err)
 if !handled {
 t.Fatal("expected handled=true for ErrRepoTargetUnknown")
 }
10 unmodified lines

}
 })

t.Run("fresh create passes invalid_target through as propagation lag", func(t *testing.T) {
 t.Parallel()
 // Same invalid_target signature, but on a just-created placement it's
 // eventual-consistency lag, not suspension — don't misdirect to resume.
 err := fmt.Errorf("authorize clone probe: %w", fmt.Errorf("repo-scoped token exchange: %w", auth.ErrRepoTargetUnknown))
 var buf bytes.Buffer
 handled, serr := explainSuspendedMirror(&buf, id, true, err)
 if handled {
 t.Error("expected handled=false for a fresh create")
 }
 if serr != nil {
 t.Errorf("expected nil error, got %v", serr)
 }
 if buf.Len() != 0 {
 t.Errorf("expected no output, got %q", buf.String())
 }
 })

t.Run("unrelated error passes through untouched", func(t *testing.T) {
 t.Parallel()
 var buf bytes.Buffer
 handled, serr := explainSuspendedMirror(&buf, id, errors.New("timed out waiting for initial clone"))
 handled, serr := explainSuspendedMirror(&buf, id, false, errors.New("timed out waiting for initial clone"))
 if handled {
 t.Error("expected handled=false for an unrelated error")
 }