# D3RO Voice 모바일 출시 핸드오프 — 2026-08-29 최종 갱신: 2026-08-30 KST. 0~14절은 시간순 증거이며 현재 판정과 재개 순서는 15절을 우선한다. ## 0. 결론 **모바일 앱은 아직 출시 가능 상태가 아니다.** Google Play 등록정보의 텍스트, 아이콘, feature graphic 초안과 Firebase project/Android app/config는 준비됐다. 그러나 Firebase SHA fingerprint와 CI secret materialization, production AAB, upload-key로 서명된 candidate, Play-signed 설치본, 실제 핵심 기능 E2E, 스토어용 앱 스크린샷은 없다. 2026-08-29 실제 `1.1.0` debug 앱을 새 Pixel 7 에뮬레이터에서 열어 보니 다음을 확인했다. - Metro가 없으면 `Unable to load script` 오류 화면이 표시된다. - Metro 연결 후 첫 부팅 중 빈 흰 화면이 잠시 표시됐다. - 이후 실제 onboarding과 로그인 화면은 열렸다. - 인증 세션이 없어 녹음·전사·AI·팀 화면에는 진입하지 못했다. - production/Play-signed 설치본이 아니므로 이번 캡처는 Google Play 제출물이 아니다. 2026-08-29에는 사용자 요청에 따라 출시 실행을 중단했다. 이 핸드오프 문서는 15절의 closeout으로 마무리하되, 제품 출시 자체를 완료 또는 production-ready로 처리하지 않는다. ## 1. 중단 상태 종료 확인일: 2026-08-29 KST | 항목 | 종료 상태 | | ------------------- | --------------------------------------------------------------------------- | | 전용 캡처 AVD | `D3RO_PlayCapture_20260829` 생성됨, `emulator-5556` 종료됨 | | 기존 AVD | `emulator-5554`만 연결 상태로 보존. 기존 `0.0.0-e2e` 데이터는 건드리지 않음 | | Metro | `localhost:8081` listener 없음 | | 로컬 Supabase | `supabase stop --workdir server` 완료, local Docker volume 보존 | | Android 앱 프로세스 | 전용 `emulator-5556` 종료로 함께 중단 | | Play Console | 더 이상 변경하지 않음. 기본 등록정보 페이지는 초안 상태 | | Firebase | 프로젝트 생성 버튼을 누르지 않음. 신규 Firebase 프로젝트 없음 | | Git | 이 핸드오프 문서 외 추가 stage/commit/push 없음 | | Forgejo 배포 | run #46 실패 상태. 반복 push/retry하지 않음 | ## 2. 이번 세션에서 이룬 것 ### 2.1 버전과 릴리즈 메타데이터 - 제품 버전: `1.1.0` - Android versionCode: `1010001` - iOS build number: `1010001` - release date: `2026-08-29` - SSOT: `release/product-version.json` - root, desktop, mobile package version과 Android/iOS 버전 동기화 검증 GREEN - `CHANGELOG.md`의 `1.1.0` 항목 갱신 - Android `ko-KR`/`en-US` release notes 준비: - `apps/mobile-rn/metadata/android/ko-KR/changelogs/1010001.txt` - `apps/mobile-rn/metadata/android/en-US/changelogs/1010001.txt` - `npm run release:metadata` GREEN - `npm run release:metadata:test` GREEN, fail-closed negative 6건 ### 2.2 Play 등록정보 초안 Play 앱: - 개발자 계정: 개인 Play Console 계정 - Play app ID: `4976102237698469110` - package: `com.d3ro.voice` - 기본 언어: `ko-KR` - 상태: draft 저장한 초안: - 앱 이름: `D3RO Voice` - 짧은 설명: 42자 - 자세한 설명: 1,213자 - 유명 음성 기록·전사 앱의 정보 구조를 비교하되 문구를 복제하지 않고, 첫 효용 → 결과 중심 기능 → 요금·광고 → 개인정보 → 책임 고지 순서로 작성 - 파일: `docs/v3/play/01-store-listing.md` 중요 경계: - 초안만 저장했다. - 리뷰, App content, Data safety, internal/closed/production 제출은 하지 않았다. - 휴대전화 스크린샷 영역에는 아무 이미지도 업로드하지 않았다. ### 2.3 아이콘과 feature graphic Play icon: - 파일: `docs/v3/play/assets/d3ro-voice-play-icon-512.png` - 규격: 512×512 PNG - SHA-256: `0FAE53FB40CCE4B9FB0213CBEDB4E087CA00A8F8C438D6731C5C47C4AFD096B1` - Play Console 업로드와 preview 확인 완료 Feature graphic canonical E: - 제출본: `docs/v3/play/assets/feature-graphic/candidate-e-generated-signal-stage.png` - 규격: 1024×500, PNG24, sRGB, alpha 없음 - SHA-256: `A5D82B65B95027ACE184FA06A3BBDA3433995C70F064932B5733C3D3A99644D9` - 생성 원본, prompt, 제출본, hash를 별도로 보존 - Play Console preview에서 crop·thumbnail contrast·규격 경고 없음을 확인 - 이것은 **feature graphic**이며 앱 스크린샷이 아니다. 검증: - `npm run release:play:assets` GREEN - `npm run release:play:assets:test` GREEN - canonical E 변조 포함 fail-closed negative 7건 GREEN ### 2.4 Git과 CI 현재 권위 있는 SHA: ```text 4872d6f6c50a15104792fb31a97176527c3cd9a2 ``` 다음 ref가 모두 위 SHA와 일치한다. - local `main` - `origin/main` - `chanpaca/main` 주요 릴리즈 커밋: ```text 4872d6f docs(release): update 1.1.0 launch metadata 0a8cf43 feat(play): finalize store graphics and copy 563d4cb fix(ci): make mobile Gradle wrapper executable 6a9608c ci: bootstrap Linux release toolchains 30d5127 build(release): forbid unsigned Windows artifacts ec5aec7 fix(release): retire legacy capture publisher 547d4ad security: remove credential automation from releases f342ea8 fix(release): sync legacy mobile version 3e9e04f ci(release): require trusted Windows signing 5205dcd feat(release): prepare 1.1.0 candidate ``` GitLab CI: - 최종 pipeline: `72066` - URL: `https://gitlab.twentyoz.kr:8443/d3r0/voice/-/pipelines/72066` - commit: `4872d6f6` - 결과: SUCCESS, 7/7 - GREEN jobs: lint/typecheck, unit, API, Edge, mobile-quality, build-workspaces, mobile-android - 이전 graphics/copy pipeline `72065`도 SUCCESS, 7/7 주의: CI GREEN은 production AAB, Play 설치, 실제 provider, 스크린샷 검증을 대체하지 않는다. ### 2.5 실제 앱 실행 시도 새 전용 AVD: - 이름: `D3RO_PlayCapture_20260829` - device profile: Pixel 7 - system image: Android 34 Google APIs Play Store x86_64 - serial: 실행 시 `emulator-5556` - 원래 해상도: 1080×2400 - 캡처 시 임시 override: 1080×1920 - 현재 상태: 종료됨 설치한 실제 debug APK: - 파일: `apps/mobile-rn/android/app/build/outputs/apk/debug/app-debug.apk` - package: `com.d3ro.voice` - label: `D3RO Voice` - versionName: `1.1.0` - versionCode: `1010001` - targetSdk: 36 - SHA-256: `7AD0FD0716D8F900053B31D72C53ADC035889D8B507D95602FEC49F5A5C78B87` - signer: Android Debug - debug signer SHA-256: `FAC61745DC0903786FB9EDE62A962B399F7348F0BB6F899B8332667591033B9C` 정상 확인: - 새 AVD 설치 성공 - cold start가 `com.d3ro.voice/.MainActivity`로 실행됨 - Metro 연결 후 React Native `D3ROVoice` root 실행 확인 - 실제 onboarding과 login UI 확인 - Keychain이 빈 새 설치에서 인증 세션 없음 확인 ## 3. 하지 못한 것 ### 3.1 실제 핵심 기능 검증 다음 화면은 실제 앱에서 열지 못했다. - 녹음 ready/recording/paused 상태 - 실제 WAV 생성과 foreground notification - 오디오/MP4 가져오기 - 실제 전사 결과와 history detail - Talk AI 응답과 신고 sheet - 생성 문서/회의록 - 팀/초대/역할 화면 - 개인정보, export/restore, account deletion의 authenticated 경로 - 광고 동의·배너·rewarded SSV - Play Billing purchase/restore 원인: - 새 debug 설치에는 로그인 세션이 없다. - debug 앱은 production Supabase를 사용한다. - 안전한 인증 우회나 기능 화면 deep link가 없다. - auth session은 Android Keychain에 암호화되므로 AsyncStorage/adb seed로 우회할 수 없다. - 사용자의 실제 계정을 캡처에 사용하지 않았다. - 외부 disposable 계정도 새로 만들지 않았다. ### 3.2 production Android release 아직 없는 것: - Firebase project와 Android app - `google-services.json` - production upload keystore path/alias/password CI 설정 - release evidence private key CI 설정 - Firebase project ID/number/mobile SDK app ID CI 설정 - `D3RO_MOBILE_PRODUCTION_RELEASE_ENABLED=true` - Android emulator runner production gate - production signed AAB - `v1.1.0` tag - Play internal track upload - Play-signed install - App Bundle Explorer/signing evidence production AAB는 생성되지 않았다. self-signed, debug, E2E 또는 출처 불명 keystore를 사용하지 않았다. ### 3.3 Google Play 스크린샷 현재 store-ready 휴대전화 스크린샷 수: **0장** - Play Console screenshot slot은 비어 있다. - 최소 2장, 권장 4장 이상이 필요하다. - 실제 핵심 기능 화면을 아직 열지 못했으므로 업로드하지 않았다. ### 3.4 Firebase 생성 예정으로 준비했던 값: - project name: `D3RO Voice Production` - project ID: `d3ro-voice-production` - Android package: `com.d3ro.voice` - Developer Program: off - Gemini: off - Analytics: off 하지만 최종 `프로젝트 만들기` 버튼은 누르지 않았다. Firebase project/API identity는 생성되지 않았다. 내일 action-time 승인 후 다시 시작해야 한다. ### 3.5 공개 사이트 배포 Forgejo push 자체는 완료됐다. - `chanpaca/main`: `4872d6f6c50a15104792fb31a97176527c3cd9a2` - sparse pack 문제를 비활성화해 136MiB 전송을 3.66MiB로 줄인 뒤 push 성공 하지만 배포는 실패했다. - Forgejo deploy-site run: `#46` - 결과: FAILURE - 실패 위치: `Set up job` - 경과: 10분 53초 - checkout/build/Cloudflare/verify: 모두 0초, 시작되지 않음 - 원인: `linux-builder` runner가 job을 claim/initialize하지 못해 timeout 현재 public 상태는 최신 배포가 아니다. - `/release-identity.json`: 404 - root `/`: 404 - live `assetlinks.json`: 과거 인증서 SHA 유지 - live download page: 과거 `v1.0.0 STABLE` / `Signed & Verified` 문구 유지 - privacy/terms/delete-account: 기존 공개 페이지는 접근 가능 새 commit이나 반복 push로 해결할 문제가 아니다. runner 복구 후 run #46만 재실행하고 public exact verification을 해야 한다. ### 3.6 Windows 공개 릴리즈 - 로컬 installer는 Authenticode `NotSigned` - public-trust code-signing certificate/PFX가 없음 - 기존 self-signed `CN=Everything2EverythingDev`는 public release에 사용 금지 - stable `v1.1.0` tag/release와 updater publish는 계속 차단 ## 4. 새로 발견된 것 ### 4.1 설치돼 있던 앱은 production 앱이 아니었다 기존 `emulator-5554`의 `com.d3ro.voice`: - versionName: `0.0.0-e2e.20260829.1` - versionCode: `202608291` - installer: `null` - microphone/notification permission: 미허용 기존 상태를 캡처해 Play 증거로 쓰면 안 된다. 기존 에뮬레이터는 보존했다. ### 4.2 debug APK는 Metro 없이 standalone 실행되지 않는다 `app-debug.apk` 첫 cold start에서 Metro가 없으면 다음 오류가 실제로 표시됐다. ```text Unable to load script. Make sure you're running Metro or that your bundle 'index.android.bundle' is packaged correctly for release. ``` 이는 debug variant의 예상 개발 경계이지만, source/build GREEN만으로 앱을 열었다고 판단하면 안 된다는 실증이다. 최종 Play 캡처는 embedded bundle을 가진 production/Play-signed 설치본이어야 한다. ### 4.3 Metro 연결 직후 빈 흰 화면이 관찰됐다 Metro bundle 로딩 뒤 첫 캡처는 status/navigation bar 외 전부 흰 화면이었다. 수 초 뒤 onboarding이 표시됐다. production build에서 다음을 별도로 검증해야 한다. - cold start splash/boot feedback - blank frame 지속 시간 - provider/auth 초기화 실패 시 fail-closed UI - offline/slow network 시작 경로 - process death 후 recovery ### 4.4 실제 onboarding은 열렸지만 핵심 기능은 인증 뒤에 있다 실제 onboarding과 signup/login 화면은 정상 렌더됐다. 그러나 debug 앱에는 안전한 local/demo 기능 route가 없고 인증 우회도 없다. local disposable Supabase + E2E APK를 사용하면 기능 UI를 열 수 있지만 그 결과는 TEST-DEMO이며 Play-signed production 증거가 아니다. ### 4.5 캡처용 해상도를 별도로 고정해야 한다 Pixel 7 AVD 기본 캡처는 1080×2400이었다. Play draft 요구와 일관된 세로 캡처를 위해 dedicated AVD에 `wm size 1080x1920`을 적용했다. 내일 재개 시 실제 값을 다시 확인하고, Play-signed 설치본에서도 동일 viewport가 제품 UI를 자르거나 왜곡하지 않는지 검증해야 한다. ### 4.6 작업 트리가 매우 dirty하다 중단 시 `git status --short` 집계: - total: 197 - modified: 55 - deleted: 17 - untracked: 125 대부분 기존 build/test/generated/user 작업이다. 전체 stage, 전체 commit, reset, restore, clean을 금지한다. 이번에 새로 생긴 캡처 디렉터리만 별도로 식별됐다. ## 5. 캡처 증거 원장 디렉터리: `docs/v3/play/assets/screenshots/1.1.0-debug-capture-20260829/` | 파일 | 규격 | SHA-256 | 내용 | 판정 | | ------------------------ | --------- | ------------------------------------------------------------------ | ----------------------------------------- | ------------------------------------------------- | | `00-launch.png` | 1080×2400 | `4818E5266471D07A63B0162D60B673354E17DF8BD0F557FE0ADDF9E04EE94711` | Metro 미연결 `Unable to load script` 오류 | **폐기**, 오류 증거만 보존 | | `01-runtime.png` | 1080×1920 | `89947F52FE4CA38A2729D1C706CAB5C0AD87A45405843F4769D0ABDFD4F440CC` | Metro 연결 직후 빈 흰 화면 | **폐기**, boot 문제 증거만 보존 | | `02-after-bootstrap.png` | 1080×1920 | `95AC6B0B2792086DE2D38AE719FBED222A3595158A2C9F37403E31F756FD6FC8` | 실제 `1.1.0` onboarding 1/5 | 실제 앱 화면이지만 **debug이며 store-ready 아님** | 세 파일 모두 Play Console에 업로드하지 않는다. `02-after-bootstrap.png`도 production/Play-signed 재촬영 전에는 참고 증거일 뿐이다. ## 6. 내일 진행해야 할 것 ### P0 — 앱을 먼저 검증한다 이 절은 2026-08-29의 역사적 계획이다. 현재 실행 순서는 15.5절로 대체됐다. 1. 아래 `재개 전 사실 확인` 명령을 실행한다. 2. Firebase project/app 생성에 대한 action-time 승인을 다시 받는다. 3. Firebase Android app `com.d3ro.voice`를 등록한다. 4. Play app-signing SHA-1/SHA-256과 upload key SHA를 Firebase에 정확히 등록한다. 5. `google-services.json`을 내려받아 package/project identity를 검증한다. 6. public-trust와 별개인 Android upload key를 기대 SHA와 대조한다. 7. GitLab protected/masked/hidden CI variables를 구성한다. 8. `mobile-production-release` job으로 production AAB를 만든다. 9. AAB version/package/SDK/merged manifest/dependency/signature/provenance를 검증한다. 10. 사용자 확인 후 Play internal track에 AAB를 업로드한다. 11. Play가 서명한 설치본을 clean install한다. 12. 로그인, 녹음, 전사, AI, 팀, 신고, 광고, Billing, 삭제를 실제로 검증한다. ### P0 — 실제 스토어 스크린샷을 만든다 최종 캡처는 Play-signed 설치본과 dedicated reviewer/capture 계정을 사용한다. 사용자의 실제 이메일, 회의, 음성, 팀, 토큰을 사용하지 않는다. 권장 6장 흐름: 1. **녹음** — 녹음 준비 또는 실제 recording 상태, 명확한 controls와 상태 표시 2. **전사 결과** — 실제 WAV에서 생성된 transcript/history detail 3. **AI 문서화** — 실제 transcript 기반 요약/문서와 책임 있는 사용 경계 4. **Talk + 신고** — 실제 AI 응답과 사용자가 접근 가능한 report affordance 5. **작업/팀** — Works hub 또는 팀 협업의 실제 접근 가능한 구조 6. **데이터 관리** — privacy, export/restore, device/account deletion 경로 각 이미지 acceptance criteria: - 1080×1920 PNG 또는 Console이 수락하는 검증된 실제 device ratio - package `com.d3ro.voice` - versionName `1.1.0`, versionCode `1010001` - installer `com.android.vending` - Play app-signing certificate SHA-256이 기대값과 일치 - DEBUGGABLE 아님 - test AdMob label/fixture/debug menu/error/red screen/blank screen 없음 - 생성형 mock UI 없음 - 실사용자 PII, reviewer credential, token, 실제 회의 내용 없음 - 화면의 모든 기능이 같은 Play 설치본에서 실제 접근 가능 - crop/자막을 쓰더라도 실제 UI를 가리거나 기능을 과장하지 않음 ### P1 — Forgejo 공개 배포 복구 1. `linux-builder` runner service/registration/labels/capacity를 복구한다. 2. 새 commit이나 push를 만들지 않는다. 3. run #46만 재실행한다. 4. public `release-identity.json`의 commit이 `4872d6f6c50a15104792fb31a97176527c3cd9a2`인지 확인한다. 5. version이 `1.1.0`인지 확인한다. 6. root/privacy/terms/delete-account가 HTTPS 200인지 확인한다. 7. download page에서 stale `v1.0.0 STABLE`과 unsigned `Signed & Verified` 문구가 사라졌는지 확인한다. 8. `assetlinks.json`이 Play app-signing SHA만 포함하고 upload SHA를 포함하지 않는지 확인한다. ### P1 — Windows 공개 릴리즈 1. public-trust Windows code-signing certificate를 준비한다. 2. expected signer subject를 CI secret와 gate에 설정한다. 3. installer와 unpacked exe 모두 Authenticode `Valid`를 검증한다. 4. 설치된 앱, registry, updater feed, visible version까지 E2E한다. 5. 그 이후에만 `v1.1.0` tag/release/updater publish를 진행한다. ## 7. 재개 전 사실 확인 저장소 루트 PowerShell: ```powershell git status --short git rev-parse HEAD git rev-parse origin/main git rev-parse chanpaca/main git ls-remote origin refs/heads/main git ls-remote chanpaca refs/heads/main adb devices -l Get-NetTCPConnection -LocalPort 8081 -State Listen -ErrorAction SilentlyContinue docker ps --format '{{.Names}}' | Select-String 'supabase_.*d3ro-voice' npm run release:metadata npm run release:play:assets npm run release:mobile:boundary ``` 기대 기준: - local `HEAD`와 실제 `origin/main`이 일치한다. `chanpaca/main`은 Forgejo runner/public deploy 복구 전까지 이전 public baseline일 수 있으므로 세 SHA가 같다고 가정하지 않는다. - 기존 `emulator-5554` 외 새 캡처 emulator는 자동으로 실행돼 있지 않음 - Metro 8081 listener 없음 - D3RO Voice local Supabase containers 없음. 다른 프로젝트의 local Supabase container는 이 판정에 포함하지 않는다. - release metadata/assets/boundary GREEN ## 8. 캡처 AVD 재개 명령 이 명령은 **debug 진단용**이다. 최종 스토어 캡처는 Play-signed 설치본으로 한다. ```powershell $emulatorExe = "$env:ANDROID_SDK_ROOT\emulator\emulator.exe" Start-Process -FilePath $emulatorExe -ArgumentList @( '-avd', 'D3RO_PlayCapture_20260829', '-port', '5556', '-no-snapshot-save', '-no-boot-anim', '-gpu', 'auto' ) adb -s emulator-5556 wait-for-device adb -s emulator-5556 shell getprop sys.boot_completed adb -s emulator-5556 shell wm size 1080x1920 ``` Debug + Metro가 필요할 때만: ```powershell Set-Location apps/mobile-rn npm run start -- --port 8081 adb -s emulator-5556 reverse tcp:8081 tcp:8081 adb -s emulator-5556 install -r android/app/build/outputs/apk/debug/app-debug.apk adb -s emulator-5556 shell am start -W -n com.d3ro.voice/.MainActivity ``` 실제 화면 캡처 기본형: ```powershell adb -s emulator-5556 shell screencap -p /sdcard/d3ro-capture.png adb -s emulator-5556 pull /sdcard/d3ro-capture.png ``` 중단: ```powershell adb -s emulator-5556 emu kill supabase stop --workdir D:\workspace\D3ROVoice\server ``` ## 9. 인증서와 identity 기준 Play app-signing SHA-256: ```text 01:00:19:21:DB:4F:33:40:85:CE:21:E4:B8:DE:CC:BD: 71:DA:87:67:C5:6E:3B:59:83:2A:A1:C8:29:EA:0D:AB ``` Upload key SHA-256: ```text 4F:AC:69:24:82:1C:50:DA:AB:ED:76:49:32:A5:3C:48: 6F:8C:6C:5F:34:B9:F1:8D:B9:20:AA:40:99:15:2B:54 ``` 두 값은 서로 달라야 한다. Firebase에는 Google/FCM/auth 요구에 맞춰 필요한 Play app-signing 및 upload certificate fingerprint를 명확히 구분해 등록한다. `assetlinks.json`에는 Play가 최종 사용자 APK에 적용하는 app-signing SHA만 둔다. ## 10. 외부 변경과 확인 경계 다음은 action-time 사용자 확인 후에만 한다. - Firebase project 최종 생성 - Firebase Android app 등록과 persistent API identity 생성 - GitLab secret/protected variable 생성·변경 - Play internal/closed/production track AAB 업로드와 저장 - App content, Data safety, 등급, 금융 기능, FGS, AI/UGC 선언 저장 - reviewer credential 입력 - merchant/payment/tax profile 변경 - production review 제출·staged rollout - public tag/release/updater publish - cloud account/project/API key 삭제 ## 11. 금지 사항 - `00-launch.png`, `01-runtime.png`, `02-after-bootstrap.png`를 Play 최종 스크린샷으로 업로드하지 않는다. - feature graphic을 앱 스크린샷이라고 부르지 않는다. - 생성 이미지, mock UI, 로그인/초대 failure evidence를 제품 스크린샷으로 쓰지 않는다. - debug/E2E APK, sideload installer, self-signed certificate를 production 증거로 쓰지 않는다. - 실제 사용자의 이메일, 토큰, 음성, 전사, 회의, 팀을 캡처에 노출하지 않는다. - unknown/nontracked keystore를 비밀번호 추측으로 사용하지 않는다. - public-trust Windows certificate 없이 stable tag/release를 만들지 않는다. - Forgejo runner failure에 새 commit/push를 반복하지 않는다. - dirty worktree 전체를 stage/commit/reset/restore/clean하지 않는다. - legal/Data safety/production 제출을 자동 진행하지 않는다. ## 12. 소스 오브 트루스 - 모바일 완료 상태: `docs/v3/MOBILE_APP_COMPLETION_SSOT.md` - 이전 제품화 핸드오프: `docs/v3/MOBILE_PRODUCTIZATION_HANDOFF_2026-08-21.md` - Play 패키지 안내: `docs/v3/play/README.md` - Play 등록정보: `docs/v3/play/01-store-listing.md` - Data safety 초안: `docs/v3/play/02-data-safety-draft.md` - App content 초안: `docs/v3/play/03-app-content-draft.md` - 출시 체크리스트: `docs/v3/play/04-release-checklist.md` - 신고 운영: `docs/v3/play/05-content-reporting-operations.md` - version SSOT: `release/product-version.json` - Android release identity: `release/android-release-identity.json` - CI production job: `.gitlab-ci.yml`의 `mobile-production-release` - Play asset verifier: `scripts/ci/verify-play-store-assets.mjs` - release metadata verifier: `scripts/ci/verify-release-metadata.mjs` - mobile boundary verifier: `scripts/ci/verify-mobile-release-boundary.mjs` ## 13. 내일 첫 체크포인트 이 절은 2026-08-29의 역사적 체크포인트다. 현재 실행 순서는 15.5절을 따른다. 내일은 Play Console부터 만지지 않는다. 다음 순서로 시작한다. 1. Git/프로세스/AVD/local stack 상태 재확인 2. 이 문서의 `새로 발견된 것`을 재현 가능한 issue로 분리 3. production-like cold start에서 red screen/blank screen이 없는지 먼저 검증 4. Firebase/signing/AAB gate를 닫음 5. Play-signed install에서 핵심 기능 E2E 6. 그 뒤 실제 스토어 스크린샷 촬영과 Play draft 업로드 이 순서를 바꾸지 않는다. ## 14. 2026-08-30 재개 감사 이 절은 2026-08-29의 중단 스냅샷을 지우지 않고, 다음 날 다시 확인한 현재 상태와 새 작업을 추가한다. 이 절의 사실이 앞 절의 역사적 상태와 충돌하면 이 절을 우선한다. ### 14.1 현재 상태 재검증 - local `main`, `HEAD`, local `origin/main`, local `chanpaca/main`, 실제 `origin/main`은 계속 `4872d6f6c50a15104792fb31a97176527c3cd9a2`다. - 실제 `chanpaca/main` 조회와 Forgejo public/LAN API, run #46 API는 timeout이라 현재 원격 상태를 확정하지 못했다. remote-tracking ref만으로 최신 원격 상태를 주장하지 않는다. - 작업 트리는 198개 변경이며 staged는 0개다. 2026-08-29의 197개에 이 핸드오프 문서 자체가 untracked로 추가된 수치다. 전체 stage/reset/restore/clean 금지는 그대로다. - Android emulator와 emulator/qemu 프로세스는 0개다. 전용 `D3RO_PlayCapture_20260829` AVD와 userdata는 디스크에 남아 있다. `emulator-5554`는 현재 연결돼 있지 않으므로 앞 절의 "연결 상태로 보존"은 2026-08-29 종료 시점의 역사적 사실로만 본다. - Fold6 무선 ADB endpoint가 두 줄로 보이지만 같은 물리기기다. 이를 두 대의 기기 증거로 세지 않는다. - Metro 8081 listener와 D3ROVoice Supabase container는 없다. DB/storage/ edge-runtime Docker volume은 남아 있지만 이번 감사에서 내용 무결성까지 다시 검증한 것은 아니다. - Firebase CLI와 GCP에서 exact `d3ro-voice-production` project는 없거나 현재 계정에서 접근 불가임을 확인했다. Firebase project 생성은 여전히 action-time 승인 대상이다. - 현재 인앱 Play Console 세션은 `TWENTYOZ` 조직 계정이며 6개 앱 목록에 `com.d3ro.voice`와 Play app ID `4976102237698469110`이 없다. 이 계정에서는 변경하지 않았다. Chrome 세션도 올바른 개인 개발자 계정으로 연결돼 있지 않다. 다음 단계 전에 해당 앱을 보유한 개인 Play 계정 로그인이 필요하다. - GitLab browser 세션은 로그인돼 있지 않고 저장된 Git credential로 variables API 조회도 401이었다. 따라서 production CI variable 존재 여부를 추측하지 않는다. ### 14.2 production-like cold start 진단 기존 `app-e2e.apk`를 전용 `emulator-5556`에 clean install해 Metro와 local Supabase 없이 cold start했다. - artifact: `apps/mobile-rn/android/app/build/outputs/apk/e2e/app-e2e.apk` - version: `0.0.0-e2e.20260829.1` / `202608291` - SHA-256: `0D20ABB8C87B088C308E2379A91448F051C13B3778C1D79962C5D004B0F5BABE` - `DEBUGGABLE=false`, `assets/index.android.bundle` 포함 - signer: Android Debug - 판정: embedded-bundle 시작 진단용 TEST-DEMO이며 production/Play 증거가 아님 진단 원본은 `scratch/mobile-release-coldstart-20260830/`에 둔다. | 프레임 | SHA-256 | 관찰 | | ------------------- | ------------------------------------------------------------------ | ------------------------------------------ | | `frame-00330ms.png` | `0D718DB0947DD7CDD598C13DA36EF06440A12ED418B9ACABE28806832678C2AC` | Android launch splash와 실제 launcher icon | | `frame-01828ms.png` | `163BC50C7AA0EB8227E7803719454256B5255359A5CB2FD6E2326878B26D23CA` | 앱 bootstrap spinner | | `frame-02526ms.png` | `3A9C6F58B2CD0FBA33D39074D659FEB6A4B2807FF8E6CD9BD8EE7DD0CBE2B624` | onboarding 1/5 표시 | | `frame-08003ms.png` | `DE53F4EF82F6D1714E9D13CA2B02104FE3F4C71015C3F689ED4DE3CDFF924BAA` | 같은 onboarding이 안정적으로 유지됨 | Android log의 `Displayed com.d3ro.voice/.MainActivity`는 `+1s606ms`였다. fatal exception, red screen, `Unable to load script`, 흰 화면 고착은 재현되지 않았다. Firebase 미구성 경고와 새 설치의 Keychain entry 없음 로그는 남았으며 production Firebase 설치본에서 다시 확인해야 한다. 진단 뒤 `emulator-5556`을 다시 종료했고 Metro/Supabase는 시작하지 않았다. 따라서 2026-08-29의 빈 흰 화면은 현재까지 debug+Metro bootstrap에서 본 증거다. embedded bundle 경로의 영구 결함으로 일반화하지 않는다. production Play-signed 설치본의 cold start 검증은 여전히 RED다. ### 14.3 새로 확인한 release artifact 경계 - 저장소 어디에도 production AAB가 없다. - 저장소/CI 작업 디렉터리에 materialize된 `google-services.json`, `android/app/release.keystore`, release evidence private key, release snapshot/publication은 없다. - 저장소 밖 upload PKCS12와 Ed25519 evidence private key는 실제로 남아 있다. `verify-windows-android-upload-key.ps1`은 alias `d3ro-upload-20260821`, certificate SHA-256 `4F:AC:69:24:82:1C:50:DA:AB:ED:76:49:32:A5:3C:48:6F:8C:6C:5F:34:B9:F1:8D:B9:20:AA:40:99:15:2B:54`, private-key readback과 Credential Manager target을 다시 GREEN으로 확인했다. evidence keypair도 keyId `2797d3e63affd2348941bc2871b57e520c958f7f5da1a4bbe8aa46904a890b7f`와 sign/verify roundtrip이 GREEN이다. 없는 것은 CI secret materialization, offline 복구 백업, 실제 AAB signer/evidence다. - `npm run release:mobile:config`는 실제 production config가 없으므로 `google_services_file_unreadable`로 fail-closed한다. - 기존 `android/app/build/outputs/apk/release/app-release.apk`는 2026-08-20의 오래된 `1.0/1`이다. SHA-256은 `8530D5ABEE72DB00E332B1362D5B3568CFBD080405D15DC27125F66E18A354B3`, signer SHA-256은 `06EEC757...2E481`이며 현재 verifier가 차단한다. 필수 Whisper model도 없어 `missing_entry_assets/models/ggml-tiny.bin`으로 실패한다. 절대 재사용하지 않는다. - local Node는 `.nvmrc`의 `24.19.0`이 아니라 `23.4.0`이다. production artifact는 이 dirty local checkout에서 만들지 않고 고정된 CI image/toolchain에서 만든다. ### 14.4 AAB/tag 교착의 소스 수정 기존 GitLab job은 exact stable tag가 있어야만 production AAB를 만들 수 있었지만, 이 문서는 Windows public-trust signing 전 stable tag를 금지했다. 이 순서는 실행 불가능했다. 다음 두 파일을 좁게 수정했다. - `.gitlab-ci.yml` - `scripts/ci/verify-mobile-release-boundary.mjs` 새 계약: - protected exact `main`에서는 `mobile-production-release`를 수동 실행해 Maintainer 전용 candidate AAB/evidence만 만든다. - branch candidate evidence ref는 `refs/heads/main`이다. - exact stable semver tag 경로는 계속 지원하며 tag evidence ref를 쓴다. - `publish-release`는 계속 tag-only다. main candidate가 public release를 트리거하지 않는다. mobile boundary self-test, release metadata self-test, YAML parser 2종, job shell syntax, `git diff --check`는 GREEN이다. 두 코드 파일은 staged/committed/pushed되지 않았고 이 핸드오프 문서도 untracked다. 사용자 확인 후 이 세 파일만 좁게 commit/push해야 실제 GitLab candidate job을 사용할 수 있다. ### 14.5 Play-signed 캡처 기기 교착 현재 production release 계약은 `arm64-v8a`만 허용하지만 전용 Pixel 7 AVD는 `x86_64`다. 따라서 현 AAB의 Play-signed APK를 이 AVD에 설치할 수 있다고 가정하면 안 된다. Google Play의 현재 공식 요구는 휴대전화 screenshot의 최소 변 320px, 최대 변 3840px, 긴 변이 짧은 변의 2배 이하이며, 추천 노출은 1080×1920 이상의 9:16 세로 이미지 4장 이상이다. 실제 앱 경험을 보여야 한다. - 규격 근거: `https://support.google.com/googleplay/android-developer/answer/9866151` - Play-signed universal/device APK 근거: `https://support.google.com/googleplay/android-developer/answer/9844279` 최종 선택지는 다음 중 하나를 production AAB 전에 확정한다. 1. arm64 Fold6에 Play internal 설치 후 실제 UI를 왜곡하지 않는 9:16 store canvas로 구성하고 Console 수락을 검증한다. 2. release AAB에 검증된 `x86_64` 지원을 추가하고 verifier/CI/실기기 matrix를 함께 확장한 뒤 전용 AVD에서 Play 설치를 검증한다. 단순 crop/stretch, debug/E2E 재촬영, installer/certificate 검증 없는 sideload는 대안이 아니다. ### 14.6 현재 재개 순서와 승인 요청 이 승인 전 순서는 15.5절로 대체됐다. 1. 위 두 CI 파일과 이 핸드오프 문서의 좁은 commit/push를 승인받는다. 2. Firebase project `d3ro-voice-production` 최종 생성과 Android app `com.d3ro.voice` 등록을 action-time 승인받는다. 3. 올바른 개인 Play Console 계정과 GitLab Maintainer 세션에 로그인한다. 4. Firebase fingerprints/config를 검증하고 GitLab protected/masked/hidden variables를 구성한다. 5. protected exact `main` candidate job과 emulator prerequisite를 실행한다. 6. AAB/evidence를 독립 검증한 뒤 별도 승인으로 Play internal track에 올린다. 7. Play-signed arm64 설치본으로 핵심 E2E를 통과한다. 8. 실제 UI 기반 6장만 제작·검증·업로드한다. 9. `linux-builder`와 Forgejo 접근 경로를 복구한 뒤 run #46만 재실행한다. Firebase 생성, GitLab variable 변경, Play 업로드, legal declaration, tag/public release는 이번 감사에서 실행하지 않았다. ## 15. 2026-08-30 승인 후 실행과 production 판정 이 절은 14절 작성 뒤 사용자가 명시적으로 승인한 작업과 병렬 production 감사를 기록한다. 0~14절의 역사적 또는 승인 전 스냅샷과 충돌하면 이 절을 우선한다. ### 15.1 승인 후 실제로 실행한 것 - 다음 세 파일만 stage해 `origin/main`에 push했다. commit은 `1f71cf6faeb209c85b49a0260312897a25cc41d9`, 작성자는 `Yun Chan `, 메시지는 `ci: allow protected mobile candidate`다. - `.gitlab-ci.yml` - `scripts/ci/verify-mobile-release-boundary.mjs` - `docs/v3/MOBILE_RELEASE_HANDOFF_2026-08-29.md` - 후속 audit 결과는 이 핸드오프 문서만 별도 closeout commit으로 반영했다. 최신 commit은 문서 안에 self-reference로 고정하지 않고 `git rev-parse HEAD`와 `git ls-remote origin refs/heads/main`의 일치로 확인한다. unrelated dirty worktree는 stage/reset/clean하지 않았다. - `origin` push는 성공했지만 최신 commit의 GitLab pipeline ID/job 결과는 로그인 전이라 확인하지 않았다. 과거 pipeline `72066`과 local verifier GREEN을 최신 CI GREEN으로 재사용하지 않는다. - Firebase production project를 생성했다. - project ID: `d3ro-voice-production` - project number: `1013351450402` - display name: `D3RO Voice Production` - Android app을 등록했다. - package: `com.d3ro.voice` - app ID: `1:1013351450402:android:4d9d0f675b12ee09bf477b` - display name: `D3RO Voice Android` - `apps/mobile-rn/android/app/google-services.json`을 내려받아 project/package/app identity를 검증했다. 실제 API key는 출력하지 않았고 파일은 gitignored다. - production Firebase/AdMob 기대값으로 `npm run release:mobile:config`를 실행해 project/package/client/API-key uniqueness와 production ad unit 형식 검사를 GREEN으로 확인했다. - Firebase Android app의 SHA fingerprint 목록은 아직 0개다. local upload certificate의 SHA-1은 `AE:2C:60:EB:9E:E3:06:FF:4D:36:D9:17:5C:F1:C6:67:CA:4A:8A:F8`, SHA-256은 `4F:AC:69:24:82:1C:50:DA:AB:ED:76:49:32:A5:3C:48:6F:8C:6C:5F:34:B9:F1:8D:B9:20:AA:40:99:15:2B:54`다. Play app-signing SHA와 upload SHA를 구분해 등록해야 하므로 임의 등록하지 않았다. - 인앱 브라우저에 Play Console account selector, GitLab 로그인, Firebase Android settings 탭을 visible 상태로 열어 뒀다. Play 개인 계정 선택과 GitLab 로그인은 아직 필요하다. - Play AAB 업로드, CI secret 변경, policy/legal declaration, stable tag/public release, Forgejo push/deploy는 실행하지 않았다. ### 15.2 병렬 production 감사 결론 최종 판정은 **NO-GO / production level 아님**이다. Android 소스는 단순 prototype이 아니라 후기 beta 또는 RC 후보에 가깝지만, 실제 배포·보안·운영 경계가 닫히지 않았다. | 영역 | 판정 | 현재 증거 | | ---------------------------------- | ------- | ------------------------------------------------------------------------- | | 정적 품질 | GREEN | ESLint 0, typecheck GREEN, Jest 59 suites / 369 tests GREEN | | Android 기본 release config | PARTIAL | API 36, release cleartext/backup 차단, Firebase/AdMob/signing fail-closed | | production artifact / Play install | RED | production AAB 0개, Play-signed Fold6 설치 증거 없음 | | 인증·계정·로컬 데이터 경계 | RED | 아래 HIGH 결함 존재 | | OAuth·FCM·Billing·AdMob 실제 E2E | RED | production provider/Play 설치본 증거 없음 | | production backend | RED | migration 4개 및 `content-report` 배포 누락 | | 운영 관측성 | RED | Crashlytics/Sentry/Bugsnag, JS ErrorBoundary, native exception hook 없음 | | 접근성·복원력·Fold matrix | RED | TalkBack/200%/offline/5xx/token expiry/process death/권한 철회 등 미완료 | | Play 제출 자산·접근 | RED | 유효 screenshot 0장, 계정에 따라 12명/14일 closed test 필요 | | iOS | RED | native release config, build/device/provider 증거 없음 | 병렬 실행 부하에서 admin Jest 2건이 5초 timeout을 냈지만 isolated와 전체 순차 재실행은 GREEN이었다. 기능 실패로 단정하지 않되 CI 환경의 flake/부하 민감성은 별도 정리한다. ### 15.3 새로 발견한 출시 차단급 보안·데이터 경계 1. **인증 callback 탈취/로그인 CSRF 경계** - Android auth callback은 검증되지 않은 `d3ro-voice://auth-callback` custom scheme이다. - `apps/mobile-rn/src/lib/auth-redirect.ts`는 pending state/PKCE 상관관계 없이 fragment의 access/refresh token pair를 `setSession`에 전달한다. - code-only PKCE, claimed HTTPS App Link, pending state 검증으로 닫기 전에는 production 로그인 경계를 GREEN으로 보지 않는다. 2. **계정 삭제의 잘못된 최근 재인증 판정** - `server/supabase/functions/account-delete/index.ts`는 access JWT의 `iat`가 10분 이내면 최근 재인증으로 본다. - auto-refresh된 access JWT도 새 `iat`를 가질 수 있으므로 명시적인 사용자 재인증 증거가 아니다. 3. **비원자적 계정 삭제** - 같은 함수가 storage object를 먼저 영구 삭제하고 auth user를 나중에 삭제한다. 후반 실패 시 데이터만 사라지고 계정은 남을 수 있다. - durable deleting state와 idempotent saga, 각 단계 failure injection이 필요하다. 4. **개인정보처리방침과 실제 처리 불일치** - live source 정책은 local transcription을 선택하면 기기 안에서 처리하고 민감 local cache를 암호화한다고 설명한다. - 실제 import/recording 경로는 audio를 Supabase에 먼저 업로드하고 cloud STT 실패 뒤 local fallback하며, 일부 durable/import/export/action history와 upload checkpoint는 평문 file 또는 AsyncStorage JSON이다. - 진짜 local-only 선택과 암호화 저장을 구현하거나 정책·Data safety를 실제 흐름과 정확히 맞춰야 한다. 5. **로그아웃 뒤 계정 간 local artifact 격리 누락** - 중앙 purge가 pending native incoming media와 resumable upload checkpoint를 제거하지 않는다. 다음 로그인 사용자가 이전 사용자 파일 또는 checkpoint를 이어받을 가능성을 닫아야 한다. - push detach가 실패하면 현재 `performAccountLogout`은 local sign-out/purge에도 도달하지 않는다. local logout을 먼저 보장하고 detach tombstone을 재시도하는 구조가 필요하다. 추가로 최대 25 MB audio를 JS `Uint8Array`, base64, multipart에서 여러 번 복제해 저메모리 기기 OOM/ANR 위험이 있다. streaming/native upload 또는 더 낮은 제한과 low-RAM 실기기 profiling이 필요하다. ### 15.4 release·backend·운영 차단 요인 - production AAB는 여전히 없다. 기존 `app-release.apk`는 1.0/1, 폐기 signer, Whisper model 누락이므로 재사용 금지다. - Fold6 설치본은 `0.0.0-dev.20260822.1`, `DEBUGGABLE`, installer `null`인 개발 sideload다. - production Supabase는 `20260821000028`까지만 적용됐다. 다음 네 migration은 local에만 있다. - `20260824000029_content_reporting.sql` - `20260824000030_profile_managed_field_acl.sql` - `20260825000031_audit_log_actor_unlink.sql` - `20260829000032_meeting_document_content_reporting.sql` - production Edge Function 목록에 `content-report`가 없다. AI response reporting과 moderation 경로를 production 기능으로 주장하지 않는다. - live `https://d3ro.chanpaca.net/.well-known/assetlinks.json`은 폐기 signer `06:EE:C7:57:...:E4:81`을 반환한다. 기록된 Play app-signing SHA-256 `01:00:19:21:...:0D:AB`와 맞지 않는다. - 실제 Google consent→mobile callback, FCM foreground/background/terminated receipt, Play Billing purchase/pending/cancel/restore/refund, production AdMob SSV reward의 최신 실증이 없다. - `npm --prefix apps/mobile-rn audit --omit=dev`는 critical 1, high 8, moderate 11, low 1을 반환했다. critical/high 대부분은 RN CLI/Metro/ESLint/Jest build-tool chain이며 현재 E2E Android bundle에 runtime dependency로 포함된 취약점이라고 과장하지 않는다. 그래도 CI 공급망 관점에서 upgrade 또는 비영향 근거를 남겨야 한다. - 기존 E2E APK와 stale release APK의 16 KB ZIP alignment는 `zipalign -c -P 16 4`를 통과했다. production AAB의 모든 ELF와 실제 16 KB device/Play 검증을 대체하지 않는다. - release `minifyEnabled`는 현재 false다. 즉시 제출 차단으로 단정하지 않지만, 2027년 Play DEX optimization/shrinking/obfuscation 요구 대응 항목으로 둔다. - 최종 Play screenshot은 0장이다. 기존 3장은 Metro 오류, 빈 화면, onboarding 참고 캡처라 제출하지 않는다. ### 15.5 다음 실행 순서 이 절이 6절, 13절, 14.6절의 이전 순서를 대체한다. 1. HIGH 인증·계정 삭제·계정 간 local data 경계를 먼저 수정하고 적대적/failure injection 테스트를 추가한다. 2. 개인정보처리방침·Data safety와 실제 audio/cache 흐름을 하나의 계약으로 정렬한다. 3. production Supabase migration 4개와 `content-report` 배포를 별도 승인 아래 수행하고 실제 reporting/purge 운영 경로를 검증한다. 4. 올바른 Play 개인 계정과 GitLab Maintainer로 로그인해 Firebase Play/upload fingerprint, OAuth/FCM, protected secret를 구분해 구성한다. 5. 고정 CI에서 production AAB/evidence를 만들고 signer/config/ABI/model/16 KB/ provenance를 독립 검증한다. 6. Play internal upload 뒤 Play-signed Fold6 설치본으로 정상·실패·복구·접근성 matrix와 OAuth/FCM/Billing/AdMob E2E를 통과한다. 7. pre-launch report와 Vitals/Crash·ANR 관측 경계를 확인한다. 8. 그 뒤 실제 사용자 UI만으로 최종 screenshot 6장을 촬영한다. 9. 적용 대상 개인 개발자 계정이면 12명 연속 14일 closed test와 production access 승인을 완료한다. 위 항목 전에는 공개 출시, production-ready 선언, stable tag/public release, debug/E2E 캡처의 store 제출을 금지한다. ### 15.6 핸드오프 최종 종료 상태 - Android emulator/qemu process: `0` - ADB emulator serial: 없음. Fold6 wireless ADB endpoint 두 줄은 같은 물리기기다. - Metro `localhost:8081` listener: `0` - D3RO Voice local Supabase container: `0` - 보존된 local volume: - `supabase_db_d3ro-voice` - `supabase_storage_d3ro-voice` - `supabase_edge_runtime_d3ro-voice` - 별도 stop-state archive: `D:\workspace\_backups\D3ROVoice\local-supabase-stop-20260830\` - archive manifest: `D:\workspace\_backups\D3ROVoice\local-supabase-stop-20260830\MANIFEST.md` - 세 archive는 source volume을 read-only mount해 만들었고 SHA-256을 기록한 뒤 전체 `tar -tzf` listing exit `0`으로 검증했다. - 캡처 3개의 실파일·화면 내용·SHA-256은 다시 대조해 원장과 일치했다. 다만 `docs/v3/play/assets/screenshots/`는 여전히 untracked이므로 remote clone에 보존된 증거라고 주장하지 않는다. owner가 별도 보존 방식을 결정할 때까지 local evidence로만 취급한다. - 전용 `emulator-5556`, Metro, local Supabase를 다시 시작하지 않았다. - Play 제출, CI secret 변경, legal declaration, stable tag/public release, Forgejo 재배포는 없다. ### 15.7 정확한 재개 명령 먼저 저장소 루트 PowerShell에서 read-only/current-state 검사를 실행한다. ```powershell git status --short git rev-parse HEAD git ls-remote origin refs/heads/main adb devices -l Get-NetTCPConnection -LocalPort 8081 -State Listen -ErrorAction SilentlyContinue docker ps --format '{{.Names}}' | Select-String 'supabase_.*d3ro-voice' firebase projects:list firebase apps:list ANDROID --project d3ro-voice-production firebase apps:android:sha:list 1:1013351450402:android:4d9d0f675b12ee09bf477b ` --project d3ro-voice-production supabase migration list --linked --workdir server supabase functions list --workdir server supabase db push --linked --workdir server --dry-run ``` 보안 경계 수정 뒤 최소 local regression gate: ```powershell npm run security:secrets:test npm run security:secrets npm run release:mobile:boundary:test npm run release:mobile:boundary npm --prefix apps/mobile-rn run lint npm --prefix apps/mobile-rn run typecheck npm --prefix apps/mobile-rn run test:ci npm --prefix apps/mobile-rn audit --omit=dev ``` 다음 production backend 변경 명령은 migration/function diff와 대상 project를 다시 확인하고 **action-time 승인 후에만** 실행한다. ```powershell supabase db push --linked --workdir server supabase functions deploy content-report --workdir server ``` GitLab에서는 올바른 Maintainer 계정으로 로그인한 뒤 protected exact `main`의 manual `mobile-production-release` job만 실행한다. secret file을 local shell에 복사하지 않는다. job의 전체 명령과 artifact 목록은 `.gitlab-ci.yml`의 같은 job을 소스 오브 트루스로 사용한다. Maintainer artifact를 별도 검증 디렉터리에 내려받은 뒤: ```powershell $identity = Get-Content release/android-release-identity.json | ConvertFrom-Json $product = Get-Content release/product-version.json | ConvertFrom-Json node scripts/ci/verify-android-artifact.mjs ` --mode release ` --apk \app-release.apk ` --aab \app-release.aab ` --bundletool ` --expected-admob-app-id $identity.adMobAppId ` --expected-upload-cert-sha256 $identity.uploadCertificateSha256 ` --expected-version-name $product.version ` --expected-version-code $product.androidVersionCode Get-FileHash \app-release.apk -Algorithm SHA256 Get-FileHash \app-release.aab -Algorithm SHA256 ``` Play internal upload은 별도 승인 뒤 수행한다. Play가 서명한 Fold6 설치본에서 installer/version/debuggable/signing identity를 먼저 증명한다. ```powershell adb -s shell pm list packages -i com.d3ro.voice adb -s shell dumpsys package com.d3ro.voice ``` 그 뒤에만 15.5절의 provider·failure·접근성 matrix와 6장 캡처를 진행한다.