M2 TUI: EngineHandle, ratatui app, markdown renderer, permission modal, session picker, snapshot tests
harness-app:
- EngineHandle: non-blocking multi-turn API (prompt/abort/permission_reply/
list_sessions/session_messages/message_parts), persistent SQLite store,
no auto-approve — TUI handles permission asks via real oneshot path
- App refactored to wrap EngineHandle; headless run -p keeps auto-approve
harness-tui:
- Terminal guard (raw mode, alternate screen, panic hook, Drop restore)
- Event loop: tokio::select! over crossterm events, bus events, 33ms render tick
- AppState with MessageView/PartView (cached Vec<Line> field), ModalState
- pulldown-cmark → ratatui markdown renderer (headings, bold, italic, code
blocks, lists, blockquotes, links, manual word-wrap)
- Layout: header, chat viewport, input (tui-textarea), status bar
- Permission modal (y/a/n) wired to EngineHandle::permission_reply
- Session picker (Ctrl+S) with resume
- Abort (Esc), Ctrl+C×2 quit, slash commands (/new /model /agent /sessions)
- 7 TestBackend snapshot tests (empty, messages, tool cards, modals)