← Docs hub

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은 기존 검증 후보의 해시를 고정했습니다.

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·기기·서버 독립 업로드 인수는 아직 끝나지 않았습니다.

최신 상태부터 확인 · 이전 이력과 구분

아래는 현재 소스에서 확인한 상태입니다. 뒤쪽의 날짜별 기록에는 당시 미완료 상태도 남겨 둡니다. “로컬 검증”은 설치된 기기에서 기능을 확인했다는 뜻이 아닙니다.

변경 묶음 실제로 바뀐 동작 현재 근거 아직 확인할 것
청정 실행·중단 접수·실행·물리 정지를 구분하고, 정지 불명확이면 자원 해제를 보류 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가지

  1. 어떤 요청인가: 호출 앱/MQTT/내부 이벤트, 입력값과 단위, 직접 API인지 Task인지.
  2. 기존에는 어디서 처리했나: 생산자 → DeviceAgent/서비스 → 실제 HAL/AMR/화면/저장 효과.
  3. 지금은 어디서 처리하나: SDK → 인증 IPC → Host/domain → provider. 아직 legacy를 타는 구간도 표시.
  4. 언제 허용·차단하나: 배터리, 오류, 모드, 자원, 연결, 상태 freshness와 경계값.
  5. 어떤 순서로 효과가 생기나: 저장·중단·예약·실행·보고·화면전환 중 선후 관계와 부분 실패.
  6. 언제 끝났다고 하나: 접수 ACK, 실행 중, 파일 저장, 서버 응답, 물리 완료, 정리 완료를 분리.
  7. 실패 후 어떻게 복구하나: 재조회/명시 재요청/중단 확인. 자동 replay 유무와 남은 차단 조건.
  8. 무엇을 검증했나: 소스 비교, 실제 메서드 회귀, Android 컴파일, APK 설치, 실기기, 제품 승인을 각각 확인.

일괄 리뷰 기록 예시: CLEAN-03 / 비교 revision·소스 SHA / 기존 stop 반환 / 현재 정지 관측 대기 / 허용·수정 필요 / 남은 복구 시나리오 / 리뷰어·날짜. “미검토”는 제품 승인 상태이며 로컬 명세·품질 검토 실패를 뜻하지 않습니다.

우선 검토할 제품 영향

  1. 청정 중단·다음 작업: STOP 응답만으로 자원을 비우지 않음. 실제 정지 근거가 없으면 다음 작업이 막힐 수 있음.
  2. 공통/수동 일시정지: 시스템 사유 해제 시 사용자의 수동 pause와 다른 사유가 남으면 재개하지 않음.
  3. 명령 결과 불명확: 재연결했다고 이전 행동 명령을 자동 재전송하지 않음. 조회/명시 복구 필요.
  4. UV: 기존 주기는 유지하지만 불명확한 명령 뒤 명시 OFF가 필요함. 자동 부팅 활성화는 별도 정책 검토.
  5. 지도 생성: 배터리 표시값 ≥27과 정리 순서를 보존하는 중. 큐의 팬 예약은 로컬 검토 완료. 공개 SDK/Binder 연결은 로컬 검토·Android 컴파일 완료. Host 제품 정책 전체·물리 완료·기기 검증은 남음.
  6. 설정/PIN: revision 충돌을 조용히 덮어쓰지 않음. 기존 데이터 이관과 초기값은 별도 검토.
  7. OTA: 알려지지 않은 phase/native 값만으로 성공·삭제·재부팅하지 않음. native 101을 실행 job 완료로 볼 수 없음.
  8. 화면/LED: 값 추출을 새 정책으로 세지 않음. 명령 수락·readback·물리 표시를 구분.

읽는 순서와 검증 경계

실제로 무엇이 달라졌는가 — 리뷰용 비교표

아래는 상세 항목을 읽기 전에 확인할 대표 사례입니다. 기존 경로의 설명은 해당 항목에 인용한 소스 범위에 한정합니다. 기존 시스템 전체에 같은 기능이 없었다는 뜻은 아닙니다.

상황 기존 경로에서 확인한 동작 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·문장·암호키·민감 파일 내용은 결과 문서에 복사하지 않습니다.

검증 중인 변경은 완료 변경과 분리

상세 기록의 단위 · 기능 이름보다 조건과 효과를 비교

한 기능에 여러 규칙이 있으면 따로 확인합니다. 예를 들어 청정 시작에는 요청 중복, 시작 허용 조건, 팬/틸트 준비, 실행 등록, 상태 보고가 있고, 중단에는 명령 전송, 실제 정지 확인, 자원 해제, 제품 복귀가 있습니다. “청정 이관 완료”라는 한 문장으로 묶지 않습니다.

확인 축 기록할 내용 누락하면 생기는 오해
진입 경로 앱/API·MQTT topic·내부 callback → 서비스 경계 → 최종 효과 SDK 메서드가 생긴 것을 실제 기기 연결로 오인
입력·기준값 모드, 단위, 허용 범위, 기본값, 임계값; 보존/변경 구분 기존 배터리/밝기/시간 기준을 새 정책으로 오인
시작 조건 권한, 배터리, 도킹, 오류, 실행 owner, 필요한 연결 모델/포트만 구현되고 실제 실행 조건이 빠짐
효과 순서 저장·팬·모터·화면·음성·보고 중 먼저 실행되는 것 중간 실패 뒤 일부 효과가 남는 상황을 숨김
완료 조건 접수, 실행 시작, 파일 저장, 물리 정지, 서버 응답 각각의 근거 ACK나 전역 상태를 현재 요청의 완료로 오인
실패·취소 미전송/거절/unknown, 취소 대상, 남은 자원, 원복 가능 여부 실제 실행 여부를 모르는 상태에서 재실행
복구·호환 재연결/부팅 뒤 조회·재시도 규칙, direct/Task/legacy 차이 기존 앱에서만 동작하는 경로를 독립 실행으로 오인
판정 근거 기준 revision·소스 SHA·검증 정본·제품 승인 오래된 테스트 통과를 최신 APK 검증으로 오인

앞으로의 기록: 기존 대장 ID에 날짜별 후속을 붙이고, 변경 전 동작과 제품 승인 여부를 삭제하지 않습니다. 테스트가 통과해도 제품 승인 칸은 자동으로 바꾸지 않습니다. 새로운 입력·분기·부수 효과를 조사하지 못했다면 “비교 필요”로 남깁니다.

한 항목을 승인할 때 확인할 순서

  1. 입력: 어떤 API·MQTT 명령·callback이 변경을 발생시키는가.
  2. 허용 조건: 배터리·오류·도킹·기존 실행·권한 조건이 보존됐는가.
  3. 실제 효과: 팬·모터·음성·화면·저장·외부 보고가 어떤 순서로 실행되는가.
  4. 종료: 접수 응답, 수행 완료, 중단 요청, 실제 정지가 각각 어떻게 판정되는가.
  5. 복구: 실패·재연결·부팅 뒤 어떤 상태가 남고 누가 해제하는가.
  6. 검증: 소스 확인·Java 회귀·Android 컴파일·APK 설치·실기기 동작 중 어디까지 확인했는가.

예를 들어 CLEAN-03 승인은 “중단 API가 성공했다”만으로 끝내지 않습니다. 동일 실행의 팬 정지 관측 → 자원 해제 → 다음 작업 허용까지 확인해야 합니다. 반대로 POWER-03 승인은 새로운 Task 정책의 유무보다 기존 배터리 보정표와 경계값이 그대로인지가 핵심입니다.

실행 순서까지 풀어 쓴 상세 사례

아래 사례는 코드에서 확인한 경로를 단계별로 설명합니다. 검증 기록의 날짜와 범위를 함께 봐야 합니다. 로컬 회귀가 통과했더라도 현재 설치된 APK가 같은 소스라는 뜻은 아닙니다. 각 사례는 기존 대장 ID의 상세 설명이며 새로운 기능 완료 건수로 세지 않습니다.

사례 A · 청정 중단 뒤 다른 작업을 시작할 때 — CLEAN-03 / RESOURCE-01

기존 경로: 앱/명령 → DeviceAgent AirCleaningManager.stop() → stopFan(). 이 메서드가 반환됐다는 사실과 팬이 실제로 멈췄다는 사실은 별개입니다. 기존 시스템 전체에 정지 감시가 없었다고 단정하는 비교는 아닙니다.

현재 Framework 경로: 취소/중단 요청 → 청정 실행 owner → 팬 중단 전달 → 정지 근거 유무에 따라 아래 두 경로로 분기 → 다음 작업의 자원 확보 검사.

시점 무엇을 확인하는가 결과와 사용자 영향
중단 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))를 사용합니다. 즉, 현재 오류가 해제돼도 같은 그룹에 다른 활성 오류가 있으면 제동 요청은 남습니다.

  1. 오류 A·B가 모두 활성: 제동을 요구합니다.
  2. A만 해제: B가 남아 있으므로 제동 해제 요청으로 바꾸지 않습니다.
  3. 해당 그룹의 남은 오류가 없어진 경우: 이 규칙이 제동 해제를 요청할 수 있습니다.
  4. 제동 해제 요청은 새로운 이동 허가나 자동 재개가 아닙니다. 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가 이 의미를 받아들이는지는 미검증

구체적인 상황별 판정:

보존한 값: 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 기준으로 멱등화됨

CLEAN-02 — 청소 명령 dispatch 실패를 자동 재시도하지 않고 UNKNOWN으로 보존

CLEAN-03 — 청소 stop/cancel은 소유한 hardware가 실제 비활성이라는 증거 전까지 FREE로 풀지 않음

CLEAN-04 — 청소 실행 수명과 단순 명령 acceptance를 분리하고 standalone Host에서 실제 연결

CLEAN-05 — 청소 정책은 Framework에 추출되었고, legacy·standalone 양 경로에서 서로 다른 조립 경계를 가짐

CLEAN-06 — 자동 tilt도 TaskManager queue와 terminal reason을 사용

AMR-01 — 직접 이동 호출에 유한 좌표 검사를 추가

AMR-02 — 회전/이동 요청을 공용 execution lane과 owner/request dedup으로 직렬화

AMR-03 — 이동 완료를 ACK가 아니라 arrival/position/station 관측 basis로 구분

AMR-04 — 이동 owner/session generation이 바뀌면 이전 callback을 폐기

AMR-05 — map command는 legacy Bundle 명령을 계속 소유하며 Framework map 객체는 보조 경계

TASK-01 — TaskManager가 owner·request·method·state를 공통 기록

TASK-02 — Task cancellation은 owner/UID 범위로 제한

RESOURCE-01 — resource lease가 FREE/HELD/UNVERIFIED/RECOVERY_REQUIRED를 구분

RESOURCE-02 — capability readiness는 execution/resource 허가와 분리

POWER-01 — 배터리 관측에 source timestamp/sequence/freshness를 부여하고 늦은 샘플을 거절

POWER-02 — PowerManager가 관측 observer ownership과 service lifecycle을 소유

POWER-03 — 배터리 표시 보정표는 그대로 이동했고, admission/charging 의미는 추가하지 않음

POWER-04 — 배터리 사용량 산식은 보존되지만 저장/clock owner는 아직 legacy

POWER-05 — AMR charging command 자체는 아직 DeviceAgent AMR service에 남음

POWER-06 — power observation direct AMR route와 legacy DeviceAgent route는 동시에 섞지 못함

단순 이동·미완료 범위 인벤토리

  1. BatteryCapacityPolicy와 BatteryUsagePolicy는 산식/표 추출이며, charging, low-battery movement admission, persistence의 전체 이관이 아니다.
  2. MapCommandHandler는 Framework map DTO를 사용하지만 legacy Bundle/MQTT/AMRMap/MapManager가 실제 command owner로 남아 있다.
  3. LegacyCleaningExecutionAdapter와 LegacyAmrExecutionAdapter는 Framework interface를 구현하지만 물리 실행은 DeviceAgent singleton/controller/AMR service에 남아 있다.
  4. PowerManagerService의 observer lifecycle은 구현되어도 AMRAgent callback registration, Binder/HIDL, board sensor, 실기기 배터리 freshness는 확인되지 않았다.
  5. cleaning fan/tilt queue의 JVM 테스트는 Task/port 계약 증거이지 제품 APK 설치·실제 fan/tilt 정지 증거가 아니다.
  6. Task/Resource/Capability의 service lifecycle generation은 Framework 내부 규칙이다. 기존 Schedule, OTA, MQTT, CleaningReport DB의 데이터 이관·재시작 복구는 이 감사에서 확인하지 않았다.
  7. CmdPolicyManager의 전역 emergency/critical-low-battery stop과 Framework owned cancellation은 서로 다른 계약이다. 하나가 다른 하나를 대체한다고 기록하지 않았다.
  8. WSS, TTS, camera, air-quality, maintenance 등은 이번 요청의 cleaning/AMR/Task/resource/power 집중 범위에서 상세 항목으로 확장하지 않았다. 관련 Framework 파일이 존재한다는 사실만으로 업무 변화로 세지 않았다.
  9. apps/*/build, generated AIDL, evidence JSON, APK/build/device 로그는 소스 동작의 보조 근거로만 취급했다. 생산 배포·Binder 연결·물리 완료는 별도 검증이 필요하다.
  10. 현재 worktree의 MapCommandHandler, MappingProgress, AMR/cleaning handler는 pre-refactor HEAD 대비 대규모 변경 파일이다. 이번 보고서의 AMR-05는 현재 파일 안에서 확인되는 ownership만 기술했으며, mappingHost의 현재 dirty 변경과 peer HEAD 전체 차이는 별도 line-by-line 검토하지 않았다.
  11. 따라서 “기존”을 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 집계

WSS-02 — PIN credential text 비노출

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에 결합

privacy/voice/LLM/TTS

VOICE-01 — privacy/manner/voice setting은 legacy owner를 통해 유지

VOICE-02 — LLM status를 content-free typed observation으로 투영

2026-10-15 후속 — DeviceAgent 없는 이동 준비의 LLM 관측 수명

위 60초 callback projection은 기존 경로의 기록입니다. 현재 독립 Host의 지도 생성·스테이션 복귀 준비는 AndroidVoiceRecognitionRuntime.llmStatusObservation(5000L)로 Agent에 인증된 상태를 조회하며, 이를 이전 callback 캐시와 섞지 않습니다.

조건 현재 판정 보존·한계
같은 연결·같은 관측, 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로 분리

VOICE-04 — Managed TTS Binder relay가 signature/peer death를 검사

VOICE-05 — TTS 완료 callback은 execution identity가 있어야 상관

VOICE-06 — TTS 문장 로그/장기 상태 노출 억제

display/night/LED/SystemUI

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 구성은 확인되며 교체·활성화 경계가 갭

공통 미검증 및 후속 검토 경계

  1. 이 대장은 소스 기반 변경 register다. JVM/host test가 통과해도 Binder transaction, signed APK, system/system_ext/vendor partition, SELinux, boot ordering, real provider callback, camera/MCU/AudioTrack, 실제 화면·LED·TTS 청취를 증명하지 않는다.
  2. WSS ACK/accepted/queued/started와 patrol movement, capture/upload, physical camera/AMR 완료는 각각 별도 신호다. 하나의 callback을 다른 완료로 승격하면 안 된다.
  3. ProductSystemUI는 TYPE_APPLICATION_OVERLAY 기반 앱 UI다. Android의 실제 status bar/SystemUI 권한, insets, lock-screen policy를 대체한다는 근거는 이 범위에서 확인되지 않았다.
  4. LLM/TTS는 legacy DeviceAgent relay를 기본 target으로 유지하는 경로가 보인다. Framework provider가 실제 설치/서명/permission까지 연결됐다는 근거는 이 읽기 전용 조사에서 확인하지 않았다.
  5. 비교 기준은 현재 dirty worktree의 소스다. 변경 목록이 큰 상태이므로, 이후 review에서는 각 항목을 기준 commit과 diff로 다시 고정해야 한다.

4. IoT·설정·지도·보고·업데이트

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 직접 조회와 초기 연결 실패 복구

마지막 변경: 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의 부수효과

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를 만들지 않았습니다.

근거: 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 조회

B-PLACE-02 — mapping command execution

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 순서

B-REPORT-02 — report 저장소 ownership

B-OTA-01 — UpdateEngine OTA lifecycle

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

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

B-SCOPE-02 — SET_APP_STATUS / MQTT producer 미위치

미포함/미검증 범위

  1. MQTT broker, AWS/KVS, cloud schema, TLS/credentials, server receipt and reconnect over real network.
  2. AndroidManifest, init rc, SystemServer registration order, SELinux, VINTF, partition placement and boot logs.
  3. UpdateEngine payload download, slot switch, reboot, rollback, AMR/MCU flashing and physical completion.
  4. APK contents/signatures, installed package paths, Binder permissions and real-device service death/rebind.
  5. Legacy preference/database/map schema migration, cross-package data retention, backup/restore and OTA upgrade continuity.
  6. Full apps/IotAgent/app/src/main/java/com/sk/airbot/iotagent/IotAgent.java topic parity, WSS upload/media delivery, schedule/holiday/streaming/background flows and all AMRAgent packet paths.
  7. Current framework source outside the inspected skix-framework/base boundary, generated AIDL implementation wiring, and backend/Launcher/SystemUI consumers.

Validation record

5. 교차 도메인 리뷰

CROSS-01 — UV 주기는 유지하지만, 불명확한 명령 뒤 자동 ON 재시도를 막음

CROSS-02 — 수동 일시정지 의도와 시스템 일시정지 사유를 분리

CROSS-03 — 야간 해제 시 센서 보정: 이력 없는 상태를 임의로 만료 처리하지 않음

CROSS-04 — 예약 시각 비교를 단말 locale과 분리

CROSS-05 — 공기질 보정 산식과 민감도는 제품 정책으로 보존

CROSS-06 — 지도 생성: 기존 배터리 기준은 유지, 정리 확인과 실행 소유권을 추가하는 중

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 — 오류 해제는 다른 활성 오류의 제동·음성·틸트 차단을 풀면 안 됨

CROSS-08 — 사용 시간·청정 보고 시간: 실제 실행 시간과 제품 집계값을 구분

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의 의미를 바꾸면 이력에 날짜·변경 전후·이유를 남기고, 이전 판정을 삭제하지 않습니다. 새 source SHA가 바뀌면 영향을 받는 항목의 근거를 다시 확인합니다.

일괄 리뷰 체크리스트

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

| 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/기기 미반영)

10/15 후속 · 청정 준비 조회의 소유자 변경 차단 (소스 검증 · APK/기기 미반영)

10/15 후속 · 센서 청정 선택등급 저장 책임 이관 (소스 검증 · APK/기기 미반영)

10/15 후속 · Launcher 이동청정 시작 조건을 Framework로 연결 (소스 검증 · APK/기기 미반영)

10/15 후속 · 센서청정 요청 문맥의 저장 책임 이관 (소스 검증 · APK/기기 미반영)

10/15 후속 · 공통 AMR Task 실행 조건 조회 실패 격리 (소스 검증 · APK/기기 미반영)

10/15 후속 · 센서청정 요청 분기·준비 순서 Core 이관 (소스 검증 · APK/기기 미반영)

10/15 후속 · 고정청정 예약복귀의 독립 Host 연결 (소스 검증 · APK/기기 미반영)

10/15 후속 · Interaction 조회의 DeviceAgent 의존 제거와 최신 관측 확인

10/15 후속 · AQM 표시·센서청정 완료 타이머의 상태 소유권 이관

10/15 이전 공유 · Vision 조회·4종 후보와 IoT 고정청정 보완

10/15 이전 공유 · 고정청정 시작 식별자 보존 / MQTT 진입 보완 중

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 도착/도킹 완료는 미완료입니다.

Keyboard shortcuts

⌘K / Ctrl+KOpen command palette
/Focus search
g hGo to home
g pGo to projects
g sGo to sessions
j / kNext / prev row (tables)
?Show this help
EscClose dialogs

Structured queries

Mix key:value filters with free text in the palette:

type:sessionOnly session pages
project:llm-wikiFilter by project name (substring)
model:claudeFilter by model name (substring)
date:>2026-03-01Sessions after a date
date:<2026-04-01Sessions before a date
tags:rustPages mentioning a tag/topic
sort:dateSort results by date (newest first)

Example: type:session project:llm-wiki date:>2026-04 sort:date