     1→import pytest
     2→from pydantic import ValidationError
     3→
     4→from mirage.types import (CommandStats, FileStat, Inode, SyncResult,
     5→                          TransferStats)
     6→
     7→
     8→def test_filestat_defaults():
     9→    fs = FileStat(name="foo.txt")
    10→    assert fs.name == "foo.txt"
    11→    assert fs.size is None
    12→    assert fs.extra == {}
    13→
    14→
    15→def test_filestat_immutable():
    16→    fs = FileStat(name="foo.txt")
    17→    with pytest.raises(ValidationError):
    18→        fs.name = "bar.txt"
    19→
    20→
    21→def test_inode_defaults():
    22→    inode = Inode(virtual_path="/a/b.txt", backend_mount="/a/")
    23→    assert inode.dirty is False
    24→    assert inode.checksum is None
    25→
    26→
    27→def test_inode_mutable():
    28→    inode = Inode(virtual_path="/a/b.txt", backend_mount="/a/")
    29→    inode.dirty = True
    30→    assert inode.dirty is True
    31→
    32→
    33→def test_syncresult_fields():
    34→    r = SyncResult(path="/a/b.txt", op="create", status="ok")
    35→    assert r.op == "create"
    36→    assert r.duration_ms == 0
    37→
    38→
    39→def test_syncresult_immutable():
    40→    r = SyncResult(path="/a/b.txt", op="create", status="ok")
    41→    with pytest.raises(ValidationError):
    42→        r.status = "error"
    43→
    44→
    45→def test_transferstats_mutable():
    46→    ts = TransferStats()
    47→    ts.bytes_read += 100
    48→    assert ts.bytes_read == 100
    49→
    50→
    51→def test_commandstats_defaults():
    52→    cs = CommandStats()
    53→    assert cs.total_commands == 0
    54→    assert cs.command_counts == {}
    55→

import pytest
from pydantic import ValidationError

from mirage.types import (CommandStats, FileStat, Inode, SyncResult,
                          TransferStats)

def test_filestat_defaults():
    fs = FileStat(name="foo.txt")
    assert fs.name == "foo.txt"
    assert fs.size is None
    assert fs.extra == {}

def test_filestat_immutable():
    fs = FileStat(name="foo.txt")
    with pytest.raises(ValidationError):
        fs.name = "bar.txt"

def test_inode_defaults():
    inode = Inode(virtual_path="/a/b.txt", backend_mount="/a/")
    assert inode.dirty is False
    assert inode.checksum is None

def test_inode_mutable():
    inode = Inode(virtual_path="/a/b.txt", backend_mount="/a/")
    inode.dirty = True
    assert inode.dirty is True

def test_syncresult_fields():
    r = SyncResult(path="/a/b.txt", op="create", status="ok")
    assert r.op == "create"
    assert r.duration_ms == 0

def test_syncresult_immutable():
    r = SyncResult(path="/a/b.txt", op="create", status="ok")
    with pytest.raises(ValidationError):
        r.status = "error"

def test_transferstats_mutable():
    ts = TransferStats()
    ts.bytes_read += 100
    assert ts.bytes_read == 100

def test_commandstats_defaults():
    cs = CommandStats()
    assert cs.total_commands == 0
    assert cs.command_counts == {}


Edit

from mirage.types import (CommandStats, FileStat, Inode, SyncResult,
                          TransferStats)

from mirage.types import (BackendType, CommandResult, CommandStats,
                          FilePayload, FileStat, Inode, RemoteCommandOptions,
                          SyncResult, TransferStats)


The file /Users/agent/strukto/mirage/tests/test_types.py has been updated successfully.

from mirage.types import (BackendType, CommandResult, CommandStats,
                          FilePayload, FileStat, Inode, RemoteCommandOptions,
                          SyncResult, TransferStats)

import pytest
from pydantic import ValidationError

from mirage.types import (CommandStats, FileStat, Inode, SyncResult,
                          TransferStats)

def test_filestat_defaults():
    fs = FileStat(name="foo.txt")
    assert fs.name == "foo.txt"
    assert fs.size is None
    assert fs.extra == {}

def test_filestat_immutable():
    fs = FileStat(name="foo.txt")
    with pytest.raises(ValidationError):
        fs.name = "bar.txt"

def test_inode_defaults():
    inode = Inode(virtual_path="/a/b.txt", backend_mount="/a/")
    assert inode.dirty is False
    assert inode.checksum is None

def test_inode_mutable():
    inode = Inode(virtual_path="/a/b.txt", backend_mount="/a/")
    inode.dirty = True
    assert inode.dirty is True

def test_syncresult_fields():
    r = SyncResult(path="/a/b.txt", op="create", status="ok")
    assert r.op == "create"
    assert r.duration_ms == 0

def test_syncresult_immutable():
    r = SyncResult(path="/a/b.txt", op="create", status="ok")
    with pytest.raises(ValidationError):
        r.status = "error"

def test_transferstats_mutable():
    ts = TransferStats()
    ts.bytes_read += 100
    assert ts.bytes_read == 100

def test_commandstats_defaults():
    cs = CommandStats()
    assert cs.total_commands == 0
    assert cs.command_counts == {}

-from mirage.types import (CommandStats, FileStat, Inode, SyncResult,

-                          TransferStats)

+from mirage.types import (BackendType, CommandResult, CommandStats,

+                          FilePayload, FileStat, Inode, RemoteCommandOptions,

+                          SyncResult, TransferStats)


def test_commandstats_defaults():
    cs = CommandStats()
    assert cs.total_commands == 0
    assert cs.command_counts == {}

def test_commandstats_defaults():
    cs = CommandStats()
    assert cs.total_commands == 0
    assert cs.command_counts == {}

def test_command_result_defaults():
    r = CommandResult(stdout="hello")
    assert r.stdout == "hello"
    assert r.output_files == {}

def test_command_result_with_files():
    r = CommandResult(stdout="done", output_files={"/out.csv": b"a,b"})
    assert r.output_files["/out.csv"] == b"a,b"

def test_file_payload_with_data():
    p = FilePayload(checksum="abc123", data=b"hello")
    assert p.checksum == "abc123"
    assert p.data == b"hello"

def test_file_payload_checksum_only():
    p = FilePayload(checksum="abc123")
    assert p.data is None

def test_remote_command_options_defaults():
    opts = RemoteCommandOptions()
    assert opts.max_file_size == "100MB"
    assert opts.cache == "enabled"

def test_remote_command_options_custom():
    opts = RemoteCommandOptions(max_file_size="500MB", cache="disabled")
    assert opts.max_file_size == "500MB"
    assert opts.cache == "disabled"

def test_remote_command_options_only_two_fields():
    opts = RemoteCommandOptions()
    assert opts.max_file_size == "100MB"
    assert opts.cache == "enabled"
    assert set(RemoteCommandOptions.model_fields.keys()) == {
        "max_file_size", "cache"
    }

def test_backend_type_values():
    assert BackendType.MEMORY == "memory"
    assert BackendType.LOCAL == "local"
    assert BackendType.S3 == "s3"
    assert BackendType.R2 == "r2"
    assert BackendType.HTTP == "http"
    assert BackendType.HTTPS == "https"

def test_backend_type_string_equality():
    assert BackendType.MEMORY.value == "memory"
    assert BackendType.LOCAL.value == "local"
    assert BackendType.S3.value == "s3"
    assert BackendType.R2.value == "r2"
    assert BackendType.HTTP.value == "http"
    assert BackendType.HTTPS.value == "https"
    assert BackendType("memory") is BackendType.MEMORY
    assert BackendType("s3") is BackendType.S3

def test_backend_type_members():
    assert set(BackendType) == {
        BackendType.MEMORY,
        BackendType.LOCAL,
        BackendType.S3,
        BackendType.R2,
        BackendType.HTTP,
        BackendType.HTTPS,
    }


def test_commandstats_defaults():
    cs = CommandStats()
    assert cs.total_commands == 0
    assert cs.command_counts == {}

def test_command_result_defaults():
    r = CommandResult(stdout="hello")
    assert r.stdout == "hello"
    assert r.output_files == {}

def test_command_result_with_files():
    r = CommandResult(stdout="done", output_files={"/out.csv": b"a,b"})
    assert r.output_files["/out.csv"] == b"a,b"

def test_file_payload_with_data():
    p = FilePayload(checksum="abc123", data=b"hello")
    assert p.checksum == "abc123"
    assert p.data == b"hello"

def test_file_payload_checksum_only():
    p = FilePayload(checksum="abc123")
    assert p.data is None

def test_remote_command_options_defaults():
    opts = RemoteCommandOptions()
    assert opts.max_file_size == "100MB"
    assert opts.cache == "enabled"

def test_remote_command_options_custom():
    opts = RemoteCommandOptions(max_file_size="500MB", cache="disabled")
    assert opts.max_file_size == "500MB"
    assert opts.cache == "disabled"

def test_remote_command_options_only_two_fields():
    opts = RemoteCommandOptions()
    assert opts.max_file_size == "100MB"
    assert opts.cache == "enabled"
    assert set(RemoteCommandOptions.model_fields.keys()) == {
        "max_file_size", "cache"
    }

def test_backend_type_values():
    assert BackendType.MEMORY == "memory"
    assert BackendType.LOCAL == "local"
    assert BackendType.S3 == "s3"
    assert BackendType.R2 == "r2"
    assert BackendType.HTTP == "http"
    assert BackendType.HTTPS == "https"

def test_backend_type_string_equality():
    assert BackendType.MEMORY.value == "memory"
    assert BackendType.LOCAL.value == "local"
    assert BackendType.S3.value == "s3"
    assert BackendType.R2.value == "r2"
    assert BackendType.HTTP.value == "http"
    assert BackendType.HTTPS.value == "https"
    assert BackendType("memory") is BackendType.MEMORY
    assert BackendType("s3") is BackendType.S3

def test_backend_type_members():
    assert set(BackendType) == {
        BackendType.MEMORY,
        BackendType.LOCAL,
        BackendType.S3,
        BackendType.R2,
        BackendType.HTTP,
        BackendType.HTTPS,
    }

import pytest
from pydantic import ValidationError

from mirage.types import (BackendType, CommandResult, CommandStats,
                          FilePayload, FileStat, Inode, RemoteCommandOptions,
                          SyncResult, TransferStats)

def test_filestat_defaults():
    fs = FileStat(name="foo.txt")
    assert fs.name == "foo.txt"
    assert fs.size is None
    assert fs.extra == {}

def test_filestat_immutable():
    fs = FileStat(name="foo.txt")
    with pytest.raises(ValidationError):
        fs.name = "bar.txt"

def test_inode_defaults():
    inode = Inode(virtual_path="/a/b.txt", backend_mount="/a/")
    assert inode.dirty is False
    assert inode.checksum is None

def test_inode_mutable():
    inode = Inode(virtual_path="/a/b.txt", backend_mount="/a/")
    inode.dirty = True
    assert inode.dirty is True

def test_syncresult_fields():
    r = SyncResult(path="/a/b.txt", op="create", status="ok")
    assert r.op == "create"
    assert r.duration_ms == 0

def test_syncresult_immutable():
    r = SyncResult(path="/a/b.txt", op="create", status="ok")
    with pytest.raises(ValidationError):
        r.status = "error"

def test_transferstats_mutable():
    ts = TransferStats()
    ts.bytes_read += 100
    assert ts.bytes_read == 100

def test_commandstats_defaults():
    cs = CommandStats()
    assert cs.total_commands == 0
    assert cs.command_counts == {}

+    r = CommandResult(stdout="done", output_files={"/out.csv": b"a,b"})

+    assert r.output_files["/out.csv"] == b"a,b"

+    p = FilePayload(checksum="abc123", data=b"hello")

+    assert opts.max_file_size == "100MB"

+    opts = RemoteCommandOptions(max_file_size="500MB", cache="disabled")

+    assert opts.max_file_size == "500MB"

+    assert set(RemoteCommandOptions.model_fields.keys()) == {

+    assert BackendType.MEMORY == "memory"

+    assert BackendType.LOCAL == "local"

+    assert BackendType.HTTPS == "https"

+    assert BackendType.MEMORY.value == "memory"

+    assert BackendType.LOCAL.value == "local"

+    assert BackendType.S3.value == "s3"

+    assert BackendType.R2.value == "r2"

+    assert BackendType.HTTP.value == "http"

+    assert BackendType.HTTPS.value == "https"

+    assert BackendType("memory") is BackendType.MEMORY

+    assert BackendType("s3") is BackendType.S3


Bash

git rm tests/types/test_types.py && git rm tests/types/__init__.py && rmdir tests/types

Remove tests/types/ directory after merge


Error: Exit code 1
rm 'tests/types/test_types.py'
rm 'tests/types/__init__.py'
rmdir: tests/types: Directory not empty

rm 'tests/types/test_types.py'
rm 'tests/types/__init__.py'
rmdir: tests/types: Directory not empty


Check what's left in tests/types/


total 0
drwxr-xr-x@  3 agent  staff   96 Mar 17 07:37 .
drwxr-xr-x@ 16 agent  staff  512 Mar 17 07:36 ..
drwxr-xr-x@  4 agent  staff  128 Mar 17 04:25 __pycache__


Remove tests/types/ including __pycache__


TodoWrite

Create 15 new test directories (no __init__.py)

Execute 15 pure git mv moves

Execute 3 renames (git mv)

Split test_configs.py into object_storage tests

Split test_default.py into fuse/backend tests

Split test_format_ops.py into csv and parquet tests

Split server/test_registry.py into commands/custom + server/constants

Merge types test files into tests/test_types.py

Merge workspace test files into tests/workspace/test_init.py

Remove existing __init__.py files from tests/ and cleanup

Removing __init__.py files and cleaning up

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

Create 15 new test directories (no __init__.py)

Execute 15 pure git mv moves

Execute 3 renames (git mv)

Split test_configs.py into object_storage tests

Split test_default.py into fuse/backend tests

Split test_format_ops.py into csv and parquet tests

Split server/test_registry.py into commands/custom + server/constants

Merge types test files into tests/test_types.py

Merge workspace test files into tests/workspace/test_init.py

Remove existing __init__.py files from tests/ and cleanup

Removing __init__.py files and cleaning up

Run tests and pre-commit to verify

Execute 15 pure git mv moves

Execute 3 renames (git mv)

Run tests and pre-commit to verify


Now merge workspace test files. Let me read all three fully:


     1→from datetime import datetime, timezone
     2→
     3→from mirage.backend.local.memory import MemoryBackend
     4→from mirage.types import ConsistencyPolicy, MountMode
     5→from mirage.workspace import Workspace
     6→
     7→
     8→def _make_ws(consistency=ConsistencyPolicy.NONE, ttl_seconds=300):
     9→    backend = MemoryBackend()
    10→    backend.put("/file.txt", b"original")
    11→    ws = Workspace(
    12→        {"/data/": backend},
    13→        mode=MountMode.WRITE,
    14→        consistency=consistency,
    15→        ttl_seconds=ttl_seconds,
    16→    )
    17→    return ws, backend
    18→
    19→
    20→def test_consistency_none_caches_forever():
    21→    ws, backend = _make_ws(ConsistencyPolicy.NONE)
    22→    assert ws.cat("/data/file.txt") == b"original"
    23→    backend._files["/file.txt"] = b"updated"
    24→    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    25→    assert ws.cat("/data/file.txt") == b"original"
    26→
    27→
    28→def test_consistency_always_refetches():
    29→    ws, backend = _make_ws(ConsistencyPolicy.ALWAYS)
    30→    assert ws.cat("/data/file.txt") == b"original"
    31→    backend._files["/file.txt"] = b"updated"
    32→    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    33→    assert ws.cat("/data/file.txt") == b"updated"
    34→
    35→
    36→def test_consistency_ttl_fresh_within_window():
    37→    ws, backend = _make_ws(ConsistencyPolicy.TTL, ttl_seconds=300)
    38→    assert ws.cat("/data/file.txt") == b"original"
    39→    backend._files["/file.txt"] = b"updated"
    40→    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    41→    assert ws.cat("/data/file.txt") == b"original"
    42→
    43→
    44→def test_consistency_ttl_stale_after_expiry():
    45→    ws, backend = _make_ws(ConsistencyPolicy.TTL, ttl_seconds=10)
    46→    assert ws.cat("/data/file.txt") == b"original"
    47→    backend._files["/file.txt"] = b"updated"
    48→    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    49→    entry = ws._cache.get("/data/file.txt")
    50→    entry.cached_at -= 20
    51→    assert ws.cat("/data/file.txt") == b"updated"
    52→
    53→
    54→def test_consistency_lazy_unchanged():
    55→    ws, backend = _make_ws(ConsistencyPolicy.LAZY)
    56→    assert ws.cat("/data/file.txt") == b"original"
    57→    assert ws.cat("/data/file.txt") == b"original"
    58→
    59→
    60→def test_consistency_lazy_changed():
    61→    ws, backend = _make_ws(ConsistencyPolicy.LAZY)
    62→    assert ws.cat("/data/file.txt") == b"original"
    63→    backend._files["/file.txt"] = b"updated-content"
    64→    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    65→    assert ws.cat("/data/file.txt") == b"updated-content"
    66→
    67→
    68→def test_dirty_file_skips_revalidation():
    69→    ws, backend = _make_ws(ConsistencyPolicy.ALWAYS)
    70→    ws.write("/data/file.txt", b"local-edit")
    71→    backend._files["/file.txt"] = b"remote-change"
    72→    assert ws.cat("/data/file.txt") == b"local-edit"
    73→
    74→
    75→def test_per_mount_consistency():
    76→    be_none = MemoryBackend()
    77→    be_none.put("/f.txt", b"v1")
    78→    be_always = MemoryBackend()
    79→    be_always.put("/f.txt", b"v1")
    80→    ws = Workspace(
    81→        {
    82→            "/a/": be_none,
    83→            "/b/": (be_always, MountMode.READ, ConsistencyPolicy.ALWAYS),
    84→        },
    85→        mode=MountMode.READ,
    86→        consistency=ConsistencyPolicy.NONE,
    87→    )
    88→    assert ws.cat("/a/f.txt") == b"v1"
    89→    assert ws.cat("/b/f.txt") == b"v1"
    90→    be_none._files["/f.txt"] = b"v2"
    91→    be_none._modified["/f.txt"] = datetime.now(timezone.utc).isoformat()
    92→    be_always._files["/f.txt"] = b"v2"
    93→    be_always._modified["/f.txt"] = datetime.now(timezone.utc).isoformat()
    94→    assert ws.cat("/a/f.txt") == b"v1"
    95→    assert ws.cat("/b/f.txt") == b"v2"
    96→

from datetime import datetime, timezone

from mirage.backend.local.memory import MemoryBackend
from mirage.types import ConsistencyPolicy, MountMode
from mirage.workspace import Workspace

def _make_ws(consistency=ConsistencyPolicy.NONE, ttl_seconds=300):
    backend = MemoryBackend()
    backend.put("/file.txt", b"original")
    ws = Workspace(
        {"/data/": backend},
        mode=MountMode.WRITE,
        consistency=consistency,
        ttl_seconds=ttl_seconds,
    )
    return ws, backend

def test_consistency_none_caches_forever():
    ws, backend = _make_ws(ConsistencyPolicy.NONE)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_always_refetches():
    ws, backend = _make_ws(ConsistencyPolicy.ALWAYS)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"updated"

def test_consistency_ttl_fresh_within_window():
    ws, backend = _make_ws(ConsistencyPolicy.TTL, ttl_seconds=300)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_ttl_stale_after_expiry():
    ws, backend = _make_ws(ConsistencyPolicy.TTL, ttl_seconds=10)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    entry = ws._cache.get("/data/file.txt")
    entry.cached_at -= 20
    assert ws.cat("/data/file.txt") == b"updated"

def test_consistency_lazy_unchanged():
    ws, backend = _make_ws(ConsistencyPolicy.LAZY)
    assert ws.cat("/data/file.txt") == b"original"
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_lazy_changed():
    ws, backend = _make_ws(ConsistencyPolicy.LAZY)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated-content"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"updated-content"

def test_dirty_file_skips_revalidation():
    ws, backend = _make_ws(ConsistencyPolicy.ALWAYS)
    ws.write("/data/file.txt", b"local-edit")
    backend._files["/file.txt"] = b"remote-change"
    assert ws.cat("/data/file.txt") == b"local-edit"

def test_per_mount_consistency():
    be_none = MemoryBackend()
    be_none.put("/f.txt", b"v1")
    be_always = MemoryBackend()
    be_always.put("/f.txt", b"v1")
    ws = Workspace(
        {
            "/a/": be_none,
            "/b/": (be_always, MountMode.READ, ConsistencyPolicy.ALWAYS),
        },
        mode=MountMode.READ,
        consistency=ConsistencyPolicy.NONE,
    )
    assert ws.cat("/a/f.txt") == b"v1"
    assert ws.cat("/b/f.txt") == b"v1"
    be_none._files["/f.txt"] = b"v2"
    be_none._modified["/f.txt"] = datetime.now(timezone.utc).isoformat()
    be_always._files["/f.txt"] = b"v2"
    be_always._modified["/f.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/a/f.txt") == b"v1"
    assert ws.cat("/b/f.txt") == b"v2"


     1→import pytest
     2→
     3→from mirage.backend.local.memory import MemoryBackend
     4→from mirage.types import MountMode, SyncPolicy
     5→from mirage.workspace import Workspace
     6→
     7→
     8→def _make_ws(mode=MountMode.WRITE, sync_policy=SyncPolicy.STAGED):
     9→    backend = MemoryBackend()
    10→    return Workspace({"/data/": backend}, mode=mode,
    11→                     sync_policy=sync_policy), backend
    12→
    13→
    14→def test_write_and_cat():
    15→    ws, _ = _make_ws()
    16→    ws.write("/data/hello.txt", b"hello world")
    17→    assert ws.cat("/data/hello.txt") == b"hello world"
    18→
    19→
    20→def test_write_marks_dirty():
    21→    ws, _ = _make_ws()
    22→    ws.write("/data/file.txt", b"x")
    23→    assert any(i.virtual_path == "/data/file.txt" and i.dirty
    24→               for i in ws.status())
    25→
    26→
    27→def test_rm_staged_only():
    28→    ws, _ = _make_ws()
    29→    ws.write("/data/new.txt", b"staged")
    30→    ws.rm("/data/new.txt")
    31→    assert not ws.exists("/data/new.txt")
    32→
    33→
    34→def test_rm_persisted():
    35→    ws, backend = _make_ws()
    36→    backend.put("/remote.txt", b"remote")
    37→    ws.rm("/data/remote.txt")
    38→    assert not ws.exists("/data/remote.txt")
    39→
    40→
    41→def test_mv_staged_only():
    42→    ws, _ = _make_ws()
    43→    ws.write("/data/a.txt", b"content")
    44→    ws.mv("/data/a.txt", "/data/b.txt")
    45→    assert ws.cat("/data/b.txt") == b"content"
    46→    assert not ws.exists("/data/a.txt")
    47→
    48→
    49→def test_cp_creates_copy():
    50→    ws, backend = _make_ws()
    51→    backend.put("/src.txt", b"original")
    52→    ws.cp("/data/src.txt", "/data/copy.txt")
    53→    assert ws.cat("/data/copy.txt") == b"original"
    54→
    55→
    56→def test_write_read_only_raises():
    57→    ws, _ = _make_ws(mode=MountMode.READ)
    58→    with pytest.raises(PermissionError):
    59→        ws.write("/data/x.txt", b"x")
    60→
    61→
    62→def test_sync_staged_writes_to_backend():
    63→    ws, backend = _make_ws(sync_policy=SyncPolicy.STAGED)
    64→    ws.write("/data/out.txt", b"synced")
    65→    results = ws.sync()
    66→    assert any(r.path == "/data/out.txt" and r.status == "ok" for r in results)
    67→    assert backend.cat("/out.txt") == b"synced"
    68→
    69→
    70→def test_sync_immediate_writes_through():
    71→    ws, backend = _make_ws(sync_policy=SyncPolicy.IMMEDIATE)
    72→    ws.write("/data/live.txt", b"live data")
    73→    assert backend.cat("/live.txt") == b"live data"
    74→    assert not any(i.dirty for i in ws.status())
    75→
    76→
    77→def test_reset_clears_dirty():
    78→    ws, _ = _make_ws()
    79→    ws.write("/data/x.txt", b"x")
    80→    ws.reset()
    81→    assert ws.status() == []
    82→
    83→
    84→def test_stat_staged_file():
    85→    ws, _ = _make_ws()
    86→    ws.write("/data/stat.txt", b"hello")
    87→    stat = ws.stat("/data/stat.txt")
    88→    assert stat.size == 5
    89→
    90→
    91→def test_exists_staged_file():
    92→    ws, _ = _make_ws()
    93→    ws.write("/data/exists.txt", b"y")
    94→    assert ws.exists("/data/exists.txt")
    95→

import pytest

from mirage.backend.local.memory import MemoryBackend
from mirage.types import MountMode, SyncPolicy
from mirage.workspace import Workspace

def _make_ws(mode=MountMode.WRITE, sync_policy=SyncPolicy.STAGED):
    backend = MemoryBackend()
    return Workspace({"/data/": backend}, mode=mode,
                     sync_policy=sync_policy), backend

def test_write_and_cat():
    ws, _ = _make_ws()
    ws.write("/data/hello.txt", b"hello world")
    assert ws.cat("/data/hello.txt") == b"hello world"

def test_write_marks_dirty():
    ws, _ = _make_ws()
    ws.write("/data/file.txt", b"x")
    assert any(i.virtual_path == "/data/file.txt" and i.dirty
               for i in ws.status())

def test_rm_staged_only():
    ws, _ = _make_ws()
    ws.write("/data/new.txt", b"staged")
    ws.rm("/data/new.txt")
    assert not ws.exists("/data/new.txt")

def test_rm_persisted():
    ws, backend = _make_ws()
    backend.put("/remote.txt", b"remote")
    ws.rm("/data/remote.txt")
    assert not ws.exists("/data/remote.txt")

def test_mv_staged_only():
    ws, _ = _make_ws()
    ws.write("/data/a.txt", b"content")
    ws.mv("/data/a.txt", "/data/b.txt")
    assert ws.cat("/data/b.txt") == b"content"
    assert not ws.exists("/data/a.txt")

def test_cp_creates_copy():
    ws, backend = _make_ws()
    backend.put("/src.txt", b"original")
    ws.cp("/data/src.txt", "/data/copy.txt")
    assert ws.cat("/data/copy.txt") == b"original"

def test_write_read_only_raises():
    ws, _ = _make_ws(mode=MountMode.READ)
    with pytest.raises(PermissionError):
        ws.write("/data/x.txt", b"x")

def test_sync_staged_writes_to_backend():
    ws, backend = _make_ws(sync_policy=SyncPolicy.STAGED)
    ws.write("/data/out.txt", b"synced")
    results = ws.sync()
    assert any(r.path == "/data/out.txt" and r.status == "ok" for r in results)
    assert backend.cat("/out.txt") == b"synced"

def test_sync_immediate_writes_through():
    ws, backend = _make_ws(sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/live.txt", b"live data")
    assert backend.cat("/live.txt") == b"live data"
    assert not any(i.dirty for i in ws.status())

def test_reset_clears_dirty():
    ws, _ = _make_ws()
    ws.write("/data/x.txt", b"x")
    ws.reset()
    assert ws.status() == []

def test_stat_staged_file():
    ws, _ = _make_ws()
    ws.write("/data/stat.txt", b"hello")
    stat = ws.stat("/data/stat.txt")
    assert stat.size == 5

def test_exists_staged_file():
    ws, _ = _make_ws()
    ws.write("/data/exists.txt", b"y")
    assert ws.exists("/data/exists.txt")


     1→import pytest
     2→
     3→from mirage.backend.local.memory import MemoryBackend
     4→from mirage.types import MountMode, SyncPolicy
     5→from mirage.workspace import Workspace
     6→
     7→
     8→def test_lock_paths_blocks_write():
     9→    ws = Workspace({"/data/": MemoryBackend()},
    10→                   mode=MountMode.WRITE,
    11→                   sync_policy=SyncPolicy.IMMEDIATE)
    12→    ws.write("/data/file.txt", b"hello")
    13→    with ws.lock_paths(["/data/file.txt"]):
    14→        with pytest.raises(PermissionError, match="locked"):
    15→            ws.write("/data/file.txt", b"world")
    16→
    17→
    18→def test_lock_paths_unlocks_on_exit():
    19→    ws = Workspace({"/data/": MemoryBackend()},
    20→                   mode=MountMode.WRITE,
    21→                   sync_policy=SyncPolicy.IMMEDIATE)
    22→    ws.write("/data/file.txt", b"hello")
    23→    with ws.lock_paths(["/data/file.txt"]):
    24→        pass
    25→    ws.write("/data/file.txt", b"world")
    26→    assert ws.cat("/data/file.txt") == b"world"
    27→
    28→
    29→def test_lock_paths_unlocks_on_exception():
    30→    ws = Workspace({"/data/": MemoryBackend()},
    31→                   mode=MountMode.WRITE,
    32→                   sync_policy=SyncPolicy.IMMEDIATE)
    33→    ws.write("/data/file.txt", b"hello")
    34→    with pytest.raises(RuntimeError):
    35→        with ws.lock_paths(["/data/file.txt"]):
    36→            raise RuntimeError("boom")
    37→    ws.write("/data/file.txt", b"world")
    38→    assert ws.cat("/data/file.txt") == b"world"
    39→
    40→
    41→def test_lock_allows_read():
    42→    ws = Workspace({"/data/": MemoryBackend()},
    43→                   mode=MountMode.WRITE,
    44→                   sync_policy=SyncPolicy.IMMEDIATE)
    45→    ws.write("/data/file.txt", b"hello")
    46→    with ws.lock_paths(["/data/file.txt"]):
    47→        assert ws.cat("/data/file.txt") == b"hello"
    48→
    49→
    50→def test_lock_multiple_paths():
    51→    ws = Workspace({"/data/": MemoryBackend()},
    52→                   mode=MountMode.WRITE,
    53→                   sync_policy=SyncPolicy.IMMEDIATE)
    54→    ws.write("/data/a.txt", b"a")
    55→    ws.write("/data/b.txt", b"b")
    56→    with ws.lock_paths(["/data/a.txt", "/data/b.txt"]):
    57→        with pytest.raises(PermissionError, match="locked"):
    58→            ws.write("/data/a.txt", b"x")
    59→        with pytest.raises(PermissionError, match="locked"):
    60→            ws.write("/data/b.txt", b"x")
    61→
    62→
    63→def test_unlock_paths_force():
    64→    ws = Workspace({"/data/": MemoryBackend()},
    65→                   mode=MountMode.WRITE,
    66→                   sync_policy=SyncPolicy.IMMEDIATE)
    67→    ws.write("/data/file.txt", b"hello")
    68→    ws._lock_path("/data/file.txt")
    69→    with pytest.raises(PermissionError, match="locked"):
    70→        ws.write("/data/file.txt", b"world")
    71→    ws.unlock_paths(["/data/file.txt"])
    72→    ws.write("/data/file.txt", b"world")
    73→

import pytest

from mirage.backend.local.memory import MemoryBackend
from mirage.types import MountMode, SyncPolicy
from mirage.workspace import Workspace

def test_lock_paths_blocks_write():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with ws.lock_paths(["/data/file.txt"]):
        with pytest.raises(PermissionError, match="locked"):
            ws.write("/data/file.txt", b"world")

def test_lock_paths_unlocks_on_exit():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with ws.lock_paths(["/data/file.txt"]):
        pass
    ws.write("/data/file.txt", b"world")
    assert ws.cat("/data/file.txt") == b"world"

def test_lock_paths_unlocks_on_exception():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with pytest.raises(RuntimeError):
        with ws.lock_paths(["/data/file.txt"]):
            raise RuntimeError("boom")
    ws.write("/data/file.txt", b"world")
    assert ws.cat("/data/file.txt") == b"world"

def test_lock_allows_read():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with ws.lock_paths(["/data/file.txt"]):
        assert ws.cat("/data/file.txt") == b"hello"

def test_lock_multiple_paths():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/a.txt", b"a")
    ws.write("/data/b.txt", b"b")
    with ws.lock_paths(["/data/a.txt", "/data/b.txt"]):
        with pytest.raises(PermissionError, match="locked"):
            ws.write("/data/a.txt", b"x")
        with pytest.raises(PermissionError, match="locked"):
            ws.write("/data/b.txt", b"x")

def test_unlock_paths_force():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    ws._lock_path("/data/file.txt")
    with pytest.raises(PermissionError, match="locked"):
        ws.write("/data/file.txt", b"world")
    ws.unlock_paths(["/data/file.txt"])
    ws.write("/data/file.txt", b"world")


Now merge all three into `test_init.py`. The `_make_ws` helpers have different signatures — I'll rename them to avoid collisions.


Write

from datetime import datetime, timezone

import pytest

from mirage.backend.local.memory import MemoryBackend
from mirage.types import ConsistencyPolicy, MountMode, SyncPolicy
from mirage.workspace import Workspace

def _make_consistency_ws(consistency=ConsistencyPolicy.NONE, ttl_seconds=300):
    backend = MemoryBackend()
    backend.put("/file.txt", b"original")
    ws = Workspace(
        {"/data/": backend},
        mode=MountMode.WRITE,
        consistency=consistency,
        ttl_seconds=ttl_seconds,
    )
    return ws, backend

def _make_ws(mode=MountMode.WRITE, sync_policy=SyncPolicy.STAGED):
    backend = MemoryBackend()
    return Workspace({"/data/": backend}, mode=mode,
                     sync_policy=sync_policy), backend

def test_consistency_none_caches_forever():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.NONE)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_always_refetches():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.ALWAYS)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"updated"

def test_consistency_ttl_fresh_within_window():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.TTL, ttl_seconds=300)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_ttl_stale_after_expiry():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.TTL, ttl_seconds=10)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    entry = ws._cache.get("/data/file.txt")
    entry.cached_at -= 20
    assert ws.cat("/data/file.txt") == b"updated"

def test_consistency_lazy_unchanged():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.LAZY)
    assert ws.cat("/data/file.txt") == b"original"
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_lazy_changed():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.LAZY)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated-content"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"updated-content"

def test_dirty_file_skips_revalidation():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.ALWAYS)
    ws.write("/data/file.txt", b"local-edit")
    backend._files["/file.txt"] = b"remote-change"
    assert ws.cat("/data/file.txt") == b"local-edit"

def test_per_mount_consistency():
    be_none = MemoryBackend()
    be_none.put("/f.txt", b"v1")
    be_always = MemoryBackend()
    be_always.put("/f.txt", b"v1")
    ws = Workspace(
        {
            "/a/": be_none,
            "/b/": (be_always, MountMode.READ, ConsistencyPolicy.ALWAYS),
        },
        mode=MountMode.READ,
        consistency=ConsistencyPolicy.NONE,
    )
    assert ws.cat("/a/f.txt") == b"v1"
    assert ws.cat("/b/f.txt") == b"v1"
    be_none._files["/f.txt"] = b"v2"
    be_none._modified["/f.txt"] = datetime.now(timezone.utc).isoformat()
    be_always._files["/f.txt"] = b"v2"
    be_always._modified["/f.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/a/f.txt") == b"v1"
    assert ws.cat("/b/f.txt") == b"v2"

def test_write_and_cat():
    ws, _ = _make_ws()
    ws.write("/data/hello.txt", b"hello world")
    assert ws.cat("/data/hello.txt") == b"hello world"

def test_write_marks_dirty():
    ws, _ = _make_ws()
    ws.write("/data/file.txt", b"x")
    assert any(i.virtual_path == "/data/file.txt" and i.dirty
               for i in ws.status())

def test_rm_staged_only():
    ws, _ = _make_ws()
    ws.write("/data/new.txt", b"staged")
    ws.rm("/data/new.txt")
    assert not ws.exists("/data/new.txt")

def test_rm_persisted():
    ws, backend = _make_ws()
    backend.put("/remote.txt", b"remote")
    ws.rm("/data/remote.txt")
    assert not ws.exists("/data/remote.txt")

def test_mv_staged_only():
    ws, _ = _make_ws()
    ws.write("/data/a.txt", b"content")
    ws.mv("/data/a.txt", "/data/b.txt")
    assert ws.cat("/data/b.txt") == b"content"
    assert not ws.exists("/data/a.txt")

def test_cp_creates_copy():
    ws, backend = _make_ws()
    backend.put("/src.txt", b"original")
    ws.cp("/data/src.txt", "/data/copy.txt")
    assert ws.cat("/data/copy.txt") == b"original"

def test_write_read_only_raises():
    ws, _ = _make_ws(mode=MountMode.READ)
    with pytest.raises(PermissionError):
        ws.write("/data/x.txt", b"x")

def test_sync_staged_writes_to_backend():
    ws, backend = _make_ws(sync_policy=SyncPolicy.STAGED)
    ws.write("/data/out.txt", b"synced")
    results = ws.sync()
    assert any(r.path == "/data/out.txt" and r.status == "ok" for r in results)
    assert backend.cat("/out.txt") == b"synced"

def test_sync_immediate_writes_through():
    ws, backend = _make_ws(sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/live.txt", b"live data")
    assert backend.cat("/live.txt") == b"live data"
    assert not any(i.dirty for i in ws.status())

def test_reset_clears_dirty():
    ws, _ = _make_ws()
    ws.write("/data/x.txt", b"x")
    ws.reset()
    assert ws.status() == []

def test_stat_staged_file():
    ws, _ = _make_ws()
    ws.write("/data/stat.txt", b"hello")
    stat = ws.stat("/data/stat.txt")
    assert stat.size == 5

def test_exists_staged_file():
    ws, _ = _make_ws()
    ws.write("/data/exists.txt", b"y")
    assert ws.exists("/data/exists.txt")

def test_lock_paths_blocks_write():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with ws.lock_paths(["/data/file.txt"]):
        with pytest.raises(PermissionError, match="locked"):
            ws.write("/data/file.txt", b"world")

def test_lock_paths_unlocks_on_exit():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with ws.lock_paths(["/data/file.txt"]):
        pass
    ws.write("/data/file.txt", b"world")
    assert ws.cat("/data/file.txt") == b"world"

def test_lock_paths_unlocks_on_exception():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with pytest.raises(RuntimeError):
        with ws.lock_paths(["/data/file.txt"]):
            raise RuntimeError("boom")
    ws.write("/data/file.txt", b"world")
    assert ws.cat("/data/file.txt") == b"world"

def test_lock_allows_read():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with ws.lock_paths(["/data/file.txt"]):
        assert ws.cat("/data/file.txt") == b"hello"

def test_lock_multiple_paths():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/a.txt", b"a")
    ws.write("/data/b.txt", b"b")
    with ws.lock_paths(["/data/a.txt", "/data/b.txt"]):
        with pytest.raises(PermissionError, match="locked"):
            ws.write("/data/a.txt", b"x")
        with pytest.raises(PermissionError, match="locked"):
            ws.write("/data/b.txt", b"x")

def test_unlock_paths_force():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    ws._lock_path("/data/file.txt")
    with pytest.raises(PermissionError, match="locked"):
        ws.write("/data/file.txt", b"world")
    ws.unlock_paths(["/data/file.txt"])
    ws.write("/data/file.txt", b"world")


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

from datetime import datetime, timezone

import pytest

from mirage.backend.local.memory import MemoryBackend
from mirage.types import ConsistencyPolicy, MountMode, SyncPolicy
from mirage.workspace import Workspace

def _make_consistency_ws(consistency=ConsistencyPolicy.NONE, ttl_seconds=300):
    backend = MemoryBackend()
    backend.put("/file.txt", b"original")
    ws = Workspace(
        {"/data/": backend},
        mode=MountMode.WRITE,
        consistency=consistency,
        ttl_seconds=ttl_seconds,
    )
    return ws, backend

def _make_ws(mode=MountMode.WRITE, sync_policy=SyncPolicy.STAGED):
    backend = MemoryBackend()
    return Workspace({"/data/": backend}, mode=mode,
                     sync_policy=sync_policy), backend

def test_consistency_none_caches_forever():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.NONE)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_always_refetches():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.ALWAYS)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"updated"

def test_consistency_ttl_fresh_within_window():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.TTL, ttl_seconds=300)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_ttl_stale_after_expiry():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.TTL, ttl_seconds=10)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    entry = ws._cache.get("/data/file.txt")
    entry.cached_at -= 20
    assert ws.cat("/data/file.txt") == b"updated"

def test_consistency_lazy_unchanged():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.LAZY)
    assert ws.cat("/data/file.txt") == b"original"
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_lazy_changed():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.LAZY)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated-content"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"updated-content"

def test_dirty_file_skips_revalidation():
    ws, backend = _make_consistency_ws(ConsistencyPolicy.ALWAYS)
    ws.write("/data/file.txt", b"local-edit")
    backend._files["/file.txt"] = b"remote-change"
    assert ws.cat("/data/file.txt") == b"local-edit"

def test_per_mount_consistency():
    be_none = MemoryBackend()
    be_none.put("/f.txt", b"v1")
    be_always = MemoryBackend()
    be_always.put("/f.txt", b"v1")
    ws = Workspace(
        {
            "/a/": be_none,
            "/b/": (be_always, MountMode.READ, ConsistencyPolicy.ALWAYS),
        },
        mode=MountMode.READ,
        consistency=ConsistencyPolicy.NONE,
    )
    assert ws.cat("/a/f.txt") == b"v1"
    assert ws.cat("/b/f.txt") == b"v1"
    be_none._files["/f.txt"] = b"v2"
    be_none._modified["/f.txt"] = datetime.now(timezone.utc).isoformat()
    be_always._files["/f.txt"] = b"v2"
    be_always._modified["/f.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/a/f.txt") == b"v1"
    assert ws.cat("/b/f.txt") == b"v2"

def test_write_and_cat():
    ws, _ = _make_ws()
    ws.write("/data/hello.txt", b"hello world")
    assert ws.cat("/data/hello.txt") == b"hello world"

def test_write_marks_dirty():
    ws, _ = _make_ws()
    ws.write("/data/file.txt", b"x")
    assert any(i.virtual_path == "/data/file.txt" and i.dirty
               for i in ws.status())

def test_rm_staged_only():
    ws, _ = _make_ws()
    ws.write("/data/new.txt", b"staged")
    ws.rm("/data/new.txt")
    assert not ws.exists("/data/new.txt")

def test_rm_persisted():
    ws, backend = _make_ws()
    backend.put("/remote.txt", b"remote")
    ws.rm("/data/remote.txt")
    assert not ws.exists("/data/remote.txt")

def test_mv_staged_only():
    ws, _ = _make_ws()
    ws.write("/data/a.txt", b"content")
    ws.mv("/data/a.txt", "/data/b.txt")
    assert ws.cat("/data/b.txt") == b"content"
    assert not ws.exists("/data/a.txt")

def test_cp_creates_copy():
    ws, backend = _make_ws()
    backend.put("/src.txt", b"original")
    ws.cp("/data/src.txt", "/data/copy.txt")
    assert ws.cat("/data/copy.txt") == b"original"

def test_write_read_only_raises():
    ws, _ = _make_ws(mode=MountMode.READ)
    with pytest.raises(PermissionError):
        ws.write("/data/x.txt", b"x")

def test_sync_staged_writes_to_backend():
    ws, backend = _make_ws(sync_policy=SyncPolicy.STAGED)
    ws.write("/data/out.txt", b"synced")
    results = ws.sync()
    assert any(r.path == "/data/out.txt" and r.status == "ok" for r in results)
    assert backend.cat("/out.txt") == b"synced"

def test_sync_immediate_writes_through():
    ws, backend = _make_ws(sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/live.txt", b"live data")
    assert backend.cat("/live.txt") == b"live data"
    assert not any(i.dirty for i in ws.status())

def test_reset_clears_dirty():
    ws, _ = _make_ws()
    ws.write("/data/x.txt", b"x")
    ws.reset()
    assert ws.status() == []

def test_stat_staged_file():
    ws, _ = _make_ws()
    ws.write("/data/stat.txt", b"hello")
    stat = ws.stat("/data/stat.txt")
    assert stat.size == 5

def test_exists_staged_file():
    ws, _ = _make_ws()
    ws.write("/data/exists.txt", b"y")
    assert ws.exists("/data/exists.txt")

def test_lock_paths_blocks_write():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with ws.lock_paths(["/data/file.txt"]):
        with pytest.raises(PermissionError, match="locked"):
            ws.write("/data/file.txt", b"world")

def test_lock_paths_unlocks_on_exit():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with ws.lock_paths(["/data/file.txt"]):
        pass
    ws.write("/data/file.txt", b"world")
    assert ws.cat("/data/file.txt") == b"world"

def test_lock_paths_unlocks_on_exception():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with pytest.raises(RuntimeError):
        with ws.lock_paths(["/data/file.txt"]):
            raise RuntimeError("boom")
    ws.write("/data/file.txt", b"world")
    assert ws.cat("/data/file.txt") == b"world"

def test_lock_allows_read():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    with ws.lock_paths(["/data/file.txt"]):
        assert ws.cat("/data/file.txt") == b"hello"

def test_lock_multiple_paths():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/a.txt", b"a")
    ws.write("/data/b.txt", b"b")
    with ws.lock_paths(["/data/a.txt", "/data/b.txt"]):
        with pytest.raises(PermissionError, match="locked"):
            ws.write("/data/a.txt", b"x")
        with pytest.raises(PermissionError, match="locked"):
            ws.write("/data/b.txt", b"x")

def test_unlock_paths_force():
    ws = Workspace({"/data/": MemoryBackend()},
                   mode=MountMode.WRITE,
                   sync_policy=SyncPolicy.IMMEDIATE)
    ws.write("/data/file.txt", b"hello")
    ws._lock_path("/data/file.txt")
    with pytest.raises(PermissionError, match="locked"):
        ws.write("/data/file.txt", b"world")
    ws.unlock_paths(["/data/file.txt"])
    ws.write("/data/file.txt", b"world")

-from mirage.types import ConsistencyPolicy, MountMode

+from mirage.types import ConsistencyPolicy, MountMode, SyncPolicy

+    return Workspace({"/data/": backend}, mode=mode,

+                     sync_policy=sync_policy), backend

-    ws, backend = _make_ws(ConsistencyPolicy.NONE)

+    ws, backend = _make_consistency_ws(ConsistencyPolicy.NONE)

-    ws, backend = _make_ws(ConsistencyPolicy.ALWAYS)

+    ws, backend = _make_consistency_ws(ConsistencyPolicy.ALWAYS)

-    ws, backend = _make_ws(ConsistencyPolicy.TTL, ttl_seconds=300)

+    ws, backend = _make_consistency_ws(ConsistencyPolicy.TTL, ttl_seconds=300)

-    ws, backend = _make_ws(ConsistencyPolicy.TTL, ttl_seconds=10)

+    ws, backend = _make_consistency_ws(ConsistencyPolicy.TTL, ttl_seconds=10)

-    ws, backend = _make_ws(ConsistencyPolicy.LAZY)

+    ws, backend = _make_consistency_ws(ConsistencyPolicy.LAZY)

+    ws.write("/data/hello.txt", b"hello world")

+    assert ws.cat("/data/hello.txt") == b"hello world"

+    assert any(i.virtual_path == "/data/file.txt" and i.dirty

+    ws.write("/data/new.txt", b"staged")

+    assert not ws.exists("/data/new.txt")

+    backend.put("/remote.txt", b"remote")

+    assert not ws.exists("/data/remote.txt")

+    ws.write("/data/a.txt", b"content")

+    ws.mv("/data/a.txt", "/data/b.txt")

+    assert ws.cat("/data/b.txt") == b"content"

+    assert not ws.exists("/data/a.txt")

+    backend.put("/src.txt", b"original")

+    ws.cp("/data/src.txt", "/data/copy.txt")

+    assert ws.cat("/data/copy.txt") == b"original"

+    ws, _ = _make_ws(mode=MountMode.READ)

+    with pytest.raises(PermissionError):

+    ws, backend = _make_ws(sync_policy=SyncPolicy.STAGED)

+    ws.write("/data/out.txt", b"synced")

+    assert any(r.path == "/data/out.txt" and r.status == "ok" for r in results)

+    assert backend.cat("/out.txt") == b"synced"

+    ws, backend = _make_ws(sync_policy=SyncPolicy.IMMEDIATE)

+    ws.write("/data/live.txt", b"live data")

+    assert backend.cat("/live.txt") == b"live data"

+    assert not any(i.dirty for i in ws.status())

+    ws.write("/data/stat.txt", b"hello")

+    assert ws.exists("/data/exists.txt")

+    ws = Workspace({"/data/": MemoryBackend()},

+                   mode=MountMode.WRITE,

+                   sync_policy=SyncPolicy.IMMEDIATE)

+    ws.write("/data/file.txt", b"hello")

+    with ws.lock_paths(["/data/file.txt"]):

+        with pytest.raises(PermissionError, match="locked"):

+            ws.write("/data/file.txt", b"world")

+    ws.write("/data/file.txt", b"world")

+    assert ws.cat("/data/file.txt") == b"world"

+        assert ws.cat("/data/file.txt") == b"hello"

+    with ws.lock_paths(["/data/a.txt", "/data/b.txt"]):

+            ws.write("/data/a.txt", b"x")

+            ws.write("/data/b.txt", b"x")

+        ws.write("/data/file.txt", b"world")

+    ws.unlock_paths(["/data/file.txt"])

from datetime import datetime, timezone

from mirage.backend.local.memory import MemoryBackend
from mirage.types import ConsistencyPolicy, MountMode
from mirage.workspace import Workspace

def _make_ws(consistency=ConsistencyPolicy.NONE, ttl_seconds=300):
    backend = MemoryBackend()
    backend.put("/file.txt", b"original")
    ws = Workspace(
        {"/data/": backend},
        mode=MountMode.WRITE,
        consistency=consistency,
        ttl_seconds=ttl_seconds,
    )
    return ws, backend

def test_consistency_none_caches_forever():
    ws, backend = _make_ws(ConsistencyPolicy.NONE)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_always_refetches():
    ws, backend = _make_ws(ConsistencyPolicy.ALWAYS)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"updated"

def test_consistency_ttl_fresh_within_window():
    ws, backend = _make_ws(ConsistencyPolicy.TTL, ttl_seconds=300)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_ttl_stale_after_expiry():
    ws, backend = _make_ws(ConsistencyPolicy.TTL, ttl_seconds=10)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    entry = ws._cache.get("/data/file.txt")
    entry.cached_at -= 20
    assert ws.cat("/data/file.txt") == b"updated"

def test_consistency_lazy_unchanged():
    ws, backend = _make_ws(ConsistencyPolicy.LAZY)
    assert ws.cat("/data/file.txt") == b"original"
    assert ws.cat("/data/file.txt") == b"original"

def test_consistency_lazy_changed():
    ws, backend = _make_ws(ConsistencyPolicy.LAZY)
    assert ws.cat("/data/file.txt") == b"original"
    backend._files["/file.txt"] = b"updated-content"
    backend._modified["/file.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/data/file.txt") == b"updated-content"

def test_dirty_file_skips_revalidation():
    ws, backend = _make_ws(ConsistencyPolicy.ALWAYS)
    ws.write("/data/file.txt", b"local-edit")
    backend._files["/file.txt"] = b"remote-change"
    assert ws.cat("/data/file.txt") == b"local-edit"

def test_per_mount_consistency():
    be_none = MemoryBackend()
    be_none.put("/f.txt", b"v1")
    be_always = MemoryBackend()
    be_always.put("/f.txt", b"v1")
    ws = Workspace(
        {
            "/a/": be_none,
            "/b/": (be_always, MountMode.READ, ConsistencyPolicy.ALWAYS),
        },
        mode=MountMode.READ,
        consistency=ConsistencyPolicy.NONE,
    )
    assert ws.cat("/a/f.txt") == b"v1"
    assert ws.cat("/b/f.txt") == b"v1"
    be_none._files["/f.txt"] = b"v2"
    be_none._modified["/f.txt"] = datetime.now(timezone.utc).isoformat()
    be_always._files["/f.txt"] = b"v2"
    be_always._modified["/f.txt"] = datetime.now(timezone.utc).isoformat()
    assert ws.cat("/a/f.txt") == b"v1"
    assert ws.cat("/b/f.txt") == b"v2"


Bash

git rm tests/workspace/test_write_ops.py && git rm tests/workspace/test_lock.py

Remove merged workspace test files


rm 'tests/workspace/test_write_ops.py'
rm 'tests/workspace/test_lock.py'


TodoWrite

Create 15 new test directories (no __init__.py)

Execute 15 pure git mv moves

Execute 3 renames (git mv)

Split test_configs.py into object_storage tests

Split test_default.py into fuse/backend tests

Split test_format_ops.py into csv and parquet tests

Split server/test_registry.py into commands/custom + server/constants

Merge types test files into tests/test_types.py

Merge workspace test files into tests/workspace/test_init.py

Remove existing __init__.py files from tests/ and cleanup

Removing __init__.py files and cleaning up

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

Create 15 new test directories (no __init__.py)

Execute 15 pure git mv moves

Execute 3 renames (git mv)

Split test_configs.py into object_storage tests

Split test_default.py into fuse/backend tests

Split test_format_ops.py into csv and parquet tests

Split server/test_registry.py into commands/custom + server/constants

Merge types test files into tests/test_types.py

Merge workspace test files into tests/workspace/test_init.py

Remove existing __init__.py files from tests/ and cleanup

Run tests and pre-commit to verify

Execute 15 pure git mv moves

Execute 3 renames (git mv)

Run tests and pre-commit to verify


Now remove all `__init__.py` files from the test directories:


/Users/agent/strukto/mirage/tests/__init__.py
/Users/agent/strukto/mirage/tests/cli/__init__.py
/Users/agent/strukto/mirage/tests/commands/__init__.py
/Users/agent/strukto/mirage/tests/server/__init__.py
/Users/agent/strukto/mirage/tests/workspace/__init__.py
/Users/agent/strukto/mirage/tests/workspace/cache/__init__.py
/Users/agent/strukto/mirage/tests/backend/__init__.py
/Users/agent/strukto/mirage/tests/runtime/__init__.py
/Users/agent/strukto/mirage/tests/observe/__init__.py
/Users/agent/strukto/mirage/tests/fuse/__init__.py
/Users/agent/strukto/mirage/tests/integration/__init__.py


Bash

git rm tests/__init__.py tests/cli/__init__.py tests/commands/__init__.py tests/server/__init__.py tests/workspace/__init__.py tests/workspace/cache/__init__.py tests/backend/__init__.py tests/runtime/__init__.py tests/observe/__init__.py tests/fuse/__init__.py tests/integration/__init__.py

Remove all __init__.py files from test directories


rm 'tests/__init__.py'
rm 'tests/backend/__init__.py'
rm 'tests/cli/__init__.py'
rm 'tests/commands/__init__.py'
rm 'tests/fuse/__init__.py'
rm 'tests/integration/__init__.py'
rm 'tests/observe/__init__.py'
rm 'tests/runtime/__init__.py'
rm 'tests/server/__init__.py'
rm 'tests/workspace/__init__.py'
rm 'tests/workspace/cache/__init__.py'


TodoWrite

Create 15 new test directories (no __init__.py)

Execute 15 pure git mv moves

Execute 3 renames (git mv)

Split test_configs.py into object_storage tests

Split test_default.py into fuse/backend tests

Split test_format_ops.py into csv and parquet tests

Split server/test_registry.py into commands/custom + server/constants

Merge types test files into tests/test_types.py

Merge workspace test files into tests/workspace/test_init.py

Remove existing __init__.py files from tests/ and cleanup

Removing __init__.py files and cleaning up

Run tests and pre-commit to verify


Execute 15 pure git mv moves

Execute 3 renames (git mv)

Run tests and pre-commit to verify

Execute 15 pure git mv moves

Execute 3 renames (git mv)

Run tests and pre-commit to verify


Bash

Run tests to verify reorganization


  /Users/agent/strukto/mirage/tests/server/fastapi/test_app.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
_____________ ERROR collecting tests/server/service/test_local.py ______________
import file mismatch:
imported module 'test_local' has this __file__ attribute:
  /Users/agent/strukto/mirage/tests/backend/local/test_local.py
which is not the same as the test file we want to collect:
  /Users/agent/strukto/mirage/tests/server/service/test_local.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
________________ ERROR collecting tests/workspace/test_init.py _________________
import file mismatch:
imported module 'test_init' has this __file__ attribute:
  /Users/agent/strukto/mirage/tests/commands/test_init.py
which is not the same as the test file we want to collect:
  /Users/agent/strukto/mirage/tests/workspace/test_init.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
=========================== short test summary info ============================
ERROR tests/commands/remote/test_client.py
ERROR tests/fuse/backend/local/test_memory.py
ERROR tests/fuse/test_loader.py
ERROR tests/fuse/test_registry.py
ERROR tests/runtime/accessors/test_local.py
ERROR tests/runtime/test_local.py
ERROR tests/runtime/test_memory.py
ERROR tests/runtime/test_remote.py
ERROR tests/server/fastapi/test_app.py
ERROR tests/server/service/test_local.py
ERROR tests/workspace/test_init.py
!!!!!!!!!!!!!!!!!!! Interrupted: 11 errors during collection !!!!!!!!!!!!!!!!!!!
11 errors in 0.65s


The issue is that without `__init__.py` files, pytest can't distinguish test modules with the same name (e.g., `test_local.py` in different directories). We need to either:

1. Keep `__init__.py` files (the user doesn't want this)
2. Use `rootdir` configuration or `importmode` in pytest config

Let me check the pytest configuration and see if we can use `importmode = importlib` which supports duplicate test names without `__init__.py`.

Also there are `__pycache__` issues. Let me clean those first, then configure pytest properly.


The duplicate test file names (e.g., `test_local.py` in multiple directories) cause import collisions without `__init__.py`. The fix is to use pytest's `importlib` import mode. Let me clean caches and update the config:


find tests -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null; true

Clean up __pycache__ directories in tests/


    51→[tool.pytest.ini_options]
    52→testpaths = ["tests"]
    53→addopts = "--cov=mirage --cov-report=term-missing -q"
    54→

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=mirage --cov-report=term-missing -q"


Edit

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=mirage --cov-report=term-missing -q"

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=mirage --cov-report=term-missing -q --import-mode=importlib"


The file /Users/agent/strukto/mirage/pyproject.toml has been updated successfully.

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=mirage --cov-report=term-missing -q --import-mode=importlib"

[project]
name = "mirage"
version = "0.0.1"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "fastapi>=0.135.1",
    "grpcio>=1.62.0",
    "httpx>=0.28.1",
    "pre-commit",
    "pytest",
    "python-dotenv>=1.2.2",
    "pyyaml>=6.0.3",
    "typer>=0.12.0",
    "uvicorn[standard]>=0.41.0",
]

[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"

[project.scripts]
mirage = "mirage.cli.app:main"

[tool.setuptools.packages.find]
include = ["mirage*"]

[project.optional-dependencies]
s3      = ["boto3>=1.34"]
r2      = ["boto3>=1.34"]
http    = []
fuse    = ["fusepy>=3.0.1"]
parquet = [
    "pandas>=3.0.1",
    "pyarrow>=15.0",
]
all     = ["boto3>=1.34", "fusepy>=3.0.1", "pyarrow>=15.0"]

[dependency-groups]
dev = [
    "grpcio>=1.78.0",
    "grpcio-tools>=1.78.0",
    "pytest-cov>=7.0.0",
    "pytest-httpx>=0.36.0",
]

[tool.flake8]
exclude = ["mirage/commands/remote/grpc/proto/command_pb2.py", "mirage/commands/remote/grpc/proto/command_pb2_grpc.py"]

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=mirage --cov-report=term-missing -q"

-addopts = "--cov=mirage --cov-report=term-missing -q"

+addopts = "--cov=mirage --cov-report=term-missing -q --import-mode=importlib"


Bash

Run tests with importlib mode


mirage/server/fastapi/routers/__init__.py                   0      0   100%
mirage/server/fastapi/routers/command_router.py            65      2    97%   25, 59
mirage/server/fastapi/routers/io_router.py                110     33    70%   22-25, 35-38, 59-60, 69-70, 79-82, 91-92, 98-105, 113-116, 125-126, 136
mirage/server/fastapi/routers/workspace_router.py          60     10    83%   23-26, 83-88
mirage/server/routes.py                                    27      0   100%
mirage/server/service/__init__.py                           0      0   100%
mirage/server/service/base.py                               7      0   100%
mirage/server/service/cache.py                             24      1    96%   22
mirage/server/service/local.py                            260     15    94%   55, 91-92, 106, 123, 204, 224-226, 337-339, 371, 388-389
mirage/types.py                                            91      0   100%
mirage/workspace/__init__.py                              241     11    95%   88, 170, 219-220, 223-224, 272-275, 333
mirage/workspace/cache/__init__.py                          3      0   100%
mirage/workspace/cache/entry.py                             7      0   100%
mirage/workspace/cache/policy.py                           26      3    88%   24-25, 37
mirage/workspace/cache/store.py                            45      1    98%   14
mirage/workspace/mount.py                                  34      1    97%   63
mirage/workspace/sync.py                                   56      0   100%
-------------------------------------------------------------------------------------
TOTAL                                                    4704   1193    75%
641 passed, 1 skipped, 2 warnings in 3.10s


All 641 tests pass. Now let me run pre-commit:


Run pre-commit hooks


Exit code 1
No commits to master.....................................................Failed
- hook id: no-commit-to-branch
- exit code: 1
End-of-file fixer........................................................Passed
mixed-line-ending........................................................Passed
Remove trailing whitespaces..............................................Passed
Check toml...............................................................Passed
Check yaml...............................................................Passed
Lint yaml................................................................Passed
Upgrade Python syntax....................................................Passed
Remove unused imports and variables......................................Passed
Format code..............................................................Passed
Sort imports.............................................................Passed
Check PEP8...............................................................Passed
Ruff formatting..........................................................Passed
Format Markdown..........................................................Passed
Check Sphinx.........................................(no files to check)Skipped

No commits to master.....................................................Failed
- hook id: no-commit-to-branch
- exit code: 1
End-of-file fixer........................................................Passed
mixed-line-ending........................................................Passed
Remove trailing whitespaces..............................................Passed
Check toml...............................................................Passed
Check yaml...............................................................Passed
Lint yaml................................................................Passed
Upgrade Python syntax....................................................Passed
Remove unused imports and variables......................................Passed
Format code..............................................................Passed
Sort imports.............................................................Passed
Check PEP8...............................................................Passed
Ruff formatting..........................................................Passed
Format Markdown..........................................................Passed
Check Sphinx.........................................(no files to check)Skipped


All checks pass (the only "failure" is the `no-commit-to-branch` hook which prevents committing directly to master — that's expected since we're on `main`).


TodoWrite

Create 15 new test directories (no __init__.py)

Execute 15 pure git mv moves

Execute 3 renames (git mv)

Split test_configs.py into object_storage tests

Split test_default.py into fuse/backend tests

Split test_format_ops.py into csv and parquet tests

Split server/test_registry.py into commands/custom + server/constants

Merge types test files into tests/test_types.py

Merge workspace test files into tests/workspace/test_init.py

Remove existing __init__.py files from tests/ and cleanup

Removing __init__.py files and cleaning up

Run tests and pre-commit to verify


All done. Here's a summary of what was done:

**Test reorganization complete** — 641 tests pass, all lint checks pass.

Changes:
- **15 moves** — test files relocated to mirror source structure (e.g., `tests/backend/test_memory.py` → `tests/backend/local/test_memory.py`)
- **3 renames** — `test_service.py` → `service/test_local.py`, `test_runtime.py` → `test_base.py`, `test_accessors.py` → `accessors/test_local.py`
- **4 splits** — `test_configs.py` → `object_storage/test_s3.py` + `test_r2.py`; `test_default.py` → `fuse/backend/test_default.py` + `fuse/backend/local/test_memory.py`; `test_format_ops.py` → `filetype/test_csv.py` + `test_parquet.py`; `server/test_registry.py` → `commands/custom/test_registry.py` + `server/test_constants.py`
- **2 merges** — `tests/types/test_types.py` into `tests/test_types.py`; `test_write_ops.py` + `test_lock.py` into `workspace/test_init.py`
- **Removed all `__init__.py`** from test directories
- **Added `--import-mode=importlib`** to pytest config to support duplicate test file names without `__init__.py`


why we have workspace init contaniing a lot of functions and classes? refactor
init only contains import etc

and also the test need to be changed

also why ConsistencyPolicy can be None? what's the default ConsistencyPolicy?


Good questions. Let me explore the workspace module and understand the current state before planning.


Entered plan mode. You should now focus on exploring the codebase and designing an implementation approach.

In plan mode, you should:
1. Thoroughly explore the codebase to understand existing patterns
2. Identify similar features and architectural approaches
3. Consider multiple approaches and their trade-offs
4. Use AskUserQuestion if you need to clarify the approach
5. Design a concrete implementation strategy
6. When ready, use ExitPlanMode to present your plan for approval

Remember: DO NOT write or edit any files yet. This is a read-only exploration and planning phase.


Agent

Explore workspace __init__.py

Prompt:
Very thoroughly explore the workspace module. I need to understand:

1. Read `mirage/workspace/__init__.py` completely - what classes, functions, and logic does it contain?
2. Read `mirage/workspace/mount.py`, `mirage/workspace/sync.py` completely
3. Read `mirage/workspace/cache/` files (entry.py, policy.py, store.py)
4. Find all imports of `from mirage.workspace import ...` or `from mirage.workspace` across the entire codebase to understand what's used from __init__.py
5. Read `mirage/types.py` to understand ConsistencyPolicy and its usage

I need to understand:
- What's in workspace/__init__.py that should be extracted
- What ConsistencyPolicy values exist and how they're used
- What the Workspace class contains and how to split it
- All external consumers of the workspace module


Here is a complete, thorough analysis of the workspace module:

---

## 1. `mirage/workspace/__init__.py` - Complete Analysis

**Imports (internal dependencies):**
- `mirage.backend.base.BaseBackend`
- `mirage.backend.filetype.guess_type`
- `mirage.types`: `ConsistencyPolicy`, `FileStat`, `Inode`, `MountMode`, `SyncPolicy`, `SyncResult`, `TransferStats`
- `mirage.workspace.cache.LRUCache`
- `mirage.workspace.cache.policy.is_fresh`
- `mirage.workspace.mount.MountRegistry`
- `mirage.workspace.sync.DirtyTracker`

**Classes defined:**

**`_PathLock`** (private context manager, lines 15-25):
- Wraps a list of locked paths in a context manager
- `__exit__` calls `ws.unlock_paths(paths)`

**`Workspace`** (lines 28-368) - the sole public export:

Constructor parameters:
- `backends: dict[str, BaseBackend | tuple]` — prefix-to-backend map; tuples can be `(backend, mode)` or `(backend, mode, consistency)`
- `cache_limit: str | int = "512MB"`
- `sync_policy: SyncPolicy = SyncPolicy.STAGED`
- `mode: MountMode = MountMode.READ`
- `consistency: ConsistencyPolicy = ConsistencyPolicy.NONE`
- `ttl_seconds: int = 300`

Internal state:
- `_registry: MountRegistry` — handles prefix routing
- `_cache: LRUCache` — in-memory LRU cache
- `_tracker: DirtyTracker` — tracks dirty/staged writes
- `_sync_policy: SyncPolicy`
- `_consistency: ConsistencyPolicy`
- `_ttl_seconds: int`
- `_locked_paths: set[str]`
- `_transfer_stats: dict[str, TransferStats]`
- `_created_at: float`

Method groups:

**Mount management:**
- `mount(prefix, backend, mode, consistency)` — delegates to `_registry.mount()`
- `backend(path) -> BaseBackend` — resolves path to backend

**Read ops:**
- `cat(path) -> bytes` — cache-aware read; checks dirty tracker first, then LRU cache with consistency validation, falls back to backend
- `ls(path) -> list[str]` — merges backend listing with staged (dirty) writes; respects staged deletes
- `ls_l(path) -> list[FileStat]` — delegates directly to backend
- `stat(path) -> FileStat` — uses tracker for staged writes, else backend
- `exists(path) -> bool` — wraps `stat()` catching `FileNotFoundError`/`ValueError`
- `head(path, lines) -> bytes` — delegates to backend
- `tail(path, lines) -> bytes` — delegates to backend
- `read(path, offset, size) -> bytes` — slice read; uses cache for dirty files, else backend
- `grep(pattern, path) -> list[str]` — delegates to backend

**Write ops:**
- `write(path, data)` — caches data, marks dirty in tracker; if `IMMEDIATE` policy, also calls `backend.put()` and clears dirty flag
- `mkdir(path)` — delegates to backend
- `rm(path)` — deletes from backend unless staged-only write; invalidates cache, marks tracker deleted
- `mv(src, dst)` — handles staged-only moves in cache/tracker; same-backend rename via `backend.rename()`; cross-backend via cat+put+unlink
- `cp(src, dst)` — reads via `cat()`, writes via `backend.put()`

**Sync lifecycle:**
- `sync(path=None) -> list[SyncResult]` — flushes all (or one) dirty inodes to their backends via `_tracker.on_sync()`
- `status() -> list[Inode]` — all tracked inodes
- `reset()` — clears tracker and cache

**Path locking:**
- `lock_paths(paths) -> _PathLock` — marks paths locked; returns context manager
- `unlock_paths(paths)` — removes paths from locked set
- `_lock_path(path)` — internal single-path lock
- `_assert_not_locked(path)` — raises `PermissionError` if locked

**Private helpers:**
- `_mount_prefix(path) -> str` — returns the mount prefix string for a path
- `_effective_consistency(path) -> ConsistencyPolicy` — returns per-mount consistency or workspace default
- `_assert_writable(path)` — raises `PermissionError` if mount mode is `READ`
- `_record_transfer(path, op, nbytes, duration_ms, cache_hit)` — accumulates `TransferStats` per mount prefix

---

## 2. `mirage/workspace/mount.py`

**`Mount`** (dataclass-like class):
- Fields: `prefix: str`, `backend: BaseBackend`, `mode: MountMode`, `consistency: ConsistencyPolicy | None`
- Normalizes prefix to always have form `"/stripped/"` (or `"/"` for root)

**`MountRegistry`**:
- Maintains `_mounts: list[Mount]` sorted by prefix length descending (longest-prefix-match)
- `mount(prefix, backend, mode, consistency)` — adds a mount, rejects duplicates, re-sorts
- `resolve(path) -> (backend, backend_path, mode)` — strips the mount prefix from the path, returns backend + stripped path
- `mount_for(path) -> Mount` — returns full `Mount` object for a path
- `mounts() -> list[Mount]` — returns copy of mount list

---

## 3. `mirage/workspace/sync.py`

**`DirtyTracker`**:
- Maintains `_inodes: dict[str, Inode]`
- `on_read(path, data, mount)` — creates or updates inode; sets `dirty=False`, updates checksum and `remote_checksum` to match
- `on_write(path, data, mount)` — creates or updates inode; sets `dirty=True`, updates `checksum`, leaves `remote_checksum` as-is (or `None` for new files)
- `on_delete(path)` — sets `dirty=True`, `checksum=None`, `size=None`
- `on_sync(virtual_path, backend_path, data, backend) -> SyncResult` — uploads (or unlinks for deletions); on success clears `dirty`, updates `remote_checksum`; returns `SyncResult` with op=`"create"/"modify"/"delete"`, status=`"ok"/"error"`
- `dirty() -> list[Inode]` — all inodes where `dirty=True`
- `status() -> list[Inode]` — all tracked inodes
- `get(path) -> Inode | None`
- `reset()` — clears all inodes

**`_md5(data) -> str`** (module-level private): MD5 hex digest for checksums

---

## 4. `mirage/workspace/cache/` files

**`entry.py`** — `CacheEntry(BaseModel)`:
- `data: bytes`
- `cached_at: float` (monotonic timestamp)
- `etag: str | None`
- `modified: str | None`
- `size: int | None`

**`policy.py`**:
- `is_fresh(entry, consistency, ttl_seconds, backend, backend_path) -> (bool, FileStat | None)`:
  - `NONE` → always fresh (returns `True, None`)
  - `ALWAYS` → never fresh (returns `False, None`)
  - `TTL` → fresh if within ttl window; else falls through to metadata check
  - `LAZY` (implicit else) → falls through to metadata check using `backend.stat()`
  - Metadata check does `backend.stat()` and delegates to `metadata_matches()`
- `metadata_matches(entry, remote) -> bool`: prefers etag, then modified timestamp, then size

**`store.py`** — `LRUCache`:
- `_parse_limit(limit)` — parses "512MB", "1GB", etc. to bytes
- `get(path) -> CacheEntry | None` — moves to end (LRU touch)
- `put(path, data, etag, modified, size)` — stores new entry, evicts oldest if over limit
- `invalidate(path)` — removes single entry
- `clear()` — wipes all entries
- Properties: `size`, `limit`
- `_evict()` — pops from front (LRU eviction) until under limit

**`cache/__init__.py`** — exports `CacheEntry` and `LRUCache`

---

## 5. `ConsistencyPolicy` in `mirage/types.py`

```python
class ConsistencyPolicy(str, Enum):
    NONE = "none"      # Always serve from cache; never validate against remote
    LAZY = "lazy"      # Validate cache by comparing metadata (etag/modified/size) with backend.stat()
    TTL = "ttl"        # Serve from cache within TTL window; after expiry, fall through to metadata check
    ALWAYS = "always"  # Never use cache; always re-fetch from backend
```

Used in: `workspace/__init__.py` (constructor, `_effective_consistency`), `mount.py` (per-mount override), `cache/policy.py` (the actual staleness logic).

---

## 6. All External Consumers of `from mirage.workspace import ...`

The only publicly exported symbol from `mirage/workspace/__init__.py` is `Workspace`. Every consumer imports exactly that:

**Production code:**
| File | Usage context |
|---|---|
| `/Users/agent/strukto/mirage/mirage/__init__.py` | Top-level re-export in `__all__` |
| `/Users/agent/strukto/mirage/mirage/runtime/base.py` | `Runtime.__init__` takes `workspace: Workspace` |
| `/Users/agent/strukto/mirage/mirage/runtime/local.py` | Constructs/uses `Workspace` |
| `/Users/agent/strukto/mirage/mirage/runtime/remote.py` | Constructs/uses `Workspace` |
| `/Users/agent/strukto/mirage/mirage/runtime/memory.py` | Constructs/uses `Workspace` |
| `/Users/agent/strukto/mirage/mirage/runtime/accessors/local.py` | Wraps `Workspace` in accessor |
| `/Users/agent/strukto/mirage/mirage/fuse/fs.py` | FUSE filesystem backed by `Workspace` |
| `/Users/agent/strukto/mirage/mirage/fuse/base.py` | TYPE_CHECKING import |
| `/Users/agent/strukto/mirage/mirage/fuse/registry.py` | TYPE_CHECKING import; accesses `ws._registry` |
| `/Users/agent/strukto/mirage/mirage/fuse/remote/remote.py` | TYPE_CHECKING import |
| `/Users/agent/strukto/mirage/mirage/fuse/filetype/parquet.py` | TYPE_CHECKING import |
| `/Users/agent/strukto/mirage/mirage/fuse/filetype/csv.py` | TYPE_CHECKING import |
| `/Users/agent/strukto/mirage/mirage/commands/builtin/read.py` | Command handlers take `Workspace` |
| `/Users/agent/strukto/mirage/mirage/commands/builtin/write.py` | Command handlers take `Workspace` |
| `/Users/agent/strukto/mirage/mirage/commands/builtin/nav.py` | Command handlers take `Workspace` |
| `/Users/agent/strukto/mirage/mirage/commands/builtin/search.py` | Command handlers take `Workspace` |
| `/Users/agent/strukto/mirage/mirage/commands/builtin/handlers.py` | Command handlers take `Workspace` |
| `/Users/agent/strukto/mirage/mirage/commands/custom/executor.py` | Command executor takes `Workspace` |
| `/Users/agent/strukto/mirage/mirage/commands/custom/arg_resolver.py` | Arg resolution takes `Workspace` |
| `/Users/agent/strukto/mirage/mirage/commands/custom/registry.py` | `CommandRegistry.run(ws, ...)` |
| `/Users/agent/strukto/mirage/mirage/commands/remote/fastapi/app.py` | FastAPI app takes `Workspace` |
| `/Users/agent/strukto/mirage/mirage/commands/remote/grpc/server.py` | gRPC server takes `Workspace` |
| `/Users/agent/strukto/mirage/mirage/server/service/local.py` | Local service constructs `Workspace` |
| `/Users/agent/strukto/mirage/mirage/cli/commands/workspace.py` | CLI workspace command |
| `/Users/agent/strukto/mirage/mirage/cli/commands/command.py` | CLI command entrypoint |
| `/Users/agent/strukto/mirage/mirage/observe/inspector.py` | TYPE_CHECKING import |

**Direct sub-module imports** (not just `Workspace`):
- `from mirage.workspace.mount import MountRegistry` — only in `tests/workspace/test_mount.py`
- `from mirage.workspace.sync import DirtyTracker` — only in `tests/workspace/test_sync.py`
- `from mirage.workspace.cache.entry import CacheEntry` — `tests/workspace/test_cache_entry.py`, `tests/workspace/cache/test_policy.py`
- `from mirage.workspace.cache.store import LRUCache` — `tests/workspace/cache/test_store.py`
- `from mirage.workspace.cache.policy import metadata_matches` — `tests/workspace/cache/test_policy.py`

**Note on internal private access:** `mirage/fuse/registry.py` accesses `ws._registry` directly (not via the public `Workspace` API). `mirage/runtime/base.py` also accesses `self.ws._registry._mounts` in `_whoami()`.

---

## Summary: What's in `__init__.py` That Could Be Extracted

The `Workspace.__init__.py` is a single monolithic class file doing five distinct things:
1. **Mount initialization** — parsing the `backends` dict, delegating to `MountRegistry`
2. **Read operations** — `cat`, `ls`, `ls_l`, `stat`, `exists`, `head`, `tail`, `read`, `grep` with cache/consistency logic
3. **Write operations** — `write`, `mkdir`, `rm`, `mv`, `cp` with dirty tracking and sync policy
4. **Sync lifecycle** — `sync`, `status`, `reset`
5. **Path locking** — `lock_paths`, `unlock_paths`, `_lock_path`, `_assert_not_locked`
6. **Private helpers** — `_mount_prefix`, `_effective_consistency`, `_assert_writable`, `_record_transfer`

The `_PathLock` helper class is small enough it could live alongside locking logic in a dedicated `lock.py` module.

agentId: a07721eac7a463825 (for resuming to continue this agent's work if needed)
<usage>total_tokens: 33681
tool_uses: 15
duration_ms: 82360</usage>

Prompt:
Very thoroughly explore the workspace module. I need to understand:

1. Read `mirage/workspace/__init__.py` completely - what classes, functions, and logic does it contain?
2. Read `mirage/workspace/mount.py`, `mirage/workspace/sync.py` completely
3. Read `mirage/workspace/cache/` files (entry.py, policy.py, store.py)
4. Find all imports of `from mirage.workspace import ...` or `from mirage.workspace` across the entire codebase to understand what's used from __init__.py
5. Read `mirage/types.py` to understand ConsistencyPolicy and its usage

I need to understand:
- What's in workspace/__init__.py that should be extracted
- What ConsistencyPolicy values exist and how they're used
- What the Workspace class contains and how to split it
- All external consumers of the workspace module


Now I have a thorough understanding. Let me think about the refactoring:

1. **Move `Workspace` class from `__init__.py` to `workspace.py`** — the `__init__.py` should only contain imports/re-exports
2. **ConsistencyPolicy default** — currently `ConsistencyPolicy = ConsistencyPolicy.NONE` — the user asks "why can it be None?" But looking at the code, the parameter type is `ConsistencyPolicy` not `ConsistencyPolicy | None`. The `NONE` is a valid enum member meaning "no consistency checking" (always use cache). However in `mount.py`, the `Mount` class has `consistency: ConsistencyPolicy | None = None` where `None` means "inherit workspace default".

So the confusion is that `ConsistencyPolicy.NONE` (the enum value) means "skip validation," while `None` (Python's None) on a mount means "use the workspace's default policy" — two different concepts with overlapping semantics that make the API harder to understand.

For the refactoring, I'd move `Workspace` into its own module at `mirage/workspace/workspace.py` and update `__init__.py` to just re-export it, then rename the test file from `test_init.py` to `test_workspace.py` to match the new structure.

On the naming issue, I'm realizing the user might be confused by both the `NONE` enum value and the fact that `Mount.consistency` can be `None` in Python. I should clarify that renaming `ConsistencyPolicy.NONE` to something like `CACHED` or `OFF` would reduce confusion, while keeping Python's `None` as a way to inherit the workspace default in per-mount configs.

For the main refactor request, the user wants me to move the Workspace class into its own dedicated file and leave `__init__.py` with just imports and re-exports—not to split the class itself. I'll plan out this restructuring and ask for clarification on the ConsistencyPolicy naming since there are a few reasonable options.


Now let me check how other `__init__.py` files in this project are structured (to follow existing patterns):


     1→from mirage.backend.base import BaseBackend
     2→from mirage.backend.local import LocalBackend, MemoryBackend
     3→from mirage.backend.network import ProxyBackend
     4→
     5→__all__ = ["BaseBackend", "LocalBackend", "MemoryBackend", "ProxyBackend"]
     6→

from mirage.backend.base import BaseBackend
from mirage.backend.local import LocalBackend, MemoryBackend
from mirage.backend.network import ProxyBackend

__all__ = ["BaseBackend", "LocalBackend", "MemoryBackend", "ProxyBackend"]


     1→from mirage.runtime.base import Runtime
     2→from mirage.runtime.local import LocalRuntime
     3→from mirage.runtime.memory import MemoryRuntime
     4→from mirage.runtime.remote import RemoteRuntime
     5→
     6→__all__ = ["MemoryRuntime", "LocalRuntime", "RemoteRuntime", "Runtime"]
     7→

from mirage.runtime.base import Runtime
from mirage.runtime.local import LocalRuntime
from mirage.runtime.memory import MemoryRuntime
from mirage.runtime.remote import RemoteRuntime

__all__ = ["MemoryRuntime", "LocalRuntime", "RemoteRuntime", "Runtime"]


     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

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


Good — `backend/__init__.py` and `runtime/__init__.py` both follow the pattern of just re-exporting from submodules. The `workspace/__init__.py` should follow the same pattern.

For `ConsistencyPolicy`, the issue is:
- `Mount.consistency: ConsistencyPolicy | None = None` — Python `None` means "inherit workspace default"
- `ConsistencyPolicy.NONE` — enum value meaning "never revalidate cache"

The naming collision between Python's `None` and `ConsistencyPolicy.NONE` is confusing. Let me ask about this:


AskUserQuestion

Question: ConsistencyPolicy.NONE means 'never revalidate, always serve from cache'. The per-mount consistency field uses Python None to mean 'inherit workspace default'. The confusing part is NONE vs None. Should we rename ConsistencyPolicy.NONE to something clearer?

Choices:
ConsistencyPolicy.CACHED — clearly means 'always serve cached data'
ConsistencyPolicy.OFF — consistency checking is turned off
Keep ConsistencyPolicy.NONE as-is, it's fine

Question: For the per-mount consistency override, should we keep allowing None (meaning 'inherit workspace default') or require an explicit policy on every mount?

Choices:
Mount.consistency: ConsistencyPolicy | None = None — None means inherit workspace default. This is useful.
Every mount must specify its own ConsistencyPolicy, no inheritance


User has answered your questions: "ConsistencyPolicy.NONE means 'never revalidate, always serve from cache'. The per-mount consistency field uses Python None to mean 'inherit workspace default'. The confusing part is NONE vs None. Should we rename ConsistencyPolicy.NONE to something clearer?"="CACHED", "For the per-mount consistency override, should we keep allowing None (meaning 'inherit workspace default') or require an explicit policy on every mount?"="Require explicit". You can now continue with the user's answers in mind.

Question: ConsistencyPolicy.NONE means 'never revalidate, always serve from cache'. The per-mount consistency field uses Python None to mean 'inherit workspace default'. The confusing part is NONE vs None. Should we rename ConsistencyPolicy.NONE to something clearer?

Choices:
ConsistencyPolicy.CACHED — clearly means 'always serve cached data'
ConsistencyPolicy.OFF — consistency checking is turned off
Keep ConsistencyPolicy.NONE as-is, it's fine

Question: For the per-mount consistency override, should we keep allowing None (meaning 'inherit workspace default') or require an explicit policy on every mount?

Choices:
Mount.consistency: ConsistencyPolicy | None = None — None means inherit workspace default. This is useful.
Every mount must specify its own ConsistencyPolicy, no inheritance


OK so the user wants:
1. Rename `ConsistencyPolicy.NONE` → `ConsistencyPolicy.CACHED`
2. Every mount must have an explicit `ConsistencyPolicy` — no `None` for "inherit"
3. Move `Workspace` class from `__init__.py` to its own file (e.g., `workspace.py`)
4. `__init__.py` should only contain imports/re-exports
5. Test file needs to be updated

Let me think about point 2 — if every mount has an explicit consistency policy, then:
- `Mount.consistency: ConsistencyPolicy` (not `| None`)

- The default would be `ConsistencyPolicy.CACHED`
- `Workspace.__init__` still accepts a `consistency` parameter that serves as the default for mounts created through the `backends` dict, but `Mount` itself always holds an actual `ConsistencyPolicy` value
- This means `_effective_consistency` becomes redundant and can be removed

Now I need to trace through the codebase to see where `_effective_consistency` is being called and understand the full scope of changes required.

Actually, thinking about this more carefully — the user wants explicit consistency on every mount, but most mounts come from the `backends` dict in `Workspace.__init__`, so they won't have it specified. The solution is that `Mount.consistency` never stores `None` and always has a concrete value, with the workspace-level default being applied when mounts are created.

So the plan is: rename `ConsistencyPolicy.NONE` to `ConsistencyPolicy.CACHED`, make `Mount.consistency` a non-optional `ConsistencyPolicy` field with that as the default, remove the `_effective_consistency` method entirely, and then move the `Workspace` class to a different location.

Now I need to complete the refactoring steps: move `Workspace` to its own module, update the imports in `workspace/__init__.py`, rename the test file, and fix all the references throughout the codebase.


Good. So:
1. Rename `ConsistencyPolicy.NONE` → `ConsistencyPolicy.CACHED`
2. `Mount.consistency` always has an explicit `ConsistencyPolicy` (never `None`)
3. Move `Workspace` class from `__init__.py` to `workspace.py`
4. `__init__.py` becomes just re-exports

Let me verify the impact scope and write the plan.


mirage/workspace/__init__.py:55:        consistency: ConsistencyPolicy = ConsistencyPolicy.NONE,
mirage/workspace/cache/policy.py:15:    if consistency == ConsistencyPolicy.NONE:
tests/workspace/test_init.py:10:def _make_consistency_ws(consistency=ConsistencyPolicy.NONE, ttl_seconds=300):
tests/workspace/test_init.py:29:    ws, backend = _make_consistency_ws(ConsistencyPolicy.NONE)
tests/workspace/test_init.py:94:        consistency=ConsistencyPolicy.NONE,
mirage/cli/commands/workspace.py:107:    consistency: ConsistencyPolicy = typer.Option(ConsistencyPolicy.NONE),
mirage/cli/commands/command.py:57:                        value, default_mode, ConsistencyPolicy.NONE)
mirage/cli/commands/command.py:69:                mounts[prefix] = (backend_obj, m, ConsistencyPolicy.NONE)
mirage/cli/commands/command.py:77:                                                 ConsistencyPolicy.NONE)
mirage/cli/commands/command.py:78:        mounts[prefix] = (backend_obj, m, ConsistencyPolicy.NONE)
mirage/cli/config.py:106:    default_consistency: ConsistencyPolicy = ConsistencyPolicy.NONE,
tests/cli/test_app.py:36:        consistency=ConsistencyPolicy.NONE,
tests/cli/test_config.py:133:    assert consistency == ConsistencyPolicy.NONE
mirage/server/fastapi/config.py:18:    consistency: ConsistencyPolicy = ConsistencyPolicy.NONE


mirage/workspace/mount.py:11:                 consistency: ConsistencyPolicy | None = None) -> None:
mirage/workspace/mount.py:35:              consistency: ConsistencyPolicy | None = None) -> None:
mirage/cli/commands/workspace.py:104:                       "consistency, @read|@write|@exec for mode.")] = None,
mirage/cli/commands/workspace.py:162:        mount_consistency = None
mirage/workspace/__init__.py:73:                mount_consistency = None
mirage/workspace/__init__.py:77:                mount_consistency = None
mirage/workspace/__init__.py:87:              consistency: ConsistencyPolicy | None = None) -> None:
mirage/workspace/__init__.py:111:            consistency = self._effective_consistency(path)
mirage/workspace/__init__.py:335:    def _effective_consistency(self, path: str) -> ConsistencyPolicy:
mirage/workspace/__init__.py:337:        if mount.consistency is not None:
mirage/server/service/local.py:98:                                     if value.get("consistency") else None)
mirage/server/service/local.py:100:                                or mount_consistency is not None)
mirage/server/service/local.py:101:                if has_override and mount_consistency is not None:
mirage/server/fastapi/config.py:11:    consistency: ConsistencyPolicy | None = None