# V2 Socket API + Test Migration

This doc tracks the migration from the existing v1 line protocol (space-delimited commands) to a v2 JSON protocol intended for LLM agents.

## Goals

- Add a **v2 JSON socket protocol** (handle-based: `window_id`, `workspace_id`, `pane_id`, `surface_id`).
- Keep **v1 fully working** until v2 reaches feature parity.
- Re-implement the existing automated test suite to use **v2**.
- Run both suites:
  - v1 tests (existing `tests/`)
  - v2 tests (new `tests_v2/`)

## Non-Goals (for initial parity)

- Removing v1.
- Changing existing v1 behaviors/output formats.

## Status

- [x] Implement v2 request/response envelope (JSON, newline-delimited)
- [x] Implement v2 core methods (workspaces/surfaces/panes/input/notifications/browser)
- [x] Implement v2 multi-window methods (windows + cross-window workspace moves)
- [x] Add `surface.trigger_flash` (agent-visible highlight for a surface)
- [x] Implement v2 debug/test methods (simulate typing, render stats, screenshots, etc.)
- [x] Add `tests_v2/` using v2 client
- [x] Add runners for v1 + v2 suites on the VM (`./scripts/run-tests-v1.sh`, `./scripts/run-tests-v2.sh`)
- [x] Verify v1 suite passes (VM)
- [x] Verify v2 suite passes (VM)

Notes:
- A close-top nested split sequence (T-shape) could leave terminal views detached from the window until the user switched workspaces.
  Fix: a debounced post-close reattach pass (see `Sources/Workspace.swift`, `Sources/Panels/TerminalPanel.swift`).

## V2 Protocol Sketch

Each request is one JSON object per line:

```json
{"id":"1","method":"workspace.list","params":{}}
```

Each response is one JSON object per line:

```json
{"id":"1","ok":true,"result":{...}}
```

Errors:

```json
{"id":"1","ok":false,"error":{"code":"not_found","message":"workspace not found"}}
```

Notes:
- `id` is echoed back when present (string or number).
- v2 methods should accept **IDs**; v2 responses may include ephemeral `index` fields for ordering/debugging, but IDs are the stable handles.

## Method Parity Checklist (v1 -> v2)

Windows:
- [x] list_windows -> `window.list`
- [x] current_window -> `window.current`
- [x] focus_window -> `window.focus`
- [x] new_window -> `window.create`
- [x] close_window -> `window.close`
- [x] move_workspace_to_window -> `workspace.move_to_window`

Workspaces:
- [x] list_workspaces -> `workspace.list`
- [x] new_workspace -> `workspace.create`
- [x] select_workspace -> `workspace.select`
- [x] current_workspace -> `workspace.current`
- [x] close_workspace -> `workspace.close`

Surfaces / Splits:
- [x] list_surfaces -> `surface.list`
- [x] focus_surface / focus_surface_by_panel -> `surface.focus`
- [x] new_split -> `surface.split`
- [x] new_surface -> `surface.create`
- [x] close_surface -> `surface.close`
- [x] drag_surface_to_split -> `surface.drag_to_split`
- [x] refresh_surfaces -> `surface.refresh`
- [x] surface_health -> `surface.health`
- [x] trigger_flash -> `surface.trigger_flash` (new in v2)

Panes:
- [x] list_panes -> `pane.list`
- [x] focus_pane -> `pane.focus`
- [x] list_pane_surfaces -> `pane.surfaces`
- [x] new_pane -> `pane.create`

Input:
- [x] send / send_surface -> `surface.send_text`
- [x] send_key / send_key_surface -> `surface.send_key`

Notifications:
- [x] notify -> `notification.create`
- [x] notify_surface -> `notification.create_for_surface`
- [x] notify_target -> `notification.create_for_target`
- [x] list_notifications -> `notification.list`
- [x] clear_notifications -> `notification.clear`
- [x] set_app_focus -> `app.focus_override.set`
- [x] simulate_app_active -> `app.simulate_active`

Browser:
- [x] open_browser -> `browser.open_split`
- [x] navigate -> `browser.navigate`
- [x] browser_back -> `browser.back`
- [x] browser_forward -> `browser.forward`
- [x] browser_reload -> `browser.reload`
- [x] get_url -> `browser.url.get`
- [x] focus_webview -> `browser.focus_webview`
- [x] is_webview_focused -> `browser.is_webview_focused`

Debug / Test-only:
- [x] set_shortcut -> `debug.shortcut.set`
- [x] simulate_shortcut -> `debug.shortcut.simulate`
- [x] simulate_type -> `debug.type`
- [x] activate_app -> `debug.app.activate`
- [x] is_terminal_focused -> `debug.terminal.is_focused`
- [x] read_terminal_text -> `debug.terminal.read_text`
- [x] render_stats -> `debug.terminal.render_stats`
- [x] layout_debug -> `debug.layout`
- [x] bonsplit_underflow_count/reset -> `debug.bonsplit_underflow.*`
- [x] empty_panel_count/reset -> `debug.empty_panel.*`
- [x] focus_notification -> `debug.notification.focus`
- [x] flash_count/reset -> `debug.flash.*`
- [x] panel_snapshot/panel_snapshot_reset -> `debug.panel_snapshot.*`
- [x] screenshot -> `debug.window.screenshot`

## Test Migration

v1 suite stays in `tests/`.

v2 suite lives in `tests_v2/` and should:
- use a v2 JSON client (`tests_v2/cmux.py`)
- avoid depending on v1 text output formats

VM runners:
- v1: `ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && ./scripts/run-tests-v1.sh'`
- v2: `ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && ./scripts/run-tests-v2.sh'`

## Open Questions

- Should v2 require explicit `workspace_id`/`surface_id` for all operations, or default to the currently-focused ones?
- For move/reorder operations (future): what are the policies for empty workspaces/windows?
