From 4d5c6e2c3236bd63b74ccbd4e6cc442276906cac Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Thu, 16 Jul 2026 22:28:34 +0200 Subject: [PATCH 1/3] Freeze measurement score indices --- src/pyrecest/filters/measurement_scoring.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pyrecest/filters/measurement_scoring.py b/src/pyrecest/filters/measurement_scoring.py index 6d0ea03210..337004b297 100644 --- a/src/pyrecest/filters/measurement_scoring.py +++ b/src/pyrecest/filters/measurement_scoring.py @@ -2,6 +2,7 @@ from __future__ import annotations +from collections.abc import Sequence from dataclasses import dataclass from typing import Any @@ -25,11 +26,18 @@ class MeasurementScore: predicted_measurements: Any | None innovation_covariance: Any | None residual: Any | None - active_measurement_indices: list[int] + active_measurement_indices: Sequence[int] measurement_weights: Any | None = None quadratic_form: float | None = None skipped_reason: str | None = None + def __post_init__(self) -> None: + object.__setattr__( + self, + "active_measurement_indices", + tuple(self.active_measurement_indices), + ) + @property def is_active(self) -> bool: """Return whether the score contains at least one active measurement.""" From df073787c5c82cd98f5921ec9b09657f9bac65de Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Thu, 16 Jul 2026 22:28:44 +0200 Subject: [PATCH 2/3] Add measurement score ownership regression --- tests/filters/test_measurement_scoring.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/filters/test_measurement_scoring.py b/tests/filters/test_measurement_scoring.py index bfd63d2767..7c750c45cf 100644 --- a/tests/filters/test_measurement_scoring.py +++ b/tests/filters/test_measurement_scoring.py @@ -15,3 +15,13 @@ def _score(active_measurement_indices): def test_measurement_score_is_active_handles_numpy_index_arrays(): assert _score(np.array([0, 2])).is_active assert not _score(np.array([], dtype=int)).is_active + + +def test_measurement_score_owns_active_measurement_indices(): + active_measurement_indices = [] + score = _score(active_measurement_indices) + + active_measurement_indices.append(0) + + assert score.active_measurement_indices == () + assert not score.is_active From a8960c73e86f80b923f348faf3712165866c8d05 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:21:14 +0200 Subject: [PATCH 3/3] Refresh measurement score immutability fix --- src/pyrecest/filters/measurement_scoring.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pyrecest/filters/measurement_scoring.py b/src/pyrecest/filters/measurement_scoring.py index 337004b297..a4e1cf6760 100644 --- a/src/pyrecest/filters/measurement_scoring.py +++ b/src/pyrecest/filters/measurement_scoring.py @@ -32,6 +32,7 @@ class MeasurementScore: skipped_reason: str | None = None def __post_init__(self) -> None: + # Own caller-provided sequences so this frozen result cannot be mutated indirectly. object.__setattr__( self, "active_measurement_indices",