feat(conductor): Use project-specific conductor directory in project_manager and app_controller

This commit is contained in:
2026-03-12 16:38:01 -04:00
parent 48e2ed852a
commit 3999e9c86d
2 changed files with 15 additions and 9 deletions

View File

@@ -430,6 +430,12 @@ class AppController:
if hasattr(self, 'perf_monitor'): if hasattr(self, 'perf_monitor'):
self.perf_monitor.enabled = value self.perf_monitor.enabled = value
@property
def active_project_root(self) -> str:
if self.active_project_path:
return str(Path(self.active_project_path).parent)
return self.ui_files_base_dir
def _update_inject_preview(self) -> None: def _update_inject_preview(self) -> None:
"""Updates the preview content based on the selected file and injection mode.""" """Updates the preview content based on the selected file and injection mode."""
if not self._inject_file_path: if not self._inject_file_path:
@@ -1859,7 +1865,7 @@ class AppController:
agent_tools_cfg = proj.get("agent", {}).get("tools", {}) agent_tools_cfg = proj.get("agent", {}).get("tools", {})
self.ui_agent_tools = {t: agent_tools_cfg.get(t, True) for t in models.AGENT_TOOL_NAMES} self.ui_agent_tools = {t: agent_tools_cfg.get(t, True) for t in models.AGENT_TOOL_NAMES}
# MMA Tracks # MMA Tracks
self.tracks = project_manager.get_all_tracks(self.ui_files_base_dir) self.tracks = project_manager.get_all_tracks(self.active_project_root)
# Restore MMA state # Restore MMA state
mma_sec = proj.get("mma", {}) mma_sec = proj.get("mma", {})
self.ui_epic_input = mma_sec.get("epic", "") self.ui_epic_input = mma_sec.get("epic", "")
@@ -1889,7 +1895,7 @@ class AppController:
self.active_tickets = [] self.active_tickets = []
# Load track-scoped history if track is active # Load track-scoped history if track is active
if self.active_track: if self.active_track:
track_history = project_manager.load_track_history(self.active_track.id, self.ui_files_base_dir) track_history = project_manager.load_track_history(self.active_track.id, self.active_project_root)
if track_history: if track_history:
with self._disc_entries_lock: with self._disc_entries_lock:
self.disc_entries = models.parse_history_entries(track_history, self.disc_roles) self.disc_entries = models.parse_history_entries(track_history, self.disc_roles)
@@ -1962,7 +1968,7 @@ class AppController:
def _cb_load_track(self, track_id: str) -> None: def _cb_load_track(self, track_id: str) -> None:
state = project_manager.load_track_state(track_id, self.ui_files_base_dir) state = project_manager.load_track_state(track_id, self.active_project_root)
if state: if state:
try: try:
# Convert list[Ticket] or list[dict] to list[Ticket] for Track object # Convert list[Ticket] or list[dict] to list[Ticket] for Track object
@@ -1980,7 +1986,7 @@ class AppController:
# Keep dicts for UI table (or convert models.Ticket objects back to dicts if needed) # Keep dicts for UI table (or convert models.Ticket objects back to dicts if needed)
self.active_tickets = [asdict(t) if not isinstance(t, dict) else t for t in tickets] self.active_tickets = [asdict(t) if not isinstance(t, dict) else t for t in tickets]
# Load track-scoped history # Load track-scoped history
history = project_manager.load_track_history(track_id, self.ui_files_base_dir) history = project_manager.load_track_history(track_id, self.active_project_root)
with self._disc_entries_lock: with self._disc_entries_lock:
if history: if history:
self.disc_entries = models.parse_history_entries(history, self.disc_roles) self.disc_entries = models.parse_history_entries(history, self.disc_roles)
@@ -2650,7 +2656,7 @@ class AppController:
if not name: return if not name: return
date_suffix = datetime.now().strftime("%Y%m%d") date_suffix = datetime.now().strftime("%Y%m%d")
track_id = f"{name.lower().replace(' ', '_')}_{date_suffix}" track_id = f"{name.lower().replace(' ', '_')}_{date_suffix}"
track_dir = paths.get_tracks_dir() / track_id track_dir = paths.get_track_state_dir(track_id, project_path=self.active_project_root)
track_dir.mkdir(parents=True, exist_ok=True) track_dir.mkdir(parents=True, exist_ok=True)
spec_file = track_dir / "spec.md" spec_file = track_dir / "spec.md"
with open(spec_file, "w", encoding="utf-8") as f: with open(spec_file, "w", encoding="utf-8") as f:
@@ -2669,7 +2675,7 @@ class AppController:
"progress": 0.0 "progress": 0.0
}, f, indent=1) }, f, indent=1)
# Refresh tracks from disk # Refresh tracks from disk
self.tracks = project_manager.get_all_tracks(self.ui_files_base_dir) self.tracks = project_manager.get_all_tracks(self.active_project_root)
def _push_mma_state_update(self) -> None: def _push_mma_state_update(self) -> None:
if not self.active_track: if not self.active_track:

View File

@@ -245,7 +245,7 @@ def save_track_state(track_id: str, state: 'TrackState', base_dir: Union[str, Pa
""" """
Saves a TrackState object to conductor/tracks/<track_id>/state.toml. Saves a TrackState object to conductor/tracks/<track_id>/state.toml.
""" """
track_dir = Path(base_dir) / paths.get_track_state_dir(track_id) track_dir = paths.get_track_state_dir(track_id, project_path=str(base_dir))
track_dir.mkdir(parents=True, exist_ok=True) track_dir.mkdir(parents=True, exist_ok=True)
state_file = track_dir / "state.toml" state_file = track_dir / "state.toml"
data = clean_nones(state.to_dict()) data = clean_nones(state.to_dict())
@@ -257,7 +257,7 @@ def load_track_state(track_id: str, base_dir: Union[str, Path] = ".") -> Optiona
Loads a TrackState object from conductor/tracks/<track_id>/state.toml. Loads a TrackState object from conductor/tracks/<track_id>/state.toml.
""" """
from src.models import TrackState from src.models import TrackState
state_file = Path(base_dir) / paths.get_track_state_dir(track_id) / "state.toml" state_file = paths.get_track_state_dir(track_id, project_path=str(base_dir)) / 'state.toml'
if not state_file.exists(): if not state_file.exists():
return None return None
with open(state_file, "rb") as f: with open(state_file, "rb") as f:
@@ -302,7 +302,7 @@ def get_all_tracks(base_dir: Union[str, Path] = ".") -> list[dict[str, Any]]:
Handles missing or malformed metadata.json or state.toml by falling back Handles missing or malformed metadata.json or state.toml by falling back
to available info or defaults. to available info or defaults.
""" """
tracks_dir = Path(base_dir) / paths.get_tracks_dir() tracks_dir = paths.get_tracks_dir(project_path=str(base_dir))
if not tracks_dir.exists(): if not tracks_dir.exists():
return [] return []
results: list[dict[str, Any]] = [] results: list[dict[str, Any]] = []