# Session Needs You Dismissal

## Problem

CTM shows operator-attention sessions in several places: the desktop Active Sessions list, the Sessions overview/standup page, and the phone/iPad Status page. These surfaces used to mix labels such as "Waiting" and "Needs User", and mobile had a local-only dismiss behavior. That made the same session appear actionable on one surface and quiet on another.

The shared contract is now:

- User-facing attention label: `Needs You`.
- Backend lane/status source of truth: `needs_user` / `waiting_input`.
- Dismissal persistence: `startup_tasks`, so the choice survives refreshes and CTM restarts.
- Reset trigger: only a newer submitted user prompt/input marker for the same session.

## Data Model

`startup_tasks` stores three attention fields:

- `attention_dismissed_at`: when the operator dismissed the current attention state.
- `attention_dismissed_input_at`: the latest user-prompt marker known when dismissed.
- `last_user_prompt_input_at`: latest known submitted prompt/input that can reset the dismissal.

The dismissal is active when:

```text
attention_dismissed_at > 0
and (last_user_prompt_input_at is empty or last_user_prompt_input_at <= attention_dismissed_input_at)
```

This intentionally ignores passive events: terminal redraws, session heartbeats, agent output, status polling, restore/resume transitions, and websocket reconnects.

## API

Desktop and mobile both use:

```http
POST /api/sessions/:sessionId/attention-dismiss
Content-Type: application/json

{ "dismissed": true }
```

The response includes both camelCase and snake_case marker fields:

- `attentionDismissedAt` / `attention_dismissed_at`
- `attentionDismissedInputAt` / `attention_dismissed_input_at`
- `lastUserPromptInputAt` / `last_user_prompt_input_at`
- `attentionDismissedActive` / `attention_dismissed_active`

Sending `{ "dismissed": false }` clears the dismissal.

## Reset Semantics

The reset marker is written when CTM sees a real submitted user prompt for a non-blocking waiting state. The guard is deliberate:

- Approval/choice prompts should not be reset by pressing `1`, `2`, `y`, `n`, or Escape.
- Running/idle flicker should not reset a dismissal.
- Agent output after dismissal should not reset a dismissal.
- A new user prompt that starts the session again should reset it.

## UI Contract

Desktop:

- Active Sessions groups show `Needs You`.
- `Needs You` rows expose a compact `Dismiss` action.
- The Sessions overview card action area shows `Dismiss` on `needs_user` cards.
- The top attention banner can be dismissed with the same durable API.

Mobile/iPad:

- Status grouping uses the same backend lane and label.
- Local storage dismissal is only a fallback for older snapshots; server marker fields override it.
- A server-reported newer `lastUserPromptInputAt` clears any local fallback.

## Testing

Important regression coverage:

- `tests/session-standup.test.js`: classifier demotes dismissed attention and resets only after newer user prompt markers.
- `tests/active-session-collapse.test.js`: desktop Active Sessions label/render harness.
- `tests/rendering/scenarios/standup-command-nav.spec.js`: overview card/banner dismiss actions.
- `tests/rendering/scenarios/mobile-pwa.spec.js`: mobile dismissal survives reload/output-only refresh and resets on newer prompt marker.

Do not reintroduce frontend-only dismissal state as the source of truth. It will regress cross-device consistency after reloads and CTM restarts.
