@@ -426,6 +426,8 @@ def _tool_result_build_web_search(tr: dict, base: dict) -> dict:
426426 result = dict (base )
427427 result ["result_type" ] = "web_search"
428428 result ["query" ] = tr .get ("query" , "" )
429+ # Defensive: legacy ``len(tr.get("results", []))`` crashed when key existed
430+ # with value None (``len(None)``). Non-sized ``results`` → count 0.
429431 raw_results = tr .get ("results" )
430432 if isinstance (raw_results , (list , tuple , set , dict )):
431433 result ["result_count" ] = len (raw_results )
@@ -449,6 +451,10 @@ def _tool_result_build_web_fetch(tr: dict, base: dict) -> dict:
449451
450452
451453def _tool_result_pred_task_message (tr : dict ) -> bool :
454+ # Broad: matches ``task_id`` OR ``message``. Runs before retrieval/completed/async
455+ # arms below — same short-circuit order as the original if/elif chain. Payloads
456+ # that also carry e.g. ``agentId`` still classify here if they have ``message``.
457+ # Refining order needs golden fixtures; track as follow-up if real collisions appear.
452458 return "task_id" in tr or "message" in tr
453459
454460
@@ -537,7 +543,16 @@ def _tool_result_build_plan(tr: dict, base: dict) -> dict:
537543 return result
538544
539545
540- # Ordered dispatch: first matching predicate wins (legacy if/elif semantics).
546+ # Dispatch registry: **first matching predicate wins** (same as legacy if/elif).
547+ # Order is load-bearing — do not sort alphabetically or “more specific first”
548+ # without replaying tests and real session fixtures.
549+ #
550+ # Notably ``task_message`` is intentionally broad (``task_id`` or ``message``)
551+ # and sits before ``task_retrieval`` / ``task_completed`` / ``task_async`` so
552+ # payloads that include overlapping keys still match the legacy branch order.
553+ #
554+ # To add a shape: append ``(pred, build)`` here, or insert only after verifying
555+ # predicates above would not steal intended matches.
541556_TOOL_RESULT_DISPATCH = (
542557 (_tool_result_pred_bash , _tool_result_build_bash ),
543558 (_tool_result_pred_file_edit , _tool_result_build_file_edit ),
@@ -561,9 +576,13 @@ def _parse_tool_result(tool_result, slug: str | None = None) -> dict | None:
561576 """Figure out what kind of tool result this is (bash, file edit, glob, etc.)
562577 by looking at which keys are present, since the JSONL doesn't always tag them.
563578
564- Classification uses ``_TOOL_RESULT_DISPATCH``: append ``(predicate, builder)``
565- pairs to register a new shape; keep order consistent with Claude Code JSONL
566- evolution (more specific branches before generic ones)."""
579+ Classification uses ``_TOOL_RESULT_DISPATCH``: ordered ``(predicate, builder)``
580+ pairs; the **first** predicate that matches wins (parity with the historical
581+ ``if``/``elif`` chain — order is not strictly “specific before generic”).
582+
583+ Append a new pair at the end to register a shape, or insert mid-table only
584+ after checking interactions with broader predicates above (see notes on the
585+ tuple)."""
567586 if not isinstance (tool_result , dict ):
568587 return None
569588
0 commit comments