# Lock clone preference updates

`6c8f798`→[main](/content/gh/entireio/cli/commits/main/index.html)·

dipree·3w ago·4 files·+88 added/-80 removed

## Sessions

f47b5f818fbeView transcript

## Changes

4

- cmd/entire/cli

- review

- Mmigration.go+41/-51

- Mpicker.go+9/-19

- settings

- Msettings.go+30

- Mtrail_context_cache.go+8/-10

```go
return fmt.Errorf("review settings migration prompt: %w", err)
}
if !migrate {
	if prefs == nil {
		prefs = &settings.ClonePreferences{}
	}
	prefs.ReviewMigrationDismissed = true
	if err := settings.SaveClonePreferences(ctx, prefs); err != nil {
		if err := settings.ModifyClonePreferences(ctx, func(prefs *settings.ClonePreferences) error {
			prefs.ReviewMigrationDismissed = true
			return nil
		}); err != nil {
			return fmt.Errorf("save migration dismissal: %w", err)
		}
	}
	return nil
}

prefs, err := settings.LoadClonePreferences(ctx)
if err != nil {
	return false, fmt.Errorf("load review preferences for migration: %w", err)
}
if prefs == nil {
	prefs = &settings.ClonePreferences{}
}

preferencesChanged := false
if project.hasReview && !isJSONNull(project.review) {
	var projectReview map[string]settings.ReviewConfig
	if err := json.Unmarshal(project.review, &projectReview); err != nil {
		return false, fmt.Errorf("parsing project review settings: %w", err)
	}
	if len(projectReview) > 0 {
		merged, mergedOK, conflicts := mergeProjectReviewIntoPrefs(prefs.Review, projectReview)
		if len(conflicts) > 0 {
			return false, fmt.Errorf(
				"review settings exist in both %s and clone-local preferences for agent(s) %v; "+
				"reconcile manually by removing the redundant keys from %s, then re-run `entire review`",
				project.path, conflicts, project.path,
			)
		}
		if mergedOK {
			prefs.Review = merged
			preferencesChanged = true
		}
	}
}
if project.hasFixAgent && !isJSONNull(project.fixAgent) {
	var fixAgent string
	if err := json.Unmarshal(project.fixAgent, &fixAgent); err != nil {
		return false, fmt.Errorf("parsing project review_fix_agent: %w", err)
	}
	if fixAgent != "" {
		if prefs.ReviewFixAgent != "" && prefs.ReviewFixAgent != fixAgent {
			return false, fmt.Errorf(
				"review_fix_agent differs between %s (%q) and clone-local preferences (%q); "+
				"reconcile manually by removing review_fix_agent from %s, then re-run `entire review`",
				project.path, fixAgent, prefs.ReviewFixAgent, project.path,
			)
		}
		if prefs.ReviewFixAgent == "" {
			prefs.ReviewFixAgent = fixAgent
			preferencesChanged = true
		}
	}
}
return nil
}); err != nil {
	return false, fmt.Errorf("save review preferences for migration: %w", err)
}

if preferencesChanged {
	if err := settings.SaveClonePreferences(ctx, prefs); err != nil {
		return false, fmt.Errorf("save review preferences for migration: %w", err)
	}
}

delete(project.raw, "review")
delete(project.raw, "review_fix_agent")
if err := settings.SaveProjectRaw(project.path, project.raw); err != nil {

```

- Mcmd/entire/cli/review/migration.go+41/-51

```go
return fmt.Errorf("load review preferences before save: %w", err)
if prefs == nil {
	prefs = &settings.ClonePreferences{}
}
prefs.ReviewFixAgent = agentName
if err := settings.SaveClonePreferences(ctx, prefs); err != nil {
	if err := settings.ModifyClonePreferences(ctx, func(prefs *settings.ClonePreferences) error {
		prefs.ReviewFixAgent = agentName
		return nil
	}); err != nil {
		return fmt.Errorf("save review preferences: %w", err)
	}
}
return nil
}

func saveReviewConfigAndFixAgent(ctx context.Context, review map[string]settings.ReviewConfig, fixAgent string) error {
prefs, err := settings.LoadClonePreferences(ctx)
if err != nil {
	return fmt.Errorf("load review preferences before save: %w", err)
}
if prefs == nil {
	prefs = &settings.ClonePreferences{}
}
prefs.Review = review
prefs.ReviewFixAgent = fixAgent
if err := settings.SaveClonePreferences(ctx, prefs); err != nil {
	if err := settings.ModifyClonePreferences(ctx, func(prefs *settings.ClonePreferences) error {
		prefs.Review = review
		prefs.ReviewFixAgent = fixAgent
		return nil
	}); err != nil {
		return fmt.Errorf("save review preferences: %w", err)
	}
}

```

- Mcmd/entire/cli/review/picker.go+9/-19

```go
return saveClonePreferencesToFile(prefs, path)
}

func ModifyClonePreferences(ctx context.Context, fn func(*ClonePreferences) error) error {
	path, err := ClonePreferencesPath(ctx)
	if err != nil {
		return err
	}
	return modifyClonePreferencesFile(path, fn)
}

func LoadFromBytes(data []byte) (*EntireSettings, error) {
return nil
}

func modifyClonePreferencesFile(filePath string, fn func(*ClonePreferences) error) error {
	if err := os.MkdirAll(filepath.Dir(filePath), 0o750); err != nil {
		return fmt.Errorf("creating preferences directory: %w", err)
	}
	release, err := flock.Acquire(filePath + ".lock")
	if err != nil {
		return fmt.Errorf("lock preferences file: %w", err)
	}
	defer release()

prefs, err := loadClonePreferencesFromFile(filePath)
	if err != nil {
		return err
	}
	if err := fn(prefs); err != nil {
		return err
	}
	return saveClonePreferencesToFile(prefs, filePath)
}

func applyClonePreferences(settings *EntireSettings, prefs *ClonePreferences) {
	if prefs == nil {
		return
	}
```

- Mcmd/entire/cli/settings/settings.go+30

```go
prefs.TrailsEnabled = &enabledCopy
prefs.TrailsEnabledCheckedAt = &checkedAtUTC
prefs.TrailsEnabledRepoKey = scope.RepoKey
prefs.TrailsEnabledAPIBase = scope.APIBase
prefs.TrailsEnabledAuthKey = scope.AuthKey
if err := settings.SaveClonePreferences(ctx, prefs); err != nil {
	if err := settings.ModifyClonePreferences(ctx, func(prefs *settings.ClonePreferences) error {
		prefs.TrailsEnabled = &enabledCopy
		prefs.TrailsEnabledCheckedAt = &checkedAtUTC
		prefs.TrailsEnabledRepoKey = scope.RepoKey
		prefs.TrailsEnabledAPIBase = scope.APIBase
		prefs.TrailsEnabledAuthKey = scope.AuthKey
		return nil
	}); err != nil {
		return fmt.Errorf("save clone preferences: %w", err)
	}
}
return nil
```

- Mcmd/entire/cli/trail_context_cache.go+8/-10
