chore(conductor): Archive completed track 'MMA Core Engine Implementation'
This commit is contained in:
@@ -3,7 +3,7 @@ import json
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
class ApiHookClient:
|
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.base_url = base_url
|
||||||
self.max_retries = max_retries
|
self.max_retries = max_retries
|
||||||
self.retry_delay = retry_delay
|
self.retry_delay = retry_delay
|
||||||
@@ -26,8 +26,8 @@ class ApiHookClient:
|
|||||||
headers = {'Content-Type': 'application/json'}
|
headers = {'Content-Type': 'application/json'}
|
||||||
|
|
||||||
last_exception = None
|
last_exception = None
|
||||||
# Lower request timeout for local server by default
|
# Increase default request timeout for local server
|
||||||
req_timeout = timeout if timeout is not None else 0.5
|
req_timeout = timeout if timeout is not None else 2.0
|
||||||
|
|
||||||
for attempt in range(self.max_retries + 1):
|
for attempt in range(self.max_retries + 1):
|
||||||
try:
|
try:
|
||||||
@@ -77,6 +77,17 @@ class ApiHookClient:
|
|||||||
def get_session(self):
|
def get_session(self):
|
||||||
return self._make_request('GET', '/api/session')
|
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):
|
def get_performance(self):
|
||||||
"""Retrieves UI performance metrics."""
|
"""Retrieves UI performance metrics."""
|
||||||
return self._make_request('GET', '/api/performance')
|
return self._make_request('GET', '/api/performance')
|
||||||
|
|||||||
29
api_hooks.py
29
api_hooks.py
@@ -111,6 +111,35 @@ class HookHandler(BaseHTTPRequestHandler):
|
|||||||
"callback": get_val
|
"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):
|
if event.wait(timeout=2):
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header('Content-Type', 'application/json')
|
self.send_header('Content-Type', 'application/json')
|
||||||
|
|||||||
9
conductor/archive/mma_core_engine_20260224/index.md
Normal file
9
conductor/archive/mma_core_engine_20260224/index.md
Normal file
@@ -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/)
|
||||||
6
conductor/archive/mma_core_engine_20260224/metadata.json
Normal file
6
conductor/archive/mma_core_engine_20260224/metadata.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": "mma_core_engine_20260224",
|
||||||
|
"title": "MMA Core Engine Implementation",
|
||||||
|
"status": "planning",
|
||||||
|
"created_at": "2026-02-24T00:00:00.000000"
|
||||||
|
}
|
||||||
85
conductor/archive/mma_core_engine_20260224/plan.md
Normal file
85
conductor/archive/mma_core_engine_20260224/plan.md
Normal file
@@ -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.
|
||||||
39
conductor/archive/mma_core_engine_20260224/spec.md
Normal file
39
conductor/archive/mma_core_engine_20260224/spec.md
Normal file
@@ -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.
|
||||||
@@ -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**
|
- [~] **Track: MMA Core Engine Implementation**
|
||||||
*Link: [./tracks/mma_core_engine_20260224/](./tracks/mma_core_engine_20260224/)*
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -70,3 +70,16 @@
|
|||||||
- [x] Task: Tiered Metrics & Log Links
|
- [x] Task: Tiered Metrics & Log Links
|
||||||
- [x] Add visual indicators for the active model Tier.
|
- [x] Add visual indicators for the active model Tier.
|
||||||
- [x] Provide clickable links to sub-agent logs.
|
- [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.
|
||||||
@@ -28,6 +28,7 @@ active = "C:\\projects\\manual_slop\\tests\\temp_project.toml"
|
|||||||
"Context Hub" = true
|
"Context Hub" = true
|
||||||
"Files & Media" = true
|
"Files & Media" = true
|
||||||
"AI Settings" = true
|
"AI Settings" = true
|
||||||
|
"MMA Dashboard" = true
|
||||||
"Discussion Hub" = true
|
"Discussion Hub" = true
|
||||||
"Operations Hub" = true
|
"Operations Hub" = true
|
||||||
Theme = true
|
Theme = true
|
||||||
|
|||||||
12
gui_2.py
12
gui_2.py
@@ -962,13 +962,16 @@ class App:
|
|||||||
def _handle_approve_ask(self):
|
def _handle_approve_ask(self):
|
||||||
"""Responds with approval for a pending /api/ask request."""
|
"""Responds with approval for a pending /api/ask request."""
|
||||||
if not self._ask_request_id: return
|
if not self._ask_request_id: return
|
||||||
|
request_id = self._ask_request_id
|
||||||
|
def do_post():
|
||||||
try:
|
try:
|
||||||
requests.post(
|
requests.post(
|
||||||
"http://127.0.0.1:8999/api/ask/respond",
|
"http://127.0.0.1:8999/api/ask/respond",
|
||||||
json={"request_id": self._ask_request_id, "response": {"approved": True}},
|
json={"request_id": request_id, "response": {"approved": True}},
|
||||||
timeout=2
|
timeout=2
|
||||||
)
|
)
|
||||||
except Exception as e: print(f"Error responding to ask: {e}")
|
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._pending_ask_dialog = False
|
||||||
self._ask_request_id = None
|
self._ask_request_id = None
|
||||||
self._ask_tool_data = None
|
self._ask_tool_data = None
|
||||||
@@ -976,13 +979,16 @@ class App:
|
|||||||
def _handle_reject_ask(self):
|
def _handle_reject_ask(self):
|
||||||
"""Responds with rejection for a pending /api/ask request."""
|
"""Responds with rejection for a pending /api/ask request."""
|
||||||
if not self._ask_request_id: return
|
if not self._ask_request_id: return
|
||||||
|
request_id = self._ask_request_id
|
||||||
|
def do_post():
|
||||||
try:
|
try:
|
||||||
requests.post(
|
requests.post(
|
||||||
"http://127.0.0.1:8999/api/ask/respond",
|
"http://127.0.0.1:8999/api/ask/respond",
|
||||||
json={"request_id": self._ask_request_id, "response": {"approved": False}},
|
json={"request_id": request_id, "response": {"approved": False}},
|
||||||
timeout=2
|
timeout=2
|
||||||
)
|
)
|
||||||
except Exception as e: print(f"Error responding to ask: {e}")
|
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._pending_ask_dialog = False
|
||||||
self._ask_request_id = None
|
self._ask_request_id = None
|
||||||
self._ask_tool_data = None
|
self._ask_tool_data = None
|
||||||
@@ -1696,7 +1702,7 @@ class App:
|
|||||||
else:
|
else:
|
||||||
imgui.text("Proposed Tool Call:")
|
imgui.text("Proposed Tool Call:")
|
||||||
imgui.begin_child("mma_preview", imgui.ImVec2(600, 300), True)
|
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.end_child()
|
||||||
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
|
|||||||
0
gui_diag.log
Normal file
0
gui_diag.log
Normal file
@@ -70,7 +70,7 @@ def get_model_for_role(role: str) -> str:
|
|||||||
elif role == 'tier2-tech-lead' or role == 'tier2':
|
elif role == 'tier2-tech-lead' or role == 'tier2':
|
||||||
return 'gemini-3-flash-preview'
|
return 'gemini-3-flash-preview'
|
||||||
elif role == 'tier3-worker' or role == 'tier3':
|
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':
|
elif role == 'tier4-qa' or role == 'tier4':
|
||||||
return 'gemini-2.5-flash-lite'
|
return 'gemini-2.5-flash-lite'
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -5,10 +5,17 @@ roles = [
|
|||||||
"System",
|
"System",
|
||||||
"Reasoning",
|
"Reasoning",
|
||||||
]
|
]
|
||||||
active = "main"
|
active = "mma_human veriffication"
|
||||||
auto_add = true
|
auto_add = true
|
||||||
|
|
||||||
[discussions.main]
|
[discussions.main]
|
||||||
git_commit = ""
|
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 = []
|
history = []
|
||||||
|
|||||||
96
tests/verify_mma_gui_robust.py
Normal file
96
tests/verify_mma_gui_robust.py
Normal file
@@ -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()
|
||||||
69
tests/visual_diag.py
Normal file
69
tests/visual_diag.py
Normal file
@@ -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()
|
||||||
184
tests/visual_mma_verification.py
Normal file
184
tests/visual_mma_verification.py
Normal file
@@ -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()
|
||||||
Reference in New Issue
Block a user