Repository navigation
[P1][R02] 일반 PR에 Android·iOS 빌드와 실제 native·앱 smoke CI를 연결하고 누락 검사를 실패로 판정 #617
Description
[P1][R02] 일반 PR에 Android·iOS 빌드와 실제 native·앱 smoke CI를 연결하고 누락 검사를 실패로 판정
문제와 결과 목표
일반 PR에서 Dart 분석·테스트가 통과하더라도 Swift/Kotlin, native plugin 등록, 기기용 iOS 구성, OS 서비스 연결이 깨질 수 있다. Android release APK의 manifest 검사는 이미 추가됐지만 iOS 빌드와 실제 native test 실행, 두 모바일 플랫폼의 실제 앱 smoke는 현재 PR workflow에 연결돼 있지 않다.
R02는 동일 PR 검증 소스에서 Android·iOS 빌드, 실제 native tests, 결정적인 앱 smoke를 실행하고, 실행하지 않은 필수 검사나 사라진 결과를 성공으로 표시하지 않는 CI를 만든다. T01/T02의 테스트 entrypoint와 증거 schema를 배선하며 별도 테스트 제품이나 중복 suite를 만들지 않는다.
- 우선순위: P1, 감사 R02. 전담 grill agent:
/root/grill_r02. - 적용 skill:
/Users/ejunpark/.agents/skills/grill-with-docs/SKILL.md. - 고정 조사 source:
d73c7863a01343004ccadcd72064b563c464dd0d. 병행 C01 WIP와 이후 HEAD의 실행 결과는 아래 고정 소스 증거가 아니다. - 실제 root 문답 3개로 결정했다. 이 문서 작성은 읽기·공식 문서 조사와 이 파일 작성만 수행했으며 workflow·제품·테스트·GitHub 설정을 변경하거나 Flutter/native test를 실행하지 않았다.
- 감사 baseline 정정: “일반 PR에는 Android build가 없다”는 현재 사실이 아니다. A01 이후 Android Notification Contract가 PR에서 release APK를 빌드하고 packaged/merged manifest를 검증한다. “iOS RunnerTests가 placeholder”도 현재 사실이 아니다. A02 malformed export 입력 계약 테스트가 있으며 다만 실제 실행을 PR에서 강제하지 않는다.
고정 코드와 기존 증거
| 고정 근거 | 현재 사실·남은 간격 |
|---|---|
| Flutter Testing | main/release//hotfix/ PR·push 및 dispatch, Ubuntu, Flutter 3.44.4, native SQLite/sodium 설치, generator, generated/local-only policy, analyzer, full unit/widget 및 coverage 80%가 있다. native 모바일 실행과 iOS build는 없다. |
| Android Notification Contract | Java17, Flutter3.44.4, 실제 release APK, source negative fixtures, packaged/merged manifest 검사, APK digest 및 30일 artifact가 있다. contents:read, concurrency, 35분 timeout도 이미 있다. 일회용 검증 서명이며 device_delivery_verified: false다. |
| 저장된 A06 CI 검증 | source e6c1438e의 Flutter 929/88.79% 및 Android APK 성공이 기록돼 있다. 실제 빌드 merge 0896d86…와 source head가 다름을 명시한다. 이 결과는 d73c7863의 새 native CI 실행 결과가 아니다. |
| Android build.gradle | compileSdk36, minSdk23, Java17, Flutter의 targetSdkVersion, release signing 필수 검증. PR용 disposable key를 재사용하고 production 키를 요구하지 않는다. |
| Android settings / Gradle wrapper | AGP8.6.0/Kotlin2.2.0/Gradle8.9를 프로젝트에서 선언한다. runner의 전역 Gradle/Java 최신값으로 바꾸지 않는다. 고정 tree에는 android/app/src/androidTest 및 src/test의 native suite가 없다. |
| RunnerTests / Runner scheme | malformed export 인자에 단일 실패 응답을 검사하는 XCTest와 TestAction의 RunnerTests 항목이 존재한다. typecheck나 build-for-testing은 test 실행 결과가 아니다. |
| iOS project / Podfile | deployment target15.0, Swift5, Runner/RunnerTests와 CocoaPods가 있다. 기존 team ID가 있다고 signing credential이 CI에서 가용하다고 가정하지 않는다. |
| AppDelegate | canImport(AlarmKit)와 iOS26 availability로 native 경로를 분기한다. SDK가 너무 오래돼 해당 코드를 빼고 컴파일한 결과를 AlarmKit 경로 컴파일 증거로 삼지 않는다. |
| pubspec / 고정 tree | integration_test 의존성과 integration_test/ 실제 앱 entrypoint가 없다. 추가는 T01이 소유한다. 생성 Dart는 ignored local artifact 정책을 유지한다. |
| Android internal deploy | 수동 main/staging 배포가 production signing·Play credential을 사용한다. 일반 PR 검사에 이를 호출하지 않는다. 동일 품질/배포 결합은 R04, track 경쟁/환경 보호는 R05다. |
Android·iOS만 지원하는 ADR0016, release network boundary의 ADR0024, CONTEXT의 Local Profile·Offline Cold Start·Schedule Notification 계약을 보존한다. runner/job/check는 구현 용어이므로 glossary/ADR를 새로 만들지 않는다.
확정한 CI 실행 범위
| 검사 | 실행·판정 | 책임·한계 |
|---|---|---|
| 기존 Flutter quality | clean checkout에서 lockfile 기반 설치, generator, generated/local-only policy, analyze, full tests, coverage80 | 기존 Flutter Testing 재사용. threshold 완화·generated commit·npm test 대체 금지. |
| Android release validation | 기존 release APK + packaged/merged manifest + digest | Android Notification Contract 재사용. 임시 키는 store 키가 아니며 OS 수신 증거가 아님. |
| iOS release device-target build | iOS26 SDK를 지원하는 명시 Xcode로 unsigned Release 기기 아키텍처 구성 전체 빌드 | simulator build만으로 대체하지 않음. unsigned app은 배포 가능한 signed IPA나 물리 설치 검증이 아님. |
| iOS native tests | 새 소유 simulator에서 기존 RunnerTests 및 T02 확장 XCTest 실제 실행, xcresult와 예상 case 확인 | build-for-testing/typecheck 성공, test0개, placeholder는 통과가 아님. |
| Android native tests | T02의 instrumented/native entrypoint를 소유 emulator에서 실행, JUnit·예상 case·assertion 확인 | 컴파일 task만 실행하거나 native suite가 없는 상태의 no-op Gradle 성공은 통과가 아님. |
| Android·iOS core app smoke | T01 entrypoint를 실제 main/startup/DI/native 저장소·UI 경로에 연결, 각 플랫폼 결정적 core 시나리오 실행 | native UI는 T01/T02 driver 사용. callback 주입 또는 widget-only 테스트로 강등하지 않음. |
| mobile aggregate | 위 새 mobile workflow의 모든 필수 job/result를 검사하는 안정적인 check | 기존 별도 Flutter/Android workflow의 성공까지 추정하지 않음. R01이 별도 check들을 함께 요구. |
T01/T02가 아직 제공하지 않은 suite/driver는 dependency pending이다. R02는 이미 실행 가능한 빌드와 XCTest부터 구현할 수 있지만 필수 Android native 또는 양 플랫폼 core smoke를 비운 채 완료하지 않는다. 구현 단계마다 실제 배선된 행·미배선 행을 기록하며 no-op 성공 check로 자리를 채우지 않는다.
유한 runtime 행렬과 toolchain
- Flutter 3.44.4, Dart 3.12.2, Java17 및 저장소가 잠근 Gradle/Kotlin/plugin/Pods를 사용한다. 지원 하한 Android23/iOS15 또는 제품 local-only 정책을 CI 편의를 위해 바꾸지 않는다.
- PR은 Android/iOS 각각 대표 runtime을 명시한 유한 행렬이다. Android는 실제 target/SDK와 설치 가능한 대표 image를 확인하고 API·ABI·image revision을 기록한다. iOS는 명시 Xcode 경로·SDK·runtime·device type을 검증한다.
latest, 연결된 첫 기기, 암묵적인 기본 simulator에 의존하지 않는다. - 조사 시 공식 macOS26 runner 이미지 목록에는 Xcode26.5/26.6, iOS26.5 SDK/runtime가 나온다. 이는 후보 선정 근거이며 이 저장소의 runner 할당·실제 부팅·호환 성공 증거는 아니다. 구현 시 제공 목록과 원격 runner의
xcodebuild -version, SDK 목록,simctlruntime 목록을 다시 확인하고 정확 버전을 고정한다. image가 바뀌어 필수 도구가 사라지면 조용히 다른 버전으로 fallback하지 않고 환경 실패를 남긴다. - T02의 Android23/26/31~32/33+/target, iOS15 호환 build/pre26 fallback runtime/26+ AlarmKit runtime 및 물리 RC 행은 별도 명시 확장 실행으로 연결한다. 최신 iOS에서 fallback이 보였다고 pre26 호환성을 통과시키지 않는다. R02 대표 PR 행이 T02 전체 물리 행렬을 면제하지 않는다.
- runner가 만든 새 disposable AVD/simulator ID·run ID만 설치·permission 변경·종료·삭제 대상으로 삼는다. 기존 QA simulator
C8BA159D-A97A-458B-A5FC-B04A8B6F2848, 연결된 사용자 기기와 다른 작업 데이터는 보존한다.
Trigger·권한·필수 검사 계약
pull_request의 main/release//hotfix/ 대상과 해당 branchpush,workflow_dispatch를 지원한다.merge_group: checks_requested도 같은 검사 경로를 지원하되 merge queue의 활성화/보호 설정은 R01에서 다룬다.- 모든 일반 PR에서 고유하고 안정적인 check 이름을 유지한다. 문서-only 변경이나 draft를 이유로 필수 workflow/job 전체를 skip하지 않는다. required check에 영향을 주는 rename은 R01 목록과 같이 검토한다. GitHub는 workflow filter로 빠진 required check가 pending을 유지할 수 있다고 설명하므로 필수 workflow에 path filter를 두지 않는다. 공식 workflow syntax
- 최소
contents: read, checkoutpersist-credentials: false. production signing, Play/App Store, OIDC, 보호 environment secret을 PR에 제공하지 않는다. Android 일회용 key/검증용 고정 password는 production credential이 아니며 실행 종료 시 제거한다. iOS는 unsigned/device build와 simulator test로 검사한다. - fork PR도 같은 테스트 코드 경로를 사용한다. GitHub의 fork 승인 대기는 미실행이며 pass가 아니다. untrusted head를
pull_request_target또는 높은 권한의workflow_run으로 우회 실행하지 않는다. 이 구분은 GitHub의 pull_request_target 보안 문서에 근거한다. - aggregate는
always()조건으로 실제 필수 job의 결과를 검사한다.success외에cancelled,skipped, 누락/0 tests, 필수 report 부재, dependency pending을 통과로 취급하지 않는다. 테스트 runner가 반환한 0 exit code 외에 예상 suite/case ID와 결과 원본을 확인한다. 전체 run 취소 등으로 aggregate 자체가 실행되지 못했으면 그 run 역시 성공 증거가 없다. - mobile aggregate는 자신의
needs와 artifact를 검증한다. 별도 workflow의 Flutter quality와 Android Notification Contract는 R01 필수 check 목록에 각각 포함한다. cross-workflow 결과를 실제 조회·검증하지 않은 aggregate가 전체 repository quality를 보장한다고 말하지 않는다. merge queue 검사 트리거 필요성은 GitHub required checks 문서를 따른다.
시간·동시 실행·실패 보존
- concurrency key는 workflow + PR 또는 검증 ref를 포함한다. 오래된 PR/merge 검증 실행만 새 실행으로 취소한다. 수동/main/release 검증은 취소하지 않는 것을 기본으로 명시하고 배포 job과 같은 그룹에 묶지 않는다. group이 다른 PR이나 다른 workflow까지 잘못 취소하지 않는지 fixture로 검증한다.
- matrix
fail-fast: false로 반대 플랫폼의 진단을 보존한다. 초깃값 목표는 기존 Android release35분, Android native45분, iOS build/native60분, 각 smoke45분이다. 전체 job과 boot/test의 watchdog을 모두 유한하게 두며 실제 실행 시간으로 조정 근거를 남긴다. 같은 build를 job마다 무조건 반복하지 않고 같은 소스·mode·toolchain으로 재사용 가능한 산출물만 공유한다. continue-on-error, 성공할 때까지 자동 재시도, 실패 case 삭제로 green을 만들지 않는다. 수동 재실행은 원 실패 run/attempt와 후속 결과를 모두 연결한다.- 실패 진단 업로드와 소유 target cleanup은 가능한 범위에서 always 수행한다. runner 강제 종료가 모든 cleanup hook 성공을 보장한다고 주장하지 않는다. 테스트 watchdog이 앱의 아직 반환하지 않은 OS 작업 owner를 임의 해제하는 근거가 되지 않는다.
- PR 작업 취소/시간 제한은 R02의 새 검사 기본값에 포함한다. Play package/track 직렬화, staging 환경 보호, 배포 작업의 중단 의미는 R05로 남긴다.
Artifact·재현성·동일 소스 판정
각 job은 기계 판독 manifest와 native/Flutter 원본 결과를 연결한다. 최소 필드는 아래와 같다.
| 범주 | 필수 내용 |
|---|---|
| 소스 | PR head SHA, base SHA, 실제 checkout/merge SHA; push·merge_group이면 해당 실제 검증 SHA. git rev-parse HEAD와 event를 같이 기록. |
| 실행 | workflow/run ID·run attempt·job/check 이름, event, 시작/종료, command, exit/result, runner image/OS/architecture. |
| 도구·입력 | Flutter/Dart, Java/Gradle/AGP/Kotlin, Xcode/SDK/runtime, lockfile hashes, native driver version, fixture/run ID. |
| 앱·대상 | build mode/version/build number, 실제 app ID, target ID/OS/ABI, signing 종류, 실제 빌드·설치 artifact digest. |
| 검사 | 예상 case IDs와 실행/pass/fail/blocked/not_run/skipped 수, 독립 assertion 위치, injection/driver 경계, 결과 원본·log·screenshot hash. |
| 한계 | compile-only/native execution/emulator/simulator/physical 구분, unsigned/disposable signing, offline/network/device delivery 미검증 행. |
- PR head와 기본 checkout merge SHA를 하나의
commit필드로 뭉개지 않는다. Android 기존 identity를 확장할 때도 이전 기록의 의미를 보존한다. 서로 다른 checkout의 build/test 결과를 같은 검증 세트로 결합하지 않는다. - Android debug/instrumented APK, Android release validation APK, iOS simulator app/test bundle, unsigned device Release app는 서로 다른 산출물이다. 설치한 binary의 digest를 기록하고 다른 release binary에 그 결과를 붙이지 않는다. app bundle directory는 재현 가능한 파일 manifest/hash 방법을 명시한다.
- 합성 fixture의 JUnit/xcresult·테스트 manifest·필요 로그·실패 화면을 성공/실패 모두30일 보존한다. binary는 재현에 필요한 검증용 산출물만 선택적으로 저장한다. production 키·암호·사용자 DB·실사용 설치 데이터·임시 signing key는 artifact와 cache에서 제외한다.
- 필수 result/identity가 없거나 필요한 업로드가 실패하면 테스트 assertion PASS와 별도로 CI evidence 실패이며 aggregate는 통과하지 않는다. 부팅 전에 실패해 report가 없는 경우는 명확한 환경 실패 진단을 남기되 성공으로 채우지 않는다.
- cache key는 OS/architecture/toolchain/lockfile을 구별한다. dependency cache만 재사용하며 생성 Dart·제품 build output·key/data를 신뢰 가능한 새 결과 대신 복원하지 않는다. 실행용 동일 run artifact 공유는 명시 provenance/digest 검증으로만 허용한다. cold cache와 warm cache를 각각 확인해 누락된 생성 단계나 로컬 잔여물이 성공을 숨기지 않는지 검사한다.
- CI의 dependency 다운로드는 도구 실행 환경이다. 제품에 network client/권한을 추가하거나 remote telemetry를 넣는 근거가 아니다. 제품 release network/최종 APK·IPA 관찰은 R09의 별도 계약이다.
구현 순서와 검증 행렬
- 기존 workflow/check 이름·source/merge 기록·실제 runner toolchain을 확정한다. T01/T02 entrypoint 상태와 필수 suite/case manifest를 읽는다.
- 기존 Android release manifest와 Flutter quality를 유지하면서 iOS unsigned device-target build 및 RunnerTests 실제 실행을 추가한다. SDK가 AlarmKit 경로를 실제 컴파일하는지 확인한다.
- T02 Android native 및 T01 양 플랫폼 core smoke를 같은 source에 연결한다. 테스트 의미/driver는 해당 이슈의 계약을 사용한다. 새 native framework를 R02에서 임의 도입하지 않는다.
- 최소 권한·fork 경로·정확 triggers·bounded 실행·artifact와 aggregate를 구현한다. R01에 최종 실제 check 이름을 전달한다.
- 아래 실패 조건을 격리된 일회성 검증 또는 gate fixture에서 확인한다. 테스트를 고장 낸 source를 일반 제품 변경으로 남기지 않는다. 각 negative는 방법/한계·원 실패·수정 후 결과를 연결한다.
- 실제 원격 PR의 동일 merge/source에서 모든 필수 검사가 실행되고 결과 artifact를 다운로드해 manifest/hash를 다시 확인한다. 그 뒤 R04/R01/R09와 증거를 연결한다.
| ID | 자극 | 기대 판정 |
|---|---|---|
| CI01 | 깨끗한 checkout, cold dependency cache | generator와 lockfile 설치 후 두 플랫폼 build/native/smoke 실행. 로컬 ignored output에 의존하지 않음. |
| CI02 | 같은 조건 warm cache | 같은 필수 cases 실행, cache hit를 테스트 대체로 쓰지 않음. |
| CI03 | 격리된 Swift compile error | iOS build 실패, mobile aggregate 실패, compiler 진단 보존. |
| CI04 | native assertion 실패 | 실제 native test result 실패가 job/aggregate로 전파됨. 컴파일 성공으로 덮이지 않음. |
| CI05 | suite0개 또는 expected case/report 삭제 | runner exit0이어도 aggregate 실패. 누락 case 목록 표시. |
| CI06 | 필수 child job skip/cancel/failure | aggregate success 불가. 전체 run cancel로 aggregate 미실행이어도 검증 완료 아님. |
| CI07 | T01 core smoke assertion 실패 | 양 플랫폼 중 한쪽 실패를 다른 쪽 성공으로 상쇄하지 않음. fixture/화면/로그 보존. |
| CI08 | emulator/simulator boot timeout·runtime 부재 | 유한 실패·대상/환경 진단, fallback fake나 skip-pass 없음. |
| CI09 | 필수 artifact 업로드/identity 부재·digest 불일치 | evidence/gate 실패, 잘못된 binary 결과 연결 거부. |
| CI10 | PR 새 commit으로 이전 검증 취소 | 이전/현재 attempt 분리, 최신 소스만 완료 근거. 다른 PR/배포는 취소되지 않음. |
| CI11 | fork/문서-only/draft PR trigger 조건 | secret 없이 동일 필수 검사가 생성·실행됨. 플랫폼 fork 승인 대기는 pending으로 기록. |
| CI12 | merge_group 및 branch push/dispatch | 실제 checkout SHA/trigger가 manifest에 일치. queue 활성화 여부는 별도 R01 상태. |
CI03/04/07은 실제 runner의 compiler/test/앱 실패 전달을 입증한다. CI05/06/09의 판정기를 fixture로 검증한 경우 workflow에서 실제 해당 판정기를 호출하는 연결도 확인한다. fixture-only 검증을 원격 실패 run으로 바꿔 부르지 않는다. 비용을 이유로 같은 실패를 무한 반복하지 않는다.
의존성과 완료 경계
- T01 #615: 실제 앱 smoke entrypoint/driver·fixture/expected/result. 전체 여정 및 물리 RC 성공은 T01에 남는다.
- T02 #616: Android instrumented·XCTest 계약·native UI bridge·OS 위험 경계 및 물리 행렬. 실제 플랫폼 test 실행을 compile-only로 대체하지 않는다.
- T03 #613: 빠른 장애 회귀는 기존 Flutter suite, 별도 host kill은 명시 entrypoint와 timeout으로 재사용. R02가 crypto/fault oracle을 복제하지 않는다.
- T04 #614: 실제 이벤트 연결/독립 기대값. fake 복제 알고리즘으로 smoke를 쉽게 통과시키지 않는다.
- R01: 실제 check 이름·최신 SHA/merge-group 검증을 ruleset으로 강제하고 원격 read-back. R02가 성공했어도 main 보호가 적용됐다고 말하지 않는다.
- R04: 같은 검증 소스·품질과 배포 artifact 연결. R02의 PR validation artifact를 store release로 승격했다고 말하지 않는다.
- R05/R06/R08/R09: 배포 경쟁·환경, 공급망 도구 pin/업데이트, release manifest·서명·store 상태, 최종 binary/network 관측. 새로운 CI 작업의 최소 안전 설정은 R02가 책임지되 이들 전체 범위를 흡수하지 않는다.
완료 기준
- 현재 baseline 정정이 이슈와 실행 기록에 반영돼 있고 기존 Flutter/Android release-manifest 검사가 유지된다.
- 동일 실제 PR merge/source에서 iOS unsigned Release device build, 실제 XCTest, Android native tests 및 두 플랫폼 core smoke가 실행된다.
- Flutter3.44.4/Dart3.12.2와 명시 native toolchain/runtime을 확인했고 생성 Dart는 ignored artifact로 남는다.
- 일반 PR·문서-only·draft·fork·push/dispatch·merge_group 정책과 stable check 이름이 명확하며 secret 없는 fork 경로가 검증된다. 실제 플랫폼 승인 대기는 미실행으로 남긴다.
- aggregate는 필수 job 성공·expected cases·결과/identity 존재를 모두 검증하고 skipped/cancelled/0 tests/missing result를 통과시키지 않는다.
- 실패 전달·timeout·artifact 누락·source/digest 불일치의 negative 증거와 원 실패가 보존된다. broad retry/continue-on-error/threshold 완화가 없다.
- 원격 artifact를 내려받아 source/merge/toolchain/실제 binary digest·native 결과를 read-back했고 각 check의 정확한 run/attempt와 결과를 연결했다.
- 검증용 signing·unsigned/simulator·device build·실제 native execution·물리 RC·store 배포를 분리한다. 사용자/다른 QA 데이터 및 기기는 보존한다.
- T01/T02 미제공 필수 entrypoint나 runner 가용성 결여가 없다. 있다면 부분 구현으로 기록하고 R02는 OPEN이다.
- R01/R04/R09에 실제 check/manifest 계약을 전달했다. 해당 이슈의 별도 미완료 물리/보호/배포/network 기준을 완료로 바꾸지 않았다.
제외: Flutter/지원 OS의 임의 업그레이드, macOS/Web을 제품으로 추가, 새 서버·로그인·cloud analytics, store 제출/배포·서명 권한 확보를 CI 통과로 주장, 사용자 기기 초기화, 무한 OEM/OS 행렬, compile만으로 OS 수신·picker·암호화 실행 증명. R02 완료는 PR CI 배선/실행의 완료이며 모든 앱 개선 완료가 아니다.
grill-with-docs 실제 전체 문답
아래는 전담 agent와 root의 실제 질문·답변이다. root가 사용자를 대신해 결정했으며 문서에 맞춰 소급 합성한 대화가 아니다.
Q1 — PR 실행 범위와 기존 검사의 재사용
Agent:
R02 grill-with-docs 및 고정 d73c786 조사했습니다. Q1 — PR 필수 실행 범위를 다음처럼 확정할까요? 권고: 기존 Flutter Testing(analyze/unit/widget/coverage80)과 Android Notification Contract(release APK·merged/packaged manifest·일회용 서명)를 유지·재사용하고, R02는 iOS unsigned Release device-target build, 기존 RunnerTests의 실제 simulator XCTest, T02 Android instrumented/native suite, T01 Android emulator/iOS simulator의 결정적 core smoke를 같은 PR 소스에 배선합니다. build 성공을 native test 실행으로 세지 않고, T01/T02 entrypoint 미구현은 명시 dependency pending으로 두어 no-op/skip green을 만들지 않습니다. PR에서는 플랫폼당 대표 runtime으로 bounded 검증하고 T02의 API23/26/31~32/33+/target, iOS15/pre26/26+ 위험 경계 전체와 물리 RC는 별도 수동·확장 run으로 연결합니다. iOS SDK는 기존 AlarmKit guarded 소스를 컴파일 가능한 Xcode26 계열 가용 runner를 명시 확인하고, Flutter3.44.4/Dart3.12.2·지원하한·local-only 정책은 고정합니다. 서명/physical/store 제출은 R02 build 통과로 주장하지 않으며 R04는 같은 소스 배포, R01은 required-check enforcement, R09는 최종 release artifact/network를 소유합니다. 새 glossary/ADR는 불필요합니다.
Root:
Q1 채택. 기존Flutterquality/AndroidreleaseAPKmanifest를재사용하고 같은PR소스에 iOS unsigned Release device-targetbuild·실제SimulatorXCTest·T02Androidnative·T01양플랫폼deterministiccore배선. no-op/skip을새requiredjob성공처럼표시금지, entrypoint미구현은dependency pending, 단계적으로실제job만완료근거. PR플랫폼당대표runtime으로유한실행,구OS경계확장/물리RC는T02명시run. Xcode26계열 runner실제가용성과SDK·runtime목록확인하여고정; 없으면가용성한계명시하고미확인label만써서실행가능주장금지. Flutter3.44.4/Dart3.12.2/지원하한/local-only고정. unsigned build는서명/설치/RC/deviceproof아님. R01/04/09책임구별, 새glossary/ADR없음. test artifacts와release배포artifact혼동방지.
Q2 — Trigger·fork·필수 검사·실행 비용
Agent:
R02 Q2 — fork·trigger·required check·실행 비용 정책을 확정하겠습니다. 권고: main/release//hotfix/ 대상 pull_request와 해당 branch push, workflow_dispatch를 유지하고 merge_group checks_requested를 함께 지원하되 merge queue 활성화 자체는 R01로 둡니다. 모든 일반 PR(문서 변경 포함)에 안정적인 고유 check 이름을 내고 path filter/draft skip으로 필수 검사를 누락시키지 않습니다. 기본 contents:read, checkout persist-credentials:false, OIDC/store/signing secrets·보호 environment 사용 없음; Android는 현재 disposable key, iOS는 unsigned/simulator, fork도 동일 코드 경로이며 승인 대기는 pass가 아닙니다. pull_request_target 또는 권한 높은 workflow_run으로 untrusted head를 실행하지 않습니다. 최종 mobile-ci 판정은 always 실행해서 필요한 실제 job 전부 success + 예상 suite/case/result 존재를 확인하고 cancelled/skipped/missing/0 tests를 failure 또는 incomplete로 처리합니다. 기존 두 workflow check를 함께 요구할 R01 목록을 문서화하며 cross-workflow 결과를 조회하지 않은 aggregate가 전체quality까지성공했다고하지 않습니다. workflow+PR/ref 단위concurrency로 오래된 PR run만 취소, 플랫폼별 fail-fast:false·유한 job timeout(초기 Android release35분 유지/native45분/iOS60분/smoke45분 목표), 부팅·테스트에도짧은별도watchdog·always diagnostics/소유target cleanup. 자동 flaky retry·continue-on-error는두지않고 재실행은원실패를보존합니다. 새로운mobilejobs의안전한실행설정은R02, deploytrack경쟁/환경보호는R05에남기는안입니다.
Root:
Q2 채택. main/release//hotfix/ PR/push+dispatch,merge_group checks_requested지원(큐활성화는R01). stable고유checks,requiredpaths/draftskip없음. contents:read/persistcredentialsfalse,prodsecret/OIDC/env권한없음,disposableAndroid/unsignediOS. fork동일경로·승인대기pass아님,PR_target/권한높은workflow_run에untrusted실행금지. aggregate always에서실제필수job성공+예상suite/result존재/0tests거부; workflow밖Flutter/Android기존checks는R01에서각각요구,조회없는globalpass주장금지. concurrency는workflow+PR/ref이지만cancel-in-progress는이전PR/merge검증작업에한정하고수동/main/release검증취소정책은명시,배포작업절대이그룹으로묶지않음. failfastfalse/유한timeout Androidrelease35 native45 iOS60 smoke45는초기상한목표,실측따라과도중복없이조정. boot/test watchdog·always실패artifact·소유targetcleanup,실제반환안된제품owner를테스트편의조기release금지. flaky재시도/continueonerror없음. 새CI안전기본R02,배포경쟁/환경R05 유지.
Q3 — Artifact·동일 소스·실패 전달·종결
Agent:
R02 Q3 — artifact·동일 SHA·negative 검증과 종결을 정하겠습니다. 권고: 각 job이 PR head SHA/base SHA/실제 checkout merge SHA(또는 push/merge_group SHA), workflow run/attempt/job, Flutter/Dart/Java/Gradle/Kotlin/Xcode·SDK/runtime/runner image, lockfile hash, target/app ID·build mode/signing종류, 실제 빌드/설치 artifact SHA256, 예상/실행/성공/실패/skip case와 result/log hash를 기계 manifest로 남깁니다. test APK/app와 release APK/unsigned device app를 별도 식별하고 product DB/key/password·서명키는 artifact/cache에서 제외; 합성fixture로그/xcresult/JUnit/manifest는성공·실패30일, binary는필요한합성validation만선택보존. 필수result누락/업로드실패는기능PASS와구별하여gate실패,부팅전실패는missing진단이있어도통과불가. lockfile/OS/arch/toolchain 기준cache,generated/build/키/data는재사용안하고cold/warm각1회로의존성감춤확인. 일회성격리검증에서 Swift compile failure·native assertion failure·zero-test/missing report·필수jobskip/cancel·smoke failure 각각 aggregate를막는지보이고실패원본을보존,제품코드를고장낸상태로게시하지않습니다. 실제원격PR의같은merge소스가두플랫폼build/native/smoke모두통과하고artifact를다운로드해manifest/hash·check이름/결과를read-back한뒤R02CI배선완료;T01/T02물리필수행,R01보호,R04배포,R09네트워크는자체완료조건을유지합니다. 참고:공식runner-images 현재목록에는macos26/Xcode26.5/26.6/iOS26.5runtime가있으나우리repo실제할당·부팅증거는아직없어구현시재확인예정으로씁니다. 이기준으로문서동결할까요?
Root:
Q3 채택. sourcehead/base/checkoutmerge(또는push/mergegroup)와runattempt/job/도구SDKruntime/runnerimage/lockhash/appID/mode/signing/build·install실물digest/expectedexecutedpassfailskip/resultloghash 명시. 서로다른test/release/unsignedartifact구별,키DBpassword·서명비밀cache/artifact금지,합성로그/manifest/xcresult/JUnit30일보존;binary는실제필요검증본만. assertionPASS여도필수증거upload/readback실패는전체gate성공아님. caches는dependency전용OSarchtoolchainlockkey,generated/build/키/data재사용금지,coldwarm각1회근거. negative는격리된fixture/검증환경에서한번씩 compile/assertion/zero-missing/requiredskipcancel/smokefailure 판정확인,실제품고장커밋게시·현재branch깨뜨리기금지. 로컬검증기negative와실제원격실패job증거수준구별, green만으로차단동작증명하지않음. 완료는동일원격PRcheckout소스양플랫폼build/native/smoke성공+artifact다운로드hash/check결과readback. T01/T02기기조건,R01보호,R04배포,R09network는별도. 공식runnerimages목록≠실제할당/부팅증거임명시. 실제문답전체와fixedsource/official읽은근거/행렬/구체AC 동결하세요.
질문과 답변의 축약 표기는 원문을 보존했다. 본문의 실행 계약은 이 답변을 풀어 쓴 것이다. 새로운 제품 정책·용어는 결정하지 않았으며 상세 이슈 게시·CI 구현·실제 실행과 원격 read-back은 root가 이어간다.
Activity