"""wr_root_resolver.py — T21.5 §7: THE ONE wait/resume root authority. T21.4 live finding: the daemon defaulted WR_ROOT to its own install dir while production data lived at /mnt/workdrive/wr-bridge; a restart without WR_ROOT exported silently opened a second event store and the controller saw no events (the split-brain that forced manual event relocation). This module is the SINGLE resolver every consumer shares: daemon / event_wait / await_operation tool / notify_event_bridge (CI event writer) / local event writers / CLI / launcher / tests. Resolution order: 1. RUNE_WR_ROOT env (explicit override; tests use it) 2. canonical production root (exists?) 3. legacy home install root (exists?) 4. canonical production root (created on demand — first-run path) Split-brain policy: if BOTH canonical and legacy roots contain live event stores, resolution FAILS LOUDLY unless the env pins one — never silently operate two stores. Reconciliation is governed, not silent: run scripts/wr_root_reconcile.py. """ from __future__ import annotations import json import os import sys from pathlib import Path CANONICAL_WR_ROOT = Path("/mnt/workdrive/wr-bridge") LEGACY_WR_ROOT = Path.home() / ".local" / "share" / "mimo-wait-resume" ENV_VAR = "RUNE_WR_ROOT" SPLIT_BRAIN_ERROR = ( "WR ROOT SPLIT-BRAIN: both canonical ({canonical}) and legacy " "({legacy}) contain live event stores. Never operate two stores " "silently — pin {env}= explicitly, or reconcile with " "scripts/wr_root_reconcile.py." ) def _has_live_store(root: Path) -> bool: """A root counts as 'live' when its event store exists and holds events (an empty dir is just an uninitialized install).""" events = root / "events" if not events.is_dir(): return False return any(events.glob("*.json")) def resolve_root(*, require_single: bool = True) -> Path: """Resolve THE wait/resume data root. Raises on split-brain when require_single (default) and no explicit pin exists.""" env = os.environ.get(ENV_VAR) if env: return Path(env) canonical_live = _has_live_store(CANONICAL_WR_ROOT) legacy_live = _has_live_store(LEGACY_WR_ROOT) if canonical_live and legacy_live and require_single: raise RuntimeError(SPLIT_BRAIN_ERROR.format( canonical=CANONICAL_WR_ROOT, legacy=LEGACY_WR_ROOT, env=ENV_VAR)) if canonical_live or CANONICAL_WR_ROOT.is_dir(): return CANONICAL_WR_ROOT if legacy_live or LEGACY_WR_ROOT.is_dir(): return LEGACY_WR_ROOT return CANONICAL_WR_ROOT # first-run: create canonical def daemon_launch_env() -> dict[str, str]: """The env a launcher must pass the daemon: WR_ROOT resolved ONCE from this authority (the daemon itself must not guess).""" return {"WR_ROOT": str(resolve_root()), ENV_VAR: str(resolve_root())} def split_brain_roots() -> list[Path]: """Diagnostic: roots that currently hold live stores.""" return [r for r in (CANONICAL_WR_ROOT, LEGACY_WR_ROOT) if _has_live_store(r)] def main() -> int: try: root = resolve_root() except RuntimeError as exc: print(str(exc), file=sys.stderr) return 2 print(json.dumps({ "resolved_root": str(root), "canonical": str(CANONICAL_WR_ROOT), "legacy": str(LEGACY_WR_ROOT), "split_brain_roots": [str(p) for p in split_brain_roots()], "env_pinned": bool(os.environ.get(ENV_VAR)), })) return 0 if __name__ == "__main__": sys.exit(main())