/** * 커널에 심을 관측 상태 — 구조가 아니라 "무엇이 어디에 얼마나". * * 커널 `hydrateObserved` 가 받는 스냅샷 모양을 그대로 따른다. 여기서 골라 담지 않는 이유: 무엇을 * 심을 수 있는지는 커널이 알고, 그 규칙(고아 작업 제외·이행 완료 오더 제외 등)도 커널에 있다. * 호스트가 미리 골라내면 규칙이 두 곳으로 갈린다. */ export interface ObservedSeed { locations: unknown[]; items: unknown[]; equipment: unknown[]; persons?: unknown[]; assets?: unknown[]; tasks?: unknown[]; orders?: unknown[]; /** 확인해 둔 주목 신호 id — 상태에서 파생되지 않아 반드시 함께 넘긴다. */ acked?: string[]; /** * 주목 신호가 **처음 성립한 시각** — 확인과 같은 성질이다(계산으로 되살릴 수 없다). * * 넘기지 않으면 재기동 뒤 세 시간째 지속된 조건이 「0초째」로 되살아난다. 방금 찬 자리와 세 시간째 * 막힌 자리는 할 일이 다르므로, 그 값이 거짓이면 화면이 사람을 잘못된 순서로 움직인다. */ attentionSince?: { id: string; since: string; }[]; } /** 심은 것의 내역 — 로그와 테스트가 같은 숫자를 본다. */ export interface WarmStartCounts { itemCount: number; equipmentCount: number; personCount: number; assetCount: number; taskCount: number; /** 원값(requested)이 있어 커널이 실제로 심을 수 있는 오더. */ orderCount: number; /** 이어받은 확인 처리 수 — 잃으면 확인해 둔 신호가 다시 빨개진다. */ ackedCount: number; /** 이어받은 시작 시각 수 — 잃으면 지속된 조건이 모두 「방금」이 된다. */ attentionSinceCount: number; /** * 원값이 없어 심지 못하는 오더 — **0 이 아니면 반드시 말한다.** * progress 만 있는 오더는 남은 수량을 알 수 없어 재계획의 근거가 되지 못한다. 지어내지 않고 뺀다. */ ordersWithoutDemand: number; /** Finished orders carried folded (ADR-0092) — 0 when the twin had folded nothing. */ foldedOrderCount: number; } export type WarmStartPlan = ({ action: 'hydrate'; seed: ObservedSeed; } & WarmStartCounts) /** 벤치 트윈 — 새 시작에서 용량을 재는 게 목적이라 현재 상태를 주입하면 측정이 오염된다. */ | { action: 'skip'; reason: 'bench'; } /** 심을 상태가 없다 — 처음 만든 트윈이거나 저널·체크포인트가 비었다. 정상이다. */ | { action: 'skip'; reason: 'no-state'; } /** 커널이 관측 주입을 지원하지 않는다 — 구조만으로 시작하므로 보유량은 0 으로 읽힌다(알려야 한다). */ | { action: 'skip'; reason: 'unsupported'; }; /** 스냅샷이 들고 올 수 있는 관측 축 — 어느 것이든 있으면 주입할 가치가 있다. */ interface ObservedState { locations?: unknown[]; items?: unknown[]; equipment?: unknown[]; persons?: unknown[]; assets?: unknown[]; tasks?: unknown[]; orders?: unknown[]; foldedOrders?: unknown; acked?: string[]; attentionSince?: { id: string; since: string; }[]; } /** * 봉투를 벗긴다 — **체크포인트가 겹포장돼 복원이 알리지 않고 죽던 자리.** * * `TwinEngine.snapshot()` 은 라이브에서는 상태를, 시뮬에서는 `runtime.resync()` 봉투(`{revision,state}`)를 * 돌려준다. 그 봉투를 다시 `{revision, state}` 로 감싸 캐시에 넣었으므로, 꺼낸 `state` 는 축이 하나도 * 없는 껍데기였다 → `planWarmStart` 가 "심을 것이 없다" 로 판정하고 **알리지 않고** 넘어갔다. 저널에 수천 건이 * 있고 캐시도 최신인데 트윈이 빈 채로 떴다(오류도 로그도 없다). * * 반환 모양을 통일하는 것이 뿌리 수정이지만, 그 값은 State 구독 페이로드로도 나가므로 클라이언트 계약을 * 건드린다. 그래서 **복원 경로에서만** 벗긴다. 겹겹이 싸여 있어도 벗긴다(과거에 저장된 값이 남아 있다). */ export declare function unwrapState(x: any, depth?: number): any; /** * 무엇을 할지 정한다. * * 오더·작업·사람·자산도 함께 주입한다. 골라내는 규칙은 커널이 들고 있고(이행 완료 오더, 고아 작업), * 여기서는 **심을 수 없는 오더의 수만 센다** — 호출부가 그 사실을 말할 수 있어야 하기 때문이다. */ export declare function planWarmStart(state: ObservedState | null | undefined, purpose: string | undefined, canHydrate: boolean): WarmStartPlan; export interface ContinuitySeed { acked?: string[]; attentionSince?: { id: string; since: string; }[]; energy?: unknown; /** 접힌 오더(ADR-0092) — 모양은 커널이 안다(호스트는 있나만 본다). */ foldedOrders?: unknown; } export interface ContinuityPlan { seed: ContinuitySeed; /** 무엇을 이어받았나 — 로그와 시험이 같은 숫자를 본다(0 이면 이어받을 것이 없었다는 사실이다). */ ackedCount: number; attentionSinceCount: number; /** 에너지 연속성을 이어받았나 — 열린 구간이 있으면 그 구간의 요금 근거가 살아난다. */ energy: boolean; /** 접힌 오더를 이어받았나(ADR-0092). */ foldedOrders: boolean; } /** 미러가 이어받을 축만 골라낸다 — 관측 축(재고·위치·설비)은 일부러 넣지 않는다. */ export declare function planLiveContinuity(raw: unknown): ContinuityPlan; export {};