diff --git a/scripts/audit_exception_handling.py b/scripts/audit_exception_handling.py index a4cfd081..e47d8216 100644 --- a/scripts/audit_exception_handling.py +++ b/scripts/audit_exception_handling.py @@ -591,47 +591,6 @@ class ExceptionVisitor(ast.NodeVisitor): f"Compliant: `try: ...; except Exception: return ` in a `-> str` tool function is the canonical MCP tool boundary pattern (per result_migration_review_pass_20260617).", ) - # 22. Narrow except + return fallback value (the function's return type is NOT Result) - if len(except_body) > 0 and not exc_set & {"Exception", "BaseException", ""} and self._has_simple_return(except_body): - enclosing_func = self._current_func_node() - if enclosing_func is None or enclosing_func.returns is None or "Result[" not in ast.unparse(enclosing_func.returns): - return ( - "INTERNAL_COMPLIANT", - f"Compliant: `try: ...; except ({', '.join(sorted(exc_set))}): return ` in a non-Result-returning function is the canonical fallback pattern (per result_migration_small_files_20260617 Phase 10.3).", - ) - - # 23. Narrow except + use error inline (the except body uses `e`/`exc` in a non-pass way) - if len(except_body) > 0 and not exc_set & {"Exception", "BaseException", ""} and self._uses_exception_inline(except_body): - return ( - "INTERNAL_COMPLIANT", - f"Compliant: `try: ...; except ({', '.join(sorted(exc_set))}) as exc: ` is the canonical inline-error pattern (per result_migration_small_files_20260617 Phase 10.3).", - ) - - # 24. Narrow except + assign fallback (no return, just sets a variable to a fallback value) - if len(except_body) > 0 and not exc_set & {"Exception", "BaseException", ""} and self._has_assign_fallback(except_body): - return ( - "INTERNAL_COMPLIANT", - f"Compliant: `try: ...; except ({', '.join(sorted(exc_set))}): = ` is the canonical assign-fallback pattern (per result_migration_small_files_20260617 Phase 10.3).", - ) - - # 25. Narrow except + uses traceback module (e.g., traceback.format_exc()) - if len(except_body) > 0 and not exc_set & {"Exception", "BaseException", ""} and self._uses_traceback(except_body): - return ( - "INTERNAL_COMPLIANT", - f"Compliant: `try: ...; except ({', '.join(sorted(exc_set))}): ` is the canonical detailed-error pattern (per result_migration_small_files_20260617 Phase 10.3).", - ) - - # 26. Narrow except + runs fallback function/loop (no `e` use, just calls something else or loops) - if len(except_body) > 0 and not exc_set & {"Exception", "BaseException", ""} and len(except_body) > 0: - # If we have any non-trivial body (excluding just `pass`), and it's a narrow catch, - # assume it's a fallback pattern. - has_trivial_body = all(isinstance(s, ast.Pass) for s in except_body) - if not has_trivial_body: - return ( - "INTERNAL_COMPLIANT", - f"Compliant: `try: ...; except ({', '.join(sorted(exc_set))}): ` is the canonical fallback pattern (per result_migration_small_files_20260617 Phase 10.3).", - ) - return None def _has_string_return(self, stmts: list[ast.stmt]) -> bool: diff --git a/tests/test_audit_exception_handling_heuristics.py b/tests/test_audit_exception_handling_heuristics.py index 22dcac49..e7ad46ae 100644 --- a/tests/test_audit_exception_handling_heuristics.py +++ b/tests/test_audit_exception_handling_heuristics.py @@ -23,6 +23,8 @@ import sys import textwrap from pathlib import Path +import pytest + ROOT = Path(__file__).resolve().parents[1] SCRIPT = ROOT / "scripts" / "audit_exception_handling.py" @@ -292,17 +294,11 @@ def test_json_parse_with_print_is_compliant(): # --------------------------------------------------------------------------- -# Heuristic 22: Narrow except + return fallback value +# Heuristic 22: Narrow except + return fallback value (REJECTED Phase 11) # --------------------------------------------------------------------------- +@pytest.mark.xfail(reason="Heuristic #22 REVERTED in Phase 11 (laundering heuristic; full Result[T] migration required). See conductor/tracks/result_migration_small_files_20260617/plan.md §11.1.1.") def test_narrow_except_returns_fallback_is_compliant(): - """try: ; except SpecificError: return (in a non-Result-returning function) is compliant. - - The function returns a meaningful fallback that the caller can check. - The error is intentionally swallowed because the caller has no use - for it (the fallback value is the canonical "not found" or "error" signal). - This is the pattern used by src/project_manager.py:get_git_commit, - src/aggregate.py:is_absolute_with_drive, src/models.py:load_mcp_configuration, etc. - """ + """REJECTED in Phase 11. Heuristic #22 classified narrow-catch + fallback as compliant, which is WRONG. The convention requires `Result[T]`; this test is preserved as xfail for traceability and to ensure the count of 11 test tiers is maintained.""" src = ''' def get_git_commit(git_dir): try: @@ -321,16 +317,11 @@ def test_narrow_except_returns_fallback_is_compliant(): # --------------------------------------------------------------------------- -# Heuristic 23: Narrow except + use error inline (formatted into another value) +# Heuristic 23: Narrow except + use error inline (REJECTED Phase 11) # --------------------------------------------------------------------------- +@pytest.mark.xfail(reason="Heuristic #23 REVERTED in Phase 11 (laundering heuristic; full Result[T] migration required). See conductor/tracks/result_migration_small_files_20260617/plan.md §11.1.2.") def test_narrow_except_uses_error_inline_is_compliant(): - """try: ; except SpecificError as exc: is compliant. - - The error is captured in `exc` and used in a formatted string assigned - to a variable (e.g., ps1_name = f"(write error: {exc})"). The fallback is - explicit and the caller can see what went wrong from the formatted string. - This is the pattern used by src/session_logger.py:log_tool_call. - """ + """REJECTED in Phase 11. Heuristic #23 classified narrow-catch + use-error-inline as compliant, which is WRONG. The convention requires `Result[T]`; this test is preserved as xfail for traceability and to ensure the count of 11 test tiers is maintained.""" src = ''' def write_script(ps1_path, script): try: