From 716d8b4e13dde6721b571d8fda4c936ae82723c3 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 26 Feb 2026 22:02:33 -0500 Subject: [PATCH] chore(conductor): Archive completed track 'MMA Core Engine Implementation' --- api_hook_client.py | 17 +- api_hooks.py | 29 +++ .../archive/mma_core_engine_20260224/index.md | 9 + .../mma_core_engine_20260224/metadata.json | 6 + .../archive/mma_core_engine_20260224/plan.md | 85 ++++++++ .../archive/mma_core_engine_20260224/spec.md | 39 ++++ conductor/tracks.md | 3 +- .../tracks/mma_core_engine_20260224/plan.md | 15 +- config.toml | 1 + gui_2.py | 36 ++-- gui_diag.log | 0 scripts/mma_exec.py | 2 +- tests/temp_project_history.toml | 11 +- tests/verify_mma_gui_robust.py | 96 +++++++++ tests/visual_diag.py | 69 +++++++ tests/visual_mma_verification.py | 184 ++++++++++++++++++ 16 files changed, 578 insertions(+), 24 deletions(-) create mode 100644 conductor/archive/mma_core_engine_20260224/index.md create mode 100644 conductor/archive/mma_core_engine_20260224/metadata.json create mode 100644 conductor/archive/mma_core_engine_20260224/plan.md create mode 100644 conductor/archive/mma_core_engine_20260224/spec.md create mode 100644 gui_diag.log create mode 100644 tests/verify_mma_gui_robust.py create mode 100644 tests/visual_diag.py create mode 100644 tests/visual_mma_verification.py diff --git a/api_hook_client.py b/api_hook_client.py index 3100658..c41c28f 100644 --- a/api_hook_client.py +++ b/api_hook_client.py @@ -3,7 +3,7 @@ import json import time class ApiHookClient: - def __init__(self, base_url="http://127.0.0.1:8999", max_retries=2, retry_delay=0.1): + def __init__(self, base_url="http://127.0.0.1:8999", max_retries=5, retry_delay=0.2): self.base_url = base_url self.max_retries = max_retries self.retry_delay = retry_delay @@ -26,8 +26,8 @@ class ApiHookClient: headers = {'Content-Type': 'application/json'} last_exception = None - # Lower request timeout for local server by default - req_timeout = timeout if timeout is not None else 0.5 + # Increase default request timeout for local server + req_timeout = timeout if timeout is not None else 2.0 for attempt in range(self.max_retries + 1): try: @@ -77,6 +77,17 @@ class ApiHookClient: def get_session(self): return self._make_request('GET', '/api/session') + def get_mma_status(self): + """Retrieves current MMA status (track, tickets, tier, etc.)""" + return self._make_request('GET', '/api/gui/mma_status') + + def push_event(self, event_type, payload): + """Pushes an event to the GUI's AsyncEventQueue via the /api/gui endpoint.""" + return self.post_gui({ + "action": event_type, + "payload": payload + }) + def get_performance(self): """Retrieves UI performance metrics.""" return self._make_request('GET', '/api/performance') diff --git a/api_hooks.py b/api_hooks.py index d9c874a..eeddad9 100644 --- a/api_hooks.py +++ b/api_hooks.py @@ -111,6 +111,35 @@ class HookHandler(BaseHTTPRequestHandler): "callback": get_val }) + if event.wait(timeout=2): + self.send_response(200) + self.send_header('Content-Type', 'application/json') + self.end_headers() + self.wfile.write(json.dumps(result).encode('utf-8')) + else: + self.send_response(504) + self.end_headers() + elif self.path == '/api/gui/mma_status': + event = threading.Event() + result = {} + + def get_mma(): + try: + result["mma_status"] = getattr(app, "mma_status", "idle") + result["active_tier"] = getattr(app, "active_tier", None) + result["active_track"] = getattr(app, "active_track", None) + result["active_tickets"] = getattr(app, "active_tickets", []) + result["mma_step_mode"] = getattr(app, "mma_step_mode", False) + result["pending_approval"] = app._pending_mma_approval is not None + finally: + event.set() + + with app._pending_gui_tasks_lock: + app._pending_gui_tasks.append({ + "action": "custom_callback", + "callback": get_mma + }) + if event.wait(timeout=2): self.send_response(200) self.send_header('Content-Type', 'application/json') diff --git a/conductor/archive/mma_core_engine_20260224/index.md b/conductor/archive/mma_core_engine_20260224/index.md new file mode 100644 index 0000000..ea0c2ea --- /dev/null +++ b/conductor/archive/mma_core_engine_20260224/index.md @@ -0,0 +1,9 @@ +# MMA Core Engine Implementation + +This track implements the 5 Core Epics defined during the MMA Architecture Evaluation. + +### Navigation +- [Specification](./spec.md) +- [Implementation Plan](./plan.md) +- [Original Architecture Proposal / Meta-Track](../mma_implementation_20260224/index.md) +- [MMA Support Directory (Source of Truth)](../../../MMA_Support/) \ No newline at end of file diff --git a/conductor/archive/mma_core_engine_20260224/metadata.json b/conductor/archive/mma_core_engine_20260224/metadata.json new file mode 100644 index 0000000..b08a27f --- /dev/null +++ b/conductor/archive/mma_core_engine_20260224/metadata.json @@ -0,0 +1,6 @@ +{ + "id": "mma_core_engine_20260224", + "title": "MMA Core Engine Implementation", + "status": "planning", + "created_at": "2026-02-24T00:00:00.000000" +} \ No newline at end of file diff --git a/conductor/archive/mma_core_engine_20260224/plan.md b/conductor/archive/mma_core_engine_20260224/plan.md new file mode 100644 index 0000000..946da44 --- /dev/null +++ b/conductor/archive/mma_core_engine_20260224/plan.md @@ -0,0 +1,85 @@ +# Implementation Plan: MMA Core Engine Implementation + +## Phase 1: Track 1 - The Memory Foundations (AST Parser) [checkpoint: ac31e41] +- [x] Task: Dependency Setup (8fb75cc) + - [x] Add `tree-sitter` and `tree-sitter-python` to `pyproject.toml` / `requirements.txt` (8fb75cc) +- [x] Task: Core Parser Class (7a609ca) + - [x] Create `ASTParser` in `file_cache.py` (7a609ca) +- [x] Task: Skeleton View Extraction (7a609ca) + - [x] Write query to extract `function_definition` and `class_definition` (7a609ca) + - [x] Replace bodies with `pass`, keep type hints and signatures (7a609ca) +- [x] Task: Curated View Extraction (7a609ca) + - [x] Keep class structures, module docstrings (7a609ca) + - [x] Preserve `@core_logic` or `# [HOT]` function bodies, hide others (7a609ca) + +## Phase 2: Track 2 - State Machine & Data Structures [checkpoint: a518a30] +- [x] Task: The Dataclasses (f9b5a50) + - [x] Create `models.py` defining `Ticket` and `Track` (f9b5a50) +- [x] Task: Worker Context Definition (ee71929) + - [x] Define `WorkerContext` holding `Ticket` ID, model config, and ephemeral messages (ee71929) +- [x] Task: State Mutator Methods (e925b21) + - [x] Implement `ticket.mark_blocked()`, `ticket.mark_complete()`, `track.get_executable_tickets()` (e925b21) + +## Phase 3: Track 3 - The Linear Orchestrator & Execution Clutch [checkpoint: e6c8d73] +- [x] Task: The Engine Core (7a30168) + - [x] Create `multi_agent_conductor.py` containing `ConductorEngine` and `run_worker_lifecycle` (7a30168) +- [x] Task: Context Injection (9d6d174) + - [x] Format context strings using `file_cache.py` target AST views (9d6d174) +- [x] Task: The HITL Execution Clutch (1afd9c8) + - [x] Before executing `write_file`/`shell_runner.py` tools in step-mode, prompt user for confirmation (1afd9c8) + - [x] Provide functionality to mutate the history JSON before resuming execution (1afd9c8) + +## Phase 4: Track 4 - Tier 4 QA Interception [checkpoint: 61d17ad] +- [x] Task: The Interceptor Loop (bc654c2) + - [x] Catch `subprocess.run()` execution errors inside `shell_runner.py` (bc654c2) +- [x] Task: Tier 4 Instantiation (8e4e326) + - [x] Make a secondary API call to `default_cheap` model passing `stderr` and snippet (8e4e326) +- [x] Task: Payload Formatting (fb3da4d) + - [x] Inject the 20-word fix summary into the Tier 3 worker history (fb3da4d) + +## Phase 5: Track 5 - UI Decoupling & Tier 1/2 Routing (The Final Boss) [checkpoint: 3982fda] +- [x] Task: The Event Bus (695cb4a) + - [x] Implement an `asyncio.Queue` linking GUI actions to the backend engine (695cb4a) +- [x] Task: Tier 1 & 2 System Prompts (a28d71b) + - [x] Create structured system prompts for Epic routing and Ticket creation (a28d71b) +- [x] Task: The Dispatcher Loop (1dacd36) + - [x] Read Tier 2 JSON flat-lists, construct Tickets, execute Stub resolution paths (1dacd36) +- [x] Task: UI Component Update (68861c0) + - [x] Refactor `gui_2.py` to push `UserRequestEvent` instead of blocking on API generation (68861c0) + +## Phase 6: Live & Headless Verification +- [x] Task: Headless Engine Verification + - [x] Run a comprehensive headless test scenario (e.g., using a mock or dedicated test script). + - [x] Verify Ticket execution, "Context Amnesia" (statelessness), and Tier 4 error interception. +- [x] Task: Live GUI Integration Verification + - [x] Launch `gui_2.py` and verify Event Bus responsiveness. + - [x] Confirm UI updates and async event handling during multi-model generation. +- [x] Task: Comprehensive Regression Suite + - [x] Run all tests in `tests/` related to MMA, Conductor, and Async Events. + - [x] Verify that no regressions were introduced in existing functionality. + +## Phase 7: MMA Observability & UX +- [x] Task: MMA Dashboard Implementation + - [x] Create a new dockable panel in `gui_2.py` for "MMA Dashboard". + - [x] Display active `Track` and `Ticket` queue status. +- [x] Task: Execution Clutch UI + - [x] Implement Step Mode toggle and Pause Points logic in the GUI. + - [x] Add `[Approve]`, `[Edit Payload]`, and `[Abort]` buttons for tool execution. +- [x] Task: Memory Mutator Modal + - [x] Create a modal for editing raw JSON conversation history of paused workers. +- [x] Task: Tiered Metrics & Log Links + - [x] Add visual indicators for the active model Tier. + - [x] Provide clickable links to sub-agent logs. + +## Phase 8: Visual Verification & Interaction Tests +- [x] Task: Visual Verification Script + - [x] Create `tests/visual_mma_verification.py` to drive the GUI into various MMA states. + - [x] Verify MMA Dashboard visibility and progress bar. + - [x] Verify Ticket Queue rendering with correct status colors. +- [x] Task: HITL Interaction Verification + - [x] Drive a simulated HITL pause through the verification script. + - [x] Manually verify the "MMA Step Approval" modal appearance. + - [x] Manually verify "Edit Payload" (Memory Mutator) functionality. +- [~] Task: Final Polish & Fixes + - [ ] Fix any visual glitches or layout issues discovered during manual testing. + - [ ] Fix any visual glitches or layout issues discovered during manual testing. \ No newline at end of file diff --git a/conductor/archive/mma_core_engine_20260224/spec.md b/conductor/archive/mma_core_engine_20260224/spec.md new file mode 100644 index 0000000..f10b65c --- /dev/null +++ b/conductor/archive/mma_core_engine_20260224/spec.md @@ -0,0 +1,39 @@ +# Specification: MMA Core Engine Implementation + +## 1. Overview +This track consolidates the implementation of the 4-Tier Hierarchical Multi-Model Architecture into the `manual_slop` codebase. The architecture transitions the current monolithic single-agent loop into a compartmentalized, token-efficient, and fully debuggable state machine. + +## 2. Functional Requirements + +### Phase 1: The Memory Foundations (AST Parser) +- Integrate `tree-sitter` and `tree-sitter-python` into `pyproject.toml` / `requirements.txt`. +- Implement `ASTParser` in `file_cache.py` to extract strict memory views (Skeleton View, Curated View). +- Strip function bodies from dependencies while preserving `@core_logic` or `# [HOT]` logic for the target modules. + +### Phase 2: State Machine & Data Structures +- Create `models.py` incorporating strict Pydantic/Dataclass schemas for `Ticket`, `Track`, and `WorkerContext`. +- Enforce rigid state mutators governing dependencies between tickets (e.g., locking execution until a stub generation ticket completes). + +### Phase 3: The Linear Orchestrator & Execution Clutch +- Build `multi_agent_conductor.py` and a `ConductorEngine` dispatcher loop. +- Embed the "Execution Clutch" allowing developers to pause, review, and manually rewrite payloads (JSON history mutation) before applying changes to the local filesystem. + +### Phase 4: Tier 4 QA Interception +- Augment `shell_runner.py` with try/except wrappers capturing process errors (`stderr`). +- Rather than feeding raw stack traces to an expensive model, instantly forward them to a stateless `default_cheap` sub-agent for a 20-word summarization that is subsequently injected into the primary worker's context. + +### Phase 5: UI Decoupling & Tier 1/2 Routing (The Final Boss) +- Disconnect `gui_2.py` from direct LLM inference requests. +- Bind the GUI to a synchronous or `asyncio.Queue` Event Bus managed by the Orchestrator, allowing dynamic tracking of parallel worker executions without thread-locking the interface. + +## 3. Acceptance Criteria +- [ ] A 1000-line script can be successfully parsed into a 100-line AST Skeleton. +- [ ] Tickets properly block and resolve depending on stub-generation dependencies. +- [ ] Shell errors are compressed into >50-token hints using the cheap utility model. +- [ ] The GUI remains responsive during multi-model generation phases. + +## 4. Meta-Track Reference & Source of Truth +For the original rationale, API formatting recommendations (e.g., Godot ECS schemas vs Nested JSON), and strict token firewall workflows, refer back to the architectural planning meta-track: `conductor/tracks/mma_implementation_20260224/`. + +**Fallback Source of Truth:** +As a fallback, any track or sub-task should absolve its source of truth by referencing the `./MMA_Support/` directory. This directory contains the original design documents and raw discussions from which the entire `mma_implementation` track and 4-Tier Architecture were initially generated. \ No newline at end of file diff --git a/conductor/tracks.md b/conductor/tracks.md index 39fbffb..64509bb 100644 --- a/conductor/tracks.md +++ b/conductor/tracks.md @@ -30,8 +30,7 @@ This file tracks all major tracks for the project. Each track has its own detail --- -- [x] **Track: MMA Core Engine Implementation** -*Link: [./tracks/mma_core_engine_20260224/](./tracks/mma_core_engine_20260224/)* +- [~] **Track: MMA Core Engine Implementation** --- diff --git a/conductor/tracks/mma_core_engine_20260224/plan.md b/conductor/tracks/mma_core_engine_20260224/plan.md index 57c7579..946da44 100644 --- a/conductor/tracks/mma_core_engine_20260224/plan.md +++ b/conductor/tracks/mma_core_engine_20260224/plan.md @@ -69,4 +69,17 @@ - [x] Create a modal for editing raw JSON conversation history of paused workers. - [x] Task: Tiered Metrics & Log Links - [x] Add visual indicators for the active model Tier. - - [x] Provide clickable links to sub-agent logs. \ No newline at end of file + - [x] Provide clickable links to sub-agent logs. + +## Phase 8: Visual Verification & Interaction Tests +- [x] Task: Visual Verification Script + - [x] Create `tests/visual_mma_verification.py` to drive the GUI into various MMA states. + - [x] Verify MMA Dashboard visibility and progress bar. + - [x] Verify Ticket Queue rendering with correct status colors. +- [x] Task: HITL Interaction Verification + - [x] Drive a simulated HITL pause through the verification script. + - [x] Manually verify the "MMA Step Approval" modal appearance. + - [x] Manually verify "Edit Payload" (Memory Mutator) functionality. +- [~] Task: Final Polish & Fixes + - [ ] Fix any visual glitches or layout issues discovered during manual testing. + - [ ] Fix any visual glitches or layout issues discovered during manual testing. \ No newline at end of file diff --git a/config.toml b/config.toml index 4891a42..397f206 100644 --- a/config.toml +++ b/config.toml @@ -28,6 +28,7 @@ active = "C:\\projects\\manual_slop\\tests\\temp_project.toml" "Context Hub" = true "Files & Media" = true "AI Settings" = true +"MMA Dashboard" = true "Discussion Hub" = true "Operations Hub" = true Theme = true diff --git a/gui_2.py b/gui_2.py index e00708b..b3a15e8 100644 --- a/gui_2.py +++ b/gui_2.py @@ -962,13 +962,16 @@ class App: def _handle_approve_ask(self): """Responds with approval for a pending /api/ask request.""" if not self._ask_request_id: return - try: - requests.post( - "http://127.0.0.1:8999/api/ask/respond", - json={"request_id": self._ask_request_id, "response": {"approved": True}}, - timeout=2 - ) - except Exception as e: print(f"Error responding to ask: {e}") + request_id = self._ask_request_id + def do_post(): + try: + requests.post( + "http://127.0.0.1:8999/api/ask/respond", + json={"request_id": request_id, "response": {"approved": True}}, + timeout=2 + ) + except Exception as e: print(f"Error responding to ask: {e}") + threading.Thread(target=do_post, daemon=True).start() self._pending_ask_dialog = False self._ask_request_id = None self._ask_tool_data = None @@ -976,13 +979,16 @@ class App: def _handle_reject_ask(self): """Responds with rejection for a pending /api/ask request.""" if not self._ask_request_id: return - try: - requests.post( - "http://127.0.0.1:8999/api/ask/respond", - json={"request_id": self._ask_request_id, "response": {"approved": False}}, - timeout=2 - ) - except Exception as e: print(f"Error responding to ask: {e}") + request_id = self._ask_request_id + def do_post(): + try: + requests.post( + "http://127.0.0.1:8999/api/ask/respond", + json={"request_id": request_id, "response": {"approved": False}}, + timeout=2 + ) + except Exception as e: print(f"Error responding to ask: {e}") + threading.Thread(target=do_post, daemon=True).start() self._pending_ask_dialog = False self._ask_request_id = None self._ask_tool_data = None @@ -1696,7 +1702,7 @@ class App: else: imgui.text("Proposed Tool Call:") imgui.begin_child("mma_preview", imgui.ImVec2(600, 300), True) - imgui.text_unformatted(self._pending_mma_approval.get("payload", "")) + imgui.text_unformatted(str(self._pending_mma_approval.get("payload", ""))) imgui.end_child() imgui.separator() diff --git a/gui_diag.log b/gui_diag.log new file mode 100644 index 0000000..e69de29 diff --git a/scripts/mma_exec.py b/scripts/mma_exec.py index 50ec018..ee7ea1e 100644 --- a/scripts/mma_exec.py +++ b/scripts/mma_exec.py @@ -70,7 +70,7 @@ def get_model_for_role(role: str) -> str: elif role == 'tier2-tech-lead' or role == 'tier2': return 'gemini-3-flash-preview' elif role == 'tier3-worker' or role == 'tier3': - return 'gemini-2.5-flash-lite' + return 'gemini-3-flash-preview' elif role == 'tier4-qa' or role == 'tier4': return 'gemini-2.5-flash-lite' else: diff --git a/tests/temp_project_history.toml b/tests/temp_project_history.toml index 2fff951..a79fbbf 100644 --- a/tests/temp_project_history.toml +++ b/tests/temp_project_history.toml @@ -5,10 +5,17 @@ roles = [ "System", "Reasoning", ] -active = "main" +active = "mma_human veriffication" auto_add = true [discussions.main] git_commit = "" -last_updated = "2026-02-26T21:34:07" +last_updated = "2026-02-26T22:00:42" +history = [ + "@2026-02-26T22:00:02\nSystem:\n[PERFORMANCE ALERT] CPU usage high: 92.2%. Please consider optimizing recent changes or reducing load.", +] + +[discussions."mma_human veriffication"] +git_commit = "" +last_updated = "2026-02-26T22:02:01" history = [] diff --git a/tests/verify_mma_gui_robust.py b/tests/verify_mma_gui_robust.py new file mode 100644 index 0000000..ed70c28 --- /dev/null +++ b/tests/verify_mma_gui_robust.py @@ -0,0 +1,96 @@ + +import subprocess +import time +import sys +import os +import unittest + +# Calculate project root +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +if PROJECT_ROOT not in sys.path: + sys.path.insert(0, PROJECT_ROOT) + +from api_hook_client import ApiHookClient + +class TestMMAGUIRobust(unittest.TestCase): + @classmethod + def setUpClass(cls): + # 1. Launch gui_2.py with --enable-test-hooks + cls.gui_command = [sys.executable, "gui_2.py", "--enable-test-hooks"] + print(f"Launching GUI: {' '.join(cls.gui_command)}") + cls.gui_process = subprocess.Popen( + cls.gui_command, + cwd=PROJECT_ROOT, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + cls.client = ApiHookClient() + print("Waiting for GUI to start...") + if not cls.client.wait_for_server(timeout=10): + cls.gui_process.terminate() + raise RuntimeError("GUI failed to start or hook server not responsive.") + print("GUI started.") + + @classmethod + def tearDownClass(cls): + if cls.gui_process: + cls.gui_process.terminate() + cls.gui_process.wait(timeout=5) + + def test_mma_state_ingestion(self): + """Verify that mma_state_update event correctly updates GUI state.""" + track_data = { + "id": "robust_test_track", + "title": "Robust Verification Track", + "description": "Verifying internal state ingestion" + } + tickets_data = [ + {"id": "T1", "target_file": "file1.py", "status": "todo"}, + {"id": "T2", "target_file": "file2.py", "status": "running"}, + {"id": "T3", "target_file": "file3.py", "status": "complete"}, + ] + + payload = { + "status": "active", + "active_tier": "Tier 2", + "track": track_data, + "tickets": tickets_data + } + + print("Pushing mma_state_update...") + self.client.push_event("mma_state_update", payload) + + # Give GUI a moment to process the async task + time.sleep(1.0) + + print("Querying mma_status...") + status = self.client.get_mma_status() + + self.assertEqual(status["mma_status"], "active") + self.assertEqual(status["active_tier"], "Tier 2") + self.assertEqual(status["active_track"]["id"], "robust_test_track") + self.assertEqual(len(status["active_tickets"]), 3) + self.assertEqual(status["active_tickets"][2]["status"], "complete") + print("MMA state ingestion verified successfully.") + + def test_mma_step_approval_trigger(self): + """Verify that mma_step_approval event sets the pending approval flag.""" + payload = { + "ticket_id": "T2", + "payload": "echo 'Robust Test'" + } + + print("Pushing mma_step_approval...") + self.client.push_event("mma_step_approval", payload) + + time.sleep(1.0) + + print("Querying mma_status for pending approval...") + status = self.client.get_mma_status() + + self.assertTrue(status["pending_approval"], "GUI did not register pending MMA approval") + print("MMA step approval trigger verified successfully.") + +if __name__ == "__main__": + unittest.main() diff --git a/tests/visual_diag.py b/tests/visual_diag.py new file mode 100644 index 0000000..b519604 --- /dev/null +++ b/tests/visual_diag.py @@ -0,0 +1,69 @@ +import subprocess +import time +import sys +import os + +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +if PROJECT_ROOT not in sys.path: + sys.path.insert(0, PROJECT_ROOT) + +from api_hook_client import ApiHookClient + +def diag_run(): + print("Launching GUI for manual inspection + automated hooks...") + # Use a log file for GUI output + with open("gui_diag.log", "w") as log_file: + gui_process = subprocess.Popen( + [sys.executable, "gui_2.py", "--enable-test-hooks"], + cwd=PROJECT_ROOT, + stdout=log_file, + stderr=log_file, + text=True + ) + + client = ApiHookClient() + print("Waiting for GUI...") + if not client.wait_for_server(timeout=10): + print("GUI failed to start.") + gui_process.terminate() + return + + # Pushing state + track_data = {"id": "diag_track", "title": "Diagnostic Track"} + tickets_data = [{"id": f"T{i}", "status": "todo"} for i in range(3)] + + print("Pushing state update...") + client.push_event("mma_state_update", { + "status": "active", + "active_tier": "Tier 1", + "track": track_data, + "tickets": tickets_data + }) + + time.sleep(2) + + print("Pushing approval request...") + client.push_event("mma_step_approval", { + "ticket_id": "T0", + "payload": "Get-ChildItem" + }) + + print("\nGUI is running. Check 'gui_diag.log' for output.") + print("I will now poll mma_status every 2 seconds. Ctrl+C to stop.") + + try: + start_poll = time.time() + while time.time() - start_poll < 30: + try: + status = client.get_mma_status() + print(f"[{time.strftime('%H:%M:%S')}] Status: {status.get('mma_status')}, Pending Approval: {status.get('pending_approval')}") + except Exception as e: + print(f"[{time.strftime('%H:%M:%S')}] Error querying status: {e}") + time.sleep(2) + except KeyboardInterrupt: + print("Stopping...") + finally: + gui_process.terminate() + +if __name__ == "__main__": + diag_run() diff --git a/tests/visual_mma_verification.py b/tests/visual_mma_verification.py new file mode 100644 index 0000000..124f308 --- /dev/null +++ b/tests/visual_mma_verification.py @@ -0,0 +1,184 @@ + +import subprocess +import time +import sys +import os +import glob # Will be used to find api_hook_client.py path if needed, though sys.path modification is better. + +# --- Configuration --- +GUI_SCRIPT = 'gui_2.py' +TEST_HOOKS_FLAG = '--enable-test-hooks' +API_HOOK_CLIENT_MODULE = 'api_hook_client' +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) # Calculate project root + +# Ensure project root is in sys.path to import modules like api_hook_client +if PROJECT_ROOT not in sys.path: + sys.path.insert(0, PROJECT_ROOT) + print(f"Added '{PROJECT_ROOT}' to sys.path for imports.") + +try: + from api_hook_client import ApiHookClient +except ImportError as e: + print(f"Error: Could not import ApiHookClient from '{API_HOOK_CLIENT_MODULE}'.") + print(f"Please ensure '{API_HOOK_CLIENT_MODULE}.py' is accessible and '{PROJECT_ROOT}' is correctly added to sys.path.") + print(f"Import error: {e}") + sys.exit(1) + +def run_visual_mma_verification(): + print("Starting visual MMA verification test...") + + # Change current directory to project root to ensure relative paths are correct for gui_2.py + original_dir = os.getcwd() + if original_dir != PROJECT_ROOT: + try: + os.chdir(PROJECT_ROOT) + print(f"Changed current directory to: {PROJECT_ROOT}") + except FileNotFoundError: + print(f"Error: Project root directory '{PROJECT_ROOT}' not found.") + return + + # 1. Launch gui_2.py with --enable-test-hooks + gui_command = [sys.executable, GUI_SCRIPT, TEST_HOOKS_FLAG] + print(f"Launching GUI with command: {' '.join(gui_command)}") + + try: + gui_process = subprocess.Popen( + gui_command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + cwd=PROJECT_ROOT # Explicitly set working directory + ) + print(f"GUI process started with PID: {gui_process.pid}") + except FileNotFoundError: + print(f"Error: Could not find Python executable '{sys.executable}' or script '{GUI_SCRIPT}'.") + return + except Exception as e: + print(f"Error starting GUI process: {e}") + return + + # Give the GUI a moment to start and open the hook server. + print("Waiting for GUI to initialize and hook server to start (5 seconds)...") + time.sleep(5) + + # Check if the GUI process exited prematurely + if gui_process.poll() is not None: + print(f"Error: GUI process exited prematurely with return code {gui_process.returncode}.") + stderr_output = gui_process.stderr.read() + if stderr_output: + print("--- GUI Stderr ---") + print(stderr_output) + print("------------------") + return + + # 2. Use api_hook_client.ApiHookClient to push events + try: + client = ApiHookClient() + print("ApiHookClient initialized successfully.") + except Exception as e: + print(f"Failed to initialize ApiHookClient. Ensure the hook server is running on port 8999. Error: {e}") + if gui_process: + gui_process.terminate() + gui_process.wait() + return + + # 3. Include at least 5 tickets in different states + track_data = { + "id": "visual_test_track", + "title": "Visual Verification Track", + "description": "A track to verify MMA UI components" + } + tickets_data = [ + {"id": "TICKET-001", "target_file": "core.py", "status": "todo"}, + {"id": "TICKET-002", "target_file": "utils.py", "status": "running"}, + {"id": "TICKET-003", "target_file": "tests.py", "status": "complete"}, + {"id": "TICKET-004", "target_file": "api.py", "status": "blocked"}, + {"id": "TICKET-005", "target_file": "gui.py", "status": "paused"}, + ] + + print("\nPushing MMA state update...") + try: + payload = { + "status": "running", + "active_tier": "Tier 3", + "track": track_data, + "tickets": tickets_data + } + client.push_event("mma_state_update", payload) + print(" - MMA state update pushed.") + except Exception as e: + print(f" - Warning: Failed to push mma_state_update: {e}") + + # 4. After a short delay, push an 'mma_step_approval' event + print("\nWaiting for GUI to render ticket queue and progress bar (3 seconds)...") + time.sleep(3) + + print("Pushing 'mma_step_approval' event to trigger HITL modal...") + try: + approval_payload = { + "ticket_id": "TICKET-002", + "payload": "powershell -Command \"Write-Host 'Hello from Tier 3'\"" + } + client.push_event("mma_step_approval", approval_payload) + print("mma_step_approval event pushed successfully.") + except Exception as e: + print(f"Error pushing mma_step_approval event: {e}") + + # 5. Provide clear print statements for manual verification + print(" +--- Manual Verification Instructions ---") + print("Please visually inspect the running GUI application:") + print("1. MMA Dashboard: Ensure the 'MMA Dashboard' panel is visible and active.") + print("2. Ticket Queue: Verify the 'Ticket Queue' section displays all 5 tickets with correct statuses:") + print(" - TICKET-001: Should be 'todo'") + print(" - TICKET-002: Should be 'running'") + print(" - TICKET-003: Should be 'complete'") + print(" - TICKET-004: Should be 'blocked'") + print(" - TICKET-005: Should be 'paused'") + print(" Observe the distinct status colors for each ticket.") + print("3. Progress Bar: Check that the progress bar correctly reflects the completed/total tickets (e.g., 1/5).") + print("4. Approval Modal: Confirm that an 'MMA Step Approval' modal has appeared.") + print(" - Verify it contains a 'Proposed Tool Call' section (e.g., showing 'powershell -Command...').") + print(" - Ensure there is an option/button to 'Edit Payload'.") + print(" +--------------------------------------") + print("The test script has finished its automated actions.") + print("The GUI application is still running. Please perform manual verification.") + print("Press Enter in this terminal to stop the GUI process and exit the test script.") + + try: + input() + except EOFError: + print("EOF received, exiting.") + pass + + print(" +Stopping GUI process...") + if gui_process: + try: + gui_process.terminate() + gui_process.wait(timeout=10) + print("GUI process terminated gracefully.") + except subprocess.TimeoutExpired: + print("GUI process did not terminate within the timeout. Killing it forcefully.") + gui_process.kill() + gui_process.wait() + except Exception as e: + print(f"Error during GUI process termination: {e}") + else: + print("GUI process was not started or already terminated.") + + print("Visual MMA verification test script finished.") + + # Restore original directory + if original_dir != PROJECT_ROOT: + try: + os.chdir(original_dir) + print(f"Restored original working directory: {original_dir}") + except FileNotFoundError: + print(f"Warning: Could not restore original working directory '{original_dir}'.") + +# --- Main execution --- +if __name__ == "__main__": + # When the script is executed directly, ensure it runs from the correct context. + run_visual_mma_verification()