Task 01: Canonical Messaging Adoption
Status: COMPLETE ✓
| Phase | Status | Branch |
|---|---|---|
| P1 (Messaging) | ✓ Complete | types-architect |
| P2 (Tooling) | ✓ Complete | types-architect |
Tests: 430 passed LOC delta: -156 lines in sanitize.py, +5 lines in messaging exports
Summary
Production message handling now uses canonical message and part types. This task was split into two phases:
- P1 (Messaging): ✓ Replaced legacy
get_message_content()withadapter.get_content() - P2 (Tooling): ✓ Consolidated tool call tracking by routing sanitize.py through adapter helpers
P1: Message Content Accessor Migration ✓
Scope
Replace 3 production call sites using legacy get_message_content() with the canonical adapter.get_content().
Changes Made
| File | Change |
|---|---|
src/tunacode/core/state.py |
✓ Import get_content from messaging, call in update_token_count() |
src/tunacode/ui/app.py |
✓ Import get_content from messaging, call in _replay_session_messages() |
src/tunacode/ui/headless/output.py |
✓ Import get_content from messaging, call in _extract_from_messages() |
src/tunacode/utils/messaging/__init__.py |
✓ Re-exports get_content from adapter for convenience |
Acceptance Criteria - VERIFIED
- ✓ All 3 call sites use
adapter.get_content() - ✓ No imports from
message_utilsin production code (legacy kept for parity tests) - ✓ All existing tests pass (430 tests)
- ✓ Token counting, session replay, and headless output work correctly
P2: Tool Call Tracking Consolidation ✓
Scope
Eliminate duplicate tool call tracking logic by routing sanitize.py through canonical adapter helpers.
Changes Made - sanitize.py
| Function | Status | Notes |
|---|---|---|
_get_attr_value() |
✓ DELETED | Uses adapter._get_attr() |
_get_message_parts() |
✓ DELETED | Uses adapter._get_parts() |
_collect_tool_call_ids_from_parts() |
✓ DELETED | Detection via adapter |
_collect_tool_return_ids_from_parts() |
✓ DELETED | Detection via adapter |
_collect_message_tool_call_ids() |
✓ DELETED | Detection via adapter |
_collect_message_tool_return_ids() |
✓ DELETED | Detection via adapter |
_collect_tool_call_ids_from_tool_calls() |
✓ DELETED | Detection via adapter |
find_dangling_tool_call_ids() |
✓ THIN WRAPPER | Calls adapter.find_dangling_tool_calls() |
Functions KEPT in sanitize.py
These are mutation/sanitization concerns, not detection:
| Function | Lines | Purpose |
|---|---|---|
_set_message_parts() |
57-64 | Set parts (mutation) |
_set_message_tool_calls() |
66-77 | Set tool_calls (mutation) |
_normalize_list() |
80-89 | Normalize to list (local helper) |
_get_message_tool_calls() |
91-99 | Read tool_calls for mutation |
_filter_dangling_tool_calls_from_parts() |
111-149 | Filter parts (mutation) |
_filter_dangling_tool_calls_from_tool_calls() |
152-178 | Filter tool_calls (mutation) |
_strip_dangling_tool_calls_from_message() |
181-233 | Strip from message (mutation) |
remove_dangling_tool_calls() |
236-280 | Mutate message history |
remove_consecutive_requests() |
283-338 | Repair abort scenarios |
remove_empty_responses() |
341-379 | Repair abort scenarios |
_strip_system_prompt_parts() |
382-392 | Strip system prompts |
sanitize_history_for_resume() |
395-464 | pydantic-ai compatibility |
run_cleanup_loop() |
467-520 | Orchestrate multi-pass cleanup |
Changes Made - main.py
BEFORE (35 lines):
max_cleanup_iterations = 10
total_cleanup_applied = False
for cleanup_iteration in range(max_cleanup_iterations):
# ... embedded cleanup loop
AFTER (3 lines):
total_cleanup_applied, dangling_tool_call_ids = run_cleanup_loop(
session_messages, tool_call_args_by_id
)
Changes Made - messaging/init.py
Exports _get_attr and _get_parts for internal modules (sanitize.py):
from tunacode.utils.messaging.adapter import (
_get_attr,
_get_parts,
find_dangling_tool_calls,
# ...
)
Acceptance Criteria - VERIFIED
- ✓ sanitize.py uses adapter functions for detection (
find_dangling_tool_calls,_get_attr,_get_parts) - ✓ main.py calls
run_cleanup_loop()instead of embedded loop - ✓ Only mutation helpers remain in sanitize.py (detection delegated to adapter)
- ✓ Resume/abort scenarios work correctly (tests pass)
- ✓ All existing tests pass (430 tests)
Sequencing - COMPLETE
P1 (Messaging) ✓ P2 (Tooling) ✓
│ │
▼ │
[Replace 3 call sites] ✓ │
│ │
▼ │
[Update __init__.py] ✓ │
│ │
▼ ▼
[Tests pass] ✓ ─────► [Route sanitize.py through adapter] ✓
│
▼
[Replace main.py cleanup loop] ✓
│
▼
[Tests pass] ✓
Files Modified
P1 Files
- ✓
src/tunacode/core/state.py- import/callget_content - ✓
src/tunacode/ui/app.py- import/callget_content - ✓
src/tunacode/ui/headless/output.py- import/callget_content - ✓
src/tunacode/utils/messaging/__init__.py- re-exportsget_content,_get_attr,_get_parts
P2 Files
- ✓
src/tunacode/core/agents/resume/sanitize.py- deleted ~156 LOC of duplicate accessors - ✓
src/tunacode/core/agents/main.py- replaced 35-line loop withrun_cleanup_loop()call - ✓
src/tunacode/utils/messaging/adapter.py- no changes needed (already had helpers)
Related Docs
- PLAN.md - Overall architecture refactor plan
- Architecture Refactor Status - Current implementation status
- Canonical Types - Target type definitions
- Message Adapter - Canonical adapter implementation