d3ro-voice/docs/v3/MOBILE_RELEASE_HANDOFF_2026-08-29.md
2026-08-30 09:50:07 +09:00

47 KiB
Raw Permalink Blame History

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.md1.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:

4872d6f6c50a15104792fb31a97176527c3cd9a2

다음 ref가 모두 위 SHA와 일치한다.

  • local main
  • origin/main
  • chanpaca/main

주요 릴리즈 커밋:

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-5554com.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가 없으면 다음 오류가 실제로 표시됐다.

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:

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 설치본으로 한다.

$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가 필요할 때만:

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

실제 화면 캡처 기본형:

adb -s emulator-5556 shell screencap -p /sdcard/d3ro-capture.png
adb -s emulator-5556 pull /sdcard/d3ro-capture.png <VERIFIED_OUTPUT_PATH>

중단:

adb -s emulator-5556 emu kill
supabase stop --workdir D:\workspace\D3ROVoice\server

9. 인증서와 identity 기준

Play app-signing SHA-256:

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:

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.ymlmobile-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는 .nvmrc24.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 <yunchan@twentyoz.kr>, 메시지는 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 HEADgit 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 검사를 실행한다.

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:

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 승인 후에만 실행한다.

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를 별도 검증 디렉터리에 내려받은 뒤:

$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 <VERIFIED_ARTIFACT_DIR>\app-release.apk `
  --aab <VERIFIED_ARTIFACT_DIR>\app-release.aab `
  --bundletool <VERIFIED_BUNDLETOOL_JAR> `
  --expected-admob-app-id $identity.adMobAppId `
  --expected-upload-cert-sha256 $identity.uploadCertificateSha256 `
  --expected-version-name $product.version `
  --expected-version-code $product.androidVersionCode
Get-FileHash <VERIFIED_ARTIFACT_DIR>\app-release.apk -Algorithm SHA256
Get-FileHash <VERIFIED_ARTIFACT_DIR>\app-release.aab -Algorithm SHA256

Play internal upload은 별도 승인 뒤 수행한다. Play가 서명한 Fold6 설치본에서 installer/version/debuggable/signing identity를 먼저 증명한다.

adb -s <FOLD6_SERIAL> shell pm list packages -i com.d3ro.voice
adb -s <FOLD6_SERIAL> shell dumpsys package com.d3ro.voice

그 뒤에만 15.5절의 provider·failure·접근성 matrix와 6장 캡처를 진행한다.