diff --git a/gui_2.py b/gui_2.py index 8b67fef..a3f13a3 100644 --- a/gui_2.py +++ b/gui_2.py @@ -754,6 +754,12 @@ class App: self.active_track = None 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) + if track_history: + self.disc_entries = _parse_history_entries(track_history, self.disc_roles) + def _save_active_project(self): if self.active_project_path: try: @@ -790,6 +796,10 @@ class App: def _flush_disc_entries_to_project(self): history_strings = [project_manager.entry_to_str(e) for e in self.disc_entries] + if self.active_track: + project_manager.save_track_history(self.active_track.id, history_strings, self.ui_files_base_dir) + return + disc_sec = self.project.setdefault("discussion", {}) discussions = disc_sec.setdefault("discussions", {}) disc_data = discussions.setdefault(self.active_discussion, project_manager.default_discussion()) @@ -1398,7 +1408,8 @@ class App: self._save_active_project() self._flush_to_config() save_config(self.config) - flat = project_manager.flat_config(self.project, self.active_discussion) + track_id = self.active_track.id if self.active_track else None + flat = project_manager.flat_config(self.project, self.active_discussion, track_id=track_id) full_md, path, file_items = aggregate.run(flat) # Build stable markdown (no history) for Gemini caching screenshot_base_dir = Path(flat.get("screenshots", {}).get("base_dir", ".")) diff --git a/project_manager.py b/project_manager.py index 942730c..0618c3f 100644 --- a/project_manager.py +++ b/project_manager.py @@ -225,11 +225,17 @@ def migrate_from_legacy_config(cfg: dict) -> dict: # ── flat config for aggregate.run() ───────────────────────────────────────── -def flat_config(proj: dict, disc_name: str | None = None) -> dict: +def flat_config(proj: dict, disc_name: str | None = None, track_id: str | None = None) -> dict: """Return a flat config dict compatible with aggregate.run().""" disc_sec = proj.get("discussion", {}) - name = disc_name or disc_sec.get("active", "main") - disc_data = disc_sec.get("discussions", {}).get(name, {}) + + if track_id: + history = load_track_history(track_id, proj.get("files", {}).get("base_dir", ".")) + else: + name = disc_name or disc_sec.get("active", "main") + disc_data = disc_sec.get("discussions", {}).get(name, {}) + history = disc_data.get("history", []) + return { "project": proj.get("project", {}), "output": proj.get("output", {}), @@ -237,7 +243,7 @@ def flat_config(proj: dict, disc_name: str | None = None) -> dict: "screenshots": proj.get("screenshots", {}), "discussion": { "roles": disc_sec.get("roles", []), - "history": disc_data.get("history", []), + "history": history, }, } @@ -267,3 +273,39 @@ def load_track_state(track_id: str, base_dir: str | Path = ".") -> 'TrackState': with open(state_file, "rb") as f: data = tomllib.load(f) return TrackState.from_dict(data) + + +def load_track_history(track_id: str, base_dir: str | Path = ".") -> list: + """ + Loads the discussion history for a specific track from its state.toml. + Returns a list of entry strings formatted with @timestamp. + """ + from models import TrackState + state = load_track_state(track_id, base_dir) + if not state: + return [] + + history = [] + for entry in state.discussion: + e = dict(entry) + ts = e.get("ts") + if isinstance(ts, datetime.datetime): + e["ts"] = ts.strftime(TS_FMT) + history.append(entry_to_str(e)) + return history + + +def save_track_history(track_id: str, history: list, base_dir: str | Path = "."): + """ + Saves the discussion history for a specific track to its state.toml. + 'history' is expected to be a list of formatted strings. + """ + from models import TrackState + state = load_track_state(track_id, base_dir) + if not state: + return + + roles = ["User", "AI", "Vendor API", "System", "Reasoning"] + entries = [str_to_entry(h, roles) for h in history] + state.discussion = entries + save_track_state(track_id, state, base_dir)