Calendar Day
Wednesday, July 8, 2026 (PR 2 of 2)
Planned Effort
3 story points (Medium–High) — sprint item #4
Companion PR: Wednesday PR 1 (renderer tests #3) — independent files.
Blocks: Thursday PR 2 (dispatch simplification #6) — adversarial fixtures must land before refactor.
Problem
utils/tool_dispatch.py classifies tool results through _parse_tool_result using _TOOL_RESULT_DISPATCH — a 15-entry ordered (predicate, builder) tuple where first matching predicate wins and order is load-bearing.
tests/test_tool_dispatch_ordering.py (#95 / PR #98) only checks ordering structurally via _predicate_index for 4 declared ORDERING_INVARIANTS. It does not construct JSON blobs that satisfy multiple predicates simultaneously and assert the actual classified winner.
Week 1 PR #104 added KNOWN_TOOL_TYPES and test_tool_dispatch_sync.py but not behavioral overlap fixtures. A new upstream tool shape matching a broad predicate before a narrower handler would misclassify silently while existing tests still pass.
Relationship to prior work (not a duplicate)
| Prior |
Status |
What it covers |
What it does not cover |
| #95 Predicate ordering structural enforcement |
Closed |
Declarative ORDERING_INVARIANTS; index(A) < index(B) in test_tool_dispatch_ordering.py |
Adversarial JSON; _parse_tool_result winner; result_type assertions |
PR #98 test: enforce tool_dispatch predicate ordering invariants |
Merged |
Implementation of #95 — structural tuple-position guards |
Behavioral overlap fixtures |
PR #104 KNOWN_TOOL_TYPES registry + sync test |
Merged |
Tool-name set parity across backend/frontend (test_tool_dispatch_sync.py) |
Predicate overlap classification |
This issue is the behavioral half that planning explicitly marked as residual after #95/#98 and #104. Keep test_tool_dispatch_ordering.py unchanged; add test_tool_dispatch_adversarial.py (or a behavioral section) alongside it.
Goal
One merged PR that adds adversarial toolUseResult fixtures asserting documented classification winners via _parse_tool_result, complementing (not replacing) the structural ordering tests.
Scope
Touch points
tests/test_tool_dispatch_adversarial.py (new) — or extend tests/test_tool_dispatch_ordering.py with a behavioral section.
- Build adversarial JSON shapes satisfying multiple predicates; assert
result_type via _parse_tool_result.
- Cover every pair in
ORDERING_INVARIANTS:
is_plan_tool_result vs is_file_write_tool_result (filePath + content).
is_task_message_tool_result vs narrower is_task_retrieval_tool_result / is_task_completed_tool_result / is_task_async_tool_result.
- Regression case: demonstrate reordering a predicate flips classification and fails the new test.
- Optional: hypothesis-based generation (pattern from
tests/test_parser_fuzz.py).
Out of scope
Acceptance Criteria
Verification
cd C:\Users\Jasen\CppAliance\claude-code-chat-browser
.\.venv\Scripts\Activate.ps1
pytest tests/test_tool_dispatch_adversarial.py tests/test_tool_dispatch_ordering.py -v
pytest tests/test_tool_dispatch_sync.py tests/test_real_session_fixtures.py -q
pytest -q
mypy .
ruff check .
Manual: temporarily swap two predicates in _TOOL_RESULT_DISPATCH — confirm adversarial test fails with a clear message, then revert.
References
Calendar Day
Wednesday, July 8, 2026 (PR 2 of 2)
Planned Effort
3 story points (Medium–High) — sprint item #4
Companion PR: Wednesday PR 1 (renderer tests #3) — independent files.
Blocks: Thursday PR 2 (dispatch simplification #6) — adversarial fixtures must land before refactor.
Problem
utils/tool_dispatch.pyclassifies tool results through_parse_tool_resultusing_TOOL_RESULT_DISPATCH— a 15-entry ordered(predicate, builder)tuple where first matching predicate wins and order is load-bearing.tests/test_tool_dispatch_ordering.py(#95 / PR #98) only checks ordering structurally via_predicate_indexfor 4 declaredORDERING_INVARIANTS. It does not construct JSON blobs that satisfy multiple predicates simultaneously and assert the actual classified winner.Week 1 PR #104 added
KNOWN_TOOL_TYPESandtest_tool_dispatch_sync.pybut not behavioral overlap fixtures. A new upstream tool shape matching a broad predicate before a narrower handler would misclassify silently while existing tests still pass.Relationship to prior work (not a duplicate)
ORDERING_INVARIANTS;index(A) < index(B)intest_tool_dispatch_ordering.py_parse_tool_resultwinner;result_typeassertionstest: enforce tool_dispatch predicate ordering invariantsKNOWN_TOOL_TYPESregistry + sync testtest_tool_dispatch_sync.py)This issue is the behavioral half that planning explicitly marked as residual after #95/#98 and #104. Keep
test_tool_dispatch_ordering.pyunchanged; addtest_tool_dispatch_adversarial.py(or a behavioral section) alongside it.Goal
One merged PR that adds adversarial
toolUseResultfixtures asserting documented classification winners via_parse_tool_result, complementing (not replacing) the structural ordering tests.Scope
Touch points
tests/test_tool_dispatch_adversarial.py(new) — or extendtests/test_tool_dispatch_ordering.pywith a behavioral section.result_typevia_parse_tool_result.ORDERING_INVARIANTS:is_plan_tool_resultvsis_file_write_tool_result(filePath+content).is_task_message_tool_resultvs narroweris_task_retrieval_tool_result/is_task_completed_tool_result/is_task_async_tool_result.tests/test_parser_fuzz.py).Out of scope
_TOOL_RESULT_DISPATCHselection logic (Thursday item Add CLI + Web GUI for browsing Claude Code sessions #6).KNOWN_TOOL_TYPESregistry (PR #104).Acceptance Criteria
ORDERING_INVARIANTSpair intest_tool_dispatch_ordering.py._parse_tool_result, not tuple index comparison.is_task_message_tool_resultfrom narrower task predicates.test_tool_dispatch_ordering.pypreserved.pytest,mypy --strict, andruffpass.Verification
Manual: temporarily swap two predicates in
_TOOL_RESULT_DISPATCH— confirm adversarial test fails with a clear message, then revert.References
test: enforce tool_dispatch predicate ordering invariantsKNOWN_TOOL_TYPES+test_tool_dispatch_sync.pyrefactor/dispatch-simplify)