From 3999e9c86d17f17d2cf653e3413654a03d46e719 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 12 Mar 2026 16:38:01 -0400 Subject: [PATCH] feat(conductor): Use project-specific conductor directory in project_manager and app_controller --- src/app_controller.py | 18 ++++++++++++------ src/project_manager.py | 6 +++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/app_controller.py b/src/app_controller.py index 3e5d05b..01b5cfc 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -430,6 +430,12 @@ class AppController: if hasattr(self, 'perf_monitor'): 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: """Updates the preview content based on the selected file and injection mode.""" if not self._inject_file_path: @@ -1859,7 +1865,7 @@ class AppController: 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} # 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 mma_sec = proj.get("mma", {}) self.ui_epic_input = mma_sec.get("epic", "") @@ -1889,7 +1895,7 @@ class AppController: self.active_tickets = [] # Load track-scoped history if track is active 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: with self._disc_entries_lock: 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: - 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: try: # 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) self.active_tickets = [asdict(t) if not isinstance(t, dict) else t for t in tickets] # 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: if history: self.disc_entries = models.parse_history_entries(history, self.disc_roles) @@ -2650,7 +2656,7 @@ class AppController: if not name: return date_suffix = datetime.now().strftime("%Y%m%d") 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) spec_file = track_dir / "spec.md" with open(spec_file, "w", encoding="utf-8") as f: @@ -2669,7 +2675,7 @@ class AppController: "progress": 0.0 }, f, indent=1) # 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: if not self.active_track: diff --git a/src/project_manager.py b/src/project_manager.py index e008929..0d866bf 100644 --- a/src/project_manager.py +++ b/src/project_manager.py @@ -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//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) state_file = track_dir / "state.toml" 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//state.toml. """ 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(): return None 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 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(): return [] results: list[dict[str, Any]] = []