← Docs hub

SKIX StateManager 읽기 전용 투영 구현

한 문장으로: 여러 Agent와 센서에서 올라온 값을 source + 관측 시각 + freshness가 포함된 typed 최신 상태로 제공하는 서비스다. 정책을 판단하거나 하드웨어 명령을 실행하지 않는다.

입력 StateManager가 하는 일 출력
Agent/센서 관측값 타입·시간 순서·lifecycle을 검증하고 key별 최신 record를 보존 current, fresh, immutable snapshot

전체 리팩터링 맥락은 SKIX 리팩터링 한눈에 보기에서 먼저 확인할 수 있다.

결론

b77c2c5에서 SKIX framework의 첫 번째 구체적인 Control ManagerService로 StateManagerService를 추가했다. 이 서비스는 Agent/DeviceAgent/센서가 가진 물리 상태의 소유자가 아니라, 관측값을 typed key별 최신 record로 투영하는 수명주기 관리 컴포넌트다. 따라서 주행 중 틸팅 다운 같은 정책과 actuator 명령을 이 서비스에 넣지 않는다. 상태 투영, Policy 판단, Task 실행, Evidence 증명은 서로 다른 소유권으로 유지한다.

Connectivity, StationLocation, LLM pipeline, AirQuality, Battery의 다섯 관측을 typed state로 변환하는 adapter까지 추가했다. 이 문서는 MR7 소스의 JVM 수준 구현과 MR6 producer 연결 상태를 구분한다. Android 부팅, Binder, AOSP product packaging, DeviceAgent 연결 및 실기기 동작은 이 문서의 검증 범위가 아니다.

현재 구현

기준 브랜치: refactor/blueprint-foundation
기준 커밋: 5fbf2e4 refactor(power): split battery manager service domain

상태 근거 의미
Implemented skix-framework/base/services/core/java/com/sk/airbot/frameworkservice/server/control/state/StateManagerService.java FrameworkManagedService의 STATE identity, shared supervisor affinity, lifecycle gate, publish/read API
Implemented .../state/StateKey.java, StateRecord.java typed key(id + Class<T>), immutable observation metadata, freshness 판정
Implemented .../state/StateProjectionException.java not_ready, type_mismatch, out_of_order 안정 코드
Implemented .../runtime/SkixFrameworkCompositionRoot.java createBaseRuntime()가 같은 supervisor로 Runtime + State를 명시 구성
Implemented .../control/lifecycle/FrameworkServiceRegistry.java, ServiceLifecycleSupervisor.java 등록/의존성 순서, READY 전이, boot phase, rollback/stop 및 health snapshot
Implemented .../domain/iot/IotStateKeys.java, AwsIotConnectivityObservationAdapter.java MR6 AWS IoT boolean callback을 typed CONNECTED/DISCONNECTED record와 5분 freshness로 변환
Source reference MR6 apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/task/observation/ConnectivityObservationBridge.java source·TTL·반복 callback freshness 계약의 근거. legacy semantic event dedup은 기존 경로에 유지
Implemented .../domain/amr/StationLocationEvidence.java, AmrStateKeys.java, StationLocationObservationAdapter.java MR6 station evidence subset을 typed value와 10분 freshness로 변환하고 fingerprint dedup 보존
Source reference MR6 .../StationLocationObservationBridge.java source·TTL·facts·fingerprint·cloud 판단 필요 assessment의 근거
Implemented .../domain/llm/LlmPipelineState.java, LlmStateKeys.java, LlmPipelineObservationAdapter.java MR6 status 0..12를 content-free typed 다축 상태와 60초 freshness로 변환. unknown status는 억제하고 반복 callback은 freshness 갱신
Implemented .../domain/airquality/IndoorAirQualityObservation.java, AirQualityStateKeys.java, AirQualityObservationAdapter.java MR6 측정 문자열과 warming_up/ready/invalid를 보존하는 60초 periodic projection
Implemented .../domain/power/PowerManagerService.java, BatteryObservation.java, PowerStateKeys.java, BatteryObservationAdapter.java Battery를 System과 분리한 독립 lifecycle owner와 5분 periodic projection
Implemented .../manager/SkixPowerManager.java, SkixDeviceManagers.power() typed client Manager. 기존 SkixManageManager battery API는 Power manager로 위임해 호환 유지
Source reference MR6 LlmPipelineObservationBridge.java, AirQualityManager.set(...), BatteryManager.setBatteryInfo(...), SourceFreshnessTracker.java status 매핑, 유효성, source 및 TTL 계약 근거
Designed ISkixFrameworkService, Android host, typed provider adapter Binder/SystemServer/process 경계 및 실제 producer 연결은 후속 단계
Device-unverified Android image, AOSP prebuilt, DeviceAgent/ROS 2/MCU/HAL/actuator boot, IPC 권한, crash/restart, 지연 및 물리 동작 미검증

기존 DeviceAgent의 server/control/state/StateRecord.java는 호환성을 위해 그대로 남는다. 이번 커밋은 이를 자동 forwarding하거나 기존 writer의 소유권을 빼앗지 않는다.

SKIX StateManager 관측 투영 구조

원본 SVG 열기

API 계약과 lifecycle

composition root
  -> one ServiceLifecycleSupervisor
  -> FrameworkServiceRegistry.register(STATE)
  -> StateManagerService.onStart()
  -> STATE = READY
  -> publish(StateKey<T>, StateRecord<T>)
  -> current / fresh / snapshot

서비스 API의 의도는 다음과 같다.

API 계약
publish(key, record) READY에서만 허용. key의 declared type과 value가 다르면 type_mismatch; 기존 record보다 observedAtMillis가 크지 않으면 out_of_order
current(key) 가장 최근 record를 반환. stale이어도 관측 이력의 최신값이므로 반환
fresh(key, nowMillis) nowMillis 기준 freshness window 안의 record만 반환. stale/missing은 empty
snapshot() 현재 map의 detached immutable view. 호출자가 map을 변경할 수 없음

StateRecord는 value, source, observed time, freshness duration을 보존한다. source와 value는 null/blank를 허용하지 않으며 시간 값은 음수가 될 수 없다. record 내부 value의 deep copy는 하지 않으므로 producer는 immutable value를 사용해야 한다. 시간 차이 계산은 overflow에 안전한 범위 조건을 사용한다.

모든 read/write는 서비스 lifecycle이 READY가 아니면 not_ready로 거부된다. 이는 상태값의 유효성과 서비스 lifecycle을 혼동하지 않기 위한 것이다. 예를 들어 READY인 서비스가 가진 connectivity record가 stale일 수 있고, 반대로 fresh record가 있어도 서비스가 STOPPED면 API를 호출할 수 없다.

AOSP 비교 근거

로컬 AOSP 기준은 다음 소스다.

/home/silogood/work/1.A1_SoC_new/SoC/g13qcs6490/LA.QSSI.13.0.R1/frameworks/base/services/core/java/com/android/server/SystemService.java는 서비스의 onStart()와 반복적인 onBootPhase() lifecycle을 정의한다. SystemServiceManager.java는 서비스를 등록한 뒤 onStart()를 호출하고, 증가하는 boot phase를 등록된 서비스 순서로 전달한다.

SKIX registry는 이 운영 개념을 참고하지만 Android SystemService 자체가 아니다. 현재는 transport-free plain Java이며, SystemServer, Binder, SELinux, init 또는 Android process가 호출하지 않는다. 향후 hybrid hosting에서 얇은 Android bridge가 composition root를 만들고, 실제 state/provider adapter를 주입하는 방향이다.

소유권 분리

영역 소유자 StateManager와의 관계 금지사항
관측 최신값/시간/source/freshness StateManagerService typed projection 입력을 받음 actuator 명령, 정책 추론
도메인 의미 상태 각 domain ManagerService (AMR, Vision, IoT, LLM, Cleaning, Air Quality, Power, UI 등) domain adapter가 key를 정하고 publish 다른 도메인의 writer를 몰래 호출
정책/충돌 해결 Policy manager 및 도메인 정책 state/evidence를 읽어 Can/Cannot 판단 상태 저장소가 우선순위를 임의로 결정
Task/workflow 실행 TaskManager / Workflow 상태와 정책을 읽고 명령 lifecycle을 관리 상태 observation을 새 Task로 오인
명령/물리 효과 typed provider, Agent, ROS 2, MCU/HAL 실행 결과를 observation/evidence로 되돌림 framework projection이 hardware 직접 호출
결과 증명/설명 Evidence manager task correlation과 observation을 결합 단순 latest state를 terminal success로 간주

이 분리는 “주행 중이면 틸팅 down” 같은 사양을 다음처럼 다룰 수 있게 한다. Mobility/AMR domain이 주행 상태를 관측해 state key로 publish하고, Policy가 틸팅 요구와 금지 조건을 평가하며, TaskManager가 허용된 command를 실행한다. 실행 결과와 실제 자세는 provider observation/evidence로 다시 투영한다. 여러 정책이 충돌하면 StateManager가 마지막 writer를 정하는 것이 아니라 Policy/ Resource 계층의 명시적인 priority와 rejection reason이 결정한다.

관측 adapter의 이식 경계

Connectivity, StationLocation, LLM pipeline, AirQuality, Battery의 typed 변환 adapter는 MR7에 구현됐다. 다만 실제 DeviceAgent producer가 이를 호출하는 process/Binder wiring과 dual-publish parity는 아직 구현 증거가 아니다.

MR6 Connectivity bridge semantics
  -> AwsIotConnectivityObservationAdapter (구현·JVM 검증)
  -> IotStateKeys.AWS_IOT_CONNECTION
  -> StateManagerService.publish()
  -> domain manager / policy / task / evidence read

MR6 StationLocation bridge semantics
  -> StationLocationObservationAdapter (구현·JVM 검증)
  -> AmrStateKeys.STATION_LOCATION_EVIDENCE
  -> StateManagerService.publish()

MR6 LlmPipelineObservationBridge
  -> LlmPipelineObservationAdapter
  -> LlmStateKeys.PIPELINE
  -> StateManagerService.publish()

MR6 AirQualityManager.set / BatteryManager.setBatteryInfo
  -> AirQualityObservationAdapter / BatteryObservationAdapter
  -> AirQualityStateKeys / PowerStateKeys
  -> StateManagerService.publish()

이식 시 확인할 항목은 source별 TTL 단위, timestamp clock, duplicate/fingerprint 규칙, Bundle/문자열 payload의 typed value 변환, producer sequence, process death 재연결, 그리고 기존 DeviceAgent writer와의 dual-publish 기간이다. adapter가 완성되기 전에는 MR6 semantics를 “호환된다”고 표시하지 않는다.

StationLocation의 MR6 fingerprint는 station_area_id를 포함하지 않는다. 현재 adapter는 이 동작을 호환성 테스트로 보존했다. station ID만 변경된 관측도 새로 발행해야 하는지는 dual-publish shadow 결과를 보고 별도 정책 변경으로 결정한다.

후속 gate

  1. DeviceAgent의 Connectivity, StationLocation, LLM, AirQuality, Battery 생산 지점에 legacy + typed dual-publish bridge를 연결한다.
  2. 다섯 source의 shadow 비교로 누락·순서·TTL 차이를 측정한다.
  3. Evidence manager가 state snapshot을 terminal success의 단독 근거로 쓰지 않도록 correlation과 provider acknowledgement 계약을 정의한다.
  4. Policy/Resource에 precedence, conflict, rejection code를 추가하고 틸팅을 포함한 domain policy 사례를 테스트한다.
  5. 그 다음 TaskManager/Workflow가 State + Policy + Resource를 읽고 provider command를 실행하도록 MR6 task_manager 브랜치 동작을 compatibility port한다.
  6. 마지막으로 Binder/SystemServer/init/SELinux/package를 연결하고 boot, crash/restart, permission, OTA 및 실기기 actuator 검증을 수행한다.

검증 범위

StateManagerServiceTest, SkixFrameworkCompositionRootTest, AwsIotConnectivityObservationAdapterTest, StationLocationObservationAdapterTest, LlmPipelineObservationAdapterTest, AirQualityObservationAdapterTest, BatteryObservationAdapterTest는 typed key, record validation, lifecycle affinity/gating, ordering, freshness, snapshot, composition과 다섯 adapter 변환을 FrameworkService JVM 89개 테스트에서 검증한다. SDK JVM 34개 테스트는 새 SkixDeviceManagers.power() API와 기존 Manage 호환 API의 공존을 포함한다. 저장소 tree verifier와 git diff --check는 정적 구조를 검증한다. 이 결과는 Android boot 또는 물리 로봇 acceptance 증거가 아니다.

Connections

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