)]}'
{"watcher/api/controllers/v1/audit.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"3a8030f366ba453a046fd4aa9cd3c74d33de6281","unresolved":false,"context_lines":[{"line_number":878,"context_line":"        except api_utils.JSONPATCH_EXCEPTIONS as e:"},{"line_number":879,"context_line":"            raise exception.PatchError(patch\u003dpatch, reason\u003de)"},{"line_number":880,"context_line":""},{"line_number":881,"context_line":"        if _patch_updates_parameters(patch):"},{"line_number":882,"context_line":"            # The parameters were explicitly modified: re-validate and"},{"line_number":883,"context_line":"            # re-resolve them against the current version of the strategy"},{"line_number":884,"context_line":"            # spec, which also updates the strategy version marker"}],"source_content_type":"text/x-python","patch_set":1,"id":"97feba9e_ba0f8168","line":881,"updated":"2026-11-06 17:50:09.000000000","message":"The PATCH handler re-resolves parameters by passing the patched parameters snapshot into _validate_and_resolve_parameters, which rejects any parameters dict containing the reserved \u0027strategy_version\u0027 key. Every audit created after this change with a predefined strategy stores that marker in its parameters snapshot, so any JSON patch that targets a parameter sub-path (for example \u0027replace /parameters/fake1\u0027 or \u0027add /parameters/para2\u0027) keeps the marker in audit.parameters and fails with 400 \u0027The strategy_version parameter is reserved and cannot be set\u0027. A full-object \u0027replace /parameters\u0027 only succeeds when the client manually strips the marker from the GET response. Before this change, PATCH stored parameter updates without re-validation, so sub-path parameter patches worked.\n\n**Severity**: HIGH | **Confidence**: 0.88\n\n**Impact**: Any client or tool that modifies a single audit parameter with a JSON patch - a workflow that worked before this change - receives an unexplained HTTP 400 for every audit created after the upgrade, breaking audit parameter updates until clients rebuild and strip the whole parameters object.\n\n**Priority**: Before merge\n**Recommendation**:\nIn the PATCH parameters branch, strip the stored marker before re-validating, e.g. call audit.parameters.pop(utils.STRATEGY_VERSION_PARAMETER, None) before self._validate_and_resolve_parameters(...), while keeping the strict rejection of user-supplied reserved keys. Add a unit test that patches \u0027/parameters/fake1\u0027 on an audit whose snapshot contains the marker and asserts HTTP 200 plus the re-resolved parameters.","commit_id":"26d593c7e136fb8c50259ad9f5b140d4b6939d4a"}],"watcher/decision_engine/strategy/strategies/basic_consolidation.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"3a8030f366ba453a046fd4aa9cd3c74d33de6281","unresolved":false,"context_lines":[{"line_number":123,"context_line":"    def get_schema(cls):"},{"line_number":124,"context_line":"        # Mandatory default setting for each element"},{"line_number":125,"context_line":"        return {"},{"line_number":126,"context_line":"            \"version\": cls.SCHEMA_VERSION,"},{"line_number":127,"context_line":"            \"properties\": {"},{"line_number":128,"context_line":"                \"migration_attempts\": {"},{"line_number":129,"context_line":"                    \"description\": \"Maximum number of combinations to be \""}],"source_content_type":"text/x-python","patch_set":1,"id":"460aff37_083ec1b0","line":126,"updated":"2026-11-06 17:50:09.000000000","message":"The decision engine Syncer soft-deletes a strategy row and creates a new one with a new ID whenever its stored parameters_spec differs from the discovered get_schema() dict, and then cancels every audit and action plan that referenced the old ID. This change adds a \"version\" key to the dict returned by every in-tree get_schema(), while parameters_spec rows written by earlier releases do not contain that key. On the first sync after upgrade, every existing strategy is therefore flagged stale, recreated with a new ID, and all audits referencing it - including ongoing CONTINUOUS audits - plus their action plans are moved to the terminal CANCELLED state. This contradicts the change\u0027s stated purpose of updating strategies without cancelling referencing audits on upgrades and the release note\u0027s upgrade claim that pre-existing audits are still executed.\n\n**Severity**: HIGH | **Confidence**: 0.85\n\n**Impact**: Every deployment that upgrades to this release stops ongoing optimization on the first decision-engine start: strategies are recreated with new IDs, all existing audits and action plans are cancelled (a terminal state), and operators must recreate audits by hand; audit and action plan history continuity is lost.\n\n**Priority**: Before merge\n**Recommendation**:\nMake the first sync a no-op for existing strategies: ignore the \u0027version\u0027 key when comparing parameters_spec in _soft_delete_stale_strategies, and update the stored spec (including its version) on matching strategies in place instead of soft-deleting and recreating them. Add a unit test that syncs a strategy row whose stored spec lacks \u0027version\u0027 and asserts it is updated in place with no audit or action plan cancellation.","commit_id":"26d593c7e136fb8c50259ad9f5b140d4b6939d4a"}]}
