chore(conductor): Mark track 'Curate Provider Registries' as complete. Includes critical fixes for RecursionError, NoneType Hook API responses, and plurality mismatches.

This commit is contained in:
2026-05-08 22:01:15 -04:00
parent b89abb2670
commit 9f18497786
15 changed files with 97 additions and 89 deletions
+9 -48
View File
@@ -134,7 +134,6 @@ class AppController:
The headless controller for the Manual Slop application.
Owns the application state and manages background services.
"""
PROVIDERS: list[str] = ["gemini", "anthropic", "gemini_cli", "deepseek", "minimax"]
def __init__(self):
# Initialize locks first to avoid initialization order issues
@@ -646,8 +645,6 @@ class AppController:
}
def _update_gcli_adapter(self, path: str) -> None:
sys.stderr.write(f"[DEBUG] _update_gcli_adapter called with: {path}\n")
sys.stderr.flush()
if not ai_client._gemini_cli_adapter:
ai_client._gemini_cli_adapter = ai_client.GeminiCliAdapter(binary_path=str(path))
else:
@@ -725,16 +722,12 @@ class AppController:
if not self._pending_gui_tasks:
return
sys.stderr.write(f"[DEBUG] _process_pending_gui_tasks: processing {len(self._pending_gui_tasks)} tasks\n")
sys.stderr.flush()
with self._pending_gui_tasks_lock:
tasks = self._pending_gui_tasks[:]
self._pending_gui_tasks.clear()
for task in tasks:
try:
action = task.get("action")
sys.stderr.write(f"[DEBUG] Processing GUI task: action={action}\n")
sys.stderr.flush()
if action:
session_logger.log_api_hook("PROCESS_TASK", action, str(task))
# ...
@@ -746,8 +739,6 @@ class AppController:
self._tool_log_dirty = True
elif action == "set_ai_status":
self.ai_status = task.get("payload", "")
sys.stderr.write(f"[DEBUG] Updated ai_status via task to: {self.ai_status}\n")
sys.stderr.flush()
elif action == "set_mma_status":
self.mma_status = task.get("payload", "")
elif action == "handle_ai_response":
@@ -775,8 +766,6 @@ class AppController:
else:
self.ai_response = text
self.ai_status = payload.get("status", "done")
sys.stderr.write(f"[DEBUG] Updated ai_status to: {self.ai_status}\n")
sys.stderr.flush()
self._trigger_blink = True
if not stream_id:
self._token_stats_dirty = True
@@ -799,9 +788,6 @@ class AppController:
if not isinstance(p, dict):
p = task # Fallback to task itself if payload is missing or wrong type
sys.stderr.write(f"[DEBUG] mma_state_update: status={p.get('status')} active_tier={p.get('active_tier')}\n")
sys.stderr.flush()
track_data = p.get("track")
is_active_track = False
if track_data and self.active_track and track_data.get("id") == self.active_track.id:
@@ -853,20 +839,14 @@ class AppController:
elif action == "set_value":
item = task.get("item")
value = task.get("value")
sys.stderr.write(f"[DEBUG] Processing set_value: {item}={value}\n")
sys.stderr.flush()
if item in self._settable_fields:
attr_name = self._settable_fields[item]
setattr(self, attr_name, value)
sys.stderr.write(f"[DEBUG] Set {attr_name} to {value}\n")
sys.stderr.flush()
if item == "gcli_path":
self._update_gcli_adapter(str(value))
elif action == "click":
item = task.get("item")
user_data = task.get("user_data")
sys.stderr.write(f"[DEBUG] Processing click: {item} (user_data={user_data})\n")
sys.stderr.flush()
if item == "btn_project_new_automated":
self._cb_new_project_automated(user_data)
elif item == "btn_mma_load_track":
@@ -1492,7 +1472,7 @@ class AppController:
def do_fetch() -> None:
try:
for p in self.PROVIDERS:
for p in models.PROVIDERS:
try:
self.all_available_models[p] = ai_client.list_models(p)
except Exception as e:
@@ -1512,14 +1492,10 @@ class AppController:
def start_services(self, app: Any = None):
"""Starts background threads."""
sys.stderr.write("[DEBUG] AppController.start_services called\n")
sys.stderr.flush()
self._prune_old_logs()
self._init_ai_and_hooks(app)
self._loop_thread = threading.Thread(target=self._run_event_loop, daemon=True)
self._loop_thread.start()
sys.stderr.write(f"[DEBUG] _loop_thread started: {self._loop_thread.ident}\n")
sys.stderr.flush()
def shutdown(self) -> None:
"""Stops background threads and cleans up resources."""
@@ -1568,13 +1544,8 @@ class AppController:
def _process_event_queue(self) -> None:
"""Listens for and processes events from the SyncEventQueue."""
sys.stderr.write("[DEBUG] _process_event_queue entered\n")
sys.stderr.flush()
while True:
event_name, payload = self.event_queue.get()
sys.stderr.write(f"[DEBUG] _process_event_queue got event: {event_name} with payload: {str(payload)[:100]}\n")
sys.stderr.flush()
if event_name == "shutdown":
break
if event_name == "user_request":
@@ -1641,8 +1612,6 @@ class AppController:
ai_client.set_model_params(self.temperature, self.max_tokens, self.history_trunc_limit, self.top_p)
ai_client.set_agent_tools(self.ui_agent_tools) # Force update adapter path right before send to bypass potential duplication issues
self._update_gcli_adapter(self.ui_gemini_cli_path)
sys.stderr.write(f"[DEBUG] Calling ai_client.send with provider={ai_client.get_provider()}, model={self.current_model}, gcli_path={self.ui_gemini_cli_path}\n")
sys.stderr.flush()
try:
resp = ai_client.send(
event.stable_md,
@@ -1659,12 +1628,8 @@ class AppController:
)
self.event_queue.put("response", {"text": resp, "status": "done", "role": "AI"})
except ai_client.ProviderError as e:
sys.stderr.write(f"[DEBUG] _handle_request_event ai_client.ProviderError: {e.ui_message()}\n")
sys.stderr.flush()
self.event_queue.put("response", {"text": e.ui_message(), "status": "error", "role": "Vendor API"})
except Exception as e:
sys.stderr.write(f"[DEBUG] _handle_request_event ERROR: {e}\n{traceback.format_exc()}\n")
sys.stderr.flush()
self.event_queue.put("response", {"text": f"ERROR: {e}", "status": "error", "role": "System"})
def _offload_entry_payload(self, entry: Dict[str, Any]) -> Dict[str, Any]:
@@ -1806,17 +1771,11 @@ class AppController:
})
def _confirm_and_run(self, script: str, base_dir: str, qa_callback: Optional[Callable[[str], str]] = None, patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> Optional[str]:
sys.stderr.write(f"[DEBUG] _confirm_and_run called. test_hooks={self.test_hooks_enabled}, manual_approve={getattr(self, 'ui_manual_approve', False)}\n")
sys.stderr.flush()
if self.test_hooks_enabled and not getattr(self, "ui_manual_approve", False):
sys.stderr.write("[DEBUG] Auto-approving script.\n")
sys.stderr.flush()
self._set_status("running powershell...")
output = shell_runner.run_powershell(script, base_dir, qa_callback=qa_callback, patch_callback=patch_callback)
self._set_status("powershell done, awaiting AI...")
return output
sys.stderr.write("[DEBUG] Creating ConfirmDialog.\n")
sys.stderr.flush()
dialog = ConfirmDialog(script, base_dir)
is_headless = "--headless" in sys.argv
if is_headless:
@@ -1834,13 +1793,7 @@ class AppController:
"base_dir": str(base_dir),
"ts": time.time()
})
sys.stderr.write(f"[DEBUG] Appended script_confirmation_required to _api_event_queue. ID={dialog._uid}\n")
sys.stderr.flush()
sys.stderr.write(f"[DEBUG] Waiting for dialog ID={dialog._uid}...\n")
sys.stderr.flush()
approved, final_script = dialog.wait()
sys.stderr.write(f"[DEBUG] Dialog ID={dialog._uid} finished wait. approved={approved}\n")
sys.stderr.flush()
if is_headless:
with self._pending_dialog_lock:
if dialog._uid in self._pending_actions:
@@ -1912,6 +1865,14 @@ class AppController:
return True
return False
@property
def _pending_mma_spawn(self) -> Optional[Dict[str, Any]]:
return self._pending_mma_spawns[0] if self._pending_mma_spawns else None
@property
def _pending_mma_approval(self) -> Optional[Dict[str, Any]]:
return self._pending_mma_approvals[0] if self._pending_mma_approvals else None
@property
def current_provider(self) -> str:
return self._current_provider