Skip to content

Commit 4092362

Browse files
codexclaude
andcommitted
fix(receipts): three bookkeeping paths that swallowed failure with pass
All three wrap a receipt/delivery-note call in 'except Exception: pass' with a comment saying bookkeeping must never break the real work. That part was right — the search still returns, the reply still goes out, the tool result still stands. 'pass' is the part that was not. A receipt path broken for weeks looked exactly like one that never needed to fire, on the live chat route, the reply-delivery note, and the dev-mode tool receipt. Each now records a degradation and returns exactly what it returned before, opting out of the fail-closed policy: a receipt recorder throwing is a degraded bookkeeping lane, not a reason to fail a delivered reply. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 18f5804 commit 4092362

3 files changed

Lines changed: 33 additions & 6 deletions

File tree

core/transparency/dev_mode.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,16 @@ async def complete_tool_execution(self, trace: ToolExecutionTrace,
195195
from core.conversation.surface_disposition import record_tool_receipt
196196

197197
record_tool_receipt(trace.tool_name, ok=bool(result.get("ok", False)))
198-
except Exception: # never let bookkeeping break a tool result
199-
pass
198+
except Exception as exc: # never let bookkeeping break a tool result
199+
# The tool result stands. A receipt recorder that throws on
200+
# every call is a real fault, and `pass` was hiding it.
201+
record_degradation(
202+
"transparency_dev_mode",
203+
exc,
204+
severity="warning",
205+
action="returned the tool result after receipt bookkeeping failed",
206+
enforce_failure_policy=False,
207+
)
200208

201209
if self.level != TransparencyLevel.SILENT:
202210
if deferred:

interface/routes/chat.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10074,6 +10074,7 @@ def _complete_repairable_truncated_reply(user_message: Any, reply_text: Any) ->
1007410074
_log_response_quality_metrics,
1007510075
_reply_assessment_requires_repair,
1007610076
)
10077+
from core.runtime.errors import record_degradation
1007710078

1007810079
# ── Conversation Lane Helpers ─────────────────────────────────
1007910080

@@ -16762,8 +16763,17 @@ async def _collect_desktop_required_search_evidence(
1676216763
from core.conversation.surface_disposition import record_tool_receipt
1676316764

1676416765
record_tool_receipt("web_search", ok=bool(result.get("ok")))
16765-
except Exception: # bookkeeping must never break a collected search
16766-
pass
16766+
except Exception as exc: # bookkeeping must never break a collected search
16767+
# `pass` here meant a receipt path broken forever looked exactly like
16768+
# one that never needed to fire. The search still returns; the
16769+
# bookkeeping failure is now on the record.
16770+
record_degradation(
16771+
"chat_routes",
16772+
exc,
16773+
severity="warning",
16774+
action="returned the collected search after tool-receipt bookkeeping failed",
16775+
enforce_failure_policy=False,
16776+
)
1676716777

1676816778
return {
1676916779
"ok": bool(result.get("ok")),

interface/routes/chat_quality.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,17 @@ def _log_response_quality_metrics(
6363
from core.conversation.surface_delivery import note_route_delivered
6464

6565
note_route_delivered(reply_text)
66-
except Exception: # bookkeeping must never break a delivered reply
67-
pass
66+
except Exception as exc: # bookkeeping must never break a delivered reply
67+
# The reply still goes out. A delivery-note path that has been broken
68+
# for weeks should not be indistinguishable from one that simply had
69+
# nothing to note.
70+
record_degradation(
71+
"chat_quality",
72+
exc,
73+
severity="warning",
74+
action="delivered the reply after route-delivery bookkeeping failed",
75+
enforce_failure_policy=False,
76+
)
6877

6978
try:
7079
assessment_reasons: tuple[str, ...] = ()

0 commit comments

Comments
 (0)