feat(conductor): Implement configurable paths and mark track 'Conductor Path Configuration' as complete

This commit is contained in:
2026-03-06 16:43:11 -05:00
parent 1294104f7f
commit f580165c5b
17 changed files with 284 additions and 58 deletions

View File

@@ -54,6 +54,16 @@ class VerificationLogger:
f.write(f"{status} {self.test_name} ({result_msg})\n\n")
print(f"[FINAL] {self.test_name}: {status} - {result_msg}")
@pytest.fixture(autouse=True)
def reset_paths() -> Generator[None, None, None]:
"""
Autouse fixture that resets the paths global state before each test.
"""
from src import paths
paths.reset_resolved()
yield
paths.reset_resolved()
@pytest.fixture(autouse=True)
def reset_ai_client() -> Generator[None, None, None]:
"""

View File

@@ -15,6 +15,7 @@ def test_mcp_tool_call_is_dispatched(app_instance: App) -> None:
mock_fc.args = {"file_path": "test.txt"}
# 2. Construct the mock AI response (Gemini format)
mock_response_with_tool = MagicMock()
mock_response_with_tool.text = ""
mock_part = MagicMock()
mock_part.text = ""
mock_part.function_call = mock_fc

View File

@@ -48,6 +48,8 @@ def app_instance(mock_config: Path, mock_project: Path, monkeypatch: pytest.Monk
app.ui_state = MagicMock()
app.ui_files_base_dir = "."
app.files = []
app.controller = MagicMock()
app.controller.event_queue = MagicMock()
# Since we bypassed __init__, we need to bind the method manually
# but python allows calling it directly.
return app

View File

@@ -11,20 +11,19 @@ def e2e_setup(tmp_path: Path, monkeypatch: Any) -> Any:
# Ensure closed before starting
session_logger.close_session()
monkeypatch.setattr(session_logger, "_comms_fh", None)
# Mock _LOG_DIR and _SCRIPTS_DIR in session_logger
original_log_dir = session_logger._LOG_DIR
session_logger._LOG_DIR = tmp_path / "logs"
monkeypatch.setattr(session_logger, "_LOG_DIR", tmp_path / "logs")
session_logger._LOG_DIR.mkdir(parents=True, exist_ok=True)
original_scripts_dir = session_logger._SCRIPTS_DIR
session_logger._SCRIPTS_DIR = tmp_path / "scripts" / "generated"
monkeypatch.setattr(session_logger, "_SCRIPTS_DIR", tmp_path / "scripts" / "generated")
session_logger._SCRIPTS_DIR.mkdir(parents=True, exist_ok=True)
logs_dir = tmp_path / "logs"
scripts_dir = tmp_path / "scripts" / "generated"
logs_dir.mkdir(parents=True, exist_ok=True)
scripts_dir.mkdir(parents=True, exist_ok=True)
from src import paths
monkeypatch.setattr(paths, "get_logs_dir", lambda: logs_dir)
monkeypatch.setattr(paths, "get_scripts_dir", lambda: scripts_dir)
yield tmp_path
# Cleanup
session_logger.close_session()
session_logger._LOG_DIR = original_log_dir
session_logger._SCRIPTS_DIR = original_scripts_dir
def test_logging_e2e(e2e_setup: Any) -> None:
tmp_path = e2e_setup

View File

@@ -28,8 +28,11 @@ class TestOrchestratorPMHistory(unittest.TestCase):
with open(track_path / "spec.md", "w") as f:
f.write(spec_content)
@patch('src.orchestrator_pm.CONDUCTOR_PATH', Path("test_conductor"))
def test_get_track_history_summary(self) -> None:
@patch('src.paths.get_archive_dir')
@patch('src.paths.get_tracks_dir')
def test_get_track_history_summary(self, mock_get_tracks: MagicMock, mock_get_archive: MagicMock) -> None:
mock_get_archive.return_value = self.archive_dir
mock_get_tracks.return_value = self.tracks_dir
self.create_track(self.archive_dir, "track_001", "Initial Setup", "completed", "Setting up the project structure.")
self.create_track(self.tracks_dir, "track_002", "Feature A", "in_progress", "Implementing Feature A.")
summary = orchestrator_pm.get_track_history_summary()
@@ -40,8 +43,11 @@ class TestOrchestratorPMHistory(unittest.TestCase):
self.assertIn("in_progress", summary)
self.assertIn("Implementing Feature A.", summary)
@patch('src.orchestrator_pm.CONDUCTOR_PATH', Path("test_conductor"))
def test_get_track_history_summary_missing_files(self) -> None:
@patch('src.paths.get_archive_dir')
@patch('src.paths.get_tracks_dir')
def test_get_track_history_summary_missing_files(self, mock_get_tracks: MagicMock, mock_get_archive: MagicMock) -> None:
mock_get_archive.return_value = self.archive_dir
mock_get_tracks.return_value = self.tracks_dir
track_path = self.tracks_dir / "track_003"
track_path.mkdir(exist_ok=True)
with open(track_path / "metadata.json", "w") as f:

67
tests/test_paths.py Normal file
View File

@@ -0,0 +1,67 @@
import os
import pytest
from pathlib import Path
from src import paths
@pytest.fixture(autouse=True)
def reset_paths():
paths.reset_resolved()
yield
paths.reset_resolved()
def test_default_paths():
assert paths.get_conductor_dir() == Path("conductor")
assert paths.get_logs_dir() == Path("logs/sessions")
assert paths.get_scripts_dir() == Path("scripts/generated")
assert paths.get_config_path() == Path("config.toml")
assert paths.get_tracks_dir() == Path("conductor/tracks")
assert paths.get_archive_dir() == Path("conductor/archive")
def test_env_var_overrides(monkeypatch):
monkeypatch.setenv("SLOP_CONDUCTOR_DIR", "custom_conductor")
monkeypatch.setenv("SLOP_LOGS_DIR", "custom_logs")
monkeypatch.setenv("SLOP_SCRIPTS_DIR", "custom_scripts")
assert paths.get_conductor_dir() == Path("custom_conductor")
assert paths.get_logs_dir() == Path("custom_logs")
assert paths.get_scripts_dir() == Path("custom_scripts")
assert paths.get_tracks_dir() == Path("custom_conductor/tracks")
def test_config_overrides(tmp_path, monkeypatch):
config_file = tmp_path / "custom_config.toml"
content = """
[paths]
conductor_dir = "cfg_conductor"
logs_dir = "cfg_logs"
scripts_dir = "cfg_scripts"
"""
config_file.write_text(content)
monkeypatch.setenv("SLOP_CONFIG", str(config_file))
# Need to update the _CONFIG_PATH in paths.py since it's set at import
# Actually, the get_config_path() uses _CONFIG_PATH which is Path(os.environ.get("SLOP_CONFIG", "config.toml"))
# But it's defined at module level. Let's see if we can reload it or if monkeypatching early enough works.
# In src/paths.py: _CONFIG_PATH: Path = Path(os.environ.get("SLOP_CONFIG", "config.toml"))
# This is set when src.paths is first imported.
# For the test to work, we might need to manually set paths._CONFIG_PATH or reload the module.
# paths._CONFIG_PATH = config_file # No longer needed
assert paths.get_conductor_dir() == Path("cfg_conductor")
assert paths.get_logs_dir() == Path("cfg_logs")
assert paths.get_scripts_dir() == Path("cfg_scripts")
def test_precedence(tmp_path, monkeypatch):
config_file = tmp_path / "custom_config.toml"
content = """
[paths]
conductor_dir = "cfg_conductor"
"""
config_file.write_text(content)
monkeypatch.setenv("SLOP_CONFIG", str(config_file))
monkeypatch.setenv("SLOP_CONDUCTOR_DIR", "env_conductor")
# paths._CONFIG_PATH = config_file # No longer needed
# Env var should take precedence over config
assert paths.get_conductor_dir() == Path("env_conductor")

View File

@@ -9,21 +9,19 @@ def temp_logs(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Generator[Path
# Ensure closed before starting
session_logger.close_session()
monkeypatch.setattr(session_logger, "_comms_fh", None)
# Mock _LOG_DIR in session_logger
original_log_dir = session_logger._LOG_DIR
session_logger._LOG_DIR = tmp_path / "logs"
monkeypatch.setattr(session_logger, "_LOG_DIR", tmp_path / "logs")
session_logger._LOG_DIR.mkdir(parents=True, exist_ok=True)
# Mock _SCRIPTS_DIR
original_scripts_dir = session_logger._SCRIPTS_DIR
session_logger._SCRIPTS_DIR = tmp_path / "scripts" / "generated"
monkeypatch.setattr(session_logger, "_SCRIPTS_DIR", tmp_path / "scripts" / "generated")
session_logger._SCRIPTS_DIR.mkdir(parents=True, exist_ok=True)
yield tmp_path / "logs"
log_dir = tmp_path / "logs"
scripts_dir = tmp_path / "scripts" / "generated"
log_dir.mkdir(parents=True, exist_ok=True)
scripts_dir.mkdir(parents=True, exist_ok=True)
from src import paths
monkeypatch.setattr(paths, "get_logs_dir", lambda: log_dir)
monkeypatch.setattr(paths, "get_scripts_dir", lambda: scripts_dir)
yield log_dir
# Cleanup: Close handles if open
session_logger.close_session()
session_logger._LOG_DIR = original_log_dir
session_logger._SCRIPTS_DIR = original_scripts_dir
def test_open_session_creates_subdir_and_registry(temp_logs: Path) -> None: