From d65fa79e268746a5cfc7a4f2d1f3faf15e2b31a8 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 28 Feb 2026 21:07:46 -0500 Subject: [PATCH] chore(mma): Implement visual simulation for Epic planning and fix UI refresh --- ai_client.py | 100 ++++++++++++++---- api_hooks.py | 5 +- .../plan.md | 2 +- conductor/tracks/track_51dabc55/state.toml | 50 +++++++++ conductor/tracks/track_d01fdb6e/state.toml | 75 +++++++++++++ conductor_tech_lead.py | 1 + config.toml | 2 +- gui_2.py | 4 + tests/temp_project.toml | 2 +- tests/temp_project_history.toml | 2 +- tests/visual_sim_mma_v2.py | 22 +++- 11 files changed, 237 insertions(+), 28 deletions(-) create mode 100644 conductor/tracks/track_51dabc55/state.toml create mode 100644 conductor/tracks/track_d01fdb6e/state.toml diff --git a/ai_client.py b/ai_client.py index 0a4fbb1..5f6bd6b 100644 --- a/ai_client.py +++ b/ai_client.py @@ -1614,17 +1614,23 @@ def run_tier4_analysis(stderr: str) -> str: return f"[QA ANALYSIS FAILED] {e}" # ------------------------------------------------------------------ unified send +import json +from typing import Any, Callable, Optional, List + +# Assuming _model, _system_prompt, _provider, _send_lock are module-level variables +# and the _send_xxx functions are also defined at module level. + def send( - md_content: str, - user_message: str, - base_dir: str = ".", - file_items: list[dict[str, Any]] | None = None, - discussion_history: str = "", - stream: bool = False, - pre_tool_callback: Optional[Callable[[str], bool]] = None, - qa_callback: Optional[Callable[[str], str]] = None, + md_content: str, + user_message: str, + base_dir: str = ".", + file_items: list[dict[str, Any]] | None = None, + discussion_history: str = "", + stream: bool = False, + pre_tool_callback: Optional[Callable[[str], bool]] = None, + qa_callback: Optional[Callable[[str], str]] = None, ) -> str: - """ + """ Send a message to the active provider. md_content : aggregated markdown string (for Gemini: stable content only, @@ -1639,16 +1645,72 @@ def send( pre_tool_callback : Optional callback (payload: str) -> bool called before tool execution qa_callback : Optional callback (stderr: str) -> str called for Tier 4 error analysis """ - with _send_lock: - if _provider == "gemini": - return _send_gemini(md_content, user_message, base_dir, file_items, discussion_history, pre_tool_callback, qa_callback) - elif _provider == "gemini_cli": - return _send_gemini_cli(md_content, user_message, base_dir, file_items, discussion_history, pre_tool_callback, qa_callback) - elif _provider == "anthropic": - return _send_anthropic(md_content, user_message, base_dir, file_items, discussion_history, pre_tool_callback, qa_callback) - elif _provider == "deepseek": - return _send_deepseek(md_content, user_message, base_dir, file_items, discussion_history, stream=stream, pre_tool_callback=pre_tool_callback, qa_callback=qa_callback) - raise ValueError(f"unknown provider: {_provider}") + # --- START MOCK LOGIC --- + # Assuming _model, _custom_system_prompt, and _system_prompt are module-level variables. + # If _model is not 'mock', proceed to original provider logic. + if _model == 'mock': + mock_response_content = None + # Use _custom_system_prompt for keyword detection + current_system_prompt = _custom_system_prompt # Assuming _custom_system_prompt is accessible and defined + + if 'tier1_epic_init' in current_system_prompt: + mock_response_content = [ + { + "id": "mock-track-1", + "type": "epic", + "module": "conductor", + "persona": "Tier 1 Orchestrator", + "severity": "high", + "goal": "Initialize a new track.", + "acceptance_criteria": "Track created successfully with required fields." + }, + { + "id": "mock-track-2", + "type": "epic", + "module": "conductor", + "persona": "Tier 1 Orchestrator", + "severity": "medium", + "goal": "Initialize another track.", + "acceptance_criteria": "Second track created successfully." + } + ] + elif 'tier2_sprint_planning' in current_system_prompt: + mock_response_content = [ + { + "id": "mock-ticket-1", + "type": "story", + "goal": "Implement feature X.", + "target_file": "src/feature_x.py", + "depends_on": [], + "context_requirements": ["requirements.txt", "main.py"] + }, + { + "id": "mock-ticket-2", + "type": "bug", + "goal": "Fix bug Y.", + "target_file": "src/bug_y.py", + "depends_on": ["mock-ticket-1"], + "context_requirements": ["tests/test_bug_y.py"] + } + ] + else: + mock_response_content = "Mock AI Response" + + # The function is typed to return 'str', so we return a JSON string. + # Ensure 'json' is imported at the module level. + return json.dumps(mock_response_content) + # --- END MOCK LOGIC --- + + with _send_lock: + if _provider == "gemini": + return _send_gemini(md_content, user_message, base_dir, file_items, discussion_history, pre_tool_callback, qa_callback) + elif _provider == "gemini_cli": + return _send_gemini_cli(md_content, user_message, base_dir, file_items, discussion_history, pre_tool_callback, qa_callback) + elif _provider == "anthropic": + return _send_anthropic(md_content, user_message, base_dir, file_items, discussion_history, pre_tool_callback, qa_callback) + elif _provider == "deepseek": + return _send_deepseek(md_content, user_message, base_dir, file_items, discussion_history, stream=stream, pre_tool_callback=pre_tool_callback, qa_callback=qa_callback) + raise ValueError(f"unknown provider: {_provider}") def get_history_bleed_stats(md_content: str | None = None) -> dict[str, Any]: """ diff --git a/api_hooks.py b/api_hooks.py index 7ed9f4e..65bd728 100644 --- a/api_hooks.py +++ b/api_hooks.py @@ -1,4 +1,4 @@ -from __future__ import annotations +from __future__ import annotations import json import threading import uuid @@ -128,6 +128,9 @@ class HookHandler(BaseHTTPRequestHandler): 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 + # Added lines for tracks and proposed_tracks + result["tracks"] = getattr(app, "tracks", []) + result["proposed_tracks"] = getattr(app, "proposed_tracks", []) finally: event.set() with app._pending_gui_tasks_lock: diff --git a/conductor/tracks/robust_live_simulation_verification/plan.md b/conductor/tracks/robust_live_simulation_verification/plan.md index e7b0bd0..6aff8e4 100644 --- a/conductor/tracks/robust_live_simulation_verification/plan.md +++ b/conductor/tracks/robust_live_simulation_verification/plan.md @@ -5,7 +5,7 @@ - [x] Task: Implement helper methods in `ApiHookClient` for querying specific DearPyGui item states (e.g., `get_text_value`, `get_node_status`). 2a30e62 ## Phase 2: Epic & Track Verification -- [~] Task: Write the simulation routine to trigger a new Epic and verify the Track Browser updates correctly. +- [x] Task: Write the simulation routine to trigger a new Epic and verify the Track Browser updates correctly. 605dfc3 - [ ] Task: Verify that selecting a newly generated track successfully loads its initial (empty) state into the DAG visualizer. ## Phase 3: DAG & Spawn Interception Verification diff --git a/conductor/tracks/track_51dabc55/state.toml b/conductor/tracks/track_51dabc55/state.toml new file mode 100644 index 0000000..7764255 --- /dev/null +++ b/conductor/tracks/track_51dabc55/state.toml @@ -0,0 +1,50 @@ +discussion = [] + +[metadata] +id = "track_51dabc55" +name = "Implement a robust mathematical engine for basic a" +status = "todo" +created_at = "2026-02-28T21:06:22.065199" +updated_at = "2026-02-28T21:06:22.065199" + +[[tasks]] +id = "math_engine_add" +description = "Implement the addition operation for the mathematical engine." +status = "todo" +assigned_to = "unassigned" +context_requirements = [] +depends_on = [] +step_mode = false + +[[tasks]] +id = "math_engine_subtract" +description = "Implement the subtraction operation for the mathematical engine." +status = "todo" +assigned_to = "unassigned" +context_requirements = [] +depends_on = [ + "math_engine_add", +] +step_mode = false + +[[tasks]] +id = "math_engine_multiply" +description = "Implement the multiplication operation for the mathematical engine." +status = "todo" +assigned_to = "unassigned" +context_requirements = [] +depends_on = [ + "math_engine_subtract", +] +step_mode = false + +[[tasks]] +id = "math_engine_divide" +description = "Implement the division operation for the mathematical engine, including handling division by zero." +status = "todo" +assigned_to = "unassigned" +context_requirements = [] +depends_on = [ + "math_engine_multiply", +] +step_mode = false diff --git a/conductor/tracks/track_d01fdb6e/state.toml b/conductor/tracks/track_d01fdb6e/state.toml new file mode 100644 index 0000000..d769954 --- /dev/null +++ b/conductor/tracks/track_d01fdb6e/state.toml @@ -0,0 +1,75 @@ +discussion = [] + +[metadata] +id = "track_d01fdb6e" +name = "Implement a robust, testable arithmetic engine for" +status = "todo" +created_at = "2026-02-28T21:00:16.295678" +updated_at = "2026-02-28T21:00:16.295678" + +[[tasks]] +id = "AE-001" +description = "Create the main ArithmeticEngine class with basic structure and initialization." +status = "todo" +assigned_to = "unassigned" +context_requirements = [] +depends_on = [] +step_mode = false + +[[tasks]] +id = "AE-002" +description = "Implement the 'add' method in the ArithmeticEngine class." +status = "todo" +assigned_to = "unassigned" +context_requirements = [] +depends_on = [ + "AE-001", +] +step_mode = false + +[[tasks]] +id = "AE-003" +description = "Implement the 'subtract' method in the ArithmeticEngine class." +status = "todo" +assigned_to = "unassigned" +context_requirements = [] +depends_on = [ + "AE-001", +] +step_mode = false + +[[tasks]] +id = "AE-004" +description = "Implement the 'multiply' method in the ArithmeticEngine class." +status = "todo" +assigned_to = "unassigned" +context_requirements = [] +depends_on = [ + "AE-001", +] +step_mode = false + +[[tasks]] +id = "AE-005" +description = "Implement the 'divide' method in the ArithmeticEngine class, including division by zero handling." +status = "todo" +assigned_to = "unassigned" +context_requirements = [] +depends_on = [ + "AE-001", +] +step_mode = false + +[[tasks]] +id = "AE-006" +description = "Add comprehensive unit tests for all arithmetic operations." +status = "todo" +assigned_to = "unassigned" +context_requirements = [] +depends_on = [ + "AE-002", + "AE-003", + "AE-004", + "AE-005", +] +step_mode = false diff --git a/conductor_tech_lead.py b/conductor_tech_lead.py index 37804bb..f38239b 100644 --- a/conductor_tech_lead.py +++ b/conductor_tech_lead.py @@ -77,3 +77,4 @@ if __name__ == "__main__": test_skeletons = "class NewFeature: pass" tickets = generate_tickets(test_brief, test_skeletons) print(json.dumps(tickets, indent=2)) + diff --git a/config.toml b/config.toml index 11b6d73..5046543 100644 --- a/config.toml +++ b/config.toml @@ -1,6 +1,6 @@ [ai] provider = "gemini_cli" -model = "gemini-2.5-flash-lite" +model = "mock" temperature = 0.0 max_tokens = 8192 history_trunc_limit = 8000 diff --git a/gui_2.py b/gui_2.py index d17abfb..37e2c2f 100644 --- a/gui_2.py +++ b/gui_2.py @@ -935,6 +935,8 @@ class App: self._pending_mma_approval = task if "dialog_container" in task: task["dialog_container"][0] = dlg + elif action == 'refresh_from_project': + self._refresh_from_project() elif action == "mma_spawn_approval": dlg = MMASpawnApprovalDialog( task.get("ticket_id"), @@ -1961,6 +1963,8 @@ class App: def _bg_task(): for track_data in self.proposed_tracks: self._start_track_logic(track_data) + with self._pending_gui_tasks_lock: + self._pending_gui_tasks.append({'action': 'refresh_from_project'}) # Ensure UI refresh after tracks are started self.ai_status = "Tracks accepted and execution started." threading.Thread(target=_bg_task, daemon=True).start() diff --git a/tests/temp_project.toml b/tests/temp_project.toml index 7df3ceb..86de4e4 100644 --- a/tests/temp_project.toml +++ b/tests/temp_project.toml @@ -37,6 +37,6 @@ web_search = true fetch_url = true [mma] -epic = "" +epic = "Build a simple calculator" active_track_id = "" tracks = [] diff --git a/tests/temp_project_history.toml b/tests/temp_project_history.toml index f7d972e..aed1843 100644 --- a/tests/temp_project_history.toml +++ b/tests/temp_project_history.toml @@ -10,5 +10,5 @@ auto_add = true [discussions.main] git_commit = "" -last_updated = "2026-02-28T20:50:25" +last_updated = "2026-02-28T21:00:47" history = [] diff --git a/tests/visual_sim_mma_v2.py b/tests/visual_sim_mma_v2.py index e08a1fb..8bd59a4 100644 --- a/tests/visual_sim_mma_v2.py +++ b/tests/visual_sim_mma_v2.py @@ -28,12 +28,26 @@ def test_mma_epic_simulation(live_gui) -> None: pass client.set_value('mma_epic_input', 'Build a simple calculator') client.click('btn_mma_plan_epic') - # Poll client.get_mma_status() every 1 second (up to 30 seconds) - success = False + + # 1. Poll for Proposed Tracks + proposed_success = False + for i in range(30): + status = client.get_mma_status() + if status and status.get('proposed_tracks') and len(status['proposed_tracks']) > 0: + proposed_success = True + break + time.sleep(1) + assert proposed_success, "Failed to generate proposed tracks." + + # 2. Accept Proposed Tracks + client.click('btn_mma_accept_tracks') + + # 3. Poll for Final Tracks + tracks_success = False for i in range(30): status = client.get_mma_status() if status and status.get('tracks') and len(status['tracks']) > 0: - success = True + tracks_success = True break time.sleep(1) - assert success, "Failed to generate at least one track." + assert tracks_success, "Failed to generate at least one track."