diff --git a/agent-roadmap/phase/workflow-integration/milestones/install-session-history.md b/agent-roadmap/archive/phase/workflow-integration/milestones/install-session-history.md similarity index 94% rename from agent-roadmap/phase/workflow-integration/milestones/install-session-history.md rename to agent-roadmap/archive/phase/workflow-integration/milestones/install-session-history.md index 01c41b7..c411938 100644 --- a/agent-roadmap/phase/workflow-integration/milestones/install-session-history.md +++ b/agent-roadmap/archive/phase/workflow-integration/milestones/install-session-history.md @@ -11,7 +11,7 @@ ## 상태 -[검토중] +[완료] ## 승격 조건 @@ -55,16 +55,16 @@ ## 완료 리뷰 -- 상태: 요청됨 +- 상태: 승인됨 - 요청일: 2026-06-16 - 완료 근거: - 현재 설치 결과 모델과 단일 결과 배너로 history Epic을 충족했다. - masking된 설치 리포트 복사, 실패 요약 표시, 현재 결과 초기화로 report Epic을 충족했다. - local 및 remote Mac runner에서 `flutter analyze`, `flutter test`가 통과했다. - 리뷰 필요: - - [ ] 사용자가 완료 결과를 확인했다 - - [ ] archive 이동을 승인했다 -- 리뷰 코멘트: 모든 기능 Task가 완료되어 사용자 최종 확인과 archive 승인 대기 상태다. + - [x] 사용자가 완료 결과를 확인했다 + - [x] archive 이동을 승인했다 +- 리뷰 코멘트: 코드 레벨 분석과 local/remote 검증 통과 후 완료 승인으로 archive 이동한다. ## 범위 제외 diff --git a/agent-roadmap/phase/workflow-integration/PHASE.md b/agent-roadmap/phase/workflow-integration/PHASE.md index 44dd8ce..2357e58 100644 --- a/agent-roadmap/phase/workflow-integration/PHASE.md +++ b/agent-roadmap/phase/workflow-integration/PHASE.md @@ -17,8 +17,8 @@ MVP 설치 흐름을 Teams/Jenkins 알림과 연결하고, 설치 실패 대응 - [완료] macOS 빌드 CI와 인증 빌드 - 경로: `agent-roadmap/archive/phase/workflow-integration/milestones/macos-build-ci.md` - 요약: Teams deep link와 오류 리포트 작업 전에 원격 Mac build/CI baseline과 Developer ID notarized 인증 빌드 절차를 확정한다. -- [검토중] 설치 오류 표시와 복사 리포트 - - 경로: `agent-roadmap/phase/workflow-integration/milestones/install-session-history.md` +- [완료] 설치 오류 표시와 복사 리포트 + - 경로: `agent-roadmap/archive/phase/workflow-integration/milestones/install-session-history.md` - 요약: 1차 MVP에서는 설치 이력을 저장하지 않고, 현재 설치 실패 원인과 masking된 복사 리포트만 제공한다. - [보류] Teams deep link 설치 진입 - 경로: `agent-roadmap/phase/workflow-integration/milestones/teams-deep-link.md` diff --git a/lib/src/models/install_attempt_result.dart b/lib/src/models/install_attempt_result.dart index b84a1f0..b723765 100644 --- a/lib/src/models/install_attempt_result.dart +++ b/lib/src/models/install_attempt_result.dart @@ -68,7 +68,7 @@ class InstallAttemptResult { } String _maskSensitive(String value) { - return value + final masked = value .replaceAll(RegExp(r'https?:\/\/[^\s]+'), '[masked-url]') .replaceAllMapped( RegExp( @@ -83,8 +83,10 @@ class InstallAttemptResult { caseSensitive: false, ), (match) => '${match.group(1)}=[masked]', - ) - .replaceAll(device.serial, maskedDeviceSerial); + ); + return device.serial.isEmpty + ? masked + : masked.replaceAll(device.serial, maskedDeviceSerial); } String _maskSerial(String serial) { diff --git a/test/widget_test.dart b/test/widget_test.dart index 73ef4e1..feb97c8 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -179,6 +179,30 @@ void main() { expect(report, isNot(contains('R5CT90A1B2C'))); }); + test('InstallAttemptResult masking tolerates an empty serial', () { + const attempt = InstallAttemptResult( + pendingInstall: PendingInstall( + apkPath: '/tmp/staged/app-release.apk', + fileName: 'app-release.apk', + buildNumber: 7, + jobName: 'android-app', + sizeBytes: 1024, + ), + device: AdbDevice(serial: '', state: 'device', model: 'SM_S918N'), + result: AdbInstallResult( + exitCode: 1, + stdout: 'token=secret-token', + stderr: 'INSTALL_FAILED', + ), + ); + + final report = attempt.maskedReport; + + expect(report, contains('Device: SM S918N ([masked-serial])')); + expect(report, contains('token=[masked]')); + expect(report, isNot(contains('secret-token'))); + }); + testWidgets('renders the app shell', (WidgetTester tester) async { await tester.pumpWidget(const AppSokApp());