From f8acb2a3ed17c5ac767558ee0171af03fcce9b18 Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Sat, 10 Oct 2026 16:39:46 -0700 Subject: [PATCH] Preserve bounded semantic compaction failure codes --- amplifier_module_loop_live/failures.py | 5 +++-- tests/test_failures.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/amplifier_module_loop_live/failures.py b/amplifier_module_loop_live/failures.py index f94127d..52af37d 100644 --- a/amplifier_module_loop_live/failures.py +++ b/amplifier_module_loop_live/failures.py @@ -57,7 +57,8 @@ def turn_failure(error, stage="manager_turn"): safe_codes = {"native_input_oversized", "native_checkpoint_invalid", "native_no_reduction", "native_measurement_unavailable", "native_compaction_failed", "disabled", "request_context_unavailable", "invalid_native_contract", - "authoritative_measurement_unavailable"} + "authoritative_measurement_unavailable", "summary_output_limit", "summary_empty", + "previous_failure", "summary_failed", "native_failed"} count_failure = count_diagnostic(error) if count_failure is not None: category, stage = 'context_measurement', 'context_preparation' @@ -89,7 +90,7 @@ def turn_failure(error, stage="manager_turn"): # Class names from arbitrary third-party exceptions are not trusted text. kind = next((cls.__name__ for cls in type(error).__mro__ if cls.__module__ in {"builtins", "amplifier_core.llm_errors"}), "Exception") - return {**({"error_code": code} if code in safe_codes else {}), + return {**({"error_code": code} if isinstance(code, str) and code in safe_codes else {}), **({'count_failure': count_failure} if count_failure is not None else {}), "error_type": "CompactionError" if compaction else kind, "error_category": category, "error_stage": stage, "error_message": message, "effects": "not_rolled_back", "replayed": False, diff --git a/tests/test_failures.py b/tests/test_failures.py index 1674951..e3e9a3e 100644 --- a/tests/test_failures.py +++ b/tests/test_failures.py @@ -59,7 +59,8 @@ async def test_local_failure_keeps_cause_and_identity_without_provider_error_or_ loop._execute_guarded_goal.assert_awaited_once() -@pytest.mark.parametrize('code', ['native_input_oversized', 'private payload']) +@pytest.mark.parametrize('code', ['native_input_oversized', 'summary_output_limit', 'summary_empty', + 'previous_failure', 'summary_failed', 'native_failed', 'private payload', {'secret': 'value'}]) def test_compaction_failure_preserves_bounded_code_without_optional_dependency(code): error_class = type('CompactionError', (RuntimeError,), { '__module__': 'amplifier_module_context_managed.errors'}) @@ -69,7 +70,7 @@ def test_compaction_failure_preserves_bounded_code_without_optional_dependency(c assert failure['error_category'] == 'context_compaction' assert failure['error_stage'] == 'context_preparation' assert failure['error_type'] == 'CompactionError' - assert failure.get('error_code') == ('native_input_oversized' if code != 'private payload' else None) + assert failure.get('error_code') == (code if isinstance(code, str) and code != 'private payload' else None) assert 'secret' not in json.dumps(failure) assert 'private payload' not in json.dumps(failure) assert failure['retryable'] is False