# Mobile E2E Coverage

ZERRO Mobile의 route, CTA, 기능 검증, Android Back/header back 정책을 함께 관리하는 현재 정본이다. 과거 local artifact 목록과 긴 Mermaid graph는 보존하지 않는다. 새 화면이나 상호작용이 생기면 이 문서에는 "무엇을 검증해야 하는가"만 남기고, 실행 증거는 PR 또는 local `artifacts/mobile/**`에 둔다.

## Update Rule

- screen route, CTA destination, modal/sheet, wizard step, Android Back hierarchy가 바뀌면 같은 PR에서 갱신한다.
- provider/backend/realtime/auth/file upload가 아직 없으면 `Missing`이 아니라 `Provider pending`으로 표기한다.
- 발표용 UX flow HTML은 review artifact일 뿐 구현 근거가 아니다. 구현 근거는 Figma, source, mock contract, 이 coverage 문서다.
- visual evidence는 red box/E-label overlay 없는 clean screenshot을 기본으로 한다. selector overlay는 보조 proof로만 둔다.

## Legend

| Status | Meaning |
|---|---|
| Covered | Maestro 또는 unit/static test가 `testID`와 상태 전이를 검증한다 |
| Partial | 화면은 있으나 provider/backend/운영 예외 coverage가 남아 있다 |
| Provider pending | 실제 provider 또는 backend 계약이 없어 mock/provider-pending surface로 막아 둔 상태다 |
| Missing | 구현된 route/CTA가 있는데 검증이 없다 |

## Harness

| Harness | Use |
|---|---|
| `scripts/e2e-maestro-dev-client.mjs` | route, CTA, modal, wizard, role flow의 기본 gate |
| `scripts/e2e-agent-device.mjs` | selector snapshot, keyboard/composer, visual evidence 보강 |
| `adb exec-out screencap -p` | clean screenshot. overlay/debug capture보다 우선한다 |
| unit/static tests | mock mutation, selector contract, domain helper 검증 |

## Current Coverage Map

| Area | Current covered scope | Next focus |
|---|---|---|
| Role shell / onboarding | root role selection, `/login` runtime credential submit, `/session/roles` context selection, session restore/logout, invalid role fallback, common notification shell. local smoke는 mock auth, backend mode는 `EXPO_PUBLIC_API_BASE` 기반 Spring auth/session adapter와 local demo seed credential까지 검증 | multi-company/duties expansion, production session-expired/access-denied parity |
| Emitter request | reported/unreported request wizard, 신고 기준 확인 동의 gate, transporter search/favorite toggle, duplicate vehicle detail rows, schedule validation, completion landing. backend mode는 `EXPO_PUBLIC_API_BASE` + Spring demo seed 기준 create API까지 검증 | Olbaro provider |
| Emitter history/detail | today/completed list, search, detail, duplicate cancel guard, receipt provider-pending viewer. backend mode는 create 이후 history/detail readback과 pre-dispatch cancel success를 검증하고, backend test는 dispatch 이후 cancel conflict를 검증 | actual receipt file provider implementation follows `PROVIDER_ADAPTER_CONTRACT.md`; local upload smoke starts with floci S3 |
| Emitter notifications/more | read/unread filter, non-chat feed projection, customer-support web handoff surface. backend notification/chat feed는 alarm read와 chat read-state projection을 검증 | push provider, support URL/auth handoff |
| Emitter chat | chat list/room/search/local draft, contact sheet, emergency contact, Gifted Chat action sheet, uploaded image/video preview. backend mode는 `createRuntimeAppChatClient`를 통해 room list/detail/read/send alias를 화면에서 직접 사용하고 `emitter-chat-backend` Maestro로 room 진입/전송을 검증한다 | realtime transport, real picker/upload provider implementation follows `PROVIDER_ADAPTER_CONTRACT.md`; local upload smoke starts with floci S3 |
| Dispatcher dispatch | board filters, detail, assign, validation, confirm modal, complete, inbound-confirm preview. backend mode는 `/dispatch/board` read와 `/dispatch/board/assign` + `/dispatch/board/confirm` adapter를 검증 | backend-mode device proof, cancel/reassign mutation, realtime conflict |
| Dispatcher/processor chat | common chat room/list/contact surfaces, Gifted Chat action sheet, uploaded media preview. backend read-state persistence는 `/chat/{roomId}/read` + notification chat feed projection으로 검증하고, 화면은 `useAppChatRuntimeSurface`를 통해 backend room list/detail/read/send alias를 사용한다. `common-chat-backend` Maestro는 dispatcher 공통 surface의 room 진입/전송을 검증한다 | realtime transport, real picker/upload provider implementation follows `PROVIDER_ADAPTER_CONTRACT.md`; local upload smoke starts with floci S3 |
| Driver schedule/sheet | dispatch confirm, all-confirmed, schedule, route guide fallback, loading checklist, unloading proof, weighing OCR, dispatch sheet/detail, vehicle change. backend mode는 `/driver/assignments/today`, confirm, loading-start, unloading-complete와 driver 계근 OCR upload/job/weighing adapter를 검증. driver OCR은 `/files/upload-sessions` create -> local/dev PUT -> `/complete` -> `/ocr/jobs` -> `/driver/assignments/{id}/weighing` 순서로 진행하며, failed OCR branch는 retryable provider error로 weighing mutation 전에 중단한다. continuous `gps_track`/background location은 `LOCATION_TRACKING_CONTRACT.md` 기준 MVP 제외이며, 위치는 explicit driver action의 foreground event-time evidence까지만 다룬다 | map/phone provider runtime values, foreground event-time location provider, Google Cloud Vision real smoke, vehicle change adapter |
| Processor weighing | list, camera permission, capture, OCR confirm, complete, denied/stop flows. backend mode는 `processor-weighing-backend` flow에서 `/login` session으로 processor capture URI를 `/files/upload-sessions` create -> local/dev PUT -> `/complete` -> `/ocr/jobs` create/read로 보내 APP OCR draft를 만든다. failed OCR branch는 retryable provider error로 confirm surface에 남기며, 계근 저장 domain mutation은 아직 APP local completion projection으로 닫는다 | `processor/handover/weighing` 저장 + inbound board/detail refresh, real OCR/file provider implementation follows `PROVIDER_ADAPTER_CONTRACT.md`; real OCR smoke is Google Cloud Vision `DOCUMENT_TEXT_DETECTION` + `ko` |
| Processor inbound | list/detail filters, approve, weighing link, treatment result, idempotency/replay mock guard. backend mode는 read/approve/treatment action과 board/detail refresh projection을 검증 | provider-backed weighing/OCR, backend conflict envelope 확장 |
| Lifecycle spine | emitter request projection through dispatcher, driver, processor approve/treatment result까지 backend endpoint smoke로 검증. notification row, chat system_card, chat read-state persistence까지 backend smoke로 검증 | APP realtime transport, push provider |

## Backend Wire Snapshot

발표용 `artifacts/mobile/ux-flow/index.html`은 이 표를 읽기 쉽게 보여주는 review artifact다. 추적 가능한 기준은 이 문서와 `docs/80-artifacts/ux-flow/mobile-index.json`이다.

| Surface | Backend 필요성 | 현재 wire | 상태 | 다음 action |
|---|---|---|---|---|
| Auth / role entry | 실제 APP 진입과 RBAC context 기준 | `/auth/login`, `/session/roles`, `/session/me?role=...` | Covered | multi-company/duties 확장 전까지 유지 |
| Emitter request/history/detail/cancel | lifecycle 시작점과 cancel conflict 기준 | APP create/history/detail/cancel backend-mode | Covered | Olbaro/PDF/file provider는 별도 slice |
| Dispatcher pending board | emitter request가 dispatcher 미배차로 보이는 projection | `/dispatch/board` read backend-mode | Covered | assignment/confirm 후 board refresh 유지 |
| Dispatcher assignment/confirm | 미배차에서 planned dispatch/confirmed dispatch로 전환하는 핵심 mutation | APP backend-mode adapter가 `/dispatch/board/assign` + `/dispatch/board/confirm` 호출. local Spring HTTP proof와 `dispatcher-assignment-backend` Maestro가 배차 완료 surface까지 검증 | Covered | realtime conflict/read-state |
| Driver assignment/proof/OCR/service log | 배차 확정 이후 현장 수행 proof persistence와 차량 정비/주유 기록 | APP backend-mode adapter가 `/driver/assignments/today`, confirm, loading-start, unloading-complete를 호출하고, 계근 OCR은 backend upload session + local fake OCR job + weighing mutation sequence로 연결됐다. 정비/주유 등록은 `driver-dispatch-sheet` Maestro에서 폼 진입/제출을 확인하고 adapter/backend test가 `/driver/vehicle-service` request/response shape와 `fuel_amount_label` persistence를 검증한다. continuous `gps_track`/background location은 MVP 제외이며 사진 첨부, map/phone/provider runtime values는 pending | Covered | foreground event-time location, map/phone, real GCV/AWS smoke, service log photo attachment |
| Processor inbound/weighing/treatment | 반입/계근/처리 결과 운영 정본 | inbound list/detail은 `/processor/inbounds` + `/processor/inbounds/{id}` backend-mode read API와 APP runtime adapter로 연결. item `actionability` projection이 next action/disabled reason을 제공하고, APP CTA는 이 projection과 runtime/provider capability를 조합한다. approve는 `/processor/inbounds/{id}/approve`, treatment result는 `/processor/inbounds/{id}/treatment-result` backend action 후 board/detail refresh로 shared APP action result를 조립한다. `MobileBackendLifecycleSmokeTest`가 dispatcher/driver/processor endpoint spine에서 processor status/actionability projection과 lifecycle event persistence까지 함께 검증한다. weighing/provider/OCR은 pending boundary 유지 | Covered | provider-backed weighing/OCR |
| Cross-role lifecycle backend smoke | 역할별 endpoint가 하나의 handover spine으로 이어지는지 확인 | backend test가 emitter request -> dispatcher assign/confirm -> driver confirm/loading-start/unloading-complete/weighing -> processor approve/treatment-result를 통과하고 notification/chat persistence를 확인한다 | Covered | APP realtime transport |
| Notifications/chat lifecycle cards | 역할 간 상태 변경 전달, unread/read-state | backend `NotificationPublisher`, `ChatRoomService`, `ChatCardPublisher`가 notification row와 chat room/member/system_card를 persistence한다. `/chat/{roomId}/read`는 path room message만 read cursor/receipt로 저장하고 notification chat feed unread를 갱신한다. cross-room messageId 거부도 backend smoke로 검증한다. APP chat 화면은 runtime client를 통해 backend alias를 사용하며, backend-mode Maestro가 전송 surface까지 확인한다 | Covered | realtime/push provider |
| External providers/files/OCR/map/location | domain backend와 별도 provider-ready implementation | provider adapter contract is decided in `docs/20-contracts/PROVIDER_ADAPTER_CONTRACT.md`; file upload local smoke uses floci S3 via `pnpm provider:s3:smoke:floci`; backend upload session local API covers create, local PUT object, complete, abort, auth/validation envelope; OCR provider direction is Google Cloud Vision `DOCUMENT_TEXT_DETECTION` + `ko`; driver APP backend-mode uses upload session + local fake OCR job before weighing mutation; processor APP backend-mode uses upload session + local fake OCR job to create OCR draft before local completion projection; location tracking scope is decided in `docs/20-contracts/LOCATION_TRACKING_CONTRACT.md` | Partial | processor weighing domain mutation, real AWS/GCP smoke, provider별 implementation issue로 분리 |

## Current Notes

- Emitter notification feed excludes `chat` category; chat belongs to the common chat surface.
- Emitter support CTA goes to `external-support.web-handoff.screen` until provider-ready URL/auth handoff is decided.
- Emitter unreported request entry must pass the 신고 기준 확인 동의 gate before step 2.
- Emitter transporter favorite uses add/remove toggle and updates favorite state without a separate popup.
- Emitter vehicle selection allows duplicate picks and creates one detail row per selection.
- Emitter request mock schedule seed uses the next five business days from the 2026-06-18 request date.
- Emitter request backend-mode smoke는 `emitter-request-backend` Maestro flow가 `/login` -> emitter context -> request submit -> completion -> history readback을 검증한다. 최신 실행 증거는 local artifact `artifacts/mobile/maestro/emitter-request-backend/latest.json`에 둔다.
- Emitter cancel backend-mode smoke는 `emitter-cancel-backend` Maestro flow가 `/login` -> emitter context -> request submit -> history detail -> cancel success feedback을 검증한다. backend API test는 같은 endpoint의 assignment 이후 `WORKFLOW_STATE_MISMATCH` conflict를 검증한다.
- Emitter request backend-mode contract candidate lookup은 현재 `/emitter/contracts` shared list를 재사용한다. PC contract list의 `dday`/`dDay` 표기 차이는 APP runtime adapter에서 정규화하되, 장기적으로는 shared contract drift를 backend/web/mobile 중 한 곳에서 정리한다.
- Figma `356:989` 기준 chat room 본문에는 visible read-state summary나 explicit `대화 더 보기` CTA를 두지 않는다. read-state는 list/badge/mock contract에서만 검증한다.
- Chat attachment draft kind는 `image | video | document`를 허용하고, bundled test image/video uploaded attachment는 `expo-image`와 `expo-video` preview selector로 검증한다.
- 운전자 APP은 assignment/proof/notification 대상이지만 APP chat room viewer role이 아니다. current chat viewer role은 `emitter | dispatcher | processor`다.
- Dispatcher dispatch backend-mode adapter는 `EXPO_PUBLIC_API_BASE`가 있을 때 `/dispatch/board/assign` 후 `/dispatch/board/confirm`을 호출하고 board를 refresh한다. #822에서 local backend HTTP proof와 `dispatcher-assignment-backend` Maestro를 확보해 `Dispatcher assignment/confirm`을 Covered로 승격했다. backend-mode Maestro는 `EXPO_PUBLIC_API_BASE=http://10.0.2.2:8080`으로 Metro를 재기동한 상태에서 실행해야 한다.
- Driver schedule backend-mode adapter는 `EXPO_PUBLIC_API_BASE`가 있을 때 `/driver/assignments/today`로 오늘 운반건을 읽고 `/driver/assignments/{handoverId}/confirm`, `/loading-start`, `/unloading-complete`, `/weighing`으로 proof mutation을 반영한다. Backend DTO가 APP screen contract보다 얇기 때문에 route/contact/vehicle/proof/OCR 표시 필드는 provider-pending/default view-model로 보강한다.
- Driver schedule backend-mode proof는 #822에서 local backend HTTP chain과 `driver-proof-backend` Maestro로 확인했다. device flow는 하단 `오늘 일정` 탭을 통해 schedule로 들어가고, completed proof row까지 스크롤해 `하차 완료`, `계근 사진 등록 완료`, `실중량 800kg`을 확인한다.
- Driver proof canonical order는 배차 확인 -> 상차 증빙 -> 하차 증빙 -> 계근 placeholder다. APP은 더 이상 driver 계근을 하차 전 prerequisite으로 다루지 않는다.
- Role picker는 dev/mock entry이므로 `/role/driver` 직접 진입은 driver assignment runtime을 mock으로 고정한다. 실제 backend-mode proof는 `/login?role=driver` 계열 진입으로 APP token을 확보한 흐름에서만 인정한다.
- Processor inbound backend-mode proof는 #824 이후 read slice를 통과했고, approve backend binding 이후에는 `POST /processor/inbounds/{id}/approve`가 성공 시 board/detail refresh로 action result를 조립한다. treatment backend binding 이후에는 `POST /processor/inbounds/{id}/treatment-result`가 처리 실적 저장 후 board/detail refresh로 `report_complete` projection을 조립한다. actionability projection은 `nextAction`/`can*`/`disabledReason`을 shared contract로 노출해 APP이 status만으로 CTA를 추론하지 않게 한다. `MobileBackendLifecycleSmokeTest`는 배출 신청 -> 배차 확정 -> 운전자 증빙/계근 -> 처리자 승인/실적까지 backend endpoint spine을 검증하며, processor board/detail status가 `weighing_pending` -> `inbound_scheduled` -> `report_pending` -> `report_complete`로 이어지는지 확인한다. 같은 smoke는 notification row, request-scoped chat room/member, chat system_card persistence도 확인한다. device flow는 `/login` -> processor context -> 입고 예정 list -> backend item detail -> action conflict surface를 확인한다. weighing/provider/OCR mutation은 다음 slice 전까지 disabled/pending boundary로 유지한다.

## Back Policy

| Policy | Rule |
|---|---|
| `router-boundary` | role home의 Android Back은 Expo Router history를 따른다 |
| `parent-surface` | role 내부 1-depth surface는 role home 또는 직접 부모로 돌아간다 |
| `origin-return` | detail 진입 출처를 보관하고 원래 출처로 돌아간다 |
| `wizard-previous-step` | wizard 입력 화면은 직전 step으로 돌아간다 |
| `completion-landing` | 완료 화면은 입력 step으로 되돌아가지 않고 안정적인 landing으로 이동한다 |
| `child-dismiss` | modal, sheet, search, camera, keyboard를 먼저 닫는다 |

Critical current decisions:

- 운전자 bottom nav는 Figma `314:2804` 기준 `홈/오늘 일정/배차표` 3-tab이다.
- 운전자 알림은 `driver.home.notifications.cta`에서 진입하며 bottom tab이 아니다.
- 운전자는 lifecycle notification 대상이 될 수 있지만 current APP chat viewer role은 아니다.
- chat room에서 emission detail로 갔다면 back target은 chat room이고, history에서 detail로 갔다면 back target은 history다.
- request/dispatch completion screen은 입력 단계가 아니라 home/list 같은 landing surface로 돌아간다.
- camera, OCR confirm, contact sheet, search, keyboard는 underlying screen보다 먼저 dismiss한다.

## Provider Decision Backlog

| Boundary | Current handling |
|---|---|
| Auth/RBAC | onboarding role selection, `/login` runtime credential submit, `/session/roles` context selection, session restore/logout, invalid role fallback을 구현. backend mode는 Spring auth/session endpoint를 사용하고 local smoke는 mock fixture를 유지 |
| Realtime chat/read-state | backend read-state persistence는 `/chat/{roomId}/read`와 notification chat feed projection으로 검증. APP chat runtime client와 화면은 backend mode에서 room list/detail/read/send alias를 사용한다. foreground realtime transport와 OS push는 후속 |
| File upload/OCR | provider-pending/offline/retry mock state와 bundled chat media preview. Adapter shape is fixed by `PROVIDER_ADAPTER_CONTRACT.md`; backend upload session local API and local fake OCR job/result API are covered. Chat attachment composer는 backend mode에서 `chat_attachment` upload session draft를 생성하고, message attachment persistence/linking은 후속이다. Driver APP backend-mode wires localUri -> upload session -> PUT -> complete -> OCR job -> weighing mutation. Processor APP backend-mode wires capture URI -> upload session -> PUT -> complete -> OCR job -> APP OCR draft -> `/processor/handover/weighing` mutation -> `/processor/weigh` board refresh. Real bucket/OCR vendor는 후속 |
| External links/location | phone/map/support는 provider-pending 또는 fallback surface로 수렴. Adapter handoff shape is fixed; runtime values/audit policy are 후속. continuous `gps_track`/background location은 `LOCATION_TRACKING_CONTRACT.md` 기준 별도 product/provider decision 전까지 구현하지 않는다 |
| Backend conflict/idempotency | duplicate/replay/conflict mock guard. 실제 envelope는 API 계약 확정 후 연결 |

## Commands

```bash
pnpm --filter @zerro/mobile e2e:maestro:role-routing
pnpm --filter @zerro/mobile e2e:maestro:login-entry
pnpm --filter @zerro/mobile e2e:maestro:emitter-request
EXPO_PUBLIC_API_BASE=http://10.0.2.2:8080 pnpm --filter @zerro/mobile exec node ./scripts/e2e-maestro-dev-client.mjs e2e/maestro/emitter-request-backend.yaml emitter-request-backend --app-link zerro://login --wait-ms 30000
pnpm --filter @zerro/mobile e2e:maestro:emitter-history
pnpm --filter @zerro/mobile e2e:maestro:emitter-chat
pnpm --filter @zerro/mobile e2e:maestro:dispatcher-dispatch
EXPO_PUBLIC_API_BASE=http://10.0.2.2:8080 pnpm --filter @zerro/mobile exec node ./scripts/e2e-maestro-dev-client.mjs e2e/maestro/dispatcher-pending-backend.yaml dispatcher-pending-backend --app-link zerro://login --wait-ms 30000
EXPO_PUBLIC_API_BASE=http://10.0.2.2:8080 pnpm --filter @zerro/mobile e2e:maestro:dispatcher-assignment-backend
pnpm --filter @zerro/mobile e2e:maestro:driver-dispatch-schedule
EXPO_PUBLIC_API_BASE=http://10.0.2.2:8080 pnpm --filter @zerro/mobile e2e:maestro:driver-proof-backend
EXPO_PUBLIC_API_BASE=http://10.0.2.2:8080 pnpm --filter @zerro/mobile e2e:maestro:processor-inbound-backend
EXPO_PUBLIC_API_BASE=http://10.0.2.2:8080 pnpm --filter @zerro/mobile e2e:maestro:processor-weighing-backend
pnpm --filter @zerro/mobile e2e:maestro:driver-dispatch-sheet
pnpm --filter @zerro/mobile e2e:maestro:processor-weighing
pnpm --filter @zerro/mobile e2e:maestro:processor-inbound
pnpm --filter @zerro/mobile e2e:agent-device:open
```
