* feat(mobile): add session.tabs.list handler to mock server
The mock WebSocket server had no handler for session.tabs.list, so the
session screen of a paired dev client hung on 'Loading tabs' forever —
the terminal pane, live input, and command input could never be
exercised against the mock. Respond with a single ready terminal tab
wired to the existing term-1 fixture so the whole session surface works
offline.
* fix(mobile): complete the session.tabs.list mock contract
The new mock response omitted four non-optional fields of
RuntimeMobileSessionTabsResult: publicationEpoch and activeGroupId on the
result, and parentTabId and leafId on the terminal tab. Nothing caught it —
the object literal had no type annotation, and MobileSessionTabsStreamHealth
is generic over both result and tab. A shape-incomplete mock yields
untrustworthy repros for exactly the bugs it gets used for (session tabs,
split panes, pane-to-tab attribution).
Fill the fields with host-realistic values: a per-process publisher epoch, a
layout UUID leaf id, and the `${parentTabId}::${leafId}` surface id
mobileTerminalSurfaceId actually emits. Pin the shape with an explicit return
type so a future required field fails typecheck instead of silently drifting.
Move the fixture into its own module: inlining it pushed
mock-server-rpc-handlers.ts to 317 lines against a 300-line max-lines cap,
which broke `pnpm lint` on the parent commit. It registers through the file's
existing delegation chain, after the native-chat scenario so MOCK_NATIVE_CHAT=1
keeps ownership of the method.
Co-authored-by: Hanjoon Choe <hanjoonchoe@gmail.com>
* test(mobile): pin session tabs mock fidelity
Normalize the selector-backed worktree ID like the real runtime and cover the complete terminal surface response so future contract drift fails the mobile suite.
* fix(mobile): share terminal.list worktree resolution with session tabs
Main added `terminalListWorktreeId`, which the rebased session-tabs fixture
duplicated with a different no-selector fallback — `terminal.list` resolved to
the active fake worktree while `session.tabs.list` returned a literal 'mock',
so a session repro saw two different worktree ids for one screen.
* test(mobile): cover the bare session-tabs worktree selector
Answers the review note that only the `id:`-prefixed path was exercised.
* fix(mobile): make the mock publication epoch unique per process
Date.now() can repeat across a sub-millisecond restart, so the epoch did not
actually guarantee the fresh-publisher identity its comment claimed.
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>