fix(effect-status): harden effect_status decode + fix tests for typed synth

- Turn.effect_status also catches TypeError: a corrupt non-string meta value
  (e.g. a dict that survived into the column) would otherwise crash a consumer
  on access, since EffectStatus(non-str) raises TypeError, not ValueError.
  Degrade to None, mirroring the meta decoders (Copilot review).
- test_lowering: the wire-repair synth now carries the _effect_status side
  channel (stripped before the provider wire) — assert it.
- test_session_mcp_dispatch_error: the _capture stub swallows the new status
  kwarg via **_ so it stays signature-compatible with _report_tool_result.
This commit is contained in:
Patrick Buckley
2026-06-26 08:24:04 -07:00
parent b74a5e116b
commit c7d8acb6a5
3 changed files with 12 additions and 2 deletions
+3
View File
@@ -126,6 +126,9 @@ def test_repair_synthesizes_trailing_orphan() -> None:
"tool_call_id": "c1",
"content": CANCELLED_TOOL_RESULT,
"is_error": True,
# The unobserved synth carries the typed disposition (wire-invisible
# side channel, stripped by the translator before the provider wire).
"_effect_status": "unknown",
}
+5 -1
View File
@@ -105,7 +105,11 @@ def _record_outputs(session) -> list[tuple[str, str, str, bool]]:
"""Patch ``_report_tool_result`` to capture (call_id, name, output, is_error)."""
captures: list[tuple[str, str, str, bool]] = []
def _capture(call_id: str, name: str, output: str, *, is_error: bool = False) -> None:
def _capture(
call_id: str, name: str, output: str, *, is_error: bool = False, **_: object
) -> None:
# ``**_`` swallows the typed ``status`` kwarg (and any future ones) so
# the stub stays signature-compatible with ``_report_tool_result``.
captures.append((call_id, name, output, is_error))
session._report_tool_result = _capture # type: ignore[method-assign]
+4 -1
View File
@@ -161,7 +161,10 @@ class Turn:
return None
try:
return EffectStatus(raw)
except ValueError:
except (ValueError, TypeError):
# ValueError: not a known status string. TypeError: a corrupt
# non-string value (e.g. a dict survived into the meta). Mirror the
# meta decoders and degrade to None rather than crash a consumer.
return None
# -- construction helpers (blunt the wrapping cost of uniform block content) --