Summary
Three related defects in PluginConfig.MovePlugin / movePlugin / newMoveOptions (internal/config/plugin_config.go):
WithBefore, WithAfter, and WithPosition are not validated as mutually exclusive. Caller can pass all three; only one is honoured (switch at the end of movePlugin picks before → after → position, first non-nil wins). Others are silently dropped with no error.
internal/config has no mutex. grep -r sync.Mutex internal/config/ returns nothing. Concurrent mutation of the plugin slices is racy.
- The sequence
moveToCategory then positional move is not atomic. If the positional step fails, the plugin is left in the new category at an arbitrary (appended) position with no rollback.
Proposal
newMoveOptions: return an error if more than one of before, after, position is set.
- Add a mutex (likely
sync.Mutex on the parent config that owns PluginConfig, or on PluginConfig itself) covering all mutating operations.
- Make cross-category + positional moves atomic: snapshot state, attempt both steps, roll back on failure; or compute the full target state first and apply in one step.
Acceptance criteria
- Unit test: passing two of
WithBefore/WithAfter/WithPosition returns a descriptive error.
- Race test (
go test -race) exercising concurrent MovePlugin calls passes.
- Unit test: a failing positional step after a successful category change leaves the original config unchanged.
Summary
Three related defects in
PluginConfig.MovePlugin/movePlugin/newMoveOptions(internal/config/plugin_config.go):WithBefore,WithAfter, andWithPositionare not validated as mutually exclusive. Caller can pass all three; only one is honoured (switch at the end ofmovePluginpicksbefore→after→position, first non-nil wins). Others are silently dropped with no error.internal/confighas no mutex.grep -r sync.Mutex internal/config/returns nothing. Concurrent mutation of the plugin slices is racy.moveToCategorythen positional move is not atomic. If the positional step fails, the plugin is left in the new category at an arbitrary (appended) position with no rollback.Proposal
newMoveOptions: return an error if more than one ofbefore,after,positionis set.sync.Mutexon the parent config that ownsPluginConfig, or onPluginConfigitself) covering all mutating operations.Acceptance criteria
WithBefore/WithAfter/WithPositionreturns a descriptive error.go test -race) exercising concurrentMovePlugincalls passes.