/* * **재기동 정책** — 이 트윈은 다시 세워질 때 자기 과거를 어떻게 대하나. * * ── 왜 이 이름인가 (ADR-0029 · plans/simulator-as-source.md §2) ───────────── * 옛 이름은 `realityMode` 였고 값은 `mirror`·`sim-world`·`sim-experiment` 였다. 그 축은 「현실이 어디서 * 오나」를 말하는 것처럼 보였지만, 코드가 그것으로 가른 것은 **재기동 거동 하나**였다. 그리고 「현실이 * 어디서 오나」는 ADR-0029 가 **원본의 종류**로 옮긴 물음이다(`TwinReference.adapterType`) — 트윈은 한 * 종류이고 언제나 원본을 반영한다. * * 그래서 이 축은 사라지지 않고 **트윈에 남아 이름을 바꿨다.** 남는 이유: 「자기 저널을 잇는가」는 원본의 * 성질이 아니라 **트윈과 자기 과거의 관계**다. 같은 시뮬레이터 원본을 보는 두 트윈이 하나는 이어가고 * 하나는 매번 처음부터 재현할 수 있다 — 원본은 그 둘을 구별하지 못한다. * * resync — 외부 실물에서 다시 읽는다(원본이 진실이므로 우리 과거를 이어 붙이지 않는다). * resume — 저널을 이어 세운다(이 트윈이 낳은 타임라인이 곧 사실이다). * reset — 저널을 비우고 씨앗부터 다시 돌린다(재현 가능한 실험). * * ── 기본값을 두지 않는다 ──────────────────────────────────────────────────── * 예전에는 미선언이 알리지 않고 `sim-experiment`(=저널 초기화)로 떨어졌다. 그 관용은 값이 하나 어긋나는 * 순간 **저널을 지우는 길**이 된다 — 개명 뒤 옛 값이 남아 있는 행을 그 길로 읽으면 재기동 한 번에 * 이력이 사라진다. 그래서 모르는 값은 **오류를 낸다.** 부르는 쪽이 선언하게 만드는 것이 이 축의 요점이다. */ /** 재기동 정책 — 셋뿐이다. */ export type RestartPolicy = 'resync' | 'resume' | 'reset' export const RESTART_POLICIES: readonly RestartPolicy[] = ['resync', 'resume', 'reset'] as const /** * 저장된 값을 정책으로 읽는다 — **모르면 오류를 낸다**(기본값으로 메우지 않는다). * * 옛 어휘(`mirror`·`sim-world`·`sim-experiment`)를 여기서 번역하지 않는다: 번역을 두면 옛 값이 살아남고, * 그 값이 다시 어딘가에 저장된다. 저장된 값은 **한 번 옮기고**(데이터 이관) 코드는 새 어휘만 안다. * 그래서 이 함수는 옛 값에 대해서도 오류를 내며, 무엇으로 옮겨야 하는지 문장으로 말한다. */ export function readRestartPolicy(raw: unknown, where: string): RestartPolicy { const value = typeof raw === 'string' ? raw.trim() : '' if ((RESTART_POLICIES as readonly string[]).includes(value)) return value as RestartPolicy const legacy: Record = { mirror: 'resync', 'sim-world': 'resume', 'sim-experiment': 'reset' } if (legacy[value]) { throw new Error( `${where}: restart policy "${value}" is the old vocabulary — move the stored value to "${legacy[value]}" ` + '(ADR-0029 · plans/simulator-as-source.md §2). Nothing is translated at read time on purpose: a translation would keep the old value alive.' ) } throw new Error( `${where}: restart policy ${value ? `"${value}"` : 'is missing'} — declare one of ${RESTART_POLICIES.join(' | ')}. ` + 'There is no default: an unspecified policy used to fall through to "reset", which erases the journal on the next reboot.' ) } /** 이 값이 정책인가 — 오류를 내지 않고 묻는 자리(화면·검사)를 위해. */ export function isRestartPolicy(raw: unknown): raw is RestartPolicy { return typeof raw === 'string' && (RESTART_POLICIES as readonly string[]).includes(raw) }