refactor(metadata_promotion): Phases 3,4,6,9,10 proper dataclass migrations

TIER-2 READ AGENTS.md, conductor/workflow.md, conductor/edit_workflow.md,
conductor/tier2/githooks/forbidden-files.txt,
conductor/tracks/tier2_leak_prevention_20260620/spec.md,
conductor/code_styleguides/data_oriented_design.md,
conductor/code_styleguides/error_handling.md,
conductor/code_styleguides/type_aliases.md before Phases 3-10.

Forward-only progress on metadata_promotion_20260624 Phases 3,4,6,9,10
(did NOT modify or revert existing commits; all work adds to the timeline).

Per-site migrations to direct dataclass attribute access:

Phase 3 (CommsLogEntry) - src/app_controller.py:2278,2303,2311:
  Added `comms_entry = CommsLogEntry.from_dict(entry)` after payload
  extraction; replaced dict access with `.source_tier`, `.model`.

Phase 4 (HistoryMessage):
  - src/synthesis_formatter.py:24,37: added HistoryMessage.from_dict
    conversion for msg dicts in format_takes_diff.
  - src/gui_2.py:7794: added HistoryMessage.from_dict conversion for
    disc_entries[-1] content comparison; added HistoryMessage import.

Phase 6 (UsageStats) - src/app_controller.py:2299-2311:
  Added `u_stats = models.UsageStats(...)` with field-name mapping
  (dict cache_read_input_tokens -> UsageStats.cache_read_tokens).
  Replaced dict access with `.input_tokens`, `.output_tokens`.

Phase 9 (RAGChunk) - src/app_controller.py:251,4171, src/ai_client.py:3262:
  RAG search returns wire-format dicts with path nested in metadata
  (mismatches RAGChunk schema which has path at top level).
  Per-site resolution: direct dict access with explicit key checks.
  Documented schema mismatch in commit.

Phase 10 (SessionInsights) - src/gui_2.py:4926-4934:
  Added `SessionInsights.from_dict(...)` for session insights dict;
  replaced .get() pattern with direct attribute access.

Verification:
- 58 tests pass (synthesis_formatter, session_insights, comms_log_entry,
  history_message, metadata_promotion_phase1, ticket_queue,
  file_item_model, rag_engine)

Open blockers for Tier 1:
- src/type_aliases.py:91 ToolCall: TypeAlias = Metadata should be
  TypeAlias = "openai_schemas.ToolCall" (Phase 0 typo; blocks Phase 7)
- src/models.py:537 FileItem.custom_slices: list[dict] blocks
  CustomSlice migration (frozen dataclass can't be mutated)
- src/rag_engine.py:367 search() returns List[Dict] not List[RAGChunk]
  (return-type cascade needed)
- ToolDefinition not wired into per-vendor tool builders (sites
  construct wire dicts)
- Remaining Phase 10 aggregates (DiscussionSettings, MMAUsageStats,
  ProviderPayload, UIPanelConfig, PathInfo, ContextPreset) deferred
This commit is contained in:
ed
2026-06-25 19:20:03 -04:00
parent 5e2d0eb7aa
commit f0a6b32704
4 changed files with 46 additions and 29 deletions
+11 -10
View File
@@ -120,6 +120,7 @@ from src import theme_2 as theme
from src import thinking_parser
from src import workspace_manager
from src.hot_reloader import HotReloader
from src.type_aliases import HistoryMessage, SessionInsights
win32gui: Any = None
win32con: Any = None
@@ -4922,15 +4923,13 @@ def render_session_insights_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_session_insights_panel")
imgui.text_colored(C_LBL(), 'Session Insights')
imgui.separator()
insights = app.controller.get_session_insights()
imgui.text(f"Total Tokens: {insights.get('total_tokens', 0):,}")
imgui.text(f"API Calls: {insights.get('call_count', 0)}")
imgui.text(f"Burn Rate: {insights.get('burn_rate', 0):.0f} tokens/min")
imgui.text(f"Session Cost: ${insights.get('session_cost', 0):.4f}")
completed = insights.get('completed_tickets', 0)
efficiency = insights.get('efficiency', 0)
imgui.text(f"Completed: {completed}")
imgui.text(f"Tokens/Ticket: {efficiency:.0f}" if efficiency > 0 else "Tokens/Ticket: N/A")
insights = SessionInsights.from_dict(app.controller.get_session_insights())
imgui.text(f"Total Tokens: {insights.total_tokens:,}")
imgui.text(f"API Calls: {insights.call_count}")
imgui.text(f"Burn Rate: {insights.burn_rate:.0f} tokens/min")
imgui.text(f"Session Cost: ${insights.session_cost:.4f}")
imgui.text(f"Completed: {insights.completed_tickets}")
imgui.text(f"Tokens/Ticket: {insights.efficiency:.0f}" if insights.efficiency > 0 else "Tokens/Ticket: N/A")
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_session_insights_panel")
def render_prior_session_view(app: App) -> None:
@@ -7791,7 +7790,9 @@ def _handle_history_logic_result(app: "App") -> Result[bool]:
)
if not changed and len(current.disc_entries) > 0:
if current.disc_entries[-1].get('content') != app._last_ui_snapshot.disc_entries[-1].get('content'):
curr_msg = HistoryMessage.from_dict(current.disc_entries[-1])
prev_msg = HistoryMessage.from_dict(app._last_ui_snapshot.disc_entries[-1])
if curr_msg.content != prev_msg.content:
changed = True
if changed: