diff --git a/conductor/tracks.md b/conductor/tracks.md index 91c9099..f4affe6 100644 --- a/conductor/tracks.md +++ b/conductor/tracks.md @@ -16,5 +16,5 @@ This file tracks all major tracks for the project. Each track has its own detail --- -- [ ] **Track: Consolidate Temp/Test Cruft & Log Taxonomy** +- [~] **Track: Consolidate Temp/Test Cruft & Log Taxonomy** *Link: [./tracks/consolidate_cruft_and_log_taxonomy_20260228/](./tracks/consolidate_cruft_and_log_taxonomy_20260228/)* diff --git a/conductor/tracks/consolidate_cruft_and_log_taxonomy_20260228/plan.md b/conductor/tracks/consolidate_cruft_and_log_taxonomy_20260228/plan.md index 68174fe..6dff148 100644 --- a/conductor/tracks/consolidate_cruft_and_log_taxonomy_20260228/plan.md +++ b/conductor/tracks/consolidate_cruft_and_log_taxonomy_20260228/plan.md @@ -6,7 +6,7 @@ - [x] Task: Conductor - User Manual Verification 'Phase 1: Directory Structure & Gitignore' (Protocol in workflow.md) (fab109e) ## Phase 2: App Logic Redirection -- [ ] Task: Update `session_logger.py` to use `logs/sessions/`, `logs/agents/`, and `logs/errors/` for its outputs. +- [~] Task: Update `session_logger.py` to use `logs/sessions/`, `logs/agents/`, and `logs/errors/` for its outputs. - [ ] Task: Modify `project_manager.py` to store temporary project TOMLs in `tests/artifacts/`. - [ ] Task: Update `shell_runner.py` or `scripts/mma_exec.py` to use `tests/artifacts/` for its temporary scripts and outputs. - [ ] Task: Add foundational support (e.g., in `metadata.json` for sessions) to store "annotated names" for logs. diff --git a/gui_2.py b/gui_2.py index 0781597..c019f8f 100644 --- a/gui_2.py +++ b/gui_2.py @@ -1224,9 +1224,8 @@ class App: """A dummy function that a custom_callback would execute for testing.""" # Note: This file path is relative to where the test is run. # This is for testing purposes only. - with open("temp_callback_output.txt", "w") as f: + with open("tests/artifacts/temp_callback_output.txt", "w") as f: f.write(data) - def _recalculate_session_usage(self) -> None: usage = {"input_tokens": 0, "output_tokens": 0, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0, "total_tokens": 0, "last_latency": 0.0} for entry in ai_client.get_comms_log(): diff --git a/scripts/mma_exec.py b/scripts/mma_exec.py index 85bbf4f..76ce114 100644 --- a/scripts/mma_exec.py +++ b/scripts/mma_exec.py @@ -8,7 +8,7 @@ import tree_sitter_python import ast import datetime -LOG_FILE: str = 'logs/mma_delegation.log' +LOG_FILE: str = 'logs/errors/mma_delegation.log' def generate_skeleton(code: str) -> str: diff --git a/session_logger.py b/session_logger.py index 02bfab2..c0d8cd6 100644 --- a/session_logger.py +++ b/session_logger.py @@ -3,33 +3,39 @@ Opens timestamped log/script files at startup and keeps them open for the lifetime of the process. The next run of the GUI creates new files; the previous run's files are simply closed when the process exits. + File layout ----------- -logs/ +logs/sessions/ comms_.log - every comms entry (direction/kind/payload) as JSON-L toolcalls_.log - sequential record of every tool invocation clicalls_.log - sequential record of every CLI subprocess call scripts/generated/ _.ps1 - each PowerShell script the AI generated, in order + Where = YYYYMMDD_HHMMSS of when this session was started. """ + import atexit import datetime import json import threading from typing import Any, Optional, TextIO from pathlib import Path -_LOG_DIR: Path = Path("./logs") + +_LOG_DIR: Path = Path("./logs/sessions") _SCRIPTS_DIR: Path = Path("./scripts/generated") + _ts: str = "" # session timestamp string e.g. "20260301_142233" _session_id: str = "" # YYYYMMDD_HHMMSS[_Label] _session_dir: Optional[Path] = None # Path to the sub-directory for this session _seq: int = 0 # monotonic counter for script files this session _seq_lock: threading.Lock = threading.Lock() -_comms_fh: Optional[TextIO] = None # file handle: logs//comms.log -_tool_fh: Optional[TextIO] = None # file handle: logs//toolcalls.log -_api_fh: Optional[TextIO] = None # file handle: logs//apihooks.log -_cli_fh: Optional[TextIO] = None # file handle: logs//clicalls.log + +_comms_fh: Optional[TextIO] = None # file handle: logs/sessions//comms.log +_tool_fh: Optional[TextIO] = None # file handle: logs/sessions//toolcalls.log +_api_fh: Optional[TextIO] = None # file handle: logs/sessions//apihooks.log +_cli_fh: Optional[TextIO] = None # file handle: logs/sessions//clicalls.log def _now_ts() -> str: return datetime.datetime.now().strftime("%Y%m%d_%H%M%S") @@ -42,29 +48,35 @@ def open_session(label: Optional[str] = None) -> None: global _ts, _session_id, _session_dir, _comms_fh, _tool_fh, _api_fh, _cli_fh, _seq if _comms_fh is not None: return + _ts = _now_ts() _session_id = _ts if label: safe_label = "".join(c if c.isalnum() or c in ("-", "_") else "_" for c in label) _session_id += f"_{safe_label}" + _session_dir = _LOG_DIR / _session_id _session_dir.mkdir(parents=True, exist_ok=True) _SCRIPTS_DIR.mkdir(parents=True, exist_ok=True) + _seq = 0 _comms_fh = open(_session_dir / "comms.log", "w", encoding="utf-8", buffering=1) _tool_fh = open(_session_dir / "toolcalls.log", "w", encoding="utf-8", buffering=1) _api_fh = open(_session_dir / "apihooks.log", "w", encoding="utf-8", buffering=1) _cli_fh = open(_session_dir / "clicalls.log", "w", encoding="utf-8", buffering=1) + _tool_fh.write(f"# Tool-call log — session {_session_id}\n\n") _tool_fh.flush() _cli_fh.write(f"# CLI Subprocess Call Log — session {_session_id}\n\n") _cli_fh.flush() + try: from log_registry import LogRegistry registry = LogRegistry(str(_LOG_DIR / "log_registry.toml")) registry.register_session(_session_id, str(_session_dir), datetime.datetime.now()) except Exception as e: print(f"Warning: Could not register session in LogRegistry: {e}") + atexit.register(close_session) def close_session() -> None: @@ -72,6 +84,7 @@ def close_session() -> None: global _comms_fh, _tool_fh, _api_fh, _cli_fh, _session_id, _LOG_DIR if _comms_fh is None: return + if _comms_fh: _comms_fh.close() _comms_fh = None @@ -84,6 +97,7 @@ def close_session() -> None: if _cli_fh: _cli_fh.close() _cli_fh = None + try: from log_registry import LogRegistry registry = LogRegistry(str(_LOG_DIR / "log_registry.toml")) @@ -122,17 +136,21 @@ def log_tool_call(script: str, result: str, script_path: Optional[str]) -> Optio global _seq if _tool_fh is None: return script_path + with _seq_lock: _seq += 1 seq = _seq + ts_entry = datetime.datetime.now().strftime("%H:%M:%S") ps1_name = f"{_ts}_{seq:04d}.ps1" ps1_path: Optional[Path] = _SCRIPTS_DIR / ps1_name + try: ps1_path.write_text(script, encoding="utf-8") except Exception as exc: ps1_path = None ps1_name = f"(write error: {exc})" + try: _tool_fh.write( f"## Call #{seq} [{ts_entry}]\n" @@ -144,6 +162,7 @@ def log_tool_call(script: str, result: str, script_path: Optional[str]) -> Optio _tool_fh.flush() except Exception: pass + return str(ps1_path) if ps1_path else None def log_cli_call(command: str, stdin_content: Optional[str], stdout_content: Optional[str], stderr_content: Optional[str], latency: float) -> None: diff --git a/simulation/sim_base.py b/simulation/sim_base.py index 0abc6f9..c8ab028 100644 --- a/simulation/sim_base.py +++ b/simulation/sim_base.py @@ -26,7 +26,7 @@ class BaseSimulation: self.client.click("btn_reset") time.sleep(0.5) git_dir = os.path.abspath(".") - self.project_path = os.path.abspath(f"tests/temp_{project_name.lower()}.toml") + self.project_path = os.path.abspath(f"tests/artifacts/temp_{project_name.lower()}.toml") if os.path.exists(self.project_path): os.remove(self.project_path) print(f"[BaseSim] Scaffolding Project: {project_name}") diff --git a/tests/test_gui2_parity.py b/tests/test_gui2_parity.py index a8a6fdf..6786135 100644 --- a/tests/test_gui2_parity.py +++ b/tests/test_gui2_parity.py @@ -12,7 +12,7 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) from api_hook_client import ApiHookClient # Define a temporary file path for callback testing -TEST_CALLBACK_FILE = Path("temp_callback_output.txt") +TEST_CALLBACK_FILE = Path("tests/artifacts/temp_callback_output.txt") @pytest.fixture(scope="function", autouse=True) def cleanup_callback_file() -> None: @@ -75,3 +75,4 @@ def test_gui2_custom_callback_hook_works(live_gui: Any) -> None: with open(TEST_CALLBACK_FILE, "r") as f: content = f.read() assert content == test_data, "Callback executed, but file content is incorrect." + diff --git a/tests/test_live_workflow.py b/tests/test_live_workflow.py index e57fc92..6389cca 100644 --- a/tests/test_live_workflow.py +++ b/tests/test_live_workflow.py @@ -21,7 +21,7 @@ def test_full_live_workflow(live_gui) -> None: client.click("btn_reset") time.sleep(1) # 2. Project Setup - temp_project_path = os.path.abspath("tests/temp_project.toml") + temp_project_path = os.path.abspath("tests/artifacts/temp_project.toml") if os.path.exists(temp_project_path): os.remove(temp_project_path) client.click("btn_project_new_automated", user_data=temp_project_path) @@ -74,3 +74,4 @@ def test_full_live_workflow(live_gui) -> None: # Verify session is empty in new discussion session = client.get_session() assert len(session.get('session', {}).get('entries', [])) == 0 + diff --git a/tests/test_sim_base.py b/tests/test_sim_base.py index d5924b3..f76997b 100644 --- a/tests/test_sim_base.py +++ b/tests/test_sim_base.py @@ -27,4 +27,4 @@ def test_base_simulation_setup() -> None: mock_client.wait_for_server.assert_called() mock_client.click.assert_any_call("btn_reset") mock_sim.setup_new_project.assert_called() - assert sim.project_path.endswith("temp_testsim.toml") + assert sim.project_path.endswith("tests/artifacts/temp_testsim.toml") diff --git a/tests/visual_sim_mma_v2.py b/tests/visual_sim_mma_v2.py index b7fc809..4077f26 100644 --- a/tests/visual_sim_mma_v2.py +++ b/tests/visual_sim_mma_v2.py @@ -24,7 +24,7 @@ def test_mma_complete_lifecycle(live_gui) -> None: mock_cli_path = f'{sys.executable} {os.path.abspath("tests/mock_gemini_cli.py")}' client.set_value('gcli_path', mock_cli_path) # Prevent polluting the real project directory with test tracks - client.set_value('files_base_dir', 'tests/temp_workspace') + client.set_value('files_base_dir', 'tests/artifacts/temp_workspace') client.click('btn_project_save') time.sleep(1) except Exception as e: