diff --git a/CLAUDE.md b/CLAUDE.md index f97842d8..148b2858 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -153,12 +153,21 @@ codeframe/ │ ├── stall_detector.py # Synchronous stall detector + StallAction enum + StallDetectedError │ ├── stall_monitor.py # Thread-based stall watchdog with callback │ ├── streaming.py # Real-time output streaming for cf work follow +│ ├── worktrees.py # TaskWorktree, WorktreeRegistry, get_base_branch +│ ├── sandbox/ # Execution isolation abstractions +│ │ ├── context.py # ExecutionContext + IsolationLevel (none/worktree/cloud) +│ │ └── worktree.py # Re-exports worktree symbols from core/worktrees.py │ └── ... ├── adapters/ -│ └── llm/ # LLM provider adapters -│ ├── base.py # Protocol + ModelSelector + Purpose enum -│ ├── anthropic.py # Anthropic Claude provider -│ └── mock.py # Mock provider for testing +│ ├── llm/ # LLM provider adapters +│ │ ├── base.py # Protocol + ModelSelector + Purpose enum +│ │ ├── anthropic.py # Anthropic Claude provider +│ │ └── mock.py # Mock provider for testing +│ └── e2b/ # E2B cloud sandbox adapter (optional — requires `pip install codeframe[cloud]`) +│ ├── __init__.py # Exports E2BAgentAdapter +│ ├── adapter.py # E2BAgentAdapter (engine="cloud") +│ ├── budget.py # Cost tracking utilities +│ └── credential_scanner.py # Pre-upload secrets scanner ├── cli/ │ └── app.py # Typer CLI entry + subcommands ├── ui/ # FastAPI server (Phase 2 - thin adapter over core) @@ -884,6 +893,9 @@ uv run pytest --cov=codeframe --cov-report=html # Required for agent execution ANTHROPIC_API_KEY=sk-ant-... +# Required for cloud sandbox execution (--engine cloud) +E2B_API_KEY=e2b_... + # Optional - Database DATABASE_PATH=./codeframe.db diff --git a/codeframe/core/sandbox/__init__.py b/codeframe/core/sandbox/__init__.py index ccbdd98a..5574ca4b 100644 --- a/codeframe/core/sandbox/__init__.py +++ b/codeframe/core/sandbox/__init__.py @@ -1,7 +1,7 @@ """Execution environment sandbox abstraction. Provides ExecutionContext and IsolationLevel for isolating task execution -from the shared filesystem. +from the shared filesystem, plus worktree registry helpers. """ from codeframe.core.sandbox.context import ( @@ -9,5 +9,19 @@ IsolationLevel, create_execution_context, ) +from codeframe.core.sandbox.worktree import ( + MergeResult, + TaskWorktree, + WorktreeRegistry, + get_base_branch, +) -__all__ = ["ExecutionContext", "IsolationLevel", "create_execution_context"] +__all__ = [ + "ExecutionContext", + "IsolationLevel", + "create_execution_context", + "MergeResult", + "TaskWorktree", + "WorktreeRegistry", + "get_base_branch", +] diff --git a/codeframe/core/sandbox/worktree.py b/codeframe/core/sandbox/worktree.py new file mode 100644 index 00000000..4bc1b097 --- /dev/null +++ b/codeframe/core/sandbox/worktree.py @@ -0,0 +1,20 @@ +"""Worktree registry re-export for the sandbox namespace. + +Re-exports ``TaskWorktree``, ``MergeResult``, ``WorktreeRegistry``, and +``get_base_branch`` from ``codeframe.core.worktrees`` so callers can import +from a single ``codeframe.core.sandbox`` sub-package. +""" + +from codeframe.core.worktrees import ( + MergeResult, + TaskWorktree, + WorktreeRegistry, + get_base_branch, +) + +__all__ = [ + "MergeResult", + "TaskWorktree", + "WorktreeRegistry", + "get_base_branch", +] diff --git a/docs/V2_STRATEGIC_ROADMAP.md b/docs/V2_STRATEGIC_ROADMAP.md index 36dfe67e..340bcf5f 100644 --- a/docs/V2_STRATEGIC_ROADMAP.md +++ b/docs/V2_STRATEGIC_ROADMAP.md @@ -278,15 +278,15 @@ Phase 4 has three parallel tracks: **Motivation**: `cf work batch run --strategy parallel` currently runs concurrent threads on the same filesystem with no isolation. Agents can corrupt each other's work, hit git index locks, or overwrite changes. -**Integration strategy**: `parallel-cc` is a production-grade tool that already solves this with git worktrees + E2B cloud sandboxes. It will be consumed as a dependency initially, then absorbed into CodeFrame as the integration matures. It will not remain a separate independent project long-term. +**Status**: The execution environment layer is complete. Worktree isolation and E2B cloud execution are fully absorbed into CodeFrame's own namespace — no external dependency was ever introduced. -#### Absorption arc +#### Absorption arc (complete ✅) -| Phase | What happens | -|-------|-------------| -| **Dependency** (now → Phase 4) | CodeFrame calls `parallel-cc` CLI/library for worktree + E2B management | -| **Integration** (during Phase 4) | parallel-cc concepts formalized as CodeFrame `ExecutionContext` abstraction | -| **Absorption** (Phase 4 complete) | parallel-cc code moves into `codeframe/core/sandbox/` and `codeframe/adapters/e2b/` | +| Phase | What happened | +|-------|--------------| +| ~~**Dependency**~~ | Skipped — logic was ported directly (parallel-cc never added as a dep) | +| **Integration** (Phase 4) | ✅ `ExecutionContext` abstraction formalized in `core/sandbox/context.py` | +| **Absorption** (Phase 4.B complete) | ✅ Worktree code in `core/worktrees.py` + `core/sandbox/worktree.py`; E2B adapter in `adapters/e2b/` | #### Deliverables @@ -296,9 +296,9 @@ Phase 4 has three parallel tracks: - CLI: `--isolation none | worktree | cloud` 2. **Worktree isolation for parallel batch** (codeframe-c0rx) - - Each parallel task gets its own git worktree via `gtr` (from parallel-cc) - - Atomic session registration prevents race conditions - - Auto-cleanup on task completion + - Each parallel task gets its own git worktree via `TaskWorktree` (`core/worktrees.py`) + - `WorktreeRegistry` provides atomic session registration (prevents race conditions) + - Auto-cleanup on task completion via `ExecutionContext.cleanup()` - Fixes the live filesystem conflict problem in `conductor.py` 3. **`E2BAgentAdapter`** (codeframe-csyd) @@ -307,10 +307,10 @@ Phase 4 has three parallel tracks: - Up to 1-hour autonomous execution with timeout management - Budget tracking per task/batch -4. **parallel-cc absorption** (codeframe-xz0f) - - Port worktree coordination logic to `codeframe/core/sandbox/` - - Port E2B pipeline to `codeframe/adapters/e2b/` - - parallel-cc repo archived once absorption is complete +4. **parallel-cc absorption** (codeframe-xz0f) ✅ + - Worktree coordination lives in `codeframe/core/worktrees.py` and `codeframe/core/sandbox/` + - E2B pipeline lives in `codeframe/adapters/e2b/` + - parallel-cc was never added as a dependency; repo archived --- @@ -346,7 +346,7 @@ Phase 4 has three parallel tracks: | codeframe-la86 | ExecutionContext abstraction | 4.B | HIGH | | codeframe-c0rx | Worktree isolation for parallel batch | 4.B | HIGH | | codeframe-csyd | E2BAgentAdapter (cloud execution) | 4.B | MEDIUM | -| codeframe-xz0f | parallel-cc absorption into CodeFrame | 4.B | MEDIUM | +| codeframe-xz0f | parallel-cc absorption into CodeFrame ✅ | 4.B | MEDIUM | | #310 | Agent roles | 4.C | MEDIUM | | #311 | Conflict detection & resolution | 4.C | MEDIUM | | #312 | Handoff protocols | 4.C | MEDIUM | diff --git a/tests/core/test_sandbox_context.py b/tests/core/test_sandbox_context.py index e9637a6c..4e32b231 100644 --- a/tests/core/test_sandbox_context.py +++ b/tests/core/test_sandbox_context.py @@ -19,6 +19,8 @@ create_execution_context, ) +pytestmark = pytest.mark.v2 + @pytest.fixture def git_repo(tmp_path: Path) -> Path: @@ -153,3 +155,28 @@ class TestCreateExecutionContextCloud: def test_raises_not_implemented(self, tmp_path: Path): with pytest.raises(NotImplementedError, match="E2B"): create_execution_context("task-1", IsolationLevel.CLOUD, tmp_path) + + +class TestSandboxWorktreeReexports: + """sandbox.worktree re-exports resolve correctly (issue #535).""" + + def test_task_worktree_importable_from_sandbox(self): + from codeframe.core.sandbox import TaskWorktree + assert TaskWorktree is not None + + def test_merge_result_importable_from_sandbox(self): + from codeframe.core.sandbox import MergeResult + assert MergeResult is not None + + def test_worktree_registry_importable_from_sandbox(self): + from codeframe.core.sandbox import WorktreeRegistry + assert WorktreeRegistry is not None + + def test_get_base_branch_importable_from_sandbox(self): + from codeframe.core.sandbox import get_base_branch + assert callable(get_base_branch) + + def test_sandbox_worktree_module_importable(self): + from codeframe.core.sandbox import worktree as wt_mod + assert hasattr(wt_mod, "TaskWorktree") + assert hasattr(wt_mod, "WorktreeRegistry")