SKIX 도메인별 Bundle 계약 상수화
결론
기존 Bundle의 command·key·프로토콜 값은 임의 문자열이 아니라 하위 호환 wire 계약으로
관리한다. 원본은 skix-framework/base/compatibility에 도메인별 Constants로 두고,
base/core의 typed Manager와 기존 Agent가 동일 원본을 컴파일한다. 상태·정책·Task 실행 권위는
Constants가 아니라 각 ManagerService에 남는다.
2026-09-09 · 후속: AI Agent로 보내는 이름도 한 곳에서 관리
b2b45fa에서 기존 LlmManager의 송신 문자열 29개를 공용 LlmBundleConstants 참조로 바꿨다.
공용 정의 24개를 추가하고 5개는 재사용했다. 개발자는 메시지 이름을 여러 앱에서 따로 찾지 않고
skix-framework/base/compatibility/java/…/legacy/bundle/에서 확인할 수 있다.
| 질문 | 변경 내용 |
|---|---|
| 실제 전송 내용이 바뀌나? | 아니다. 29개 기존 문자열을 그대로 보존했다. |
| 기존 호출 코드를 모두 고쳐야 하나? | 아니다. 기존 METHOD_*, ARGS_*, CMD_STOP_TTS 이름은 별칭으로 유지한다. |
| 오타가 있던 이름은? | 기존 ARGS_RESUIT, ARGS_WITTIME는 호환을 위해 유지하고 정의 쪽에는 RESULT, WI_TIME을 쓴다. |
| TTS나 날씨 기능의 주인이 바뀌나? | 아니다. 송신 형식만 모았으며 실제 실행·제품 정책 소유권 이관과는 별개다. |
검증 당시 SDK 42개·DeviceAgent 22개 JVM 테스트와 DeviceAgent release 빌드가 통과했다.
29개 참조를 원문으로 되돌려 대조했을 때 import 외 전체 LlmManager 소스가 직전 버전과 같았다.
즉 메서드 분기·호출 순서는 변경하지 않았다. 당시 9개 handler 검사기는 이 새 별칭을 검사하지 않았으나,
후속 261f689에서 9개 handler + LlmManager = 10개 소스로 기본 범위를 넓혔다.
새 계약 테스트는 29개 문자열 값을 확인하고, 검사기는 별칭이 LlmBundleConstants.Command/Key/Value를
직접 참조하는지 확인한다. 원시 문자열 복귀·다른 소유자 참조 등을 다루는 검사기 회귀 6개도 당시 통과했다.
두 검사는 다른 문제를 막는다. 값 검사는 Agent에 보내는 이름이 바뀌는 것을, 소유 위치 검사는 각 파일에서 문자열을 다시 따로 관리하는 것을 막는다. 소유 위치 검사는 정규식 기반이며 모든 Java 표현식 해석이나 같은 Constants 안의 잘못된 멤버 선택까지 증명하지 않는다.
최초 오프라인 release 빌드는 lint 의존성 캐시 부재로 실패했으나 기본 wrapper의 온라인 빌드로 재검증했다. lint를 끄지 않았다. 기존 빌드 경고·테스트 로그 한계가 있으며 APK 설치와 기기 검증은 하지 않았다. 정책·콜백 후속 상황은 현재 진행 현황을 따른다.
2026-09-09 · 앞선 handler 이관에서 바뀐 폴더 책임
기준 커밋은 850d2f2(LLM 계약 이관), aeaa175(DeviceAgent 빌드 경계 수정)다.
쉽게 말해 통신할 때 쓰는 단어의 정의는 기능별 공용 폴더로 옮겼고,
그 명령을 실행할지 판단하는 기존 정책 코드는 아직 별도 이관이 필요하다.
| 실제 소스 위치 | 이번에 맡긴 책임 | 맡기지 않은 책임 |
|---|---|---|
base/compatibility/java/…/legacy/bundle/LlmBundleConstants.java |
LLM 상태·발화 명령과 데이터 키의 정의 | 음성 합성·재생, 청소 시작 판단 |
| 같은 폴더의 AMR·Security·IoT Constants | 주행 상태 조회·보안 요청·IoT 상태 통지의 각 계약 | 도메인 간 실행 정책 |
apps/DeviceAgent/…/domain/interaction/LlmCommandHandler.java |
기존 수신 경로에서 각 도메인의 상수를 참조 | 새 ManagerService로의 정책 이관 완료를 의미하지 않음 |
apps/DeviceAgent/app/build.gradle |
공유 소스 중 internal/legacy/bundle 폴더만 컴파일 |
SDK 전용 연결 어댑터까지 앱에 포함하지 않음 |
base/는 모두 skix-framework/base/를 뜻한다. 공유 폴더 전체를 가져오는 것과
필요한 계약 폴더만 가져오는 것은 다르다. 기존 설정은 SDK 전용 연결 코드까지 포함해
DeviceAgent 컴파일 오류를 냈다. 이제 실제 Gradle 컴파일 소스 목록을 검사해
정식 Bundle 계약 25개가 들어오고 SDK 전용 파일은 들어오지 않는지 확인한다.
이번 검증과 남은 작업
- DeviceAgent handler 검사 대상은 9개 handler다. 기존 8개에
LlmCommandHandler를 추가했고, 후속으로LlmManager별칭 검사를 더해 현재 기본 대상은 10개 소스다. - 명령 소유권은 273 methods / 273 unique owners로 유지했다.
- 직전 구현 검증에서 서버 290개·SDK 41개·DeviceAgent 16개 JVM 테스트가 통과했다. SDK·FrameworkService release 빌드와 DeviceAgent release 빌드도 통과했다.
- 기존
setTts(수신)와setLlmTts(기존 송신)는 서로 다른 통신 이름이므로 합치지 않았다. - 잠금 조건, 호출어 반응, 자동 TTS 후 청소 시작 등 제품 정책은 그대로다. 후속은 이 조건들을 특성 테스트로 고정하고 해당 도메인 및 공통 정책 서비스로 나누는 작업이다.
- 실기기 미검증이다. 빌드 경고가 남아 있으며 실제 AI Agent 왕복·부팅 등록·제품 전환 완료를 뜻하지 않는다.
이번 문서 갱신은 위 커밋의 검증 기록을 반영한다. 문서 배포 과정에서 Android 테스트를 다시 실행했다는 뜻은 아니다.
아래 SVG와 뒤의 이전 검증 표는 LLM 추가 전 8개 handler 시점의 기록이다.
구조도는 유지한다. handler 9개와 송신 LlmManager 1개를 합친 최신 검사 범위는 위의 10개 소스 기준을 따른다.
왜 compatibility에 두는가
Bundle 문자열은 Binder 양쪽에서 byte가 같아야 하지만 신규 앱이 직접 사용해야 할 public API는
아니다. 따라서 AOSP식 책임을 다음처럼 분리한다.
| 위치 | 책임 |
|---|---|
base/core |
앱이 호출하는 typed Skix<Domain>Manager와 공개 모델 |
base/compatibility |
기존 command·Bundle key·value의 byte-exact 번역 계약 |
base/services |
lifecycle, 상태, 정책, 자원 조정, Task 실행·완료 판단 |
| 기존 Agent / DeviceAgent | 물리 provider 또는 단계적 legacy gateway |
Gradle SDK, AOSP DeviceControlAPIJavaLib, standalone Make Agent와 DeviceAgent의 Gradle/AOSP
빌드가 같은 compatibility 소스를 직접 컴파일하도록 연결했다. 복사본을 각 앱에 다시 만들지 않는다.
현재 소스 반영 범위
현재 Constants 소스는 25개다. 기존 Manager 17개 도메인(AirCleaner, AirQuality, AMR, Camera, Error, Event, ICT, LCD, Manage, Map, Provisioning, Schedule, Security, SelfTest, Setting, Toast, Wi-Fi)에 더해 공통 envelope, IoT, Update, Environment, LLM, MCU, Vision, AMR Agent 보고 계약을 분리했다.
| 범위 | 판정 | 확인 방법 |
|---|---|---|
| canonical core와 기존 17개 Manager | 상수화·JVM 검증 | core raw-wire verifier, byte-exact unit test |
| LLM Agent | 상수화·정적 검증 | receiver-aware verifier |
| MCU Agent service/manager | 상수화·정적 검증 | receiver-aware verifier |
| Vision Agent entry | 상수화·정적 검증 | receiver-aware verifier |
| Update Agent service/UI command | 상수화·정적 검증 | receiver-aware verifier |
| AMR service/dispatcher/receive/callback | 상수화·정적 검증 | receiver-aware verifier |
| AMR 이동 primitive | Position, RobotSpeed, Rotation, Motor_VW, ZUT 상수화·정적 검증 |
x/y/theta, 속도, 회전, timestamp의 Bundle byte 보존 |
| AMR 센서·진단 primitive | LidarData, LidarSensor, LineLaser, DiagnosisMotor 상수화·정적 검증 |
front/rear/left/right 및 진단 필드의 Bundle byte 보존 |
| AMR Map model | MapData, ModifiedMapData, EncMapData, ZipMapData, SetMapData, MapCopy, MapStatus 상수화·정적 검증 |
MapBundleConstants가 Map payload를 소유하고 putObject도 verifier가 검사 |
| AMR OTA/Error helper | OTAUploadTask, OTAStart, OTAStatus, ErrorList, BootReceiver 상수화·정적 검증 |
envelope는 AmrAgentBundleConstants, payload는 UpdateBundleConstants·ErrorBundleConstants가 소유 |
| AMR runtime state | AMRState, AllMovingInfo, DockingStatus, Status 상수화·정적 검증 |
reportCmd envelope는 Agent, 이동·도킹·상태 payload는 AmrBundleConstants; JSON 필드는 유지 |
| AMR calibration | CalDistanceData, MultiToFCalibration 상수화·정적 검증 |
대문자 CALIBRATION_RESULT (Result)를 소문자 result와 분리하고 JSON·동적 metric/raw index 키는 유지 |
| 검증 대상 Agent 합계 | 37개 소스 파일 | Agent Bundle verifier PASS |
| DeviceAgent AMR 이동 | MovementCommandHandler 상수화·JVM 검증 |
AMR Manager와 같은 AmrBundleConstants 사용 |
| DeviceAgent AMR 상태 callback | MainService의 AllMovingInfo·DockingStatus 소비 구간 상수화·빌드 검증 |
생산자와 같은 AmrBundleConstants; 전체 MainService 이관 완료를 의미하지 않음 |
| DeviceAgent AMR calibration callback | AMRSystem의 CalDistance·MultiToFCalibration dispatch 값 상수화·빌드 검증 |
생산자와 같은 AmrBundleConstants; 나머지 AMR report dispatch는 후속 slice |
| DeviceAgent Map | MapCommandHandler 상수화·JVM 검증 |
Map Manager와 같은 MapBundleConstants 사용 |
| DeviceAgent Cleaning | CleaningLifecycleCommandHandler 상수화·JVM 검증 |
Cleaning 고유 계약은 AirCleanerBundleConstants, 기존 Schedule·IoT·AMR 계약은 원 소유자 재사용 |
| DeviceAgent AirQuality | AirQualityCommandHandler 상수화·JVM 검증 |
센서/리포트는 AirQuality, animation/청정 동작은 AirCleaner, 이동 실패 callback은 AMR 계약 재사용 |
| DeviceAgent System/DeviceStatus | DeviceStatusCommandHandler 상수화·JVM 검증 |
device/app status는 Manage, error list는 Error 계약 재사용 |
| DeviceAgent System/Recovery | RecoveryCommandHandler 상수화·JVM 검증 |
fault acknowledgement는 Error, OTA mode는 Update 계약 사용 |
| DeviceAgent System/Runtime | RuntimeCommandHandler 상수화·JVM 검증 |
Manage·Wi-Fi·Setting·ICT·Update의 실제 protocol owner를 유지 |
| DeviceAgent System/Control | SystemControlCommandHandler 상수화·JVM 검증 |
system multiplexer는 Manage, 이동 실패 callback은 AMR 계약 재사용 |
| DeviceAgent LLM/Interaction | LlmCommandHandler 상수화·JVM 검증 |
LLM·AMR·Security·IoT·Manage의 실제 계약 소유자 참조; 제품 정책 분리는 후속 |
| DeviceAgent command owner | 273 methods / 273 unique owners | raw literal과 Constants를 함께 해석하는 constant-aware verifier; 기존 로컬 상수였던 cancelAirSensorCleaning까지 해석 |
| DeviceAgent 나머지 handler·AMR diagnostics/composite helper | 미이관 | 실제 Bundle receiver와 owner 분류 필요 |
| IotAgent·SKMLauncher2·EventAssetAgent | 미이관 | producer/consumer 쌍별 회귀시험 필요 |
초기 전역 검색에서는 수천 개의 후보가 나왔지만 JSONObject, Intent, DB 필드와 같은 동명
메서드가 섞인다. 따라서 검색 수를 완료율로 사용하지 않고 실제 receiver 타입을 확인해 verifier에
편입된 파일만 완료로 판정한다.
호환성 보존 규칙
isActive와is_active,scheduleId와schedule_id는 서로 다른 계약으로 유지한다.appDeleteMap,allremove같은 역사적 값도 임의로 정규화하지 않는다.op_ommand는 기존 외부 wire typo이므로 고치지 않고OP_COMMAND상수로 byte-exact 보존한다.- JSON, Intent, DB, UI, ROS 메시지 필드는 이름이 같다는 이유로 일괄 치환하지 않는다.
- AMR primitive·Map·OTA/Error·runtime-state·calibration slice도
Bundlereceiver만 이관했으며 같은 클래스의JSONObject키는 변경하지 않았다. - Multi-ToF aggregate metric 이름과 raw index는 런타임 생성 map key이므로 상수화하지 않았다.
- 도메인별 producer와 consumer를 함께 확인하고 wire byte가 같은지 단위 테스트로 고정한다.
- public 앱은 Constants가 아니라 typed Manager를 사용한다.
이전 검증 결과 — LLM handler 추가 전
| Gate | 결과 |
|---|---|
| canonical Bundle contract verifier | PASS |
| migrated Agent Bundle verifier | 37개 파일 PASS |
| migrated DeviceAgent Bundle verifier | AMR 이동·Map·Cleaning·AirQuality·System 4종 8개 handler PASS |
| SDK unit test + debug AAR | clean 기준 32 actionable tasks, PASS |
| SkixFrameworkService unit test + debug APK | clean 기준 58 actionable tasks, PASS |
| DeviceAgent unit test + debug APK | clean 기준 39 actionable tasks, PASS |
| framework tree / layer boundary | PASS |
| framework API boundary | canonical skix-framework/base/core 검사 및 성공 시 stderr 없음 |
| legacy command owner | 273 methods / 273 unique owners |
위 결과는 소스 계약과 로컬 빌드 증거다. AOSP product 전체 Make/Soong 빌드, platform 인증서, 부팅 등록, Binder 호출, 센서·모터 결과는 아직 실기기 미검증이다. DeviceAgent release의 기존 duplicate-library 및 missing-annotation R8 경고는 남아 있으나 빌드는 성공했다.
System handler 기준점은 31ffa7e, AMR primitive 기준점은 165bef2, Map model 기준점은
621cce0, OTA/Error helper 기준점은 4293cb9, AMR runtime-state 생산자와 DeviceAgent 소비 구간
기준점은 1ddf15b, AMR calibration 생산자와 대응 report dispatch 기준점은
refactor/blueprint-foundation의 커밋 0af6824이다.
다음 순서
- DeviceAgent의 273 owner map을 기준으로 다음 도메인 handler의 Bundle producer/consumer를 묶는다.
- AMR의 남은 diagnostics/composite helper를 실제 타입 기준으로 분류한다.
- IotAgent, SKMLauncher2, EventAssetAgent를 도메인별로 편입한다.
- 신규 typed Binder가 안정화된 도메인부터 legacy Bundle adapter를 deprecated 처리한다.
- signed AOSP build와 실기기 shadow comparison 후 기능별로 cutover한다.