返回 DeepSeek-Reasonix
historical_conversion_target.go
根目录 / desktop / historical_conversion_target.go
1 package main
2
3 import (
4 "context"
5 "errors"
6 "sort"
7
8 "reasonix/desktop/internal/workspacestate"
9 "reasonix/internal/session"
10 )
11
12 // A receipt proves adoption of an exact source revision. The target may have
13 // advanced since then. Registry ownership takes precedence over old receipts;
14 // absent that, redundant retired receipts must not obscure a unique live owner.
15 func (a *App) selectCanonicalConversionTarget(ctx context.Context, source desktopMigrationSource, path, digest string, receipts []desktopMigrationReceipt) (string, error) {
16 candidates := map[string]bool{}
17 for _, receipt := range receipts {
18 if receipt.ContentDigest == digest && receipt.TargetSessionID != "" {
19 candidates[receipt.TargetSessionID] = true
20 }
21 }
22 if len(candidates) == 0 {
23 return "", nil
24 }
25 state, err := a.workspaceRegistry().Load(ctx)
26 if err != nil {
27 return "", err
28 }
29 if mapping, found, resolveErr := state.ResolveSource(source.mappingKey(path)); resolveErr == nil && found && candidates[mapping.SessionID] {
30 return mapping.SessionID, nil
31 }
32 if len(candidates) == 1 {
33 for id := range candidates {
34 return id, nil
35 }
36 }
37 live, retired := []string{}, []string{}
38 for id := range candidates {
39 if state.SessionStates[id].Lifecycle == workspacestate.Deleted {
40 retired = append(retired, id)
41 continue
42 }
43 if _, err := a.desktopSessionService("").Query().Stat(ctx, session.SessionRef{HostID: localDesktopHostID, SessionID: id}); err != nil {
44 // A missing active destination still owns a recoverable identity.
45 // Never pick another target merely because its files survived.
46 return "", errors.Join(historicalOwnershipConflict(), err)
47 }
48 live = append(live, id)
49 }
50 if len(live) == 1 {
51 return live[0], nil
52 }
53 if len(live) == 0 && len(retired) != 0 {
54 // Every proved adoption was deleted. Keep the source retired rather
55 // than importing it again; no active conversation is chosen here.
56 sort.Strings(retired)
57 return retired[0], nil
58 }
59 return "", historicalOwnershipConflict()
60 }
61
62 func historicalOwnershipConflict() error {
63 return errors.Join(newSessionOperationError("source_ambiguous", "Conflicting migration receipts prevent identifying this historical source."), workspacestate.ErrMutationConflict)
64 }
65
65 lines GO