| index 9e9a31eaf..895ecf69d 100644 |
| --- a/internal/release/adopt.go |
| +++ b/internal/release/adopt.go |
| @@ -117,7 +117,15 @@ func PassFilter(ctx context.Context, reg *registry.Registry, s Store, pub ed2551 |
| // pass: one listing per (account, app), not one per channel. It decides |
| // which of the two missing-pointer lines below an app gets. |
| type acctApp struct{ acct, app string } |
| - migrated := map[acctApp]bool{} |
| + // migratedState caches one pass's answer to "has this app anything under |
| + // its qualified prefix", together with the listing failure when there was |
| + // one — a failed listing must neither invent the migrate advice nor claim |
| + // the healthy skip (2026-08-03 review). |
| + type migratedState struct { |
| + has bool |
| + listErr error |
| + } |
| + migrated := map[acctApp]migratedState{} |
| for _, rt := range routes { |
| if !adopts(rt) { |
| continue |
| @@ -142,19 +150,25 @@ func PassFilter(ctx context.Context, reg *registry.Registry, s Store, pub ed2551 |
| // which names the verb. Advice that fires every hour on a |
| // healthy fleet is advice nobody reads. |
| ak := acctApp{k.acct, k.app} |
| - has, cached := migrated[ak] |
| + st, cached := migrated[ak] |
| if !cached { |
| - var lerr error |
| - has, lerr = AppHasQualifiedObjects(ctx, s, k.acct, k.app) |
| + has, lerr := AppHasQualifiedObjects(ctx, s, k.acct, k.app) |
| // A listing we could not perform suppresses the advice |
| // rather than inventing it: a bucket this box cannot list |
| - // is not evidence that an app never migrated. |
| - has = has || lerr != nil |
| - migrated[ak] = has |
| + // is not evidence that an app never migrated. It is not |
| + // evidence of a healthy skip either, so the failure rides |
| + // along and is printed instead of a claim this box never |
| + // established (realistic trigger: GetObject IAM without |
| + // ListBucket). |
| + st = migratedState{has: has || lerr != nil, listErr: lerr} |
| + migrated[ak] = st |
| } |
| - if has { |
| + switch { |
| + case st.listErr != nil: |
| + fmt.Printf(" %s/%s/%s: channel state unknown (could not list the app's prefix: %v)\n", k.acct, k.app, k.channel, st.listErr) |
| + case st.has: |
| fmt.Printf(" %s/%s/%s: channel has no release yet\n", k.acct, k.app, k.channel) |
| - } else { |
| + default: |
| fmt.Printf(" %s/%s/%s: channel has no release yet (%v)\n", k.acct, k.app, k.channel, err) |
| } |
| } else { |