From 442ca8dbc2e7b3faaaf5fb8ec4a8a9239fc11e29 Mon Sep 17 00:00:00 2001 From: David Dotson Date: Tue, 17 Mar 2026 16:50:27 -0600 Subject: [PATCH] Fix StrategyResult.resolve() mutating internal weights resolve() operated on self.weights directly (a reference to self._weights), so calling it would normalize the internal dict in-place. Subsequent calls would double-normalize, producing incorrect results. Copy the dict first. Co-Authored-By: Claude Opus 4.6 --- src/stratocaster/base/strategy.py | 2 +- src/stratocaster/tests/test_strategy_base.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/stratocaster/base/strategy.py b/src/stratocaster/base/strategy.py index a7e3d3e..327e4ce 100644 --- a/src/stratocaster/base/strategy.py +++ b/src/stratocaster/base/strategy.py @@ -33,7 +33,7 @@ def weights(self) -> dict[GufeKey, float | None]: def resolve(self) -> dict[GufeKey, float | None]: """Normalize the proposal weights relative to all non-None Transformation weights.""" - weights = self.weights + weights = dict(self.weights) weight_sum = sum([weight for weight in weights.values() if weight is not None]) modified_weights = { key: weight / weight_sum diff --git a/src/stratocaster/tests/test_strategy_base.py b/src/stratocaster/tests/test_strategy_base.py index 6b20058..fc890b4 100644 --- a/src/stratocaster/tests/test_strategy_base.py +++ b/src/stratocaster/tests/test_strategy_base.py @@ -17,6 +17,16 @@ class TestStrategyResult: def test_dict_roundtrip(self): assert StrategyResult.from_dict(self.result.to_dict()) == self.result + def test_resolve_does_not_mutate_weights(self): + original_weights = dict(self.result.weights) + self.result.resolve() + assert self.result.weights == original_weights + + def test_resolve_is_idempotent(self): + first = self.result.resolve() + second = self.result.resolve() + assert first == second + class DummyStrategySettings(StrategySettings): pass