SKIX 비즈니스 로직 변경 대장
리팩터링 중 실제 동작에 영향을 주는 변경을 한 곳에서 리뷰하기 위한 대장입니다. 정책 값의 이동, 실행 규칙의 변경, 신뢰성 보강, 미이관 항목을 구분합니다. 모든 항목을 검토·승인했다는 의미는 아닙니다.
2026-10-15 소스 기준 · Framework refactor/blueprint-foundation / 4d9b1a57 + 미커밋 변경 · 팀원 비교 기준 164654fc. 현재 DeviceAgent도 이미 수정된 소스이므로 이를 무조건 리팩터링 전 원본으로 부르지 않습니다. 공통 조상 5d0ee581을 직접 비교한 항목에는 별도로 적었습니다. HEAD 번호만으로 현재 코드를 재현할 수 없으므로 로컬 기준선 JSON에 소스 SHA를 함께 기록합니다.
문서 공유 범위: 최신 LLM 관측 수명과 지도 조회·수신·업로드, 원본 응답 FD 수신 및 파일쌍 변환까지 이번 상세 공유본에 포함합니다. 기록 이력의 “미배포/초안”은 해당 작업 당시 상태입니다. 문서를 공개해도 APK 설치·실기기 검증·제품 승인이 완료되는 것은 아닙니다.
최신 소스 후속 · 조회 화면과 모드 생략 청정 명령
아래 두 변경은 소스 검증·Java 컴파일 완료이며, 기존 APK 후보에는 아직 포함하지 않았습니다. 기기·서버 검증과 제품 승인은 남았습니다.
| 변경 | 기존 동작 | 현재 동작과 보존 규칙 | 검증 / 미검증 |
|---|---|---|---|
| 공기질 조회 시험 화면 | DeviceAgent 전용 중복 버튼·응답 지연 시 불명확한 대기 | 개별 실내/실외 버튼을 공개 측정 경로로 정리. 실내·실외·AQM 측정/공간 목록은 6초 UI 한도, 늦은 응답 폐기·예약 실패 표시. 명시적 legacy17 시험은 유지 | 영향 26건·컴파일 32초·명세→품질. 실행 중 Binder 취소나 실제 센서/화면 인수는 아님 |
| 모드 없는 PAUSE/RESUME | MQTT 요청이 DeviceAgent 준비 상태를 요구 | 현재 Framework 독립 실행이면 기존 BASIC command Task 사용. 기존 요청 기록 우선 조회, 새 START 식별자 저장 규칙 보존. Host의 명시 COMPATIBILITY만 legacy 유지 | 영향 12건·IoT 컴파일 33초·명세→품질. NEXT·명시 비고정 모드·전체 CLEAN 수명은 미이관 |
연결/조회 실패, 차단 상태, 소유자 교체, 결과 불명확은 실패로 남기며 legacy 우회·자동 재전송을 하지 않습니다. Task 접수 결과와 실제 팬·이동 완료는 다릅니다.
정본: skix-airquality-query-consumer-evidence-2026-10-15.json, skix-iot-optionless-cleaning-evidence-2026-10-15.json.
기존 후보 패키징 상태 · 10/15 IoT 후보 갱신
고정청정 Task 접수·시작 요청 식별자 보존·MQTT parser 수정·독립 진입 4종은 소스 검증에 이어 IoT APK 후보 반영까지 완료했습니다. 이전 이력의 “APK 미반영”은 각 작업 당시 상태이며 이 문단이 최신 패키징 기준입니다. Host·시험앱·AMR은 기존 검증 후보의 해시를 고정했습니다.
- 검증 정본:
skix-iot-independent-apk-evidence-2026-10-15.json. IoT devRelease 빌드·기기에서 보관한 IoT 인증서와 후보 서명 일치·SDK/R8/DEX·native 검사·명세→품질 검토를 기록했습니다. - 기기 설치·실제 MQTT 동작·서버 최종 수신·제품 승인은 미완료입니다. 패키징 자체는 새로운 비즈니스 정책 변경이 아닙니다.
- 센서/이동청정 전체 실행 조립, CLEAN 이벤트 전체 수명, 요청별 AMR 도착·도킹 완료 근거는 여전히 남았습니다. 전체 진행 현황에서 다음 작업과 검증 단계를 확인합니다.
1. 먼저 볼 내용
| 구분 | 뜻 | 예시 | 일괄 리뷰에서 확인할 것 |
|---|---|---|---|
| 기존 규칙 보존·이동 | 계산 값과 제품 조건은 유지하고 책임을 이동 | 배터리 보정, LCD 밝기, UV 110/30분, 사용 시간 | 기존 경계값·부수 효과까지 동일한가 |
| 실행 규칙 변경 | 언제 허용·거절·복구할지 달라짐 | requestId 중복 제어, 설정 revision 충돌, 불확실 UV의 명시 OFF | 제품에서 원하는 제한인가 |
| 신뢰성 보강 | 동일 기능을 실행해도 실패·재연결·완료 처리 방식이 달라짐 | 이전 callback 폐기, ACK와 실제 정지 분리 | 대기/차단 뒤 복구 경로가 있는가 |
| 미이관·비교 필요 | 새 경로가 기존 모든 기능을 대신한다는 근거가 부족 | 지도 삭제 부수 효과, OTA 데이터, AMR 완료 상관 | 빠진 기능을 변경으로 오인하지 않았는가 |
이번 대장은 기존/현재 소스의 기능별 감사 결과입니다. 전체 파일의 모든 분기와 과거 버전의 완전한 의미 동등성 검증은 끝나지 않았습니다. 아래 목록에 포함된 항목 수도 진행률이나 완료율이 아닙니다.
로컬 구현 후속 · 지도 파일쌍 공개 전달 (공개 배포 전)
B-MAP-03 추가 변경: 기존에는 IoT가 DeviceAgent의 REQMAP 결과와 공용 파일 경로에 의존합니다. 이번에는 공개 SDK → 인증 AMR Binder → 현재 Host → 현재 AMR 관측 owner → 원본 FD 변환 → 요청별 PGM/YAML 바이트 쌍을 연결했습니다. IoT 진입부도 이 SDK에 연결했습니다. 아래 사례 J가 요청→파일쌍→HTTP→MQTT의 실제 변경을 설명합니다. 소스 연결과 로컬 검증이며 APK·기기·서버 독립 업로드 인수는 아직 끝나지 않았습니다.
- 입력: 저장 성공한 관측의 epoch·순번·시각·형식·binding generation을 고정합니다. 5초 초과/미래 관측·승인 전 binding·다른 연결·저장 실패·export 없음은 전달하지 않습니다.
- 실행: Binder 호출은 bounded pipe를 할당하고 변환은 writer에서 수행합니다. 동시에 2개, 대기 큐 없음, 10초 종료 제한입니다. 새 GET·자동 재전송·DeviceAgent fallback은 없습니다.
- 반환: 합계 16MiB 이하의 PGM/YAML 한 쌍입니다. 원본 plain/ZIP은 plain 쌍, AES128은 암호문 쌍이며 여기서 암호문을 임의 복호화하지 않습니다. SDK는 길이·이름·형식·동일 관측·EOF·reliable FD 오류를 검사하고 모든 경로에서 FD를 닫습니다.
- 전달 중 연결 교체: 마지막 owner 검사 실패는 pipe 오류로 종료합니다. 바이트가 완전해도 SDK의 checkError에서 실패해야 합니다. 이전 연결의 결과를 최신 연결 성공으로 표시하지 않습니다.
- 완료 의미: 파일쌍 반환만 확인합니다. HTTP 업로드·MQTT 최종 응답·지도 생성 Task 완료·모터 정지를 의미하지 않습니다.
- 검증 정본:
skix-canonical-map-export-evidence-2026-10-15.json. 실제 runtime/JSON/gzip/tar/codec/writer를 사용하고 Android FD/Binder 경계는 대역입니다. APK·기기·서버 미검증이며 제품 승인도 미완료입니다.
최신 상태부터 확인 · 이전 이력과 구분
아래는 현재 소스에서 확인한 상태입니다. 뒤쪽의 날짜별 기록에는 당시 미완료 상태도 남겨 둡니다. “로컬 검증”은 설치된 기기에서 기능을 확인했다는 뜻이 아닙니다.
| 변경 묶음 | 실제로 바뀐 동작 | 현재 근거 | 아직 확인할 것 |
|---|---|---|---|
| 청정 실행·중단 | 접수·실행·물리 정지를 구분하고, 정지 불명확이면 자원 해제를 보류 | CLEAN-01~04 / 사례 A | 전체 모드·장애·제품 복귀에 대한 인수 |
| 지도 생성 | 수동1/자동2 유지, 정리·팬 예약·전송 직전 조건 검사, IoT 요청을 공개 Task에 연결 | CROSS-06·B-PLACE-02 / 사례 B·G | native 실행별 완료, 저장 뒤 업로드·제품 복귀 |
| 이동 준비의 LLM 상태 | 지도 생성·스테이션 복귀에서 현재 Agent 연결과 마지막 검증 관측을 전송 직전 대조; 연결 종료·후속 관측·NOT_READY면 기존 허용 무효 | VOICE-02·B-PLACE-02 / 관측 수명 후속; 검증 정본은 아래 참조 | Agent의 미조회 상태 전환·실제 IPC 경합·APK/기기; 상태 관측은 실행 lease가 아님 |
| 지도 파일 GET | 시험앱이 DeviceAgent 없이 공개 SDK→Host→AMR에 요청 | B-MAP-03 / GET 후속 기록 | 반환 true는 큐 접수; 요청별 파일 완료는 미연결 |
| 일반/ENC/ZIP 수신 실패 | 일반 지도 invalid·파싱/저장 예외도 실패 보고 시도 1회. 짧은 메시지가 수신 switch에서 예외를 내지 않음; 처리 실패를 false 관측으로 전달 | B-MAP-03 / 사례 H; 로컬 회귀·AMR Java 컴파일 기록 | null 입력은 여전히 결과 보고 없음; APK·기기 수신 |
| ZIP 저장 | 실제 암호화 서비스 boolean을 보존; 누락·중복·다른 stem 파일쌍은 provider 호출 전 거절 | B-MAP-03 / 사례 H; 실제 AAR 계약 확인 | 실제 provider 저장·부분 파일 원복·SELinux |
| 원본 지도 파일쌍 변환 | 저장 성공 관측의 epoch·순번·시각·형식이 같은 FD만 읽고, plain/ZIP/AES128 자료를 호출별 PGM/YAML 쌍으로 변환; 오래된 연결·잘못된 파일·자원 한도 초과는 거절 | B-MAP-03 / 원본 지도 응답 파일쌍 후속; 실제 JSON/gzip/tar 회귀 | APK·Task 완료 상관·실제 서버/기기 |
| 설정·PIN | 저장 책임을 Framework로 이동하고 revision 충돌을 명시 | B-SET-01·B-PIN-01 / 사례 E | 기존 장치 데이터 이관·제품 초기값 합의 |
| IoT 지도 업로드 | 요청마다 복호화 바이트·서비스·FD를 소유; 연결 누락·실패는 false, 연결/응답 대기 제한 추가. 초기화 없는 backend 선택 후속의 소비자 예외 처리 공백은 로컬 수정·검토 완료(후속 원본 초안) | B-MAP-03 / 사례 I; 로컬 회귀·IoT Java 컴파일 기록 | APK·실제 서버 최종 응답·native 요청 상관·부분 업로드 원복 |
리뷰 판정은 별도입니다. 소스 수정 완료, 로컬 검증, APK 설치, 실기기 동작, 제품 승인 중 어느 단계인지 항목별로 확인합니다. 77개는 감사 후보 수이며 “77개 모두 변경·완료”라는 뜻이 아닙니다.
상세 검토 지도 · 무엇을 먼저 비교할 것인가
이 문서의 목적은 구조 변경 설명이 아니라 제품 동작의 변경 여부를 검토하는 것입니다. 아래 영역을 선택한 뒤 전체 항목 표의 ID를 누르면 기존/현재 소스와 실행 조건으로 바로 이동합니다. 분류는 각 상세 기록의 표현을 보존하며, 후보가 모두 실제 변경이라는 뜻은 아닙니다.
| 검토 영역 | 상세 ID | 바뀐 규칙·보존할 규칙 | 리뷰에서 반드시 비교할 입력과 결과 |
|---|---|---|---|
| 청정 시작·풍량·중단 | CLEAN-01~06 | 요청 중복 제어·명령 결과·실행 수명 분리. 풍량/배터리/금지 조건은 기존 정책과 대조 | 같은 요청 2회, 다른 풍량에 같은 ID, 팬 정지 전 다음 작업, 통신 유실 후 조회 |
| 이동·회전·도킹 | AMR-01~05 | 유한 좌표·모터 실행 순서·연결 세대 검사. 접수와 도착 분리 | NaN/무한 좌표, 동시 회전/이동, 연결 교체 후 늦은 결과, 도착 근거 없는 ACK |
| 공통 Task·자원 | TASK-01~02 / RESOURCE-01~02 | 요청자별 실행 추적·취소 권한·불명확한 자원 점유 | 다른 앱의 취소, 종료/timeout 후 자원 상태, capability 준비와 실행 허가 차이 |
| 배터리·충전 | POWER-01~06 | 보정표 보존·관측 시각/순번 추가. 충전 실행과 기존 저장 책임은 별도 | 경계 배터리값, 오래된 샘플, 두 producer 동시 연결, 충전 명령의 실제 실행 owner |
| 감시·촬영·인증 | WSS-01~08 / B-PIN-01~02 | 실행별 시작·정리·중단·PIN/얼굴 context 검사 | 자연 종료, 취소와 timeout 경합, 인증 owner 교체, 녹화 정리 실패 |
| 음성·LLM·TTS | VOICE-01~06 | 문장 비노출·실행별 완료·현재 관측 검사. 기존 상태 번호 보존 | 오래된 상태, NOT_READY, TTS 접수 후 완료 누락, 종료된 연결의 callback |
| 화면·LED·SystemUI | UI-01~08 | 밝기/LED 값 이동과 owner별 화면 유지·popup 종료를 구분 | 야간 ON/OFF, 중첩 화면 hold, 앱 사망, overlay와 실제 SystemUI의 책임 |
| 설정·PIN·기존 데이터 | B-SET-01~03 / B-STORAGE-01 | 저장 owner·revision 충돌·조회 복구. 기존 데이터 이관은 미완료 | 두 앱의 동시 쓰기, 저장 실패, 최초 조회 실패, 업데이트 후 기존 설정 보존 |
| 지도·공간·파일·업로드 | B-MAP-01~03 / B-PLACE-01~02 | 조회/생성 Task/파일 저장/HTTP/MQTT 완료를 분리 | 공간 ID/이름, 지도 삭제 부수 효과, 파일쌍 실패, 중복/취소, 부분 업로드 |
| IoT·MQTT·부팅 | B-IOT-01~04 / B-BOOT-01 / B-BIND-01 / B-SCOPE-01~02 | 연결 수명과 실제 명령 책임 분리. 기존 외부 응답 규격 대조 | 재연결, 이전 callback, topic별 응답 누락, SET_APP_STATUS 생산자 미확인 |
| 보고·업데이트 | B-REPORT-01~02 / B-OTA-01~05 | 저장/발행 순서·실행별 OTA 결과·불명확한 결과 보존 | 19초/60초 집계 차이, 저장 실패, 중복 완료, 알 수 없는 native phase |
| 여러 도메인이 얽힌 정책 | CROSS-01~08 | UV 주기/공기질 산식 보존, pause 사유·오류 집계·예약 시각 검사 보강 | UV 전송 유실, 수동+시스템 pause, 복수 오류, 지도 생성 배터리/팬/LLM 경계 |
상세 기록에서 확인할 8가지
- 어떤 요청인가: 호출 앱/MQTT/내부 이벤트, 입력값과 단위, 직접 API인지 Task인지.
- 기존에는 어디서 처리했나: 생산자 → DeviceAgent/서비스 → 실제 HAL/AMR/화면/저장 효과.
- 지금은 어디서 처리하나: SDK → 인증 IPC → Host/domain → provider. 아직 legacy를 타는 구간도 표시.
- 언제 허용·차단하나: 배터리, 오류, 모드, 자원, 연결, 상태 freshness와 경계값.
- 어떤 순서로 효과가 생기나: 저장·중단·예약·실행·보고·화면전환 중 선후 관계와 부분 실패.
- 언제 끝났다고 하나: 접수 ACK, 실행 중, 파일 저장, 서버 응답, 물리 완료, 정리 완료를 분리.
- 실패 후 어떻게 복구하나: 재조회/명시 재요청/중단 확인. 자동 replay 유무와 남은 차단 조건.
- 무엇을 검증했나: 소스 비교, 실제 메서드 회귀, Android 컴파일, APK 설치, 실기기, 제품 승인을 각각 확인.
일괄 리뷰 기록 예시: CLEAN-03 / 비교 revision·소스 SHA / 기존 stop 반환 / 현재 정지 관측 대기 / 허용·수정 필요 / 남은 복구 시나리오 / 리뷰어·날짜. “미검토”는 제품 승인 상태이며 로컬 명세·품질 검토 실패를 뜻하지 않습니다.
우선 검토할 제품 영향
- 청정 중단·다음 작업: STOP 응답만으로 자원을 비우지 않음. 실제 정지 근거가 없으면 다음 작업이 막힐 수 있음.
- 공통/수동 일시정지: 시스템 사유 해제 시 사용자의 수동 pause와 다른 사유가 남으면 재개하지 않음.
- 명령 결과 불명확: 재연결했다고 이전 행동 명령을 자동 재전송하지 않음. 조회/명시 복구 필요.
- UV: 기존 주기는 유지하지만 불명확한 명령 뒤 명시 OFF가 필요함. 자동 부팅 활성화는 별도 정책 검토.
- 지도 생성: 배터리 표시값 ≥27과 정리 순서를 보존하는 중. 큐의 팬 예약은 로컬 검토 완료. 공개 SDK/Binder 연결은 로컬 검토·Android 컴파일 완료. Host 제품 정책 전체·물리 완료·기기 검증은 남음.
- 설정/PIN: revision 충돌을 조용히 덮어쓰지 않음. 기존 데이터 이관과 초기값은 별도 검토.
- OTA: 알려지지 않은 phase/native 값만으로 성공·삭제·재부팅하지 않음. native 101을 실행 job 완료로 볼 수 없음.
- 화면/LED: 값 추출을 새 정책으로 세지 않음. 명령 수락·readback·물리 표시를 구분.
읽는 순서와 검증 경계
- 각 항목의 트리거 → 기존 경로 → 현재 경로 → 실제 차이 → 영향 → 검증/공백을 순서대로 읽습니다.
- 항목에 적힌 테스트/빌드/evidence는 과거 해당 묶음의 근거입니다. 이번 감사에서 제품 테스트나 기기 동작을 다시 실행한 결과가 아닙니다.
- 소스에서 현재 제어 흐름을 확인한 것과, 그 규칙이 이번 리팩터링에서 최초로 생겼음을 확인한 것은 다릅니다. 역사적 diff가 부족한 항목은 비교 필요입니다.
Policy, SDK 모델, 포트가 존재해도 실제 Host·provider·소비 앱 조립이 완료됐다고 보지 않습니다. legacy 호환 경로와 독립 실행 경로를 나눠 읽습니다.- 표시된 줄 번호는 이 감사 시점의 위치입니다. 구현 진행 후에는 클래스/메서드명과 SHA로 다시 찾습니다.
실제로 무엇이 달라졌는가 — 리뷰용 비교표
아래는 상세 항목을 읽기 전에 확인할 대표 사례입니다. 기존 경로의 설명은 해당 항목에 인용한 소스 범위에 한정합니다. 기존 시스템 전체에 같은 기능이 없었다는 뜻은 아닙니다.
| 상황 | 기존 경로에서 확인한 동작 | Framework 경로에서 확인한 동작 | 사용자에게 보이는 차이 | 상세 ID |
|---|---|---|---|---|
| 같은 청정 요청을 두 번 보냄 | 직접 명령 경로는 공통 Task 요청 식별을 제공하지 않음 | 요청자·requestId로 동일 요청을 구분 | 같은 요청을 중복 실행하지 않도록 제어 | CLEAN-01 |
| 명령을 보낸 뒤 응답이 끊김 | 직접 호출 결과와 실행 추적이 분리되어 있음 | 미전송 확정과 결과 불명확을 구분; 불명확한 행동은 재전송하지 않음 | 연결이 복구돼도 이전 행동이 자동으로 반복되지 않음 | CLEAN-02 |
| 청정 중단 응답을 받음 | stopFan 호출과 실제 정지 확인이 별개 | 해당 실행의 실제 정지 근거가 있어야 자원 해제 | 중단 응답 뒤에도 정지 확인·복구 대기가 표시될 수 있음 | CLEAN-03 |
| 고정청정 시작을 요청함 | 정책 허용 반환과 물리 실행이 구분됨 | 명령 접수 Task와 실행 수명 Task를 연결 | 접수 성공과 청정 진행·정지를 따로 조회 | CLEAN-04 |
| 여러 앱이 동시에 이동 요청 | 기존 controller 직접 호출 | 공용 모터 실행 순서·소유자·중복 요청 검사 | 다른 동작이 모터를 사용하면 대기·거절 가능 | AMR-02 |
| 이동 명령에 성공 응답이 옴 | 허용/접수 반환만으로 도착을 증명하지 못함 | 도착·위치·도킹 관측과 실행 식별을 분리 | 완료 이벤트 연결이 부족한 기능은 완료 미확인 유지 | AMR-03 |
| AMR 연결이 교체됨 | legacy adapter/controller 경로 | 이전 실행 세대의 callback 폐기 | 늦게 도착한 이전 결과가 새 실행을 완료시키지 않음 | AMR-04 |
| 지도 생성 버튼을 누름 | 수동=1·자동=2를 AMR에 전달 | 동일 모드 규격 유지; 시험앱의 반대 연결 오류 수정 | 자동 버튼이 수동 명령을 보내던 개발 중 오류 해소 | CROSS-06 |
| 지도 생성 전 청정이 남아 있음 | 다른 동작 중단 후 지도 생성 시작 | 정리 확인 뒤 팬과 이동 자원을 확보 | 정리 미확인·팬 사용 중이면 지도 생성 시작 보류 | CROSS-06 |
| 지도 생성 제한 8분이 지남 | 이번 비교는 새 Task 제한에 대한 것 | 큐가 중단을 요청; 물리 정지 완료로 표시하지 않음 | 시간 만료와 실제 정지를 별도 확인 | CROSS-06 |
| 여러 오류 중 하나만 해제됨 | 기존 오류별 route와 비교 필요 | 다른 활성 오류의 차단을 유지하는 집계 규칙 확인 | 오류 하나가 해제돼도 이동·음성 등이 계속 차단될 수 있음 | CROSS-07 |
| 19초 청정 뒤 보고서를 조회 | 제품 보고 시간은 분 단위 집계 | 실제 경과 시간과 기존 보고 집계값을 구분 | 실행 19초와 보고 60초가 동시에 표시될 수 있음 | CROSS-08 |
기능별 상세 리뷰 시나리오 · 입력부터 복구까지
아래는 승인을 위해 앞으로 확인할 시험 기준입니다. 실행했다고 기록한 결과가 아닙니다. 상세 ID의 기존/현재 소스와 검증 정본을 함께 확인합니다. 정상 결과만 확인하지 않고 실패 뒤 다음 명령이 가능한지도 확인해야 합니다.
| 영역 / 상세 ID | 준비와 입력 | 확인할 실행 순서 | 정상 판정 | 실패·경합에서 확인할 것 |
|---|---|---|---|---|
| 청정 중복 / CLEAN-01 | 같은 호출자가 같은 requestId·동작·풍량을 두 번 제출 | 등록 → 기존 요청 조회 → 실제 명령 횟수 확인 | 동일 요청 보호 규격대로 Task를 돌려주고 중복 dispatch 없음 | 같은 ID에 다른 동작/풍량은 거절; 새 ID는 별도 요청으로 처리되는지 확인 |
| 청정 중단 / CLEAN-03·04 | 실행 중인 청정을 중단하고 다음 행동 요청 | 중단 요청 → 해당 실행의 팬 정지 관측 → 자원 해제 → 다음 행동 허용 | 정지 근거가 해당 실행과 연결되고 다음 실행을 침범하지 않음 | 응답만 성공/관측 없음/이전 실행 관측이면 완료로 판정하지 않음; 복구 대기와 운영자 조치 확인 |
| 일시정지 / CROSS-02 | 수동 pause와 시스템 pause 사유를 겹쳐 발생 | 사유별 등록 → 하나씩 해제 → 원래 실행의 재개 조건 검사 | 사용자 의도와 남은 사유가 없어야 재개 가능 | 시스템 사유 해제가 수동 pause를 지우지 않음; 종료된 실행을 자동으로 되살리지 않음 |
| 지도 생성 / CROSS-06·B-PLACE-02 | 수동1/자동2·배터리 표시값 경계·청정 잔여·LLM 상태 조합 | IoT journal → Task 접수 → 제품/배터리/정리 검사 → 자원/음성 준비 → 전송 직전 재검사 → AMR | 모드와 요청 식별 유지; 접수와 실제 시작·완료를 구분 | 8분 timeout은 중단 요청; 물리 정지 아님. 응답 유실 시 같은 요청 조회, 정확한 Task 취소; upload/제품복귀는 별도 미완료 |
| 지도 파일 / B-MAP-03 | 정상·잘못된 지도 입력, PGM/YAML 한쪽 쓰기 또는 rename 실패 | 입력 검사 → 두 파일 처리 → 결과 보고 → 호환 완료 효과 guard → 독립 관측 | 두 파일 처리 성공과 파일 결과만 일치 | 부분 교체 rollback 보장 없음. false/누락을 완료로 보지 않음. 전역 결과·최근 결과를 현재 Task 완료로 연결하지 않음 |
| 설정 저장 / B-SET-01 | 같은 revision을 읽은 두 앱이 서로 다른 값을 저장 | 각 요청의 revision 검사 → AtomicFile 저장 → 새 revision 반환 | 정상 저장은 새 revision으로 조회됨 | 뒤늦은 쓰기가 조용히 덮어쓰지 않음; 저장 실패·결과 불명확과 자동 재전송 금지 확인. 기존 데이터 복사는 별도 |
| PIN / B-PIN-01·02 | 정상/잘못된 PIN·revision 충돌·서비스 교체 | 인증 Binder → 현재 Host → private 저장/검증 → 해당 보안 실행의 context 검사 | 저장·검증 책임과 실행 허용 책임을 구분 | 원문 PIN을 조회/로그로 노출하지 않음; 이전 PIN 데이터 이관·지도 영역 조합은 별도 인수 필요 |
| UV / CROSS-01 | 저장 설정 ON·덮개 닫힘·MCU 준비, 전송 예외 주입 | 시작 전 OFF → 주기 관리 → ON/OFF 시도 → 불명확 상태 관리 | 110분 OFF/30분 ON 규격과 오류 처리 보존 | 불명확 뒤 타이머 ON 재전송 금지; 명시 OFF 복구 확인. 설정 ON만으로 부팅 자동 실행하지 않는 정책 승인 필요 |
| 다중 오류 / CROSS-07 | 차단 오류 두 개 활성화 뒤 하나만 해제 | 오류별 사실 갱신 → 남은 활성 사유 집계 → 각 도메인 실행 조건 검사 | 해제한 오류만 빠지고 다른 사유의 제동/음성/틸트 차단 유지 | 단일 오류 해제를 전체 정상 복귀로 보고하지 않음; 해제 순서와 늦은 보고에 따른 영향 확인 |
| 화면/LED / UI-02·03·04 | 야간 효과·화면 hold를 여러 owner가 사용 | 요청 owner 검사 → 출력 적용 → readback/상태 보고 → 해당 owner 효과 해제 | 유지 값과 효과 소유권이 구분됨 | 한 앱 종료가 다른 앱의 hold를 해제하지 않음; readback과 실제 화면/LED 확인을 별도 기록 |
| OTA / B-OTA-01~05 | 정상/실패/알 수 없는 phase·늦은 job 결과 | 요청 job 생성 → component 수행 → 해당 job 결과 수신 → IoT 보고 → 재부팅/저장 수명 확인 | 동일 job 결과와 전송 보고를 연결 | native 값/ACK를 flashing 성공으로 보지 않음; 늦은 결과·중복 ingress·재시작 후 이력 유실·기존 데이터 이관 확인 |
모든 시나리오의 결과 기록: 요청/실행 ID, 입력값, 전후 상태, 실제 효과 순서, 반환값, 실패 사유, 시험 소스 SHA 또는 APK SHA, 로그 위치, 미확인 범위를 남깁니다. PIN·문장·암호키·민감 파일 내용은 결과 문서에 복사하지 않습니다.
검증 중인 변경은 완료 변경과 분리
- 검토·검증된 provider 단계: 지도 파일 GET의 같은 서명 검사, ZIP→암호화→일반 선택, 요청 시점의 Handler/소켓 고정, 자동 재전송 없음. 근거는
skix-amr-map-request-provider-evidence-2026-10-15.json입니다. true는 AMR 로컬 큐 접수이며 파일 생성/교체 가능성이 있습니다. - 로컬 검증 완료한 소비 연결: Host→공개 SDK→시험앱의 명시 지도 파일 GET 버튼/결과 카드. 관련 회귀 17 PASS·독립 SPEC→QUALITY PASS·Host/SDK/시험앱 Android 컴파일2분5초 exit0. 접수·거절·미전송·unknown을 구분하며 저장·업로드·Task 완료로 표시하지 않습니다. 정본은
skix-amr-map-request-consumer-evidence-2026-10-15.json; APK 설치·기기·서버 검증은 남았습니다. - 계속 미완료: native 요청별 결과 식별, IoT 업로드 최종 응답, 지도 생성 종료 후 제품 복귀, 해당 최신 경로의 APK/기기/서버 인수. 파일 결과에 현재 Task ID를 임의로 붙여 이 공백을 지우지 않습니다.
상세 기록의 단위 · 기능 이름보다 조건과 효과를 비교
한 기능에 여러 규칙이 있으면 따로 확인합니다. 예를 들어 청정 시작에는 요청 중복, 시작 허용 조건, 팬/틸트 준비, 실행 등록, 상태 보고가 있고, 중단에는 명령 전송, 실제 정지 확인, 자원 해제, 제품 복귀가 있습니다. “청정 이관 완료”라는 한 문장으로 묶지 않습니다.
| 확인 축 | 기록할 내용 | 누락하면 생기는 오해 |
|---|---|---|
| 진입 경로 | 앱/API·MQTT topic·내부 callback → 서비스 경계 → 최종 효과 | SDK 메서드가 생긴 것을 실제 기기 연결로 오인 |
| 입력·기준값 | 모드, 단위, 허용 범위, 기본값, 임계값; 보존/변경 구분 | 기존 배터리/밝기/시간 기준을 새 정책으로 오인 |
| 시작 조건 | 권한, 배터리, 도킹, 오류, 실행 owner, 필요한 연결 | 모델/포트만 구현되고 실제 실행 조건이 빠짐 |
| 효과 순서 | 저장·팬·모터·화면·음성·보고 중 먼저 실행되는 것 | 중간 실패 뒤 일부 효과가 남는 상황을 숨김 |
| 완료 조건 | 접수, 실행 시작, 파일 저장, 물리 정지, 서버 응답 각각의 근거 | ACK나 전역 상태를 현재 요청의 완료로 오인 |
| 실패·취소 | 미전송/거절/unknown, 취소 대상, 남은 자원, 원복 가능 여부 | 실제 실행 여부를 모르는 상태에서 재실행 |
| 복구·호환 | 재연결/부팅 뒤 조회·재시도 규칙, direct/Task/legacy 차이 | 기존 앱에서만 동작하는 경로를 독립 실행으로 오인 |
| 판정 근거 | 기준 revision·소스 SHA·검증 정본·제품 승인 | 오래된 테스트 통과를 최신 APK 검증으로 오인 |
앞으로의 기록: 기존 대장 ID에 날짜별 후속을 붙이고, 변경 전 동작과 제품 승인 여부를 삭제하지 않습니다. 테스트가 통과해도 제품 승인 칸은 자동으로 바꾸지 않습니다. 새로운 입력·분기·부수 효과를 조사하지 못했다면 “비교 필요”로 남깁니다.
한 항목을 승인할 때 확인할 순서
- 입력: 어떤 API·MQTT 명령·callback이 변경을 발생시키는가.
- 허용 조건: 배터리·오류·도킹·기존 실행·권한 조건이 보존됐는가.
- 실제 효과: 팬·모터·음성·화면·저장·외부 보고가 어떤 순서로 실행되는가.
- 종료: 접수 응답, 수행 완료, 중단 요청, 실제 정지가 각각 어떻게 판정되는가.
- 복구: 실패·재연결·부팅 뒤 어떤 상태가 남고 누가 해제하는가.
- 검증: 소스 확인·Java 회귀·Android 컴파일·APK 설치·실기기 동작 중 어디까지 확인했는가.
예를 들어 CLEAN-03 승인은 “중단 API가 성공했다”만으로 끝내지 않습니다. 동일 실행의 팬 정지 관측 → 자원 해제 → 다음 작업 허용까지 확인해야 합니다. 반대로 POWER-03 승인은 새로운 Task 정책의 유무보다 기존 배터리 보정표와 경계값이 그대로인지가 핵심입니다.
실행 순서까지 풀어 쓴 상세 사례
아래 사례는 코드에서 확인한 경로를 단계별로 설명합니다. 검증 기록의 날짜와 범위를 함께 봐야 합니다. 로컬 회귀가 통과했더라도 현재 설치된 APK가 같은 소스라는 뜻은 아닙니다. 각 사례는 기존 대장 ID의 상세 설명이며 새로운 기능 완료 건수로 세지 않습니다.
사례 A · 청정 중단 뒤 다른 작업을 시작할 때 — CLEAN-03 / RESOURCE-01
기존 경로: 앱/명령 → DeviceAgent AirCleaningManager.stop() → stopFan(). 이 메서드가 반환됐다는 사실과 팬이 실제로 멈췄다는 사실은 별개입니다. 기존 시스템 전체에 정지 감시가 없었다고 단정하는 비교는 아닙니다.
현재 Framework 경로: 취소/중단 요청 → 청정 실행 owner → 팬 중단 전달 → 정지 근거 유무에 따라 아래 두 경로로 분기 → 다음 작업의 자원 확보 검사.
- 근거를 먼저 확인한 경로: 현재 HELD lease를 유지한 상태에서 정지 근거를 확인 →
releaseConfirmedInactive(token)→ FREE. - 근거 없이 반납한 경로:
release(token)→ UNVERIFIED, 기존 token 제거 → 별도 reconciliation token으로 관측을 검증해reconcile(..., available=true)→ FREE. 일반 release 뒤 같은 lease token으로 releaseConfirmedInactive를 호출하는 순서가 아닙니다.
| 시점 | 무엇을 확인하는가 | 결과와 사용자 영향 |
|---|---|---|
| 중단 API 반환 | 요청 전달/접수 여부 | 아직 다음 작업을 실행해도 된다는 근거가 아님 |
| 근거 없는 일반 반납 경로 | 현재 epoch·정확한 token이 소유한 HELD 자원인지 | release()는 UNVERIFIED로 전환하고 lease token 제거; 이후 별도 reconciliation 필요 |
| 근거를 먼저 확인한 경로 | 실행 owner가 정지 근거를 검증했고 token이 아직 현재 HELD lease인지 | 일반 release 없이 releaseConfirmedInactive()가 해당 lease의 자원만 FREE로 전환 |
| 연결 상실/결과 불명확 | 정지 여부를 확정할 수 있는지 | 복구·확인 대기가 남을 수 있음; 새 작업의 즉시 시작을 보장하지 않음 |
| 이전 실행의 늦은 결과 | 그 token이 후속 작업의 현재 token과 같은지 | 불일치하면 후속 작업 자원을 해제하지 않음 |
코드 근거: ResourceManagerService.release/releaseConfirmedInactive/isHeld, CleaningFanTaskQueue의 stop/reconciliation 경로. 자원 서비스 자체가 RPM의 의미를 판정하지는 않습니다. 정지 근거 판정은 실행 owner의 책임입니다.
리뷰 결정: “멈추라는 명령을 보냈으니 바로 FREE”로 되돌릴지의 문제가 아니라, 실제 정지 확인 실패 시 어떤 복구 절차를 제품에 제공할지 결정해야 합니다. 팬 회전/통신 장애/늦은 callback/후속 작업을 함께 시험해야 합니다.
사례 B · 자동/수동 지도 생성을 시작할 때 — CROSS-06 / B-PLACE-02
보존 규격: AMR 명령의 수동=1, 자동=2. 배터리 판단은 원시 값이 아닌 BatteryCapacityPolicy.displayPercent(sample.percent)의 표시 보정값 27 이상입니다. 도킹 중이라고 이 기준을 생략하지 않습니다.
현재 조립 순서: 공개 SDK 제출 → 인증 Binder/Endpoint → 지도 생성 Task → 기존 동작 정리 대기 → 이동/팬 자원 확보 → Host의 준비·제품 조건 검사 → 실행별 SoundFlow 지도 모드 적용 → 전송 직전 조건 재검사 → AMR managed mapping 전달.
| 단계 | 구체적인 검사/효과 | 실패하면 |
|---|---|---|
| 제출 | 모드 1/2·지연·제한 시간·요청 식별 검사 | 유효하지 않은 요청을 시작하지 않음 |
| 준비 | 기존 동작 정리와 현재 Task의 준비 결과 | 정리가 끝나기 전 모터 시작을 보내지 않음 |
| 제품 조건 | 현재 제품 mode, 현재 배터리 관측 owner, 보정 배터리 ≥27 | 관측 없음/저전력/owner 변경을 서로 구분하여 차단 |
| SoundFlow | 같은 실행 ID가 지도 음성 모드를 소유; 적용 뒤 readback 확인 | 이미 준비 효과가 생겼을 수 있으므로 무효과 실패로 간주하지 않음 |
| 전송 직전 | 제품 조건·준비 결과·동일 오류 owner·정확한 amr.mapping Task RUNNING 재확인 |
준비 중 오류 활성화/owner 교체/Task 취소 시 native 시작을 막음 |
| AMR 전달 | 모드·실행 ID·지연을 managed provider에 전달 | 접수/전송 결과와 물리 완료는 분리 |
이번에 실제 바뀐 부분: SoundFlow 적용 이전 검사만 믿고 시작하던 경로에 적용 이후 최종 검사를 추가했습니다. 적용 이후 정책 거절은 일반 실패로 감싸 UNKNOWN/음성 억제/무재전송 의미를 유지합니다. 반면 provider가 신뢰 가능한 REJECTED_BEFORE_DISPATCH를 반환하면 기존 미사용 SoundFlow 정리 경로를 사용합니다. 이것은 제품 배터리 기준 변경이 아니라, 준비 도중 상황이 바뀌었을 때의 전송·복구 규칙 변경입니다.
구체적 경합 예: 시작 조건 통과 → SoundFlow 적용 중 오류가 활성화됨 → 최종 오류 검사 실패 → AMR 시작 전송 0회. 또는 마지막 오류 조회 callback 중 Task가 취소됨 → 그 뒤 Task RUNNING 검사 실패 → 시작 전송 0회. 두 경우 모두 SoundFlow 준비 효과가 자동으로 없었던 일이 되지는 않습니다.
확인 범위: skix-mapping-final-admission-evidence-2026-10-15.json의 실제 Host 추출 회귀, 8 PASS, Host/시험앱 Java 컴파일과 독립 검토 기록. 배터리/준비/Task 경계 일부는 probe stub을 사용합니다. 마지막 검사와 provider 호출 사이의 모든 비동기 경합에 대한 원자성, 물리 정지 뒤 SoundFlow 복원, 지도 저장, 실기기 완료는 남아 있습니다.
UI 오류와 제품 규칙을 구분: 개발 중 시험앱의 자동/수동 버튼 값이 뒤바뀐 오류를 수정했습니다. AMR 숫자 규격을 새로 바꾼 것은 아닙니다. 8분 제한도 “8분 뒤 지도 생성 성공”이 아니라 큐의 중단 요청 시점입니다.
사례 C · 연결이 끊겼다가 돌아왔을 때 — CLEAN-02 / B-SET-03
행동 명령의 재전송과 상태 조회의 재시도는 서로 다른 규칙입니다.
| 상황 | 현재 규칙 | 이유/사용자 영향 |
|---|---|---|
| 행동 명령을 보낸 뒤 응답 상실 | 미전송을 확정하지 못하면 결과 불명확 유지; 자동 replay하지 않음 | 이미 팬/모터가 움직였을 수 있어 같은 행동의 중복 실행을 피함 |
| 같은 요청 식별자로 다시 제출 | 해당 API의 owner/requestId 멱등 규격으로 처리 | 새 요청 ID로 바꿔 보내면 동일 요청 보호를 우회할 수 있으므로 소비 앱 정책도 확인 필요 |
| IoT 초기 ProductSettings 조회가 null/UNAVAILABLE | 같은 binding/epoch에서 1·2·4초 간격, 최대 3회 재조회 | 초기 연결 준비 지연을 흡수; 행동 명령 재실행이 아님 |
| 재조회 대기 중 binding/epoch 교체 | 이전 연결의 예약 결과를 현재 상태에 적용하지 않음 | 이전 설정이 새 서비스 상태를 덮는 것을 방지 |
코드 근거: 청정 dispatch/실행 결과 경로와 FrameworkProductSettingsClient 초기 조회 경로. 모든 SDK 기능에 동일한 멱등 계약이 이미 구현됐다는 뜻은 아닙니다. 조회 복구 성공과 실제 행동 완료를 별도로 리뷰합니다.
사례 D · 오류가 여러 개 있다가 하나만 풀릴 때 — CROSS-07
ProductErrorRuntime.apply의 BLDC_STOP 해제는 setEmergencyBrake(!resolved || errorActive(BLDC_STOP))를 사용합니다. 즉, 현재 오류가 해제돼도 같은 그룹에 다른 활성 오류가 있으면 제동 요청은 남습니다.
- 오류 A·B가 모두 활성: 제동을 요구합니다.
- A만 해제: B가 남아 있으므로 제동 해제 요청으로 바꾸지 않습니다.
- 해당 그룹의 남은 오류가 없어진 경우: 이 규칙이 제동 해제를 요청할 수 있습니다.
- 제동 해제 요청은 새로운 이동 허가나 자동 재개가 아닙니다. Task/제품 정책/다른 자원 검사와 실제 효과 확인이 별도로 필요합니다.
이력 주의: 현재 aggregate guard의 코드 의미는 확인했지만, 이 규칙이 이번 리팩터링에서 처음 생긴 것인지의 역사적 revision은 확정하지 않았습니다. “현재 규칙 확인/역사 비교 필요”로 기록합니다. 동일한 집계 방식이 모든 오류·음성·틸트 분기에 적용됐다고 일반화하지 않습니다.
사례 E · 설정 저장 owner를 옮길 때 — B-SET-01 / B-PIN-01 / B-STORAGE-01
현재 경로: 공개 설정/PIN API → 권한·현재 Host 검사 → Framework store → revision 기반 저장 → 결과 반환. 설정은 버전이 있는 AtomicFile과 lock/CAS를 사용하고, PIN은 원문 대신 salt/derived hash/revision을 저장합니다.
실제로 달라질 수 있는 점: 두 소비 앱이 같은 revision을 읽고 서로 다른 값을 쓰면 후속 충돌을 조용히 덮어쓰지 않습니다. caller가 충돌 결과를 받고 최신 값을 다시 확인해야 합니다. 저장 완료와 관련 하드웨어 적용 완료도 구분해야 합니다.
아직 해결되지 않은 점: 새 store가 존재해도 기존 DeviceAgent 저장 데이터를 자동으로 가져왔다는 뜻은 아닙니다. OTA에서 기존 파일/DB 접근·변환·중복 방지·rollback을 설계해야 합니다. 기존 장치 값을 새 기본값으로 덮어쓰는 것을 이관 성공으로 판정하면 안 됩니다. 이 항목은 기능 이관과 데이터 이관을 나누어 리뷰합니다.
사례 F · 지도 파일 저장 결과를 조회할 때 — B-MAP-03
기존 경로와 남아 있는 책임: AMR 수신 → 파일 쓰기/rename → MapData callback → DeviceAgent MainService → 화면·저장 설정·업로드 효과. 현재 호환 경로에도 성공값 검사 보강이 반영되어 있으므로, 지금의 DeviceAgent 소스를 수정 전 원본으로 비교하지 않습니다. 저장 실패 보고 수정과 독립 관측 연결은 서로 다른 변경 묶음입니다.
새로 연결한 경로: AMRAgent CallbackHandler → AmrPowerObservationRuntime.receiveMapStorage → Host amrMotionSnapshot → FrameworkAmrMotionSnapshot.MapStorageObservation → 시험앱 amrMotionSummary. 이 경로에서 DeviceAgent는 필요하지 않습니다. 파일을 저장하는 실제 책임은 AMRAgent에 남아 있고, 업로드·제품 복귀·지도 생성 완료 처리는 이 관측 경로에 붙이지 않았습니다.
| 입력/상황 | 현재 분기 | 시험앱·후속 실행에 미치는 영향 |
|---|---|---|
result=true, 정상 출처·순번·시각 |
파일 처리 성공 관측을 보존 | 저장 성공 표시; Task 성공·업로드 성공으로 승격하지 않음 |
result=false, 정상 출처·순번·시각 |
파일 처리 실패 관측을 보존 | 실패를 실제 값으로 표시; 결과를 기다리는 중으로 숨기지 않음 |
stage=onProgress |
진행 파일 보고로 표시 | 최종 지도 저장으로 해석하지 않음 |
stage=onCompleted |
최종 파일 보고로 표시 | 이름에 Completed가 있어도 모터 정지·Task 완료 근거가 아님 |
| stage 없음 또는 알 수 없는 정상 문자열 | UNKNOWN으로 표시 |
임의로 진행/최종 단계를 추정하지 않음 |
| 정상 출처의 더 최신 순번이지만 result 타입 오류 또는 stage 타입/길이 오류 | watermark를 전진시키고 이전 유효 저장값 제거 | 오래된 성공이 최신 결과처럼 남거나 역순 callback으로 복원되지 않음 |
| 출처·순번·시각 자체가 잘못됨 | 관측으로 채택하지 않음; 이 분기는 watermark도 변경하지 않음 | 위의 최신 payload 무효화 분기와 다름; 기존 값은 시간이 지나면 stale로 표시 |
| 이전 순번 또는 이전 시각 | 무시 | 최신 결과를 과거 성공/실패로 덮지 않음 |
| 같은 binding에서 생산자 epoch 충돌 | 현재 저장 관측 제거·해당 연결 관측 차단 | 서로 다른 생산자의 결과를 같은 흐름으로 섞지 않음 |
| 구독 승인 ACK 이전 callback | 공개하지 않고 대기; 승인 뒤 유효 결과 공개 | 승인되지 않은 구독 결과를 현재 상태로 보이지 않게 함 |
| 수신 후 5초 초과 | 값은 남기고 fresh=false로 표시 |
마지막 보고를 볼 수 있지만 현재 동작의 최신 결과라는 뜻은 아님 |
| 새 조회 때 이동 상태가 없음 | 저장 관측은 별도로 전달 가능 | 이동 관측 부재 때문에 실제 파일 저장 결과가 사라지지 않음 |
SDK 입력에 physicalComplete=true가 포함됨 |
잘못된 계약으로 거절 | 파일 결과를 물리 완료로 위장한 값을 소비하지 않음 |
정상 예: 순번 41의 onProgress/true 뒤 순번 42의 onCompleted/true를 받으면 최종 파일 성공 보고를 표시합니다. 그 뒤 Task를 자동 성공 처리하거나 다음 이동을 시작하지 않습니다.
실패 예: 순번 42 성공 뒤 순번 43의 result가 문자열 "true"이면 최신 payload는 잘못된 타입입니다. 이전 성공은 제거합니다. 순번 42가 다시 도착해도 watermark 때문에 이전 성공을 되살리지 않습니다. 반면 순번 자체가 누락된 메시지는 신뢰 가능한 최신 보고로 볼 수 없어 동일한 무효화 처리를 하지 않습니다.
연결 복구 예: 새 observer에게 마지막 결과를 재전달할 수 있습니다. 이는 조회용 캐시 전달이며 지도 생성·파일 저장·업로드 명령 재실행이 아닙니다. 캐시에는 실행 ID가 없으므로 새 Task의 결과로 연결하지 않습니다.
완료 판정: saved는 파일 처리 결과, correlation=UNCORRELATED, physicalComplete=false입니다. 현재 코드에는 이 결과와 특정 지도 생성 Task를 연결하는 실행 ID가 없습니다. Task·물리 정지·업로드 완료·제품 복귀는 각각 별도의 실제 근거가 필요합니다.
검증 기준: skix-amr-map-storage-observation-evidence-2026-10-15.json에 변경 전/후 5개 소스 SHA와 실제 RED·영향 회귀·Android 컴파일·독립 검토 기록이 있습니다. 기록된 관련 회귀는 30 PASS 및 별도 배터리 runtime 4 PASS입니다. 이 문서 보강에서 재실행한 테스트로 합산하지 않습니다. APK 설치·실기기 저장·SELinux·서버 업로드는 미검증입니다.
현재 이관 공백: DeviceAgent MapUploader의 파일 선택·좌표·키/업로드 설정·외부 UploadService 호출, IoT의 업로드 결과 응답, 저장 후 제품 복귀는 후속입니다. 클라우드 지도 start/stop/end의 DeviceAgent 진입 의존은 후속 B-PLACE-02에서 로컬 이관했습니다. APK·서버·실기기 검증과 최종 완료 응답은 남아 있습니다.
사례 G · 클라우드 지도 요청을 다시 받거나 취소할 때 — B-PLACE-02
검토할 변경의 성격: DeviceAgent 호출 경로 이동뿐 아니라, 중복·응답 유실·재시작·취소 대상·MQTT 응답 의미가 달라졌습니다. 아래는 현재 소스의 동작이며 제품 승인 완료를 뜻하지 않습니다.
기존 경로: MQTT mapping → IoT Util/Gateway → DeviceAgent readiness 및 기존 map 명령 → AMR. 기존 명령 전달과 파일 생성·업로드 보고가 같은 기능 흐름 안에 있지만, 명령 반환 자체가 실제 완료 근거는 아닙니다.
현재 경로: MQTT CmdTopic.subscribeMappingData → 3인자 Util.controlMapping → SkixDeviceGateway → 기존 FrameworkStateClient 연결 → 공개 movement/Task API → Host 지도 큐 → managed AMR provider. 기존 2인자 직접 API는 보존하되, 이 MQTT 경로에서 실패 시 DeviceAgent로 자동 우회하지 않습니다.
| 순서 | 현재 실제 처리 | 실패·제품 영향 |
|---|---|---|
| 1. 입력 | 요청별 local correlation을 추출. null/공백/문자열 null은 응답 대상 없이 종료. worker에서 최대 256자 검사 | 이전 요청 ID로 잘못 응답하지 않음. 길이 초과는 유효 local ID가 있으면 실패 응답 |
| 2. 요청 식별 | 원문 correlation의 UTF-8 SHA256으로 start/stop별 안정 ID 생성 | 같은 ID는 같은 요청으로 취급. 공백을 제거한 별도 ID로 정규화하지 않음 |
| 3. 직렬 처리 | mappingLock으로 시작·중단 처리 직렬화, 현재 binding/binder/epoch 캡처 | 앱 내부 경합은 줄이지만 native 물리 동작의 원자성을 보장하는 lock은 아님 |
| 4. 기존 요청 확인 | journal에 같은 start가 있으면 저장 taskId 또는 최근 100개 중 requestId+amr.mapping으로 조회 | mode 충돌·결과 없음·중복 식별은 거절. 찾지 못했다고 새 동작을 보내지 않음 |
| 5. 새 시작 허용 | 이전 active Task가 terminal인지 확인. 적용된 제품 mode와 최근 5초 LLM IDLE 확인 | 누락·busy·미래 시각·stale이면 시작 거절. Host의 배터리·자원·정리 조건은 별도로 유지 |
| 6. 요청 선저장 | private SharedPreferences에 active/request mode를 동기 commit한 뒤 submitMapping | 저장 실패이면 RPC 없음. RPC 전후 연결이 바뀌면 결과 unknown, 자동 재전송 없음 |
| 7. Task 저장 | 반환 requestId 일치 확인 후 taskId commit | 서버가 접수했는데 로컬 저장에 실패할 수 있음. 실패가 미전송을 뜻하지 않음 |
| 8. stop/end | active에 연결된 정확한 Task 조회, stop ID→대상 taskId를 먼저 저장하고 cancelTask | cancel-all 없음. 같은 stop ID로 다른 Task 취소는 거절 |
| 9. 취소 재수신 | 이전 취소가 있으면 조회만. CANCELLING/terminal이면 상태 반환, RUNNING이면 unknown | 응답 유실 뒤 같은 취소를 자동 재전송하지 않음. 실제 정지 확인과 구분 |
| 10. MQTT 응답 | Task 접수/상태 설명과 local correlation 반환. filelist 제거, 접수만으로 Started 이벤트 없음 | result=true는 파일 생성·업로드 성공이 아님. backend가 이 의미를 받아들이는지는 미검증 |
구체적인 상황별 판정:
- 같은 start가 두 번 도착: 첫 요청의 journal이 있으면 Task를 조회합니다. 두 번째 모터 시작을 보내지 않습니다. 이미 terminal인 동일 요청도 새 실행으로 바꾸지 않습니다.
- 수동 start 이후 같은 correlation의 자동 start: 같은 요청에 mode가 달라졌으므로 거절합니다. 새로운 의도는 새 correlation이 필요합니다.
- submit 후 응답이 끊김: pending journal을 남깁니다. 재연결·앱 재시작 후 조회로 식별하고, 최근 100개 안에 찾지 못하면 unknown을 유지합니다. 이것은 기능 복구 완료가 아니라 중복 행동 방지입니다.
- start 접수 뒤 taskId 저장 실패: 이미 기기가 움직일 수 있습니다. 실패 응답만 보고 명령 미전송으로 판단해서는 안 됩니다.
- stop 후 응답이 끊기고 Task는 RUNNING: 같은 stop 재수신은 unknown을 반환합니다. 자동 retry하지 않으므로 명시 복구 절차가 필요할 수 있습니다.
- 이전 stop ID를 새 Task에 사용: target conflict로 거절하여 후속 동작을 잘못 중단하지 않습니다.
- Task가 CANCELLING: 취소 처리 중이라는 뜻입니다. 팬·모터 정지나 파일 저장 완료를 뜻하지 않습니다.
- pause/resume/save 요청: 이번 경로에서 명시적으로 미지원입니다. 기존 전체 지도 기능이 모두 이관됐다고 표시하지 않습니다.
보존한 값: manual=1/그 외 auto=2, delay=0, timeout=480000ms. 의도적으로 바꾼 규칙: 정확한 Task 취소, 요청 선저장, 불명확 결과 무재전송, 접수와 완료 응답 분리. 8분 timeout은 성공 판정이 아닙니다.
저장·복구 한계: journal은 skix-framework-mapping-v1의 active·mode·taskId·취소 대상입니다. 현재 오래된 요청을 자동 삭제하지 않습니다. 손상/조회 누락 시 자동 초기화·재실행도 하지 않습니다. 이 데이터는 OTA 기존 DeviceAgent 저장소 이관과 별개입니다. 장기 보관량·명시 복구 UX·서버 이력 만료 이후 동작은 추가 검토가 필요합니다.
소스 근거: IoT FrameworkStateClient.controlMapping/resolveMappingTask/requireMappingBinding, CmdTopic.subscribeMappingData/getMappingTaskAdmission, Util/Gateway 3인자 overload. 검증 정본: skix-iot-mapping-task-evidence-2026-10-15.json. 실제 ingress 및 수정 RED, 최종 영향 16 PASS, SDK 빌드와 IoT Java 컴파일, 독립 명세→품질 검토 기록입니다. 실제 Binder 왕복·APK 설치·native 행동·서버 해석·업로드는 이번 묶음에서 검증하지 않았습니다.
리뷰 때 결정할 점: backend result=true의 접수 해석, unknown 복구 방법, journal 보관 정책, pause/resume/save 단계별 호환, 실행별 저장/업로드 최종 이벤트 계약. 캐시된 MapData나 전역 MAP_STATUS를 현재 Task 완료로 붙여 해결하지 않습니다.
리뷰 판정표에 남길 실제 값
일괄 리뷰 때 ID별로 다음 값을 남깁니다. 현재 제품 승인 판정은 미검토이며, 아래 예시는 승인 결과가 아닙니다.
| 기록 칸 | 적어야 할 내용 | 예시 |
|---|---|---|
| 대장 ID·코드 기준 | 관련 ID, source SHA/commit+dirty 식별 | CROSS-06, mapping-final-admission evidence의 source SHA |
| 보존해야 할 제품값 | 경계값·enum·시간·순서 | 수동1/자동2, 표시 배터리27 |
| 실제 변경 | 기존/현재 조건 또는 효과의 차이 | SoundFlow 후 오류 owner/Task 상태 재검사 |
| 적용 경로 | standalone/legacy/direct/Task 중 실제 연결 범위 | Host mapping Task 경로 |
| 정상·실패 결과 | 무엇이 수락/실행/완료/불명확인가 | native 시작 차단, 준비 효과 불명확 유지 |
| 검증 수준 | 소스/Java/Android 컴파일/APK/실동작을 각각 기록 | Java·컴파일 확인, 실기기 미검증 |
| 결정·후속 담당 | 수용/수정 필요/보존 확인/이관 필요 및 담당 | 물리 정지 후 SoundFlow 복원 검토 필요 |
전체 항목 찾아보기
현재 77개 변경·비교 후보입니다. 아래 리뷰 칸은 사용자의 제품 동작 승인 여부입니다. 코드 명세·품질 검토 통과, APK 설치, 실기기 검증과 구분합니다. 이 수치는 전체 기능의 완료율이 아닙니다.
| 상세 ID | 확인할 기능/규칙 | 상세 기록의 분류·적용 상황 | 제품 리뷰 |
|---|---|---|---|
| CLEAN-01 | 청소 명령이 Task로 등록되고 요청자·requestId 기준으로 멱등화됨 | 실제 규칙 변화. 청소 start/stop/pause/resume/speed 명령 제출. | 미검토 |
| CLEAN-02 | 청소 명령 dispatch 실패를 자동 재시도하지 않고 UNKNOWN으로 보존 | 신뢰성 보강. 하드웨어 포트가 예외를 던지거나 응답이 사라지는 경우. | 미검토 |
| CLEAN-03 | 청소 stop/cancel은 소유한 hardware가 실제 비활성이라는 증거 전까지 FREE로 풀지 않음 | 신뢰성 보강. 취소, 실패, Host 재시작, 교체 명령. | 미검토 |
| CLEAN-04 | 청소 실행 수명과 단순 명령 acceptance를 분리하고 standalone Host에서 실제 연결 | 실제 계약 변화. basic cleaning start가 accepted 된 뒤 fan 상태/전체 청소 상태를 추적할 때. | 미검토 |
| CLEAN-05 | 청소 정책은 Framework에 추출되었고, legacy·standalone 양 경로에서 서로 다른 조립 경계를 가짐 | 이관 공백. | 미검토 |
| CLEAN-06 | 자동 tilt도 TaskManager queue와 terminal reason을 사용 | 실제 규칙/신뢰성 보강. cleaning tilt 실행·취소·실패. | 미검토 |
| AMR-01 | 직접 이동 호출에 유한 좌표 검사를 추가 | 실제 입력 규칙 변화. Framework가 moveTo를 직접 호출. |
미검토 |
| AMR-02 | 회전/이동 요청을 공용 execution lane과 owner/request dedup으로 직렬화 | 실제 규칙 변화. 두 caller가 동시에 같은 motor lane을 요청하거나 같은 requestId를 재전송. | 미검토 |
| AMR-03 | 이동 완료를 ACK가 아니라 arrival/position/station 관측 basis로 구분 | 실제 완료 판정 변화. | 미검토 |
| AMR-04 | 이동 owner/session generation이 바뀌면 이전 callback을 폐기 | 신뢰성 보강. AMR binding 재연결·재시작·오래된 callback. | 미검토 |
| AMR-05 | map command는 legacy Bundle 명령을 계속 소유하며 Framework map 객체는 보조 경계 | 단순 relocation/부분 이관. | 미검토 |
| TASK-01 | TaskManager가 owner·request·method·state를 공통 기록 | 실제 구조/신뢰성 변화. Framework endpoint가 task submit/claim/transition. | 미검토 |
| TASK-02 | Task cancellation은 owner/UID 범위로 제한 | 보안/권한 규칙 변화. | 미검토 |
| RESOURCE-01 | resource lease가 FREE/HELD/UNVERIFIED/RECOVERY_REQUIRED를 구분 | 실제 규칙 변화. fan/motor/camera 등 공유 하드웨어 acquire/release/restart. | 미검토 |
| RESOURCE-02 | capability readiness는 execution/resource 허가와 분리 | 실제 권한 모델 변화. | 미검토 |
| POWER-01 | 배터리 관측에 source timestamp/sequence/freshness를 부여하고 늦은 샘플을 거절 | 신뢰성/실제 관측 규칙 변화. AMRAgent BatteryStatus2 callback. | 미검토 |
| POWER-02 | PowerManager가 관측 observer ownership과 service lifecycle을 소유 | 실제 소유권/신뢰성 변화. | 미검토 |
| POWER-03 | 배터리 표시 보정표는 그대로 이동했고, admission/charging 의미는 추가하지 않음 | 단순 relocation(상수 보존). | 미검토 |
| POWER-04 | 배터리 사용량 산식은 보존되지만 저장/clock owner는 아직 legacy | 단순 relocation + migration gap. | 미검토 |
| POWER-05 | AMR charging command 자체는 아직 DeviceAgent AMR service에 남음 | migration gap. | 미검토 |
| POWER-06 | power observation direct AMR route와 legacy DeviceAgent route는 동시에 섞지 못함 | 실제 admission/reliability 규칙. | 미검토 |
| WSS-01 | 공통 WSS admission reason bit 집계 | 정책 규칙 변경 + 재배치 | 미검토 |
| WSS-02 | PIN credential text 비노출 | 정책 규칙 변경 + privacy/security 신뢰성 | 미검토 |
| WSS-03 | trigger vocabulary 고정 | 정책 규칙 변경 | 미검토 |
| WSS-04 | start 요청 owner/deadline/cancel 추적 | 신뢰성 변경 | 미검토 |
| WSS-05 | stop 대상 execution identity 일치 검사 | 신뢰성 변경 | 미검토 |
| WSS-06 | stationary watch가 장수 task/cleanup owner가 됨 | 신뢰성 변경 + 재배치 | 미검토 |
| WSS-07 | capture와 watch queue 분리 | 재배치 + 신뢰성 변경 | 미검토 |
| WSS-08 | face/PIN verification을 EventAsset owner에 결합 | 정책 규칙 변경 + 보안 신뢰성 | 미검토 |
| VOICE-01 | privacy/manner/voice setting은 legacy owner를 통해 유지 | 재배치 | 미검토 |
| VOICE-02 | LLM status를 content-free typed observation으로 투영 | 정책 규칙 변경 + privacy | 미검토 |
| VOICE-03 | TTS 요청이 Task와 provider dispatch로 분리 | 재배치 + 신뢰성 변경 | 미검토 |
| VOICE-04 | Managed TTS Binder relay가 signature/peer death를 검사 | 보안 신뢰성 변경 + 재배치 | 미검토 |
| VOICE-05 | TTS 완료 callback은 execution identity가 있어야 상관 | 신뢰성 변경 | 미검토 |
| VOICE-06 | TTS 문장 로그/장기 상태 노출 억제 | privacy 규칙 변경 | 미검토 |
| UI-01 | display state decision을 pure policy로 분리 | 현재 정책 추출·재배치 확인 / 역사적 행동 변경 미확정 | 미검토 |
| UI-02 | night mode brightness의 기존 값을 정책으로 재배치하고 readback을 보강 | 정책 보존 + 신뢰성 변경/재배치 | 미검토 |
| UI-03 | Eye LED 기존 값 경계를 runtime/policy로 재배치 | 정책 보존 가능성 + 재배치/신뢰성 변경 | 미검토 |
| UI-04 | display hold를 Binder token/UID owner로 제한 | 보안 신뢰성 변경 | 미검토 |
| UI-05 | ProductSystemUI top bar는 실제 SystemUI가 아닌 overlay owner | 재배치 + 마이그레이션 갭 | 미검토 |
| UI-06 | popup owner 사망/취소 시 negative 종료 | 신뢰성 변경 | 미검토 |
| UI-07 | SystemUI status state는 producer snapshot을 요구 | 신뢰성 변경 | 미검토 |
| UI-08 | Host의 display/night/LED/privacy 구성은 확인되며 교체·활성화 경계가 갭 | 재배치 + 마이그레이션 갭(생산자/활성화 경계) | 미검토 |
| B-IOT-01 | MQTT 시작과 부팅 서비스 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-IOT-02 | MQTT topic fan-in/fan-out | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-IOT-03 | MQTT 연결 상태와 stale callback | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-IOT-04 | IoT domain observation owner 교체 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-SET-01 | ProductSettings 저장 책임 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-SET-02 | 설정 consumer binding/reconnect | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-SET-03 | 초기 설정 read와 command retry 분리 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-PIN-01 | PIN credential 저장/검증 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-PIN-02 | PIN context와 map area 결합 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-MAP-01 | 지도 metadata 직접 조회와 초기 연결 실패 복구 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-MAP-02 | map delete의 부수효과 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-MAP-03 | 지도 파일 저장 실패를 성공·완료로 처리하지 않도록 변경 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-PLACE-01 | place catalog 조회 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-PLACE-02 | mapping command execution | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-REPORT-01 | cleaning report record/publish/save 순서 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-REPORT-02 | report 저장소 ownership | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-OTA-01 | UpdateEngine OTA lifecycle | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-OTA-02 | OTA result ingress and dedup | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-OTA-03 | OTA result uncertainty/no replay | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-OTA-04 | legacy AMR/MCU OTA sequencing | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-OTA-05 | OTA persistent state/data migration | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-BOOT-01 | UpdateAgent and AMRAgent boot behavior | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-BIND-01 | UpdateAgent Framework binding lifetime | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-STORAGE-01 | Preferences and encrypted map/report stores | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| B-SCOPE-01 | Remaining legacy cloud surface | gap (scope/control item, not a claimed regression). |
미검토 |
| B-SCOPE-02 | SET_APP_STATUS / MQTT producer 미위치 | gap(unconfirmed producer gap). 정책 변경이나 기능 삭제로 분류하지 않는다. |
미검토 |
| CROSS-01 | UV 주기는 유지하지만, 불명확한 명령 뒤 자동 ON 재시도를 막음 | 주기 정책 보존 + 실패 복구 규칙 변경. 제품 영향: 장애 후 자동 복구 대신 운영자 OFF가 필요할 수 있다. 부팅 자동 활성화의 기존 제품 요구와 일치하는지 리뷰 필요. | 미검토 |
| CROSS-02 | 수동 일시정지 의도와 시스템 일시정지 사유를 분리 | requester별 중재·수동 의도는 보존 대상, owner/fresh-stop 조건은 강화된 실행 규칙. 기존 모든 경로가 같은 의미였다고 단정하지 않는다. | 미검토 |
| CROSS-03 | 야간 해제 시 센서 보정: 이력 없는 상태를 임의로 만료 처리하지 않음 | 관측/이력 부족 처리 강화 + 데이터 이관 공백. 제품 영향: 야간 해제 때 보정이 생략되는 원인을 설명할 수 있지만, 기존 장치의 주기를 이어받으려면 이관 설계가 필요하다. | 미검토 |
| CROSS-04 | 예약 시각 비교를 단말 locale과 분리 | locale 의존 제거라는 실제 표현/실행 조건 변경 + 주간 계산 이동. 비ASCII 숫자를 쓰는 locale에서 예약 누락 동작이 달라질 수 있다. | 미검토 |
| CROSS-05 | 공기질 보정 산식과 민감도는 제품 정책으로 보존 | 산식 추출/보존 대상. 이번 조사만으로 모든 과거 버전·센서 종류의 수치 동등성을 확정하지 않으며, '공기질 알고리즘을 새로 바꿨다'고 계산하지 않는다. | 미검토 |
| CROSS-06 | 지도 생성: 기존 배터리 기준은 유지, 정리 확인과 실행 소유권을 추가하는 중 | 상세 항목에서 보존/변경/공백 확인 | 미검토 |
| CROSS-07 | 오류 해제는 다른 활성 오류의 제동·음성·틸트 차단을 풀면 안 됨 | 일부 기존 오류 정책 추출 + 다중 오류 해제의 교차 영향 검토 항목. 이번 비교에서 제동 aggregate guard의 역사적 도입 revision까지 특정하지 않았으므로 확정된 '이번 변경'으로 단정하지 않는다. | 미검토 |
| CROSS-08 | 사용 시간·청정 보고 시간: 실제 실행 시간과 제품 집계값을 구분 | 산식/저장 순서 보존 + 관측 UI 계약 명확화. 실제 19초 실행과 보고 60초 집계를 같은 값으로 수정하지 않는다. clock rollback/overflow도 무단 수정하지 않는다. | 미검토 |
사례 H · 지도 저장 실패가 성공이나 무응답으로 보이던 경우 — B-MAP-03
비교 기준: 이 사례는 ZIP 저장·수신 실패 수정 직전 보존본과 현재 소스의 차이입니다. 전체 리팩터링 전 원본의 모든 동작을 검증한 것은 아닙니다. 변경 전후 SHA와 로그는 skix-zip-map-storage-result-evidence-2026-10-15.json, skix-amr-map-receive-failure-evidence-2026-10-15.json에 기록합니다.
기존에 문제가 발생하는 순서: AMR 메시지 수신 → 길이와 무관하게 앞 64자 로그 추출 → ENC/ZIP 파싱·저장 → 성공 callback. 짧은 메시지는 파싱 전에 예외가 났습니다. ZIP 저장에서는 실제 gz2EncryptedEntryFile이 boolean을 반환하는데도 반환값을 버렸고, 서비스가 둘 다 없을 때도 함수 마지막 true에 도달할 수 있었습니다. 결과적으로 “호출하지 못했음”이나 “서비스가 실패를 반환함”이 성공으로 보일 수 있었습니다. 파싱/저장 실패에서 결과 보고를 생략하는 분기도 있었습니다.
현재 실행 순서: 수신 switch는 null 여부와 문자 수만 기록 → 파싱 → 기존 checksum 검사 → Base64 → gzip → tar → 현재 archive의 PGM/YAML 존재 검사 → 현재 암호화 서비스 한 번 확보 → 필요 시 기존 Setting fallback → FD 생성 → 서비스 한 번 호출 → 실제 boolean 반환 → FD 닫기 → 수신 handler가 파일 결과 관측 전달. 저장 작업을 자동으로 재시도하지 않습니다.
| 조건 | 수정 직전 문제 / 기존 처리 | 현재 처리 | 후속 동작에 미치는 영향 |
|---|---|---|---|
| 64자보다 짧은 ENC/ZIP 문자열 | 로그 substring에서 파싱 전에 예외 가능 | 문자 수만 기록하고 handler까지 전달 | 잘못된 입력을 처리할 기회를 유지; 실제 다음 switch 입력 도달 회귀 확인 |
| 수신 문자열 null | handler 자체는 결과 없이 반환; 앞 로그에서 예외 가능 | switch 로그는 안전, handler는 결과 없이 반환 유지 | null을 새 요청 실패로 지어내지 않음; 모든 무응답이 제거된 것은 아님 |
| 파서가 null 반환 또는 파싱 예외 | 실패 보고 없이 끝나는 경로 | 파일 경로 없이 MapData/result=false 관측 시도 | 최근 파일 실패를 조회할 수 있음; 요청별 완료 응답은 아님 |
| ZIP archive에 PGM 또는 YAML 없음 | 저장 요청을 진행하거나 이전 객체의 파일명이 남을 수 있음 | 이번 파일명을 초기화하고 둘 다 있어야 provider 호출 | 잘못된 파일쌍으로 저장 시작 금지; 이전 이름을 이번 성공으로 사용하지 않음 |
| manager 서비스 없음, Setting도 없음 | 호출 없이 true가 될 수 있음 | FD 생성 전에 false | 서비스가 없는 상태를 저장 성공으로 표시하지 않음 |
| manager 서비스 정상 | 서비스 조회를 반복 | 확보한 동일 service로 한 번 호출 | 호출 직전 다른 조회값으로 대상이 바뀌는 문제 축소; 원격 장애 자체는 남음 |
| manager 서비스 없음, Setting 서비스 정상 | fallback 사용 | 기존 우선순위 유지, 확보한 fallback으로 호출 | 제품별 fallback 호환성 유지 |
| FD 생성 결과 null | 유효 FD 없이 호출 위험 | false, provider 호출 없음 | 저장 미실행과 저장 성공을 구분 |
| provider가 false 반환 | 반환값을 버리고 true 가능 | false 그대로 반환 | 실제 서비스 실패를 파일 실패 관측에 전달 |
| provider가 true 반환 | 성공으로 반환 | true 그대로 반환 | 정상 경로 보존; 디스크 원자성·물리 완료까지 증명하지 않음 |
| provider 호출 예외 | 실패 반환이 가능해도 FD 해제 보장 부족 | false, finally에서 FD close 시도 | 재호출하지 않음; 부분 저장 여부는 확인되지 않음 |
| FD close에서 IOException | 명시 수명 정리 부족 | 경고 기록, 이미 받은 provider 결과는 유지 | 정리 오류와 파일 결과를 같은 실패로 뒤집지 않음 |
| ENC의 PGM 또는 YAML rename 실패 | 두 rename 결과를 성공 판정에 반영하지 않는 문제 | 둘 다 시도, 둘 다 성공해야 true; 실패 시 파일 경로 생략 | 부분 교체를 정상 파일쌍으로 전달하지 않음; 이미 이동한 파일 rollback은 없음 |
| 결과 전달 callback이 예외 발생 | 예외 후 반대 결과나 재호출 위험 | 성공/실패 결과 전달 시도 후 추가 결과를 만들지 않음 | callback 장애가 파일 재저장·두 번째 결과로 이어지지 않음; 전달 성공은 보장하지 않음 |
정상 예: PGM/YAML이 들어 있는 정상 ZIP, 현재 서비스, 정상 FD, provider=true이면 파일 처리 true를 보고합니다. 이는 그 호출의 서비스 반환값을 반영한 것이며, 특정 지도 생성 Task의 성공이나 S3 업로드 완료가 아닙니다.
실패 예: archive가 정상이어도 provider=false이면 false를 보고합니다. 암호화 서비스가 모두 없으면 FD를 만들거나 저장 호출을 보내지 않습니다. 암호화 파일 두 개 중 하나만 rename에 성공해도 실패이며, 소비자가 완전한 파일쌍으로 사용하지 않도록 결과에 경로를 넣지 않습니다.
복구와 남은 위험: 서비스 복구 후 새 명시 요청을 보낼 수 있지만 이전 저장을 자동 재전송하지 않습니다. 이미 부분 변경된 파일 복구·같은 입력의 원자적 저장·정확한 요청별 결과 식별은 구현 완료로 기록하지 않습니다. 조회용 false 관측이 생겨도 IoT 요청에 최종 실패 응답이 자동 전송된다는 뜻은 아닙니다.
검증 범위: ZIP·수신·관측·SDK/시험앱 영향 회귀의 당시 최종 기록은 17 PASS와 AMR Java 컴파일 PASS, 독립 명세→품질 검토입니다. 실제 저장 메서드·switch 분기를 사용하지만 archive/암호화 서비스/FD 경계 일부는 대역입니다. 이번 문서 작업에서는 해당 테스트를 반복 실행하지 않았습니다. 실제 기기 provider 저장, APK 설치, 서버 업로드, 제품 복귀 및 제품 승인은 별도 확인 대상입니다.
사례 I · 지도 업로드 두 요청이 겹치거나 암호화 서비스가 끊길 때 — B-MAP-03
대상과 범위: IoT S3TransferManager.decryptUploadFile(presignedUrlString, filePath, type)의 keystore 복호화 업로드 경로입니다. 지도 생성·파일 저장·HTTP 업로드·MQTT 응답은 각각 별도 단계입니다. 이번 수정으로 모든 단계를 독립 실행하게 된 것은 아닙니다.
변경 전: 복호화한 바이트를 서비스 객체의 공용 필드에 보관했습니다. 이전 PGM 업로드가 실패한 뒤 다음 YAML 요청에서 암호화 서비스가 없으면 이전 바이트가 남아 잘못 쓰일 수 있었습니다. 두 요청이 겹치면 다른 요청이 공용 버퍼를 덮거나 비워 첫 요청의 파일이 바뀌거나 실패할 수 있었습니다. 서비스 조회를 반복하고 늦은 bind callback을 사용하므로 요청 종료와 복호화 결과의 수명이 일치하지 않았습니다.
변경 후 실제 순서: 입력 null/빈 값 검사 → 현재 암호화 서비스 한 번 확보 → 지정 파일 복호화 FD 요청 → 이 요청의 지역 바이트 배열로 읽기 → FD 닫기 → PUT 연결 및 대기 제한 설정 → 이 요청의 바이트 전송 → HTTP 상태 판정 → 출력 스트림과 연결 정리. 서비스가 없으면 false를 반환하며 이 메서드에서 bind하거나 나중에 같은 행동을 다시 보내지 않습니다. IoT 부팅 초기화의 기존 서비스 binding은 유지합니다.
| 조건 | 변경 전 문제 / 확인 범위 | 현재 반환·처리 | 남는 영향 / 확인할 사항 |
|---|---|---|---|
| URL·파일 경로·MIME가 null 또는 빈 값 | 유효하지 않은 입력에서도 복호화를 시도할 수 있음 | 복호화 전에 false | 상위 MQTT 호출자가 실패 응답을 끝까지 보내는지는 별도 |
| 현재 암호화 서비스 없음 | 이전 요청 바이트 잔존·늦은 callback 위험 | false, 새 bind·명령 replay 없음 | 서비스 복구 뒤 새 명시 요청이 필요함 |
| 확보 후 manager의 서비스 참조 변경 | 다시 조회한 참조가 null이면 예외·혼선 가능 | 확보한 service를 이 호출 안에서 사용 | 원격 provider 자체가 죽으면 예외→false; native 동작 검증 별도 |
| 복호화 FD가 null 또는 복호화 예외 | 정상 파일 확보 여부가 불명확 | false, PUT 미시도 | native 저장·암호화 실패 원인은 별도 조사 |
| PGM과 YAML 업로드가 겹침 | 공용 byte[]를 서로 덮거나 제거 | 각 호출의 지역 byte[]로 해당 파일 전송 | 두 파일이 같은 지도 버전이라는 원자적 보장은 아직 없음 |
| HTTP 200 | 기존 성공 규격 | true 유지 | 서버가 파일 의미까지 승인했다는 증거는 아님 |
| HTTP 403 등 200 이외 | 기존 실패 규격 | false, HTTP 코드만 기록 | 첫 파일만 성공한 부분 업로드의 원복/재시도 정책은 별도 |
| 연결 또는 응답이 지연됨 | 이 메서드에 명시 timeout 없음 | 연결 15초·읽기 30초 제한 추가 | 전체 작업 45초 보장 아님; Binder 복호화·쓰기 시간의 총 deadline은 별도 |
| I/O·원격 호출·실행 예외 | 예외 메시지에 서명 URL 등이 들어갈 위험 | false, 예외 클래스명만 기록 | 다른 메서드와 전체 앱 로그에 같은 보호가 적용됐다는 뜻은 아님 |
| 요청 종료 | FD/HTTP 수명 정리 보강 필요 | FD 자동 close·출력 close·finally disconnect | 실제 Android provider/HTTP 리소스와 서버는 실환경 확인 필요 |
보존한 규격: PUT, 기존 Host 헤더 계산, Content-Type, Content-Length, HTTP 200 성공 조건. 새로 달라진 규칙: 요청별 데이터 소유권, 연결 누락의 즉시 실패, 이 경로의 늦은 bind/replay 제거, 입력 검사, timeout, 오류 로그 내용과 자원 정리. HTTP 라이브러리 내부 redirect/retry까지 금지됐다고 주장하지 않습니다.
코드 근거: apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/mqtt/manager/S3TransferManager.java, decryptUploadFile; 검증 정본 skix-iot-map-upload-isolation-evidence-2026-10-15.json에 변경 전·후 SHA와 로그를 고정했습니다. 실제 메서드를 추출한 호스트 회귀의 경계 대역은 native keystore·FD·HTTP입니다.
기록된 검증: 변경 전 행동 실패 5개 재현 → 최종 회귀 5개 통과, 독립 명세→품질 검토, IoT Java 단위 컴파일 통과. 이번 문서 갱신에서 테스트·빌드를 다시 실행한 결과가 아닙니다. 첫 컴파일의 class 접근 ENOMEM 실패와 단위 재실행 성공도 정본에 함께 남아 있습니다.
여전히 미완료: Util.requestMapUpload의 DeviceAgent gateway 준비 조건, native 요청별 파일 결과 식별, PGM/YAML 동일 버전 snapshot, AES128 임시 평문 파일 경합, 실제 서버/MQTT 최종 응답, 제품 복귀, 최신 APK·기기 인수. “한 파일 PUT 성공”을 지도 생성 Task 완료로 판정하지 않습니다.
2. 청정·이동·Task·자원·배터리
사례 J · DeviceAgent 없이 지도 업로드를 요청할 때 — B-MAP-03
무엇이 달라졌나: MQTT 지도 업로드 요청이 더 이상 DeviceAgent의 준비 검사와 REQMAP callback을 기다리지 않습니다. IoT가 공개 Framework AMR 서비스에 연결하여 최신 파일쌍을 읽고 기존 서버 URL에 업로드한 뒤, 기존 replyTo·correlationId로 최종 응답을 한 번 시도합니다. 기존 호환 callback과 파일 업로드 API는 다른 소비자를 위해 남겨 두었습니다.
기존 실제 경로: MQTT → Util.requestMapUpload → SkixDeviceGateway.isReady() → DeviceAgent requestMapUpload/REQMAP → callback → 기존 지도 파일 업로드. DeviceAgent 준비 검사에서 종료되면 이 진입부가 실패 응답을 보내지 않았습니다. 공용 파일에서 읽은 PGM과 YAML이 같은 관측 버전인지도 이 진입부에서 확인하지 않았습니다.
현재 실제 경로: MQTT → Util.requestMapUpload의 작업자 → SkixDeviceGateway.readFrameworkMapSnapshot → FrameworkAmrMapClient → 공개 SkixAmrManager → AMR Binder/Host → 현재 AMR 파일 관측과 원본 FD → SDK의 소유된 PGM/YAML 쌍 → S3TransferManager.uploadMapBytes 두 번 → ResponsePublishHelper.publishMapUploadResponse.
| 순서 | 실제 처리 | 새로 생긴 제한 / 보존한 규칙 |
|---|---|---|
| 1. 요청 검사 | 두 업로드 URL과 현재 transfer manager 확인 | 누락이면 GET·HTTP 없이 실패 응답 시도 |
| 2. 작업 접수 | 동시에 작업자 2개, 대기 큐 없음 | 과부하면 실패; 무제한 적재·자동 재전송 없음 |
| 3. 기준 관측 고정 | 현재 AMR 연결, producer epoch·sequence·binding generation 확보 | 기준 없음/잘못된 메타데이터면 GET 전에 실패 |
| 4. 파일 요청 | 공개 SDK로 GET 1회 | 접수 true는 파일 생성·저장 완료가 아님 |
| 5. 새 관측 대기 | 요청 이후 시각, 동일 epoch/generation, 더 큰 sequence 확인 | polling 5초; 같은 순번·다른 생산자 결과를 새 결과로 수락하지 않음 |
| 6. 파일쌍 읽기 | 저장 성공·fresh·export 가능 관측의 FD를 SDK에서 검증 | 이전 연결 결과 폐기; 합계 16MiB, 동일 관측 파일쌍 |
| 7. PGM/YAML PUT | plain은 그대로, AES128은 호출별 복호화 바이트 사용 | ZIP은 Framework에서 plain 쌍으로 변환; 기존 MIME/PUT/HTTP 200 조건 유지 |
| 8. 결과 응답 | 두 HTTP 호출이 모두 true일 때 success | replyTo/correlationId 보존; publish 예외에 자동 재발행 없음 |
| 상황 | 현재 결과 | 리뷰에서 확인할 제품 영향 |
|---|---|---|
| DeviceAgent 없음 | 이 경로는 DeviceAgent를 조회하지 않음 | Framework Host와 AMR provider는 필요함 |
| AMR binding 없음·종료·교체 | 실패, 이전 연결 결과 사용하지 않음 | 재연결은 지원하되 이전 GET은 재전송하지 않음 |
| 기준 관측 없음 | GET 미전송, 실패 | 초기 연결 직후 사용자는 관측 준비 후 새 요청 필요 |
| GET 명시 거절 | export·HTTP 미시도, 실패 | 같은 요청 자동 재시도 없음 |
| 새 관측 없음 | 5초 관측 대기 후 실패 | GET 실제 수행 여부는 unknown; 전체 작업의 5초 종료 보장은 아님 |
| 새 저장 결과 false | 실패, 파일 전송 안 함 | 저장 실패와 서버 실패를 내부 단계에서 구분해야 함 |
| 첫 파일만 성공 | 둘째 호출도 시도하고 최종 false | 서버의 부분 업로드를 자동 원복하지 않음; 두 PUT은 서버 트랜잭션이 아님 |
| AES128 설정·복호화 누락/실패 | 해당 PUT false | 암호문을 평문인 것처럼 보내지 않음 |
| HTTP 200 외·I/O·실행 예외 | false; 서명 URL 대신 예외 클래스명 로그 | 연결 15초·읽기 30초; 전체 실행/쓰기의 총 deadline은 별도 |
| 응답 publish 실패 | 한 번 시도 후 종료 | 서버가 MQTT 응답을 받았다는 보장은 아님 |
완료 근거를 혼동하지 않기: GET 접수 → 새 저장 관측 → 검증한 파일쌍 → HTTP 200 두 건 → MQTT 발행 시도는 서로 다른 단계입니다. 현재 AMR 관측에는 native GET 요청 ID가 없어, 다른 동시 GET 결과와의 정확한 요청별 상관은 증명하지 못합니다. 같은 생산자의 최신 파일을 확보하는 계약입니다. 지도 생성 Task의 SUCCEEDED, 실제 모터 정지, 제품 화면 복귀로 승격하지 않습니다.
시험앱 후속 (소스 구현·로컬 검증; APK/기기 미검증): 기존 GET 버튼과 별도로 “현재 지도 파일쌍 확인” 버튼을 연결했습니다. 현재 저장 관측을 공개 AMR SDK에 전달해 실제 PGM/YAML 바이트를 읽고 이름·크기·SHA-256·encoding·epoch·순번·부팅 기준 관측 시각을 표시합니다. 파일 생성 명령이나 서버 전송은 없습니다. AES128은 암호문 그대로 표시하며 평문 검증으로 부르지 않습니다. 동일 관측 자료 확인은 native GET 요청별 상관/Task 완료가 아닙니다. 실제 연결 종료 후 새 조회가 가능하고, 이전 UI 응답은 새 요청의 busy 상태를 해제하지 않습니다. 검증 정본은 skix-map-file-test-app-evidence-2026-10-15.json입니다.
실제 수정 위치: apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/ 아래 infrastructure/device/FrameworkAmrMapClient.java, infrastructure/device/SkixDeviceGateway.java, util/Util.java, IotAgent.java, mqtt/manager/S3TransferManager.java의 해당 메서드입니다. 공개 모델/Manager는 skix-framework/base/core/java/com/sk/intellix/framework/에 있습니다. 문서 작성 시 경로는 소스와 대조합니다.
검증과 남은 작업: 실제 client와 업로드/진입부 메서드를 실행한 영향 25 PASS, baseline 누락 회귀 RED→GREEN입니다. Android binding·SDK IPC·HTTP·MQTT 경계에는 대역을 사용했습니다. SDK AAR를 IoT 소비자로 갱신했습니다. 현재 묶음의 최종 빌드·독립 품질 검토 기록은 skix-iot-canonical-map-evidence-2026-10-15.json에 보관합니다. APK 설치·실기기·실제 S3/MQTT 서버·native 요청 상관·부분 업로드 원복은 미검증/미완료입니다.
CLEAN-01 — 청소 명령이 Task로 등록되고 요청자·requestId 기준으로 멱등화됨
- 분류/트리거: 실제 규칙 변화. 청소 start/stop/pause/resume/speed 명령 제출.
- 기존:
CmdPolicyManager가 조건 검사 후newClean/stopAllAction을 직접 호출하는 정책 중심 경로였다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/policy/CmdPolicyManager.java:383-388,448-474). 동일 요청의 Task ID/재제출 멱등 계약은 이 경로에 없다. - 새 동작:
CleaningCommandTaskQueue.submitWithContext가 owner+requestId를 key로 저장하고 같은 action/speed면 기존 ID를 반환하며, 다른 action/speed 재사용은 거절한다. action 범위, speed 범위, Host limit 100도 등록 시 검사한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/CleaningCommandTaskQueue.java:80-123). - 이유: 검증된 사실은 명령 수명과 중복 dispatch를 TaskManager로 관리하려는 구현이다. “중복 요청이 제품에서 실제로 발생했다”는 원인은 추론이다.
- 제품 영향/보존: 기존 action 의미와 기본 speed=1은 보존된다 (
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/CleaningCommandTaskQueue.java:42-48,90-97). 호출자는 즉시 물리 완료가 아니라 Task/acceptance 결과를 받게 된다. - 검증/공백:
CleaningFanTaskQueueTest및 TaskManager 테스트가 멱등/limit을 검증한다. 기존 cloud→DeviceAgent 전체 경로와 실기기 명령 결과는 미검증.
CLEAN-02 — 청소 명령 dispatch 실패를 자동 재시도하지 않고 UNKNOWN으로 보존
- 분류/트리거: 신뢰성 보강. 하드웨어 포트가 예외를 던지거나 응답이 사라지는 경우.
- 기존:
AirCleaningManager.stop()은 Handler callback을 제거한 뒤DeviceCleaning.stopFan()을 호출한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/cleaning/AirCleaningManager.java:88-97). 예외/응답 유실을 Task 상태로 보존하는 공통 계약은 없다. - 새 동작: Queue는 trusted
ExecutionDispatchRejectedException만 REJECTED로 확정하고, 그 밖의 RuntimeException/transport failure는 UNKNOWN으로 남긴다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/CleaningCommandTaskQueue.java:179-184). accepted observation 실패도 명령 재전송을 만들지 않는다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/CleaningCommandTaskQueue.java:170-174). - 이유: 재전송으로 팬/이동이 중복 실행되는 위험을 줄이는 명시적 계약.
- 제품 영향/보존: 성공 판정은 포트의
ACCEPTED에만 묶인다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/CleaningCommandTaskQueue.java:157-178). 물리 청소 완료는 여전히 별도 execution monitor가 필요하다. - 검증/공백: Queue 테스트에 observer failure/unknown 경로가 있다. 실제 MCU/AMR transport의 예외 분류가 해당 예외 타입을 사용하는지는 미검증.
CLEAN-03 — 청소 stop/cancel은 소유한 hardware가 실제 비활성이라는 증거 전까지 FREE로 풀지 않음
- 분류/트리거: 신뢰성 보강. 취소, 실패, Host 재시작, 교체 명령.
- 기존:
AirCleaningManager.stop()은stopFan()호출 직후 반환하고, callback 제거가 stop 확인인지 구별하지 않는다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/cleaning/AirCleaningManager.java:88-97). - 새 동작: Resource/cleaning runtime에는
RECOVERY_REQUIRED,UNVERIFIED,releaseConfirmedInactive가 분리되어 있고, 확인된 inactive 관측에서만 FREE로 전환한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/control/resource/ResourceManagerService.java:88-115,132-161). 팬 Queue도 failed/replacement 뒤 reconcile stop을 별도 Task로 둔다 (skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/domain/cleaning/CleaningFanTaskQueue.java:126-145,179-201). - 이유: timeout/ACK를 물리 정지 증거로 오인하지 않기 위한 설계 사실.
- 제품 영향/보존: stop 명령 자체는 유지되나 다음 작업 admission이 지연되거나 recovery가 필요할 수 있다.
- 검증/공백:
CleaningFanTaskQueueTest가 stop 대기/reconciliation을 확인한다. 실제 fan telemetry가 정확한 generation으로 연결되는지는 미검증.
CLEAN-04 — 청소 실행 수명과 단순 명령 acceptance를 분리하고 standalone Host에서 실제 연결
- 분류/트리거: 실제 계약 변화. basic cleaning start가 accepted 된 뒤 fan 상태/전체 청소 상태를 추적할 때.
- 기존 legacy 경로:
LegacyCleaningExecutionAdapter.startCleaning()은CmdPolicyManager.newClean의 정책 결과를 boolean으로 반환한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/cleaning/LegacyCleaningExecutionAdapter.java:63-71). 이 값은 acceptance이며 physical completion으로 해석하지 않는다. - 새 standalone Host 동작:
dispatchFrameworkBasicCleaningStart는 error admission, filter cover, battery capacity, fan readiness, replacement 가능성을 순서대로 확인한 뒤prepareHostBasicCleaning과prepared.startOwned()를 호출한다 (skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:9967-10018,10035-10066). accepted start callback은recordAcceptedBasicStart후startBasicLifecycle로 연결된다 (skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:10749-10754,10834-10842). - 실행 상태:
startBasicLifecycle가queue.beginBasicLifecycle(commandId, run.executionId)로 별도CleaningExecutionTask를 만들고, 500ms observation poll이 owned execution snapshot의 phase/physicalStopObserved/failure를 Task에 전달한다 (skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:10767-10802,10804-10831). Queue completion 자체는 여전히 physical cleaning completion이 아니다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/CleaningCommandTaskQueue.java:11-14). - 이유: standalone 경로에는 실제 lifecycle 조립이 확인되며, legacy adapter 경로와 동일하다고 일반화할 수 없다.
- 제품 영향/보존: standalone caller는 command Task와 execution Task를 모두 볼 수 있고, legacy caller는 기존 policy/adapter에 남는다.
executionId는 pause/recovery 동안 owner별로 유지된다 (skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:10087-10095). - 검증/공백: Host 소스와 cleaning queue/runtime 테스트로 연결을 확인했다. Binder/APK/실제 fan 물리 완료는 여전히 미검증.
CLEAN-05 — 청소 정책은 Framework에 추출되었고, legacy·standalone 양 경로에서 서로 다른 조립 경계를 가짐
- 분류/트리거: 이관 공백.
- 현재 legacy 경로:
AirCleaningPolicy가 lock, battery capacity, on-station, range를 legacy singleton에서 읽어CleaningOperationPolicy.evaluateLegacyAdmission에 전달하고 결과를 반환한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/cleaning/AirCleaningPolicy.java:61-112).LegacyCleaningExecutionAdapter는 실제 start를CmdPolicyManager.newClean에 위임한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/cleaning/LegacyCleaningExecutionAdapter.java:63-71). - Framework 정책 추출:
CleaningOperationPolicy는 lock/skip/return predicate와 fan product table/RPM tolerance를 제공하며, 주석상 legacy behavior를 보존하지만 resource lease나 execution completion을 부여하지 않는다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/policy/CleaningOperationPolicy.java:3-8,52-95,200-215). - standalone Host 조립: Host는
requireBasicCleaningErrorAdmission,basicCleaningCapacityAllowed, fanadmissionStatus()==READY, replacement 가능성을 직접 검사한 뒤 Basic runtime을 준비한다 (skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:9967-10018,10000-10007). 따라서 Framework direct route에는 실제 admission이 존재한다. - 이유: 정책 함수의 ownership은 Framework로 추출됐지만, 입력 사실의 공급과 전체 조립은 route별로 다르다. legacy route를 “정책이 adapter에만 남았다”고 표현하는 것은 부정확하다.
- 제품 영향/보존: fan table, tolerance, lock/return predicate와 battery low rejection 의미를 보존하려는 코드가 양쪽에 존재한다. 다만 legacy
CmdPolicyManager와 standalone Host의 모든 rejection reason이 완전히 parity인지 별도 비교가 필요하다. - 검증/공백:
CleaningOperationPolicyTest, Host source, DeviceAgent policy source가 근거다. Framework Binder direct caller와 legacy caller의 모든 edge condition/15% threshold parity 및 실기기 검증은 미완료.
CLEAN-06 — 자동 tilt도 TaskManager queue와 terminal reason을 사용
- 분류/트리거: 실제 규칙/신뢰성 보강. cleaning tilt 실행·취소·실패.
- 기존: DeviceAgent 소스 인벤토리에는 tilt가 청소 manager/legacy movement에 결합되어 있고, 단일 Handler 기반 fan lifecycle과 함께 처리된다 (
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/cleaning/AirCleaningManager.java:34-104). - 새 동작:
CleaningTiltTaskQueue는 TaskManager에 tilt task를 등록하고 stop/cancel/failed 상태를 terminal state로 남긴다. 관련 명시 테스트가CleaningTiltTaskQueueTest에 있다 (apps/SkixFrameworkService/app/src/test/java/com/sk/intellix/frameworkservice/lab/CleaningTiltTaskQueueTest.java:14-36및 후속 시나리오). - 이유: tilt를 fan과 같은 managed execution lane에 포함하려는 구현 사실.
- 제품 영향/보존: target tilt 값 자체는 기존 port에 전달되지만, 취소/실패가 즉시 다음 tilt 허용을 의미하지 않는다.
- 검증/공백: JVM queue 테스트만 확인; 실제 tilt HAL callback 및 installed APK 경로 미검증.
AMR-01 — 직접 이동 호출에 유한 좌표 검사를 추가
- 분류/트리거: 실제 입력 규칙 변화. Framework가
moveTo를 직접 호출. - 기존: 기존 command 경로는
MapCommandHandler가 Bundle/legacy parser로 method를 분기하고MovingController·AMR API로 전달했다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/map/MapCommandHandler.java:68-155). direct typed target 경계의 finite 검사 증거는 없다. - 새 동작:
LegacyAmrExecutionAdapter.moveTo는 null/NaN/infinity x,y,degree를 즉시 false로 거절하고, 이후에만 Position을 만들어 기존ExecutionPolicyGateway.admitMoveTo를 호출한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/movement/LegacyAmrExecutionAdapter.java:47-67). - 이유: legacy parsing 전에 typed boundary에서 잘못된 좌표를 차단.
- 제품 영향/보존: 유효 좌표와 기존 appCall/airSensor 정책은 보존 (
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/movement/LegacyAmrExecutionAdapter.java:53-56). - 검증/공백: adapter source/unit 근거. 실제 Binder caller의 malformed parcel은 미검증.
AMR-02 — 회전/이동 요청을 공용 execution lane과 owner/request dedup으로 직렬화
- 분류/트리거: 실제 규칙 변화. 두 caller가 동시에 같은 motor lane을 요청하거나 같은 requestId를 재전송.
- 기존:
LegacyAmrExecutionAdapter는 pause/resume/stop을RobotMovementController에 직접 전달하고, return은AMRMotor.returnToStation()또는 policy gateway를 즉시 호출한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/movement/LegacyAmrExecutionAdapter.java:21-44). - 새 동작:
AmrRotationTaskQueue는 owner/request 검증, 중복 방지, 단일 lane 제출과 소유자 취소를 담당하고 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/amr/execution/AmrRotationTaskQueue.java:287-355,418-462,582-584),ManagedMovementRuntime은 generation/attempt/target을 포함한 실행 시도와 observation 구조를 제공한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/amr/execution/ManagedMovementRuntime.java:29-73,118-120). 둘을 통해 TaskManager task와 movement 결과를 상관시킨다. - 이유: 공용 motor owner와 Task completion correlation을 확보하려는 구현.
- 제품 영향/보존: low-level motor 호출은
AmrExecutionPortadapter에 남고, pause/resume/stop 의미도 기존 controller에 전달된다. - 검증/공백: queue/runtime 테스트와 source evidence가 있으나 real AMR callback·물리 arrival은 미검증.
AMR-03 — 이동 완료를 ACK가 아니라 arrival/position/station 관측 basis로 구분
- 분류/트리거: 실제 완료 판정 변화.
- 기존:
LegacyAmrExecutionAdapter.moveTo의 반환값은admitMoveTo(...).isAllowed()이며, 이는 명령 허용 결과다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/movement/LegacyAmrExecutionAdapter.java:47-57). - 새 동작:
MovementExecutionResult는ARRIVAL_OBSERVED,POSITION_STABLE,STATION_CHARGING_OBSERVED,ALREADY_ON_STATION을 별도 basis로 만들고 source envelope가 관측과 정확히 일치하는지 검사한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/amr/execution/MovementExecutionResult.java:46-83,86-99). - 이유: 명령 acceptance/ACK와 실제 이동 완료를 분리.
- 제품 영향/보존: station return은 기존 port를 쓰지만 completion consumer는 관측 basis를 해석해야 한다.
- 검증/공백: JVM result/session 테스트 근거; 실제 pose timestamp/arrival event freshness 미검증.
AMR-04 — 이동 owner/session generation이 바뀌면 이전 callback을 폐기
- 분류/트리거: 신뢰성 보강. AMR binding 재연결·재시작·오래된 callback.
- 기존:
AMRService/callback 등록은 service binding과 callback lifecycle에 의존하며, 기존 DeviceAgent 테스트도 binding lifecycle을 별도로 다룬다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/device/item/amr/AMRService.java및apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/device/item/amr/AmrBindingLifecycle.java). - 새 동작:
ManagedMovementRuntime은 generation/attempt/target identity를 실행·관측에 보존하고 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/amr/execution/ManagedMovementRuntime.java:29-73),AmrObservationIngress.register/accept/takeAccepted는 등록된 attempt, ATTEMPT_BOUND envelope, generation/sequence/시간 순서를 검증하며 stale generation을 닫힌 결과로 거절한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/amr/observation/AmrObservationIngress.java:8-15,52-105,108-137). - 이유: 재연결 후 이전 이동 결과가 새 이동을 완료 처리하는 위험을 차단.
- 제품 영향/보존: 현재 callback source가 정상 generation이면 기존 AMR 상태를 계속 사용한다.
- 검증/공백: source/unit 근거; AMR service 재연결을 실제 기기에서 반복한 증거 없음.
AMR-05 — map command는 legacy Bundle 명령을 계속 소유하며 Framework map 객체는 보조 경계
- 분류: 단순 relocation/부분 이관.
- 기존:
MapCommandHandler가SET_START_MAPPING,DELETE_MAP,GET_LOCATION,SET_MAP등 method를 직접 분기하고 MQTT/AMRMap/MapManager를 호출한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/map/MapCommandHandler.java:68-155). - 새 동작: Handler는
MappingProgress,MapSynchronizationData,OnDeviceMapMetadataReader를 Framework 타입으로 주입받지만 실제 legacy command dispatch와 MQTT publish는 동일 클래스에 남아 있다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/map/MapCommandHandler.java:1-5,47-66,88-153). - 이유: 타입/관측 모델만 Framework로 연결되고 map business command owner는 아직 DeviceAgent.
- 제품 영향/보존: 기존 map method와 Bundle key semantics 보존.
- 검증/공백: source compile/test 근거; Framework public map manager가 모든 기존 map operation을 대체했다는 증거 없음.
TASK-01 — TaskManager가 owner·request·method·state를 공통 기록
- 분류/트리거: 실제 구조/신뢰성 변화. Framework endpoint가 task submit/claim/transition.
- 기존:
TaskExecutor는 단일AndroidCommandQueue에 integer taskId와 callback을 넣고(apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/ipc/TaskExecutor.java:38-47,65-82),removeTask/isTaskQueued/isTaskRunning범위에서만 상태를 노출한다. 공통 persistent Task record 경계는 이 API에 없다. - 새 동작:
TaskManagerService는 requestId 중복을 owner와 persisted history까지 확인해 admission하고 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/execution/task/TaskManagerService.java:153-192), 이후 claim/transition/history와 owned execution API를 제공한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/execution/task/TaskManagerService.java:195-254,287-359,410-469). Cleaning queue도 이 API를 사용한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/CleaningCommandTaskQueue.java:120-123,148-190). - 이유: cross-domain execution state와 cancellation을 공통화.
- 제품 영향/보존: 개별 legacy command는 adapter로 유지되지만 caller-visible state가 Task ID/state/history를 포함할 수 있다.
- 검증/공백:
TaskManagerServiceLifecycleTest, admission/history 테스트 존재. 기존 OTA/schedule DB와의 영속 이관은 미확인.
TASK-02 — Task cancellation은 owner/UID 범위로 제한
- 분류/트리거: 보안/권한 규칙 변화.
- 기존: DeviceAgent
stopAllAction은 policy 상태에 따라 전역 stop을 호출한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/policy/CmdPolicyManager.java:468-505). 개별 request owner 검사가 중심이 아니다. - 새 동작: Task endpoint/manager는
TaskOwner를 task에 보존하고 owned cancellation을 사용한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/execution/task/TaskManagerService.java:287-359,441-469). Cleaning test는 다른 owner가 cancel하지 못하고 동일 owner만 취소할 수 있는 계약을 다룬다 (apps/SkixFrameworkService/app/src/test/java/com/sk/intellix/frameworkservice/lab/CleaningFanTaskQueueTest.java:56-74). - 이유: client가 다른 caller의 Task를 종료하지 못하게 함.
- 제품 영향/보존: 기존 emergency/global stop 경로는 별도 유지되며, 일반 request cancel은 범위가 좁아진다.
- 검증/공백: Binder authorization과 실제 Android UID/package/signature 결합은 Framework test 범위 밖.
RESOURCE-01 — resource lease가 FREE/HELD/UNVERIFIED/RECOVERY_REQUIRED를 구분
- 분류/트리거: 실제 규칙 변화. fan/motor/camera 등 공유 하드웨어 acquire/release/restart.
- 기존: DeviceAgent는
stopAllAction과 각 manager의 상태/boolean 조합으로 공유 hardware를 조정한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/policy/CmdPolicyManager.java:473-531). 정확한 lease token/state machine은 없다. - 새 동작:
ResourceManagerService.acquire는 owner/requestId 멱등과 atomic multi-resource reservation을 제공하고, 일반 release는 UNVERIFIED, confirmed inactive만 FREE로 만든다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/control/resource/ResourceManagerService.java:59-115). restart/owner failure는 recovery로 격리한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/control/resource/ResourceManagerService.java:164-190,232-313). - 이유: stale owner가 새 owner 자원을 해제하는 문제를 방지.
- 제품 영향/보존: 동일 resource가 사용 중이면 새 작업은 unavailable로 거절되며, 재시작 후 재조정 없이는 자동 재사용되지 않는다.
- 검증/공백:
ResourceManagerServiceTest가 상태 전이를 검증. 실제 fan/AMR resource ID 매핑과 product admission 조립은 미검증.
RESOURCE-02 — capability readiness는 execution/resource 허가와 분리
- 분류: 실제 권한 모델 변화.
- 트리거: provider 등록/연결/ready/disconnected.
- 기존: DeviceAgent binding이 살아 있으면 manager 호출이 가능한 구조였고, readiness와 resource lease를 같은 정책 흐름에서 판단했다 (
AMRService,CmdPolicyManager관련 소스). - 새 동작:
CapabilityManagerService.register는 duplicate ID conflict/registry limit을 판정하고, provider connection은 generation/sequence를 검사한다. 주석상 provider ready만으로 policy approval/resource ownership/execution permission을 주지 않는다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/control/capability/CapabilityManagerService.java:66-84,118-190,236-239). - 이유: provider 연결 상태를 business admission과 분리.
- 제품 영향/보존: 기존 provider가 ready이면 동작하지만, capability 등록·connection readiness·resource acquire의 3단계가 필요하다.
- 검증/공백: Capability/Resource unit tests 근거; 모든 기존 DeviceAgent providers가 등록되는지 미확인.
POWER-01 — 배터리 관측에 source timestamp/sequence/freshness를 부여하고 늦은 샘플을 거절
- 분류/트리거: 신뢰성/실제 관측 규칙 변화. AMRAgent BatteryStatus2 callback.
- 기존:
PowerObservationMailbox.offer는 raw type, timestamp 범위/단조 증가를 검사하고 pending 최신 샘플 하나만 보존한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/power/PowerObservationMailbox.java:21-41). 그러나 오래된 legacy cache 자체에는 freshness expiry가 없다. - 새 동작:
BatteryObservationAdapter가 source와 5분 freshness를 기록하고 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/power/observation/BatteryObservationAdapter.java:10-40),PowerObservationIngress.Session.publish가 current session, strictly newer sequence, source timestamp 범위를 요구한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/power/observation/PowerObservationIngress.java:57-74). - 이유: 늦은 callback/재연결 이전 세대가 현재 배터리 상태를 덮지 않게 함.
- 제품 영향/보존: BatteryStatus2 percent/charge/rawCapacity 의미와 기존 source 문자열은 기본 경로에서 보존 (
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/power/observation/BatteryObservationAdapter.java:18-21). - 검증/공백:
AmrPowerObservationRuntimeTest및 mailbox tests가 늦은/missing timestamp를 확인. 실제 AMRAgent callback 설치 경로는 미검증.
POWER-02 — PowerManager가 관측 observer ownership과 service lifecycle을 소유
- 분류: 실제 소유권/신뢰성 변화.
- 기존:
BatteryManager.setBatteryInfo가 raw Bundle을 저장하고 level/voltage/current/usage를 한 singleton에서 갱신한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/device/manager/BatteryManager.java:177-251). - 새 동작:
PowerManagerService.openBatteryObserver는 power service READY와 state lifecycle affinity를 확인하고, observer identity가 바뀌거나 service가 stop되면 stale observer를 거절한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/power/PowerManagerService.java:30-79,82-94). - 이유: callback delivery owner를 singleton cache에서 lifecycle-managed service로 이동.
- 제품 영향/보존: BatteryManager가 계속 legacy edge로 값을 계산하지만 Framework state projection은 current observer만 반영한다.
- 검증/공백: power service tests/device test 근거. 전체 BatteryManager consumers가 Framework state로 전환됐는지는 미확인.
POWER-03 — 배터리 표시 보정표는 그대로 이동했고, admission/charging 의미는 추가하지 않음
- 분류: 단순 relocation(상수 보존).
- 기존:
BatteryManager.getBatteryCapacity는 negative sentinel을 유지하고 >100을 100으로 clamp한 뒤 보정표를 적용한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/device/manager/BatteryManager.java:80-92). - 새 동작:
BatteryCapacityPolicy.DISPLAY_PERCENT와displayPercent가 같은 보정표와 negative/clamp 규칙을 명시한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/power/policy/BatteryCapacityPolicy.java:3-25). 주석은 이 projection이 charging/low-battery admission/sensor readiness를 만들지 않는다고 명시한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/power/policy/BatteryCapacityPolicy.java:3-5). - 이유: 제품 표시값만 Framework 정책으로 추출.
- 제품 영향/보존: 표시 숫자와 sentinel 보존. low battery 15% 등 command admission은 별도 정책에 남는다.
- 검증/공백: BatteryManager/Framework policy tests 근거; 실제 UI와 모든 locale 소비자 확인 없음.
POWER-04 — 배터리 사용량 산식은 보존되지만 저장/clock owner는 아직 legacy
- 분류: 단순 relocation + migration gap.
- 기존: BatteryManager가 charging=false일 때 battery time을 +1하고 전압×전류를 누적한 뒤 preferences에 저장한다 (
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/device/manager/BatteryManager.java:202-211,144-161). - 새 동작:
BatteryUsagePolicy.recordUsage/includesDay/averageUsage는 7일 제거, 날짜 선택, 정수 나눗셈,--, overflow/parse 예외 동작을 그대로 명시한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/power/policy/BatteryUsagePolicy.java:7-43). 저장/JSON/clock은 여전히 product edge가 소유한다. - 이유: 산식과 저장 수명을 분리해 추출했으나 데이터 owner 이관은 완료되지 않음.
- 제품 영향/보존: 기존 usage 수치 기대값을 보존해야 하며, Framework policy 호출만으로 persistence가 생기지 않는다.
- 검증/공백: policy tests 존재. 실제 preferences migration, concurrent map atomicity, 생산 데이터 parity 미검증.
POWER-05 — AMR charging command 자체는 아직 DeviceAgent AMR service에 남음
- 분류: migration gap.
- 기존:
AMRBattery.startCharging/stopCharging은 action 1/4를IAMRControl.opChargingBattery로 보내고 service 없음/예외를 -1로 반환한다 (apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/device/item/amr/AMRBattery.java:15-31). - 새 동작: Framework
PowerManagerService는 배터리 관측 lifecycle만 소유하며 “physical sensors subscribe”하지 않는다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/power/PowerManagerService.java:19-20). charging transport/firmware command를 대체하는 구현은 이 경로에서 확인되지 않는다. - 이유: 관측 이관과 제어 이관을 분리한 상태.
- 제품 영향/보존: 기존 charging behavior는 DeviceAgent/AMR service에 의존한다. Framework power readiness를 charging 완료로 해석하면 안 된다.
- 검증/공백: 소스 확인만. Framework direct charging API, Binder, 실기기 충전은 미검증.
POWER-06 — power observation direct AMR route와 legacy DeviceAgent route는 동시에 섞지 못함
- 분류: 실제 admission/reliability 규칙.
- 트리거: ingress open/openDirectAmr 및 기존 callback 교체.
- 새 동작:
PowerObservationIngress.open은 direct AMR session이 살아 있으면 거절하고,openDirectAmr는 Framework provenance를 별도로 기록한다 (skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/power/observation/PowerObservationIngress.java:22-39). 기존 route는AMRAgent.BatteryStatus2@DeviceAgentsource를 유지한다. - 이유: 두 callback owner가 같은 battery state를 덮지 못하게 함.
- 제품 영향/보존: 기존 DeviceAgent source는 기본 경로에서 보존되며, direct Framework route를 조립할 때 교체 수명이 필요하다.
- 검증/공백: ingress/runtime 테스트 근거; 실제 앱 간 callback owner 선택과 전환 정책 미확인.
단순 이동·미완료 범위 인벤토리
BatteryCapacityPolicy와BatteryUsagePolicy는 산식/표 추출이며, charging, low-battery movement admission, persistence의 전체 이관이 아니다.MapCommandHandler는 Framework map DTO를 사용하지만 legacy Bundle/MQTT/AMRMap/MapManager가 실제 command owner로 남아 있다.LegacyCleaningExecutionAdapter와LegacyAmrExecutionAdapter는 Framework interface를 구현하지만 물리 실행은 DeviceAgent singleton/controller/AMR service에 남아 있다.PowerManagerService의 observer lifecycle은 구현되어도 AMRAgent callback registration, Binder/HIDL, board sensor, 실기기 배터리 freshness는 확인되지 않았다.- cleaning fan/tilt queue의 JVM 테스트는 Task/port 계약 증거이지 제품 APK 설치·실제 fan/tilt 정지 증거가 아니다.
- Task/Resource/Capability의 service lifecycle generation은 Framework 내부 규칙이다. 기존 Schedule, OTA, MQTT, CleaningReport DB의 데이터 이관·재시작 복구는 이 감사에서 확인하지 않았다.
CmdPolicyManager의 전역 emergency/critical-low-battery stop과 Framework owned cancellation은 서로 다른 계약이다. 하나가 다른 하나를 대체한다고 기록하지 않았다.- WSS, TTS, camera, air-quality, maintenance 등은 이번 요청의 cleaning/AMR/Task/resource/power 집중 범위에서 상세 항목으로 확장하지 않았다. 관련 Framework 파일이 존재한다는 사실만으로 업무 변화로 세지 않았다.
apps/*/build, generated AIDL, evidence JSON, APK/build/device 로그는 소스 동작의 보조 근거로만 취급했다. 생산 배포·Binder 연결·물리 완료는 별도 검증이 필요하다.- 현재 worktree의
MapCommandHandler,MappingProgress, AMR/cleaning handler는 pre-refactor HEAD 대비 대규모 변경 파일이다. 이번 보고서의 AMR-05는 현재 파일 안에서 확인되는 ownership만 기술했으며,mappingHost의 현재 dirty 변경과 peer HEAD 전체 차이는 별도 line-by-line 검토하지 않았다. - 따라서 “기존”을 pre-refactor 원본 전체로 읽으면 안 된다. pre-refactor에서 삭제·축소된
AirClearTimer, cleaning manager, MCU parser 등의 동작 보존 여부는 이 보고서 항목의 직접 비교 대상에서 제외하고, 현재 source에 남은 adapter/policy와 Framework source만 근거로 삼았다.
감사 결론
확정된 business motion은 “legacy 포트/정책을 즉시 제거”한 것이 아니라, 청소·AMR 실행을 Task/owner/correlation으로 감싸고, 공유 resource를 lease/recovery 상태로 만들며, 배터리 관측을 generation/sequence/freshness 기준으로 투영한 것이다. 표시 보정표·사용량 산식·기존 DeviceAgent 정책과 transport는 여러 곳에서 보존된다. 따라서 현재 소스만으로 Framework 단독 제품 완성이나 물리 완료를 주장할 수 없고, 위 migration gap을 별도 통합 작업으로 취급해야 한다.
3. 감시·음성·화면·SystemUI
WSS-01 — 공통 WSS admission reason bit 집계
- 분류: 정책 규칙 변경 + 재배치
- 트리거: Front/Remote가 WSS 시작 가능 여부를 조회하거나 시작 요청을 보냄.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/ipc/WssControlStub.java:58-63의checkWssAvailability()가WssManager로 위임하고, 실제 조건은 DeviceAgent singleton 안에 흩어져 있었다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/security/WssAdmissionPolicy.java:12-33,128-205가 구독/PIN/privacy/home-lock/network/battery/map/hardware/LLM 상태를 bit mask로 합산한다. - 행동 차이: caller별 임의 boolean 대신 이유 코드가 조합된
Result로 노출된다.checkRequest()는LLMtrigger에만 일부 reason을 mask한다(skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/security/WssAdmissionPolicy.java:110-125). - 이유: 확인. 상수, 순서, mask가 코드에 명시됨. 기존 의미 보존 여부는
LegacyDeviceCommandPolicy와 전부 대조하지 않아 일부 추론. - 제품 영향: UI/IoT의 오류 표시가 reason bit 해석에 의존하며, LLM 시작은 일반 WSS 시작과 다른 허용 규칙을 갖는다.
- 보존 값:
REASON_PRIVACY_MODE=1<<3,REASON_HOME_LOCKED=1<<4,REASON_BATTERY_LOW=1<<6, critical1<<20. - 검증/갭:
WssAdmissionPolicyfocused tests는 존재하나 전체 DeviceAgent IPC와 제품 error-string 매핑, 실기기 거부 토스트는 미검증.
WSS-02 — PIN credential text 비노출
- 분류: 정책 규칙 변경 + privacy/security 신뢰성
- 트리거: WSS availability가 PIN 설정 여부를 확인.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/ipc/WssControlStub.java:173-184의 Remote APIsetPinCode/getPinCode가WssManager와 평문 문자열 경계를 갖는다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/security/WssAdmissionPolicy.java:35-45는 기본pinCode()를UnsupportedOperationException으로 두고hasPinCode()만 허용한다. - 행동 차이: 새 정책은 credential text가 없으면 permissive default로 통과하지 않고 실패한다.
- 이유: 확인. 주석과 default 구현이 명시됨. 실제
AndroidWssPinStore가 모든 호출에서 이 경계를 사용하는지는 미검증. - 제품 영향: PIN 미설정/저장소 읽기 실패가 WSS 시작 거부로 나타날 수 있다. PIN 자체가 정책 입력·관측 payload에 들어갈 위험은 감소한다.
- 보존 값: PIN 입력/검증 API 이름과 Front/Remote caller 분리는
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/ipc/WssControlStub.java:121-177에 남아 있다. - 검증/갭: PIN 해시 저장과 UI 오류 표시의 end-to-end, 저장소 migration은 미검증.
WSS-03 — trigger vocabulary 고정
- 분류: 정책 규칙 변경
- 트리거: WSS patrol start/stop 요청의
trigger문자열 입력. - 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/ipc/WssControlStub.java:88-115는triggerType을 받고WssManager로 전달한다. 기존 허용 목록은 manager 내부 경로에 있었다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/security/WssAdmissionPolicy.java:83-89가PUI,SCHEDULE,LLM,PIN,FACE_RECOGNITION,APP,ADMIN,NULL만 허용한다. - 행동 차이: 미지/NULL 이외 문자열은 admission 전에 거부된다. stop도
WssPatrolStopRequests에서 지원 trigger 검사를 거친다(apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/wss/WssManager.java:557-564,588-590). - 이유: 확인. exact equality 목록과 caller가 존재함.
- 제품 영향: 새 IoT/Launcher가 기존 별칭이나 오타를 보내면 시작/중지가 거부된다.
- 보존 값: 기존 trigger 명칭 7개와
NULLliteral을 유지. - 검증/갭: 실제 관제 payload의 모든 trigger 값과 backward compatibility는 미검증.
WSS-04 — start 요청 owner/deadline/cancel 추적
- 분류: 신뢰성 변경
- 트리거: 같은 WSS start 요청의 중복/취소 또는 caller binder 사망.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/ipc/WssControlStub.java:88-114는 synchronous integer 반환 API이고TaskExecutor큐는 manager가 관리했다. - 신규:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/wss/WssManager.java:525-555의WssPatrolStartRequests.Entry가 binder owner, requestId, deadline, fingerprint를 등록하고 snapshot/cancel을 제공한다. - 행동 차이: caller 사망/기한 만료 요청은 후속 start를 실행하지 않고 상태 조회가 가능하다.
- 이유: 확인. submit/dispatch/get/cancel 호출이 실제 소스에 있음.
- 제품 영향: UI가 즉시
accepted를 physical start로 오해할 가능성이 줄지만, 새 snapshot 상태를 소비하지 않는 legacy caller는 의미를 잃는다. - 보존 값: 실제 hardware start는 여전히
TaskExecutor.WSS_CMD_START_REQUEST로 보낸다(apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/wss/WssManager.java:528-542). - 검증/갭: binder death와 재부팅 이후 request history, actual launcher client 처리는 미검증.
WSS-05 — stop 대상 execution identity 일치 검사
- 분류: 신뢰성 변경
- 트리거: 중지 요청이 도착했으나 이전/다른 patrol의 callback이 늦게 도착.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/wss/WssManager.java:714-734는 stop 요청을 queue하고 이미 stopped인 경우에도 완료 보고를 보내는 제품 호환 로직을 갖는다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/security/WssPatrolExecutionIdentity.java:24-44는runId/requestId/trigger가 현재 owner run과 일치하지 않으면unavailablesnapshot을 반환한다.skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/security/WssPatrolStopRequests.java:114-140은 현재 snapshot의 run/state를 확인하고 나서COMMITTING으로 전이한다. - 행동 차이: 이전 run의 stop/완료 이벤트가 현재 run을 정지시키는 경로가 차단된다.
- 이유: 확인. identity equality와
ownerLost처리 코드가 명시됨. - 제품 영향: 늦은 WSS callback으로 인한 오정지·잘못된 완료 보고가 줄지만, run ID를 전달하지 않는 legacy stop caller는
UNKNOWN/REJECTED가 될 수 있다. - 보존 값:
STOPPEDsnapshot은 빈 identity를 유지(skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/security/WssPatrolExecutionIdentity.java:29-31). - 검증/갭: 실제 EventAsset/AMR provider callback이 항상 같은 run ID를 보존하는지 미검증.
WSS-06 — stationary watch가 장수 task/cleanup owner가 됨
- 분류: 신뢰성 변경 + 재배치
- 트리거: stationary watch 시작, timeout, owner binder 사망, stop.
- 기존: DeviceAgent WSS/vision callback과
TaskExecutor가 감시·정지 순서를 함께 처리했다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/security/WssWatchTaskQueue.java:9-39,40-65가security.watch.stationaryqueue, registration, task recovery fence, cleanup timeout을 소유한다. - 행동 차이: process 재기동에서 미정리 watch가 fence로 남고, provider cleanup 관측 없이는 정상 종료로 단정하지 않는다.
- 이유: 확인.
recoveredHistory()와CLEANUP_TIMEOUT_MS=30000L가 코드에 있음. - 제품 영향: watch 시작/종료 UI가 늦게
STOPPED가 되거나RECOVERY_REQUIRED를 보일 수 있다. 자원 누수/중복 카메라 캡처 위험은 감소한다. - 보존 값: provider 호출은
Port.begin/snapshot/stopOwned로 분리되어 실제 카메라 구현은 바뀌지 않는다. - 검증/갭: 실제 provider가 snapshot/cleanup을 구현한 APK에서 확인되지 않음.
WSS-07 — capture와 watch queue 분리
- 분류: 재배치 + 신뢰성 변경
- 트리거: WSS capture/video upload가 patrol watch와 동시에 진행.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/wss/WssManager.java:1538-1544,1611-1630의 EventAsset manager가 capture와 upload 결과를 callback으로 직접 연결했다. - 신규:
WssWatchTaskQueue와WssCaptureTaskQueue가 별도 queue/owner identity를 사용하며,skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/security/WssEventAssetManager.java:82-105는 cancel acknowledgement가 movement/capture completion이 아니라고 명시한다. - 행동 차이: transport ACK, capture start/stop, upload result가 서로 다른 완료 신호가 된다.
- 이유: 확인. queue 분리와 주석, upload reporter의 fail target 매핑이 실제 코드에 있음.
- 제품 영향: 서버에 업로드 접수됐다는 사실만으로 patrol/capture 완료 UI를 띄울 수 없다.
- 보존 값:
FAILED_TARGET_BOTH/VOD/THUMBNAIL매핑은skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/security/WssUploadResultReporter.java:182-195에 유지. - 검증/갭: 실제 EventAsset service의 upload/thumbnail/VOD 결과 순서와 사용자 UI는 미검증.
WSS-08 — face/PIN verification을 EventAsset owner에 결합
- 분류: 정책 규칙 변경 + 보안 신뢰성
- 트리거: PIN 또는 얼굴 인증 시작/성공/실패.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/ipc/WssControlStub.java:121-157이 Front caller를 검사한 뒤WssManager.verifyPinCode/verifyFace를 호출했다. - 신규:
skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/security/WssEventAssetManager.java:119-151은 SafeCare owner가 verification start를 수락한 경우에만 clip 요청을 이어가고, clip 실패 시 verify session만 유지한다. - 행동 차이: 인증 session과 clip transport의 실패가 분리되고, owner 미연결은 즉시 빈 결과/오류가 된다.
- 이유: 확인.
startVerifySessionAndRequestClip()의 두 단계 return이 명시됨. - 제품 영향: 인증 화면은 성공했지만 영상 clip이 실패하는 경우를 별도 오류로 표시해야 한다.
- 보존 값: Front 전용 caller 권한은
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/ipc/WssControlStub.java:121-157에 유지. - 검증/갭: 실제 camera/face callback의 낮은 조도·인식 결과와 제품 화면 전환 미검증.
privacy/voice/LLM/TTS
VOICE-01 — privacy/manner/voice setting은 legacy owner를 통해 유지
- 분류: 재배치
- 트리거: privacy mode, manner mode, voice recognition, region 변경.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/settings/Settings.java:59,191-201이 system property/preferences를 직접 초기화하고,apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/policy/CmdPolicyManager.java:328-336이 privacy mode를 제품 명령 admission에서 검사했다. - 신규:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/infrastructure/setting/LegacyPrivacyVoiceSettingsGateway.java:8-22가Settings.INSTANCE와CleaningTransaction을PrivacyVoiceSettingsGateway로 노출한다. - 행동 차이: 저장/실행 owner는 그대로이고, Framework domain은 typed gateway 뒤에서 접근한다.
- 이유: 확인. gateway method-to-legacy call이 1:1로 보임.
- 제품 영향: Framework 요청 경로에서도 같은 privacy 값을 읽어야 하며, gateway 미조립이면 UI와 Framework 판단이 갈릴 수 있다.
- 보존 값:
setPrivacyModeEnabled/getPrivacyModeEnabled, sync mode, region,Settings.IOTGetConfig()호출. - 검증/갭: 실제 Framework service가 이 gateway를 product composition에 등록했는지, property 동기화는 미검증.
VOICE-02 — LLM status를 content-free typed observation으로 투영
- 분류: 정책 규칙 변경 + privacy
- 트리거: DeviceAgent가
setLlmStatuscallback을 발행. - 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/llm/LlmManager.java:20-101,104-125가 상태 정수와 callback을 singleton 내부에 보관하고 상태 변경을 Bundle로 전송한다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/llm/LlmPipelineObservationAdapter.java:10-39가DeviceAgent.MainApi.setLlmStatus를 source로 기록하고, 알려진 status만StateManagerService에 60초 freshness로 publish한다. - 행동 차이: unknown integer는 삭제되며, TTS 문장·prompt는 상태 projection에 저장되지 않는다.
- 이유: 확인.
known()검사와FRESHNESS_MILLIS=60_000L가 코드에 있음. - 제품 영향: 60초 이상 callback이 없으면 LLM idle/processing 판단이 stale로 만료될 수 있다. unknown legacy 상태를 전제로 한 UI는 빈 상태가 된다.
- 보존 값: legacy status 번호 매핑은
LlmPipelineState.fromLegacyStatus()를 통해 유지. - 검증/갭: 실제 state consumer가 만료를 어떻게 표시하는지, LLM callback 주기가 제품에서 안정적인지 미검증.
2026-10-15 후속 — DeviceAgent 없는 이동 준비의 LLM 관측 수명
위 60초 callback projection은 기존 경로의 기록입니다. 현재 독립 Host의 지도 생성·스테이션 복귀 준비는 AndroidVoiceRecognitionRuntime.llmStatusObservation(5000L)로 Agent에 인증된 상태를 조회하며, 이를 이전 callback 캐시와 섞지 않습니다.
- 이전 작업본의 공백: 준비 worker에서 조회한 뒤 Host 객체와 5초 시간만 검사했습니다. 같은 runtime 내부의 Agent binding이 종료되거나 다른 조회가 새 상태를 확인해도 이전 허용 관측이 남을 수 있었습니다.
- 현재 실행 순서: 준비 worker의 인증된 조회 → 기존 LLM 코드 정책 검사 → 기존 청정/상호작용 정리 → 전송 직전의 로컬 재검사 → AMR 전송. 재검사는 현재 runtime 소유권, peer 종료 여부, 현재 binding, connection ID, engine ID, 순번·코드·관측 시각을 함께 확인합니다. 큐 안에서 IPC·재연결·행동 재전송을 하지 않습니다.
| 조건 | 현재 판정 | 보존·한계 |
|---|---|---|
| 같은 연결·같은 관측, 5초 미만 | 기존 LLM 코드 정책에 따라 허용 | 허용 코드 0/6/7/8/9/12, 처리 중 1~5/10/11은 차단 유지 |
| Agent death/disconnect/bindingDied, runtime 종료·owner 상실 | 이전 관측 사용 거절 | 다른 연결에서 자동 재전송하지 않음 |
| 더 최신 검증 관측을 받음 | 이전 순번의 관측 사용 거절 | 새 코드가 같은 IDLE이어도 기존 receipt는 갱신하지 않음; 재준비 필요 |
| NOT_READY 또는 엔진이 아직 없음 | 이전 관측 사용 거절 | 기존 순번/시간 단조 검사 이력은 지우지 않음 |
| 다른 connection/engine/코드/순번/시각, malformed receipt | 거절 | 임의 Task 상태를 LLM 상태로 대신 쓰지 않음 |
| 관측 후 정확히 5초 경과·clock 역전 | 거절 | 기존 Host의 만료 경계 유지 |
| Agent 상태가 바뀌었지만 Host가 아직 조회하지 않음 | 새 상태를 알았다고 주장하지 않음 | 이 재검사는 마지막 관측의 수명 검사이며 실행 동안의 LLM 상호배제 lease가 아님 |
기록된 검증: Host 무효 관측 허용 행동 RED → 영향 회귀 11 PASS(14.70초), 새 runtime API 누락 RED는 별도 기록. 독립 명세→품질 PASS, Host compileFactoryReleaseJavaWithJavac exit0(1분16초). 기기 시험 결과가 아닙니다.
제품 영향: 이전에는 허용되던 준비가 연결/관측 변경으로 거절될 수 있습니다. 다른 소비자가 관측을 자주 조회하면 준비가 취소되는 빈도도 실기기에서 확인해야 합니다. 거절을 물리 동작 완료·중단으로 표현하지 않습니다.
근거: AndroidVoiceRecognitionRuntime.isLlmStatusObservationCurrent, Peer.validateLlmStatus, SkixFrameworkHostService.observeStationLlmAdmission; 검증 정본 skix-station-llm-receipt-evidence-2026-10-15.json. 실제 Host 행동 RED와 새 API 누락 RED를 구분해 기록합니다. Android/Binder 경계 대역의 Java 회귀는 실제 Agent/native/기기 검증이 아닙니다. 이 후속의 설명은 이번 상세 문서 공개에 포함합니다. 문서 공개와 코드/APK의 제품 배포는 별개입니다.
VOICE-03 — TTS 요청이 Task와 provider dispatch로 분리
- 분류: 재배치 + 신뢰성 변경
- 트리거: LLM 또는 외부 모듈이 TTS 재생을 요청.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/llm/LlmManager.java:171-214가 상태가 IDLE이 아니면 요청을 버리거나CMD_STOP_TTS를 보내고, 최종 Bundle을AppCmd.sendModuleCallback_main()으로 전달한다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/llm/execution/TtsTaskDispatcher.java:24-44가llm.ttstask/queue와TtsManagedHost를 사용한다. provider는 dispatch return을 acceptance로만 의미화하며 physical playback은 주장하지 않는다. - 행동 차이: 요청은 실행 key/task state를 가지며 provider non-acceptance만 명시적 reject로 처리하고, 일반 exception은 이미 실행됐을 가능성을 보존한다.
- 이유: 확인. interface 주석과 dispatcher 상수가 실제 코드에 있음.
- 제품 영향: 기존 “Bundle 전달 완료=재생 시작” 식의 UI를 task dispatch/result 기반으로 바꿔야 한다.
- 보존 값: TTS method
llm.tts; legacy text payload 자체는 adapter 경계에서 전달되지만 observation/state에는 저장하지 않는다. - 검증/갭: 실제 AudioTrack/speaker audible playback과 task completion 상관은 미검증.
VOICE-04 — Managed TTS Binder relay가 signature/peer death를 검사
- 분류: 보안 신뢰성 변경 + 재배치
- 트리거: Framework가 DeviceAgent 또는 direct Agent TTS provider에 연결.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/llm/ManagedTtsPeerRegistry.java:21-45도 signature를 확인하지만 provider binding과 relay owner가 DeviceAgent 쪽에 있었다. - 신규:
skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/agent/AndroidManagedTtsRelayTransport.java:31-83,165-188가 callback Binder,DeathRecipient, sender thread, target service(ManagedTtsRelayService또는 direct Agent)를 관리하고 signature/package 검사를 수행한다. - 행동 차이: peer death는 connection close/queued request failure로 귀결되고, 익명 callback class가 Binder parcel로 노출되지 않도록 base
ResultReceiver로 변환한다(skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/agent/AndroidManagedTtsRelayTransport.java:126-127). - 이유: 확인. target 선택, death cleanup, parcel 주석과 코드가 있음.
- 제품 영향: provider 재시작 때 TTS가 자동 성공으로 남지 않고 transport failure가 된다.
- 보존 값: legacy DeviceAgent target
com.sk.airbot.deviceagent.llm.ManagedTtsRelayService가 기본 경로로 남아 있다(skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/agent/AndroidManagedTtsRelayTransport.java:75-90). direct Agent target은 별도 target/action/permission으로 분기된다. - 검증/갭:
apps/SkixFrameworkService/app/build.gradle:5-15,62-71에서skixFactoryManagedTts기본값과skixFactoryDirectAgentTts기본값이 모두 false이고 direct 경로는 factory 설정일 때만 자원화된다. 따라서 기본/release composition은 legacy relay를 향한다. installed package signature, exported/permission, factory APK 선택과 실제 provider callback은 미검증.
VOICE-05 — TTS 완료 callback은 execution identity가 있어야 상관
- 분류: 신뢰성 변경
- 트리거: TTS provider가 status/complete callback을 늦게 반환하거나 이전 연결 callback이 도착.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/llm/LlmManager.java:104-118의 callback은status,tts_type중심이며 실행 ID가 없다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/llm/completion/TtsTaskCompletionService.java와TtsManagedHost는 completion key/event/sequence 기반 observation만 task에 배정하도록 설계되어 있다(실제 구현 파일skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/llm/completion/TtsTaskCompletionService.java). - 행동 차이: key 없는 callback은 현재 task에 추정 배정되지 않고 uncorrelated/unknown으로 남는다.
- 이유: 확인은 completion service의 key API와 테스트 파일 존재, 추론은 legacy callback과의 의미 비교. 전체 파일 구현의 모든 상태 branch는 별도 정독 범위 밖.
- 제품 영향: 완료 UI가 늦은 callback으로 잘못 닫히는 현상은 줄지만, provider가 key를 누락하면 완료가 보류된다.
- 보존 값: idle/start/stop/wakeup 관련 legacy status 매핑은 adapter 계층에서 유지하도록 되어 있다.
- 검증/갭: actual Agent callback payload가 completionKey/eventId/positive sequence를 모두 포함하는지 미검증.
VOICE-06 — TTS 문장 로그/장기 상태 노출 억제
- 분류: privacy 규칙 변경
- 트리거: TTS 요청 및 LLM observation 기록.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/llm/LlmManager.java:197-214는 현재 소스에서 문장 자체를 로그로 찍지 않도록 수정되어 있으나 Bundle은ARGS_TTS로 전송된다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/llm/LlmPipelineObservationAdapter.java:10-39는 content-freeLlmPipelineState만 publish하고, TTS completion contract는 text를 queue/history에 넣지 않는 구조다. - 행동 차이: 상태/관측 저장소에서 사용자 음성 응답 원문이 분리된다.
- 이유: 확인은 adapter의 타입과 source/fields, 추론은 content-free 설계의 privacy 효과.
- 제품 영향: 디버깅 화면에서 문장을 재생성할 수 없으며, 문제 재현은 별도 transient log/trace가 필요하다.
- 보존 값: 실제 TTS dispatch payload는 provider relay로 전달될 수 있다.
- 검증/갭: 모든 logging path와 crash report에 text가 없는지 전체 검색은 하지 않았고, 음성 privacy end-to-end는 미검증.
display/night/LED/SystemUI
UI-01 — display state decision을 pure policy로 분리
- 분류: 현재 정책 추출·재배치 확인 / 역사적 행동 변경 미확정
- 트리거: station 상태, touch/event/person/LLM activity, timeout, forced display request.
- 기존: DeviceAgent의
LCDManager를apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/infrastructure/setting/LegacyDisplaySettingsGateway.java:28-37이 직접 호출하고, streaming은apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/streaming/StreamingManager.java:106-109에서setAlwaysOn을 직접 변경했다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/system/policy/DisplayStatePolicy.java:3-50이 normal=0, wait=1, off=2, dimmed=10을 결정하고, 실제 Android output은 caller가 소유한다. - 현재 계약: forced LCD/state, station change, activity, timeout의 우선순위가 명시적인 순수 함수로 표현된다. 과거 우선순위와 달라졌다는 역사적 비교 근거는 아직 확보하지 못했다. off-station dimmed→normal에서는
dimmedToOn부가 결과를 반환한다. - 이유: 확인. 분기 순서와 값이 코드에 있음.
- 제품 영향: 정책 caller가
dimmedToOn을 적용하지 않으면 화면은 논리적으로 normal이어도 실제 밝기가 회복되지 않을 수 있다. - 보존 값: 기존 state 값 0/1/2/10과 streaming display hold의 legacy owner.
- 검증/갭: 실제 LCDManager 상태와 policy 입력을 연결한 product composition/실제 화면 전환 미검증.
UI-02 — night mode brightness의 기존 값을 정책으로 재배치하고 readback을 보강
- 분류: 정책 보존 + 신뢰성 변경/재배치
- 트리거: LCD brightness 요청이 night mode 또는 dimmed 상태에서 처리됨.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/device/item/soc/SocLcdBrightness.java:5-15와apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/device/manager/LCDManager.java:569-575,622-645,845-914에 이미 허용값 28/53/65/77/89/100, night 28%, dimmed 77% readback, night 진입 시 저장/복원이 있다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/system/policy/DisplayBrightnessPolicy.java:7-15가 같은 허용값과 night/dimmed 분기를 순수 정책으로 분리하고,skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/system/AndroidProductDisplayRuntime.java:67-116및skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:5503-5575,6030-6086가 canonical settings revision, Settings.System write, readback 상태를 관리한다. - 행동 차이: 확인된 제품 값 자체는 바뀌었다고 볼 수 없고, 정책 적용·desired persistence·readback 결과가 별도 상태로 드러난다. 기존
SocLcdBrightness의 알 수 없는 값 fallback(기본 100%)과 새 정책의 허용값 거부 경계는 추가 비교가 필요하다. - 이유: 확인. legacy 상수/분기와 신규 policy/runtime의 동일 값 및 호출 경로를 각각 확인했다. 신규가 제품 규칙을 새로 만들었다고 분류하지 않는다.
- 제품 영향: night/dimmed 표시값은 유지되지만, 잘못된 입력과 Settings readback 불일치를 성공으로 표시할 수 있는 경계가 줄어든다.
- 보존 값: 28/53/65/77/89/100, night=28%, dimmed high request=77%; 정상 요청의
persistDesired=true, override의false. - 검증/갭: 소비자별 상수 alias 이름·소유 파일·적용 범위 대조, actual Settings value-to-percent normalization, night schedule clock 계산, signed APK composition, 실물 LCD luminance는 미검증.
UI-03 — Eye LED 기존 값 경계를 runtime/policy로 재배치
- 분류: 정책 보존 가능성 + 재배치/신뢰성 변경
- 트리거: AQM, WSS, night mode, scene 전환, LED brightness 요청.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/device/manager/EyeLedManager.java:151-160,197-225에 legacy brightness 값 0/20/30/40/50/60, 미지 값의 기본 60 fallback, Settings/WssTransaction/AppInfo 연동,EyeLedRuntime위임이 이미 있다. - 신규:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/system/policy/EyeLedPolicy.java:11-29,31-65와skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:5870-5900이 AQM/WSS/night override와 scene delay를 계산한 뒤 hardware readiness/owner gate를 거쳐EyeLedRuntime/McuEyeLedOutput으로 보낸다. - 행동 차이: brightness/scene 상수가 새로 바뀌었다는 근거는 이 비교에서 확인하지 못했다. 차이는 policy 결과와 MCU write, owner/readiness gate가 분리되고 요청을 physical completion으로 승격하지 않는 데 있다.
- 이유: 확인 + 일부 미확인. legacy 값/fallback과 신규 호출 경계는 확인했으나 모든 scene별 역사적 상수 동등성은 미확인이다.
- 제품 영향: 현재 경계에서 관찰한 0/20/30/40/50/60 입력값을 바탕으로, WSS/night 우선순위와 hardware 미준비를 성공처럼 표시하지 않아야 한다.
- 관찰 값(전체 alias 보존 확정 아님): legacy brightness 0/20/30/40/50/60 및 기본 60; MCU scene 번호 1,2,3,4,5,6,7,8,9,10,11,13,14,15와 93-99 temporary 분류.
- 검증/갭: scene별 legacy/new 상수 대조, 실제 MCU command, physical eye LED feedback은 미검증. policy는 requested output이지 LED completion이 아니다.
UI-04 — display hold를 Binder token/UID owner로 제한
- 분류: 보안 신뢰성 변경
- 트리거: streaming 또는 화면 유지 기능이 display wake lock을 요청/해제.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/streaming/StreamingManager.java:106-109이LCDManager.setAlwaysOn(enabled, ALWAYS_ON_TYPE_STREAMING_MODE)를 직접 호출했다. - 신규:
skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/system/AndroidDisplayRuntime.java:12-55는 token+UID를 등록하고PowerManager.SCREEN_BRIGHT_WAKE_LOCK|ACQUIRE_CAUSES_WAKEUP을 사용한다. 같은 token의 반복 enable은 idempotent이며 UID mismatch는 SecurityException이다. - 행동 차이: caller binder death/owner unavailable이면 hold가 release되고, 여러 token은 하나의 non-reference-counted lock을 공유한다.
- 이유: 확인. token map, death recipient, owner monitor(
skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/system/AndroidDisplayRuntime.java:73-95)가 있음. - 제품 영향: streaming process가 죽어도 화면이 계속 켜지는 상태가 줄지만, owner supplier가 false이면 정상 요청도 거부된다.
- 보존 값: streaming의 always-on 목적과 wake lock 동작.
- 검증/갭: 실제 PowerManager permission, wake lock 정책, screen-off/on 결과와 App process death 미검증.
UI-05 — ProductSystemUI top bar는 실제 SystemUI가 아닌 overlay owner
- 분류: 재배치 + 마이그레이션 갭
- 트리거: 상단 상태바/네비게이션/배터리/개인정보 아이콘 표시.
- 기존:
apps/ProductSystemUI/app/src/main/java/com/sk/productsystemui/TopBarService.java:220-229가TYPE_APPLICATION_OVERLAY,FLAG_NOT_FOCUSABLE,FLAG_LAYOUT_IN_SCREEN으로 top bar를 붙인다. - 신규/현재 구조:
apps/ProductSystemUI/app/src/main/java/com/sk/productsystemui/SystemPopupService.java:29-33,38-95가 popup/navigation/top-bar policy의 Binder owner가 되고TopBarPolicyRegistry로 caller UID/package/token을 확인한다. 여전히 top bar 자체는TopBarServiceoverlay다. - 행동 차이: UI policy 권한/owner가 중앙화되고, navigation visibility에 따라
FLAG_NOT_TOUCHABLE을 켠다(apps/ProductSystemUI/app/src/main/java/com/sk/productsystemui/TopBarService.java:231-251). Android real status bar/insets를 대체하지 않는다. - 이유: 확인. Window type, binder ownership, package-for-UID check가 코드에 있음.
- 제품 영향: 전체 화면/잠금 화면/실제 status bar를 제어한다고 표현하면 안 되며, overlay가 critical system window 아래에 있을 수 있다.
- 보존 값: 1920x1200 design grid, privacy/manner/night/error/sound/wifi/battery indicator 목록(
apps/ProductSystemUI/app/src/main/java/com/sk/productsystemui/TopBarService.java:206-218). - 검증/갭: privileged permission allowlist, 실제 launcher/system image 설치, 다른 앱 위 가시성은 미검증.
UI-06 — popup owner 사망/취소 시 negative 종료
- 분류: 신뢰성 변경
- 트리거: 제품 confirmation popup 요청, caller cancel, callback binder death, SystemPopupService destroy.
- 기존: legacy UI popup 경로는 Launcher/DeviceAgent callback에 의존했다.
- 신규:
apps/ProductSystemUI/app/src/main/java/com/sk/productsystemui/SystemPopupService.java:38-66,111-119,106-109가 caller UID/requestId/callback binder를 확인하고, callback death 또는 service destroy 시NEGATIVE로 dismiss한다. - 행동 차이: 화면만 사라지고 business completion이 성공으로 남는 경로를 줄인다.
- 이유: 확인.
isPopupOwner,linkToDeath,dismissPopup(NEGATIVE)가 코드에 있음. - 제품 영향: popup owner process가 종료되면 사용자의 positive 승인 없이 취소로 처리된다.
- 보존 값: positive/negative action callback 계약과 full-screen overlay window.
- 검증/갭: 실제 Framework caller가 negative 결과를 task cancellation으로 연결하는지 미검증.
UI-07 — SystemUI status state는 producer snapshot을 요구
- 분류: 신뢰성 변경
- 트리거: battery/network/error/privacy 상태가 top bar에 표시됨.
- 기존: DeviceAgent
Settings,AMRBattery,LCDManager등 각 owner가 직접 UI callback을 보냈다. - 신규:
apps/ProductSystemUI/app/src/main/java/com/sk/productsystemui/TopBarService.java:231-260은SystemUiTopBarPolicy를 적용하고 renderer flags/layout을 유지하며,TopBarDeviceState/TopBarNetworkState에서 snapshot을 갱신한다(파일:apps/ProductSystemUI/app/src/main/java/com/sk/productsystemui/TopBarDeviceState.java,apps/ProductSystemUI/app/src/main/java/com/sk/productsystemui/TopBarNetworkState.java). - 행동 차이: UI는 policy가 허용한 위치/가시성/interaction 상태를 소비하며, policy owner가 없으면 service가 표시를 적용하지 못한다.
- 이유: 확인. policy apply 경로와 state helper 파일 존재는 확인. 모든 producer의 freshness 규칙은 미확인.
- 제품 영향: 배터리 숫자/아이콘이 실제 hardware 값보다 늦거나
--%초기값으로 보일 수 있다. - 보존 값: battery/wifi/privacy/night/error/sound indicator 디자인 리소스.
- 검증/갭: 실제 reboot/boot broadcast 동기화와 A1 board 상태는 미검증.
UI-08 — Host의 display/night/LED/privacy 구성은 확인되며 교체·활성화 경계가 갭
- 분류: 재배치 + 마이그레이션 갭(생산자/활성화 경계)
- 트리거: Framework public API에서 display/privacy/LED 값을 요청.
- 기존:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/infrastructure/setting/LegacyDisplaySettingsGateway.java:10-41,apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/infrastructure/setting/LegacyPrivacyVoiceSettingsGateway.java:8-22가 DeviceAgent singleton에 연결되어 있다. - 신규: Host가 실제
skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/system/AndroidProductDisplayRuntime.java:15-17,67-116,skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:5503-5575(night/display),skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:5870-5900(EyeLed),skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:6030-6086(WSS display override),skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:2467-2479(manner/privacy configure)와skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/setting/AndroidProductSettingsStore.java:66-68(night/manner/privacy persistence)를 조립한다. LED는skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/system/EyeLedRuntime.java:578-620에서EyeLedPolicy를 적용하고skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/mcu/McuEyeLedOutput.java:41-47,83으로 MCU command를 만든다. - 행동 차이: pure policy만 존재하는 상태가 아니라 Host helper가 settings write/readback, LED output, privacy/night state를 실제 producer/runtime 경계까지 연결한다. 다만
standaloneHardwareRequested()/mayOwnHardware()와WAKE_LOCK/owner/readiness gate가 활성화를 제한하며, legacy DeviceAgent gateway가 완전히 교체됐다는 뜻은 아니다. - 이유: 확인. Host의 night/display/LED/privacy helper와 runtime 호출을 소스에서 확인했다. “Host composition 미확인”은 부정확하므로 “제품 선택·활성화·교체 결과 미검증”으로 범위를 좁힌다.
- 제품 영향: static/JVM PASS는 Host의 논리 경로와 일부 write/readback 상태만 증명한다. factory/release variant에서 실제 producer가 선택되고 hardware ownership을 얻는지 확인해야 화면/LED/privacy 완료를 판정할 수 있다.
- 보존 값: legacy DeviceAgent owner/gateway, LCD 28/53/65/77/89/100, EyeLed legacy 0/20/30/40/50/60은 현재 코드 경계에서 유지된다.
- 검증/갭: factory/release APK variant·manifest·service registration,
standaloneHardwareRequested설정, installed package/signature, Binder/SELinux, physical display/LED/privacy transition은 미검증.
공통 미검증 및 후속 검토 경계
- 이 대장은 소스 기반 변경 register다. JVM/host test가 통과해도 Binder transaction, signed APK, system/system_ext/vendor partition, SELinux, boot ordering, real provider callback, camera/MCU/AudioTrack, 실제 화면·LED·TTS 청취를 증명하지 않는다.
- WSS ACK/accepted/queued/started와 patrol movement, capture/upload, physical camera/AMR 완료는 각각 별도 신호다. 하나의 callback을 다른 완료로 승격하면 안 된다.
ProductSystemUI는TYPE_APPLICATION_OVERLAY기반 앱 UI다. Android의 실제 status bar/SystemUI 권한, insets, lock-screen policy를 대체한다는 근거는 이 범위에서 확인되지 않았다.- LLM/TTS는 legacy DeviceAgent relay를 기본 target으로 유지하는 경로가 보인다. Framework provider가 실제 설치/서명/permission까지 연결됐다는 근거는 이 읽기 전용 조사에서 확인하지 않았다.
- 비교 기준은 현재 dirty worktree의 소스다. 변경 목록이 큰 상태이므로, 이후 review에서는 각 항목을 기준 commit과 diff로 다시 고정해야 한다.
4. IoT·설정·지도·보고·업데이트
B-IOT-01 — MQTT 시작과 부팅 서비스
- 트리거/경로:
BOOT_COMPLETED수신. - 구형:
apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/receiver/BootReceiver.java:9-16의onReceive()가IotAgent를startService()한다. 서비스 본체는apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/IotAgent.java:155-260부근의 장수명 MQTT 초기화/구독 경로다. - 현재:
skix-framework/base/core/java/com/sk/intellix/framework/service/MqttSessionService.java:19-49는 주석 그대로IotAgent가 backing하는 공유 세션 경계이며connect()도 gateway에 위임한다.skix-framework/base/core/java/com/sk/intellix/framework/iot/SkixIotManager.java:15-18와skix-framework/base/core/java/com/sk/intellix/framework/manager/SkixCloudBridgeManager.java:11-20도 topic/AWS와 cloud owner를 IotAgent에 남긴다. 현재 코드에서 동일한BOOT_COMPLETED -> connect()자동 기동은 확인하지 못했다. - 차이/분류: MQTT broker/부팅 owner가 Framework로 교체된 증거가 없고, typed SDK가 호출 경계를 추가한
relocation이다. 자동 부팅 연결은gap이며 IotAgent owner 유실로 분류하지 않는다. - 영향/검증: 부팅 직후 MQTT가 자동 연결되는지와 SystemServer 시작 순서는 매니페스트/init/device에서 별도 확인 필요. 소스 검색과 상태 문서만 근거이며 APK/기기 미검증.
B-IOT-02 — MQTT topic fan-in/fan-out
- 트리거/경로: cloud command/event 수신 및 상태 publish.
- 구형:
apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/IotAgent.java:3-39,70-121에GET_CONFIG,MAPPING,SET_CLEAN,GET_FIRMWARE_VERSION,SET_BACKGROUND등 topic 상수와 다수 publish/subscribe serializer가 한 서비스에 집중된다. - 현재:
skix-framework/base/core/java/com/sk/intellix/framework/service/MqttSessionService.java:100-146은 일부 typed command만 노출하고execute()는 연결이 없으면 drop한다(skix-framework/base/core/java/com/sk/intellix/framework/service/MqttSessionService.java:146-159). 그러나apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/mqtt/client/AWSIoTMQTTClient.java:14-와apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/IotAgent.java:327,346-365,740,995,1214,1347,1589-1610,1883-가 여전히 broker/topic owner다.skix-framework/base/core/java/com/sk/intellix/framework/manager/SkixCloudBridgeManager.java:11-20은 manager가 session/subscription을 소유하지 않는다고 명시한다. - 차이/분류: 전체 topic handler 대체가 아니라 IotAgent transport 위에 typed 호출/관찰 경계를 추가한
relocation이다. 모든 구형 topic의 현재 consumer 연결은 확인되지 않았다(gap); API 표면의 부분집합을 full parity로 세지 않는다. - 영향/검증: cloud compatibility와 누락 topic 위험. core tests의
MqttSessionServiceTest,MqttSessionServiceConnectionStateTest는 계약 단위 근거이나 실제 broker/server 수신은 미검증.
B-IOT-03 — MQTT 연결 상태와 stale callback
- 트리거/경로: 연결/해제/서비스 binder 변화.
- 구형:
apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/IotAgent.java:155-260의 장수명 service가 MQTT 연결 상태와 command path를 함께 유지하고, 관련 topic 경로는apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/mqtt/아래에 있다. - 현재:
skix-framework/base/core/java/com/sk/intellix/framework/service/MqttSessionService.java:23-71,164-177은IotConnectionStatelistener와 별도 service-connection listener를 둔다. 문서 주석처럼 binder invalidation은 MQTT와 독립이고 호출자는 fresh query를 해야 한다. - 차이/분류: 두 수명 축 분리와 listener 예외 격리(
신뢰성 변경). 구형의 reconnect backoff와 현재 gateway 내부 구현은 이 저장소 경계 밖이다(gap). - 영향/검증: MQTT 연결은 살아 있어도 DeviceAgent binder가 죽으면 fresh read가 실패할 수 있다. core contract/test 소스만 확인.
B-IOT-04 — IoT domain observation owner 교체
- 트리거/경로: 새로운 framework IoT observer 등록, close, sequence publish.
- 구형:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/iot/IotManager.java및apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/domain/command/IotCommandHandler.java가 callback/command를 product app 쪽으로 전달했다. - 현재:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/iot/IotManagerService.java:20-86은observerIdentity,ownerSequence, ordered sequence를 검사하고 기존 observer를 대체/close한다. - 차이/분류: 마지막 등록 owner만 관측을 publish하는 명시적 경계(
정책 변경,신뢰성 변경). - 영향/검증: 늦은 구형 publisher가 새 owner 상태를 덮지 못하도록 보호된다.
skix-framework/base/core/tests/src/com/sk/intellix/framework/iot/*와 service tests는 contract 근거, 실제 IoTAgent callback은 미검증.
B-SET-01 — ProductSettings 저장 책임
- 트리거/경로: product setting read/write.
- 구형:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/infrastructure/setting/LegacyConfigurationSettingsGateway.java,apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/infrastructure/setting/LegacySystemPreferenceSettingsGateway.java,apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/infrastructure/setting/LegacyUserPreferenceGateway.java가 앱/legacy preference gateway를 제공한다. - 현재:
skix-framework/base/core/java/com/sk/intellix/framework/manager/SkixSettingManager.java:18-59,80-94는IProductSettingsBinder snapshot/revision write를 호출한다. Host가skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:11159에서getFilesDir()를 넘겨 실제skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/setting/AndroidProductSettingsStore.java:19-35,45-85,101-133의skix-product-settings-v1, AtomicFile, file lock, version 1..11 reader와 CAS를 연결한다. - 차이/분류: 설정 저장소 authority가 legacy gateway에서 Framework service/store로 이동한
relocation; revision CAS와 no-retry는 concurrent write 보호 계약이다. 기존 설정 파일의 cross-package migration은 별도gap이다. - 영향/검증: concurrent write가 조용히 덮어써지지 않는다. 위 store/contract tests와
apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/infrastructure/setting/FrameworkProductSettingsClient.java:27-69,448-470source inspection 근거; Android permission/upgrade migration은 미검증.
B-SET-02 — 설정 consumer binding/reconnect
- 트리거/경로: ProductSettings service bind 실패/null/death.
- 구형: legacy gateway에는 별도 binding epoch/reconnect 로직이 없고 호출자가 현재 preference를 직접 읽는다.
- 현재:
apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/infrastructure/setting/FrameworkProductSettingsClient.java:27-69,122-145,473-547는 1s에서 30s까지 reconnect, binding epoch, death/null retirement를 구현한다. - 차이/분류: Binder lifecycle 신뢰성 보강(
신뢰성 변경). - 영향/검증: stale binder 명령과 close 중 unbind 위험이 감소한다. source-level tests/evidence 문서의 domain binding recovery는 참고만 했으며 APK/기기 미검증.
B-SET-03 — 초기 설정 read와 command retry 분리
- 트리거/경로: boot/start 직후 snapshot이 null/UNAVAILABLE.
- 구형:
IotAgent의 legacy settings 경로는 topic response/저장값을 통해 재조회하나 binding epoch 제약은 확인되지 않았다. - 현재:
apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/infrastructure/setting/FrameworkProductSettingsClient.java:148-169,455-470은 initial read만 같은 epoch에서 예약 재조회하며, command/reporting failure는apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/infrastructure/setting/FrameworkProductSettingsClient.java:438-446에서 no command retry로 처리한다. - 차이/분류: read-only retry와 write replay를 분리(
정책 변경,신뢰성 변경). - 영향/검증: write 결과 불명확 상태에서 중복 설정을 방지한다. source inspection과
skix-framework/base/core/tests/src/com/sk/intellix/framework/model/SettingSnapshotContractTest.java확인; 실제 boot race는 미검증.
B-PIN-01 — PIN credential 저장/검증
- 트리거/경로: WSS PIN set/verify.
- 구형:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/wss/WssManager.java:1303-1322,1408-1446는 salt/hash를 생성해SKMPreferences에 저장하고 입력 PIN을 hash 비교한다. - 현재:
skix-framework/base/core/java/com/sk/intellix/framework/manager/SkixWssPinManager.java:9-45는IWssPinBinder와 revision을 사용한다. Host 연결은skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/SkixFrameworkHostService.java:11352-11372에서 실제skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/security/AndroidWssPinStore.java:10-42,55-84,87-123를 생성한다. store는 Host CE-private AtomicFile에 salt/derived hash/revision을 저장하고 원문 PIN을 저장하지 않는다.skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/entrypoint/service/WssPinBinderService.java:30-64는 root/UID/signature permission/Host generation을 검사한다. - 차이/분류: PIN credential owner가 WssManager preference에서 Framework Host store로 이동(
relocation); revision CAS/no-retry 및 owner 재검사는신뢰성 변경이다. - 영향/검증: 저장 형식과 hash 처리는 소스상 확인되지만 Android CE permission, 기존 PIN data migration, 실기기 인증은 미검증. core PIN tests와 위 store/binder source만 근거다.
B-PIN-02 — PIN context와 map area 결합
- 트리거/경로: PIN 인증으로 security/WSS 실행을 허용.
- 구형:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/wss/WssManager.java:193-205,669-674,1037-1045는MapManager의 station/all area와 PIN context를 함께 policy checker에 제공한다. - 현재:
skix-framework/base/core/java/com/sk/intellix/framework/model/SecurityPinContext.java및skix-framework/base/core/tests/src/com/sk/intellix/framework/model/SecurityPinContext*는 typed context 계약을 제공하고SkixWssPinManager는 credential metadata만 담당한다. - 차이/분류: PIN secret과 security/map execution context의 분리(
정책 변경,relocation). 실제 WSS provider가 새 typed context를 소비하는 연결은 이 항목 범위에서 미확인(gap). - 영향/검증: PIN 확인 성공을 곧 patrol 완료로 해석하면 안 된다. model tests/source inspection only.
B-MAP-01 — 지도 metadata 직접 조회와 초기 연결 실패 복구
마지막 변경: 2026-10-15 · 분류: 조회 책임 이동 + 연결 실패 복구 보강 · 제품 판정: 미검토 · 공유 상태: 이번 상세 문서 공개에 포함합니다. 코드/APK/기기 검증 상태는 아래 근거와 공백을 따릅니다.
기존 경로: DeviceAgent MapManager.loadAreaList가 keystore flag에 따라 SD map 복사 또는 encrypted internal backup을 읽습니다. 기존 SkixMapManager의 typed catalog 계약만으로 이 파일 접근이 대체됐다고 보지는 않습니다.
현재 독립 경로: 테스트앱의 공개 위치 관측 요청 → SkixAmrManager.requestFrameworkPositionObservation → 인증 IAmrMotionSnapshot/Binder → Host requestAmrPositionObservation → 초기 지도 연결 실패일 때 AndroidRoomMapSource.refresh → 기존 서명 일치 EncryptionService의 decryptFile("map_meta_sample.json") → 실제 room_list 디코딩 → Host 공간 이름/ID와 위치 관측 결합. DeviceAgent private 데이터 직접 읽기나 신규 데이터 복사는 없습니다.
| 상황 | 변경 전 독립 조회 구현 | 변경 후 | 사용자에게 보이는 영향 |
|---|---|---|---|
| 부팅 초기 KeyStore bind가 false 또는 예외 | source가 close되어 후속 refresh로 복구 불가 | BIND_FAILED로 유지; 명시 위치 조회 시 bind 재시도 | 서비스가 늦게 준비되면 조회로 연결을 다시 시도할 수 있음 |
| 재시도 bind 진행 중/이미 등록됨 | 실패 복구 경로 없음 | bindInFlight/registrationAccepted로 중복 등록 차단 | 반복 조회가 중복 binding을 만들지 않음 |
| bind 재시도만 접수됨 | 조회 재개 불가 | refresh는 false; 연결 callback 뒤 실제 파일 read | 연결 시도만으로 공간 조회 성공을 표시하지 않음 |
| 유효한 지도 또는 Android 자동 재연결 중 | 기존 조회/자동 재연결 | 공개 위치 조회는 BIND_FAILED만 복구; 유효 지도 재로드 안 함 | 위치 새로고침 때문에 정상 공간 목록을 지우지 않음 |
| source 종료/owner 퇴역/시작 전 | 기존 owner 검증 | 재진입 close 이후도 검사; 퇴역 source bind 차단 | 늦은 요청이 이전 소유자를 되살리지 않음 |
| 지도 복구 오류 | 위치 조회와 별도 경로 | 지도 오류를 격리하고 독립 AMR 위치 요청은 계속 | 공간 metadata 장애가 좌표 조회까지 중단시키지 않음 |
보존: 기존 encrypted 파일명·room_list 형식·서명/명시 component 검사·2MiB/UTF-8 입력 제한·generation/serial/FD 수명 검사. 실제 decrypt/디코딩 성공 전에는 snapshot을 만들지 않습니다. 공간명은 실제 지도 이름과 ID를 사용합니다.
남은 범위: 이는 metadata 읽기 복구이며 지도 PGM/YAML 저장 owner·지도 삭제의 예약/AQ 부수 효과·OTA 데이터 이관을 대신하지 않습니다. null binding/binding death 전체 자동 복구는 이번 범위 밖입니다. 지도 조회 성공과 Task/모터 실제 완료는 별개입니다. APK/기기 KeyStore 권한·실제 공간명은 이번 묶음에서 검증하지 않았습니다.
소스/검증: AndroidRoomMapSource.start/refresh/requestBinding, Host requestAmrPositionObservation. 정본 skix-room-map-bind-recovery-evidence-2026-10-15.json에 실제 행동 RED, room-map 10 PASS, 공개 position/place/motion 14 PASS, Android compile과 독립 검토 결과를 기록합니다. 과거 직접 파일 reader 근거는 skix-room-map-evidence-2026-10-08.json입니다.
B-MAP-02 — map delete의 부수효과
- 트리거/경로: map deletion.
- 구형:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/map/MapManager.java:385-394는 map file 삭제와 함께ScheduleDao.deleteAllSchedules(), preference reset, air-quality link false를 수행한다. - 현재:
SkixMapManagerpublic API는 map edit/sync listener와 place catalog binder를 노출하지만 해당 line 범위에서 구형 delete의 schedule/link side effect는 확인되지 않는다. - 차이/분류: 지도 삭제 정책의 side effect가 현재 API에서 명시되지 않음(
gap, 잠재정책 변경). - 영향/검증: 사용자가 map 삭제할 때 schedule/AQ link가 계속 남거나 별도 cleanup이 누락될 수 있다. current source search and map tests only; no end-to-end deletion test.
B-MAP-03 — 지도 파일 저장 실패를 성공·완료로 처리하지 않도록 변경
기록일: 2026-10-15 · 분류: 신뢰성 보강 + 입력 허용 조건 강화 · 제품 판정: 미검토 · 적용 범위: AMRAgent plain 저장/기본 encrypted rename, DeviceAgent 호환 소비 경로. Framework 독립 저장 owner의 이관 완료 항목은 아닙니다.
실제 호출 경로: AMR 지도 응답 → RecvThread.reportMapData/reportEncMapData → 파일 쓰기 또는 rename → MAP_DATA.RESULT → DeviceAgent MainService의 MapData 분기 → 기존 화면·설정·업로드 효과. 이 경로의 파일 성공과 지도 생성 Task의 물리 완료는 서로 다른 판정입니다.
| 비교 지점 | 변경 전 스냅샷에서 확인한 동작 | 현재 코드의 동작 | 실제 영향 |
|---|---|---|---|
| plain 저장 API | void saveMapData가 쓰기 예외를 호출자에게 결과로 전달하지 않음 |
기존 void API 보존; 새 saveMapDataChecked가 boolean 결과 반환 |
기존 호출 호환 유지, 새 수신 경로는 실패 식별 가능 |
| 입력 검사 | 기존 수신측 isValid() 중심; 모든 저장 입력을 검사하지 않음 |
filename, 양의 정수 폭/높이, 유한 해상도/좌표, Base64, 실제 픽셀 수 검사 | 이전에 저장을 시도했던 비정상 입력이 저장 전에 거절될 수 있음 |
| plain 파일 성공 | 저장 결과와 무관하게 응답 RESULT=true |
PGM/YAML 각각 쓰기·flush·fdatasync·close 성공 뒤에만 true | 디스크·권한·파일 충돌 실패를 성공으로 보고하지 않음 |
| metadata 삭제 순서 | 저장 성공 확정 전에 기존 metadata가 삭제될 수 있음 | 양쪽 파일 쓰기 성공 뒤 map_meta_sample.json 삭제 시도 |
입력 거절/쓰기 실패에서 metadata 선삭제 방지 |
| plain 오류 해제/임시파일 | 저장 결과에 성공 처리가 연결되지 않음 | 저장 성공 때만 오류 해제 및 진행용 임시파일 삭제 예약 | 저장 실패만으로 지도 오류를 해제하지 않음 |
| 기본 encrypted 파일 이동 | renameTo의 성공값을 확인하지 않고 true 보고 |
PGM/YAML rename을 모두 시도하고 양쪽 성공일 때만 true | 파일 하나만 이동된 경우 실패 보고 |
| 호환 소비자 | 결과 false를 완료 효과 전에 걸러내는 조건이 없음 | Boolean.TRUE만 기존 MapData 분기 실행; false/누락/다른 타입은 return |
실패로 화면 완료·저장 완료 설정·업로드 완료 처리를 하지 않도록 차단 |
보존한 규격: P5/YAML 형식, 파일명, 기존 stage와 callback, void API. 기본 설정은 CRYPTO_MAP=true, ZIP_MAP=false, SKKEYSTORE=false이므로 plain 경로뿐 아니라 encrypted 비-keystore 경로도 비교했습니다. ZIP/keystore 저장 동작은 이번 수정 범위 밖입니다.
정상 예: 픽셀 수와 폭×높이가 일치 → PGM/YAML 쓰기 완료 → metadata 삭제 시도 → 결과 true → 기존 호환 효과를 허용합니다. true는 해당 파일 처리 성공이며 모터 정지·지도 생성 Task 완료 증명이 아닙니다.
실패 예: PGM 쓰기가 실패하거나 encrypted YAML rename이 실패 → 결과 false → 호환 소비자는 완료 효과 전에 종료합니다. 일부 파일은 이미 바뀌었을 수 있으므로 자동 복구/원복 성공으로 표시하지 않습니다.
남은 한계와 리뷰 포인트: 두 파일 전체의 crash-atomic 교체/rollback은 없습니다. metadata 삭제 반환값도 성공 판정에 포함되지 않습니다. parser/null early drop은 기존처럼 유지되어 모든 실패에서 명시 false 응답을 보장하지 않습니다. 누락 결과를 거절하는 소비자 변경은 RESULT를 생략하는 구형 생산자와 호환 시험이 필요합니다. Framework 저장 owner·OTA 기존 지도 이관·Task 실행 ID와 native 완료 상관·기기 저장 장애 시험은 남아 있습니다.
소스 위치: apps/AMRAgent/java/com/sk/airbot/amragent/model/MapData.java의 saveMapDataChecked, apps/AMRAgent/java/com/sk/airbot/amragent/RecvThread.java의 reportMapData/reportEncMapData, apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/main/MainService.java의 MapData 결과 guard.
검증 범위: actual Java 파일/수신 블록/결과 guard를 실행하는 host 회귀 로그는 20261015-map-storage-result/green-production-block.log(4 PASS), AMR Java compile 로그는 같은 디렉터리 amr-compile.log입니다. 이는 로컬 파일·결과 분기 근거입니다. 초기 RED는 API/소스 확인 수준이어서 실제 동작 TDD 완료로 세지 않습니다. SPEC→QUALITY PASS. APK 설치·Android 실제 파일시스템/SELinux·기기·서버 업로드는 미검증입니다.
2026-10-15 독립 관측 연결 후속 · 로컬 검증 완료: 기존 실제 MapData callback의 저장 결과를 AMRAgent → Framework AmrPowerObservationRuntime → 기존 공개 AMR motion snapshot → 시험앱에 연결했습니다. DeviceAgent MainService가 없어도 결과를 조회하는 경로이며, 파일 저장·업로드·제품 복귀 전체 owner의 이관 완료를 뜻하지 않습니다.
| 구간 | 새로 연결한 책임 | 보존/제약 |
|---|---|---|
| AMRAgent callback | 실제 MapData에 생산자 epoch·단조 순번·수신 시각 추가; 신규 observation 구독에 마지막 결과 재전달 |
legacy 명령/결과는 보존, 최대 순번 이후 같은 epoch에서 순번 재사용 금지 |
| Framework 관측 | Boolean 결과를 검증하고 실패도 보존; 단계 없는 기존 경로는 UNKNOWN | ACK 전 보고는 등록 승인 뒤 공개, 이전 binding callback/역순/epoch 충돌 차단 |
| 공개 SDK | 저장 결과를 독립 immutable 값으로 읽음; 이동 관측이 없어도 조회 | source/타입/순번/세대 검증; physicalComplete=true는 거절; 구형 서버 필드 누락 호환 |
| 시험앱 | 저장 성공/실패, 진행 중/최종 파일/단계 미확인, 최근 5초/오래된 보고 표시 | 파일 경로·key 비공개; 업로드·Task 완료·실제 정지와 별개임을 명시 |
구체적 예: 암호화 저장 결과가 false이고 stage가 없으면 “지도 파일 저장 실패 · 단계 미확인”으로 표시합니다. onCompleted/true도 “최종 파일 보고”일 뿐 현재 Task 성공으로 바꾸지 않습니다. 새 결과가 잘못된 Boolean 타입이면 이전 성공 값을 지우고 watermark를 전진시켜 오래된 성공 보고가 되살아나는 것을 막습니다.
근거와 남음: 실제 callback·runtime·Host·SDK·UI 행동 RED 보존, 관련 Java 회귀 30 PASS + 배터리 runtime 4 PASS, AMR/Host/시험앱 Android 컴파일 exit0(2분7초), SPEC→QUALITY PASS. 구형 추가 suite의 변경 전·후 동일 실패와 fixture 보완은 skix-amr-map-storage-observation-evidence-2026-10-15.json에 별도 기록했습니다. 이번 묶음은 APK/기기/업로드 미검증입니다. 후속은 MapUploader·IoT 응답·제품 복귀 책임의 실제 조립이며, 실행 ID가 없는 파일 결과를 현재 Task에 억지로 상관시키지 않습니다.
2026-10-15 후속 · DeviceAgent 없는 지도 파일 요청 provider: AMRAgent.requestMapStorageObservation → AMRCmdDispatcher → 기존 MapData/EncryptedMap/ZipMapData GET을 추가했습니다. 기존 opGetMap은 유지합니다. 새 진입점은 같은 서명만 허용하고 기존 ZIP 우선→암호화→일반 선택을 보존합니다. 요청 당시 Handler/소켓을 고정해 연결 교체 후 새 소켓으로 재전송하지 않습니다. 다른 호출자의 기존 지도 요청도 삭제하지 않습니다.
반환 true는 로컬 큐 접수입니다. false는 접수 전 거절이며, post 예외는 처리 여부 미확정으로 전파합니다. 접수 이후 소켓이 닫히거나 교체되면 GET을 보내지 않을 수 있고, native 응답도 오지 않을 수 있습니다. 파일 수신은 기존 저장 경로를 타므로 파일 생성/교체 효과가 있습니다. 순수 읽기 조회나 저장·업로드 성공으로 표시하지 않습니다. native 요청 ID가 없어 현재 Task 완료로 연결하지 않습니다.
정본 skix-amr-map-request-provider-evidence-2026-10-15.json: 기존 메서드의 서명 검사 부재 행동 RED와 신규 API 누락 RED, 관련 4 PASS·AMR Java/AIDL compile29초 exit0·독립 SPEC→QUALITY PASS. 테스트의 legacy 우회 재현은 메서드 수준이며 실제 배포 Binder 권한 우회 증거가 아닙니다. 이 기록은 provider 단계 당시 결과입니다. Host/공개 SDK/시험앱 소비 연결은 아래 후속에서 로컬 검증했고, IoT 소비 연결·APK·실기기·업로드는 여전히 미검증입니다.
2026-10-15 후속 · 지도 파일 요청 소비 연결 로컬 검증 완료: 시험앱 명시 버튼 → SkixAmrManager.requestFrameworkMapStorageObservation → 권한 검사 AMR Binder → 현재 Host → 승인된 AMR 관측 binding → provider 파일 GET. DeviceAgent fallback은 없습니다. 기존 관측 연결을 재사용하며 별도 서비스나 Task를 만들지 않았습니다.
- provider true/false를 그대로 전달합니다. true는 로컬 큐 접수이며 실제 전송·파일 저장·업로드 완료를 보장하지 않습니다.
- RPC 실패 또는 호출 전후 binding/Host owner 교체는 unknown으로 표시하며 자동 재전송하지 않습니다.
- 앱 진입·상태 조회·재연결로 파일 GET을 자동 실행하지 않습니다. 중복 클릭을 막고, worker 거절은 미전송으로 표시합니다.
- 이전 연결의 worker 결과는 현재 화면을 덮거나 후속 요청의 busy gate를 해제하지 않습니다. 연결 전환에서 해당 gate를 초기화합니다.
- 결과 카드와 전역 파일 저장 관측 카드는 별개입니다. native 요청 ID가 없으므로 새 저장 보고를 이 요청이나 지도 생성 Task 완료로 연결하지 않습니다.
근거: skix-amr-map-request-consumer-evidence-2026-10-15.json: API 누락 RED → runtime/Host/SDK/UI·권한·수명·기존 파일 관측 관련17 PASS, SPEC→QUALITY PASS, Host/SDK/시험앱 Android 컴파일2분5초 exit0. 첫 빌드는 SDK 경로 환경변수 누락으로 중단돼 환경변수만 보완했습니다. APK/기기/서버 미검증이며 최신 SDK·Host·AMRAgent를 함께 배포해야 합니다.
2026-10-15 후속 · 암호화/ZIP 실패 보고 — 로컬 검토·컴파일 완료, 기기 미검증: 수신 파일 처리 실패가 callback 없이 끝나 독립 관측 화면에 새 실패값이 전달되지 않던 분기를 보강 중입니다. 실제 파일 저장 책임은 AMRAgent에 남습니다. 이 후속은 아래 표의 경로에 한정하며 모든 지도 명령의 완료 규격을 바꾸는 작업이 아닙니다.
| 입력/실패 지점 | 변경 전 해당 분기의 동작 | 현재 작업본의 변경 | 검증·남은 조건 |
|---|---|---|---|
| 암호화/ZIP parser가 null 반환 | 로그/예외 또는 조기 종료; 실패값 전달 없음 | MapData/result=false 보고 |
실제 private 수신 메서드 Java 회귀 범위 |
saveEncMapData() / saveZipMapData()가 false 반환 |
조기 반환으로 실패값 전달 없음 | 실패 보고 후 반환 | 초기 행동 RED에서 재현 |
| 암호화 PGM/YAML 한쪽 누락·null·빈 경로 | 파일 결과 없이 반환 | 실패 보고, 재사용할 파일 경로 미포함 | archive/parser 대역 사용; 실제 native 압축파일 인수 아님 |
| 암호화 일반 경로의 부분 rename 실패 | 실패값에도 PGM/YAML 경로 노출 가능 | 두 rename 모두 시도하되 두 개 모두 성공한 때만 경로 전달 | 실제 File.renameTo 실패 RED 보존; 부분 교체 원복은 없음 |
| 파일 처리 중 예외 | 로그만 남기고 실패값 전달 없음 | 결과를 아직 보고하지 않았다면 실패 보고 | 파일 결과 보고 뒤 callback 예외와 구분 |
| 결과 보고 callback에서 예외 | 보고 전송과 파일 결과가 혼동될 여지 | 같은 결과 자동 재전송·성공 후 추가 실패 보고 없음 | callback 회귀 범위; 상대 프로세스 수신 보장 아님 |
| null 패킷을 private 메서드에 직접 전달 | 무보고 반환 | 유지 | 실제 수신 dispatch 전체의 무보고 보장은 아래 결함 때문에 미확인 |
품질 검토에서 발견한 결함과 후속 수정: 실제 RecvThread.run()의 ENC/ZIP dispatch는 private 메서드 호출 전에 jstr.substring(0, 64)로 로그를 만듭니다. 길이가 64보다 짧거나 해당 위치에 null이 도달하면 예외가 나므로, 새 실패 처리에 도달하지 못하고 수신 루프가 종료될 수 있습니다. private 메서드 테스트만으로 이 경계를 검증했다고 볼 수 없습니다. null이 실제 프로토콜 처리에서 이 위치까지 도달하는 조건도 별도 확인이 필요합니다. 이 문제와 dispatch 회귀를 해결하기 전 품질 검토 통과·이관 완료로 표시하지 않습니다.
후속 수정 결과: 실제 ENC/ZIP dispatch 로그를 null에 안전한 문자열 길이 표시로 교체했습니다. 길이2 응답의 StringIndexOutOfBoundsException 행동 RED 후, 실제 switch 분기를 추출한 회귀에서 짧은/null 입력 뒤 정상 결과 처리를 확인했습니다. 위 문단의 결함은 발견 당시 이력이며 현재 작업본에서 수정됐습니다. null은 전체 switch에도 도달할 수 있으나 private 수신 메서드는 무보고 반환합니다. 소켓 재연결·GET 재전송은 추가하지 않았습니다.
현재 근거: skix-amr-map-receive-failure-evidence-2026-10-15.json. 초기11 PASS/8.74초 뒤 dispatch 회귀를 추가한 최종 영향 검증은12 PASS/9.41초, SPEC→QUALITY delta PASS, AMR Java compile16초 exit0입니다. 이전17초 빌드와 품질 수정 요청은 선행 이력입니다. APK/기기/native 암호화 서비스 실제 영구 저장은 검증하지 않았습니다.
제품 의미와 한계: 실패 보고가 전달되면 시험앱이 새 전역 파일 실패를 표시할 수 있습니다. 요청 ID가 없어 특정 GET/지도 생성 Task의 실패로 확정할 수 없습니다. ZIP/keystore의 반환 true가 실제 영구 저장 완료를 보장하는지도 미확인입니다. IoT 업로드 응답, 저장 후 제품 복귀, 부분 파일 원복, APK/기기 검증은 계속 별도 미완료입니다.
2026-10-15 후속 · ZIP 저장 결과를 실제 암호화 서비스 반환값에 맞춤: 실제 AMR KeystoreLib-release.aar의 IEncryptionService.gz2EncryptedEntryFile(ParcelFileDescriptor)는 boolean 계약입니다. 기존 ZipMapData.saveZipMapData()는 반환값을 무시하고 서비스가 둘 다 없어도 true를 반환했습니다. 앞선 문서에서 true의 실제 저장 보장을 미확인으로 둔 것과 별개로, 이번에는 실제 라이브러리 계약을 확인하고 명시적인 false 누락을 수정했습니다.
| 조건 | 이전 해당 분기의 결과 | 현재 결과/효과 |
|---|---|---|
| manager·Setting 서비스 모두 없음 | 저장 호출 없이 true | false; descriptor 할당 없음 |
| 암호화 서비스가 false 반환 | 반환값 무시 후 true | false 그대로 전달→기존 수신 실패 관측 |
| manager 참조가 두 조회 사이 교체 | 두 번째 조회에서 null/다른 서비스 가능 | 한 번 캡처한 서비스에 한 번 호출; fallback 우선순위 유지 |
| descriptor 생성 결과 null | null로 서비스 호출 가능 | 호출하지 않고 false |
| 서비스 호출에서 예외 | false지만 descriptor 누수 가능 | false, descriptor 해제; 자동 재전송 없음 |
| 현재 archive PGM/YAML 한쪽 또는 둘 다 없음 | 이전 이름 유지/저장 시도/true 가능 | 이름을 새로 추출하고 쌍이 없으면 저장 요청 차단 |
| 정상 서비스 true | true, descriptor 해제 없음 | true 유지·호출 후 descriptor 해제 |
부수 효과·호환: checksum→decode→압축 해제→archive 순서는 유지합니다. manager 우선/기존 Setting fallback과 공개 메서드 시그니처도 보존합니다. 한쪽 파일만 있는 archive를 이제 거절하는 것은 의도적인 허용 조건 강화이며 제품 리뷰 대상입니다. 로컬 descriptor close의 IOException은 이미 받은 저장 결과를 바꾸지 않습니다. 실제 압축파일 처리·서비스 구현·영구 저장·부분 파일 원복은 아직 기기 인수하지 않았습니다.
근거: skix-zip-map-storage-result-evidence-2026-10-15.json: 실제메서드 행동 RED4건/2.93초, 서비스false 별도RED0.84초→ZIP/수신/관측/SDK/시험앱 관련17 PASS/13.31초, SPEC→QUALITY PASS, AMR 단위Java compile16초 exit0. archive/parser/서비스/PFD 경계는 호스트 대역이므로 실제 파일 영구저장 시험으로 세지 않습니다. 반환true를 Task 완료·모터 정지·IoT 업로드 성공으로 연결하지 않습니다. APK/기기는 변경하지 않았습니다.
2026-10-15 후속 · IoT 지도 업로드 요청별 파일 소유권 — 로컬 검토·IoT Java 컴파일 완료, 실제 서버 미검증: 기존 IoT S3TransferManager.decryptUploadFile은 복호화 바이트를 instance 공용 필드에 저장했습니다. HTTP 실패 뒤 남은 PGM이 서비스가 없는 다음 YAML 요청에 쓰이거나, 동시에 수행한 요청이 다른 요청의 바이트를 사용/제거할 수 있었습니다. 실제 메서드 회귀에서 재현한 문제이며 새 Framework의 정상 규격으로 보존하지 않습니다.
| 지점 | 수정 직전 | 현재 변경 | 의미/남은 확인 |
|---|---|---|---|
| 복호화 파일 | 모든 요청이 공용 byte[] 사용 | 호출별 local byte[] | 동시 요청의 파일 혼선 제거; PGM/YAML 한 쌍의 원자적 snapshot은 별도 |
| 서비스 선택 | 두 번 조회; 없음이면 늦은 bind callback이 공용 필드 갱신 | 기존 IoT 부팅 binding에서 provider 한 번 확보; 없음이면 false | 늦은 복호화·이전 요청 재실행 없음; 새로운 명시 요청 필요 |
| 파일 descriptor | 별도 helper가 raw FD stream 사용 | AutoCloseInputStream이 반환 FD를 소유하고 read 종료 시 닫음 | 실제 provider FD 인수는 기기에서 별도 확인 |
| 입력·실패 | null URL에서도 먼저 복호화; 일부 RuntimeException 이탈 | null/빈 URL·경로·MIME은 미실행 false; null FD/복호화 예외/HTTP 비200은 false | 정상 완료/실패값 유지; MQTT 최종 응답 전체를 보장하는 변경 아님 |
| HTTP 대기·로그 | connect/read 제한 없음; 실패 시 서명 URL 출력 | connect15초/read30초, 상태 코드 또는 예외 클래스만 기록 | 타임아웃은 원격 저장 여부 확인이 아니며 자동 재요청하지 않음 |
현재 순서: 입력 확인 → 현재 provider 확보 → 해당 파일 복호화 → FD 읽기/닫기 → 그 요청의 바이트로 PUT → HTTP200 여부 반환 → HTTP disconnect. PUT·기존 Host 헤더·MIME·Content-Length·HTTP200 성공 규칙은 유지했습니다. plain 파일 내용을 로그로 기록하지 않습니다. 직접 요청 재시도는 추가하지 않았으며 HTTP 라이브러리 내부 전송 동작까지 검증한 것은 아닙니다.
검증 정본: skix-iot-map-upload-isolation-evidence-2026-10-15.json. 실제 메서드 다섯 행동 RED→최종5 PASS(5.42초)·독립 SPEC→QUALITY; IoT Java 단위 컴파일46초 exit0. 첫 컴파일은 기존 class 파일 접근 Cannot allocate memory로 실패했고, 해당 파일 javap 정상 확인 후 소스 수정 없이 단위 컴파일을 재시도했습니다. native 복호화와 HTTP endpoint는 대역이며 실제 서버 성공 증거가 아닙니다. 이 변경은 독립 지도 export 소비의 전제를 보강한 것입니다. Util.requestMapUpload의 DeviceAgent 진입 의존, AES128 임시 평문 경로, native 요청별 파일 결과 식별, 부분 업로드 복구, 전체 MQTT 응답/제품 복귀는 남아 있습니다. APK·기기 변경은 하지 않았습니다.
2026-10-15 후속 · DeviceAgent 초기화 없이 지도 암호화 선택과 업로드 결과 전달
적용 범위: IoT Util.getKeyStore/setKeyStore와 명시 지도 업로드 IotAgent.onS3Upload(type, pgmurl, yamlurl, id, replyto, result)입니다. DeviceAgent가 요청을 넘기는 진입 조건까지 제거한 변경은 아닙니다. 초기 검토에서는 품질 수정 필요로 판정됐습니다. 최신 로컬 수정 결과는 바로 아래 “공용 backend 실패 계약의 소비 경로 정합” 후속을 따릅니다. 문서 공유와 코드 릴리즈를 구분합니다. 이번 상세 공유에는 검토 반려 내용까지 포함하며 APK 배포는 하지 않습니다.
변경 전: IoT backend 값은 기본 false였고, DeviceAgent의 isA1Initialized 통지로만 갱신됐습니다. keystore 사용 제품이라도 DeviceAgent가 없으면 AES128을 선택했습니다. 업로드 worker는 null URL이나 selector/transfer 예외로 끝나면 요청의 실패 응답을 보내지 못했습니다.
변경 후 순서: 명시 legacy 설정 유무 확인 → 미설정이면 AMR Setting.useSKKeyStore·기존 DeviceAgent Utils.getMapKeyStore와 같은 property 조회 → 요청에서 backend 한 번 선택 → 기존 PGM/YAML 파일 경로와 MIME로 업로드 → 두 결과가 true일 때만 성공 → 원래 id/replyto로 결과 발행 한 번 시도. 발행 예외를 파일 업로드 재시도로 연결하지 않습니다.
| 입력·상황 | 현재 동작 | 보존·제품 영향 |
|---|---|---|
| DeviceAgent 초기화 없음, 플랫폼 keystore=true | keystore 경로 선택 | 기존 제품 설정을 새로 만들지 않음 |
| 초기화 없음, property=false 또는 미설정 | AES128 선택 | 기존 property default=false 보존 |
| legacy 초기화가 true/false를 명시 | 명시값 우선, property 미조회 | 기존 통지 계약 보존; 서로 다르면 legacy 값 우선 |
| backend property 접근/반사 호출 실패 | IllegalStateException; 해당 업로드 worker는 false 응답 | AES128로 조용히 대체하지 않음; 실제 기기의 hidden API 접근은 미검증 |
| mapupload 외 type 또는 null type | 업로드 worker 생성 안 함 | 알 수 없는 작업을 지도 요청으로 수행하지 않음 |
| result=false 또는 어느 URL이 null/빈 문자열 | transfer 없이 false 응답 한 번 | 기존 There is no AMR map 문구 보존 |
| 첫 파일 transfer가 false 반환 | 두 번째 파일도 기존 순서대로 시도, 최종 false | 두 파일 처리 방식 보존; 한 파일 성공을 전체 성공으로 보지 않음 |
| selector 또는 첫/두 번째 transfer가 RuntimeException | 이후 transfer 중단, false 응답 한 번 | 예외 클래스만 기록; 서명 URL·키·내용 미기록 |
| PGM/YAML 모두 true | true 응답, Map upload success |
기존 MIME·파일명·성공 조건 보존 |
| 응답 publisher가 RuntimeException | 클래스명 기록, 재업로드/재발행 없음 | 발행 시도 1회이지 서버 수신 보장은 아님 |
품질 검토 반려 · 공용 getter의 예외 계약이 다른 경로에 미치는 영향: 이전에는 boolean만 반환하던 getter가 property 조회 실패 때 IllegalStateException을 던지도록 바뀌었습니다. 명시 mapupload worker가 예외를 처리해도 모든 소비자가 안전해지는 것은 아닙니다. 아래는 실제 호출부를 다시 확인한 결과이며, 수정 완료로 표시하지 않습니다.
| 소비 경로·소스 | 초기 검토 당시 코드 | 발생 가능한 영향 | 후속 수정·검증 기준 |
|---|---|---|---|
IotAgent.onS3Upload(String type) · 기존 지도 생성 업로드 |
getter를 worker 안에서 호출; RuntimeException 처리 없음 | SLAM_STATUS 실패 발행과 mMapping 등 후속 정리에 도달하지 못할 수 있음 |
정상 성공/실패 및 예외에도 기존 terminal event·정리 순서를 검증; 미확인 물리 완료를 만들지 않음 |
IotAgent.executeIotCommand · factoryReset 분기 |
mode 변경·설정 요청 뒤 getter 호출; 분기 내부 예외 처리 없음 | backend 실패 전에 일부 효과가 이미 발생; 전체 상위 handler 영향은 추가 확인 필요 | backend 실패 때 삭제 경로 진입 금지 및 부분 효과/결과 전달 검증; 실제 reset은 실행하지 않음 |
CmdTopic.setOnDeviceJsonMapPing · metadata 재조회 |
JSON/IO/RemoteException만 처리 | 새 RuntimeException이 method 밖으로 전파될 수 있음 | 예외 격리·기존 metadata 보존/동기화 효과 대조 |
CmdTopic.subscribeSetMapData · 지도 다운로드 |
RuntimeException catch가 이미 있음; catch 뒤 기존 응답 호출 | uncaught로 분류하지 않음. 단, 실패 응답의 실제 의미와 부분 파일 효과는 추가 비교 필요 | setter/getter 실패와 topic 최종 결과가 일치하는지 확인 |
초기 판정: 명시 mapupload 경로의 로컬 회귀는 통과했지만, 공용 getter 변경 묶음은 다른 소비자 영향 때문에 수정 필요입니다. 같은 backend 사용 여부에 대한 명세 검토 PASS를 전체 예외 안전성 PASS로 확장하지 않습니다. 여기서 factory reset이나 파일 삭제를 실행한 것은 아닙니다.
검증 경계: 실제 getter/worker Java를 추출해 실행하며 property·Thread·transfer·publisher만 경계 대역입니다. RED 3실패/1기존통과 → 관련 9통과(기존 파일격리 5 포함); IoT Java 단위 컴파일26초 exit0. 명세 검토 PASS 뒤 품질 검토에서 위 소비자 예외 공백을 지적했습니다. 정본은 skix-iot-independent-map-backend-evidence-2026-10-15.json이며 이 묶음의 최종 검토는 수정 필요입니다. 이 기록은 native keystore·HTTP·MQTT·APK·기기 동작 증거가 아닙니다.
남은 것: Util.requestMapUpload의 DeviceAgent gate, native 요청별 완료 식별, PGM/YAML 동일 버전 snapshot/export, AES128 평문 임시파일 경합(아래 후속에서 로컬 수정), 서버 수신·제품 복귀·최신 APK 기기 검증. 부분 업로드 원복과 요청 간 파일쌍 일관성을 이번 수정으로 보장하지 않습니다.
2026-10-15 후속 수정 · 공용 backend 실패 계약의 소비 경로 정합
최신 로컬 상태: 위 초기 품질 반려의 세 소비 경로를 수정하고 명세→품질 검토 및 영향 시험·IoT Java 컴파일을 마쳤습니다. 이 후속 수정 설명은 로컬 정본/Wiki 원본 초안입니다. 직전 공개본의 품질 반려 기록과 구분하며, APK·기기 배포는 하지 않았습니다.
| 경로·입력 | 수정 전 실패/불일치 | 현재 수정·실행 순서 | 보존·남은 확인 |
|---|---|---|---|
| factoryReset backend 조회 실패 | mode 설정·config 요청 후 예외; 부분 효과와 예외 전파 | backend 먼저 확인; 예외면 -1 반환, mode/config/삭제/명령 시작 안 함 | 정상 keystore/AES128·인증파일 유무 분기 유지; backend 외 reset 실패/전체 reset 이관은 별도 |
| 기존 지도 생성 업로드의 backend/첫·두 번째 transfer 예외 | worker 종료로 SLAM_STATUS 결과/로컬 정리 누락 가능 | 업로드 실패로 변환→기존 Failed 결과 1회 시도→finally에서 mapping/map ID 정리 | 기존 두 파일 순서·경로·MIME·성공 조건 유지; 전역 legacy ID를 native 실행별 완료 근거로 쓰지 않음 |
| 위 업로드의 결과 발행 예외 | local mapping/map ID 정리 미도달 가능 | 예외 클래스 기록·finally 정리; 업로드/발행 재시도 없음 | 발행 시도와 서버 수신은 다름; 전역 요청 간 소유권 문제는 남음 |
| metadata 최초 backend 조회 예외 | method 밖 RuntimeException 전파 | 예외 격리·class만 기록; synchronize/reload 호출 없음 | 잘못된 AES128로 대체하지 않음; 실제 파일/서비스 인수는 미검증 |
| metadata decode 뒤 설정 변화 | 두 번째 getter가 다른 backend의 reload 경로 선택 가능 | 한 번 선택한 backend로 decode와 reload 모두 수행 | ON_DEVICE room_list·공간 ID/이름 계약 유지; 늦은 bind callback의 공용 바이트 수명은 별도 |
소스: IotAgent.executeIotCommand의 factoryReset 분기, IotAgent.onS3Upload(String), CmdTopic.setOnDeviceJsonMapPing. subscribeSetMapData의 기존 RuntimeException catch는 그대로 보존했습니다. 이번 수정은 임의 reset이나 지도 삭제를 실행하는 작업이 아닙니다.
검증 정본: skix-iot-map-backend-callers-evidence-2026-10-15.json. 실제 분기/메서드를 실행한 행동 RED 4건(3.00초)→최종 영향 15 PASS(18.25초; callers4/backend4/isolation5/metadata2의 한 묶음), 독립 SPEC→QUALITY PASS, IoT Java 단위 컴파일35초 exit0. 중간 시험의 JSON 예외 상속에 따른 컴파일 실패를 별도 catch로 수정한 이력도 기록했습니다. 실제 reset·native 파일 서비스·HTTP/MQTT·APK·기기 시험은 하지 않았습니다.
전체 목표의 남은 부분: 지도 업로드 요청의 DeviceAgent gate 제거, 동일 버전 PGM/YAML 내보내기, native 요청별 완료 식별과 소유권, 서버 최종 응답·제품 복귀는 아직 미완료입니다. 이 실패 처리 정합만으로 독립 지도 업로드가 완성됐다고 보지 않습니다.
2026-10-15 후속 수정 · AES128 업로드 요청별 평문 격리
변경 경로: S3TransferManager.decryptUploadFile_AES128. 기존 keystore 경로에 이어 AES128 경로의 업로드 수명도 분리했습니다. 클라우드 요청의 DeviceAgent 진입 경로를 이 변경만으로 제거한 것은 아닙니다.
| 비교 항목 | 기존 동작 | 변경 동작·영향 |
|---|---|---|
| 복호화 후 전송 | /mnt/data3/logs/comm/office.pgm 또는 office.yaml 공용 파일에 쓰고 다시 읽음 |
해당 호출의 byte[]를 바로 PUT; 두 요청이 같은 파일을 덮어쓰거나 삭제하지 않음 |
| HTTP 실패 | 평문 파일이 남고 signed URL이 로그에 출력될 수 있음 | 새 평문 임시파일 생성 없음; 상태 코드 또는 예외 클래스만 기록 |
| 네트워크 대기 | 명시 connect/read 제한 없음 | connect 15초/read 30초; 실패 시 false, 자동 재업로드 없음 |
| 잘못된 입력·복호화 예외 | null 입력이나 RuntimeException이 worker 밖으로 전파 가능 | 입력 검증·IOException/RuntimeException 격리, 전송하지 않고 false |
| 보존 계약 | AES CBC decode·PGM/YAML MIME·순차 업로드·HTTP 200 성공 판정 | 같은 계약 유지; 기존 파일/URL/응답 ID를 다른 요청의 성공 근거로 쓰지 않음 |
검증 정본: skix-iot-aes128-upload-evidence-2026-10-15.json. 행동 RED 4건(3.32초)→최종 영향 17 PASS(12.44초), 독립 SPEC→QUALITY PASS, IoT Java 단위 컴파일27초 exit0. 실제 메서드의 동시 요청, HTTP 거절, 입력/복호화 실패, 전송 데이터·시간 제한을 확인했습니다. 시험에서는 기존 절대 임시 디렉터리만 fixture 디렉터리로 바꾸며 파일 읽기·전송 body는 실제 처리하고 암호 서비스/HTTP 서버는 대역을 사용합니다. 이 시험은 암호 provider·실서버·실기기 성공을 입증하지 않습니다.
후속: 이번 변경은 로컬 코드·Wiki 원본 초안입니다. 기존에 남아 있던 평문 파일을 기기에서 삭제하지 않았습니다. DeviceAgent gate 제거, 동일 버전 PGM/YAML export, native 완료 상관, 서버 응답·제품 복귀는 계속 이관해야 합니다.
2026-10-15 후속 수정 · ZIP archive의 모호한 파일쌍 거절
기존: ZipMapData.saveZipMapData는 archive의 마지막 .pgm과 마지막 .yaml을 각각 선택했습니다. old.pgm/new.yaml, 서로 다른 디렉터리의 파일, 같은 entry가 두 번 있는 archive도 provider 호출에 도달할 수 있었습니다.
현재: PGM/YAML 각각 하나만 허용하고, 전체 경로에서 확장자를 뺀 이름이 일치하며 basename이 비어 있지 않을 때만 기존 저장 provider에 전달합니다. 모호한 archive는 FD 생성·provider 호출 전에 false로 거절합니다. metadata와 YAML/PGM 순서 변경은 허용하며 manager 우선/fallback·provider boolean·FD finally close는 보존합니다. 서로 다른 지도의 파일을 임의로 선택하는 기존 동작은 의도적으로 제한했습니다.
사용자 영향: 모호한 archive는 기존 지도 수신 경로의 실패 관측으로 이어집니다. 정상 이름의 파일쌍이 동일 시점의 내용을 담는지, YAML 내부 image 참조가 맞는지는 이 검사만으로 증명하지 않습니다. 파일 내용·archive parser·저장 provider의 실기기 검증도 남습니다.
검증 정본: skix-amr-map-archive-pair-evidence-2026-10-15.json. 실제 저장 메서드 RED 2 FAIL/기존·정상 6 PASS(6.41초)→지도 영향 23 PASS(20.02초), 독립 SPEC→QUALITY PASS, AMR Java compile19초 exit0. archive entry와 저장 provider/FD는 대역이므로 실제 tar 해제나 암호 저장 검증이 아닙니다. 로컬 소스 검증이며 APK/기기·공개 Wiki 배포는 하지 않았습니다. DeviceAgent 업로드 gate·요청별 버전/PFD export·native 완료 상관·MQTT 최종 응답은 미완료입니다.
2026-10-15 후속 수정 · 저장 성공한 원본 지도 응답을 FD로 고정
기존 문제: 저장 성공 관측과 이후의 공용 지도 파일 읽기는 별개의 시점입니다. 다음 응답이 파일을 교체하면 소비자가 처음 관측한 응답과 다른 자료를 읽을 수 있습니다. 이번 변경은 파일을 다시 읽는 대신 저장 성공 직후의 원본 native 응답을 보관합니다.
실제 경로: RecvThread.reportMapData/reportEncMapData/reportZipMapData → reportMapStorage → CallbackHandler.notifyMapStorage → MapStorageExport.capture → 같은 서명의 호출자가 IAMRControl.openMapStorageSnapshot → read-only reliable pipe. 일반·AES128·ZIP 형식은 각각 plain/aes128/zip으로 구분합니다. 성공 관측의 epoch·순번·수신시각과 원문을 같은 callback lock 아래에서 연결합니다.
| 조건·상황 | 변경 동작 | 업무 동작에 미치는 영향 |
|---|---|---|
| 저장 성공·유효 원문 | 최신 성공 응답의 원본 UTF-8 바이트와 관측 식별자를 보관 | 나중에 바뀌는 공용 경로를 다시 읽지 않음 |
| 실패·후속 관측·순번 overflow | 이전 export를 무효화 | 과거 성공 데이터를 현재 실패의 대체 자료로 쓰지 않음 |
| epoch·순번·형식 불일치 | FD를 반환하지 않음 | 다른 지도 응답을 임의로 선택하지 않음 |
| 관측 후 5초 초과·미래 시각 | FD를 반환하지 않음; 5초 경계는 허용 | 최신 원문만 요청 가능; 자동 재요청 없음 |
| 원문이 UTF-8 16MiB 초과 | export 보관 거절 | 기존 저장 결과는 유지하되 export 가능으로 표시하지 않음 |
| 다른 서명 호출자 | 캐시 접근·FD 생성 전에 거절 | 이 내부 자료 전달은 동일 서명 앱에 한정 |
| writer 2개가 사용 중 | 추가 요청에 null; 대기열 없음 | 느린 소비자가 무제한 작업을 적재하지 못함 |
| 전송 중 오류·10초 초과 | reliable pipe 오류 후 FD 정리 | 소비자는 전체 프레임과 checkError()를 확인해야 성공 판정 가능 |
전달 계약: SKIXMAP1 magic, epoch, 순번, 관측시각, 형식, 바이트 수, 원본 응답 바이트 순서입니다. 모든 text 값은 Java DataOutputStream 규약을 따릅니다. 원문은 내부 native packet이며 공개 SDK의 정규 지도 파일쌍이 아닙니다. 이미 연 FD는 선택한 바이트를 유지하며 다음 관측으로 내용이 바뀌지 않습니다. 기존 파일 저장·callback 결과·정상 파일 삭제 순서·서비스 fallback은 보존합니다.
완료를 뜻하지 않는 것: FD 전달은 파일의 원자적 교체, YAML image 참조 검증, native 요청 ID 상관, 특정 Task 완료, 모터 정지, 클라우드 업로드 성공을 증명하지 않습니다. Host의 검증/변환, 공개 SDK 소비 계약, IoT 업로드 진입의 DeviceAgent 의존 제거와 최종 MQTT 응답 연결은 아직 남습니다.
검증 정본: skix-amr-map-snapshot-export-evidence-2026-10-15.json. 실제 클래스·생산 메서드 시험 7개를 포함한 지도 영향 30 PASS(31.70초), 독립 SPEC→QUALITY PASS. Android pipe·저장 provider·인증 환경은 대역으로 검증했으며 실제 Binder/PackageManager·프로세스 사망·기기 FD 수명 시험은 남습니다. APK·기기·공개 Wiki 배포는 하지 않았습니다.
2026-10-15 후속 수정 · Framework에서 현재 지도 snapshot FD 받기
기존: AMR의 원본 export provider가 있어도 Framework의 AmrPowerObservationRuntime은 저장 성공/실패만 관측했습니다. export 형식·가용 여부를 전달하거나 해당 관측의 FD를 받는 경로는 없었습니다.
현재 경로: AMR MapData callback → receiveMapStorage → immutable 저장 관측 → mappingSnapshot.mapStorageObservation의 exportFormat/exportAvailable → runtime의 openMapStorageSnapshot(epoch, sequence, format, bindingGeneration) → 현재 AMR Binder. 이 메서드는 내부 native packet FD를 반환하며 아직 공개 SDK나 IoT 업로드 호출에 연결하지 않았습니다.
| 판정 시점 | 조건·처리 | 기존 업무와 다른 점 |
|---|---|---|
| callback 수신 | 실제 Boolean 저장 성공·export 가능 표시와 plain/aes128/zip 형식만 인정 | 잘못된 export 표시는 무시하되 기존 저장 결과 관측은 보존 |
| 요청 전 | 정확한 epoch/순번/형식/연결세대, 최신 성공 관측·등록 ACK·owner·5초 freshness 확인 | 조건 불일치·실패 자료는 AMR에 RPC하지 않음 |
| RPC | 기존 acknowledged AMR 연결에서 lock 밖으로 한 번 호출 | DeviceAgent에 바인딩하거나 자동 재요청하지 않음 |
| RPC 후 | owner·연결·같은 관측 객체·freshness·clock 재검사 | 수신 중 상태가 바뀌면 받은 FD를 닫고 null; 이전 세대 자료를 내보내지 않음 |
| 소비 책임 | 전체 프레임·식별자·길이·checkError 검증 후 close | FD 반환만으로 자료 내용·Task/물리/업로드 완료 판정 금지 |
진행 범위: Framework의 내부 AMR 수신 연결까지입니다. 기존 IoT Util.requestMapUpload → SkixDeviceGateway → SkixMapManager → DeviceAgent REQMAP은 여전히 남습니다. Host의 원문 검증/지도 모델 변환·공개 SDK·IoT 전송·최종 MQTT 응답을 이관해야 독립 업로드 경로가 완성됩니다. native 요청 ID가 없으므로 전역 후속 관측을 특정 요청의 완료로 단정하지 않습니다.
검증 정본: skix-host-map-snapshot-export-evidence-2026-10-15.json. 실제 runtime 행동시험과 관련 회귀·Host Java 컴파일 결과는 정본에 기록합니다. Android Bundle/Binder/FD는 대역이므로 실제 서명 인증·프로세스 사망·기기 FD 수명은 검증하지 않았습니다. APK/기기·공개 Wiki 배포는 하지 않았습니다.
2026-10-15 후속 수정 · 원본 지도 응답에서 요청별 파일쌍 만들기
기존: Framework는 성공한 원본 응답 FD를 받을 수 있지만 업로드에 필요한 PGM/YAML 자료를 아직 만들지 못했습니다. 기존 IoT 경로는 DeviceAgent 결과를 기다린 뒤 공용 경로의 파일을 읽었습니다.
현재 경로: AmrPowerObservationRuntime.readMapStorageSnapshot → 현재 관측의 AMR FD → MapSnapshotDecoder.decode → reliable pipe checkError() → 동일 관측·연결·owner·freshness 확인 → 호출 전용 FilePair. 입력·출력은 메모리이며 공용 파일을 생성하거나 다시 읽지 않습니다. FD는 성공·실패 모두 닫고 실패를 자동 재요청하지 않습니다.
| 형식·조건 | 기존 처리와 보존한 계약 | 현재 변경·사용자 영향 |
|---|---|---|
| 일반 지도 | Response/Get/MapData의 width·height·resolution·posX·posY·base64 pixels로 P5/YAML 생성 | 같은 P5 header·origin·negate와 office.pgm 참조로 byte pair 생성; 숫자 표기는 Locale.ROOT로 고정 |
| ZIP | base64 문자열의 SHA-256, gzip/tar에 든 PGM/YAML | 원본 문자열 hash 검증 후 실제 archive를 메모리로 읽음; storage/keystore 서비스 재호출 없음 |
| AES128 | EncryptedMap archive의 .pgm.enc/.yaml.enc |
암호문 그대로 pair 생성하고 encoding=aes128; 복호화 성공이나 평문 지도라고 보고하지 않음 |
| frame | epoch·순번·형식·관측시각 | 모두 예상 관측과 일치해야 하며 magic·길이·EOF·UTF-8도 확인; 잘못된 원문 거절 |
| freshness | 저장 결과와 요청 완료는 다른 사실 | 수신 후 5초 이내만 처리하며 decode 후에도 최신 관측 재검사 |
| 파일쌍 | 각각 단일 파일·같은 전체 stem | 중복·서로 다른 stem·빈 파일·누락 거절; metadata·YAML/PGM 순서는 허용 |
| archive 경계 | 기존 tar/gzip 자료 형식 유지 | absolute/부모 경로·symlink·sparse·특수 entry 거절; 디스크 extraction 없음 |
| 자원 제한 | 기존 저장 경로의 제한 없는 확장과 별개인 신규 export 조건 | 원문16MiB·누적 gzip32MiB·entry256·파일쌍 합계16MiB; 초과 자료는 실패 |
| JSON 구조 | 기존 parser에 depth/구조 한도 없음 | 파싱 전에 depth32·구조문자/문자열 시작4096·따옴표 밖 연속 literal256 제한; base64 문자열은 원문 byte 한도 내 허용 |
| 결과 소유 | 공용 파일의 후속 변경 가능 | pair 내부 byte 배열은 외부에 노출하지 않고 getter는 복사본 반환 |
완료 근거: 자료 변환 성공은 해당 전역 저장 관측에 대한 성공만 뜻합니다. native 요청 ID가 없어 특정 Task/새 GET 요청 완료로 단정하지 않으며 모터 정지·원자적 파일 교체·클라우드 업로드·MQTT 최종 응답을 보장하지 않습니다. ZIP 파일 내용의 PGM/YAML 의미·YAML image 참조 검증과 AES128 실제 복호화는 별도 후속입니다.
검증 정본: skix-map-snapshot-decoder-evidence-2026-10-15.json. 실제 JSON/gzip/tar와 Framework runtime의 FD 수신·변환·오류 정리 시험을 포함해 33 PASS(11.48초), 최종 Host Java 단위 컴파일80초 exit0입니다. 앞선 컴파일의 종료 신호(exit143)는 보존하고 최종 소스로 재실행했습니다. 품질 검토에서 JSON 깊이 제한 누락이 반려되어 실제 입력 RED 2 FAIL 뒤 제한을 추가했고 재검토 PASS입니다. Android Binder/FD는 대역이며 native 실기기·실제 archive 생산자·APK 설치 증거는 아닙니다. 공개 SDK·IoT 업로드 연결·DeviceAgent REQMAP 제거는 아직 남습니다. 원본 Wiki와 공개 문서의 배포 상태는 아래 기록 이력 및 별도 업로드 근거로 확인합니다.
2026-10-15 후속 수정 · 일반 지도 수신의 실패 관측 누락
기존 문제: RecvThread.reportMapData의 parser null·유효하지 않은 지도·파싱/저장 예외는 로그만 남기고 끝났습니다. 암호화/ZIP 수신과 달리 Framework·시험앱이 해당 수신 실패를 관측하지 못할 수 있었습니다.
수정: 기존 MapData/result=false envelope로 실패를 1회 보고합니다. 이 예외/invalid 실패 보고에는 파일 경로를 넣지 않고 지도 본문도 로그에 쓰지 않습니다. 결과 callback을 시도하기 전에 보고 상태를 기록하여, callback 실패가 저장 결과를 실패로 바꾸거나 재발행하지 않게 합니다. reportToDA라는 기존 메서드 이름을 유지하지만 현재 observer callback도 이 결과를 받으며 DeviceAgent 프로세스가 실패 판별을 대신하는 구조가 아닙니다.
보존: 정상 onProgress/onCompleted·PGM/YAML 경로, 저장 false 결과, 성공 후 파일 정리 예약·오류 해제 순서는 유지합니다. null 패킷이나 종료된 소켓에는 결과를 만들지 않습니다. 기존 저장 false 응답의 경로와 파일 부분 효과는 이 수정으로 제거·원복하지 않습니다.
분기별 실제 효과: 기준 소스는 apps/AMRAgent/java/com/sk/airbot/amragent/RecvThread.java의 reportMapData와 reportMapStorageFailure입니다. 아래는 현재 코드를 풀어 쓴 설명이며, 새 기기 시험 결과가 아닙니다.
| 입력·발생 시점 | 기존 수신 처리 | 현재 수신 처리 | 파일·상태에 남는 효과 / 리뷰 포인트 |
|---|---|---|---|
| 소켓 종료 또는 null 패킷 | 결과 없이 종료 | 같은 동작 유지 | 진입 시 clearCheckMapData()는 호출됨. 따라서 이 경우에도 완전한 무효과는 아님 |
| parser가 null 또는 invalid 반환 | 로그 후 결과 없이 종료 | MapData/result=false 보고 시도 1회 |
새 파일 저장 없음. 실패 envelope에는 PGM/YAML 경로 없음; 지도 본문 로그 제거 |
| 파싱 또는 저장 중 예외 | 예외 로그 후 결과 없이 종료 | 예외 클래스만 기록하고 실패 보고 시도 1회 | 저장 중 이미 생긴 부분 파일은 원복하지 않음. 실패 보고도 전달 실패할 수 있음 |
| 저장 함수가 false 반환 | 정상 결과 경로에서 false 전달 | 같은 동작 유지 | 기존 PGM/YAML 경로는 포함됨. 경로 존재가 성공 근거가 아니며 소비자는 반드시 result 검사 |
| mapping 중 저장 성공 | 임시 파일·onProgress·10초 뒤 삭제 예약 | 같은 동작 유지 | 삭제 예약 → 결과 callback → 지도 오류 해제 순서. onProgress는 전체 지도 생성 완료가 아님 |
| mapping 밖 저장 성공 | office 파일·onCompleted 보고 | 같은 동작 유지 | 이 stage는 기존 파일 수신 규격. native 실행 ID가 없어 특정 Task 완료로 연결할 수 없음 |
| 결과 callback이 예외 발생 | 기존 예외 처리로 종료 | 이미 보고 시도한 결과를 실패로 바꾸거나 재발행하지 않음 | 저장 성공이라도 뒤의 오류 해제에 도달하지 못할 수 있음. 전달 성공을 보장하거나 자동 재시도하지 않음 |
사용자가 보게 되는 차이: 이전에는 invalid/예외 수신에서 시험앱의 기다림이 남을 수 있었지만, 현재는 observer 전달이 성공하면 파일 처리 실패를 표시할 근거가 생깁니다. 특정 요청과의 상관이나 전달 보장은 아직 없으므로 모든 기다림이 해결됐다고 판단하지 않습니다.
검증: skix-amr-plain-map-failure-evidence-2026-10-15.json. 실제 메서드 행동 RED 2 FAIL/기존 정상 2 PASS(6.27초)→최종 영향 20 PASS(27.33초), 독립 명세→품질 PASS, AMR Java 단위 컴파일26초 exit0. parser/저장/native callback은 시험 대역이며 실제 기기·실제 서버 결과가 아닙니다. 기존 block 추출 시험에는 새 enclosing 지역변수 선언만 추가했습니다.
남은 범위: 로컬 코드·Wiki 원본 초안이며 APK/기기는 변경하지 않았습니다. native 요청 ID가 없는 전역 파일 관측을 특정 Task나 업로드 요청의 완료로 쓰지 않습니다. DeviceAgent 업로드 진입 의존, 같은 버전 파일쌍 export, 최종 MQTT 수신·제품 복귀는 계속 미완료입니다.
B-PLACE-01 — place catalog 조회
- 트리거/경로: cloud/UI place query.
- 구형:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/map/MapManager.java:182-204,359-365의getAreaById,getAllAreaList,getStationAreaId가 process-local list를 반환한다. - 현재:
skix-framework/base/core/java/com/sk/intellix/framework/manager/SkixMapManager.java:46-58은 Binder snapshot을FrameworkPlaceCatalog.Snapshot으로 변환한다. 확인된 typed consumer는apps/SkixFrameworkTest/src/androidTest/java/com/sk/intellix/frameworktest/AmrPlaceConsumerDeviceTest.java이며, IotAgent 쪽에는apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/mqtt/topic/ServerMapSnapshotDecoder.java/legacy topic decoder가 남아 있다. - 차이/분류: local singleton에서 versioned remote snapshot으로 이동(
relocation,신뢰성 변경). - 영향/검증: stale process-local map 대신 owner/revision을 검사할 수 있으나 APK/기기 place query는 미검증.
B-PLACE-02 — mapping command execution
- 트리거/경로: cloud
MAPPING/map generation command. - 구형:
apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/mqtt/data/subscribe/MappingSubscribe.java,apps/AMRAgent/java/com/sk/airbot/amragent/command/MappingCmd.java,apps/AMRAgent/java/com/sk/airbot/amragent/AMRAgent.java:337-343이 packet/bundle로 mapping을 지시한다. - 현재: 실제 실행은
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/amr/execution/AmrRotationTaskQueue.java,skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/amr/AmrMotionRuntime.java:71,811-841,skix-framework/base/README.md:96의 동일 movement queue/MOVEMENT_BASE경로에 있다.skix-framework/base/core/java/com/sk/intellix/framework/model/MapSyncRequest.java,skix-framework/base/core/java/com/sk/intellix/framework/model/MapSynchronizationUpdate.java,skix-framework/base/core/java/com/sk/intellix/framework/manager/SkixMapManager.java:75-93는 metadata/sync listener 계약이다. - 차이/분류: raw MQTT/bundle command에서 AMR task queue와 typed synchronization boundary로 분리된
relocation이다. Host battery/stop-all/SoundFlow admission, SDK/UI 연결과 execution-specific completion correlation은 아직 pending이므로 physical mapping 완료는gap이다. - 영향/검증: command accepted/queued와 map file uploaded/completed를 구분해야 한다. model tests and existing mapping evidence are leads, no device proof here.
2026-10-15 후속 · IoT 클라우드 지도 요청의 실제 이관(로컬):
IoT 지도 시작/중단 요청을 CmdTopic.subscribeMappingData → Util.controlMapping(3인자) → SkixDeviceGateway → FrameworkStateClient → 공개 mapping Task에 연결했습니다. DeviceAgent의 main state/readiness를 이 경로의 진입 조건으로 사용하지 않습니다. 기존 2인자 legacy API는 호환 경로로 남깁니다.
| 비교 지점 | 기존 진입 경로 | 현재 독립 경로 | 제품 영향 |
|---|---|---|---|
| 실행 허용 | DeviceAgent main-state/Interaction 조회 필수 | 동일 Framework binding의 product mode·최근 5초 LLM IDLE 관측+기존 Host admission | DeviceAgent 부재만으로 거절하지 않음. 관측 불명확/LLM 초기화 중 시작 거절 |
| 수동/자동 | manual=1, 그 외 auto=2 | 같은 값, delay0, timeout480000ms | 8분은 Task 중단 제한이며 성공 판정 아님 |
| 중복 요청 | void 호출; 미연결은 로그만 남기고 종료 | correlation SHA256으로 안정 requestId, private journal을 RPC 전에 저장 | 같은 요청은 조회만 수행. 동일 ID에 다른 mode는 거절 |
| 응답 상실/재시작 | 이 진입 경로에 실행 추적 없음 | 저장된 taskId 또는 requestId+amr.mapping으로 조회 | 미확인일 때 자동 재전송하지 않음. 재연결만으로 기기가 움직이지 않음 |
| stop/end | legacy 전체 mapping 중단 | journal에 연결된 정확한 Task만 취소 | 다른 이동/다른 요청을 cancel-all하지 않음. 취소와 실제 정지 분리 |
| 취소 응답 상실 | 이 진입 경로에서 추적하지 않음 | 취소 의도 선저장; 동일 취소 ID는 재전송 없이 상태 조회 | RUNNING이면 성공 응답 대신 unknown. CANCELLING/terminal은 상태만 반환 |
| Started 보고 | 명령 전달 전에 이벤트 생성 | 접수만으로 Started를 보내지 않음 | 실제 동작·업로드 이벤트 연결은 후속 |
| 요청 응답 | 완료 응답과 office.pgm/yaml 고정 목록 | 접수/Task 상태·미확인 사유 반환, filelist 제거 | 파일 생성 성공을 주장하지 않음. backend의 접수/완료 해석은 미검증 |
| JSON 해석 실패 | 이전 공유 mappingID가 남을 수 있음 | 이번 local correlation만 사용; 얻지 못하면 응답하지 않음 | 앞선 요청으로 잘못된 실패 응답을 보내지 않음 |
| pause/resume/save | legacy 호출 | 미이관 명시 거절 | 독립 실행 완료로 표시하지 않음 |
준비 조건 후속: 같은 Host 안에서 Agent 연결·최신 LLM 관측이 바뀌면 이전 허용 근거를 재사용하지 않도록 전송 직전 로컬 검사 연결. 구체 조건·제품 영향·미검증은 VOICE-02의 2026-10-15 후속 및 skix-station-llm-receipt-evidence-2026-10-15.json에 기록합니다. 지도 생성 전체 완료 판정을 추가한 변경은 아닙니다.
남은 기능: 저장/업로드, 외부 UploadService, IoT 최종 응답, 제품 복귀, native 완료의 Task ID 상관. 현재 구현은 클라우드 시작/취소 접수 경로의 이관이며 지도 기능 전체 이관 완료가 아닙니다. 검증 정본은 skix-iot-mapping-task-evidence-2026-10-15.json입니다. APK/실기기/서버 수신은 미검증입니다.
B-REPORT-01 — cleaning report record/publish/save 순서
- 트리거/경로: cleaning start/end and report publication.
- 구형:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/db/CleaningReportDao.java:20-52가AndroidCleaningReportStore를 통해 insert/get/delete를 제공하고CleaningReportManager가 report orchestration을 수행한다. - 현재:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/execution/CleaningReportOperations.java:16-63,87-114는 start/end bookkeeping과publish()후save()순서를 core utility로 고정한다. - 차이/분류: Android Bundle/DAO 중심 orchestration을 core operation + caller-owned persistence로 이동한
relocation이다. 확인된 publish/save 순서 자체의 정책 변경으로 보지 않는다. - 영향/검증: report publish 성공과 DB save 성공을 한 결과로 취급할 수 없다. cleaning report unit/core tests 및 source inspection; DB/IoT server 수신 미검증.
B-REPORT-02 — report 저장소 ownership
- 트리거/경로: report insert/query/delete.
- 구형:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/db/CleaningReportDao.java:44-52는 process app이AndroidCleaningReportStore를 직접 생성해 저장한다. - 현재:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/execution/CleaningReportOperations.java:63-84,191-197는 Android/persistence를 caller port로 남기고 core는 DB를 clear/write하지 않는다고 명시한다. - 차이/분류: persistence ownership이 core 밖으로 분리된
relocation이다. 현재 소스에서 기존 report semantics를 정책 변경으로 바꾼 근거는 없다. - 영향/검증: 현재 consumer가 동일 DB path/schema를 연결했는지 별도 확인해야 한다. 소스상
AndroidCleaningReportDatabase참조와 report tests만 확인.
B-OTA-01 — UpdateEngine OTA lifecycle
- 트리거/경로: user/cloud update request, engine callback, reboot-needed.
- 구형:
apps/UpdateAgent/java/com/sk/airbot/updateagent/UpdateManager.java:45-110,207-300,334-358,417-464가UpdateEngine.bind/unbind,applyPayload, cancel/reset, state synchronization/re-apply를 수행한다. - 현재:
skix-framework/base/core/java/com/sk/intellix/framework/manager/SkixUpdateManager.java:15-46은 report ingress와 explicit MCU/EyeLED maintenance admission/status API만 제공한다. AndroidUpdateEngine실행 owner는 이 manager에 없다. - 현재 UI/파일 경로:
apps/UpdateAgent/java/com/sk/airbot/updateagent/ui/MainActivity.java:159,259,713-741,996,1230-1255,1282-1287,1330-1335,1354-1370,1420-1444는SOC7no-file, unknown phase ignore, selectedCtrlFW.bin/EyeledFW.binatomic copy, native status 101/phase/elapsed를 표시하지만 completion proof로 표시하지 않는다. UI update report는 job/status/failure를 Framework gateway로 보낸다. - 차이/분류: OS OTA engine lifecycle을 typed reporting/maintenance surface와 분리(
relocation), selected MCU/EyeLED file admission/unknown phase handling은정책 변경및신뢰성 변경이다. full payload download/apply/reboot ownership은gap. - 영향/검증: current Framework OTA API가 UpdateAgent의 full-device OTA를 대체한다고 말할 수 없다. UpdateManager/MainActivity source and current SDK contract tests only; native 101은 install success가 아니며 device proof가 없다.
2026-10-15 후속 · AWS OTA 시작 전 바이탈사인 상태 확인 (소스 구현·로컬 검증; APK/기기 미검증)
변경 전: IoT의 onDownloadUpdateAgent()는 getVitalsignStatus()가 false이면 바이탈사인을 사용하지 않는 것으로 판단했습니다. 그러나 전달 실패·연결 없음도 false였습니다. 도킹·배터리 조건을 만족하면 바이탈 상태를 알 수 없어도 UpdateAgent 화면을 실행할 수 있었습니다.
변경 후: 조회 성공 여부와 사용 여부를 각각 반환합니다. AWS job은 조회 성공이 확인되고 비사용일 때만 기존 다음 검사로 진행합니다. 미확인은 FAILED / vital sign 상태 미확인으로 보고하고 UpdateAgent를 시작하지 않습니다. 이는 기존 false 처리보다 보수적인 실행 허용 정책 변경이며, 제품 승인 여부는 미검토입니다.
| 입력·조건 | 이전 판단 | 현재 처리·부수 효과 |
|---|---|---|
| 도킹 안 됨 또는 배터리 30% 미만 | 기존 배터리/스테이션 거절 | 그대로 먼저 거절; 바이탈 상태를 추가 조회하지 않음 |
전달된 제품 상태 7 |
바이탈 사용 중 | 기존 vital sign 실행 중 거절 유지 |
전달된 알려진 상태 0~11 중 7 제외 |
비사용 | 비사용으로 판정한 뒤 기존 Live View/Safe Care 검사 수행 |
| null·미전달·null 값·알 수 없는 문자열·조회 예외 | false로 비사용과 구별 불가 | 미확인으로 거절; 로봇 업데이트 상태 변경·상태 전송·Activity 실행 없음 |
| 빈 job ID의 로컬/Samba 업데이트 | 상태 조건을 건너뛰고 실행 | 기존 예외 유지; 이번 변경으로 안전 검증된 경로가 되는 것은 아님 |
실제 경로: IotAgent.onDownloadUpdateAgent → DeviceUtil.queryVitalsignStatus → SkixDeviceGateway.getMainState → 기존 제품 주상태 조회 → DeviceStateInterpretation.queryVitalsignActive → OTA 허용 또는 실패 보고. 조회는 한 번만 수행하며 실패 시 재전송하지 않습니다. 기존 boolean 함수는 호환성을 위해 유지하고 OTA 호출자를 가용성 포함 조회로 전환했습니다.
남은 경계: mode 7의 실제 상태 생산자는 아직 Framework로 완전히 이관되지 않았습니다. Framework 제품 snapshot의 일부 mode만 보고 측정을 하지 않는다고 추측하지 않습니다. 이 수정은 미확인 시 OTA 실행을 막는 것이며, DeviceAgent 없는 바이탈 상태 제공이나 OTA 전체 독립 실행 완료를 뜻하지 않습니다. 확인된 주상태만으로 다른 도메인 상태까지 원자적으로 확인했다고 보지 않습니다.
검증 정본: skix-iot-ota-vital-state-evidence-2026-10-15.json. 실제 Java 메서드의 unknown/active/idle·로컬 예외·배터리 우선·스트리밍 분기를 회귀 검사합니다. APK 설치·실기기 OTA·서버 실패 보고 수신은 별도 미검증입니다.
B-OTA-02 — OTA result ingress and dedup
- 트리거/경로: UpdateAgent receives job/status/failure result.
- 구형:
apps/UpdateAgent/java/com/sk/airbot/updateagent/UpdateManager.java:480-527callback state/error is process-local; old cloud report path was coupled to agent/IotAgent. - 현재:
apps/UpdateAgent/java/com/sk/airbot/updateagent/infrastructure/device/FrameworkOtaResultClient.java:16-85,91-103binds authenticated Framework and callsSkixUpdateManager.reportFirmwareUpdate;skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/update/OtaFirmwareResultRelay.java:9-71rejects invalid/duplicate/retired report and isolates dead consumers. - 차이/분류: result reporting ownership moves to Framework relay and adds dedup/retirement (
relocation,신뢰성 변경). - 영향/검증: duplicate terminal callbacks are suppressed; server receipt and cross-reboot durability are not proven. source + core OTA tests only.
B-OTA-03 — OTA result uncertainty/no replay
- 트리거/경로: Binder unavailable during report or MCU job admission.
- 구형: UpdateEngine calls may be asynchronous;
apps/UpdateAgent/java/com/sk/airbot/updateagent/UpdateManager.java:281-300,334-358starts prepare/apply and callback later updates state, with re-apply path atapps/UpdateAgent/java/com/sk/airbot/updateagent/UpdateManager.java:450-464. - 현재:
apps/UpdateAgent/java/com/sk/airbot/updateagent/infrastructure/device/FrameworkOtaResultClient.java:46-82throws “outcome unknown; do not replay” on unavailable binding;skix-framework/base/core/java/com/sk/intellix/framework/manager/SkixUpdateManager.java:24-46preserves the same contract. - 차이/분류: uncertain write/result is fenced instead of automatically replayed (
정책 변경,신뢰성 변경). - 영향/검증: protects against duplicate flashing/reporting but requires operator/query recovery. Current tests/source only; no interrupted-device trial.
B-OTA-04 — legacy AMR/MCU OTA sequencing
- 트리거/경로:
OTAUpdate, component result, recovery/start. - 구형:
apps/AMRAgent/java/com/sk/airbot/amragent/AMRAgent.java:688-689,800-805,938-949sends OTA file/result and reports failure to DeviceAgent; command classesapps/AMRAgent/java/com/sk/airbot/amragent/command/OTACmd.java,apps/AMRAgent/java/com/sk/airbot/amragent/model/OTAStart.java,apps/AMRAgent/java/com/sk/airbot/amragent/model/OTAStatus.javahold packet state. - 현재:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/maintenance/OtaWorkflowRuntime.java:4-18,43-68,80-184keeps component selection, AMR/MCU/SOC sequencing, and bounded MCU recovery count in a generic port. - 차이/분류: AMRAgent packet orchestration relocated to Framework workflow port (
relocation). Actual port implementation/flashing remains outside this class (gap). - 영향/검증: sequence logic is testable without hardware, but
port.flashAmr/MCU command success is not physical completion. source inspection/core workflow tests only.
B-OTA-05 — OTA persistent state/data migration
- 트리거/경로: upgrade/reboot after OTA.
- 구형:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/db/CleaningReportDatabaseHelper.java:1-6explicitly says product-owned DB path and no OTA/cross-package copy;apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/infrastructure/maintenance/LegacyFirmwareVersionStore.java:7-25bridges a DeviceAgent runtime cache. - 현재:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/update/OtaFirmwareResultRelay.java:9calls itself transient and only holdslastReportin memory; current ProductSettings is durable (skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/setting/AndroidProductSettingsStore.java:19-35,45-85) and current PIN is durable (skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/security/AndroidWssPinStore.java:10-42,55-84), but no legacy OTA result/schema migration is present in the inspected OTA classes. - 차이/분류: settings/PIN persistence exists, while durable legacy OTA result continuity is not implemented in the inspected boundary (
gap). - 영향/검증: reboot/package replacement may lose result history or old firmware cache unless another store is wired. Source search only; migration/device reboot unverified.
B-BOOT-01 — UpdateAgent and AMRAgent boot behavior
- 트리거/경로: boot broadcast.
- 구형:
apps/UpdateAgent/java/com/sk/airbot/updateagent/receiver/BootReceiver.java:8-15starts update service;apps/AMRAgent/java/com/sk/airbot/amragent/receiver/BootReceiver.javaandapps/AMRAgent/java/com/sk/airbot/amragent/AMRAgent.java:272gate initialization on boot-completed state. - 현재: inspected
skix-framework/baseservice/domain classes have lifecycleonStart/onStopbut no equivalent receiver. Framework runtime startup is therefore a packaging/SystemServer concern outside these classes. - 차이/분류: receiver-driven process startup to managed service lifecycle (
relocation), with exact boot ordering asgap. - 영향/검증: boot race between IoT, UpdateAgent, AMR and Framework remains an integration risk. Manifest/init/actual boot log not checked by this read-only audit.
B-BIND-01 — UpdateAgent Framework binding lifetime
- 트리거/경로: bind success/null/death/close.
- 구형:
UpdateManager.bind()/unbind()only wrapsUpdateEngine(apps/UpdateAgent/java/com/sk/airbot/updateagent/UpdateManager.java:97-110); no Binder epoch around Framework result reporting existed in that class. - 현재:
apps/UpdateAgent/java/com/sk/airbot/updateagent/infrastructure/device/FrameworkOtaResultClient.java:21-43,91-103tracks one binding, retires on disconnected/death/null, exponential retry, and unbinds the exact retired connection. - 차이/분류: authenticated service binding lifecycle added (
신뢰성 변경). - 영향/검증: old callback cannot report through a newer binder; retry does not replay the report. Source inspection and OTA contract tests only.
B-STORAGE-01 — Preferences and encrypted map/report stores
- 트리거/경로: settings, PIN, map, report persistence.
- 구형:
SKMPreferences/PrefsCryptoUtilin DeviceAgent and encrypted map file handling inapps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/map/MapManager.java:86-106,385-394; reports throughAndroidCleaningReportStoreinapps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/db/CleaningReportDao.java:25-52. - 현재: ProductSettings is implemented by
skix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/setting/AndroidProductSettingsStore.java:19-35,45-85,101-133with versioned AtomicFile/CAS, and WSS PIN byskix-framework/base/services/runtime/java/com/sk/intellix/frameworkservice/server/integration/security/AndroidWssPinStore.java:10-42,55-123with salt/hash/revision and owner checks. Report and legacy map/OTA migration is not shown in the inspected boundary. - 차이/분류: framework-owned settings/PIN persistence is a
relocation; legacy map/report/OTA migration remains agap. Encryption/CE permissions and cross-package upgrade behavior still need device evidence. - 영향/검증: compatibility requires schema/path/certificate-aware migration tests. No destructive action, build, or storage inspection performed.
B-SCOPE-01 — Remaining legacy cloud surface
- 트리거/경로: all topic methods outside settings/map/report/OTA.
- 구형:
apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/IotAgent.javaimports and handles holiday, schedule, background, outdoor WI, AQM, health check, logs, KVS, streaming, WSS and many serializer types (apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/IotAgent.java:3-39,90-121plus the 3,558-line class). - 현재: inspected typed Framework classes cover selected IoT requests, settings, map, WSS PIN, reports and OTA; complete parity for every legacy topic is not demonstrated.
- 분류:
gap(scope/control item, not a claimed regression). - 영향/검증: audit follow-up must enumerate each
TopicNameand map it to a current consumer/producer or explicitly mark retained legacy ownership. No completeness claim is made here.
B-SCOPE-02 — SET_APP_STATUS / MQTT producer 미위치
- 트리거/경로:
SET_APP_STATUS또는setAppStatus를 통한 앱 상태/지도 편집 상태 보고. - 확인된 사실: 현재 checkout에는
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/domain/system/DeviceStatusCommandHandler.java:41,60의ManageBundleConstants.Command.SET_APP_STATUS소비와apps/IotAgent/README.md:47-49의SET_APP_STATUSlegacy 경로 설명이 있다.skix-framework/base/core/tests/src/com/sk/intellix/framework/internal/legacy/bundle/LegacyBundleConstantsTest.java:225는 상수 문자열만 확인한다. - 미위치 사실: 이 감사의 제한된 검색에서
SET_APP_STATUS를 실제 MQTT로 publish하는 producer와 현재 Framework로 완전히 대체된 producer를 찾지 못했다. MQTT 자체가 없어졌다고 결론내리지 않는다. - 분류:
gap(unconfirmed producer gap). 정책 변경이나 기능 삭제로 분류하지 않는다. - 영향/검증: 지도 편집 상태 및 SoundFlow effect가 실제 cloud topic으로 전달되는지 별도
IotAgentfull path/실 broker 검증이 필요하다. 문서 lead와 source search만 근거.
미포함/미검증 범위
- MQTT broker, AWS/KVS, cloud schema, TLS/credentials, server receipt and reconnect over real network.
- AndroidManifest, init rc, SystemServer registration order, SELinux, VINTF, partition placement and boot logs.
- UpdateEngine payload download, slot switch, reboot, rollback, AMR/MCU flashing and physical completion.
- APK contents/signatures, installed package paths, Binder permissions and real-device service death/rebind.
- Legacy preference/database/map schema migration, cross-package data retention, backup/restore and OTA upgrade continuity.
- Full
apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/IotAgent.javatopic parity, WSS upload/media delivery, schedule/holiday/streaming/background flows and all AMRAgent packet paths. - Current framework source outside the inspected
skix-framework/baseboundary, generated AIDL implementation wiring, and backend/Launcher/SystemUI consumers.
Validation record
- Read-only commands:
rg --files, targetedrg -n,wc -l,find,sed/nlsource inspection; no source edit, build, APK install, server call, or device command. - Supporting leads reviewed:
docs/refactor/skix-next-session.md(current handoff and evidence boundaries), current framework core/service tests, and existing evidence names referenced there. These leads were not treated as runtime proof. - Baseline commands:
git rev-parse HEAD(4d9b1a57a2e86fabda2bbcddd21ce3269584eff2),git -C ../packages-mr7 rev-parse HEAD(164654fc4cc4e871c74db961a2b63bba4b464589),git status --short(dirty legacy/framework tree), andgit log -1 -- apps/IotAgent apps/DeviceAgent apps/UpdateAgent(29967241 refactor(iot): move legacy connectivity requests out of system domain).
5. 교차 도메인 리뷰
CROSS-01 — UV 주기는 유지하지만, 불명확한 명령 뒤 자동 ON 재시도를 막음
- 비교 기준: 공통 조상
5d0ee581의apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/device/manager/UVLedManager.java, 현재skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/execution/CleaningUvCycle.java:43–90, HostupdateHardwareUvCycle:15255–15327. - 기존: 110분 OFF 뒤 ON, 30분 ON 뒤 OFF. F19가 들어오면 OFF 명령. 타이머 상태와 MCU 전송을 직접 갱신하며, 전송 예외 후 불명확 결과를 별도 상태로 기억하지 않는다.
- 현재: 110/30분과 오류 보고 순서는 보존. 명령 시도 전에
commandUncertain=true를 두고 정상 반환 때만 해제한다. 불명확하면 타이머가 ON을 재전송하지 않으며 명시 OFF 후 재진입을 요구한다. - 추가 실행 조건: Host 시작은 저장된 UV=true·MCU 준비·현재 owner·닫힌 덮개를 요구하고 먼저 OFF를 보낸다. 설정 OFF·owner 교체·큐 제출 실패 시 예약 실행을 해제한다. 저장 설정만으로 부팅 때 자동 ON하지 않는다.
- 분류: 주기 정책 보존 + 실패 복구 규칙 변경. 제품 영향: 장애 후 자동 복구 대신 운영자 OFF가 필요할 수 있다. 부팅 자동 활성화의 기존 제품 요구와 일치하는지 리뷰 필요.
- 검증:
skix-uv-automatic-host-evidence-2026-10-14.json,tools/tests/test_skix_uv_cycle_host.py의 기존 trace 비교. 기존 결과를 인용했으며 이번 문서 작업에서 재실행하지 않았다. 실제 UV 발광·재부팅 지속성은 미검증. - 리뷰: [ ] 불명확 후 OFF 요구 승인 [ ] 부팅 정책 확인 [ ] 실제 LED/덮개 시험.
CROSS-02 — 수동 일시정지 의도와 시스템 일시정지 사유를 분리
- 근거: Host
pauseOwned/resumeOwned/updateCommonPause:10334–10446;SystemModeRuntime.PauseRequests; 기존StateManager의 requester별 pause arbitration 및BaseCleaningManager의 duplicate-action 억제. - 현재: 이미 PAUSED에서 사용자가 pause해도 수동 의도를 기록한다. MultiAP 등 시스템 사유 하나를 해제해도 다른 사유나 수동 pause가 남으면 자동 resume하지 않는다. resume에는 원래 실행 owner와 새로운 팬 정지 근거가 필요하다.
- 분류: requester별 중재·수동 의도는 보존 대상, owner/fresh-stop 조건은 강화된 실행 규칙. 기존 모든 경로가 같은 의미였다고 단정하지 않는다.
- 제품 영향: 덮개 닫기/MultiAP 종료 후 사용자가 다시 시작해야 하는 경우가 명확히 구분된다. 팬만 재개하고 원래 Task/타이머는 멈춰 있는 불일치를 방지한다.
- 검증:
skix-product-pause-migration-evidence-2026-10-13.json,skix-basic-lifecycle-evidence-2026-10-14.json등 기존 근거. Basic 경로와 Interaction/AMR 전체 제품 경로는 별도로 리뷰해야 한다. - 리뷰: [ ] 수동 pause 유지 [ ] 복수 사유 해제 순서 [ ] 원래 실행 종료 후 자동 재개 금지.
CROSS-03 — 야간 해제 시 센서 보정: 이력 없는 상태를 임의로 만료 처리하지 않음
- 근거: Host
applyNightCalibrationEffect:5805–5863,sensorCalibrationHistory:14776–14784; 기존SensorCalibrationManager및LegacyCalibrationSettingsGateway. - 현재: 야간 ON은 보정을 요청하지 않는다. OFF에서는 실제 이력으로 DUE/NOT_DUE/NO_HISTORY를 계산한다. DUE만 보정을 실행하고, 이력 없음은 skip한다. 상태를 읽는 도중 설정 revision·owner가 바뀌면 결과를 성공으로 확정하지 않는다.
- 보존/차이: 기존 보정 주기·판정 의도는 보존 대상. 새 저장소의 NO_HISTORY를 기존 장치의 '한 번도 보정 안 함'과 같다고 가정하지 않는다. OTA 이력이 이관되지 않으면 기존 만료된 보정이 생략될 수 있다.
- 분류: 관측/이력 부족 처리 강화 + 데이터 이관 공백. 제품 영향: 야간 해제 때 보정이 생략되는 원인을 설명할 수 있지만, 기존 장치의 주기를 이어받으려면 이관 설계가 필요하다.
- 검증:
skix-night-calibration-effect-evidence-2026-10-14.json; 명령 창 수락은 센서 보정 완료가 아니다. - 리뷰: [ ] NO_HISTORY 제품 정책 [ ] 기존 이력 이관 [ ] 원래 generation의 OFF 정리.
CROSS-04 — 예약 시각 비교를 단말 locale과 분리
- 비교 기준:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/schedule/ScheduleAlarmReceiver.java:82–109와skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/execution/scheduler/WeeklyScheduleCalculator.java:46–61. - 기존:
SimpleDateFormat("HH_mm", Locale.getDefault())로 현재 분을 문자열로 비교한다. - 현재:
String.format(Locale.ROOT, "%02d_%02d", ...)로 canonical ASCII 시각과 비교한다. GPS bypass·활성 요일·휴일·동일 분 trigger/reschedule/ignore는 정책으로 분리한다. - 분류: locale 의존 제거라는 실제 표현/실행 조건 변경 + 주간 계산 이동. 비ASCII 숫자를 쓰는 locale에서 예약 누락 동작이 달라질 수 있다.
- 보존: 최소 실행 간격은
now-last > minimum이며 경계 equality는 거절한다. 다음 알람은 엄격히 now 이후, 7일 후 같은 요일까지 찾는다. missed alarm을 임의 catch-up하지 않는다. - 검증:
WeeklyScheduleCalculatorTest및LegacySchedulePolicyTest소스. AlarmManager·저장소·재부팅 duplicate 방지는 이 계산 함수가 제공하지 않는다. - 리뷰: [ ] locale별 예약 [ ] 휴일/GPS [ ] DST/clock 변경 [ ] 저장·실행 원자성.
CROSS-05 — 공기질 보정 산식과 민감도는 제품 정책으로 보존
- 근거:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/airquality/policy/LegacyAirQualityCalibrationPolicy.java:sensitivityFactor/calculatePM25Fitting/calculatePM10/calculateCO2Correction/hchoCalibrate/calculateCorrectedHumidity, 기존 AirQuality 관련 manager의 호출 경로. - 현재 확인: 민감도 0=0.85, 2=1.15, 기본 1.0; CO₂ 1.06; PM2.5 구간별 fitting 및 HCHO 구간 보정이 명시되어 있다. 파싱 실패는 값 보존/기존 error log 경계를 따른다.
- 분류: 산식 추출/보존 대상. 이번 조사만으로 모든 과거 버전·센서 종류의 수치 동등성을 확정하지 않으며, '공기질 알고리즘을 새로 바꿨다'고 계산하지 않는다.
- 실제 바뀌는 주변 조건: Host 준비·warmup·freshness가 부족하면 임의 정상값 대신 unavailable이 반환될 수 있다. IoT는 같은 실행·목적지·pose에서 얻은 관측만 보고하고 늦은 묶음을 폐기한다.
- 검증:
skix-host-airquality-processing-evidence-2026-10-07.json,skix-iot-airquality-report-evidence-2026-10-14.json. 서버 수신/실제 센서 정확도는 별도. - 리뷰: [ ] 수치 parity [ ] 준비 시간 [ ] 목적지별 보고 누락/중복.
CROSS-06 — 지도 생성: 기존 배터리 기준은 유지, 정리 확인과 실행 소유권을 추가하는 중
- 근거: 현재 Host
mappingStatus/prepareMapping/startMapping/cancelMapping:7170–7228,requireMappingProductAdmission:13900–13916,applyHostMappingSoundMode:13917 이후;AmrRotationTaskQueue와 기존 제품 mapping 정책. - 보존 대상: 보정된 표시 배터리 ≥27, 수동 지도 모드=1·자동 지도 모드=2, 시작 전 다른 동작 정리, SoundFlow mapping 사유 등록, 실제 AMR 시작 순서.
- 바뀌는 부분: 실제 cleanup·LLM 정리 확인 전에는 PREPARING_MAPPING으로 대기한다. 같은 실행 요청을 재전송하지 않고, 취소 ACK만으로 모터 owner나 mapping 음성 억제를 해제하지 않는다. 믿을 수 있는 '명령 전 취소/미전송'만 즉시 정리한다.
- 2026-10-15 후속 변경 — 팬 배타성: 지도 생성 큐는 기존 동작 정리가 READY인 뒤 별도의
CLEANING_FAN예약을 확보한다. 팬이 사용 중이거나 상태가 불명확하면 지도 생성 모터 명령을 보내지 않는다. 마지막 정책 조회 중 취소·소유권 변경이 발생해도 전송 전에 재확인한다. - 종료·복구 차이: 명령 미전송이 확인된 종료만 해당 예약을 해제한다. 전송 결과 불명확·연결 퇴역은 팬과 이동 자원을 모두
RECOVERY_REQUIRED로 유지한다. 이전 Task 조회는 후속 Task의 팬 예약을 자신의 예약으로 표시하지 않는다. - 상태: 위 큐 변경은 독립 SPEC→QUALITY 검토 PASS 및 실제 Java 동작 회귀로 확인했다. 공개 SDK → 인증 Endpoint/Binder → 현재 Host 큐 연결은 로컬 검토·Android 컴파일 완료다. 모드 1/2와 최대 8분 제한을 검사하며, 불명확한 접수 결과를 자동 재전송하지 않는다. 기존 dirty Host 제품 정책 전체는 별도 미검토이며 시험앱은 자동/수동 모드 제출·공통 Task 조회/취소·팬 예약 표시까지 로컬 연결했다. 앱 재시작 뒤에는 저장된 완료값 대신 정확히 일치하는 서버 Task 조회로 재등록 차단을 해제한다. 물리 완료 producer 상관·APK/기기 검증이 남았다. raw
LocalHostBinder.setFanCommand의 자원 검사 우회 가능성은 별도 확인 대상이지만 실제 생산 코드 호출자는 이번 검색에서 확인되지 않았다. 전체 팬 경로 이관이나 배포 완료로 표현하지 않는다. - 후속 검증 정본:
skix-amr-mapping-fan-evidence-2026-10-15.json. 실제 동작 RED→최종 probe 1 PASS; 비매핑 영향 11 PASS/기존 Host 추출 fixture 1 FAIL(변경 전 동일 재현). 테스트 수는 누적 합산하지 않는다. - 공개 계약 검증 정본:
skix-public-mapping-task-evidence-2026-10-15.json. 영향 28 PASS, 최종 공개 경로 4 PASS, Android SDK/AIDL/Host 컴파일 PASS. 초기 기능의 test-first 근거는 부족하여 회고적 API 누락 재현으로 따로 기록했다. 자원 enum 검증 보완은 실제 RED→GREEN으로 확인했다. - 시험앱 검증 정본:
skix-public-mapping-consumer-evidence-2026-10-15.json. 실제 제출·관측 반영·표시 Java probe 및 Android 컴파일 확인. 8분 만료는 큐 중단 요청이며 물리 정지 완료가 아니다. 수동 지도 조작과 지도 저장은 미완료로 표시한다. 초기 test-first 로그 부재는 별도 기록했다. - 2026-10-15 오류 수정 — 시험앱 모드: 기존 AMR
model/Mapping.java는MANUAL=1,AUTO=2이고 DeviceAgentMapCommandHandler.executeMapping도 이 값을 사용한다. 이전 개발 중 시험앱은 자동 버튼에 1, 수동 버튼에 2를 연결해 실제 요청 의미가 반대였다.MainActivity.submitPublicAmrMappingTask호출과 안내,FactoryTaskResults의 유효 모드 표시를 수동=1·자동=2로 맞췄다. SDK·Host·AMR 숫자 전달 규격은 바꾸지 않았다. 실제 Java/소스 회귀 5개 PASS와 독립 명세→품질 검토 PASS이며 이번 수정 후 Android 재컴파일·APK 설치·실제 지도 생성은 미검증이다. - 표시 잔여: formatter는 1 이외의 값을 자동으로 표시한다. 공개 제출은 1/2만 허용하지만, 비정상 관측값까지 명확히 표시하는 보완은 아직 하지 않았다. 유효 입력에 대한 검토 PASS를 임의 모드 값까지의 표시 검증으로 확장하지 않는다.
- 2026-10-15 후속 수정 — 전송 직전 검사: 위 검토 공백을 실제 Host
startMapping실행 회귀로 재현했다. 초기 오류 관측 owner를 고정하고 SoundFlow/준비 callback 이후 동일 owner·현재 오류 허용 조건·정확한amr.mappingTask RUNNING을 다시 확인한 뒤 AMR 시작을 호출한다. 오류 활성화·owner 교체·Task 취소 때 native 전송을 막는다. SoundFlow 적용 뒤의 정책 거절은 이미 발생한 준비 효과가 있으므로 일반 실패로 감싸 UNKNOWN/음성 억제/무재전송 의미를 유지한다. 정상 시작과 신뢰 가능한 미전송 거절의 정리는 보존한다. 정본skix-mapping-final-admission-evidence-2026-10-15.json: 영향 8 PASS, 독립 SPEC→QUALITY PASS, Host·시험앱 Java 컴파일 1분 6초 PASS. 모든 비동기 경합에 대한 원자성·실제 자원 복구·APK 설치·지도 물리 완료 검증을 끝냈다는 뜻은 아니다. - 제품 영향: 이전보다 시작/취소가 지연되거나 mapping 음성 억제가 유지될 수 있다. 물리 정지 후 해제 경로를 조립하지 않으면 기능이 장시간 막힐 수 있으므로 릴리즈 전 리뷰 필수.
- 검증: provider/runtime/Task 각각의 10/15 evidence는 이전 단계 결과이며 현재 Host 전체 변경 검증이 아니다.
- 리뷰: [ ] 배터리 27 경계 [ ] cleanup 순서 [ ] 팬/모터 배타성 [ ] stop 근거 후 SoundFlow 복구.
2026-10-15 관측 후속(로컬·미배포): native MAP_STATUS를 DeviceAgent 없이 기존 AMR 읽기전용 binding과 공개 motion snapshot에 연결했습니다. 지도 생성 활성 여부·원시 data·수신 순번·세대·5초 freshness를 시험앱에 표시합니다. 유효한 최신 payload가 잘못되면 이전 값은 제거하고 순번 watermark를 전진시켜 오래된 값의 재등장을 막습니다. 이전 연결 callback과 binding 내 epoch 충돌은 현재 값으로 채택하지 않습니다. 이 전역 관측에는 실행 ID가 없으므로 Task 완료·실제 정지·자원 해제 근거로 쓰지 않습니다. 지도 저장과 실행별 완료 상관은 미완료입니다. 정본: skix-amr-mapping-observation-evidence-2026-10-15.json (초기 기능 RED 기록 누락, 수정 RED 및 Java/Android 컴파일 확인; APK/실기기 미검증).
CROSS-07 — 오류 해제는 다른 활성 오류의 제동·음성·틸트 차단을 풀면 안 됨
- 근거:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/system/ProductErrorRuntime.java:Rule/Group/handle/apply/recognition/tilt, 특히setEmergencyBrake(!resolved || errorActive(BLDC_STOP)); 기존DeviceErrorManager계열의 제품 오류 route. - 현재: 한 오류가 해결돼도 해당 group에 다른 오류가 남으면 제동/LLM/tilt 차단을 유지하는 규칙이 있다. S02 복귀 지연 3000ms와 S03 critical 처리, report/non-report route가 구분된다.
- 분류: 일부 기존 오류 정책 추출 + 다중 오류 해제의 교차 영향 검토 항목. 이번 비교에서 제동 aggregate guard의 역사적 도입 revision까지 특정하지 않았으므로 확정된 '이번 변경'으로 단정하지 않는다.
- 제품 영향: 단일 오류 clear만으로 기능이 풀리지 않을 수 있으며 UI는 남은 차단 원인을 함께 보여줘야 한다. 전체 오류별 자동 제품 복귀는 함수 존재만으로 이관 완료가 아니다.
- 검증: 관련 error-policy evidence 및 Host 오류 관측 연결. 실제 전체 오류 주입/복귀 시나리오는 미검증.
- 리뷰: [ ] 복수 오류 해제 [ ] 오류 보고/화면/제동 일치 [ ] 복귀 실행 owner.
CROSS-08 — 사용 시간·청정 보고 시간: 실제 실행 시간과 제품 집계값을 구분
- 근거:
skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/maintenance/UsageTimeAccumulator.java:start/update/reset,skix-framework/base/services/core/java/com/sk/intellix/frameworkservice/server/domain/cleaning/execution/CleaningReportOperations.java:191–218, 기존UsageTimeManager·CleaningReportManager·report DAO. - 현재: 사용 시간은 caller의 기존 wall clock 차이를 누적하고 저장 후 알림 순서를 유지한다. 파싱 실패의 기본 0과 cursor 전진도 기존 의미를 보존한다. 보고 시간은 제품의 위치별 분 집계 규칙을 따른다.
- 분류: 산식/저장 순서 보존 + 관측 UI 계약 명확화. 실제 19초 실행과 보고 60초 집계를 같은 값으로 수정하지 않는다. clock rollback/overflow도 무단 수정하지 않는다.
- 제품 영향: 테스트앱에서 실행 elapsed와 제품 보고 duration을 나란히 봐야 한다. 보고 저장 성공은 팬 정지 확인이나 서버 수신 성공을 뜻하지 않는다.
- 검증:
skix-usage-accumulator-evidence-2026-10-10.json,skix-host-cleaning-reports-evidence-2026-10-08.json, 10/14 Basic device evidence의 범위만 참조. - 리뷰: [ ] 집계 단위 [ ] clock 변경 [ ] 저장 실패 [ ] 데이터 이관 후 누적값.
6. 빠짐없이 마무리하기 위한 후속 비교 범위
이 대장은 아래 영역을 제외해 '완료' 처리하지 않습니다. 전체 목표에 포함하여 남은 검토로 추적합니다.
| 영역 | 아직 확인할 세부 동작 | 완료에 필요한 근거 |
|---|---|---|
| 청정 전체 | 자동/이동/추종의 시작·일시정지·재개·중단·복귀, 공간 전환, 팬/틸트, 보고 부수 효과 | legacy/direct 경로별 입력·효과 trace 비교 + 실기기 |
| AMR 전체 | 수동 이동·회전·속도·도킹·지도 생성/삭제·위치 변경·충전 제어 | 실제 execution ID/세대/순번 완료·중단 상관 |
| 감시·생체·촬영 | 감지/녹화/업로드/순찰/인증/생체측정 전체 순서 | provider 생성 이벤트와 cleanup/제품복귀 trace |
| 음성·Interaction | 음성 gate, TTS, 콘텐츠·미디어, 중단/시간표·동시 실행 | 실제 Agent/Streaming 생산자 및 제어 효과 |
| 설정·데이터 | 기존 설정·PIN·지도·예약·보고·사용시간 OTA 이관 | 스키마/기본값/복사·중복 방지·rollback 설계와 시험 |
| IoT·MQTT | 모든 legacy topic의 수신·응답·에러·중복·목적지별 보고 | topic/command별 producer→Host→HW→reply 대조 |
| 오류·부팅 | 오류별 제품복귀, 다중 오류, 부팅 owner, 실패 후 복구 | 전체 오류 주입/재부팅/DA-free 인수 |
| UI·SystemUI | 상태바/화면 hold/popup/음량/LED/야간·임시 효과 복원 | 정확한 출력·다중 caller·owner 사망 실기기 확인 |
전수 조사 종료 기준: 기존 외부 명령/콜백 인벤토리의 각 항목을 대장 ID 또는 '동작 동일 확인'에 연결하고, 미분류가 0인지 확인합니다. 이후 조건·부수 효과·종료·복구를 비교하고 미검증은 그대로 남깁니다. 이번에는 그 종료 기준을 달성했다고 주장하지 않습니다.
7. 앞으로 기록하는 규칙
변경 대장의 로컬 정본은 docs/refactor/skix-business-logic-change-register-2026-10-15.md입니다. Wiki는 공유본입니다. 동일 내용이 서로 달라지지 않도록 정본에서 갱신하고 문서 묶음 종료 시 공유본을 반영합니다.
실행 조건·순서·상태·완료/중단·오류복귀·설정/저장값·알림·외부 응답이 달라지는 작업은 구현 묶음 종료 전 이 대장을 갱신합니다. 코드 이동만이면 '보존/이동'으로 기록하고, 영향 없는 내부 정리는 항목을 억지로 추가하지 않습니다.
새 항목에는 다음을 적습니다.
- ID, 최초 기록일/마지막 변경일, 기준 commit 및 dirty 소스 SHA.
- 트리거와 입력, 기존 producer→service→실제 효과 경로, 새 경로.
- 기존/현재 허용 조건, 부수 효과 순서, 결과·완료·실패·중단 판정.
- 보존 값과 의도적으로 바꾼 값, 변경 이유의 확인/추론 구분.
- 실제 사용자가 보게 될 차이, 호환 API/direct API/Task의 적용 범위.
- 테스트·빌드·설치·실동작을 나눈 검증 정본 링크와 미검증.
- 리뷰 판정: 미검토/수용/수정 필요/보존 확인/이관 필요. 작성했다고 승인으로 바꾸지 않습니다.
기존 ID의 의미를 바꾸면 이력에 날짜·변경 전후·이유를 남기고, 이전 판정을 삭제하지 않습니다. 새 source SHA가 바뀌면 영향을 받는 항목의 근거를 다시 확인합니다.
일괄 리뷰 체크리스트
- [ ] 제품 규칙 자체를 바꾼 항목이 추출·이동 항목과 구분되어 있음.
- [ ] 정상 동작뿐 아니라 차단·실패·취소·timeout·재연결·부팅 조건을 비교함.
- [ ] 명령 수락과 실제 완료/물리 정지가 구분되어 있음.
- [ ] 제한 강화 때문에 기존 기능이 영구 차단되는 경우의 복구 방법이 있음.
- [ ] legacy/Task/direct 경로에서 제품 정책이 빠지거나 두 번 실행되지 않음.
- [ ] 데이터 이관 전 초기값이 기존 장치의 상태를 덮지 않음.
- [ ] MQTT/앱이 기대하는 결과값·보고·화면·알림을 보존함.
- [ ] 각 승인 판정은 해당 코드/검증 revision과 연결됨.
8. 기록 이력
| 날짜 | 기록 | 상태 |
|---|---|---|
| 2026-10-15 | B-MAP-03 ZIP archive 중복/불일치 파일쌍 provider 전 거절·정상 순서/metadata/FD 보존 | RED2FAIL6PASS→영향23PASS·SPEC→QUALITY·AMRJava19초; 로컬원본 초안·APK/기기/공개배포 없음 |
| 2026-10-15 | B-MAP-03 일반 MapData invalid/예외 실패 관측·callback 결과 재분류 방지 | 행동RED2→영향20PASS·SPEC→QUALITY·AMRJava26초; 원본초안/APK·기기미배포 |
| 2026-10-15 | B-MAP-03 AES128 upload 공용 평문파일 제거·호출별byte[] PUT·실패/시간제한 | 행동RED4→영향17PASS·SPEC→QUALITY·IoTJava27초; 원본초안/APK·기기미배포 |
| 2026-10-15 | B-MAP-03 backend 소비 계약 정합: reset 효과 전 선택·기존 upload 실패event/finally정리·metadata 동일backend/Runtime격리 | 행동RED4→최종영향15PASS·SPEC→QUALITY·IoTcompile35초; 로컬/Wiki원본 초안, APK/기기없음 |
| 2026-10-15 | B-MAP-03 IoT 미설정 backend를 기존 플랫폼 property로 선택·명시값 보존·mapupload worker null/예외 실패응답1회·무replay | actual RED3FAIL1PASS→영향9PASS·IoT Java compile26초; 품질 검토 반려(다른 getter 소비자 예외 공백) · APK/기기/서버·DA gate 후속; 문서 공유와 코드 릴리즈 구분 |
| 2026-10-15 | 사용자 “디테일하게” 요청: 12영역 검토 지도·8축 상세 기준·77개 상세 ID 직접 이동 및 기존 분류/적용 상황 표시 | 문서 보강; 전수 역사 비교·제품 승인·새 기기 시험 완료 주장은 없음 |
| 2026-10-15 | VOICE-02/B-PLACE-02 이동 준비 관측 수명: 현재 peer·exact 최신 관측·NOT_READY·5초 경계 재검사 | 코드 로컬 검증 기록은 receipt-evidence 정본; APK/기기 없음, Wiki 후속 원본 초안 |
| 2026-10-15 | 사용자 상세 요청 보강: 사례 I의 입력·경합·실패·대기·복구 10조건과 최신 상태표 정합 | 문서 소스 대조; 과거 시험과 신규 문서 검증 분리, 제품 승인 미완료 |
| 2026-10-15 | B-MAP-03 IoT 업로드 요청별 바이트/provider/FD 수명, 입력/오류 처리·대기 제한·서명URL 로그 제외 | 실제RED5→최종5PASS·SPEC→QUALITY·IoTcompile46s PASS; DA진입/native상관/서버/APK/기기 남음, Wiki 후속원본 미배포 |
| 2026-10-15 | 사용자 상세 요청: 최신 상태 비교표와 사례 H 14개 조건·실행 순서·반환값·복구 한계 추가 | 문서 소스 대조; 과거 검증을 신규 시험으로 계산하지 않음 |
| 2026-10-15 | B-MAP-03 ZIP 서비스 결과·연결누락·FD수명·현재archive파일쌍 및 ENC/ZIP dispatch 수신실패 보완 | 로컬SPEC→QUALITY/회귀/AMRcompile PASS; APK/기기/업로드 미검증·Wiki 후속원본 미배포 |
| 2026-10-15 | 상세 비교 8축·암호화/ZIP 실패 7분기·실제 dispatch 잔여 결함과 검증 한계 기록 | 문서 갱신; 수신 실패 코드 품질 수정 요청, 제품 승인/기기 검증 아님 |
| 2026-10-15 | B-MAP-03 Host/공개 SDK/시험앱 명시 지도 GET 이관·접수/미전송/unknown·수명 경계 | 17PASS·SPEC→QUALITY·Android compile2분5초 exit0; APK/기기/IoT 업로드 미검증 · Wiki 공유본은 로컬 초안 |
| 2026-10-15 | 기능별 입력·실행 순서·정상 판정·실패/경합 시나리오 11개 및 검증 중 소비 연결 구분 추가 | 문서 상세화 · 시나리오는 인수 기준이며 새 시험 통과 결과 아님 |
| 2026-10-15 | B-MAP-03 상세 사례: 실제 전달 경로·13가지 입력/실패 분기·캐시 재전달·파일/Task/물리/업로드 완료 구분 보강 | 문서 보강·현재 소스 대조; 추가 테스트/기기 검증 없음 · Wiki 원본 반영, 공개 배포 전 |
| 2026-10-15 | B-PLACE-02 IoT 지도 시작/중단 Task 실제 연결: DeviceAgent 진입 gate 제거·persistent journal·unknown 무재전송·요청별 응답·접수/파일 완료 분리 | 최종16PASS·SPEC→QUALITY·SDK/IoT compile 확인; APK/기기/서버·업로드/제품복귀 미완료 |
| 2026-10-15 | B-PLACE-02 상세 사례: 실제 호출/저장 순서·중복·유실·취소·MQTT 의미·복구 한계와 최신 이관 범위 정합 | 문서 상세화 · 제품 승인/신규 실기기 검증 없음 |
| 2026-10-15 | B-MAP-01: 직접 encrypted metadata 조회의 실제 경로 보완; 초기 bind 실패 후 공개 위치 조회로 명시 재연결·중복 등록/퇴역 차단 | 로컬 소스/검증 묶음 · 공개 배포 후속 · APK/기기 미검증 |
| 2026-10-15 | B-MAP-03 추가: plain 저장 결과·기본 encrypted rename·호환 소비자 실패 차단, 정상/실패 예와 데이터 원복 한계 상세 기록 | 로컬 회귀/AMR compile·SPEC→QUALITY PASS · 기기 미검증 |
| 2026-10-15 | CROSS-06 지도 생성 native 관측→기존 공개 SDK→시험앱 표시. 최신 invalid 값 제거·순번 유지·stale 구분·완료 추정 금지 | SPEC→QUALITY·Java/Android compile 확인 · 로컬 미배포/APK·기기 미검증 |
| 2026-10-15 | 청정 정지·지도 생성·재연결·복수 오류·설정/PIN 저장의 실행 순서와 실패/복구 상세 사례, ID별 승인 기록 칸 추가 | 문서 상세화 · 신규 제품 승인 없음 |
| 2026-10-15 | CROSS-06 Host 준비 효과 이후 오류 owner/Task 상태 재검사·post-effect 거절 UNKNOWN 유지. 이전 모드 수정 포함 Host·시험앱 묶음 컴파일 | 영향8PASS·SPEC→QUALITY PASS·compile66초 · APK/기기 미검증 |
| 2026-10-15 | 대표 12개 상황의 기존/현재/제품 영향 비교표·6단계 승인 기준 추가. CROSS-06 수동=1/자동=2 시험앱 오류 수정과 표시·최종 전송 검사 잔여 명시 | 상세화 · 신규 제품 승인 없음 |
| 2026-10-15 | 기능별 3개 감사 영역과 교차 도메인 항목을 첫 대장으로 통합. 추출을 변경으로 오인한 밝기/IoT/report 분류를 소스 재대조 후 교정 | 소스 감사 초안 · 사용자 리뷰 전 |
| 2026-10-15 | CROSS-06 시험앱 지도 생성: 모드별 명시 제출·Task 상태/취소·팬 예약 표시·서버 terminal 조회 후 재등록 | Java 회귀·Android 컴파일 확인 · 기기 미검증 |
| 2026-10-15 | CROSS-06 공개 지도 생성: SDK·인증 Binder·현재 Host 큐 단일 접수, 입력/관측 검증, 불명확 결과 무재전송 | 소스 검토·Android 컴파일 완료 · 시험앱/기기 미완료 |
| 2026-10-15 | CROSS-06 지도 생성: 정리 후 팬 예약·최종 전송 전 재검사·불명확한 종료 시 두 자원 복구 대기·이전 Task 예약 표시 보강 | 큐 변경 SPEC→QUALITY PASS · Host 전체/공개 연결/기기 미완료 |
Connections
- 전체 리팩터링 진행 현황
- 현재·목표 구조와 통합 경계
- 로컬 소스 기준선:
docs/refactor/skix-business-logic-audit-baseline-2026-10-15.json(공개 전체 소스/로그 아님).
| 2026-10-15 | B-MAP-03 선행 native 원본 snapshot export (canonical 공개전달 이전): epoch/seq/format·동일서명 FD·16MiB/5초/2writer/10초 제한 | 지도영향30PASS·SPEC→QUALITY; Host/SDK/IoT 연결·APK/기기 미완료, Wiki 원본 초안 |
10/15 최신 소스 이관 · 공개 기록
아래는 이번 문서 공유에 포함한 소스 변경입니다. APK·기기 반영과 제품 승인을 뜻하지 않습니다. 이전 기록의 미배포 표기는 당시 상태입니다.
10/15 후속 · 공간별 외부 AQM 공개 조회와 시험앱 (소스 검증 · APK/기기 미반영)
- 경로: 현재 Framework 공기질 도메인 → 기존 AIR_QUALITY 인증 조회 → SDK immutable snapshot → 시험앱 공간명/ID 선택. DeviceAgent 바인딩과 본체 MCU 대체값을 사용하지 않습니다.
- 표시: PM10/PM2.5/PM1, VOC/NOx, HCHO/CO₂, 온습도 및 등급 등 실제 21필드와 수신시각입니다. 데이터 없음·장소 미설정·조회 실패를 구분합니다. 수신시각은 센서 측정시각·freshness·청정 완료 근거가 아닙니다.
- 수명/권한: READ 권한과 generation 전후 검사, 현재 root 및 요청 공간 검증, 소비 앱 binder/epoch/조회순번 검증을 적용합니다. 기존 AIDL 뒤에 추가해 기존 호출 순번을 유지합니다.
- 검증/남음: 정본
skix-aqm-query-evidence-2026-10-15.json. 독립 센서 청정 range3의 실행 owner/설정/완료 조립과 APK·실기기 검증은 별개이며 아직 미완료입니다.
10/15 후속 · 청정 준비 조회의 소유자 변경 차단 (소스 검증 · APK/기기 미반영)
- 이전: follow/moving 청정 공용 준비 조회가 처음 캡처한 follow 실행이 product facts·공기질·fan 조회 사이 바뀌어도 READY를 반환할 수 있었습니다.
- 변경: 조회 후 hardware 권한을 읽고 현재 root/fan/follow 실행 identity를 다시 확인합니다. successor가 닫힌 객체여도 이전 준비값을 재사용하지 않고 RETIRED로 반환합니다. 기존 정상·사용중·팬 미정리 판단과 입력 순서는 유지합니다.
- 한계: 준비 조회는 시점별 관측이며 실행 허가를 영구 보장하지 않습니다. 실제 Task dispatch의 권한·자원 재검사는 계속 필요합니다. 센서 청정 실행/완료 이관과 별개입니다.
- 근거: 실제메서드 RED 재현·영향10PASS9.51s·독립 명세→품질 PASS. 빌드/미검증 최신 결과 정본
skix-cleaning-admission-owner-evidence-2026-10-15.json.
10/15 후속 · 센서 청정 선택등급 저장 책임 이관 (소스 검증 · APK/기기 미반영)
- 이전/이후: DeviceAgent enum private airSensorAirQual → 기존 Framework 공기질 서비스 인스턴스 저장소. 공개 호환 getter/setter와 완료 판정 읽기·성공 후 clear가 모두 같은 서비스에 위임됩니다.
- 보존: 빈 기본값, null/raw 값과 예외, animation/처리중/range/설정 조건, 등급 두 번 읽기, 10초 경계·역방향 시각·성공 때만 clear를 보존합니다. 기존 실행 스레드와 수명 규칙을 바꾸지 않았습니다.
- 근거/한계: 실제 서비스·호환 caller·원본10009 trace/state 대조 포함 영향12PASS14.95s, 명세→품질 PASS. 정본 skix-aqm-quality-owner-evidence-2026-10-15.json. 범위3 독립 Host 실행 조립·실행별 수명 격리·센서 freshness·물리완료는 이번 상태 이동으로 해결되지 않습니다. APK/기기 미변경입니다.
10/15 후속 · Launcher 이동청정 시작 조건을 Framework로 연결 (소스 검증 · APK/기기 미반영)
- 이전/이후: Launcher 버튼에서 검사하던 개인정보 모드 → 표시 배터리 15% 이하 → S02 오류를 기존 공통 정책으로 독립 Host 이동청정 Task admission에도 적용합니다. 제출과 실제 dispatch 때 현재 Framework 설정·AMR 관측으로 재검사하며, DeviceAgent 조회를 사용하지 않습니다.
- 보존/변경: 기존 이동청정 기준·검사 순서와 고정청정의 10%/온스테이션 예외는 유지합니다. 새 Framework 경로는 관측 없음·오류 미확인·소유자/설정 변경을 허용으로 간주하지 않고 차단합니다. 앱의 안내용 검사를 제거했다는 뜻은 아닙니다.
- 큐 경계: 최초 연결에서 정책 예외가 큐의 첫 준비 조회 밖으로 전파될 수 있음을 검토에서 확인했습니다. 준비 조회가 거절/관측 불능을 비-READY 상태로 반환하도록 수정해 기존 Task 실패 처리로 연결하고, 차단 후 후속 요청이 실행되는지 확인했습니다. 기존 BASIC 소유권 응답 우선순위도 유지합니다.
- 한계: 시점별 시작 조건이며 배터리·설정·모터의 원자적 실행 보장이나 물리 완료 근거가 아닙니다. 전체 이동청정·센서청정 range3 이관/도착 판정·다음 공간 진행과 별개입니다.
- 정본:
skix-moving-cleaning-admission-evidence-2026-10-15.json. 명세→품질 재검토 PASS; 최신 빌드 및 미검증 범위는 정본 참조. APK/기기 배포 없음; 문서만 이번 공유본에 반영.
10/15 후속 · 센서청정 요청 문맥의 저장 책임 이관 (소스 검증 · APK/기기 미반영)
- 이전/이후: DeviceAgent AirQualityManager의 private 감지 오염종류·자동호출 당시 AQ snapshot → 기존 Framework AirQualityManagerService 인스턴스. 실제 getter/setter/reset은 모두 같은 서비스에 위임하며 이중 저장하지 않습니다.
- 보존: 감지종류 기본/초기화 -1, parse 후 대입과 잘못된 입력 예외·이전값 유지, snapshot null 입력 무시·명시 빈값 교체·입출력 깊은 복사를 유지합니다. 저장값 없음의 로그와 현재 청정보고 snapshot 대체 조회는 기존 제품 caller에 남겨 호환성을 유지합니다.
- 한계: 기존 프로세스 내 상태 책임 이동입니다. 새 Host가 자동호출을 수신해 해당 실행 문맥을 구성하는 것, 공간 도착·센서 청정 시작/완료 연결은 아직 별도입니다. 이 저장값을 fresh 센서값·실행별 완료·공유 프로세스 상태로 해석하지 않습니다.
- 정본: skix-aqm-request-context-evidence-2026-10-15.json. 실제 원본/현재 메서드 대조와 기존 선택·HCHO·저장·완료 영향 검증 포함; 빌드/검토/미검증 최신 결과는 정본에 기록합니다. APK/기기 배포 없음; 문서만 이번 공유본에 반영.
10/15 후속 · 공통 AMR Task 실행 조건 조회 실패 격리 (소스 검증 · APK/기기 미반영)
- 이전/이후: 첫 실행 준비 조회에서 provider/정책 예외가 발생하면 선택된 Task가 큐에 남을 수 있었습니다. 이제 명령 전송 전 해당 요청만
FAILED / amr_admission_unavailable_before_dispatch로 종결하고, 다음 명시 요청을 처리합니다. 회전·좌표이동·복귀·추종·수동·지도생성·절전·추종청정·이동청정이 같은 처리를 공유합니다. - 책임/보존: DeviceAgent 우회·자동 재시도·물리 중단을 추가하지 않습니다. 다른 실행의 자원을 해제하지 않으며, 두 번째 준비 조회와 전송 후 오류는 기존 불확실성/복구 대기를 유지합니다. Task claim 시 RUNNING/시작시각이 이미 기록되므로 시작시각만으로 실제 기기 명령 여부를 판단하지 않습니다.
- 재시작/표시: 실제 journal을 닫고 새 TaskManager로 복원해 이 정확한 실패 이력이 새 요청을 막거나 과거 명령을 재전송하지 않는지 검증했습니다. 시험앱은 정확한 종료 사유·FAILED·해당 기능 조합일 때만 조건 조회 실패/명령 미전송으로 설명합니다.
- 근거/한계: 정본
skix-amr-admission-failure-evidence-2026-10-15.json. 기존 통합 추출 fixture의 56개 심볼 컴파일 오류는 변경 전에도 동일하게 재현되어 별도 미검증으로 기록합니다. 전체 DeviceAgent 독립 실행·APK/기기·native 완료 증거를 확보한 것은 아닙니다. Wiki 배포 없음.
10/15 후속 · 센서청정 요청 분기·준비 순서 Core 이관 (소스 검증 · APK/기기 미반영)
- 실제 경로:
AirQualityCommandHandler.handleAirSensorDetected→ 기존AirQualityManager위임 →AirQualityManagerService.handleLegacySensorCleaning. Core가 분기·효과 순서를 소유하고 Android Bundle/정책 조회/장치 효과는 현재 제품 adapter가 제공합니다. - 보존한 동작: LLM busy 무응답, 잠금/야간/목적지/정책 거절의 기존 실패통지, preparation 판정 후 요청 flag → IoT 발행 → 요청 준비 → 종류/AQM 문맥 저장 → callback → 도킹/준비 안내 순서. 발행 후 예외의 부분효과를 되돌리거나 재전송하지 않습니다.
- 근거: 실제 Core 독립 실행 및 frozen 원본/실제 adapter의 trace·입력 변경·예외·종류 상태 대조 포함 영향7PASS8.31s, SPEC→QUALITY PASS, Android Java compile73s exit0. 정본
skix-aqm-sensor-request-flow-evidence-2026-10-15.json. - 남음: 효과 구현은 아직 DeviceAgent에 있습니다. 독립 Host의 실행별 목적지/공간/스캔/센서청정 효과 조립과 native 도착/완료 상관은 미완료입니다. APK/기기/Wiki 배포 없이 소스만 검증했으며, 이전 누적 APK 후보에는 이 변경이 없습니다.
10/15 후속 · 고정청정 예약복귀의 독립 Host 연결 (소스 검증 · APK/기기 미반영)
- 실제 경로: 소비 SDK
scheduleBasicReturn→ 인증 Cleaning Binder → 현재 Host Basic 실행 ID/예약 타이머 → worker → 기존 스테이션 복귀 Task. DeviceAgent 명령 발행이나 Launcher 타이머를 요구하지 않습니다. - 보존/변경: 기존 절대시각·예약 교체·취소·정지 후 이전 callback 폐기·한 번 소비 규칙을 재사용합니다. 등록은 Cleaning+AMR 제어 권한, 예약 취소는 Cleaning 권한을 검사합니다. 등록자의 UID/package·trace를 복귀 Task에 유지하고, 기존 복귀 준비의 팬 정리·LLM·배터리 조건을 사용합니다.
- 불확실성: Task 제출 중 실행 owner가 바뀌면 확인된 Task ID를 보존해
OUTCOME_UNKNOWN으로 표시하며 자동 재전송하지 않습니다. 이미 제출된 Task는 예약 취소로 지우지 않습니다. 타이머 상태는 기존처럼 프로세스 내에만 있으며 재부팅 복원은 추가하지 않았습니다. - 시험앱: 1/5/10분 예약·취소, 예약 상태 한글 설명·시각·Task ID·오류를 표시합니다. 연결이 바뀐 작업은 폐기하고, 예약·Task 등록을 실제 도킹 완료로 표시하지 않습니다.
- 정본/한계:
skix-basic-return-host-evidence-2026-10-15.json. 소스 회귀·실제 공통 타이머 조립·SPEC→QUALITY·SDK/Host/시험앱 컴파일 완료. APK/기기 미배포; 문서만 이번 공유본에 반영, native 도킹 완료 상관·전체 이동/센서청정 독립 실행은 미완료입니다.
10/15 후속 · Interaction 조회의 DeviceAgent 의존 제거와 최신 관측 확인
- 실제 경로: 시험앱 조회 탭 → 기존
readPublicLlmPipeline→ 공개 LLM Binder → Host → 인증 Agent. 기존 DeviceAgent 전용 Interaction 조회 버튼을 교체했으며 새 API나 실행 owner는 추가하지 않았습니다. - 변경: Host가 한 번 조회한 결과를 반환하기 전 현재 연결·엔진·순번·관측 시각·owner와 일치하는지 기존 runtime checker로 확인합니다. 같은 runtime 내부에서 연결이나 최신 관측이 달라졌다면 오류로 반환합니다. 재조회나 IDLE 대체는 하지 않습니다.
- 소비 표시: 기존 한글 상태명/원시 코드·엔진·순번·관측 시각 표시와 epoch/binder/요청 순번에 따른 오래된 결과 폐기를 재사용합니다. LLM 상태 조회는 마이크 실제 동작이나 Task/물리 동작 완료 판정이 아닙니다.
- 근거/한계: 정본
skix-llm-independent-query-evidence-2026-10-15.json. APK·기기 미배포; 문서만 이번 공유본에 반영이며 최신 검사는 반환 시점의 관측 검증이지 이후 상태를 보장하는 lease가 아닙니다.
10/15 후속 · AQM 표시·센서청정 완료 타이머의 상태 소유권 이관
- 원본 의존: 센서청정의 완료 판단은 센서 등급의 10초 유지뿐 아니라, DeviceAgent가 소유한 표시 품질·목표·화면 타이머 완료 flag를 사용했습니다. 이 값은 원본에서도 Task/실제 팬 정지의 증거가 아닙니다.
- 실제 변경: 통합 등급·표시 등급·목표 등급·경과 tick·완료 flag를
AirQualityManagerService소유LegacyCleaningDisplayState로 옮겼습니다. DeviceAgent AQM의 동일 필드는 제거하고, 통합값 반영·조회 초기화·모니터 시작/중단·보고서 입력·완료 판단을 같은 상태로 연결했습니다. - 보존: 기존 개선 20 callback tick당 한 단계 감소, 완료 전용 10 tick, 감소 우선, 완료 이후 감소, 목표가 같을 때 타이머 중단/다르면 재예약, 일반 표시 timer callback 순서, 악화 즉시 반영을 유지합니다. 실제 시간 기반 타이머로 변경하지 않았고 Handler/화면 callback은 기존 adapter에 남았습니다.
- 검증: 원본 tick frozen fixture와 새 상태·실제 adapter를 30,000가지 입력으로 비교하고 기존 완료판정 10,009개 trace/state 대조를 포함한 영향 검증·SPEC→QUALITY를 수행했습니다. 정본
skix-aqm-display-state-evidence-2026-10-15.json. - 남음: 독립 Host의 센서청정 목적지/공간·전체 실행 조립과 native 도착/완료 상관은 별도 미완료입니다. 본 변경만으로 DeviceAgent 제거 후 센서청정 전체 동작을 보장하지 않습니다. APK/기기 배포 없음; 문서만 이번 공유본에 반영.
10/15 이전 공유 · Vision 조회·4종 후보와 IoT 고정청정 보완
- Vision 이전/이후: 시험앱 조회 버튼의 DeviceAgent 준비 요구와 legacy 호출을 제거하고 기존 Host 요청 상태 조회로 연결했습니다. 요청 flag를 실제 카메라 동작/Task 완료로 해석하지 않습니다. 영향 8건·명세→품질 검토와 새 APK 후보 반영까지 확인했습니다.
- 패키징 기준 갱신: 예약 복귀·Interaction 조회·AQM 요청/표시 상태를 포함한 Host/시험앱/AMR/IoT 4종 후보를 다시 만들고 서명·내용·SDK를 검사했습니다. 위 각 절의 ‘APK 미반영’은 해당 소스 묶음 당시 기록입니다. 최신 정본은
skix-independent-vision-query-apks-evidence-2026-10-15.json이며 기기 설치·실동작은 미검증입니다. - IoT 고정청정 진행 중: MQTT BASIC 명령을 기존 Cleaning Task에 연결하고 접수 상태를 응답하도록 변경 중입니다. correlation별 영속 기록으로 중복·연결 교체·접수 결과 불명확 시 자동 재전송을 막습니다. 큐 접수나 명령 Task 상태를 청정 물리 완료로 표시하지 않습니다.
- 명세 검토에서 발견한 미완료: 새 경로가 기존 시작 이벤트에 쓰는
APPCLEANUUID저장을 건너뜁니다. 새 START 접수와 과거 중복 요청 조회를 구분해 현재 실행의 correlation을 보존해야 합니다. 영향 7건 PASS·1차 Java 컴파일 통과 기록은 있으나 이 보완과 최종 검토 전이므로 이관 완료가 아닙니다. 현재 4종 후보에도 포함되지 않았습니다. - 남은 실행 책임: MQTT 센서/이동청정 경로, 독립 Host 전체 청정 조립, CLEAN 시작/종료 이벤트 수명과 실제 완료 상관은 별도 미완료입니다. DeviceAgent와 Launcher의 조건·상태·효과가 모두 제거됐다는 뜻이 아닙니다.
10/15 이전 공유 · 고정청정 시작 식별자 보존 / MQTT 진입 보완 중
- 완료한 소스 보완: 고정청정 Task 응답에 새 접수인지 기존 요청 조회인지 구분하는 불변 receipt를 사용합니다. 새 START의 정상 접수만
APPCLEANUUID에 반영하며, 과거 중복 조회·다른 명령·접수 실패는 현재 실행의 식별자를 보존합니다. 이전 절의 ‘저장 누락’은 이 보완 전 상태입니다. - 근거: 실제 MQTT helper의 저장 누락을 실패 시험으로 재현한 뒤 영향 시험 7건(6.31초), 명세→품질 검토, IoT Java 컴파일(31초)을 확인했습니다. 정본
skix-iot-basic-task-evidence-2026-10-15.json. CLEAN 시작/종료 이벤트 생산자 전체 이관이나 실제 청정 완료의 근거는 아닙니다. - 진행 중인 상위 경로:
IotAgent.subscribeToTopic의 기존 준비 조건은 DeviceAgent 초기화 응답에 의존했습니다. AWS 연결 확인 후 BASIC만 Framework 경로로 먼저 전달하고 나머지 명령의 기존 준비 조건을 유지하는 코드를 작성했습니다. - 품질 검토에서 발견: 실제 parser는 RuntimeException을 잡고 빈 요청 객체를 반환합니다. 현재 helper는 이를 미처리로 돌려줄 수 있어 기존 준비 상태가 참이면 같은 메시지가 legacy router로 다시 전달될 수 있습니다. 시험의 예외 전파 가정과 실제 parser가 달랐습니다. 영향 시험 15건(12.26초)·Java 컴파일(27초) 통과만으로 이 문제를 닫지 않으며 품질 보완 중으로 유지합니다.
- 다음 완료 조건: 파싱 실패/빈 요청과 미이관 모드를 구분하고, 잘못된 요청의 legacy 재전달 차단을 실제 parser 계약으로 검증한 뒤 명세→품질 검토를 마칩니다. 전역 준비 값을 강제로 켜지 않습니다.
- 배포 경계: 위 두 소스 변경은 현재 4종 APK 후보와 기기에 미포함입니다. 독립 센서/이동청정·전체 CLEAN 수명·실행별 native 완료 계약은 여전히 남아 있습니다.
10/15 최신 공유 · MQTT 파싱 수정과 독립 진입 4종
이 절은 위 ‘보완 중’ 기록 이후의 수정 결과입니다. 이전 검토 실패 기록은 이력으로 보존합니다.
| 항목 | 이전 → 현재 | 보존/미완료 |
|---|---|---|
| 잘못된 청정 요청 | parser 빈 객체가 legacy로 재전달될 수 있음 → 기존 수신 예외 처리로 거절 | 실제 Gson/parser 회귀 16건·명세/품질·Java 24초. 옵션 없는 NEXT/PAUSE/RESUME 기존 경로 유지 |
| 상위 준비 조건 | 모든 topic이 DeviceAgent 초기화 대기 → BASIC·mapping·mapUpload·setBackground만 먼저 Framework handler로 전달 | 미이관 센서/이동 모드와 다른 topic은 기존 gate 유지. 전역 ready 조작 없음 |
| 연결·시각 검사 | 독립 BASIC 우회에서 기존 검사 누락 → 독립/legacy가 같은 AWS·30초 검사 공유 | 빈 timestamp·30초 초과 거절, 정확히 30초 허용. 잘못된 timestamp 파싱의 기존 허용 정책 유지 |
| 중복 실행 | stale/disconnected 명령이 fallback될 위험 → 처리된 것으로 소비해 재전달 차단 | 명령 접수와 물리 완료는 별도; 기존 journal/owner 검사 유지 |
검증: 영향 21건 통과, 기존 배경화면 전체 시험 1건은 Android 대역 누락으로 컴파일 실패. 실제 배경 callback의 중복·교체 owner·종료·callback 후 예외를 별도 집중시험 1건으로 확인했습니다. 명세→품질 검토 PASS, IoT Java 컴파일 28초. 정본 skix-iot-migrated-ingress-evidence-2026-10-15.json.
배포/잔여: 새 APK 패키징·설치·실제 MQTT/서버/기기 동작은 미검증입니다. 독립 센서/이동청정 전체 실행, CLEAN 이벤트 수명, 요청별 native 도착/도킹 완료는 미완료입니다.