# Mobile Remote Submission Lifecycle

Phone sends are split into two separate contracts:

1. **Transport delivery**: the phone delivered an authenticated remote message to CTM on the Mac.
2. **Agent acceptance**: CTM has evidence that the target Claude/Codex/Wall-E session accepted that message.

The phone must not treat transport delivery as final. PTY-based agents use real terminal line editors, so a successful write plus Enter only proves that CTM interacted with the terminal. It does not prove the agent added the prompt to its transcript.

## Data Model

`ctm_remote_submissions` is the durable backend ledger keyed by the remote message id.

Stored fields intentionally avoid a second full prompt copy:

- `message_id`, `session_id`, `device_id`, `message_type`
- `text_hash` for matching and diagnostics
- `text_preview` for UI status only
- `status`, `result_json`, timestamps, attempt count, and last error

The full prompt should live in the provider/session transcript once accepted. Before acceptance, the phone keeps its transport outbox locally until CTM acknowledges the remote envelope.

## Statuses

- `received`: CTM received the remote envelope.
- `typed_to_pty`: CTM wrote the text to the terminal.
- `observing`: CTM pressed Enter and is waiting for acceptance evidence.
- `durable_user_message_seen`: the prompt is present in CTM's session prompt/message index.
- `response_started`: the target session became busy after submission.
- `accepted_by_terminal`: the terminal advanced after Enter and the prompt is no longer visibly waiting.
- `blocked`: CTM refused to type because the session was busy, blocked by approval, or not ready.
- `failed`: CTM started delivery but could not complete it.
- `needs_attention`: CTM delivered bytes but could not prove acceptance, or the prompt still appears at the terminal prompt.

## Observation Rules

For Claude/Codex terminal sessions, CTM records a pre-submit terminal marker, writes text, presses Enter, then observes asynchronously:

- First preference: match the submitted text in the durable prompt/message index.
- Second preference: session status turns busy after the submit.
- Third preference: terminal output advances after Enter. If CTM recorded a
  before-Enter terminal marker, advancement from the pre-typing marker is not
  enough because it can be only the terminal echo of CTM typing the reply.
- Attention path: the submitted text still appears at the visible prompt after a grace period, or no acceptance signal arrives before timeout.

If the exact submitted prompt is still active and the terminal marker did not
move after the first Enter, CTM may press Enter one more time. This retry is
only for a visible stuck composer state; it is not a generic resend of the
remote message and it does not duplicate the phone envelope.

Observation broadcasts `remote-submission-status` over the shared websocket and exposes reconnect state at:

`GET /api/remote/submissions?session_id=<id>`

Both CTM web and phone can consume the same status stream. The phone renders durable pending/attention rows in the timeline. The composer status is only transient feedback for the current text box action, so attention rows must not be mirrored into a second red warning below the input. Desktop can add the same affordance later without changing the backend contract.

## UX Rules

- Phone outbox removal means “Mac accepted the envelope,” not “agent accepted the prompt.”
- While observing, the composer may show “Delivered to Mac. Waiting for the agent to accept it...” as in-flight feedback.
- If attention is needed, show the backend reason in the remote-submission timeline row and keep the composer free for the next action.
- Once accepted, remove pending attention rows and refresh the transcript so the durable prompt/reply replaces optimistic UI.
- Do not hide a pending/attention row just because the phone rendered an optimistic local copy of the submitted prompt; only durable transcript evidence should resolve the remote-submission row.

## Regression Risks

- Do not retry an envelope after CTM acknowledged it. Retries use the same message id only for network ambiguity before a server response.
- Do not store full prompt text in the backend ledger.
- Do not change Claude/Codex copy/paste behavior; this lifecycle observes the existing text-then-Enter terminal contract.
