The file /Users/agent/strukto/mirage/mirage/cli/commands/workspace.py has been updated successfully.

import os
import sys
import threading
import time
from pathlib import Path
from typing import TYPE_CHECKING, Annotated

import typer

from mirage.backend.loader import load_backend_class
from mirage.cli.client import ClientError
from mirage.cli.config import (load_workspace_yaml, parse_mount_uri,
                               parse_mount_uri_full, split_mount_spec)
from mirage.cli.helpers import _client, _ensure_server
from mirage.fuse.loader import build_registry_from_config
from mirage.fuse.registry import FuseOpRegistry
from mirage.observe.inspector import (workspace_status_from_ws,
                                      workspace_transfers_from_ws)
from mirage.types import ConsistencyPolicy, MountMode, RuntimeType
from mirage.workspace import Workspace

if TYPE_CHECKING:
    from mirage.fuse.fs import MirageFS

workspace_app = typer.Typer(no_args_is_help=True)

REPORT_INTERVAL = 2

def _report_loop(ws: Workspace, mountpoint: str, fs: "MirageFS") -> None:
    from mirage.cli.client import MirageClient
    client = MirageClient()
    time.sleep(2)
    while True:
        try:
            status = workspace_status_from_ws(ws)
            transfers = workspace_transfers_from_ws(ws)
            ops = fs.drain_ops()
            client.report_fuse_stats(mountpoint, status, transfers, ops)
        except Exception:
            pass
        time.sleep(REPORT_INTERVAL)

def _parse_mounts_for_fuse(
    specs: list[str],
    cfg: dict,
    default_mode: MountMode,
    default_consistency: ConsistencyPolicy,
    ttl: int,
    agent_id: str | None,
) -> tuple[dict, MountMode, ConsistencyPolicy, int, str | None]:
    resolved_agent_id = agent_id
    mounts: dict = {}
    if cfg:
        if "consistency" in cfg:
            default_consistency = ConsistencyPolicy(cfg["consistency"])
        if "ttl" in cfg:
            ttl = cfg["ttl"]
        if not resolved_agent_id and "agent_id" in cfg:
            resolved_agent_id = cfg["agent_id"]
        for prefix, value in cfg.get("mounts", {}).items():
            if isinstance(value, str):
                backend_obj, m, c = parse_mount_uri_full(
                    value, default_mode, default_consistency)
            elif isinstance(value, dict) and "backend" in value:
                cls = load_backend_class(value["backend"])
                backend_obj = cls(**(value.get("args") or {}))
                m = (MountMode(value["mode"])
                     if "mode" in value else default_mode)
                c = (ConsistencyPolicy(value["consistency"])
                     if "consistency" in value else default_consistency)
            elif isinstance(value, dict):
                backend_obj = parse_mount_uri(value.get("uri", ""))
                m = (MountMode(value["mode"])
                     if "mode" in value else default_mode)
                c = (ConsistencyPolicy(value["consistency"])
                     if "consistency" in value else default_consistency)
            else:
                continue
            has_override = (m != default_mode or c != default_consistency)
            if has_override:
                mounts[prefix] = (backend_obj, m, c)
            else:
                mounts[prefix] = backend_obj
    for spec in specs:
        uri, prefix = split_mount_spec(spec)
        backend_obj, m, c = parse_mount_uri_full(uri, default_mode,
                                                 default_consistency)
        has_override = (m != default_mode or c != default_consistency)
        if has_override:
            mounts[prefix] = (backend_obj, m, c)
        else:
            mounts[prefix] = backend_obj
    return mounts, default_mode, default_consistency, ttl, resolved_agent_id

def mount(
    mount_specs: Annotated[
        list[str] | None,
        typer.Argument(help="Mount specs: backend:/path "
                       "(e.g. memory:/data/, s3://bucket:/). "
                       "Append #none|#lazy|#ttl|#always for "
                       "consistency, @read|@write|@exec for mode.")] = None,
    sync_policy: str = typer.Option("staged", help="staged or immediate"),
    mode: MountMode = typer.Option(MountMode.WRITE, help="read or write"),
    consistency: ConsistencyPolicy = typer.Option(ConsistencyPolicy.CACHED),
    ttl: int = typer.Option(300, help="TTL in seconds for ttl consistency"),
    config: str | None = typer.Option(None,
                                      help="Path to YAML workspace config"),
    runtime: RuntimeType = typer.Option(RuntimeType.MEMORY,
                                        help="Runtime: memory or local"),
    fuse: bool = typer.Option(False, help="Also mount as FUSE filesystem"),
    mountpoint: str | None = typer.Option(
        None, help="Local directory for FUSE mount (requires --fuse)"),
    agent_id: str | None = typer.Option(
        None, help="Agent identity exposed via /.mirage/whoami"),
    backend: str | None = typer.Option(
        None,
        help="Backend URI mounted at /. Prefix specs override for subtree."),
) -> None:
    """Create a workspace and mount backends.

    Each backend URI is routed by prefix. The longest matching prefix wins,
    so /data/logs/ takes priority over /data/ for paths under /data/logs/.

    With --fuse, also mounts the workspace as a local FUSE filesystem
    (requires macFUSE / libfuse). The process blocks while FUSE is mounted.

    URI formats:
      memory             in-memory (no disk, no deps)
      local              local filesystem at current directory
      local:./path       local filesystem at ./path (converted to abs)
      s3://bucket/pfx    AWS S3  (requires mirage[s3])
      r2://account/bkt   Cloudflare R2  (requires mirage[s3])
      https://host       HTTP read-only (requires mirage[http])
    """
    specs = list(mount_specs or [])
    if backend:
        specs.insert(0, f"{backend}:/")

    if fuse:
        _mount_fuse(specs, config, mode, consistency, ttl, agent_id,
                    mountpoint, runtime)
        return

    _ensure_server()
    mounts: dict[str, str | dict] = {}
    if config:
        cfg = load_workspace_yaml(config)
        mounts.update(cfg.get("mounts", {}))
        consistency_str = cfg.get("consistency")
        if consistency_str:
            consistency = ConsistencyPolicy(consistency_str)
        ttl = cfg.get("ttl", ttl)
    consistency_suffixes = ("#none", "#lazy", "#ttl", "#always")
    for spec in specs:
        try:
            uri, prefix = split_mount_spec(spec)
        except ValueError as e:
            raise typer.BadParameter(str(e))
        mount_consistency = None
        for suffix in consistency_suffixes:
            if uri.endswith(suffix):
                mount_consistency = suffix[1:]
                uri = uri[:-len(suffix)]
                break
        mount_mode = None
        for suffix in ("@read", "@write", "@exec"):
            if uri.endswith(suffix):
                mount_mode = suffix[1:]
                uri = uri[:-len(suffix)]
                break
        entry: dict[str, str] = {"uri": uri}
        if mount_mode:
            entry["mode"] = mount_mode
        if mount_consistency:
            entry["consistency"] = mount_consistency
        if len(entry) == 1:
            mounts[prefix] = uri
        else:
            mounts[prefix] = entry
    ws_id = _client().create_workspace(
        mounts,
        sync_policy=sync_policy,
        mode=mode.value,
        consistency=consistency.value,
        ttl_seconds=ttl,
        runtime=runtime.value,
    )
    print(ws_id)

def _mount_fuse(
    specs: list[str],
    config: str | None,
    mode: MountMode,
    consistency: ConsistencyPolicy,
    ttl: int,
    agent_id: str | None,
    mountpoint: str | None,
    runtime: RuntimeType,
) -> None:
    from mirage.backend.network.proxy import ProxyBackend
    from mirage.fuse.fs import MirageFS
    from mirage.fuse.fs import mount as fuse_mount_fn
    from mirage.fuse.remote.remote import RemoteFuseOp

    if not mountpoint:
        raise typer.BadParameter("--mountpoint is required when using --fuse")
    if not specs and not config:
        raise typer.BadParameter(
            "No backends specified. Use --backend URI or provide mount specs.")
    for spec in specs:
        if ":" not in spec:
            raise typer.BadParameter(
                f"Invalid mount spec: {spec!r}. Use backend:/path")

    Path(mountpoint).mkdir(parents=True, exist_ok=True)
    _ensure_server()
    cfg = load_workspace_yaml(config) if config else {}

    if runtime == RuntimeType.REMOTE:
        url = cfg.get("url", "")
        ws_id_remote = cfg.get("ws_id", "")
        proxy = ProxyBackend(url, ws_id_remote)
        ws = Workspace({"/": proxy}, mode=MountMode.WRITE)
        fuse_registry = FuseOpRegistry()
        fuse_registry.set_default(RemoteFuseOp(proxy))
        fs = MirageFS(ws, agent_id=agent_id, fuse_registry=fuse_registry)
    else:
        mounts, mode, consistency, ttl, agent_id = _parse_mounts_for_fuse(
            specs, cfg, mode, consistency, ttl, agent_id)
        ws = Workspace(mounts,
                       mode=mode,
                       consistency=consistency,
                       ttl_seconds=ttl)
        fuse_ops_config = cfg.get("fuse_ops", {}) if cfg else {}
        fuse_registry = build_registry_from_config(fuse_ops_config)
        fs = MirageFS(ws, agent_id=agent_id, fuse_registry=fuse_registry)

    pid = os.getpid()
    _client().register_fuse(mountpoint, pid, specs, runtime=runtime.value)

    thread = threading.Thread(target=_report_loop,
                              args=(ws, mountpoint, fs),
                              daemon=True)
    thread.start()

    fuse_mount_fn(mountpoint=mountpoint, foreground=True, fs=fs)

def exec_cmd(
        ws_id: str = typer.Argument(),
        command: list[str] = typer.Argument(),
) -> None:
    """Execute a command in a running workspace."""
    cmd_str = " ".join(command)
    try:
        output = _client().exec(ws_id, cmd_str)
        if output:
            print(output)
    except ClientError as e:
        print(f"Error: {e}", file=sys.stderr)
        raise typer.Exit(1)

def kill(ws_id: str = typer.Argument()) -> None:
    """Stop a workspace daemon."""
    _client().delete_workspace(ws_id)
    print(f"Killed workspace {ws_id}")

def sync(ws_id: str = typer.Argument()) -> None:
    """Flush dirty inodes to backends for a workspace."""
    try:
        results = _client().sync(ws_id)
        if results:
            for r in results:
                print(f"{r['path']}: {r['status']}")
        else:
            print("All clean.")
    except ClientError as e:
        print(f"Error: {e}", file=sys.stderr)
        raise typer.Exit(1)

def status(ws_id: str = typer.Argument()) -> None:
    """Show dirty/clean inode status for a workspace."""
    try:
        inodes = _client().status(ws_id)
        if inodes:
            for i in inodes:
                marker = "D" if i["dirty"] else " "
                print(f"{marker} {i['path']}")
        else:
            print("No tracked files.")
    except ClientError as e:
        print(f"Error: {e}", file=sys.stderr)
        raise typer.Exit(1)

@workspace_app.command(name="ls")
def workspace_ls() -> None:
    """List active workspaces."""
    _ensure_server()
    workspaces = _client().list_workspaces()
    if not workspaces:
        print("No active workspaces.")
        return
    for w in workspaces:
        parts = [w["id"], w.get("type", "workspace")]
        if w.get("mountpoint"):
            parts.append(w["mountpoint"])
        print("\t".join(parts))

-                       "Append #none|#lazy|#ttl|#always for "

+                       "Append #cached|#lazy|#ttl|#always for "


Also check if there are any test references to `#none`:


mirage/cli/commands/workspace.py:156:    consistency_suffixes = ("#none", "#lazy", "#ttl", "#always")


   150→        cfg = load_workspace_yaml(config)
   151→        mounts.update(cfg.get("mounts", {}))
   152→        consistency_str = cfg.get("consistency")
   153→        if consistency_str:
   154→            consistency = ConsistencyPolicy(consistency_str)
   155→        ttl = cfg.get("ttl", ttl)
   156→    consistency_suffixes = ("#none", "#lazy", "#ttl", "#always")
   157→    for spec in specs:
   158→        try:
   159→            uri, prefix = split_mount_spec(spec)
   160→        except ValueError as e:
   161→            raise typer.BadParameter(str(e))
   162→        mount_consistency = None
   163→        for suffix in consistency_suffixes:
   164→            if uri.endswith(suffix):

        cfg = load_workspace_yaml(config)
        mounts.update(cfg.get("mounts", {}))
        consistency_str = cfg.get("consistency")
        if consistency_str:
            consistency = ConsistencyPolicy(consistency_str)
        ttl = cfg.get("ttl", ttl)
    consistency_suffixes = ("#none", "#lazy", "#ttl", "#always")
    for spec in specs:
        try:
            uri, prefix = split_mount_spec(spec)
        except ValueError as e:
            raise typer.BadParameter(str(e))
        mount_consistency = None
        for suffix in consistency_suffixes:
            if uri.endswith(suffix):


   160→        except ValueError as e:
   161→            raise typer.BadParameter(str(e))
   162→        mount_consistency = None
   163→        for suffix in consistency_suffixes:
   164→            if uri.endswith(suffix):
   165→                mount_consistency = suffix[1:]
   166→                uri = uri[:-len(suffix)]
   167→                break
   168→        mount_mode = None
   169→        for suffix in ("@read", "@write", "@exec"):
   170→            if uri.endswith(suffix):
   171→                mount_mode = suffix[1:]
   172→                uri = uri[:-len(suffix)]
   173→                break
   174→        entry: dict[str, str] = {"uri": uri}
   175→        if mount_mode:
   176→            entry["mode"] = mount_mode
   177→        if mount_consistency:
   178→            entry["consistency"] = mount_consistency
   179→        if len(entry) == 1:

        except ValueError as e:
            raise typer.BadParameter(str(e))
        mount_consistency = None
        for suffix in consistency_suffixes:
            if uri.endswith(suffix):
                mount_consistency = suffix[1:]
                uri = uri[:-len(suffix)]
                break
        mount_mode = None
        for suffix in ("@read", "@write", "@exec"):
            if uri.endswith(suffix):
                mount_mode = suffix[1:]
                uri = uri[:-len(suffix)]
                break
        entry: dict[str, str] = {"uri": uri}
        if mount_mode:
            entry["mode"] = mount_mode
        if mount_consistency:
            entry["consistency"] = mount_consistency
        if len(entry) == 1:


Edit

consistency_suffixes = ("#none", "#lazy", "#ttl", "#always")

consistency_suffixes = ("#cached", "#lazy", "#ttl", "#always")


import os
import sys
import threading
import time
from pathlib import Path
from typing import TYPE_CHECKING, Annotated

import typer

from mirage.backend.loader import load_backend_class
from mirage.cli.client import ClientError
from mirage.cli.config import (load_workspace_yaml, parse_mount_uri,
                               parse_mount_uri_full, split_mount_spec)
from mirage.cli.helpers import _client, _ensure_server
from mirage.fuse.loader import build_registry_from_config
from mirage.fuse.registry import FuseOpRegistry
from mirage.observe.inspector import (workspace_status_from_ws,
                                      workspace_transfers_from_ws)
from mirage.types import ConsistencyPolicy, MountMode, RuntimeType
from mirage.workspace import Workspace

if TYPE_CHECKING:
    from mirage.fuse.fs import MirageFS

workspace_app = typer.Typer(no_args_is_help=True)

REPORT_INTERVAL = 2

def _report_loop(ws: Workspace, mountpoint: str, fs: "MirageFS") -> None:
    from mirage.cli.client import MirageClient
    client = MirageClient()
    time.sleep(2)
    while True:
        try:
            status = workspace_status_from_ws(ws)
            transfers = workspace_transfers_from_ws(ws)
            ops = fs.drain_ops()
            client.report_fuse_stats(mountpoint, status, transfers, ops)
        except Exception:
            pass
        time.sleep(REPORT_INTERVAL)

def _parse_mounts_for_fuse(
    specs: list[str],
    cfg: dict,
    default_mode: MountMode,
    default_consistency: ConsistencyPolicy,
    ttl: int,
    agent_id: str | None,
) -> tuple[dict, MountMode, ConsistencyPolicy, int, str | None]:
    resolved_agent_id = agent_id
    mounts: dict = {}
    if cfg:
        if "consistency" in cfg:
            default_consistency = ConsistencyPolicy(cfg["consistency"])
        if "ttl" in cfg:
            ttl = cfg["ttl"]
        if not resolved_agent_id and "agent_id" in cfg:
            resolved_agent_id = cfg["agent_id"]
        for prefix, value in cfg.get("mounts", {}).items():
            if isinstance(value, str):
                backend_obj, m, c = parse_mount_uri_full(
                    value, default_mode, default_consistency)
            elif isinstance(value, dict) and "backend" in value:
                cls = load_backend_class(value["backend"])
                backend_obj = cls(**(value.get("args") or {}))
                m = (MountMode(value["mode"])
                     if "mode" in value else default_mode)
                c = (ConsistencyPolicy(value["consistency"])
                     if "consistency" in value else default_consistency)
            elif isinstance(value, dict):
                backend_obj = parse_mount_uri(value.get("uri", ""))
                m = (MountMode(value["mode"])
                     if "mode" in value else default_mode)
                c = (ConsistencyPolicy(value["consistency"])
                     if "consistency" in value else default_consistency)
            else:
                continue
            has_override = (m != default_mode or c != default_consistency)
            if has_override:
                mounts[prefix] = (backend_obj, m, c)
            else:
                mounts[prefix] = backend_obj
    for spec in specs:
        uri, prefix = split_mount_spec(spec)
        backend_obj, m, c = parse_mount_uri_full(uri, default_mode,
                                                 default_consistency)
        has_override = (m != default_mode or c != default_consistency)
        if has_override:
            mounts[prefix] = (backend_obj, m, c)
        else:
            mounts[prefix] = backend_obj
    return mounts, default_mode, default_consistency, ttl, resolved_agent_id

def mount(
    mount_specs: Annotated[
        list[str] | None,
        typer.Argument(help="Mount specs: backend:/path "
                       "(e.g. memory:/data/, s3://bucket:/). "
                       "Append #cached|#lazy|#ttl|#always for "
                       "consistency, @read|@write|@exec for mode.")] = None,
    sync_policy: str = typer.Option("staged", help="staged or immediate"),
    mode: MountMode = typer.Option(MountMode.WRITE, help="read or write"),
    consistency: ConsistencyPolicy = typer.Option(ConsistencyPolicy.CACHED),
    ttl: int = typer.Option(300, help="TTL in seconds for ttl consistency"),
    config: str | None = typer.Option(None,
                                      help="Path to YAML workspace config"),
    runtime: RuntimeType = typer.Option(RuntimeType.MEMORY,
                                        help="Runtime: memory or local"),
    fuse: bool = typer.Option(False, help="Also mount as FUSE filesystem"),
    mountpoint: str | None = typer.Option(
        None, help="Local directory for FUSE mount (requires --fuse)"),
    agent_id: str | None = typer.Option(
        None, help="Agent identity exposed via /.mirage/whoami"),
    backend: str | None = typer.Option(
        None,
        help="Backend URI mounted at /. Prefix specs override for subtree."),
) -> None:
    """Create a workspace and mount backends.

    Each backend URI is routed by prefix. The longest matching prefix wins,
    so /data/logs/ takes priority over /data/ for paths under /data/logs/.

    With --fuse, also mounts the workspace as a local FUSE filesystem
    (requires macFUSE / libfuse). The process blocks while FUSE is mounted.

    URI formats:
      memory             in-memory (no disk, no deps)
      local              local filesystem at current directory
      local:./path       local filesystem at ./path (converted to abs)
      s3://bucket/pfx    AWS S3  (requires mirage[s3])
      r2://account/bkt   Cloudflare R2  (requires mirage[s3])
      https://host       HTTP read-only (requires mirage[http])
    """
    specs = list(mount_specs or [])
    if backend:
        specs.insert(0, f"{backend}:/")

    if fuse:
        _mount_fuse(specs, config, mode, consistency, ttl, agent_id,
                    mountpoint, runtime)
        return

    _ensure_server()
    mounts: dict[str, str | dict] = {}
    if config:
        cfg = load_workspace_yaml(config)
        mounts.update(cfg.get("mounts", {}))
        consistency_str = cfg.get("consistency")
        if consistency_str:
            consistency = ConsistencyPolicy(consistency_str)
        ttl = cfg.get("ttl", ttl)
    consistency_suffixes = ("#none", "#lazy", "#ttl", "#always")
    for spec in specs:
        try:
            uri, prefix = split_mount_spec(spec)
        except ValueError as e:
            raise typer.BadParameter(str(e))
        mount_consistency = None
        for suffix in consistency_suffixes:
            if uri.endswith(suffix):
                mount_consistency = suffix[1:]
                uri = uri[:-len(suffix)]
                break
        mount_mode = None
        for suffix in ("@read", "@write", "@exec"):
            if uri.endswith(suffix):
                mount_mode = suffix[1:]
                uri = uri[:-len(suffix)]
                break
        entry: dict[str, str] = {"uri": uri}
        if mount_mode:
            entry["mode"] = mount_mode
        if mount_consistency:
            entry["consistency"] = mount_consistency
        if len(entry) == 1:
            mounts[prefix] = uri
        else:
            mounts[prefix] = entry
    ws_id = _client().create_workspace(
        mounts,
        sync_policy=sync_policy,
        mode=mode.value,
        consistency=consistency.value,
        ttl_seconds=ttl,
        runtime=runtime.value,
    )
    print(ws_id)

def _mount_fuse(
    specs: list[str],
    config: str | None,
    mode: MountMode,
    consistency: ConsistencyPolicy,
    ttl: int,
    agent_id: str | None,
    mountpoint: str | None,
    runtime: RuntimeType,
) -> None:
    from mirage.backend.network.proxy import ProxyBackend
    from mirage.fuse.fs import MirageFS
    from mirage.fuse.fs import mount as fuse_mount_fn
    from mirage.fuse.remote.remote import RemoteFuseOp

    if not mountpoint:
        raise typer.BadParameter("--mountpoint is required when using --fuse")
    if not specs and not config:
        raise typer.BadParameter(
            "No backends specified. Use --backend URI or provide mount specs.")
    for spec in specs:
        if ":" not in spec:
            raise typer.BadParameter(
                f"Invalid mount spec: {spec!r}. Use backend:/path")

    Path(mountpoint).mkdir(parents=True, exist_ok=True)
    _ensure_server()
    cfg = load_workspace_yaml(config) if config else {}

    if runtime == RuntimeType.REMOTE:
        url = cfg.get("url", "")
        ws_id_remote = cfg.get("ws_id", "")
        proxy = ProxyBackend(url, ws_id_remote)
        ws = Workspace({"/": proxy}, mode=MountMode.WRITE)
        fuse_registry = FuseOpRegistry()
        fuse_registry.set_default(RemoteFuseOp(proxy))
        fs = MirageFS(ws, agent_id=agent_id, fuse_registry=fuse_registry)
    else:
        mounts, mode, consistency, ttl, agent_id = _parse_mounts_for_fuse(
            specs, cfg, mode, consistency, ttl, agent_id)
        ws = Workspace(mounts,
                       mode=mode,
                       consistency=consistency,
                       ttl_seconds=ttl)
        fuse_ops_config = cfg.get("fuse_ops", {}) if cfg else {}
        fuse_registry = build_registry_from_config(fuse_ops_config)
        fs = MirageFS(ws, agent_id=agent_id, fuse_registry=fuse_registry)

    pid = os.getpid()
    _client().register_fuse(mountpoint, pid, specs, runtime=runtime.value)

    thread = threading.Thread(target=_report_loop,
                              args=(ws, mountpoint, fs),
                              daemon=True)
    thread.start()

    fuse_mount_fn(mountpoint=mountpoint, foreground=True, fs=fs)

def exec_cmd(
        ws_id: str = typer.Argument(),
        command: list[str] = typer.Argument(),
) -> None:
    """Execute a command in a running workspace."""
    cmd_str = " ".join(command)
    try:
        output = _client().exec(ws_id, cmd_str)
        if output:
            print(output)
    except ClientError as e:
        print(f"Error: {e}", file=sys.stderr)
        raise typer.Exit(1)

def kill(ws_id: str = typer.Argument()) -> None:
    """Stop a workspace daemon."""
    _client().delete_workspace(ws_id)
    print(f"Killed workspace {ws_id}")

def sync(ws_id: str = typer.Argument()) -> None:
    """Flush dirty inodes to backends for a workspace."""
    try:
        results = _client().sync(ws_id)
        if results:
            for r in results:
                print(f"{r['path']}: {r['status']}")
        else:
            print("All clean.")
    except ClientError as e:
        print(f"Error: {e}", file=sys.stderr)
        raise typer.Exit(1)

def status(ws_id: str = typer.Argument()) -> None:
    """Show dirty/clean inode status for a workspace."""
    try:
        inodes = _client().status(ws_id)
        if inodes:
            for i in inodes:
                marker = "D" if i["dirty"] else " "
                print(f"{marker} {i['path']}")
        else:
            print("No tracked files.")
    except ClientError as e:
        print(f"Error: {e}", file=sys.stderr)
        raise typer.Exit(1)

@workspace_app.command(name="ls")
def workspace_ls() -> None:
    """List active workspaces."""
    _ensure_server()
    workspaces = _client().list_workspaces()
    if not workspaces:
        print("No active workspaces.")
        return
    for w in workspaces:
        parts = [w["id"], w.get("type", "workspace")]
        if w.get("mountpoint"):
            parts.append(w["mountpoint"])
        print("\t".join(parts))

-    consistency_suffixes = ("#none", "#lazy", "#ttl", "#always")

+    consistency_suffixes = ("#cached", "#lazy", "#ttl", "#always")


Now let me also check tests that reference the `#none` suffix:


No matches found


Good. Let me verify no more `ConsistencyPolicy.NONE` references remain:


Bash

grep -rn "ConsistencyPolicy\.NONE" --include="*.py" | grep -v __pycache__

Verify no remaining ConsistencyPolicy.NONE references


TodoWrite

Rename ConsistencyPolicy.NONE → CACHED in types.py and all references

Renaming ConsistencyPolicy.NONE to CACHED

Make Mount.consistency always explicit (no None)

Making Mount.consistency always explicit

Move Workspace class to workspace/workspace.py

Rename test file test_init.py → test_workspace.py

Run tests and pre-commit to verify


Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable

Rename ConsistencyPolicy.NONE → CACHED in types.py and all references

Renaming ConsistencyPolicy.NONE to CACHED

Make Mount.consistency always explicit (no None)

Making Mount.consistency always explicit

Move Workspace class to workspace/workspace.py

Rename test file test_init.py → test_workspace.py

Run tests and pre-commit to verify

Run tests and pre-commit to verify


Step 2: Make `Mount.consistency` always explicit. Read and update `mount.py`:


     1→from mirage.backend.base import BaseBackend
     2→from mirage.types import ConsistencyPolicy, MountMode
     3→
     4→
     5→class Mount:
     6→
     7→    def __init__(self,
     8→                 prefix: str,
     9→                 backend: BaseBackend,
    10→                 mode: MountMode = MountMode.READ,
    11→                 consistency: ConsistencyPolicy | None = None) -> None:
    12→        stripped = prefix.strip("/")
    13→        self.prefix = "/" + stripped + "/" if stripped else "/"
    14→        self.backend = backend
    15→        self.mode = mode
    16→        self.consistency = consistency
    17→
    18→
    19→class MountRegistry:
    20→    """
    21→    Longest-prefix-match router.
    22→
    23→    Given a virtual path like "/s3-prod/data/file.json", resolves to the
    24→    backend mounted at "/s3-prod/" and returns the stripped path
    25→    "/data/file.json".
    26→    """
    27→
    28→    def __init__(self) -> None:
    29→        self._mounts: list[Mount] = []
    30→
    31→    def mount(self,
    32→              prefix: str,
    33→              backend: BaseBackend,
    34→              mode: MountMode = MountMode.READ,
    35→              consistency: ConsistencyPolicy | None = None) -> None:
    36→        m = Mount(prefix, backend, mode, consistency)
    37→        for existing in self._mounts:
    38→            if existing.prefix == m.prefix:
    39→                raise ValueError(f"duplicate mount prefix: {m.prefix!r}")
    40→        self._mounts.append(m)
    41→        self._mounts.sort(key=lambda x: len(x.prefix), reverse=True)
    42→
    43→    def resolve(self, path: str) -> tuple[BaseBackend, str, MountMode]:
    44→        """
    45→        Returns (backend, backend_path, mode).
    46→
    47→        backend_path has the mount prefix stripped.
    48→        Raises ValueError if no mount matches.
    49→        """
    50→        norm = "/" + path.strip("/")
    51→        # ensure trailing slash when checking prefix match for directories
    52→        for m in self._mounts:
    53→            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
    54→                backend_path = "/" + norm[len(m.prefix):]
    55→                return m.backend, backend_path, m.mode
    56→        raise ValueError(f"no mount matches path: {path!r}")
    57→
    58→    def mount_for(self, path: str) -> Mount:
    59→        norm = "/" + path.strip("/")
    60→        for m in self._mounts:
    61→            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
    62→                return m
    63→        raise ValueError(f"no mount matches path: {path!r}")
    64→
    65→    def mounts(self) -> list[Mount]:
    66→        return list(self._mounts)
    67→

from mirage.backend.base import BaseBackend
from mirage.types import ConsistencyPolicy, MountMode

class Mount:

    def __init__(self,
                 prefix: str,
                 backend: BaseBackend,
                 mode: MountMode = MountMode.READ,
                 consistency: ConsistencyPolicy | None = None) -> None:
        stripped = prefix.strip("/")
        self.prefix = "/" + stripped + "/" if stripped else "/"
        self.backend = backend
        self.mode = mode
        self.consistency = consistency

class MountRegistry:
    """
    Longest-prefix-match router.

    Given a virtual path like "/s3-prod/data/file.json", resolves to the
    backend mounted at "/s3-prod/" and returns the stripped path
    "/data/file.json".
    """

    def __init__(self) -> None:
        self._mounts: list[Mount] = []

    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy | None = None) -> None:
        m = Mount(prefix, backend, mode, consistency)
        for existing in self._mounts:
            if existing.prefix == m.prefix:
                raise ValueError(f"duplicate mount prefix: {m.prefix!r}")
        self._mounts.append(m)
        self._mounts.sort(key=lambda x: len(x.prefix), reverse=True)

    def resolve(self, path: str) -> tuple[BaseBackend, str, MountMode]:
        """
        Returns (backend, backend_path, mode).

        backend_path has the mount prefix stripped.
        Raises ValueError if no mount matches.
        """
        norm = "/" + path.strip("/")
        # ensure trailing slash when checking prefix match for directories
        for m in self._mounts:
            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
                backend_path = "/" + norm[len(m.prefix):]
                return m.backend, backend_path, m.mode
        raise ValueError(f"no mount matches path: {path!r}")

    def mount_for(self, path: str) -> Mount:
        norm = "/" + path.strip("/")
        for m in self._mounts:
            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
                return m
        raise ValueError(f"no mount matches path: {path!r}")

    def mounts(self) -> list[Mount]:
        return list(self._mounts)


Edit

    def __init__(self,
                 prefix: str,
                 backend: BaseBackend,
                 mode: MountMode = MountMode.READ,
                 consistency: ConsistencyPolicy | None = None) -> None:

    def __init__(self,
                 prefix: str,
                 backend: BaseBackend,
                 mode: MountMode = MountMode.READ,
                 consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED) -> None:


The file /Users/agent/strukto/mirage/mirage/workspace/mount.py has been updated successfully.

    def __init__(self,
                 prefix: str,
                 backend: BaseBackend,
                 mode: MountMode = MountMode.READ,
                 consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED) -> None:

from mirage.backend.base import BaseBackend
from mirage.types import ConsistencyPolicy, MountMode

class Mount:

    def __init__(self,
                 prefix: str,
                 backend: BaseBackend,
                 mode: MountMode = MountMode.READ,
                 consistency: ConsistencyPolicy | None = None) -> None:
        stripped = prefix.strip("/")
        self.prefix = "/" + stripped + "/" if stripped else "/"
        self.backend = backend
        self.mode = mode
        self.consistency = consistency

class MountRegistry:
    """
    Longest-prefix-match router.

    Given a virtual path like "/s3-prod/data/file.json", resolves to the
    backend mounted at "/s3-prod/" and returns the stripped path
    "/data/file.json".
    """

    def __init__(self) -> None:
        self._mounts: list[Mount] = []

    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy | None = None) -> None:
        m = Mount(prefix, backend, mode, consistency)
        for existing in self._mounts:
            if existing.prefix == m.prefix:
                raise ValueError(f"duplicate mount prefix: {m.prefix!r}")
        self._mounts.append(m)
        self._mounts.sort(key=lambda x: len(x.prefix), reverse=True)

    def resolve(self, path: str) -> tuple[BaseBackend, str, MountMode]:
        """
        Returns (backend, backend_path, mode).

        backend_path has the mount prefix stripped.
        Raises ValueError if no mount matches.
        """
        norm = "/" + path.strip("/")
        # ensure trailing slash when checking prefix match for directories
        for m in self._mounts:
            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
                backend_path = "/" + norm[len(m.prefix):]
                return m.backend, backend_path, m.mode
        raise ValueError(f"no mount matches path: {path!r}")

    def mount_for(self, path: str) -> Mount:
        norm = "/" + path.strip("/")
        for m in self._mounts:
            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
                return m
        raise ValueError(f"no mount matches path: {path!r}")

    def mounts(self) -> list[Mount]:
        return list(self._mounts)

-                 consistency: ConsistencyPolicy | None = None) -> None:

+                 consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED) -> None:


    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy | None = None) -> None:

    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED) -> None:


    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED) -> None:

from mirage.backend.base import BaseBackend
from mirage.types import ConsistencyPolicy, MountMode

class Mount:

    def __init__(self,
                 prefix: str,
                 backend: BaseBackend,
                 mode: MountMode = MountMode.READ,
                 consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED) -> None:
        stripped = prefix.strip("/")
        self.prefix = "/" + stripped + "/" if stripped else "/"
        self.backend = backend
        self.mode = mode
        self.consistency = consistency

class MountRegistry:
    """
    Longest-prefix-match router.

    Given a virtual path like "/s3-prod/data/file.json", resolves to the
    backend mounted at "/s3-prod/" and returns the stripped path
    "/data/file.json".
    """

    def __init__(self) -> None:
        self._mounts: list[Mount] = []

    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy | None = None) -> None:
        m = Mount(prefix, backend, mode, consistency)
        for existing in self._mounts:
            if existing.prefix == m.prefix:
                raise ValueError(f"duplicate mount prefix: {m.prefix!r}")
        self._mounts.append(m)
        self._mounts.sort(key=lambda x: len(x.prefix), reverse=True)

    def resolve(self, path: str) -> tuple[BaseBackend, str, MountMode]:
        """
        Returns (backend, backend_path, mode).

        backend_path has the mount prefix stripped.
        Raises ValueError if no mount matches.
        """
        norm = "/" + path.strip("/")
        # ensure trailing slash when checking prefix match for directories
        for m in self._mounts:
            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
                backend_path = "/" + norm[len(m.prefix):]
                return m.backend, backend_path, m.mode
        raise ValueError(f"no mount matches path: {path!r}")

    def mount_for(self, path: str) -> Mount:
        norm = "/" + path.strip("/")
        for m in self._mounts:
            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
                return m
        raise ValueError(f"no mount matches path: {path!r}")

    def mounts(self) -> list[Mount]:
        return list(self._mounts)

-              consistency: ConsistencyPolicy | None = None) -> None:

+              consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED) -> None:


Now update `workspace/__init__.py` — change the tuple parsing to use `self._consistency` instead of `None`, update `mount()` signature, and remove `_effective_consistency`:


        for prefix, value in backends.items():
            if isinstance(value, tuple) and len(value) == 3:
                backend, mount_mode, mount_consistency = value
            elif isinstance(value, tuple):
                backend, mount_mode = value
                mount_consistency = None
            else:
                backend = value
                mount_mode = mode
                mount_consistency = None
            self._registry.mount(prefix, backend, mount_mode,
                                 mount_consistency)

        for prefix, value in backends.items():
            if isinstance(value, tuple) and len(value) == 3:
                backend, mount_mode, mount_consistency = value
            elif isinstance(value, tuple):
                backend, mount_mode = value
                mount_consistency = consistency
            else:
                backend = value
                mount_mode = mode
                mount_consistency = consistency
            self._registry.mount(prefix, backend, mount_mode,
                                 mount_consistency)


<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>

Error: File has not been read yet. Read it first before writing to it.


    67→
    68→        for prefix, value in backends.items():
    69→            if isinstance(value, tuple) and len(value) == 3:
    70→                backend, mount_mode, mount_consistency = value
    71→            elif isinstance(value, tuple):
    72→                backend, mount_mode = value
    73→                mount_consistency = None
    74→            else:
    75→                backend = value
    76→                mount_mode = mode
    77→                mount_consistency = None
    78→            self._registry.mount(prefix, backend, mount_mode,
    79→                                 mount_consistency)
    80→
    81→    # ── mount management ────────────────────────────────────────────────────
    82→
    83→    def mount(self,
    84→              prefix: str,
    85→              backend: BaseBackend,
    86→              mode: MountMode = MountMode.READ,

        for prefix, value in backends.items():
            if isinstance(value, tuple) and len(value) == 3:
                backend, mount_mode, mount_consistency = value
            elif isinstance(value, tuple):
                backend, mount_mode = value
                mount_consistency = None
            else:
                backend = value
                mount_mode = mode
                mount_consistency = None
            self._registry.mount(prefix, backend, mount_mode,
                                 mount_consistency)

    # ── mount management ────────────────────────────────────────────────────

    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,


        for prefix, value in backends.items():
            if isinstance(value, tuple) and len(value) == 3:
                backend, mount_mode, mount_consistency = value
            elif isinstance(value, tuple):
                backend, mount_mode = value
                mount_consistency = None
            else:
                backend = value
                mount_mode = mode
                mount_consistency = None
            self._registry.mount(prefix, backend, mount_mode,
                                 mount_consistency)

        for prefix, value in backends.items():
            if isinstance(value, tuple) and len(value) == 3:
                backend, mount_mode, mount_consistency = value
            elif isinstance(value, tuple):
                backend, mount_mode = value
                mount_consistency = consistency
            else:
                backend = value
                mount_mode = mode
                mount_consistency = consistency
            self._registry.mount(prefix, backend, mount_mode,
                                 mount_consistency)


The file /Users/agent/strukto/mirage/mirage/workspace/__init__.py has been updated successfully.

        for prefix, value in backends.items():
            if isinstance(value, tuple) and len(value) == 3:
                backend, mount_mode, mount_consistency = value
            elif isinstance(value, tuple):
                backend, mount_mode = value
                mount_consistency = consistency
            else:
                backend = value
                mount_mode = mode
                mount_consistency = consistency
            self._registry.mount(prefix, backend, mount_mode,
                                 mount_consistency)

from __future__ import annotations

import time

from mirage.backend.base import BaseBackend
from mirage.backend.filetype import guess_type
from mirage.types import (ConsistencyPolicy, FileStat, Inode, MountMode,
                          SyncPolicy, SyncResult, TransferStats)
from mirage.workspace.cache import LRUCache
from mirage.workspace.cache.policy import is_fresh
from mirage.workspace.mount import MountRegistry
from mirage.workspace.sync import DirtyTracker

class _PathLock:

    def __init__(self, ws: Workspace, paths: list[str]) -> None:
        self._ws = ws
        self._paths = paths

    def __enter__(self) -> _PathLock:
        return self

    def __exit__(self, *_: object) -> None:
        self._ws.unlock_paths(self._paths)

class Workspace:
    """
    Unified virtual filesystem over heterogeneous backends.

    Mounts backends at virtual path prefixes and provides a command-level API
    (cat, ls, write, cp, mv, rm, ...) with transparent caching
    and write staging.

    Example::

        ws = Workspace(
            {"/data/": S3Backend(S3Config(bucket="my-bucket")),
             "/tmp/":  MemoryBackend()},
            cache_limit="256MB",
            sync_policy=SyncPolicy.STAGED,
        )
        content = ws.cat("/data/reports/q1.json")
        ws.write("/tmp/summary.txt", b"hello")
        ws.sync()
    """

    def __init__(
        self,
        backends: dict[str, BaseBackend | tuple],
        cache_limit: str | int = "512MB",
        sync_policy: SyncPolicy = SyncPolicy.STAGED,
        mode: MountMode = MountMode.READ,
        consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED,
        ttl_seconds: int = 300,
    ) -> None:
        self._registry = MountRegistry()
        self._cache = LRUCache(cache_limit)
        self._tracker = DirtyTracker()
        self._sync_policy = sync_policy
        self._consistency = consistency
        self._ttl_seconds = ttl_seconds
        self._locked_paths: set[str] = set()
        self._transfer_stats: dict[str, TransferStats] = {}
        self._created_at: float = time.monotonic()

        for prefix, value in backends.items():
            if isinstance(value, tuple) and len(value) == 3:
                backend, mount_mode, mount_consistency = value
            elif isinstance(value, tuple):
                backend, mount_mode = value
                mount_consistency = None
            else:
                backend = value
                mount_mode = mode
                mount_consistency = None
            self._registry.mount(prefix, backend, mount_mode,
                                 mount_consistency)

    # ── mount management ────────────────────────────────────────────────────

    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy | None = None) -> None:
        self._registry.mount(prefix, backend, mode, consistency)

    def backend(self, path: str) -> BaseBackend:
        backend, _, _ = self._registry.resolve(path)
        return backend

    # ── read ops ────────────────────────────────────────────────────────────

    def cat(self, path: str) -> bytes:
        inode = self._tracker.get(path)
        if inode and inode.dirty:
            entry = self._cache.get(path)
            if entry is not None:
                self._record_transfer(path,
                                      "read",
                                      len(entry.data),
                                      0,
                                      cache_hit=True)
                return entry.data

        entry = self._cache.get(path)
        remote_stat = None
        if entry is not None:
            consistency = self._effective_consistency(path)
            backend, bp, _ = self._registry.resolve(path)
            fresh, remote_stat = is_fresh(entry, consistency,
                                          self._ttl_seconds, backend, bp)
            if fresh:
                self._record_transfer(path,
                                      "read",
                                      len(entry.data),
                                      0,
                                      cache_hit=True)
                return entry.data
        else:
            backend, bp, _ = self._registry.resolve(path)

        t0 = time.monotonic()
        data = backend.cat(bp)
        duration_ms = int((time.monotonic() - t0) * 1000)
        self._record_transfer(path,
                              "read",
                              len(data),
                              duration_ms,
                              cache_hit=False)
        if remote_stat is None:
            remote_stat = backend.stat(bp)
        self._cache.put(path,
                        data,
                        etag=remote_stat.extra.get("etag"),
                        modified=remote_stat.modified,
                        size=remote_stat.size)
        self._tracker.on_read(path, data, self._mount_prefix(path))
        return data

    def ls(self, path: str) -> list[str]:
        backend, bp, _ = self._registry.resolve(path)
        prefix = self._mount_prefix(path).rstrip("/")
        backend_ok = False
        try:
            backend_entries = backend.ls(bp)
            backend_ok = True
        except FileNotFoundError:
            backend_entries = []
        result: set[str] = {
            prefix + "/" + e.lstrip("/")
            for e in backend_entries
        }
        norm_path = "/" + path.strip("/")
        dir_prefix = norm_path.rstrip("/") + "/"
        has_staged_children = False
        for inode in self._tracker.dirty():
            vp = inode.virtual_path
            if not vp.startswith(dir_prefix):
                continue
            if inode.checksum is None:
                result.discard(vp)
                continue
            has_staged_children = True
            remainder = vp[len(dir_prefix):]
            first = remainder.split("/")[0]
            if not first:
                continue
            if "/" in remainder:
                result.add(dir_prefix + first + "/")
            else:
                result.add(dir_prefix + first)
        if not backend_ok and not has_staged_children:
            raise FileNotFoundError(path)
        return sorted(result)

    def ls_l(self, path: str) -> list[FileStat]:
        backend, bp, _ = self._registry.resolve(path)
        return backend.ls_l(bp)

    def stat(self, path: str) -> FileStat:
        # For staged writes, build FileStat from the tracker rather than
        # hitting the backend (which hasn't received the write yet).
        inode = self._tracker.get(path)
        if inode and inode.dirty and inode.checksum is not None:
            entry = self._cache.get(path)
            size = len(entry.data) if entry is not None else inode.size
            return FileStat(
                name=path.rstrip("/").rsplit("/", 1)[-1],
                size=size,
                type=guess_type(path),
            )
        backend, bp, _ = self._registry.resolve(path)
        return backend.stat(bp)

    def exists(self, path: str) -> bool:
        try:
            self.stat(path)
            return True
        except (FileNotFoundError, ValueError):
            return False

    def head(self, path: str, lines: int = 10) -> bytes:
        backend, bp, _ = self._registry.resolve(path)
        return backend.head(bp, lines)

    def tail(self, path: str, lines: int = 10) -> bytes:
        backend, bp, _ = self._registry.resolve(path)
        return backend.tail(bp, lines)

    def read(self, path: str, offset: int, size: int) -> bytes:
        inode = self._tracker.get(path)
        if inode and inode.dirty:
            entry = self._cache.get(path)
            if entry is not None:
                return entry.data[offset:offset + size]
        backend, bp, _ = self._registry.resolve(path)
        return backend.read(bp, offset, size)

    def grep(self, pattern: str, path: str) -> list[str]:
        backend, bp, _ = self._registry.resolve(path)
        return backend.grep(pattern, bp)

    # ── write ops ───────────────────────────────────────────────────────────

    def write(self, path: str, data: bytes) -> None:
        self._assert_not_locked(path)
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        self._cache.put(path, data)
        self._tracker.on_write(path, data, self._mount_prefix(path))
        self._record_transfer(path, "write", len(data), 0)
        if self._sync_policy == SyncPolicy.IMMEDIATE:
            backend.put(bp, data)
            self._tracker.on_read(path, data, self._mount_prefix(path))

    def mkdir(self, path: str) -> None:
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        backend.mkdir(bp)

    def rm(self, path: str) -> None:
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        inode = self._tracker.get(path)
        staged_only = inode and inode.dirty and inode.remote_checksum is None
        if not staged_only:
            backend.unlink(bp)
        self._cache.invalidate(path)
        self._tracker.on_delete(path)

    def mv(self, src: str, dst: str) -> None:
        self._assert_writable(src)
        sb, sbp, _ = self._registry.resolve(src)
        db, dbp, _ = self._registry.resolve(dst)
        src_inode = self._tracker.get(src)
        staged_only = (src_inode and src_inode.dirty
                       and src_inode.remote_checksum is None)
        if staged_only:
            entry = self._cache.get(src)
            data = entry.data if entry else b""
            self._cache.put(dst, data)
            self._tracker.on_write(dst, data, self._mount_prefix(dst))
            self._cache.invalidate(src)
            self._tracker.on_delete(src)
        elif sb is db:
            sb.rename(sbp, dbp)
            self._cache.invalidate(src)
        else:
            data = sb.cat(sbp)
            db.put(dbp, data)
            sb.unlink(sbp)
            self._cache.invalidate(src)

    def cp(self, src: str, dst: str) -> None:
        self._assert_writable(dst)
        data = self.cat(src)
        db, dbp, _ = self._registry.resolve(dst)
        db.put(dbp, data)

    # ── sync lifecycle ──────────────────────────────────────────────────────

    def sync(self, path: str | None = None) -> list[SyncResult]:
        results: list[SyncResult] = []
        targets = [
            i for i in self._tracker.dirty()
            if path is None or i.virtual_path == path
        ]
        for inode in targets:
            backend, bp, _ = self._registry.resolve(inode.virtual_path)
            entry = self._cache.get(inode.virtual_path)
            data = entry.data if entry else None
            result = self._tracker.on_sync(inode.virtual_path, bp, data,
                                           backend)
            results.append(result)
        return results

    def status(self) -> list[Inode]:
        return self._tracker.status()

    def reset(self) -> None:
        self._tracker.reset()
        self._cache.clear()

    # ── path locking ─────────────────────────────────────────────────────────

    def _lock_path(self, path: str) -> None:
        self._locked_paths.add(path)

    def unlock_paths(self, paths: list[str]) -> None:
        for p in paths:
            self._locked_paths.discard(p)

    def lock_paths(self, paths: list[str]) -> _PathLock:
        for p in paths:
            self._lock_path(p)
        return _PathLock(self, paths)

    def _assert_not_locked(self, path: str) -> None:
        if path in self._locked_paths:
            raise PermissionError(
                f"path {path!r} is locked by remote execution")

    # ── helpers ─────────────────────────────────────────────────────────────

    def _mount_prefix(self, path: str) -> str:
        norm = "/" + path.strip("/")
        for m in self._registry.mounts():
            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
                return m.prefix
        return "/"

    def _effective_consistency(self, path: str) -> ConsistencyPolicy:
        mount = self._registry.mount_for(path)
        if mount.consistency is not None:
            return mount.consistency
        return self._consistency

    def _assert_writable(self, path: str) -> None:
        _, _, mode = self._registry.resolve(path)
        if mode == MountMode.READ:
            raise PermissionError(f"mount at {path!r} is read-only")

    def _record_transfer(self,
                         path: str,
                         op: str,
                         nbytes: int,
                         duration_ms: int,
                         cache_hit: bool | None = None) -> None:
        prefix = self._mount_prefix(path)
        if prefix not in self._transfer_stats:
            self._transfer_stats[prefix] = TransferStats()
        stats = self._transfer_stats[prefix]
        if op == "read":
            stats.bytes_read += nbytes
            stats.read_ops += 1
            stats.total_read_ms += duration_ms
            if cache_hit is True:
                stats.cache_hits += 1
            elif cache_hit is False:
                stats.cache_misses += 1
        elif op == "write":
            stats.bytes_written += nbytes
            stats.write_ops += 1
            stats.total_write_ms += duration_ms

-                mount_consistency = None

+                mount_consistency = consistency


Now update `Workspace.mount()` signature:


    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy | None = None) -> None:

    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED) -> None:


from __future__ import annotations

import time

from mirage.backend.base import BaseBackend
from mirage.backend.filetype import guess_type
from mirage.types import (ConsistencyPolicy, FileStat, Inode, MountMode,
                          SyncPolicy, SyncResult, TransferStats)
from mirage.workspace.cache import LRUCache
from mirage.workspace.cache.policy import is_fresh
from mirage.workspace.mount import MountRegistry
from mirage.workspace.sync import DirtyTracker

class _PathLock:

    def __init__(self, ws: Workspace, paths: list[str]) -> None:
        self._ws = ws
        self._paths = paths

    def __enter__(self) -> _PathLock:
        return self

    def __exit__(self, *_: object) -> None:
        self._ws.unlock_paths(self._paths)

class Workspace:
    """
    Unified virtual filesystem over heterogeneous backends.

    Mounts backends at virtual path prefixes and provides a command-level API
    (cat, ls, write, cp, mv, rm, ...) with transparent caching
    and write staging.

    Example::

        ws = Workspace(
            {"/data/": S3Backend(S3Config(bucket="my-bucket")),
             "/tmp/":  MemoryBackend()},
            cache_limit="256MB",
            sync_policy=SyncPolicy.STAGED,
        )
        content = ws.cat("/data/reports/q1.json")
        ws.write("/tmp/summary.txt", b"hello")
        ws.sync()
    """

    def __init__(
        self,
        backends: dict[str, BaseBackend | tuple],
        cache_limit: str | int = "512MB",
        sync_policy: SyncPolicy = SyncPolicy.STAGED,
        mode: MountMode = MountMode.READ,
        consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED,
        ttl_seconds: int = 300,
    ) -> None:
        self._registry = MountRegistry()
        self._cache = LRUCache(cache_limit)
        self._tracker = DirtyTracker()
        self._sync_policy = sync_policy
        self._consistency = consistency
        self._ttl_seconds = ttl_seconds
        self._locked_paths: set[str] = set()
        self._transfer_stats: dict[str, TransferStats] = {}
        self._created_at: float = time.monotonic()

        for prefix, value in backends.items():
            if isinstance(value, tuple) and len(value) == 3:
                backend, mount_mode, mount_consistency = value
            elif isinstance(value, tuple):
                backend, mount_mode = value
                mount_consistency = consistency
            else:
                backend = value
                mount_mode = mode
                mount_consistency = consistency
            self._registry.mount(prefix, backend, mount_mode,
                                 mount_consistency)

    # ── mount management ────────────────────────────────────────────────────

    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy | None = None) -> None:
        self._registry.mount(prefix, backend, mode, consistency)

    def backend(self, path: str) -> BaseBackend:
        backend, _, _ = self._registry.resolve(path)
        return backend

    # ── read ops ────────────────────────────────────────────────────────────

    def cat(self, path: str) -> bytes:
        inode = self._tracker.get(path)
        if inode and inode.dirty:
            entry = self._cache.get(path)
            if entry is not None:
                self._record_transfer(path,
                                      "read",
                                      len(entry.data),
                                      0,
                                      cache_hit=True)
                return entry.data

        entry = self._cache.get(path)
        remote_stat = None
        if entry is not None:
            consistency = self._effective_consistency(path)
            backend, bp, _ = self._registry.resolve(path)
            fresh, remote_stat = is_fresh(entry, consistency,
                                          self._ttl_seconds, backend, bp)
            if fresh:
                self._record_transfer(path,
                                      "read",
                                      len(entry.data),
                                      0,
                                      cache_hit=True)
                return entry.data
        else:
            backend, bp, _ = self._registry.resolve(path)

        t0 = time.monotonic()
        data = backend.cat(bp)
        duration_ms = int((time.monotonic() - t0) * 1000)
        self._record_transfer(path,
                              "read",
                              len(data),
                              duration_ms,
                              cache_hit=False)
        if remote_stat is None:
            remote_stat = backend.stat(bp)
        self._cache.put(path,
                        data,
                        etag=remote_stat.extra.get("etag"),
                        modified=remote_stat.modified,
                        size=remote_stat.size)
        self._tracker.on_read(path, data, self._mount_prefix(path))
        return data

    def ls(self, path: str) -> list[str]:
        backend, bp, _ = self._registry.resolve(path)
        prefix = self._mount_prefix(path).rstrip("/")
        backend_ok = False
        try:
            backend_entries = backend.ls(bp)
            backend_ok = True
        except FileNotFoundError:
            backend_entries = []
        result: set[str] = {
            prefix + "/" + e.lstrip("/")
            for e in backend_entries
        }
        norm_path = "/" + path.strip("/")
        dir_prefix = norm_path.rstrip("/") + "/"
        has_staged_children = False
        for inode in self._tracker.dirty():
            vp = inode.virtual_path
            if not vp.startswith(dir_prefix):
                continue
            if inode.checksum is None:
                result.discard(vp)
                continue
            has_staged_children = True
            remainder = vp[len(dir_prefix):]
            first = remainder.split("/")[0]
            if not first:
                continue
            if "/" in remainder:
                result.add(dir_prefix + first + "/")
            else:
                result.add(dir_prefix + first)
        if not backend_ok and not has_staged_children:
            raise FileNotFoundError(path)
        return sorted(result)

    def ls_l(self, path: str) -> list[FileStat]:
        backend, bp, _ = self._registry.resolve(path)
        return backend.ls_l(bp)

    def stat(self, path: str) -> FileStat:
        # For staged writes, build FileStat from the tracker rather than
        # hitting the backend (which hasn't received the write yet).
        inode = self._tracker.get(path)
        if inode and inode.dirty and inode.checksum is not None:
            entry = self._cache.get(path)
            size = len(entry.data) if entry is not None else inode.size
            return FileStat(
                name=path.rstrip("/").rsplit("/", 1)[-1],
                size=size,
                type=guess_type(path),
            )
        backend, bp, _ = self._registry.resolve(path)
        return backend.stat(bp)

    def exists(self, path: str) -> bool:
        try:
            self.stat(path)
            return True
        except (FileNotFoundError, ValueError):
            return False

    def head(self, path: str, lines: int = 10) -> bytes:
        backend, bp, _ = self._registry.resolve(path)
        return backend.head(bp, lines)

    def tail(self, path: str, lines: int = 10) -> bytes:
        backend, bp, _ = self._registry.resolve(path)
        return backend.tail(bp, lines)

    def read(self, path: str, offset: int, size: int) -> bytes:
        inode = self._tracker.get(path)
        if inode and inode.dirty:
            entry = self._cache.get(path)
            if entry is not None:
                return entry.data[offset:offset + size]
        backend, bp, _ = self._registry.resolve(path)
        return backend.read(bp, offset, size)

    def grep(self, pattern: str, path: str) -> list[str]:
        backend, bp, _ = self._registry.resolve(path)
        return backend.grep(pattern, bp)

    # ── write ops ───────────────────────────────────────────────────────────

    def write(self, path: str, data: bytes) -> None:
        self._assert_not_locked(path)
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        self._cache.put(path, data)
        self._tracker.on_write(path, data, self._mount_prefix(path))
        self._record_transfer(path, "write", len(data), 0)
        if self._sync_policy == SyncPolicy.IMMEDIATE:
            backend.put(bp, data)
            self._tracker.on_read(path, data, self._mount_prefix(path))

    def mkdir(self, path: str) -> None:
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        backend.mkdir(bp)

    def rm(self, path: str) -> None:
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        inode = self._tracker.get(path)
        staged_only = inode and inode.dirty and inode.remote_checksum is None
        if not staged_only:
            backend.unlink(bp)
        self._cache.invalidate(path)
        self._tracker.on_delete(path)

    def mv(self, src: str, dst: str) -> None:
        self._assert_writable(src)
        sb, sbp, _ = self._registry.resolve(src)
        db, dbp, _ = self._registry.resolve(dst)
        src_inode = self._tracker.get(src)
        staged_only = (src_inode and src_inode.dirty
                       and src_inode.remote_checksum is None)
        if staged_only:
            entry = self._cache.get(src)
            data = entry.data if entry else b""
            self._cache.put(dst, data)
            self._tracker.on_write(dst, data, self._mount_prefix(dst))
            self._cache.invalidate(src)
            self._tracker.on_delete(src)
        elif sb is db:
            sb.rename(sbp, dbp)
            self._cache.invalidate(src)
        else:
            data = sb.cat(sbp)
            db.put(dbp, data)
            sb.unlink(sbp)
            self._cache.invalidate(src)

    def cp(self, src: str, dst: str) -> None:
        self._assert_writable(dst)
        data = self.cat(src)
        db, dbp, _ = self._registry.resolve(dst)
        db.put(dbp, data)

    # ── sync lifecycle ──────────────────────────────────────────────────────

    def sync(self, path: str | None = None) -> list[SyncResult]:
        results: list[SyncResult] = []
        targets = [
            i for i in self._tracker.dirty()
            if path is None or i.virtual_path == path
        ]
        for inode in targets:
            backend, bp, _ = self._registry.resolve(inode.virtual_path)
            entry = self._cache.get(inode.virtual_path)
            data = entry.data if entry else None
            result = self._tracker.on_sync(inode.virtual_path, bp, data,
                                           backend)
            results.append(result)
        return results

    def status(self) -> list[Inode]:
        return self._tracker.status()

    def reset(self) -> None:
        self._tracker.reset()
        self._cache.clear()

    # ── path locking ─────────────────────────────────────────────────────────

    def _lock_path(self, path: str) -> None:
        self._locked_paths.add(path)

    def unlock_paths(self, paths: list[str]) -> None:
        for p in paths:
            self._locked_paths.discard(p)

    def lock_paths(self, paths: list[str]) -> _PathLock:
        for p in paths:
            self._lock_path(p)
        return _PathLock(self, paths)

    def _assert_not_locked(self, path: str) -> None:
        if path in self._locked_paths:
            raise PermissionError(
                f"path {path!r} is locked by remote execution")

    # ── helpers ─────────────────────────────────────────────────────────────

    def _mount_prefix(self, path: str) -> str:
        norm = "/" + path.strip("/")
        for m in self._registry.mounts():
            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
                return m.prefix
        return "/"

    def _effective_consistency(self, path: str) -> ConsistencyPolicy:
        mount = self._registry.mount_for(path)
        if mount.consistency is not None:
            return mount.consistency
        return self._consistency

    def _assert_writable(self, path: str) -> None:
        _, _, mode = self._registry.resolve(path)
        if mode == MountMode.READ:
            raise PermissionError(f"mount at {path!r} is read-only")

    def _record_transfer(self,
                         path: str,
                         op: str,
                         nbytes: int,
                         duration_ms: int,
                         cache_hit: bool | None = None) -> None:
        prefix = self._mount_prefix(path)
        if prefix not in self._transfer_stats:
            self._transfer_stats[prefix] = TransferStats()
        stats = self._transfer_stats[prefix]
        if op == "read":
            stats.bytes_read += nbytes
            stats.read_ops += 1
            stats.total_read_ms += duration_ms
            if cache_hit is True:
                stats.cache_hits += 1
            elif cache_hit is False:
                stats.cache_misses += 1
        elif op == "write":
            stats.bytes_written += nbytes
            stats.write_ops += 1
            stats.total_write_ms += duration_ms


Now replace `self._effective_consistency(path)` with direct mount access and remove the method:


from __future__ import annotations

import time

from mirage.backend.base import BaseBackend
from mirage.backend.filetype import guess_type
from mirage.types import (ConsistencyPolicy, FileStat, Inode, MountMode,
                          SyncPolicy, SyncResult, TransferStats)
from mirage.workspace.cache import LRUCache
from mirage.workspace.cache.policy import is_fresh
from mirage.workspace.mount import MountRegistry
from mirage.workspace.sync import DirtyTracker

class _PathLock:

    def __init__(self, ws: Workspace, paths: list[str]) -> None:
        self._ws = ws
        self._paths = paths

    def __enter__(self) -> _PathLock:
        return self

    def __exit__(self, *_: object) -> None:
        self._ws.unlock_paths(self._paths)

class Workspace:
    """
    Unified virtual filesystem over heterogeneous backends.

    Mounts backends at virtual path prefixes and provides a command-level API
    (cat, ls, write, cp, mv, rm, ...) with transparent caching
    and write staging.

    Example::

        ws = Workspace(
            {"/data/": S3Backend(S3Config(bucket="my-bucket")),
             "/tmp/":  MemoryBackend()},
            cache_limit="256MB",
            sync_policy=SyncPolicy.STAGED,
        )
        content = ws.cat("/data/reports/q1.json")
        ws.write("/tmp/summary.txt", b"hello")
        ws.sync()
    """

    def __init__(
        self,
        backends: dict[str, BaseBackend | tuple],
        cache_limit: str | int = "512MB",
        sync_policy: SyncPolicy = SyncPolicy.STAGED,
        mode: MountMode = MountMode.READ,
        consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED,
        ttl_seconds: int = 300,
    ) -> None:
        self._registry = MountRegistry()
        self._cache = LRUCache(cache_limit)
        self._tracker = DirtyTracker()
        self._sync_policy = sync_policy
        self._consistency = consistency
        self._ttl_seconds = ttl_seconds
        self._locked_paths: set[str] = set()
        self._transfer_stats: dict[str, TransferStats] = {}
        self._created_at: float = time.monotonic()

        for prefix, value in backends.items():
            if isinstance(value, tuple) and len(value) == 3:
                backend, mount_mode, mount_consistency = value
            elif isinstance(value, tuple):
                backend, mount_mode = value
                mount_consistency = consistency
            else:
                backend = value
                mount_mode = mode
                mount_consistency = consistency
            self._registry.mount(prefix, backend, mount_mode,
                                 mount_consistency)

    # ── mount management ────────────────────────────────────────────────────

    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED) -> None:
        self._registry.mount(prefix, backend, mode, consistency)

    def backend(self, path: str) -> BaseBackend:
        backend, _, _ = self._registry.resolve(path)
        return backend

    # ── read ops ────────────────────────────────────────────────────────────

    def cat(self, path: str) -> bytes:
        inode = self._tracker.get(path)
        if inode and inode.dirty:
            entry = self._cache.get(path)
            if entry is not None:
                self._record_transfer(path,
                                      "read",
                                      len(entry.data),
                                      0,
                                      cache_hit=True)
                return entry.data

        entry = self._cache.get(path)
        remote_stat = None
        if entry is not None:
            consistency = self._effective_consistency(path)
            backend, bp, _ = self._registry.resolve(path)
            fresh, remote_stat = is_fresh(entry, consistency,
                                          self._ttl_seconds, backend, bp)
            if fresh:
                self._record_transfer(path,
                                      "read",
                                      len(entry.data),
                                      0,
                                      cache_hit=True)
                return entry.data
        else:
            backend, bp, _ = self._registry.resolve(path)

        t0 = time.monotonic()
        data = backend.cat(bp)
        duration_ms = int((time.monotonic() - t0) * 1000)
        self._record_transfer(path,
                              "read",
                              len(data),
                              duration_ms,
                              cache_hit=False)
        if remote_stat is None:
            remote_stat = backend.stat(bp)
        self._cache.put(path,
                        data,
                        etag=remote_stat.extra.get("etag"),
                        modified=remote_stat.modified,
                        size=remote_stat.size)
        self._tracker.on_read(path, data, self._mount_prefix(path))
        return data

    def ls(self, path: str) -> list[str]:
        backend, bp, _ = self._registry.resolve(path)
        prefix = self._mount_prefix(path).rstrip("/")
        backend_ok = False
        try:
            backend_entries = backend.ls(bp)
            backend_ok = True
        except FileNotFoundError:
            backend_entries = []
        result: set[str] = {
            prefix + "/" + e.lstrip("/")
            for e in backend_entries
        }
        norm_path = "/" + path.strip("/")
        dir_prefix = norm_path.rstrip("/") + "/"
        has_staged_children = False
        for inode in self._tracker.dirty():
            vp = inode.virtual_path
            if not vp.startswith(dir_prefix):
                continue
            if inode.checksum is None:
                result.discard(vp)
                continue
            has_staged_children = True
            remainder = vp[len(dir_prefix):]
            first = remainder.split("/")[0]
            if not first:
                continue
            if "/" in remainder:
                result.add(dir_prefix + first + "/")
            else:
                result.add(dir_prefix + first)
        if not backend_ok and not has_staged_children:
            raise FileNotFoundError(path)
        return sorted(result)

    def ls_l(self, path: str) -> list[FileStat]:
        backend, bp, _ = self._registry.resolve(path)
        return backend.ls_l(bp)

    def stat(self, path: str) -> FileStat:
        # For staged writes, build FileStat from the tracker rather than
        # hitting the backend (which hasn't received the write yet).
        inode = self._tracker.get(path)
        if inode and inode.dirty and inode.checksum is not None:
            entry = self._cache.get(path)
            size = len(entry.data) if entry is not None else inode.size
            return FileStat(
                name=path.rstrip("/").rsplit("/", 1)[-1],
                size=size,
                type=guess_type(path),
            )
        backend, bp, _ = self._registry.resolve(path)
        return backend.stat(bp)

    def exists(self, path: str) -> bool:
        try:
            self.stat(path)
            return True
        except (FileNotFoundError, ValueError):
            return False

    def head(self, path: str, lines: int = 10) -> bytes:
        backend, bp, _ = self._registry.resolve(path)
        return backend.head(bp, lines)

    def tail(self, path: str, lines: int = 10) -> bytes:
        backend, bp, _ = self._registry.resolve(path)
        return backend.tail(bp, lines)

    def read(self, path: str, offset: int, size: int) -> bytes:
        inode = self._tracker.get(path)
        if inode and inode.dirty:
            entry = self._cache.get(path)
            if entry is not None:
                return entry.data[offset:offset + size]
        backend, bp, _ = self._registry.resolve(path)
        return backend.read(bp, offset, size)

    def grep(self, pattern: str, path: str) -> list[str]:
        backend, bp, _ = self._registry.resolve(path)
        return backend.grep(pattern, bp)

    # ── write ops ───────────────────────────────────────────────────────────

    def write(self, path: str, data: bytes) -> None:
        self._assert_not_locked(path)
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        self._cache.put(path, data)
        self._tracker.on_write(path, data, self._mount_prefix(path))
        self._record_transfer(path, "write", len(data), 0)
        if self._sync_policy == SyncPolicy.IMMEDIATE:
            backend.put(bp, data)
            self._tracker.on_read(path, data, self._mount_prefix(path))

    def mkdir(self, path: str) -> None:
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        backend.mkdir(bp)

    def rm(self, path: str) -> None:
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        inode = self._tracker.get(path)
        staged_only = inode and inode.dirty and inode.remote_checksum is None
        if not staged_only:
            backend.unlink(bp)
        self._cache.invalidate(path)
        self._tracker.on_delete(path)

    def mv(self, src: str, dst: str) -> None:
        self._assert_writable(src)
        sb, sbp, _ = self._registry.resolve(src)
        db, dbp, _ = self._registry.resolve(dst)
        src_inode = self._tracker.get(src)
        staged_only = (src_inode and src_inode.dirty
                       and src_inode.remote_checksum is None)
        if staged_only:
            entry = self._cache.get(src)
            data = entry.data if entry else b""
            self._cache.put(dst, data)
            self._tracker.on_write(dst, data, self._mount_prefix(dst))
            self._cache.invalidate(src)
            self._tracker.on_delete(src)
        elif sb is db:
            sb.rename(sbp, dbp)
            self._cache.invalidate(src)
        else:
            data = sb.cat(sbp)
            db.put(dbp, data)
            sb.unlink(sbp)
            self._cache.invalidate(src)

    def cp(self, src: str, dst: str) -> None:
        self._assert_writable(dst)
        data = self.cat(src)
        db, dbp, _ = self._registry.resolve(dst)
        db.put(dbp, data)

    # ── sync lifecycle ──────────────────────────────────────────────────────

    def sync(self, path: str | None = None) -> list[SyncResult]:
        results: list[SyncResult] = []
        targets = [
            i for i in self._tracker.dirty()
            if path is None or i.virtual_path == path
        ]
        for inode in targets:
            backend, bp, _ = self._registry.resolve(inode.virtual_path)
            entry = self._cache.get(inode.virtual_path)
            data = entry.data if entry else None
            result = self._tracker.on_sync(inode.virtual_path, bp, data,
                                           backend)
            results.append(result)
        return results

    def status(self) -> list[Inode]:
        return self._tracker.status()

    def reset(self) -> None:
        self._tracker.reset()
        self._cache.clear()

    # ── path locking ─────────────────────────────────────────────────────────

    def _lock_path(self, path: str) -> None:
        self._locked_paths.add(path)

    def unlock_paths(self, paths: list[str]) -> None:
        for p in paths:
            self._locked_paths.discard(p)

    def lock_paths(self, paths: list[str]) -> _PathLock:
        for p in paths:
            self._lock_path(p)
        return _PathLock(self, paths)

    def _assert_not_locked(self, path: str) -> None:
        if path in self._locked_paths:
            raise PermissionError(
                f"path {path!r} is locked by remote execution")

    # ── helpers ─────────────────────────────────────────────────────────────

    def _mount_prefix(self, path: str) -> str:
        norm = "/" + path.strip("/")
        for m in self._registry.mounts():
            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
                return m.prefix
        return "/"

    def _effective_consistency(self, path: str) -> ConsistencyPolicy:
        mount = self._registry.mount_for(path)
        if mount.consistency is not None:
            return mount.consistency
        return self._consistency

    def _assert_writable(self, path: str) -> None:
        _, _, mode = self._registry.resolve(path)
        if mode == MountMode.READ:
            raise PermissionError(f"mount at {path!r} is read-only")

    def _record_transfer(self,
                         path: str,
                         op: str,
                         nbytes: int,
                         duration_ms: int,
                         cache_hit: bool | None = None) -> None:
        prefix = self._mount_prefix(path)
        if prefix not in self._transfer_stats:
            self._transfer_stats[prefix] = TransferStats()
        stats = self._transfer_stats[prefix]
        if op == "read":
            stats.bytes_read += nbytes
            stats.read_ops += 1
            stats.total_read_ms += duration_ms
            if cache_hit is True:
                stats.cache_hits += 1
            elif cache_hit is False:
                stats.cache_misses += 1
        elif op == "write":
            stats.bytes_written += nbytes
            stats.write_ops += 1
            stats.total_write_ms += duration_ms

-            consistency = self._effective_consistency(path)

+            consistency = self._registry.mount_for(path).consistency


    def _effective_consistency(self, path: str) -> ConsistencyPolicy:
        mount = self._registry.mount_for(path)
        if mount.consistency is not None:
            return mount.consistency
        return self._consistency

    def _assert_writable


from __future__ import annotations

import time

from mirage.backend.base import BaseBackend
from mirage.backend.filetype import guess_type
from mirage.types import (ConsistencyPolicy, FileStat, Inode, MountMode,
                          SyncPolicy, SyncResult, TransferStats)
from mirage.workspace.cache import LRUCache
from mirage.workspace.cache.policy import is_fresh
from mirage.workspace.mount import MountRegistry
from mirage.workspace.sync import DirtyTracker

class _PathLock:

    def __init__(self, ws: Workspace, paths: list[str]) -> None:
        self._ws = ws
        self._paths = paths

    def __enter__(self) -> _PathLock:
        return self

    def __exit__(self, *_: object) -> None:
        self._ws.unlock_paths(self._paths)

class Workspace:
    """
    Unified virtual filesystem over heterogeneous backends.

    Mounts backends at virtual path prefixes and provides a command-level API
    (cat, ls, write, cp, mv, rm, ...) with transparent caching
    and write staging.

    Example::

        ws = Workspace(
            {"/data/": S3Backend(S3Config(bucket="my-bucket")),
             "/tmp/":  MemoryBackend()},
            cache_limit="256MB",
            sync_policy=SyncPolicy.STAGED,
        )
        content = ws.cat("/data/reports/q1.json")
        ws.write("/tmp/summary.txt", b"hello")
        ws.sync()
    """

    def __init__(
        self,
        backends: dict[str, BaseBackend | tuple],
        cache_limit: str | int = "512MB",
        sync_policy: SyncPolicy = SyncPolicy.STAGED,
        mode: MountMode = MountMode.READ,
        consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED,
        ttl_seconds: int = 300,
    ) -> None:
        self._registry = MountRegistry()
        self._cache = LRUCache(cache_limit)
        self._tracker = DirtyTracker()
        self._sync_policy = sync_policy
        self._consistency = consistency
        self._ttl_seconds = ttl_seconds
        self._locked_paths: set[str] = set()
        self._transfer_stats: dict[str, TransferStats] = {}
        self._created_at: float = time.monotonic()

        for prefix, value in backends.items():
            if isinstance(value, tuple) and len(value) == 3:
                backend, mount_mode, mount_consistency = value
            elif isinstance(value, tuple):
                backend, mount_mode = value
                mount_consistency = consistency
            else:
                backend = value
                mount_mode = mode
                mount_consistency = consistency
            self._registry.mount(prefix, backend, mount_mode,
                                 mount_consistency)

    # ── mount management ────────────────────────────────────────────────────

    def mount(self,
              prefix: str,
              backend: BaseBackend,
              mode: MountMode = MountMode.READ,
              consistency: ConsistencyPolicy = ConsistencyPolicy.CACHED) -> None:
        self._registry.mount(prefix, backend, mode, consistency)

    def backend(self, path: str) -> BaseBackend:
        backend, _, _ = self._registry.resolve(path)
        return backend

    # ── read ops ────────────────────────────────────────────────────────────

    def cat(self, path: str) -> bytes:
        inode = self._tracker.get(path)
        if inode and inode.dirty:
            entry = self._cache.get(path)
            if entry is not None:
                self._record_transfer(path,
                                      "read",
                                      len(entry.data),
                                      0,
                                      cache_hit=True)
                return entry.data

        entry = self._cache.get(path)
        remote_stat = None
        if entry is not None:
            consistency = self._registry.mount_for(path).consistency
            backend, bp, _ = self._registry.resolve(path)
            fresh, remote_stat = is_fresh(entry, consistency,
                                          self._ttl_seconds, backend, bp)
            if fresh:
                self._record_transfer(path,
                                      "read",
                                      len(entry.data),
                                      0,
                                      cache_hit=True)
                return entry.data
        else:
            backend, bp, _ = self._registry.resolve(path)

        t0 = time.monotonic()
        data = backend.cat(bp)
        duration_ms = int((time.monotonic() - t0) * 1000)
        self._record_transfer(path,
                              "read",
                              len(data),
                              duration_ms,
                              cache_hit=False)
        if remote_stat is None:
            remote_stat = backend.stat(bp)
        self._cache.put(path,
                        data,
                        etag=remote_stat.extra.get("etag"),
                        modified=remote_stat.modified,
                        size=remote_stat.size)
        self._tracker.on_read(path, data, self._mount_prefix(path))
        return data

    def ls(self, path: str) -> list[str]:
        backend, bp, _ = self._registry.resolve(path)
        prefix = self._mount_prefix(path).rstrip("/")
        backend_ok = False
        try:
            backend_entries = backend.ls(bp)
            backend_ok = True
        except FileNotFoundError:
            backend_entries = []
        result: set[str] = {
            prefix + "/" + e.lstrip("/")
            for e in backend_entries
        }
        norm_path = "/" + path.strip("/")
        dir_prefix = norm_path.rstrip("/") + "/"
        has_staged_children = False
        for inode in self._tracker.dirty():
            vp = inode.virtual_path
            if not vp.startswith(dir_prefix):
                continue
            if inode.checksum is None:
                result.discard(vp)
                continue
            has_staged_children = True
            remainder = vp[len(dir_prefix):]
            first = remainder.split("/")[0]
            if not first:
                continue
            if "/" in remainder:
                result.add(dir_prefix + first + "/")
            else:
                result.add(dir_prefix + first)
        if not backend_ok and not has_staged_children:
            raise FileNotFoundError(path)
        return sorted(result)

    def ls_l(self, path: str) -> list[FileStat]:
        backend, bp, _ = self._registry.resolve(path)
        return backend.ls_l(bp)

    def stat(self, path: str) -> FileStat:
        # For staged writes, build FileStat from the tracker rather than
        # hitting the backend (which hasn't received the write yet).
        inode = self._tracker.get(path)
        if inode and inode.dirty and inode.checksum is not None:
            entry = self._cache.get(path)
            size = len(entry.data) if entry is not None else inode.size
            return FileStat(
                name=path.rstrip("/").rsplit("/", 1)[-1],
                size=size,
                type=guess_type(path),
            )
        backend, bp, _ = self._registry.resolve(path)
        return backend.stat(bp)

    def exists(self, path: str) -> bool:
        try:
            self.stat(path)
            return True
        except (FileNotFoundError, ValueError):
            return False

    def head(self, path: str, lines: int = 10) -> bytes:
        backend, bp, _ = self._registry.resolve(path)
        return backend.head(bp, lines)

    def tail(self, path: str, lines: int = 10) -> bytes:
        backend, bp, _ = self._registry.resolve(path)
        return backend.tail(bp, lines)

    def read(self, path: str, offset: int, size: int) -> bytes:
        inode = self._tracker.get(path)
        if inode and inode.dirty:
            entry = self._cache.get(path)
            if entry is not None:
                return entry.data[offset:offset + size]
        backend, bp, _ = self._registry.resolve(path)
        return backend.read(bp, offset, size)

    def grep(self, pattern: str, path: str) -> list[str]:
        backend, bp, _ = self._registry.resolve(path)
        return backend.grep(pattern, bp)

    # ── write ops ───────────────────────────────────────────────────────────

    def write(self, path: str, data: bytes) -> None:
        self._assert_not_locked(path)
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        self._cache.put(path, data)
        self._tracker.on_write(path, data, self._mount_prefix(path))
        self._record_transfer(path, "write", len(data), 0)
        if self._sync_policy == SyncPolicy.IMMEDIATE:
            backend.put(bp, data)
            self._tracker.on_read(path, data, self._mount_prefix(path))

    def mkdir(self, path: str) -> None:
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        backend.mkdir(bp)

    def rm(self, path: str) -> None:
        self._assert_writable(path)
        backend, bp, _ = self._registry.resolve(path)
        inode = self._tracker.get(path)
        staged_only = inode and inode.dirty and inode.remote_checksum is None
        if not staged_only:
            backend.unlink(bp)
        self._cache.invalidate(path)
        self._tracker.on_delete(path)

    def mv(self, src: str, dst: str) -> None:
        self._assert_writable(src)
        sb, sbp, _ = self._registry.resolve(src)
        db, dbp, _ = self._registry.resolve(dst)
        src_inode = self._tracker.get(src)
        staged_only = (src_inode and src_inode.dirty
                       and src_inode.remote_checksum is None)
        if staged_only:
            entry = self._cache.get(src)
            data = entry.data if entry else b""
            self._cache.put(dst, data)
            self._tracker.on_write(dst, data, self._mount_prefix(dst))
            self._cache.invalidate(src)
            self._tracker.on_delete(src)
        elif sb is db:
            sb.rename(sbp, dbp)
            self._cache.invalidate(src)
        else:
            data = sb.cat(sbp)
            db.put(dbp, data)
            sb.unlink(sbp)
            self._cache.invalidate(src)

    def cp(self, src: str, dst: str) -> None:
        self._assert_writable(dst)
        data = self.cat(src)
        db, dbp, _ = self._registry.resolve(dst)
        db.put(dbp, data)

    # ── sync lifecycle ──────────────────────────────────────────────────────

    def sync(self, path: str | None = None) -> list[SyncResult]:
        results: list[SyncResult] = []
        targets = [
            i for i in self._tracker.dirty()
            if path is None or i.virtual_path == path
        ]
        for inode in targets:
            backend, bp, _ = self._registry.resolve(inode.virtual_path)
            entry = self._cache.get(inode.virtual_path)
            data = entry.data if entry else None
            result = self._tracker.on_sync(inode.virtual_path, bp, data,
                                           backend)
            results.append(result)
        return results

    def status(self) -> list[Inode]:
        return self._tracker.status()

    def reset(self) -> None:
        self._tracker.reset()
        self._cache.clear()

    # ── path locking ─────────────────────────────────────────────────────────

    def _lock_path(self, path: str) -> None:
        self._locked_paths.add(path)

    def unlock_paths(self, paths: list[str]) -> None:
        for p in paths:
            self._locked_paths.discard(p)

    def lock_paths(self, paths: list[str]) -> _PathLock:
        for p in paths:
            self._lock_path(p)
        return _PathLock(self, paths)

    def _assert_not_locked(self, path: str) -> None:
        if path in self._locked_paths:
            raise PermissionError(
                f"path {path!r} is locked by remote execution")

    # ── helpers ─────────────────────────────────────────────────────────────

    def _mount_prefix(self, path: str) -> str:
        norm = "/" + path.strip("/")
        for m in self._registry.mounts():
            if norm == m.prefix.rstrip("/") or norm.startswith(m.prefix):
                return m.prefix
        return "/"

    def _effective_consistency(self, path: str) -> ConsistencyPolicy:
        mount = self._registry.mount_for(path)
        if mount.consistency is not None:
            return mount.consistency
        return self._consistency

    def _assert_writable(self, path: str) -> None:
        _, _, mode = self._registry.resolve(path)
        if mode == MountMode.READ:
            raise PermissionError(f"mount at {path!r} is read-only")

    def _record_transfer(self,
                         path: str,
                         op: str,
                         nbytes: int,
                         duration_ms: int,
                         cache_hit: bool | None = None) -> None:
        prefix = self._mount_prefix(path)
        if prefix not in self._transfer_stats:
            self._transfer_stats[prefix] = TransferStats()
        stats = self._transfer_stats[prefix]
        if op == "read":
            stats.bytes_read += nbytes
            stats.read_ops += 1
            stats.total_read_ms += duration_ms
            if cache_hit is True:
                stats.cache_hits += 1
            elif cache_hit is False:
                stats.cache_misses += 1
        elif op == "write":
            stats.bytes_written += nbytes
            stats.write_ops += 1
            stats.total_write_ms += duration_ms

-    def _effective_consistency(self, path: str) -> ConsistencyPolicy:

-        mount = self._registry.mount_for(path)

-        if mount.consistency is not None:


Now update `server/service/local.py` — the mount consistency parsing uses `None`: