"""Create an isolated, disposable copy of a workspace directory for a Coder/Validator node -- or, in the Supervisor's blue-green deploy flow, for a candidate instance -- to operate in. isolate_workspace(source_dir) never modifies `source_dir`; it copies it into a fresh temporary directory and returns the new path. Adapted from graphloop's isolated_coding_workspace.py (2026-07-30). Only change from the original: the default parent-dir constant and prefix now follow graphlemon's own `.graphlemon/` runtime-directory convention instead of graphloop's `.hermes/graphloop-isolated-workspaces`. Reconcile DEFAULT_ISOLATION_PARENT_DIR against the final runtime-directory layout in docs/sdd/05-实施约束与依赖.md when this is wired into a real Enabler unit. """ import os import shutil import tempfile from typing import Optional # Matches the graphlemon architecture doc's `.graphlemon/` convention for # platform-owned runtime state. Kept as a module constant (not hardcoded at # each call site) so a future SDD decision can change it in one place. DEFAULT_ISOLATION_PARENT_DIR = os.path.expanduser("~/.graphlemon/isolated-workspaces") class IsolatedWorkspaceError(Exception): """Raised when the source directory cannot be isolated.""" def isolate_workspace(source_dir: str, *, parent_dir: Optional[str] = None) -> str: """Copy `source_dir` into a fresh temporary directory and return its path. `parent_dir`, if given, is used as the parent of the fresh temp directory (created first if missing) instead of the OS default temp location. Some agent runtimes refuse to write under the OS default temp directory (e.g. a sandboxed CLI treating macOS's `/var/folders/.../T` as a sensitive system path and blocking writes to it even from a coding role) -- `parent_dir` lets callers that hit this route the isolated copy somewhere the worker/candidate-instance process can actually write to. Callers that don't pass `parent_dir` get plain `tempfile.mkdtemp()` behavior; callers that want the graphlemon-standard location should pass `DEFAULT_ISOLATION_PARENT_DIR` explicitly. """ if not source_dir or not os.path.isdir(source_dir): raise IsolatedWorkspaceError( f"source_dir is not a directory: {source_dir!r}" ) if parent_dir: os.makedirs(parent_dir, exist_ok=True) destination = tempfile.mkdtemp(prefix="graphlemon-isolated-", dir=parent_dir) shutil.copytree(source_dir, destination, dirs_exist_ok=True) return destination