feat(sync): add per-field merge strategies for conflict resolution
Implements configurable per-field merge strategies (hq-ew1mbr.11):
- Add FieldStrategy type with strategies: newest, max, union, manual
- Add conflict.fields config section for per-field overrides
- compaction_level defaults to "max" (highest value wins)
- estimated_minutes defaults to "manual" (flags for user resolution)
- labels defaults to "union" (set merge)
Manual conflicts are displayed during sync with resolution options:
bd sync --ours / --theirs, or bd resolve <id> <field> <value>
Config example:
conflict:
strategy: newest
fields:
compaction_level: max
estimated_minutes: manual
labels: union
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
e0dc3a37c3
commit
9a9704b451
@@ -76,6 +76,20 @@ const (
|
||||
ConflictStrategyManual ConflictStrategy = "manual"
|
||||
)
|
||||
|
||||
// FieldStrategy represents the merge strategy for a specific field
|
||||
type FieldStrategy string
|
||||
|
||||
const (
|
||||
// FieldStrategyNewest uses last-write-wins (default for scalar fields)
|
||||
FieldStrategyNewest FieldStrategy = "newest"
|
||||
// FieldStrategyMax takes the maximum value (for counters like compaction_level)
|
||||
FieldStrategyMax FieldStrategy = "max"
|
||||
// FieldStrategyUnion performs set union (for arrays like labels, waiters)
|
||||
FieldStrategyUnion FieldStrategy = "union"
|
||||
// FieldStrategyManual flags conflict for user resolution (for fields like estimated_minutes)
|
||||
FieldStrategyManual FieldStrategy = "manual"
|
||||
)
|
||||
|
||||
// validConflictStrategies is the set of allowed conflict strategy values
|
||||
var validConflictStrategies = map[ConflictStrategy]bool{
|
||||
ConflictStrategyNewest: true,
|
||||
@@ -84,6 +98,14 @@ var validConflictStrategies = map[ConflictStrategy]bool{
|
||||
ConflictStrategyManual: true,
|
||||
}
|
||||
|
||||
// validFieldStrategies is the set of allowed per-field strategy values
|
||||
var validFieldStrategies = map[FieldStrategy]bool{
|
||||
FieldStrategyNewest: true,
|
||||
FieldStrategyMax: true,
|
||||
FieldStrategyUnion: true,
|
||||
FieldStrategyManual: true,
|
||||
}
|
||||
|
||||
// ValidConflictStrategies returns the list of valid conflict strategy values.
|
||||
func ValidConflictStrategies() []string {
|
||||
return []string{
|
||||
@@ -99,6 +121,21 @@ func IsValidConflictStrategy(strategy string) bool {
|
||||
return validConflictStrategies[ConflictStrategy(strings.ToLower(strings.TrimSpace(strategy)))]
|
||||
}
|
||||
|
||||
// ValidFieldStrategies returns the list of valid per-field strategy values.
|
||||
func ValidFieldStrategies() []string {
|
||||
return []string{
|
||||
string(FieldStrategyNewest),
|
||||
string(FieldStrategyMax),
|
||||
string(FieldStrategyUnion),
|
||||
string(FieldStrategyManual),
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidFieldStrategy returns true if the given string is a valid per-field strategy.
|
||||
func IsValidFieldStrategy(strategy string) bool {
|
||||
return validFieldStrategies[FieldStrategy(strings.ToLower(strings.TrimSpace(strategy)))]
|
||||
}
|
||||
|
||||
// Sovereignty represents the federation sovereignty tier
|
||||
type Sovereignty string
|
||||
|
||||
@@ -223,3 +260,8 @@ func (s ConflictStrategy) String() string {
|
||||
func (s Sovereignty) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// String returns the string representation of the FieldStrategy.
|
||||
func (f FieldStrategy) String() string {
|
||||
return string(f)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user