feat: add mobile shell and unified search
Some checks failed
ci / 정본·보안·린트·타입·테스트 (push) Failing after 50s
ci / 워크스페이스 빌드 검증 (push) Has been skipped
ci / Supabase Edge Functions + Cloudflare Worker (push) Successful in 20s
ci / .NET API 서버 테스트 (push) Successful in 12s
deploy-site / deploy (push) Successful in 39s
ci / 모바일 린트·타입·Jest (push) Failing after 3h10m31s

This commit is contained in:
Yun Chan 2026-09-28 23:23:36 +09:00
parent f505a03d34
commit 5ca3709b32
98 changed files with 5168 additions and 2093 deletions

View file

@ -9,6 +9,7 @@
> 제품화 재개 핸드오프: [`MOBILE_PRODUCTIZATION_HANDOFF_2026-08-21.md`](./MOBILE_PRODUCTIZATION_HANDOFF_2026-08-21.md)
> 이 문서는 이전 `docs/v3/00-mobile-master-plan.md`의 미래 로드맵을 대체하는 구현·검증 정본이다.
> 2026-09-26: `.github/workflows`는 삭제되었고 CI는 `.forgejo/workflows`가 정본이다 — 아래 날짜별 기록에 남은 `.github/workflows` 언급은 해당 시점의 스냅샷이다.
> 2026-09-28 Wave 4/5 로컬 증거: [`../evidence/wave45-mobile-shell-search-2026-09-28.md`](../evidence/wave45-mobile-shell-search-2026-09-28.md). 이 E2E APK와 런타임 보고서는 production release 증거가 아니다.
## 0. 2026-09-16 출시 게이트 스냅샷
@ -94,14 +95,18 @@
| 묶음 | 현재 판정 | 확인된 증거 / 남은 게이트 |
|---|---|---|
| Auth·계정 | 구현·자동화 GREEN, 외부 OAuth·privacy purge 기기 E2E 대기 | auth/signup/invite 경계는 최신 exact APK `.11`에서 PASS. Keychain 세션, 중앙 `purgeAllAccountLocalData`, cold no-session·SIGNED_OUT·refresh session loss·A→B 전환·실패 재시도 경계는 전체 모바일 57 suites/353 tests에 포함돼 GREEN. 실제 token-expiry 강제 로그아웃·logout/delete/provider 연결/해제 기기 E2E와 Google/GitHub/Apple 동의→callback은 남음 |
| 온보딩·테마 | 구현·기기 journey GREEN | 신규/기존 분기, skip/replay, 계정별 preference merge, system/light/dark, reduced motion, 알림·마이크 권한 상태 구현. production preference `0→skip→1`, Android PID 유지, 5.8초 내 Main, navigation warning 0을 실측 |
| 온보딩 | 기존 구현·기기 journey GREEN | 신규/기존 분기, skip/replay, 계정별 preference merge, 알림·마이크 권한 상태 구현. production preference `0→skip→1`, Android PID 유지, 5.8초 내 Main, navigation warning 0을 실측. 이 과거 증거를 2026-09-28 v4 셸의 전 테마·viewport 증거로 재사용하지 않는다 |
| v4 셸·테마·접근성 | 셸 `[x]`, 디자인 시스템·접근성 `[~]` | visible Home/Records/Meetings/More 4탭, hidden Record route, 세 동작 FAB, Home/More, 새/전환된 v4 흐름의 `ScreenHeader`, ui-native dark/light 의미 토큰과 48dp control을 구현했다. 전체 Jest 86 suites/589 tests, 320dp dark 1/1 PASS(88초), 411dp light 1/1 PASS(100초). 기존 stack 화면 전부의 헤더 전환을 뜻하지 않으며 실제 TalkBack/font-scale/rotation은 남음 |
| 녹음·전사·Talk | 핵심 경로 GREEN | API 34 microphone FGS, 지속 알림, pause/resume/discard, 실제 통화 audio-focus, 프로세스 kill 후 WAV 복구, 파일 import, cloud STT fail-closed, on-device Whisper, Claude SSE, Android TTS를 통합 APK에서 확인. STT provider 호출 전 원자 quota reservation·실패 환불은 20-way 동시성으로 검증. Android `ACTION_SEND` 수신은 허용 MIME·크기·provider 예외·MP4 AAC 추출을 fail-closed로 구현했고 API 34 instrumentation 4/4(CSPRNG 1 + share 3)와 별도 UID 외부 ContentProvider APK→`ACTION_SEND`→Record→durable queue→cancel cleanup E2E가 GREEN |
| 데이터 portability | 구현·공유 E2E GREEN | 00021 atomic export/restore, 손상/타 사용자/충돌 rollback, legacy import를 local integration으로 확인. `portable_exports`→FileProvider `shared_exports` 경계와 PDF Print/DOCX chooser·30초 cleanup을 실제 기기에서 확인 |
| History·회의 | 실제 데이터·회의 생성 계약 GREEN | Supabase history pagination/search/favorite/CAS, Web↔mobile history E2E, meetings/memos/documents RLS/CAS, meeting recording lifecycle 구현. 00025의 제목·참석자·4개 언어·소유 템플릿 원자 생성은 2계정 43 assertions와 competing-payload PT409로 검증. speaker/audio timestamp와 최종 교차 앱 E2E는 남음 |
| 기록+회의 통합 검색 | `[~]` source/full Jest + 320dp dark·411dp light runtime GREEN | 사용자 범위 history title/original/polished/summary와 meetings title/raw/edited transcript를 병렬 검색해 최신순으로 합치고 각 상세로 이동한다. 두 viewport/theme에서 두 source→detail을 실측했다. 다만 별도 `transcripts` 구간은 검색하지 않고, 모바일 raw-text 과전송·실행 요청 미취소, FTS/index·대규모 성능 미검증이 남음 |
| Teams·Push | 팀 GREEN, 외부 Push 대기 | production 17 assertions와 local 36 assertions로 원자 팀/초대/RLS/role/device token 계약 확인. durable push outbox 00015~00020과 logout device `1→0` 확인. 실제 FCM 수신은 Firebase/FCM 자격 증명·cron 배포가 필요 |
| Billing·Ads | test 광고와 production AdMob identity/SSV 준비, store·실제 지급 E2E RED | Google Play restore는 실제 store list→모든 token 서버 검증→entitlement snapshot 일치 후에만 성공. Google 공식 test adaptive banner와 rewarded `AdActivity` 완주를 실측했다. production app `ca-app-pub-1039714767792854~6427959892`, banner `/9840591290`, rewarded `/2255790918`, `50 cloud_ai_tokens`, SSV URL, Edge v13, durable receipt 원장은 SSOT에 반영했다. 그러나 live는 `검토 필요`, `광고 게재 제한`, store 미연결, 결제 프로필 미완료다 |
| Admin | 서버·Web·모바일 E2E와 production DB·Edge 배포 GREEN | .NET 21/21, 00023 관리 integration 25 assertions, Edge/JWT 47 assertions와 TLS Chromium E2E GREEN. 모바일 API 34에서 ordinary/manager/admin/super_admin 및 stale 강등을 55 assertions로 실측: Free test 광고, Pro 광고 미요청, 관리자 메뉴/API 차단, 구독 수정, 역할 경계·변경, 삭제 확인·취소, stale JWT 403와 민감 state purge가 모두 PASS. fixture 잔존 0. production 00023 및 admin Edge 4개 배포 후 익명 GET은 모두 `401 authentication_required` + `Cache-Control: no-store`로 재검증 |
| 품질·배포 | exact-APK 에뮬레이터 gate GREEN, production 배포 RED | mobile ESLint 0, typecheck GREEN, 전체 Jest 57 suites/353 tests GREEN은 2026-08-24 실행 증거다. universal non-debuggable TEST-DEMO `0.0.0-e2e.20260821.11`/`202608221`을 API 34에 fresh install해 cold launch·auth/signup/invite를 검증했지만 production artifact가 아니다. 2026-08-29 release identity `1.1.0`/`1010001`, local upload key, Ed25519 evidence keypair, AdMob identity는 확정했지만 production Firebase·CI secret 주입·AAB·Play track·Fold6 Play 설치 증거는 없다 |
| 품질·배포 | current local source/runtime gate GREEN, production 배포 RED | 2026-09-28 현재 tree 전체 Jest 86 suites/589 tests GREEN. 로컬 E2E APK는 `com.d3ro.voice`, `1.9.0-e2e.20260928.2`/`202609282`, 147063583 B, SHA-256 `96EBA2A47E54033EB0DEFCE4E4D282F1D06F162A5F7DF9457E99609D98745D58`이며 두 canonical Maestro 보고서가 GREEN이다. 이것은 production-signed artifact가 아니므로 Firebase·CI secret 주입·AAB·Play track·Fold6 Play 설치 게이트는 그대로 RED다 |
**v4 제거 범위:** `DashScreen`과 `WorksHubScreen`을 삭제했다. Home과 1급 탭/More가 기존 목적지를 보존하며 사용자 콘텐츠는 삭제하지 않았다. WorksHub 전용 로컬 도구 검색·고정은 복잡도 축소를 위해 의도적으로 종료했고, 비활성 `works-hub-pins.v1` AsyncStorage 키는 업그레이드 설치에 남을 수 있다.
## 3. 플랫폼 동등성 원칙
@ -164,11 +169,11 @@
- [ ] O-005 첫 녹음→전사→공유를 안내하는 단계별 튜토리얼
- [ ] O-006 주요 탭과 파일 가져오기·동기화·결제 관리 튜토리얼
- [ ] O-007 튜토리얼 다시 보기·건너뛰기·완료 상태 동기화
- [ ] O-008 system/light/dark 테마와 D3RO 토큰 기반 렌더링
- [~] O-008 system/light/dark 테마와 D3RO 토큰 기반 렌더링 — ui-native v4 dark/light palette parity·provider/component Jest, 320dp dark·411dp light runtime GREEN. 새/전환된 v4 흐름 밖의 앱 전체 테마 적용 감사까지 완료했다는 뜻은 아님
- [ ] O-009 테마·언어·햅틱·자동 교정·STT 옵션 저장 및 기기간 동기화
- [ ] O-010 글자 확대, TalkBack label/role, focus order, 48dp 터치 영역, 색 대비 검증
- [~] O-010 글자 확대, TalkBack label/role, focus order, 48dp 터치 영역, 색 대비 검증 — touched controls/ScreenHeader 48dp와 theme contrast source tests GREEN, 실제 TalkBack·focus order·font scale·rotation은 대기
- [ ] O-011 reduced motion과 애니메이션 비활성 경로
- [ ] O-012 한국어/영어 필수 전체 번역과 하드코딩 문자열 제거
- [~] O-012 한국어/영어 필수 전체 번역과 하드코딩 문자열 제거 — v4 셸·FAB·통합 검색과 touched enum label은 ko/en typed keys 사용, 앱 전체 감사 대기
### D — 데이터 가져오기·동기화·오프라인
@ -229,10 +234,11 @@
- [ ] H-013 회의 목록 search/filter/sort/delete/share
- [ ] H-014 화자 구분 상태·화자명 편집·미지원 fallback
- [ ] H-015 Web↔모바일 동일 회의 교차 편집 E2E
- [~] H-016 홈 기록+회의 통합 검색 — 사용자 범위 history/meetings 병렬 조회, 입력 정규화·최신순 merge·record/meeting 상세 이동 service/row Jest와 320dp dark·411dp light runtime GREEN. 별도 transcript segment 미검색, 모바일 raw-text 과전송·실행 요청 미취소, FTS/index·대규모 성능 미검증 때문에 부분 완료
### F — 데스크톱/Web 전체 기능의 모바일 화면
- [ ] F-001 Dashboard 통계·사용량·처리 job·구독·동기화 상태를 실제 데이터로 표시
- [~] F-001 Home 통계·사용량·처리 job·구독·동기화 상태를 실제 데이터로 표시 — 실제 dashboard stats + 최근 기록 5/회의 3 + Free plan 진입 구현, 320dp dark·411dp light runtime GREEN. 처리 job·동기화 상태 전체 범위는 대기
- [x] F-002 Dictionary 목록/검색/추가/편집/삭제/import/export/sync — CRUD/sync GREEN, export는 `data-portability` CSV/TXT, import는 CSV/JSON/TXT
- [ ] F-003 Custom instructions 목록/활성화/CRUD/reorder/sync
- [ ] F-004 Voice commands 목록/활성화/CRUD와 모바일 실행 가능한 action 구분
@ -316,8 +322,8 @@
- [x] Q-017 Web seeded 데이터→모바일 조회/편집→Web 확인 sync E2E
- [x] Q-018 Free banner/rewarded test ad와 Pro 광고 미요청 E2E — official Google test ad 실측, ordinary Free 광고 노출과 manager/admin Pro 광고 비노출·미요청 API 34 PASS
- [ ] Q-019 Play Billing test purchase/restore/manage E2E
- [ ] Q-020 모든 탭·설정·팀·관리·실패 복구 journey E2E
- [ ] Q-021 회전·폰트 확대·TalkBack·dark/light·작은/큰 화면 visual gate
- [ ] Q-020 모든 탭·설정·팀·관리·실패 복구 journey E2E — v4 4탭/FAB/More/통합 검색의 320dp dark·411dp light 부분 journey만 GREEN; 전체 범위를 대신하지 않음
- [~] Q-021 회전·폰트 확대·TalkBack·dark/light·작은/큰 화면 visual gate — 이 v4 journey의 320dp dark·411dp light는 GREEN; 회전·폰트 확대·TalkBack 실기기 증거는 대기
- [ ] Q-022 airplane mode·process kill·token expiry·server 5xx·중복 tap 회복 E2E
- [~] Q-023 앱 로그 crash/ANR/React error/민감정보 0건 확인 — exact `.9` auth/signup/invite/small-screen 및 admin role journeys에서 fatal/ANR/React fatal 0, production provider journey 민감정보 로그 감사 대기
- [x] Q-024 APK package/version/signature/SHA-256와 설치된 앱 버전 증거
@ -361,10 +367,14 @@
| Historical baseline | `git status --short --branch` | 2026-08-21 당시 `main`이 remote보다 2 commit 앞서고 dirty 변경이 다수였다. 현재 release SHA로 재사용하지 않는 역사 증거다 | 본 문서 1절 | 2026-08-21 |
| Mobile lint | `npm --prefix apps/mobile-rn run lint` | GREEN, warning/error 0 (`--max-warnings=0`) | `apps/mobile-rn/.eslintrc.js`, `package.json` | 2026-08-21 |
| Mobile typecheck | `npm --prefix apps/mobile-rn run typecheck` | GREEN | `apps/mobile-rn/tsconfig.json` | 2026-08-21 |
| Mobile unit | `npm --prefix apps/mobile-rn test` | 전체 통합 tree 57 suites / 353 tests GREEN; 중앙 account-local privacy purge, cold/session-loss/A→B auth 경계, secure logout, identity safety, mobile admin, rewarded/banner retry·template/memo·meeting creation·recording language·generation idempotency 포함 | `apps/mobile-rn/__tests__` | 2026-08-24 |
| Historical mobile unit | `npm --prefix apps/mobile-rn test` | 당시 전체 통합 tree 57 suites / 353 tests GREEN; 현재 tree 수치가 아님 | `apps/mobile-rn/__tests__` | 2026-08-24 |
| Current mobile unit | `npm --prefix apps/mobile-rn test` | 전체 tree **86 suites / 589 tests PASS**. navigation shell/header/theme와 unified-search service/result-row 계약을 포함 | `apps/mobile-rn/__tests__`, `docs/evidence/wave45-mobile-shell-search-2026-09-28.md` | 2026-09-28 |
| Wave 4/5 local runtime APK | output metadata + file size/SHA-256 + installed/runtime identity 확인 | `com.d3ro.voice`, versionName `1.9.0-e2e.20260928.2`, versionCode `202609282`, 147063583 B, SHA-256 `96EBA2A47E54033EB0DEFCE4E4D282F1D06F162A5F7DF9457E99609D98745D58`. 로컬 E2E artifact이며 production-signed APK/AAB가 아니다 | `apps/mobile-rn/android/app/build/outputs/apk/e2e/{app-e2e.apk,output-metadata.json}`, `docs/evidence/wave45-mobile-shell-search-2026-09-28.md` | 2026-09-28 |
| Mobile v4 320dp dark runtime | `apps/mobile-rn/.maestro/navigation-shell-search-local.yaml` | **1/1 PASS, 0 failure, 88초**: visible 4 tabs, FAB open/back close, Home unified search, record+meeting results→both details, Records/Meetings/More navigation | `scratch/design-audit/wave45/navigation-shell-search-320-dark-verified.xml`, `docs/evidence/wave45-mobile-shell-search-2026-09-28.md` | 2026-09-28 |
| Mobile v4 411dp light runtime | same journey on 411dp light | **1/1 PASS, 0 failure, 100초**. 두 보고서는 shell/viewport/theme runtime proof이며 TalkBack/font-scale/rotation 또는 production release proof가 아니다 | `scratch/design-audit/wave45/navigation-shell-search-411-light-verified.xml`, `docs/evidence/wave45-mobile-shell-search-2026-09-28.md` | 2026-09-28 |
| Supabase local | migration-up + shadow replay 00001~00028 및 domain integration | 기존 domain integration에 더해 AdMob receipt 원장의 unknown/ineligible/cooldown replay, account-delete unlink, ACL, 교차 사용자·동시 요청을 검증했다. mobile-platform 85/85, reward race 2회 연속, SSV Deno 6/6, db lint error 0, shadow diff empty, fixture cleanup 0 | `server/supabase/migrations/20260821000028_ad_reward_receipt_replay_protection.sql`, `server/supabase/tests`, `server/supabase/functions/_shared/admob-ssv.test.ts` | 2026-08-24 |
| Android build | `:app:assembleDebug -PreactNativeArchitectures=arm64-v8a,x86_64` | universal BUILD SUCCESSFUL; 두 ABI의 screens/gesture/Nitro/Whisper SO 완전성 확인 후 x86_64 clean cold launch GREEN, SHA-256 `E4647A4C041174DF2B2F094EF7BB61B4B01DD10A379DB32CCBE4105231E04A7C` | `apps/mobile-rn/android/app/build/outputs/apk/debug/app-debug.apk`, `scratch/demo/d3ro-mobile-dual-abi-installed.png` | 2026-08-21 |
| Latest Android E2E test artifact | JDK 17 `:app:assembleE2e -PreactNativeArchitectures=arm64-v8a,x86_64` + artifact/build-config verifiers | BUILD/verifier GREEN; `com.d3ro.voice`, version `0.0.0-e2e.20260821.11`/`202608221`, non-debuggable, embedded bundle 3,808,068 B·Whisper model 77,691,713 B/SHA 검증, Google test AdMob ID, arm64+x86_64. APK 146,739,935 B, SHA-256 `74035B69DB0A564846DF5ED4B85CA5D0E2289D5BB7D73DEE02C35DFD57380C2B`. API 34 exact-APK fresh install와 cold launch GREEN. production artifact가 아니다 | `apps/mobile-rn/android/app/build/outputs/apk/e2e/app-e2e.apk`, `scripts/ci/verify-android-artifact.mjs`, `scripts/ci/verify-mobile-build-config.mjs` | 2026-08-21 |
| Historical Android E2E test artifact | JDK 17 `:app:assembleE2e -PreactNativeArchitectures=arm64-v8a,x86_64` + artifact/build-config verifiers | 당시 BUILD/verifier GREEN; `com.d3ro.voice`, version `0.0.0-e2e.20260821.11`/`202608221`, APK 146,739,935 B, SHA-256 `74035B69DB0A564846DF5ED4B85CA5D0E2289D5BB7D73DEE02C35DFD57380C2B`. 위 2026-09-28 artifact로 대체됐지만 당시 auth/cold-launch 증거의 identity로 보존한다. production artifact가 아니다 | historical installed/runtime evidence, `scripts/ci/verify-android-artifact.mjs`, `scripts/ci/verify-mobile-build-config.mjs` | 2026-08-21 |
| Public Forgejo distribution | 최신 E2E TEST-DEMO 고유 asset upload + anonymous redownload hash 대조 | PENDING; `.11` SHA-256 `74035B69…380C2B` 공개 업로드·anonymous redownload 검증은 수행하지 않음 | Forgejo release asset | 2026-08-21 |
| Mobile release boundary | `npm run release:mobile:boundary:test` + source-contract/security/syntax/YAML checks | GREEN; release mode·expected version/package/cert/prod AdMob·Ed25519 signed evidence와 artifact hash 일치, immutable snapshot, hardlink/reparse/path-swap/static APK 우회·overwrite·test AdMob/debug signer/nonrelease 거부를 검증. 실제 production APK/AAB에는 아직 실행하지 않음 | `scripts/ci/mobile-release-evidence-lib.mjs`, `scripts/ci/verify-mobile-release-boundary.mjs`, `.github/workflows/release.yml` | 2026-08-21 |
| Production Android release gate | release/config/artifact/provenance self-tests + Play/Firebase/AdMob live 재확인 | release identity `1.2.0`/`1020001`, Play app ID `4976102237698469110`, package `com.d3ro.voice`, app-signing SHA-256, local upload/evidence key, AdMob identity를 확인했다. Play는 `임시`, 설정 0/11, AAB 0건, closed tester 0/12명, production access disabled다. Firebase project·CI secret 주입·AAB가 없으므로 현재 판정은 RED다 | `release/product-version.json`, `release/android-release-identity.json`, `scripts/ci/verify-mobile-release-config.mjs`, `scripts/ci/verify-android-artifact.mjs`, live consoles | 2026-08-29 |