conductor(archive): move legacy superpowers specs/plans from docs/superpowers/ to conductor/archive/superpowers/

Resolves HIGH-priority recommendation #3 from superpowers_review_20260619 §16.1 (dual-convention).
41 files moved (21 specs + 20 plans + 2 subdirs + root):
- docs/superpowers/specs/*.md  ->  conductor/archive/superpowers/specs/*.md (21 files)
- docs/superpowers/plans/*.md  ->  conductor/archive/superpowers/plans/*.md (20 files)

Original taxonomy preserved (specs/ and plans/ subdirectories intact).
The superpowers-plugin source itself remains at C:/Users/Ed/.cache/opencode/.../superpowers/skills/.

Future spec/plan artifacts must use the conductor convention: conductor/tracks/<id>/spec.md + plan.md.
This commit is contained in:
ed
2026-07-05 13:57:53 -04:00
parent 137868a193
commit 508baa69b6
44 changed files with 0 additions and 0 deletions
@@ -1,351 +0,0 @@
# ImGui Context Manager Suite Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Create a context manager suite that pairs `begin`/`end` ImGui calls using Python's `with` statement to reduce scope pairing errors and improve AI legibility.
**Architecture:** Base `ImGuiScope` class with factory functions returning scope objects. Each scope handles begin on `__enter__` and end on `__exit__`.
**Tech Stack:** Python 3.11+, imgui-bundle (Dear PyGui)
---
## File Structure
| Action | Path |
|--------|------|
| Create | `src/imgui_scopes.py` |
| Create | `tests/test_imgui_scopes.py` |
---
## Task 1: Create `src/imgui_scopes.py`
**Files:**
- Create: `src/imgui_scopes.py`
- [ ] **Step 1: Write the module**
```python
from __future__ import annotations
import imgui_bundle
class ImGuiScope:
def __init__(self, begin_fn, end_fn, *args, **kwargs):
self._begin_fn = begin_fn
self._end_fn = end_fn
self._args = args
self._kwargs = kwargs
self._opened: bool | tuple = False
self._entered = False
def __enter__(self):
result = self._begin_fn(*self._args, **self._kwargs)
if isinstance(result, tuple):
self._opened = result[0]
else:
self._opened = result
self._entered = bool(self._opened)
return self._opened
def __exit__(self, *args):
if self._entered:
self._end_fn()
return False
def imgui_window(name: str, visible: bool = True, flags: int = 0):
return ImGuiScope(imgui_bundle.imgui.begin, imgui_bundle.imgui.end, name, visible, flags)
def imgui_table(name: str, columns: int, flags: int = 0):
return ImGuiScope(imgui_bundle.imgui.begin_table, imgui_bundle.imgui.end_table, name, columns, flags)
def imgui_menu_bar():
return ImGuiScope(imgui_bundle.imgui.begin_menu_bar, imgui_bundle.imgui.end_menu_bar)
def imgui_menu(label: str):
return ImGuiScope(imgui_bundle.imgui.begin_menu, imgui_bundle.imgui.end_menu, label)
def imgui_child(id_str: str, width: float = 0, height: float = 0, flags: int = 0):
return ImGuiScope(imgui_bundle.imgui.begin_child, imgui_bundle.imgui.end_child, id_str, width, height, flags)
def imgui_group():
return ImGuiScope(imgui_bundle.imgui.begin_group, imgui_bundle.imgui.end_group)
def imgui_popup(id_str: str):
return ImGuiScope(imgui_bundle.imgui.begin_popup, imgui_bundle.imgui.end_popup, id_str)
def imgui_tooltip():
return ImGuiScope(imgui_bundle.imgui.begin_tooltip, imgui_bundle.imgui.end_tooltip)
def imgui_clipper(count: int):
return ImGuiScope(
lambda n: imgui_bundle.imgui.listing_builder.begin_clipper(n, -1),
imgui_bundle.imgui.listing_builder.end_clipper,
count
)
def node_editor_scope(name: str):
ed = imgui_bundle.imgui_node_editor
return ImGuiScope(ed.begin, ed.end, name)
```
- [ ] **Step 2: Verify syntax**
Run: `uv run python -c "import ast; ast.parse(open('src/imgui_scopes.py').read())"`
Expected: No output (parse successful)
- [ ] **Step 3: Commit**
```bash
git add src/imgui_scopes.py
git commit -m "feat(gui): Add ImGuiScope base class and scope helpers"
```
---
## Task 2: Create `tests/test_imgui_scopes.py`
**Files:**
- Create: `tests/test_imgui_scopes.py`
- Depends on: Task 1
- [ ] **Step 1: Write tests**
```python
from __future__ import annotations
from unittest.mock import patch, MagicMock
import pytest
class TestImGuiScope:
def test_enter_calls_begin_with_args(self):
from src.imgui_scopes import ImGuiScope
mock_begin = MagicMock(return_value=True)
mock_end = MagicMock()
scope = ImGuiScope(mock_begin, mock_end, "arg1", kwarg="val")
with scope:
pass
mock_begin.assert_called_once_with("arg1", kwarg="val")
def test_exit_calls_end_when_entered(self):
from src.imgui_scopes import ImGuiScope
mock_begin = MagicMock(return_value=True)
mock_end = MagicMock()
scope = ImGuiScope(mock_begin, mock_end)
with scope:
pass
mock_end.assert_called_once()
def test_exit_does_not_call_end_when_not_entered(self):
from src.imgui_scopes import ImGuiScope
mock_begin = MagicMock(return_value=False)
mock_end = MagicMock()
scope = ImGuiScope(mock_begin, mock_end)
with scope:
pass
mock_end.assert_not_called()
def test_enter_returns_opened_value(self):
from src.imgui_scopes import ImGuiScope
mock_begin = MagicMock(return_value=True)
mock_end = MagicMock()
scope = ImGuiScope(mock_begin, mock_end)
result = scope.__enter__()
assert result is True
def test_enter_handles_tuple_return(self):
from src.imgui_scopes import ImGuiScope
mock_begin = MagicMock(return_value=(True, "extra"))
mock_end = MagicMock()
scope = ImGuiScope(mock_begin, mock_end)
with scope:
pass
mock_end.assert_called_once()
class TestImguiWindow:
def test_imgui_window_returns_scope(self):
from src.imgui_scopes import imgui_window
with patch("src.imgui_scopes.imgui_bundle.imgui.begin", return_value=True) as mock_begin:
with patch("src.imgui_scopes.imgui_bundle.imgui.end") as mock_end:
scope = imgui_window("Test")
assert hasattr(scope, "__enter__")
assert hasattr(scope, "__exit__")
class TestImguiTable:
def test_imgui_table_returns_scope(self):
from src.imgui_scopes import imgui_table
with patch("src.imgui_scopes.imgui_bundle.imgui.begin_table", return_value=True) as mock_begin:
with patch("src.imgui_scopes.imgui_bundle.imgui.end_table") as mock_end:
scope = imgui_table("TestTable", 3)
with scope:
pass
mock_begin.assert_called_once()
mock_end.assert_called_once()
class TestImguiMenuBar:
def test_imgui_menu_bar_no_args(self):
from src.imgui_scopes import imgui_menu_bar
with patch("src.imgui_scopes.imgui_bundle.imgui.begin_menu_bar", return_value=True) as mock_begin:
with patch("src.imgui_scopes.imgui_bundle.imgui.end_menu_bar") as mock_end:
scope = imgui_menu_bar()
with scope:
pass
mock_begin.assert_called_once_with()
mock_end.assert_called_once()
class TestImguiMenu:
def test_imgui_menu_returns_scope(self):
from src.imgui_scopes import imgui_menu
with patch("src.imgui_scopes.imgui_bundle.imgui.begin_menu", return_value=True) as mock_begin:
with patch("src.imgui_scopes.imgui_bundle.imgui.end_menu") as mock_end:
scope = imgui_menu("File")
with scope:
pass
mock_begin.assert_called_once()
mock_end.assert_called_once()
class TestImguiChild:
def test_imgui_child_returns_scope(self):
from src.imgui_scopes import imgui_child
with patch("src.imgui_scopes.imgui_bundle.imgui.begin_child", return_value=True) as mock_begin:
with patch("src.imgui_scopes.imgui_bundle.imgui.end_child") as mock_end:
scope = imgui_child("child_id", 100, 200)
with scope:
pass
mock_begin.assert_called_once()
mock_end.assert_called_once()
class TestImguiGroup:
def test_imgui_group_no_args(self):
from src.imgui_scopes import imgui_group
with patch("src.imgui_scopes.imgui_bundle.imgui.begin_group", return_value=True) as mock_begin:
with patch("src.imgui_scopes.imgui_bundle.imgui.end_group") as mock_end:
scope = imgui_group()
with scope:
pass
mock_begin.assert_called_once()
mock_end.assert_called_once()
class TestImguiPopup:
def test_imgui_popup_returns_scope(self):
from src.imgui_scopes import imgui_popup
with patch("src.imgui_scopes.imgui_bundle.imgui.begin_popup", return_value=True) as mock_begin:
with patch("src.imgui_scopes.imgui_bundle.imgui.end_popup") as mock_end:
scope = imgui_popup("popup_id")
with scope:
pass
mock_begin.assert_called_once()
mock_end.assert_called_once()
class TestImguiTooltip:
def test_imgui_tooltip_no_args(self):
from src.imgui_scopes import imgui_tooltip
with patch("src.imgui_scopes.imgui_bundle.imgui.begin_tooltip", return_value=True) as mock_begin:
with patch("src.imgui_scopes.imgui_bundle.imgui.end_tooltip") as mock_end:
scope = imgui_tooltip()
with scope:
pass
mock_begin.assert_called_once()
mock_end.assert_called_once()
class TestNodeEditorScope:
def test_node_editor_scope_returns_scope(self):
from src.imgui_scopes import node_editor_scope
mock_ed = MagicMock()
mock_ed.begin = MagicMock(return_value=True)
mock_ed.end = MagicMock()
with patch("src.imgui_scopes.imgui_bundle.imgui_node_editor", mock_ed):
scope = node_editor_scope("TestNode")
with scope:
pass
mock_ed.begin.assert_called_once()
mock_ed.end.assert_called_once()
```
- [ ] **Step 2: Run tests**
Run: `uv run pytest tests/test_imgui_scopes.py -v`
Expected: All tests PASS
- [ ] **Step 3: Commit**
```bash
git add tests/test_imgui_scopes.py
git commit -m "test(gui): Add tests for ImGuiScope context managers"
```
---
## Task 3: Integration Verification
**Files:**
- Modify: `src/gui_2.py` (sample migration of 2-3 windows)
- Depends on: Task 1
- [ ] **Step 1: Add import to gui_2.py**
Add near line 1 (after existing imports):
```python
from src.imgui_scopes import imgui_window, imgui_table, imgui_menu_bar, imgui_menu, imgui_child, imgui_group, imgui_popup, imgui_tooltip, imgui_clipper, node_editor_scope
```
- [ ] **Step 2: Migrate one window as proof-of-concept**
Pick one window in `_gui_func` (e.g., lines 1061-1056 for "AI Settings"):
Before:
```python
exp, opened = imgui.begin("AI Settings", self.show_windows["AI Settings"])
if exp:
# ... content ...
imgui.end()
```
After:
```python
if imgui_window("AI Settings", self.show_windows["AI Settings"]):
# ... content ...
# end() called automatically
```
- [ ] **Step 3: Run smoke test**
Run: `uv run pytest tests/test_gui_startup_smoke.py -v`
Expected: PASS (no regression from import)
- [ ] **Step 4: Commit**
```bash
git add src/gui_2.py
git commit -m "refactor(gui): Migrate AI Settings window to imgui_window scope"
```
---
## Self-Review Checklist
- [ ] All scope helpers implemented with correct signatures
- [ ] Tests mock all begin/end pairs
- [ ] Integration test confirms no regression in gui_2.py
- [ ] No placeholders (TBD, TODO) in any step
- [ ] Each task commits independently
@@ -1,208 +0,0 @@
# ai_client.py Style Convention Curation Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Refactor `src/ai_client.py` to follow `conductor/code_styleguides/python.md` conventions (region blocks, module-level `ProviderError`, vertical compaction)
**Architecture:** Organizational refactor only — no logic changes. Move `ProviderError` class to module level, add `#region` sections, apply vertical alignment to dense conditionals.
**Tech Stack:** Python 3.11+, same codebase
---
## Pre-Work: Audit Current State
Before modifying, verify current line ranges and confirm exact locations for edits.
- [ ] **Step 1: Verify `ProviderError` location**
Run: `python -c "import src.ai_client; print(src.ai_client.ProviderError.__module__)"`
Expected: `src.ai_client` (currently nested but accessible)
- [ ] **Step 2: Get exact line range of ai_client.py**
Run: `Get-Content src/ai_client.py | Measure-Object -Line`
Expected: ~2522 lines
---
## Task 1: Move `ProviderError` to Module Level
**Files:**
- Modify: `src/ai_client.py:301-324`
- [ ] **Step 1: Read current `ProviderError` definition**
```python
# Lines 301-324
class ProviderError(Exception):
def __init__(self, provider: str, message: str, code: str | None = None):
self.provider = provider
self.message = message
self.code = code
def ui_message(self) -> str:
lines = self.message.split("\n")
first = lines[0]
prefix = f"[{self.provider.upper()}] "
if first.startswith(prefix):
return first
return f"{prefix}{first}"
```
- [ ] **Step 2: Find insertion point (after imports, before first function)**
Read `src/ai_client.py:1-60` to find where imports end and first function begins (~line 54).
- [ ] **Step 3: Insert `ProviderError` at module level** (before line 54)
- [ ] **Step 4: Remove original `ProviderError` class definition** (lines 301-324)
- [ ] **Step 5: Verify no import changes needed** — class moved within same module
- [ ] **Step 6: Run syntax check**
Run: `uv run python -m py_compile src/ai_client.py`
Expected: No errors
- [ ] **Step 7: Commit**
```bash
git add src/ai_client.py
git commit -m "refactor(ai_client): Move ProviderError to module level"
```
---
## Task 2: Add Region Blocks
**Files:**
- Modify: `src/ai_client.py:54-2522` (throughout)
Insert `#region: Name` before first function of each section, `#endregion: Name` after last function.
Region order (follows logical flow from setup to public API):
| Order | Region Name | Start Line | End Line |
|-------|-------------|------------|----------|
| 1 | `#region: Provider Configuration` | after `ProviderError` | before `_get_proxy` |
| 2 | `#region: Credentials & Setup` | `_get_proxy` | before `set_model_params` |
| 3 | `#region: System Prompt Management` | `set_model_params` | before `get_current_tier` |
| 4 | `#region: Thread Context` | `get_current_tier` | before `set_custom_system_prompt` |
| 5 | `#region: Comms Log` | `set_custom_system_prompt` section | before `_classify_anthropic_error` |
| 6 | `#region: Error Classification` | `_classify_anthropic_error` | before `set_provider` |
| 7 | `#region: Provider Lifecycle` | `set_provider` | before `set_agent_tools` |
| 8 | `#region: Tool Configuration` | `set_agent_tools` | before `_execute_tool_calls_concurrently` |
| 9 | `#region: Tool Execution` | `_execute_tool_calls_concurrently` | before `_reread_file_items` |
| 10 | `#region: File Context Building` | `_reread_file_items` | before `_estimate_message_tokens` |
| 11 | `#region: Token Estimation` | `_estimate_message_tokens` | before `_send_gemini` |
| 12 | `#region: Gemini Provider` | `_send_gemini` | before `_send_anthropic` |
| 13 | `#region: Anthropic Provider` | `_send_anthropic` | before `_send_deepseek` |
| 14 | `#region: DeepSeek Provider` | `_send_deepseek` | before `_send_minimax` |
| 15 | `#region: MiniMax Provider` | `_send_minimax` | before `run_tier4_analysis` |
| 16 | `#region: Tier 4 Analysis` | `run_tier4_analysis` | before `get_token_stats` |
| 17 | `#region: Session & Public API` | `get_token_stats` | end of file |
- [ ] **Step 1: For each region, insert opening `#region: Name` on its own line before the first function in that group**
- [ ] **Step 2: For each region, insert closing `#endregion: Name` on its own line after the last function in that group**
- [ ] **Step 3: Run syntax check after all insertions**
Run: `uv run python -m py_compile src/ai_client.py`
Expected: No errors
- [ ] **Step 4: Commit**
```bash
git add src/ai_client.py
git commit -m "refactor(ai_client): Add region blocks for organization"
```
---
## Task 3: Vertical Compaction Pass
**Files:**
- Modify: `src/ai_client.py` (targeted sections)
- [ ] **Step 1: Identify dense conditionals for alignment**
Read `_classify_anthropic_error` (lines 326-351), `_classify_gemini_error` (353-377), `_classify_deepseek_error` (379-410), `_classify_minimax_error` (412-441).
- [ ] **Step 2: Apply vertical alignment to status/color mappings if present**
Look for patterns like:
```python
if status == 'running': col = (0.0, 1.0, 0.0, 1.0)
elif status == 'starting': col = (1.0, 1.0, 0.0, 1.0)
elif status == 'error': col = (1.0, 0.0, 0.0, 1.0)
```
- [ ] **Step 3: Run syntax check**
Run: `uv run python -m py_compile src/ai_client.py`
Expected: No errors
- [ ] **Step 4: Commit**
```bash
git add src/ai_client.py
git commit -m "refactor(ai_client): Apply vertical compaction alignment"
```
---
## Task 4: Verify No Call Sites Broken
**Files:**
- Test: `tests/test_ai_client_concurrency.py`, `tests/test_bias_efficacy.py`, others
- [ ] **Step 1: Run ai_client-related tests**
Run: `uv run pytest tests/test_ai_client_concurrency.py tests/test_bias_efficacy.py tests/test_agent_capabilities_list_models.py -v --timeout=60`
Expected: All pass
- [ ] **Step 2: If failures, diagnose and fix inline**
Do not skip — fix the issue or escalate.
- [ ] **Step 3: Commit final verification**
```bash
git add src/ai_client.py
git commit -m "test(ai_client): Verify no call sites broken after style refactor"
```
---
## Verification
- [ ] **Step 1: Run full ai_client test suite**
Run: `uv run pytest tests/test_ai_client*.py -v --timeout=120` (batch if many files)
Expected: All pass
- [ ] **Step 2: Run lint check**
Run: `uv run ruff check src/ai_client.py`
Expected: No errors (or only pre-existing warnings)
- [ ] **Step 3: Final commit**
```bash
git add -A
git commit -m "conductor(checkpoint): ai_client.py style curation complete"
```
---
**Plan complete and saved to `docs/superpowers/plans/2026-05-13-ai-client-style-curation-plan.md`.**
**Two execution options:**
**1. Subagent-Driven (recommended)** - Dispatch a fresh subagent per task, review between tasks, fast iteration
**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints
**Which approach?**
@@ -1,592 +0,0 @@
# AI Server IPC Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Decouple heavy AI SDK imports from GUI via subprocess command queue, achieving ~0.5s instant GUI startup.
**Architecture:** Subprocess spawns `python -m src.ai_server` which loads google.genai/anthropic. GUI communicates via JSON-RPC over stdin/stdout pipe. Command queue pattern with response matching by UUID.
**Tech Stack:** Python subprocess, JSON-RPC, threading, queue-based IPC
---
## File Structure
### New Files
- `src/ai_server.py` - Subprocess AI server (stdin/stdout JSON-RPC)
- `src/ai_client_proxy.py` - Queue client for GUI
- `tests/test_ai_server.py` - Server tests
- `tests/test_ai_client_proxy.py` - Proxy tests
### Modified Files
- `src/ai_client.py` - Add proxy routing when AI_SERVER_ENABLED env var
- `sloppy.py` - Set AI_SERVER_ENABLED=1
---
## Task 1: Create ai_client_proxy.py - Queue Client
**Files:**
- Create: `src/ai_client_proxy.py`
- Test: `tests/test_ai_client_proxy.py`
- [ ] **Step 1: Write failing test for proxy basic operations**
```python
# tests/test_ai_client_proxy.py
import pytest
from src.ai_client_proxy import AIProxyClient
def test_proxy_initialization():
proxy = AIProxyClient()
assert proxy._status == "disconnected"
assert proxy._pending == {}
def test_proxy_status_states():
proxy = AIProxyClient()
assert proxy.status in ("disconnected", "init", "ready", "busy", "error")
```
Run: `uv run pytest tests/test_ai_client_proxy.py -v`
Expected: FAIL - module not found
- [ ] **Step 2: Implement minimal AIProxyClient**
```python
# src/ai_client_proxy.py
import json
import uuid
import threading
import subprocess
from typing import Any, Optional
class AIProxyClient:
def __init__(self):
self._process: Optional[subprocess.Popen] = None
self._status: str = "disconnected"
self._pending: dict[str, threading.Event] = {}
self._reader_thread: Optional[threading.Thread] = None
@property
def status(self) -> str:
return self._status
def start_server(self):
self._process = subprocess.Popen(
["python", "-m", "src.ai_server"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
self._status = "init"
self._reader_thread = threading.Thread(target=self._read_loop, daemon=True)
self._reader_thread.start()
def _read_loop(self):
pass # stub
def stop(self):
if self._process:
self._process.terminate()
self._process.wait(timeout=5)
self._process = None
self._status = "disconnected"
def send_command(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
request_id = str(uuid.uuid4())
event = threading.Event()
self._pending[request_id] = event
command = {"id": request_id, "method": method, "params": params}
self._process.stdin.write(json.dumps(command) + "\n")
self._process.stdin.flush()
event.wait(timeout=60)
result = self._pending.pop(request_id, None)
return result or {"error": "timeout"}
```
Run: `uv run pytest tests/test_ai_client_proxy.py::test_proxy_initialization -v`
Expected: PASS
- [ ] **Step 3: Write test for command/response matching**
```python
# Add to tests/test_ai_client_proxy.py
def test_send_command_returns_response():
proxy = AIProxyClient()
proxy._status = "ready"
proxy._process = MockPopen()
response = proxy.send_command("list_models", {"provider": "gemini"})
assert "result" in response or "error" in response
```
Run: `uv run pytest tests/test_ai_client_proxy.py -v`
Expected: FAIL - MockPopen not defined
- [ ] **Step 4: Implement command/response matching**
Add to `src/ai_client_proxy.py`:
```python
def _read_loop(self):
for line in self._process.stdout:
try:
response = json.loads(line.strip())
rid = response.get("id")
if rid in self._pending:
self._pending[rid] = response
self._pending[rid + "_event"].set()
except json.JSONDecodeError:
pass
def send_command(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
request_id = str(uuid.uuid4())
event = threading.Event()
self._pending[request_id] = None
self._pending[request_id + "_event"] = event
command = {"id": request_id, "method": method, "params": params}
self._process.stdin.write(json.dumps(command) + "\n")
self._process.stdin.flush()
if not event.wait(timeout=60):
return {"error": "timeout"}
result = self._pending.pop(request_id, {})
self._pending.pop(request_id + "_event", None)
return result
```
Run: `uv run pytest tests/test_ai_client_proxy.py -v`
Expected: PASS
- [ ] **Step 5: Write test for status tracking**
```python
# Add to tests/test_ai_client_proxy.py
def test_status_reflects_server_state():
proxy = AIProxyClient()
assert proxy.status == "disconnected"
proxy._status = "ready"
assert proxy.status == "ready"
```
Run: `uv run pytest tests/test_ai_client_proxy.py -v`
Expected: PASS
- [ ] **Step 6: Commit**
```bash
git add src/ai_client_proxy.py tests/test_ai_client_proxy.py
git commit -m "feat(ai-server): Add AIProxyClient queue communication layer"
```
---
## Task 2: Create ai_server.py - Subprocess Server
**Files:**
- Create: `src/ai_server.py`
- Test: `tests/test_ai_server.py`
- [ ] **Step 1: Write failing test for server startup**
```python
# tests/test_ai_server.py
import pytest
import subprocess
import json
import time
import sys
import os
def test_server_starts_and_loads():
proc = subprocess.Popen(
[sys.executable, "-m", "src.ai_server"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Server should output ready marker
line = proc.stdout.readline()
proc.stdin.close()
proc.stdout.close()
proc.wait(timeout=5)
assert "ready" in line.lower() or proc.returncode == 0
```
Run: `uv run pytest tests/test_ai_server.py::test_server_starts_and_loads -v`
Expected: FAIL - module not found
- [ ] **Step 2: Implement minimal ai_server.py**
```python
# src/ai_server.py
#!/usr/bin/env python
import json
import sys
def main():
# Signal ready
print(json.dumps({"type": "ready"}))
sys.stdout.flush()
for line in sys.stdin:
try:
cmd = json.loads(line.strip())
print(json.dumps({"id": cmd.get("id"), "result": {}}))
sys.stdout.flush()
except json.JSONDecodeError:
pass
if __name__ == "__main__":
main()
```
Run: `uv run pytest tests/test_ai_server.py::test_server_starts_and_loads -v`
Expected: PASS
- [ ] **Step 3: Write test for list_models command**
```python
# Add to tests/test_ai_server.py
def test_list_models_returns_models():
proc = subprocess.Popen(
[sys.executable, "-m", "src.ai_server"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Read ready
proc.stdout.readline()
# Send list_models command
cmd = json.dumps({"id": "1", "method": "list_models", "params": {"provider": "gemini"}})
proc.stdin.write(cmd + "\n")
proc.stdin.flush()
# Read response
resp = proc.stdout.readline()
result = json.loads(resp)
assert "result" in result or "error" in result
proc.stdin.close()
proc.stdout.close()
proc.wait()
```
Run: `uv run pytest tests/test_ai_server.py::test_list_models_returns_models -v`
Expected: FAIL - list_models not implemented
- [ ] **Step 4: Implement list_models command**
Update `src/ai_server.py`:
```python
# src/ai_server.py
#!/usr/bin/env python
import json
import sys
_PROVIDERS = {
"gemini": ["gemini-2.5-flash-lite", "gemini-3-flash-preview"],
"anthropic": ["claude-sonnet-4-20250514", "claude-3-5-sonnet-20241022"],
}
def handle_command(cmd: dict) -> dict:
method = cmd.get("method", "")
params = cmd.get("params", {})
if method == "list_models":
provider = params.get("provider", "gemini")
return {"id": cmd.get("id"), "result": {"models": _PROVIDERS.get(provider, [])}}
return {"id": cmd.get("id"), "error": f"Unknown method: {method}"}
def main():
print(json.dumps({"type": "ready"}))
sys.stdout.flush()
for line in sys.stdin:
try:
cmd = json.loads(line.strip())
response = handle_command(cmd)
print(json.dumps(response))
sys.stdout.flush()
except json.JSONDecodeError:
pass
if __name__ == "__main__":
main()
```
Run: `uv run pytest tests/test_ai_server.py::test_list_models_returns_models -v`
Expected: PASS
- [ ] **Step 5: Write test for google.genai loading**
```python
# Add to tests/test_ai_server.py
def test_server_loads_google_genai():
import time
proc = subprocess.Popen(
[sys.executable, "-m", "src.ai_server"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
start = time.time()
ready_line = proc.stdout.readline()
elapsed = time.time() - start
proc.stdin.close()
proc.stdout.close()
proc.wait(timeout=10)
# Should load in reasonable time (allow up to 5s for SDK)
assert elapsed < 5, f"Server took {elapsed}s to start"
```
Run: `uv run pytest tests/test_ai_server.py::test_server_loads_google_genai -v`
Expected: FAIL - needs implementation
- [ ] **Step 6: Implement google.genai loading**
Update `src/ai_server.py`:
```python
# src/ai_server.py
#!/usr/bin/env python
import json
import sys
import time
_PROVIDERS = {
"gemini": ["gemini-2.5-flash-lite", "gemini-3-flash-preview"],
"anthropic": ["claude-sonnet-4-20250514", "claude-3-5-sonnet-20241022"],
}
_google_genai = None
_anthropic = None
def _ensure_google_genai():
global _google_genai
if _google_genai is None:
from google import genai
_google_genai = genai
return _google_genai
def handle_command(cmd: dict) -> dict:
method = cmd.get("method", "")
params = cmd.get("params", {})
if method == "list_models":
provider = params.get("provider", "gemini")
if provider == "gemini":
_ensure_google_genai()
return {"id": cmd.get("id"), "result": {"models": _PROVIDERS.get(provider, [])}}
if method == "send":
_ensure_google_genai()
return {"id": cmd.get("id"), "result": {"status": "processed"}}
return {"id": cmd.get("id"), "error": f"Unknown method: {method}"}
def main():
print(json.dumps({"type": "ready"}))
sys.stdout.flush()
for line in sys.stdin:
try:
cmd = json.loads(line.strip())
response = handle_command(cmd)
print(json.dumps(response))
sys.stdout.flush()
except Exception as e:
print(json.dumps({"error": str(e)}))
sys.stdout.flush()
if __name__ == "__main__":
main()
```
Run: `uv run pytest tests/test_ai_server.py::test_server_loads_google_genai -v`
Expected: PASS
- [ ] **Step 7: Commit**
```bash
git add src/ai_server.py tests/test_ai_server.py
git commit -m "feat(ai-server): Add ai_server subprocess with google.genai lazy loading"
```
---
## Task 3: Wire AIProxyClient into ai_client.py
**Files:**
- Modify: `src/ai_client.py` (add proxy routing)
- Modify: `sloppy.py` (enable AI server)
- [ ] **Step 1: Write test for proxy integration**
```python
# tests/test_ai_client_integration.py
import pytest
import os
def test_ai_client_uses_proxy_when_enabled(monkeypatch):
os.environ["AI_SERVER_ENABLED"] = "1"
# Import should trigger proxy initialization
from src import ai_client
assert hasattr(ai_client, "_proxy")
assert ai_client._proxy is not None
```
Run: `uv run pytest tests/test_ai_client_integration.py -v`
Expected: FAIL - AI_SERVER_ENABLED not checked
- [ ] **Step 2: Add proxy to ai_client.py**
Modify `src/ai_client.py` - add near top after imports:
```python
# At top of ai_client.py, after other imports
_ai_proxy = None
def _get_proxy():
global _ai_proxy
if _ai_proxy is None and os.environ.get("AI_SERVER_ENABLED"):
from src.ai_client_proxy import AIProxyClient
_ai_proxy = AIProxyClient()
_ai_proxy.start_server()
return _ai_proxy
```
Modify `_list_gemini_models()`:
```python
def _list_gemini_models() -> list[str]:
proxy = _get_proxy()
if proxy and proxy.status == "ready":
result = proxy.send_command("list_models", {"provider": "gemini"})
if "result" in result:
return result["result"].get("models", [])
# Fallback to direct import
global _gemini_client
_ensure_gemini_client()
return [m.name for m in _gemini_client.models.list()]
```
- [ ] **Step 3: Test the integration**
Run: `uv run pytest tests/test_ai_client_integration.py -v`
Expected: PASS
- [ ] **Step 4: Modify sloppy.py to enable AI server**
```python
# sloppy.py - add near top
os.environ["AI_SERVER_ENABLED"] = "1"
```
- [ ] **Step 5: Commit**
```bash
git add src/ai_client.py sloppy.py tests/test_ai_client_integration.py
git commit -m "feat(ai-server): Wire AIProxyClient into ai_client"
```
---
## Task 4: Add GUI Status Indicator and Panel Tinting
**Files:**
- Modify: `src/gui_2.py` - add AI status tracking
- Modify: `src/app_controller.py` - track AI server status
- [ ] **Step 1: Write test for AI status in GUI**
```python
# tests/test_ai_status_gui.py
def test_gui_shows_ai_status():
from src.gui_2 import App
# App should have ai_status property
app = App.__new__(App)
assert hasattr(app, "ai_status") or "ai_status" in dir(app)
```
Run: `uv run pytest tests/test_ai_status_gui.py -v`
Expected: FAIL - ai_status not in App
- [ ] **Step 2: Add ai_status to App.__init__**
In `src/gui_2.py` `App.__init__`, add:
```python
self.ai_status = "disconnected" # disconnected, init, ready, busy, error
```
- [ ] **Step 3: Add status panel rendering**
In `src/gui_2.py` `_render_provider_panel()` or similar, add tint indicator:
```python
# At top of panel
if getattr(self, 'ai_status', 'ready') != 'ready':
imgui.push_style_color(imgui.COLOR_WINDOW_BACKGROUND, 0.5, 0.5, 0.5, 1.0)
imgui.text_colored(imgui.Vec4(1, 0.5, 0, 1), "[AI: Initializing...]")
# ... rest of panel ...
if self.ai_status != 'ready':
imgui.pop_style_color()
```
- [ ] **Step 4: Test tinted panel**
Run: `uv run pytest tests/test_ai_status_gui.py -v`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add src/gui_2.py tests/test_ai_status_gui.py
git commit -m "feat(gui): Add AI status indicator and panel tinting"
```
---
## Task 5: End-to-End Test
- [ ] **Step 1: Full startup test**
```bash
cd C:\projects\manual_slop
timeout 30 uv run python sloppy.py &
sleep 3
# Check if GUI responds
curl http://localhost:8999/status 2>/dev/null || echo "Hook API not ready"
```
- [ ] **Step 2: Verify startup time**
```powershell
Measure-Command { uv run python sloppy.py } | Select TotalSeconds
```
Target: < 2 seconds to GUI visible
- [ ] **Step 3: Commit**
```bash
git add -A
git commit -m "feat(ai-server): Complete AI server IPC integration"
```
---
## Verification
1. **Startup time**: GUI should appear in < 2 seconds
2. **AI panels**: Should show "Initializing..." tint until AI server ready
3. **AI functionality**: After ~1.2s, AI panels become active
4. **No regressions**: Existing tests pass
@@ -1,623 +0,0 @@
# Hot Reloader Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Implement selective, state-preserving hot-reload for `src/gui_2.py` with delegation pattern refactor, manual trigger via `Ctrl+Alt+R` and GUI button, and visual error tint feedback on failure.
**Architecture:** Separating code into **delegators** (thin `App` wrappers in `gui_2.py`) and **delegation targets** (actual render logic as module-level functions). State preservation via `copy.deepcopy` capture/restore around `importlib.reload`. Error tint overlay on reload failure using ImGui draw list.
**Tech Stack:** Python `importlib.reload`, `copy.deepcopy`, Dear PyGui / ImGui draw lists, `traceback`
---
## File Structure
| File | Responsibility |
|------|----------------|
| `src/hot_reloader.py` | New — `HotModule` dataclass and `HotReloader` class with registry, capture/restore, reload API |
| `src/gui_2.py` | Refactor render methods from `App._render_xxx` to module-level `render_xxx(app)` |
| `src/app_controller.py` | Delegation wrappers — `App._render_xxx` becomes `import src.gui_2 as g2; g2.render_xxx(self)` |
| `tests/test_hot_reloader.py` | Unit tests for `HotModule`, `HotReloader` registry and reload |
| `tests/test_hot_reload_integration.py` | Integration tests via `live_gui` fixture |
---
## Task 1: Create `src/hot_reloader.py` — HotModule Dataclass and HotReloader Core
**Files:**
- Create: `src/hot_reloader.py`
- Test: `tests/test_hot_reloader.py`
- [ ] **Step 1: Write failing test for HotModule dataclass**
```python
# tests/test_hot_reloader.py
import pytest
from dataclasses import dataclass
from src.hot_reloader import HotModule, HotReloader
def test_hot_module_dataclass_fields():
hm = HotModule(
name="test_module",
file_path="/path/to/test_module.py",
state_keys=["attr1", "attr2"],
delegation_targets=["method_a", "method_b"],
)
assert hm.name == "test_module"
assert hm.file_path == "/path/to/test_module.py"
assert hm.state_keys == ["attr1", "attr2"]
assert hm.delegation_targets == ["method_a", "method_b"]
def test_hot_reloader_register_and_get():
HotReloader.HOT_MODULES.clear()
hm = HotModule(name="test_mod", file_path="/fake/path.py", state_keys=[], delegation_targets=[])
HotReloader.register(hm)
assert "test_mod" in HotReloader.HOT_MODULES
assert HotReloader.HOT_MODULES["test_mod"] is hm
def test_hot_reloader_register_duplicate_raises():
HotReloader.HOT_MODULES.clear()
hm1 = HotModule(name="dup", file_path="/a.py", state_keys=[], delegation_targets=[])
HotReloader.register(hm1)
with pytest.raises(ValueError, match="already registered"):
HotReloader.register(hm1)
def test_hot_reloader_is_error_state():
HotReloader.HOT_MODULES.clear()
HotReloader.last_error = None
HotReloader.is_error_state = False
assert HotReloader.is_error_state is False
```
- [ ] **Step 2: Run test to verify it fails**
Run: `uv run pytest tests/test_hot_reloader.py -v`
Expected: FAIL — `src/hot_reloader.py` does not exist
- [ ] **Step 3: Write minimal HotModule dataclass and HotReloader skeleton**
```python
# src/hot_reloader.py
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
import copy
import traceback
@dataclass
class HotModule:
name: str
file_path: str
state_keys: list[str] = field(default_factory=list)
delegation_targets: list[str] = field(default_factory=list)
class HotReloader:
HOT_MODULES: dict[str, HotModule] = {}
last_error: str | None = None
is_error_state: bool = False
@classmethod
def register(cls, module: HotModule) -> None:
if module.name in cls.HOT_MODULES:
raise ValueError(f"Module {module.name} already registered")
cls.HOT_MODULES[module.name] = module
@classmethod
def capture_state(cls, app: Any, state_keys: list[str]) -> dict[str, Any]:
return {key: copy.deepcopy(getattr(app, key, None)) for key in state_keys if hasattr(app, key)}
@classmethod
def restore_state(cls, app: Any, state: dict[str, Any]) -> None:
for key, value in state.items():
setattr(app, key, value)
```
- [ ] **Step 4: Run test to verify it passes**
Run: `uv run pytest tests/test_hot_reloader.py -v`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add src/hot_reloader.py tests/test_hot_reloader.py
git commit -m "feat(hot-reload): Add HotModule dataclass and HotReloader registry"
```
---
## Task 2: Implement `HotReloader.reload()` and `HotReloader.reload_all()`
**Files:**
- Modify: `src/hot_reloader.py`
- Test: `tests/test_hot_reloader.py`
- [ ] **Step 1: Write failing test for reload method**
```python
# tests/test_hot_reloader.py (add these tests)
import importlib
from unittest.mock import MagicMock, patch
def test_reload_unknown_module_returns_false():
HotReloader.HOT_MODULES.clear()
HotReloader.register(HotModule(name="nonexistent_mod", file_path="/nonexistent.py", state_keys=[], delegation_targets=[]))
app = MagicMock()
result = HotReloader.reload("nonexistent_mod", app)
assert result is False
assert HotReloader.is_error_state is True
assert HotReloader.last_error is not None
def test_reload_success_clears_error_state():
HotReloader.HOT_MODULES.clear()
# Create a minimal test module that can be reloaded
import sys
import types
test_mod = types.ModuleType("src._test_reload_mod")
sys.modules["src._test_reload_mod"] = test_mod
HotReloader.register(HotModule(name="src._test_reload_mod", file_path=str(Path(__file__).parent.parent / "src" / "_test_reload_mod.py"), state_keys=[], delegation_targets=[]))
app = MagicMock()
result = HotReloader.reload("src._test_reload_mod", app)
assert result is True
assert HotReloader.is_error_state is False
assert HotReloader.last_error is None
del sys.modules["src._test_reload_mod"]
def test_reload_captures_and_restores_state_on_failure():
HotReloader.HOT_MODULES.clear()
HotReloader.register(HotModule(name="bad_mod", file_path="/bad.py", state_keys=["_test_attr"], delegation_targets=[]))
app = MagicMock()
app._test_attr = "preserved_value"
result = HotReloader.reload("bad_mod", app)
assert result is False
assert HotReloader.is_error_state is True
# State should be restored even on failure
def test_reload_all():
HotReloader.HOT_MODULES.clear()
# Register two modules and verify reload_all calls both
```
- [ ] **Step 2: Run test to verify it fails**
Run: `uv run pytest tests/test_hot_reloader.py -v`
Expected: FAIL — `reload` method not implemented
- [ ] **Step 3: Write `reload()` and `reload_all()` implementations**
```python
# src/hot_reloader.py (add after existing methods)
@classmethod
def reload(cls, module_name: str, app: Any) -> bool:
if module_name not in cls.HOT_MODULES:
cls.last_error = f"Module {module_name} not registered"
cls.is_error_state = True
return False
hm = cls.HOT_MODULES[module_name]
state = cls.capture_state(app, hm.state_keys)
try:
import importlib
import sys
if module_name in sys.modules:
old_module = sys.modules[module_name]
importlib.reload(old_module)
else:
importlib.import_module(module_name)
cls.last_error = None
cls.is_error_state = False
return True
except Exception:
cls.restore_state(app, state)
cls.last_error = traceback.format_exc()
cls.is_error_state = True
return False
@classmethod
def reload_all(cls, app: Any) -> bool:
success = True
for name in cls.HOT_MODULES:
if not cls.reload(name, app):
success = False
return success
```
- [ ] **Step 4: Run test to verify it passes**
Run: `uv run pytest tests/test_hot_reloader.py -v`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add src/hot_reloader.py tests/test_hot_reloader.py
git commit -m "feat(hot-reload): Implement HotReloader.reload and reload_all"
```
---
## Task 3: Register `src.gui_2` with state_keys and delegation_targets
**Files:**
- Modify: `src/app_controller.py` — add `HotModule` registration
- Test: `tests/test_hot_reloader.py`
- [ ] **Step 1: Write test for src.gui_2 registration**
```python
def test_gui_2_module_registered():
from src.hot_reloader import HotReloader
assert "src.gui_2" in HotReloader.HOT_MODULES
hm = HotReloader.HOT_MODULES["src.gui_2"]
assert len(hm.state_keys) > 0
assert len(hm.delegation_targets) > 0
assert "_render_main_interface" in hm.delegation_targets
```
- [ ] **Step 2: Run test to verify it fails**
Run: `uv run pytest tests/test_hot_reloader.py::test_gui_2_module_registered -v`
Expected: FAIL — registration not yet added
- [ ] **Step 3: Add src.gui_2 HotModule registration in app_controller.py**
Find the end of `AppController.__init__` or add a `_register_hot_modules()` function called at end of `__init__`:
```python
# In src/app_controller.py, near end of AppController.__init__ or as standalone:
from src.hot_reloader import HotReloader, HotModule
from pathlib import Path
def _register_hot_modules() -> None:
HotReloader.register(HotModule(
name="src.gui_2",
file_path=str(Path(__file__).parent / "gui_2.py"),
state_keys=[
"_active_discussion", "_disc_entries", "_disc_roles",
"show_windows", "ui_discussion_split_h", "active_tickets",
"show_preset_manager_window", "show_tool_preset_manager_window",
"show_persona_editor_window", "_pending_patch",
"_tool_log", "_comms_log",
],
delegation_targets=[
"_render_main_interface", "_render_discussion_hub",
"_render_discussion_panel", "_render_discussion_selector",
"_render_discussion_entries", "_render_discussion_entry",
"_render_files_and_media", "_render_files_panel",
"_render_screenshots_panel", "_render_context_composition_panel",
"_render_ai_settings_hub", "_render_provider_panel",
"_render_persona_selector_panel", "_render_agent_tools_panel",
"_render_rag_panel", "_render_system_prompts_panel",
"_render_preset_manager_content", "_render_persona_editor_window",
"_render_tool_preset_manager_content",
"_render_operations_hub", "_render_tool_calls_panel",
"_render_comms_history_panel", "_render_mma_dashboard",
"_render_mma_modals", "_render_ticket_queue",
"_render_task_dag_panel", "_render_approve_script_modal",
"_render_patch_modal",
],
))
```
Call `_register_hot_modules()` at the end of `AppController.__init__` after all state is initialized.
- [ ] **Step 4: Run test to verify it passes**
Run: `uv run pytest tests/test_hot_reloader.py::test_gui_2_module_registered -v`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add src/app_controller.py
git commit -m "feat(hot-reload): Register src.gui_2 with state_keys and delegation_targets"
```
---
## Task 4: Phase 1 — Refactor gui_2 render methods to module-level functions
**Files:**
- Modify: `src/gui_2.py` — extract `App._render_xxx` to `render_xxx(app)`
- Modify: `src/app_controller.py` — delegation wrappers
- Test: `tests/test_hot_reloader.py`
**Refactor Pattern** (for each render method):
```python
# BEFORE (in App class):
def _render_main_interface(self) -> None:
if self.perf_profiling_enabled:
self.perf_monitor.start_component("_render_main_interface")
with imscope.window("Main Interface", self.show_windows.get("main_interface", True)) as (exp, opened):
if not exp: return
# ... all render logic
# AFTER — App method becomes thin delegation wrapper:
def _render_main_interface(self) -> None:
import src.gui_2 as g2
g2.render_main_interface(self)
# NEW module-level function:
def render_main_interface(app: App) -> None:
if app.perf_profiling_enabled:
app.perf_monitor.start_component("_render_main_interface")
with imscope.window("Main Interface", app.show_windows.get("main_interface", True)) as (exp, opened):
if not exp: return
# ... same render logic, replacing self with app
```
**Methods to refactor** (in order):
1. `_render_main_interface` (lines 769-857)
2. `_render_discussion_hub` (lines 3235-3245)
3. `_render_discussion_panel` (lines 3395-3415)
4. `_render_discussion_selector` (lines 3430-3473)
5. `_render_discussion_entries` (lines 3247-3259)
6. `_render_discussion_entry` (lines 3261-3304)
7. `_render_files_and_media` (lines 2529-2636)
8. `_render_files_panel` (lines 2638-2716)
9. `_render_screenshots_panel` (lines 2718-2750)
10. `_render_context_composition_panel` (lines 2752-2765)
11. `_render_ai_settings_hub` (lines 1754-1759)
12. `_render_provider_panel` (lines 2377-2441)
13. `_render_persona_selector_panel` (lines 2443-2523)
14. `_render_agent_tools_panel` (lines 1850-1913)
15. `_render_rag_panel` (lines 1761-1796)
16. `_render_system_prompts_panel` (lines 1798-1848)
17. `_render_preset_manager_content` (lines 1915-2002)
18. `_render_persona_editor_window` (lines 2205-2375)
19. `_render_tool_preset_manager_content` (lines 2014-2193)
20. `_render_operations_hub` (lines 3750-3788)
21. `_render_tool_calls_panel` (lines 3790-3853)
22. `_render_comms_history_panel` (lines 3855-3971)
23. `_render_mma_dashboard` (lines 4664-4695)
24. `_render_mma_modals` (lines 4711-4785)
25. `_render_ticket_queue` (lines 4365-4478)
26. `_render_task_dag_panel` (lines 4480-4621)
27. `_render_approve_script_modal` (lines 4269-4310)
28. `_render_patch_modal` (lines 4135-4177)
- [ ] **Step 1: Write test for module-level function pattern**
```python
def test_gui2_has_module_level_render_functions():
import src.gui_2 as g2
# After refactor, these should exist as module-level functions
assert hasattr(g2, 'render_main_interface')
assert hasattr(g2, 'render_discussion_hub')
assert hasattr(g2, 'render_discussion_panel')
# ... etc
# Each should accept app: App as first parameter
import inspect
sig = inspect.signature(g2.render_main_interface)
assert 'app' in sig.parameters
```
- [ ] **Step 2: Run test to verify it fails**
Run: `uv run pytest tests/test_hot_reloader.py::test_gui2_has_module_level_render_functions -v`
Expected: FAIL — module-level functions don't exist yet
- [ ] **Step 3: Refactor `_render_main_interface` first as pilot**
Extract the method body, replace `self` with `app`, create module-level `render_main_interface(app: App) -> None`, and convert `App._render_main_interface` to delegation wrapper.
- [ ] **Step 4: Run test after each method refactor**
Run: `uv run pytest tests/test_hot_reloader.py::test_gui2_has_module_level_render_functions -v`
Continue until all module-level functions exist.
- [ ] **Step 5: Commit after completing all methods**
```bash
git add src/gui_2.py src/app_controller.py
git commit -m "refactor(hot-reload): Extract gui_2 render methods to module-level functions"
```
---
## Task 5: Add `Ctrl+Alt+R` trigger and GUI Hot Reload button
**Files:**
- Modify: `src/gui_2.py:App.run` — capture keyboard shortcut
- Modify: `src/gui_2.py:_render_mma_dashboard` — add Hot Reload button
- Modify: `src/gui_2.py` — add `_trigger_hot_reload` and `_hot_reload_error` state
- Test: `tests/test_hot_reload_integration.py`
- [ ] **Step 1: Write test for hot reload trigger**
```python
def test_ctrl_alt_r_triggers_hot_reload(live_gui):
client = ApiHookClient(port=8999)
# Verify initial state
initial_state = client.get_value("_hot_reload_error")
# Note: Direct keyboard simulation via hooks not yet implemented
# Test will verify button-based trigger
```
- [ ] **Step 2: Write test for Hot Reload button in MMA dashboard**
```python
def test_hot_reload_button_in_mma_dashboard(live_gui):
client = ApiHookClient(port=8999)
# Verify _render_mma_dashboard has a Hot Reload button
# via exposed component state or via behavior
```
- [ ] **Step 3: Add `_hot_reload_error` state to App.__init__**
In `src/gui_2.py:App.__init__`, add:
```python
self._hot_reload_error = False
```
- [ ] **Step 4: Add `_trigger_hot_reload()` method to App**
```python
def _trigger_hot_reload(self) -> None:
from src.hot_reloader import HotReloader
success = HotReloader.reload_all(self)
self._hot_reload_error = not success
def _render_hot_reload_button(self) -> None:
imgui.push_style_color(imgui.Col_.button, imgui.get_color_u32_rgba(0.2, 0.2, 0.2, 1.0))
if imgui.button("Hot Reload"):
self._trigger_hot_reload()
imgui.pop_style_color()
if self._hot_reload_error:
imgui.same_line()
imgui.text_colored(imgui.get_color_u32_rgba(1.0, 0.3, 0.2, 1.0), "Reload failed")
```
- [ ] **Step 5: Add keyboard capture in `App.run` main loop**
In `src/gui_2.py:App.run`, inside the main `while running:` loop:
```python
# Check for Ctrl+Alt+R
io = imgui.get_io()
if io.key_ctrl and io.key_alt and imgui.is_key_down(imgui.KEY_R):
self._trigger_hot_reload()
```
- [ ] **Step 6: Integrate button into `_render_mma_global_controls` or `_render_mma_dashboard`**
Add `self._render_hot_reload_button()` call in `_render_mma_dashboard` or a nearby section.
- [ ] **Step 7: Run tests**
Run: `uv run pytest tests/test_hot_reload_integration.py -v --timeout=60`
Expected: PASS
- [ ] **Step 8: Commit**
```bash
git add src/gui_2.py src/app_controller.py
git commit -m "feat(hot-reload): Add Ctrl+Alt+R trigger and GUI Hot Reload button"
```
---
## Task 6: Implement visual error tint on reload failure
**Files:**
- Modify: `src/gui_2.py` — add `_render_error_tint()` and call from `render_main_interface`
- Test: `tests/test_hot_reload_integration.py`
- [ ] **Step 1: Write test for error tint overlay**
```python
def test_error_tint_renders_on_failure(live_gui):
# Inject a failure state and verify tint appears
pass
```
- [ ] **Step 2: Add `_render_error_tint(app: App)` function**
```python
def _render_error_tint(app: App) -> None:
if not getattr(app, '_hot_reload_error', False):
return
# NERV theme error tint
tint_color = imgui.get_color_u32_rgba(1.0, 0.28, 0.25, 0.15)
draw_list = imgui.get_background_draw_list()
viewport = imgui.get_main_viewport()
draw_list.add_rect_filled(
viewport.pos,
viewport.pos + viewport.size,
tint_color,
)
```
- [ ] **Step 3: Call `_render_error_tint(app)` at end of `render_main_interface`**
Add at the very start of `render_main_interface` since it's an overlay:
```python
def render_main_interface(app: App) -> None:
_render_error_tint(app)
if app.perf_profiling_enabled:
# ...
```
- [ ] **Step 4: Run test**
Run: `uv run pytest tests/test_hot_reload_integration.py -v --timeout=60`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add src/gui_2.py
git commit -m "feat(hot-reload): Add visual error tint on reload failure"
```
---
## Task 7: End-to-End Integration Test
**Files:**
- Create: `tests/test_hot_reload_integration.py`
- Test: `tests/test_hot_reload_integration.py`
- [ ] **Step 1: Write e2e tests using live_gui fixture**
```python
import pytest
from api_hook_client import ApiHookClient
from simulation.sim_base import BaseSimulation, run_sim
class HotReloadSimulation(BaseSimulation):
def run(self) -> None:
# 1. Verify hot reload button is accessible
# 2. Trigger hot reload via API hook
# 3. Verify GUI still renders after reload
# 4. Verify error tint clears on success
pass
def test_hot_reload_e2e(live_gui):
client = ApiHookClient(port=8999)
# Trigger hot reload
response = client.post("/api/gui", {"action": "hot_reload"})
# Verify GUI still responds
state = client.get("/api/gui_state")
assert state is not None
def test_hot_reload_preserves_state(live_gui):
client = ApiHookClient(port=8999)
# Set some state
# Trigger reload
# Verify state preserved
```
- [ ] **Step 2: Run e2e tests**
Run: `uv run pytest tests/test_hot_reload_integration.py -v --timeout=120`
Expected: PASS
- [ ] **Step 3: Commit**
```bash
git add tests/test_hot_reload_integration.py
git commit -m "test(hot-reload): Add e2e integration test"
```
---
## Success Criteria Verification
1. Pressing `Ctrl+Alt+R` reloads `src.gui_2` without losing App state — tested via manual trigger + state verification
2. GUI "Hot Reload" button triggers same reload — tested via button click in live_gui
3. Failed reload shows error tint, preserves last-good state — tested via mock failure injection
4. New hot modules can be added via `HotReloader.register()` without modifying core — tested in unit tests
5. No performance impact when not reloading (< 1ms overhead per frame) — verified via perf profiling
---
## Out of Scope (Per Spec)
- Automatic file watching (manual trigger only)
- Hot-reloading of C extensions or native code
- Cross-platform reload support (Windows focus)
@@ -1,442 +0,0 @@
# OpenCode Agent Definition Fix Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Fix OpenCode agent definition files so subagents spawned via @mention follow conductor workflow (1-space indentation, TDD, checkpoints, atomic commits)
**Architecture:** Rewrite `.opencode/agents/tier3-worker.md` and `.opencode/agents/tier4-qa.md` to add CRITICAL enforcement blocks, TDD phase structure, and pre-delegation checkpoint protocols. Tier 2 and Tier 1 agents are secondary targets.
**Tech Stack:** OpenCode agent definitions (Markdown with YAML frontmatter), Manual Slop MCP tools
---
## File Structure
- **Modify:** `.opencode/agents/tier3-worker.md` - Add enforcement blocks, TDD structure
- **Modify:** `.opencode/agents/tier4-qa.md` - Add architecture reference, strengthen analysis format
- **Modify:** `.opencode/agents/tier2-tech-lead.md` - Add delegation enforcement, phase completion protocol
- **Modify:** `.opencode/agents/tier1-orchestrator.md` - Add track initialization workflow
---
## Phase 1: Fix Tier 3 Worker Agent Definition
### Task 1.1: Add CRITICAL Indentation Block
**Files:**
- Modify: `.opencode/agents/tier3-worker.md`
- [ ] **Step 1: Read current tier3-worker.md header section**
Read `.opencode/agents/tier3-worker.md` lines 1-50 to identify insertion point after frontmatter.
- [ ] **Step 2: Add CRITICAL indentation block after "STRICT SYSTEM DIRECTIVE"**
Insert this block immediately after the STRICT SYSTEM DIRECTIVE line:
```markdown
## CRITICAL: 1-Space Indentation for Python
**ALL Python code MUST use exactly 1 (ONE) space for indentation.**
VIOLATIONS:
- Using 4 spaces or tabs will corrupt the codebase
- Native edit tools destroy 1-space indentation - use MCP tools ONLY
MCP Edit Tools (SAFE):
- `manual-slop_edit_file` - find/replace, preserves indentation
- `manual-slop_py_update_definition` - replace function/class
- `manual-slop_set_file_slice` - replace line range
DO NOT use native `edit` or `write` tools on Python files.
```
- [ ] **Step 3: Commit**
```bash
git add .opencode/agents/tier3-worker.md
git commit -m "fix(agents): Add CRITICAL 1-space indentation block to tier3-worker"
```
### Task 1.2: Add Pre-Delegation Checkpoint Protocol
**Files:**
- Modify: `.opencode/agents/tier3-worker.md` (same file, different section)
- [ ] **Step 1: Read tier3-worker.md around "Task Start Checklist"**
Read lines 45-80 to find the Task Start Checklist section.
- [ ] **Step 2: Add Pre-Delegation Checkpoint Protocol before Task Start Checklist**
Insert this section:
```markdown
## Pre-Delegation Checkpoint Protocol (MANDATORY)
Before implementing ANY code change:
1. **Stage your work:** `manual-slop_run_powershell` with `git add .`
2. **Why:** Prevents work loss if the implementation fails or needs rollback
3. **When:** Always - before touching any file that matters
This is NOT optional. It is the difference between recoverable and catastrophic failure.
```
- [ ] **Step 3: Commit**
```bash
git add .opencode/agents/tier3-worker.md
git commit -m "fix(agents): Add pre-delegation checkpoint protocol to tier3-worker"
```
### Task 1.3: Add TDD Phase Enforcement
**Files:**
- Modify: `.opencode/agents/tier3-worker.md` (same file, different section)
- [ ] **Step 1: Read tier3-worker.md around "Task Execution Protocol"**
Read lines 60-120 to find the Task Execution Protocol section.
- [ ] **Step 2: Enhance Task Execution Protocol with TDD Phase Structure**
Replace the existing "Task Execution Protocol" section with:
```markdown
## Task Execution Protocol (MANDATORY TDD)
### Phase 1: RED - Write Failing Test
- Write a test that defines the expected behavior
- Run: `manual-slop_run_powershell` with `uv run pytest tests/path/test.py -v`
- Confirm: Test MUST fail before proceeding
- DO NOT skip this phase
### Phase 2: GREEN - Implement to Pass
- Implement the minimal code to make the test pass
- Run tests again
- Confirm: Test MUST pass
- DO NOT skip this phase
### Phase 3: REFACTOR - Optional
- With passing tests, improve code quality
- DO NOT change behavior
- Re-run tests to confirm still passing
### Commit Protocol (ATOMIC PER TASK)
After each task completion:
1. `manual-slop_run_powershell` with `git add .`
2. `git commit -m "feat(scope): description"`
3. DO NOT batch commits across tasks
```
- [ ] **Step 3: Commit**
```bash
git add .opencode/agents/tier3-worker.md
git commit -m "fix(agents): Add TDD phase enforcement to tier3-worker"
```
### Task 1.4: Add BLOCKED Protocol
**Files:**
- Modify: `.opencode/agents/tier3-worker.md` (same file, different section)
- [ ] **Step 1: Read tier3-worker.md end of file**
Read lines 120-150 to find where to insert BLOCKED protocol.
- [ ] **Step 2: Add BLOCKED Protocol section at end of file**
Append before the Limitations section:
```markdown
## BLOCKED Protocol
If you cannot complete the task:
1. Start your response with: `BLOCKED:`
2. Explain exactly why you cannot proceed
3. List what information or changes would unblock you
4. DO NOT attempt partial implementations that break the build
Examples of BLOCKED conditions:
- Missing required context about the codebase
- Task requires architectural decisions not in the spec
- Target file/line range does not exist as described
- Cyclic dependency discovered that wasn't documented
```
- [ ] **Step 3: Commit**
```bash
git add .opencode/agents/tier3-worker.md
git commit -m "fix(agents): Add BLOCKED protocol to tier3-worker"
```
---
## Phase 2: Fix Tier 4 QA Agent Definition
### Task 2.1: Add Architecture Reference to Tier 4 QA
**Files:**
- Modify: `.opencode/agents/tier4-qa.md`
- [ ] **Step 1: Read current tier4-qa.md**
Read `.opencode/agents/tier4-qa.md` lines 1-80 to understand current structure.
- [ ] **Step 2: Add Architecture Reference section after "Context Amnesia"**
Insert after Context Amnesia section:
```markdown
## Architecture Reference
When analyzing errors, trace data flow through thread domains documented in:
- `docs/guide_architecture.md`: Thread domains, event system, AI client, HITL mechanism
- `docs/guide_mma.md`: 4-tier orchestration, DAG engine, worker lifecycle
Key threading model:
- GUI main thread: UI rendering only
- asyncio worker thread: AI communication
- HookServer thread: API hook handling
- NEVER write GUI state from background threads
```
- [ ] **Step 3: Commit**
```bash
git add .opencode/agents/tier4-qa.md
git commit -m "fix(agents): Add architecture reference to tier4-qa"
```
### Task 2.2: Strengthen Tier 4 QA Analysis Format
**Files:**
- Modify: `.opencode/agents/tier4-qa.md`
- [ ] **Step 1: Read tier4-qa.md around "Root Cause Analysis"**
Read lines 60-100 to find the Root Cause Analysis section.
- [ ] **Step 2: Enhance Root Cause Analysis with data flow tracing instructions**
Replace the existing Analysis Protocol section with enhanced version:
```markdown
## Analysis Protocol
### 1. Understand the Error
- Read the provided error output, test failure, or log carefully
- Identify affected files from traceback
- Do NOT assume - base analysis on evidence only
### 2. Investigate
Use MCP tools to understand the context:
- `manual-slop_read_file` - Read relevant source files
- `manual-slop_py_find_usages` - Search for related patterns
- `manual-slop_search_files` - Find related files
- `manual-slop_get_git_diff` - Check recent changes
### 3. Root Cause Analysis (MANDATORY FORMAT)
Provide a structured analysis in this exact format:
```
## Error Analysis
### Summary
[One-sentence description of the error]
### Root Cause
[Detailed explanation of WHY the error occurred - not just what went wrong]
### Evidence
[File:line references supporting the analysis]
### Data Flow Trace
[How data moved through the system to cause this error]
[Reference specific thread domains if applicable: GUI main, asyncio worker, HookServer]
### Impact
[What functionality is affected]
### Recommendations
[Suggested fixes - but DO NOT implement them]
```
### 4. DO NOT FIX
- Your job is ANALYSIS ONLY
- Do NOT modify any files
- Do NOT write code
- Return the analysis and let the controller decide
```
- [ ] **Step 3: Commit**
```bash
git add .opencode/agents/tier4-qa.md
git commit -m "fix(agents): Strengthen tier4-qa analysis format"
```
---
## Phase 3: Fix Tier 2 Tech Lead Agent Definition
### Task 3.1: Add Delegation Enforcement to Tier 2
**Files:**
- Modify: `.opencode/agents/tier2-tech-lead.md`
- [ ] **Step 1: Read current tier2-tech-lead.md**
Read `.opencode/agents/tier2-tech-lead.md` lines 1-100.
- [ ] **Step 2: Add CRITICAL Delegation Rules after STRICT SYSTEM DIRECTIVE**
Insert after STRICT SYSTEM DIRECTIVE:
```markdown
## CRITICAL: Delegate Implementation - Do NOT Implement Directly
You are Tier 2 Tech Lead. Your role is ARCHITECTURE and DELEGATION.
**NEVER** implement code directly. ALWAYS delegate to Tier 3 via Task tool.
**Delegation Pattern:**
1. Research with skeleton tools
2. Draft surgical prompt with WHERE/WHAT/HOW/SAFETY
3. Delegate to Tier 3 via Task tool
4. Verify result
**Pre-Delegation Checkpoint:**
Before delegating ANY non-trivial change:
1. Run: `manual-slop_run_powershell` with `git add .`
2. Reason: Prevents work loss if subagent fails
```
- [ ] **Step 3: Commit**
```bash
git add .opencode/agents/tier2-tech-lead.md
git commit -m "fix(agents): Add delegation enforcement to tier2-tech-lead"
```
### Task 3.2: Add Phase Completion Protocol to Tier 2
**Files:**
- Modify: `.opencode/agents/tier2-tech-lead.md`
- [ ] **Step 1: Read tier2-tech-lead.md around "TDD Protocol" section**
Read lines 100-150 to find the TDD Protocol section.
- [ ] **Step 2: Add Phase Completion Verification Protocol**
After TDD Protocol section, add:
```markdown
## Phase Completion Protocol
When all tasks in a phase are complete:
1. **Run verification:** `/conductor-verify`
2. **Present results:** Show user the results
3. **Await confirmation:** Wait for user yes/no
4. **Create checkpoint:** `git add . && git commit -m "conductor(checkpoint): Phase N complete"`
5. **Attach note:** `git notes add -m "<verification report>" <commit_hash>`
6. **Update plan:** Mark phase `[x]` with checkpoint SHA in plan.md
```
- [ ] **Step 3: Commit**
```bash
git add .opencode/agents/tier2-tech-lead.md
git commit -m "fix(agents): Add phase completion protocol to tier2-tech-lead"
```
---
## Phase 4: Fix Tier 1 Orchestrator Agent Definition
### Task 4.1: Add Track Initialization Workflow
**Files:**
- Modify: `.opencode/agents/tier1-orchestrator.md`
- [ ] **Step 1: Read current tier1-orchestrator.md**
Read `.opencode/agents/tier1-orchestrator.md` lines 1-100.
- [ ] **Step 2: Add Track Initialization Protocol after Session Start Checklist**
Find the Session Start Checklist section and add after it:
```markdown
## Track Initialization Protocol
When starting a new track:
1. **Read track context:**
- `conductor/tracks.md` - active tracks
- `conductor/tech-stack.md` - technology constraints
- `conductor/product.md` - product vision
2. **Audit existing state:**
- Use `manual-slop_py_get_code_outline` to map files
- Use `manual-slop_get_git_diff` to check recent changes
- Document "Current State Audit" in spec
3. **Create track spec:**
- Follow spec template with: Overview, Current State Audit, Goals, Requirements
- Include Architecture Reference section
4. **Initialize track directory:**
- Create `conductor/tracks/{name}_{YYYYMMDD}/`
- Write spec.md, plan.md, metadata.json
```
- [ ] **Step 3: Commit**
```bash
git add .opencode/agents/tier1-orchestrator.md
git commit -m "fix(agents): Add track initialization workflow to tier1-orchestrator"
```
---
## Verification
After all phases complete:
- [ ] **Step 1: Verify indentation block in tier3-worker.md**
Run: `grep -n "1-space" .opencode/agents/tier3-worker.md`
Expected: Multiple matches including "CRITICAL"
- [ ] **Step 2: Verify TDD phase structure**
Run: `grep -n "RED\|GREEN\|REFACTOR" .opencode/agents/tier3-worker.md`
Expected: All three phases present
- [ ] **Step 3: Verify checkpoint protocol**
Run: `grep -n "git add \." .opencode/agents/tier3-worker.md`
Expected: Pre-delegation checkpoint present
- [ ] **Step 4: Verify tier4-qa architecture reference**
Run: `grep -n "guide_architecture" .opencode/agents/tier4-qa.md`
Expected: Reference present
---
**Plan complete and saved to `docs/superpowers/plans/2026-05-16-opencode-agent-fix-plan.md`. Two execution options:**
**1. Subagent-Driven (recommended)** - Dispatch a fresh subagent per task, review between tasks, fast iteration
**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints
**Which approach?**
@@ -1,458 +0,0 @@
# Agent Config Refresh Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Apply the explicit divergence model to `AGENTS.md`, `CLAUDE.md`, and `GEMINI.md`. `AGENTS.md` becomes a thin pointer document (~1K). `CLAUDE.md` becomes a 1-line deprecation stub. `GEMINI.md` is lightly refreshed to remove any content that should have moved to `AGENTS.md`. Light verification pass (not rewrites) on the skill files in `.agents/`, `.gemini/`, and the legacy `.claude/commands/`.
**Architecture:** Single-agent sequential execution. Each top-level file is its own task. Skill file verification is a separate task that touches many files but only makes surgical edits where drift is found.
**Tech Stack:** Manual Slop's MCP research tools. Git for version control.
**Spec:** `docs/superpowers/specs/2026-06-02-comprehensive-documentation-refresh-design.md` §6 (Sub-Track 3).
**Depends on:** Sub-Track 1 (`docs_layer_refresh_20260602`) must complete first. The human-facing docs are the source of truth that the agent config files point to.
---
## Execution Constraints
These apply to every task in this plan:
- **No subagents.** Execute as a single agent.
- **Pre-edit checkpoint:** Before any file edit, run `git add .`.
- **Per-file atomic commits:** One file = one commit. The skill file verification task may have many commits (one per file) since there are many files, but each is still atomic.
- **Commit message format:** `docs(<scope>): <imperative description>`.
- **Git note format:** Attach a 3-8 line rationale to each commit.
- **The "no content duplication" rule:** After this plan completes, a section header that appears in 2+ of `AGENTS.md`/`CLAUDE.md`/`GEMINI.md` must be either (a) a short pointer (≤3 lines) to the canonical home, or (b) a violation to fix.
---
## Task 1: Rewrite `AGENTS.md` to a Thin Pointer
**Files:**
- Modify: `AGENTS.md` (current size ~5.4K)
**Target structure:** A thin orientation document of ~1K that:
- Describes what Manual Slop is (1-2 sentences)
- States the conductor convention (1 paragraph)
- Lists the canonical locations for agent guidance (with file links)
- Lists the 5-6 most critical project-wide anti-patterns
- Points to `docs/Readme.md` for human-facing docs
**Specific target content (use as a starting template, adapt to current state):**
```markdown
# AGENTS.md
## What This Is
Manual Slop is a local GUI orchestrator for LLM-driven coding sessions. It bridges high-latency AI reasoning with a low-latency ImGui render loop via a thread-safe async pipeline; every AI-generated payload passes through a human-auditable gate before execution.
## The Conductor Convention
All AI agents consuming this project must read `./conductor/workflow.md` and treat `./conductor/tracks.md` as the task registry. Track implementation follows the TDD protocol documented in `conductor/workflow.md` with per-file atomic commits and git notes.
## Guidance for AI Agents
Detailed agent guidance lives in the following locations — read these directly, do not duplicate content here:
- **Operational workflow:** `conductor/workflow.md`
- **Code style and process:** `conductor/product-guidelines.md`
- **Tech stack and constraints:** `conductor/tech-stack.md`
- **Product context:** `conductor/product.md`
- **MMA orchestrator role:** `mma-orchestrator/SKILL.md`
- **Tier 1 (Orchestrator):** `.agents/skills/mma-tier1-orchestrator/SKILL.md`
- **Tier 2 (Tech Lead):** `.agents/skills/mma-tier2-tech-lead/SKILL.md`
- **Tier 3 (Worker):** `.agents/skills/mma-tier3-worker/SKILL.md`
- **Tier 4 (QA):** `.agents/skills/mma-tier4-qa/SKILL.md`
## Human-Facing Documentation
For understanding, using, and maintaining the tool, see `docs/Readme.md` and the guides it indexes.
## Critical Anti-Patterns
- Do not read full files >50 lines without first using `py_get_skeleton` or `get_file_summary`
- Do not modify the tech stack without updating `conductor/tech-stack.md` first
- Do not implement code directly as Tier 2 — delegate to Tier 3 workers (or in the case of this docs track, do it yourself as a single agent)
- Do not skip TDD — write failing tests before implementation
- Do not batch commits — commit per-task for atomic rollback
- Do not add comments to source code; documentation lives in `/docs`
```
- [ ] **Step 1.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 1.2: Read the current `AGENTS.md`**
Use `manual-slop_read_file` (the file is small enough to read in full).
- [ ] **Step 1.3: Inventory what content currently lives in `AGENTS.md`**
For each major section in the current file, note:
- **Keep here:** Project-level orientation, anti-patterns, the conductor convention
- **Move to `conductor/workflow.md`:** Workflow details
- **Move to `conductor/product-guidelines.md`:** Style rules
- **Move to `conductor/tech-stack.md`:** Tech stack details
- **Move to `conductor/product.md`:** Feature descriptions
- **Move to `mma-orchestrator/SKILL.md`:** MMA role details
- **Move to the relevant `.agents/skills/*/SKILL.md`:** Tier-specific guidance
- **Move to `docs/Readme.md`:** Anything that documents the tool for humans (not agents)
- [ ] **Step 1.4: Verify the target skill files exist and have the content**
Before writing `AGENTS.md`, confirm:
- `mma-orchestrator/SKILL.md` exists
- `.agents/skills/mma-tier1-orchestrator/SKILL.md` exists
- `.agents/skills/mma-tier2-tech-lead/SKILL.md` exists
- `.agents/skills/mma-tier3-worker/SKILL.md` exists
- `.agents/skills/mma-tier4-qa/SKILL.md` exists
- `conductor/workflow.md` exists
- `conductor/product-guidelines.md` exists
- `conductor/tech-stack.md` exists
- `conductor/product.md` exists
- `docs/Readme.md` exists (and is updated per Sub-Track 1)
If any are missing, do NOT write `AGENTS.md` until they exist. (They should exist by the time Sub-Track 3 runs, since Sub-Track 1 has completed.)
- [ ] **Step 1.5: Write the new `AGENTS.md`**
Use the target structure above as a starting template. Adapt the language to match the project's style. Keep the file under 1.5K.
- [ ] **Step 1.6: Validate all cross-doc links in the new file**
For every `[text](./relative/path)` or file mention:
- Use `manual-slop_search_files` to confirm the target exists
- If broken, fix the link or remove the mention
- [ ] **Step 1.7: Confirm size is reasonable**
```powershell
(Get-Item C:\projects\manual_slop\AGENTS.md).Length
```
Should be ≤ 1500 bytes. If larger, trim.
- [ ] **Step 1.8: Commit**
```powershell
git -C C:\projects\manual_slop add AGENTS.md
git -C C:\projects\manual_slop commit -m "docs(agents): rewrite as thin pointer document; defer to skill files and conductor docs"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Reduced from [N]K to ~1K. Content relocated to: [list canonical homes]. Anti-patterns compressed to 6 most critical." $_ }
```
---
## Task 2: Replace `CLAUDE.md` with a 1-Line Stub
**Files:**
- Modify: `CLAUDE.md` (current size ~6.7K)
**Target content:** A 1-line deprecation stub that preserves the file's existence for any external tooling that may reference it.
- [ ] **Step 2.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 2.2: Read the current `CLAUDE.md`**
- [ ] **Step 2.3: Verify no external scripts reference the CLAUDE.md content**
Use `manual-slop_py_find_usages` to grep for any code that reads CLAUDE.md's specific sections. If anything does, the stub approach still works (the script will get a 1-line file instead of the rich content, but it's a graceful failure).
If critical tooling depends on the rich content, flag this to the user before continuing.
- [ ] **Step 2.4: Write the stub**
```markdown
# CLAUDE.md
This project is no longer actively used with Claude Code. For project context, see `AGENTS.md`. The conductor system in `./conductor/` is the cross-tool abstraction and works with any agent toolchain.
```
- [ ] **Step 2.5: Confirm size is small**
```powershell
(Get-Item C:\projects\manual_slop\CLAUDE.md).Length
```
Should be ≤ 500 bytes.
- [ ] **Step 2.6: Commit**
```powershell
git -C C:\projects\manual_slop add CLAUDE.md
git -C C:\projects\manual_slop commit -m "docs(claude): replace with deprecation stub; project no longer uses Claude Code"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Reduced from [N]K to stub. Preserves file existence for any legacy tooling. Project migrated to Gemini CLI + OpenCode." $_ }
```
---
## Task 3: Refresh `GEMINI.md`
**Files:**
- Modify: `GEMINI.md` (current size ~3.8K)
- Reference: `conductor/product.md` (now updated per Sub-Track 2), `conductor/tech-stack.md`, `src/gemini_cli_adapter.py`
**Scope:** This file should contain ONLY Gemini-CLI-specific content. Any content that was previously shared with `AGENTS.md` and has been relocated should now point to `AGENTS.md` instead.
- [ ] **Step 3.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 3.2: Read the current `GEMINI.md`**
- [ ] **Step 3.3: Identify any content in `GEMINI.md` that is not Gemini-specific**
For each section, ask: "Would this content be useful to an agent that is NOT using Gemini CLI?" If yes, it should be in `AGENTS.md` or a conductor doc, not here.
Likely candidates to move out (or point to canonical home):
- General MMA tier descriptions (should be in `AGENTS.md` or `mma-orchestrator/SKILL.md`)
- General session startup checklist (should be in `AGENTS.md`)
- General anti-patterns (should be in `AGENTS.md` or `conductor/product-guidelines.md`)
Content that should stay (Gemini-CLI-specific):
- Gemini model routing per MMA tier
- `activate_skill` usage for MMA roles
- Gemini CLI bridge architecture (`gemini_cli_adapter.py`, `GEMINI_CLI_HOOK_CONTEXT`, synchronous HITL approval)
- Gemini-specific caching/TTL behavior
- Provider-specific quirks (cache rebuilding at 90% TTL, server-side context cache)
- `gemini-3.1-pro-preview` / `gemini-3-flash-preview` / `gemini-2.5-flash-lite` model names
- [ ] **Step 3.4: Rewrite `GEMINI.md` to contain only Gemini-CLI-specific content**
For each non-Gemini-specific section, replace with a short pointer to the canonical home (e.g., "See `AGENTS.md` for the MMA tier overview. This document covers Gemini-CLI-specific implementation only.").
- [ ] **Step 3.5: Verify Gemini-specific content is accurate**
For each Gemini-specific claim:
- Verify model names match `conductor/tech-stack.md`
- Verify TTL/cache behavior matches `src/ai_client.py` and `src/gemini_cli_adapter.py`
- Verify the bridge architecture description matches the actual code
- [ ] **Step 3.6: Validate all cross-doc links**
- [ ] **Step 3.7: Commit**
```powershell
git -C C:\projects\manual_slop add GEMINI.md
git -C C:\projects\manual_slop commit -m "docs(gemini): refresh and reduce to Gemini-CLI-specific content"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Replaced [N] sections with pointers to AGENTS.md. Kept [list of Gemini-specific sections]." $_ }
```
---
## Task 4: Light Verification of `.agents/skills/*/SKILL.md` and `mma-orchestrator/SKILL.md`
**Files:**
- Read: `mma-orchestrator/SKILL.md` (~9.3K)
- Read: `.agents/skills/mma-tier1-orchestrator/SKILL.md` (~2.4K)
- Read: `.agents/skills/mma-tier2-tech-lead/SKILL.md` (~3.8K)
- Read: `.agents/skills/mma-tier3-worker/SKILL.md`
- Read: `.agents/skills/mma-tier4-qa/SKILL.md` (~813 bytes)
- Read: `.agents/skills/mma-orchestrator/SKILL.md` (~9.7K)
**Scope:** This is a LIGHT verification pass. For each file:
1. Read it
2. Confirm it covers the role accurately (cross-reference with the orchestrator skill's role description)
3. If drift is found, make a surgical fix
4. If the file is fundamentally outdated, FLAG for a separate track — do NOT rewrite wholesale here
- [ ] **Step 4.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 4.2: Read the canonical orchestrator skill**
Use `manual-slop_read_file` on `mma-orchestrator/SKILL.md`. This is the source of truth for the MMA architecture.
- [ ] **Step 4.3: For each tier skill (1-4), verify role accuracy**
For each `.agents/skills/mma-tierN-*/SKILL.md`:
- Read it (use `manual-slop_read_file`)
- Cross-reference with the orchestrator skill's description of the tier
- Note any drift: missing responsibilities, stale model names, wrong tool references
- [ ] **Step 4.4: For each drift found, make a surgical fix**
If the drift is small (a model name, a tool name, a single responsibility statement):
- Make the fix in place
- Commit per file
If the drift is large (the whole role description is outdated):
- DO NOT rewrite in this plan
- Note it in the commit message
- Add a task to a SEPARATE follow-up track (do not create new tasks in this plan)
- [ ] **Step 4.5: For each file with a surgical fix, commit**
```powershell
git -C C:\projects\manual_slop add .agents/skills/mma-tierN-*/SKILL.md
git -C C:\projects\manual_slop commit -m "docs(skills): fix drift in [tierN] skill - [specific fix]"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Found drift: [description]. Surgical fix applied: [what changed]. Larger rewrites deferred to [reason]." $_ }
```
- [ ] **Step 4.6: If no drift is found, commit an empty verification log**
```powershell
git -C C:\projects\manual_slop commit --allow-empty -m "docs(skills): verify all 5 skill files are coherent with orchestrator SKILL.md"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Read all 5 skill files. Cross-referenced with orchestrator SKILL.md. No drift found." $_ }
```
---
## Task 5: Light Verification of `.gemini/...` Mirrors
**Files:**
- Read: `.gemini/skills/mma-orchestrator/SKILL.md`
- Read: `.gemini/skills/mma-tier1-orchestrator/SKILL.md`
- Read: `.gemini/skills/mma-tier2-tech-lead/SKILL.md`
- Read: `.gemini/skills/mma-tier3-worker/SKILL.md`
- Read: `.gemini/skills/mma-tier4-qa/SKILL.md`
- Read: `.gemini/agents/tier1-orchestrator.md`
- Read: `.gemini/agents/tier2-tech-lead.md`
- Read: `.gemini/agents/tier3-worker.md`
- Read: `.gemini/agents/tier4-qa.md`
**Scope:** Compare the `.gemini/` mirror files against their canonical counterparts (`.agents/...`). If drift is found, fix it. This is the same LIGHT verification as Task 4 but for the Gemini-specific mirrors.
- [ ] **Step 5.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 5.2: For each `.gemini/...` file, compare to its canonical counterpart**
For each file:
- Read both the mirror and the canonical
- Diff them
- If different, determine which is the source of truth
- If the mirror is stale, fix it to match the canonical (or vice versa, depending on the file's role)
- [ ] **Step 5.3: For each drift found, commit per file**
```powershell
git -C C:\projects\manual_slop add .gemini/[path]
git -C C:\projects\manual_slop commit -m "docs(gemini-mirror): sync [file] with [canonical source]"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Mirror was out of sync. Updated to match canonical [path]." $_ }
```
---
## Task 6: Light Verification of `.claude/commands/*.md` (Legacy)
**Files:**
- Read: every file in `.claude/commands/` (9 files per the directory listing)
**Scope:** These are legacy files for backward compatibility. Lightly verify they still work and don't reference removed features. Do NOT rewrite — they are legacy.
- [ ] **Step 6.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 6.2: Read each file and check for broken references**
For each `.claude/commands/*.md`:
- Use `manual-slop_read_file` (they're small)
- Check for references to removed features (e.g., `gui_legacy.py`, `tkinter`, removed modules)
- Check for links that no longer resolve
- [ ] **Step 6.3: For each broken reference, decide**
Options:
- (a) Fix the broken reference (small edit)
- (b) Add a "(DEPRECATED)" notice to the file
- (c) Leave alone (legacy)
Default: (a) if it's a trivial fix (e.g., a link to a renamed file). (b) if the whole file is suspect. (c) if it's working and just old.
- [ ] **Step 6.4: For each file with a fix, commit per file**
```powershell
git -C C:\projects\manual_slop add .claude/commands/[file]
git -C C:\projects\manual_slop commit -m "docs(claude-commands): fix broken reference in [file]"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Found broken reference to [X]. Fixed by [what]." $_ }
```
---
## Task 7: Sub-Track 3 Verification (Drift Check)
- [ ] **Step 7.1: Section header drift audit**
For the section headers that should have been relocated, confirm each appears in at most one of `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`:
```powershell
$headers = @("Session Startup", "Conductor System", "MMA 4-Tier", "Anti-Patterns", "Development Workflow", "Code Style", "MCP Tool")
foreach ($h in $headers) {
$inAgents = Select-String -Path C:\projects\manual_slop\AGENTS.md -Pattern $h -SimpleMatch -Quiet
$inClaude = Select-String -Path C:\projects\manual_slop\CLAUDE.md -Pattern $h -SimpleMatch -Quiet
$inGemini = Select-String -Path C:\projects\manual_slop\GEMINI.md -Pattern $h -SimpleMatch -Quiet
$count = @($inAgents, $inClaude, $inGemini) | Where-Object { $_ } | Measure-Object | Select-Object -ExpandProperty Count
if ($count -gt 1) { Write-Host "DRIFT: '$h' appears in $count files" }
}
```
If any header appears in 2+ files, the divergence model is violated. Either:
- (a) The duplicate is a short pointer (≤3 lines) → OK
- (b) The duplicate is full content → Fix by relocating
- [ ] **Step 7.2: Size sanity check**
```powershell
(Get-Item C:\projects\manual_slop\AGENTS.md).Length
(Get-Item C:\projects\manual_slop\CLAUDE.md).Length
(Get-Item C:\projects\manual_slop\GEMINI.md).Length
```
- AGENTS.md should be ≤ 1.5K
- CLAUDE.md should be ≤ 500 bytes
- GEMINI.md can be larger (Gemini-specific content is allowed) but should not contain duplicated agent-orientation content
- [ ] **Step 7.3: Link validation**
For every `[text](./relative/path)` in each of the 3 files, verify the target exists.
- [ ] **Step 7.4: Per-file commit verification**
```powershell
git -C C:\projects\manual_slop log --oneline -- AGENTS.md CLAUDE.md GEMINI.md .agents/ .gemini/ .claude/ | head -30
```
Confirm every file has its own commit history (not batched).
- [ ] **Step 7.5: Git note verification**
```powershell
git -C C:\projects\manual_slop log --format="%H" -- AGENTS.md CLAUDE.md GEMINI.md .agents/ .gemini/ .claude/ | Select-Object -First 20 | ForEach-Object { $note = git -C C:\projects\manual_slop notes show $_ 2>$null; if (-not $note) { Write-Host "MISSING NOTE: $_" } }
```
- [ ] **Step 7.6: Phase completion checkpoint**
Per the project's Phase Completion Verification protocol in `conductor/workflow.md`:
- Run the automated test suite
- Present the verification report to the user
- Get user sign-off
- Create the checkpoint commit: `conductor(checkpoint): Sub-Track 3 (agent config refresh) complete`
- Attach the verification report as a git note
---
## Self-Review (after writing this plan)
1. **Spec coverage:** Spec §6 has 6 phases (3.1-3.6). Tasks 1-3 cover 3.1-3.3. Tasks 4-6 cover 3.4-3.5 (skills, mirrors, legacy commands). Task 7 covers 3.6 (verification). ✓ All phases covered.
2. **Placeholder scan:** No "TBD" / "TODO" markers. The "If drift is found, fix it; if fundamentally outdated, flag for separate track" decision rule is explicit in Task 4.4. ✓
3. **Type/symbol consistency:** File paths, section header names, and the drift audit script are all consistent. ✓
4. **Risk check:** The stub approach for CLAUDE.md preserves compatibility. The "light verification, flag for separate track" pattern prevents scope creep. The drift audit script in Step 7.1 is concrete and runnable. ✓
@@ -1,245 +0,0 @@
# Clean Install Test Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add an opt-in pytest test that clones the Manual Slop repo to a temp dir, runs `uv sync`, launches `sloppy.py --enable-test-hooks`, and verifies the Hook API responds. Catches "works on my machine" failures by exercising the full install-and-launch path in an isolated environment.
**Architecture:** Standard subprocess-based integration test. `git clone` from the user's Gitea server to `tmp_path`. `uv sync` in the cloned dir. Launch the app as a background subprocess. Poll the Hook API endpoint with `requests` until ready. Test a write hook. Clean up the process tree.
**Tech Stack:** Python 3.11+, pytest, subprocess, requests, git CLI
**Spec:** `docs/superpowers/specs/2026-06-02-clean-install-test-design.md`
---
## Execution Constraints
- **No subagents.** Execute as a single agent.
- **Pre-edit checkpoint:** `git add .` before any file edit.
- **Per-file atomic commits.**
- **Commit message format:** `<type>(<scope>): <imperative description>`.
- **Git note format:** 3-8 line rationale per commit.
- **Style baseline:** 1-space indent, no comments, type hints.
---
## File Structure
| File | Action | Responsibility |
|---|---|---|
| `tests/test_clean_install.py` | Create | Opt-in test (RUN_CLEAN_INSTALL_TEST=1) |
| `pyproject.toml` | Modify | Add `clean_install` marker |
---
## Task 1: Add the `clean_install` marker to `pyproject.toml`
**Files:**
- Modify: `pyproject.toml`
- [ ] **Step 1.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 1.2: Read current markers section**
Use `manual-slop_py_get_code_outline` or grep for `markers` in `pyproject.toml`.
- [ ] **Step 1.3: Add the `clean_install` marker**
Find the existing markers list (e.g., under `[tool.pytest.ini_options]`) and add:
```toml
markers = [
"integration: integration tests requiring live GUI",
"strict: tests that require strict mode",
"clean_install: clean install verification (opt-in via RUN_CLEAN_INSTALL_TEST=1)",
]
```
If markers aren't already in `pyproject.toml`, add them. If they are, append the new entry.
- [ ] **Step 1.4: Commit**
```bash
git -C C:\projects\manual_slop add pyproject.toml
git -C C:\projects\manual_slop commit -m "test(pytest): add clean_install marker"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Registers the clean_install marker so tests can be selected with pytest -m clean_install or filtered with -m 'not clean_install'." $_ }
```
---
## Task 2: Write the clean install test
**Files:**
- Create: `tests/test_clean_install.py`
- [ ] **Step 2.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 2.2: Create the test file**
```python
# tests/test_clean_install.py
import os
import subprocess
import sys
import time
from pathlib import Path
import pytest
import requests
REPO_URL = "https://git.cozyair.dev/ed/manual_slop"
STARTUP_TIMEOUT_SECONDS = 30
READINESS_POLL_INTERVAL = 0.5
HOOK_PORT = 8999
@pytest.mark.clean_install
def test_clean_install_runs_with_hooks(tmp_path):
"""Clone the repo, install deps, launch sloppy.py, verify Hook API.
Opt-in: set RUN_CLEAN_INSTALL_TEST=1 to enable. Otherwise skipped.
Requires network access to the configured REPO_URL.
"""
if os.environ.get("RUN_CLEAN_INSTALL_TEST") != "1":
pytest.skip("Set RUN_CLEAN_INSTALL_TEST=1 to enable")
clone_dir = tmp_path / "manual_slop"
project_root = Path(__file__).resolve().parent.parent
subprocess.run(
["git", "clone", REPO_URL, str(clone_dir)],
capture_output=True, text=True, timeout=60,
check=True,
)
subprocess.run(
["uv", "sync"],
cwd=str(clone_dir),
capture_output=True, text=True, timeout=180,
check=True,
)
creationflags = 0
if os.name == "nt":
creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
process = subprocess.Popen(
["uv", "run", "sloppy.py", "--enable-test-hooks"],
cwd=str(clone_dir),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
creationflags=creationflags,
)
try:
start = time.time()
ready = False
while time.time() - start < STARTUP_TIMEOUT_SECONDS:
if process.poll() is not None:
stderr_out = process.stderr.read(2000) if process.stderr else b""
pytest.fail(f"Process exited early. stderr: {stderr_out.decode('utf-8', errors='replace')}")
try:
response = requests.get(
f"http://127.0.0.1:{HOOK_PORT}/status",
timeout=1.0,
)
if response.status_code == 200:
payload = response.json()
if payload.get("status") in ("running", "ready", "ok"):
ready = True
break
except (requests.ConnectionError, requests.Timeout):
pass
time.sleep(READINESS_POLL_INTERVAL)
assert ready, (
f"Hook server did not respond within {STARTUP_TIMEOUT_SECONDS}s. "
f"stderr: {process.stderr.read(2000).decode('utf-8', errors='replace') if process.stderr else 'N/A'}"
)
response = requests.get(
f"http://127.0.0.1:{HOOK_PORT}/api/gui/mma_status",
timeout=5.0,
)
assert response.status_code == 200, f"mma_status returned {response.status_code}"
data = response.json()
assert isinstance(data, dict), f"mma_status returned non-dict: {data!r}"
finally:
_cleanup_process(process)
def _cleanup_process(process: subprocess.Popen) -> None:
if os.name == "nt":
subprocess.run(
["taskkill", "/F", "/T", "/PID", str(process.pid)],
capture_output=True,
)
else:
process.terminate()
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
```
- [ ] **Step 2.3: Run the test in skip mode**
```powershell
uv run pytest tests/test_clean_install.py -v
```
Expected: SKIPPED (RUN_CLEAN_INSTALL_TEST not set).
- [ ] **Step 2.4: Commit**
```bash
git -C C:\projects\manual_slop add tests/test_clean_install.py
git -C C:\projects\manual_slop commit -m "test(clean-install): add opt-in clone-and-verify pytest test"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Clones the Gitea repo to tmp_path, runs uv sync, launches sloppy.py --enable-test-hooks, polls :8999/status until ready, then tests /api/gui/mma_status write hook. Robust Windows/Unix process cleanup. Skipped unless RUN_CLEAN_INSTALL_TEST=1." $_ }
```
---
## Task 3: Phase Completion Verification
- [ ] **Step 3.1: Confirm test is properly gated**
```powershell
uv run pytest tests/test_clean_install.py -v
```
Expected: 1 skipped.
- [ ] **Step 3.2: Manual opt-in run (if network is available)**
```powershell
RUN_CLEAN_INSTALL_TEST=1 uv run pytest tests/test_clean_install.py -v
```
Expected: 1 passed (or 1 failed with clear diagnostic if the clone target is unreachable).
- [ ] **Step 3.3: Create the checkpoint commit**
```bash
git -C C:\projects\manual_slop commit --allow-empty -m "conductor(checkpoint): Clean install test complete"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Track complete. Opt-in test (RUN_CLEAN_INSTALL_TEST=1) added. Verifies clone + uv sync + launch + hook API. Marked with @pytest.mark.clean_install." $_ }
```
---
## Self-Review
- **Spec coverage:** All design tasks have a plan task. ✓
- **Placeholder scan:** Test code is complete. ✓
- **Type consistency:** `tmp_path`, `process`, `response` used consistently. ✓
- **Robust cleanup:** `_cleanup_process` handles Windows + Unix. ✓
@@ -1,652 +0,0 @@
# Command Palette Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Implement the Command Palette feature (Phase 2 of `command_palette_and_performance_20260602`) with fuzzy search, command registry, and Ctrl+Shift+P activation, plus comprehensive unit and integration tests.
**Architecture:** Module-level functions in `src/command_palette.py` (per the project's UI delegation pattern). Static command definitions in `src/commands.py` registered via decorator. Thin `_render_command_palette(self)` wrapper in `App` class. Fuzzy matcher is a pure function for testability.
**Tech Stack:** Python 3.11+, imgui-bundle (Dear ImGui), pytest
**Spec:** `docs/superpowers/specs/2026-06-02-command-palette-design.md`
---
## Execution Constraints
These apply to every task:
- **No subagents.** Execute as a single agent.
- **Pre-edit checkpoint:** Before any file edit, run `git add .` to stage current state.
- **Per-file atomic commits:** One file = one commit. Never batch.
- **Commit message format:** `<type>(<scope>): <imperative description>` (e.g., `feat(palette): add fuzzy matcher`).
- **Git note format:** Attach a 3-8 line rationale to each commit.
- **Style baseline:** `conductor/product-guidelines.md` — 1-space indent, no comments, type hints.
- **Test framework:** pytest. Live GUI tests use the `live_gui` fixture.
---
## File Structure
| File | Action | Responsibility |
|---|---|---|
| `src/command_palette.py` | Create | `Command` dataclass, `CommandRegistry`, `fuzzy_match`, `render_palette_modal` |
| `src/commands.py` | Create | ~30-50 command definitions across categories |
| `src/gui_2.py` | Modify | Add `show_command_palette` flag, `_render_command_palette` wrapper, Ctrl+Shift+P handler |
| `tests/test_command_palette.py` | Create | Unit tests for fuzzy matcher and registry |
| `tests/test_command_palette_sim.py` | Create | Integration tests via `live_gui` |
---
## Task 1: Define the `Command` dataclass and `CommandRegistry`
**Files:**
- Create: `src/command_palette.py`
- [ ] **Step 1: Create the file with the dataclass and registry skeleton**
```python
# src/command_palette.py
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Optional, Callable, List, Dict, Any
@dataclass
class Command:
id: str
title: str
category: str
shortcut: Optional[str] = None
description: str = ""
enabled_when: Optional[str] = None
action: Optional[Callable] = None
@dataclass
class ScoredCommand:
command: Command
score: float
class CommandRegistry:
def __init__(self) -> None:
self._commands: Dict[str, Command] = {}
def register(self, command_or_callable: Any) -> Any:
if isinstance(command_or_callable, Command):
cmd = command_or_callable
else:
cmd = Command(
id=command_or_callable.__name__,
title=command_or_callable.__name__.replace("_", " ").title(),
category="uncategorized",
action=command_or_callable,
)
if cmd.id in self._commands:
raise ValueError(f"Command {cmd.id} already registered")
self._commands[cmd.id] = cmd
return command_or_callable
def all(self) -> List[Command]:
return list(self._commands.values())
def get(self, command_id: str) -> Optional[Command]:
return self._commands.get(command_id)
```
- [ ] **Step 2: Commit**
```bash
git -C C:\projects\manual_slop add src/command_palette.py
git -C C:\projects\manual_slop commit -m "feat(palette): add Command dataclass and CommandRegistry"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Defines Command (id, title, category, shortcut, description, action) and CommandRegistry (register, all, get). Decorator-based registration for functions, explicit for Command instances." $_ }
```
---
## Task 2: Implement the fuzzy matcher
**Files:**
- Modify: `src/command_palette.py`
- Test: `tests/test_command_palette.py`
- [ ] **Step 2.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 2.2: Write the failing test (TDD Red)**
Create `tests/test_command_palette.py`:
```python
# tests/test_command_palette.py
from src.command_palette import Command, ScoredCommand, fuzzy_match
def _cmd(id: str, title: str) -> Command:
return Command(id=id, title=title, category="test")
def test_fuzzy_match_prefix_ranks_first():
candidates = [
_cmd("find", "Find in Selection"),
_cmd("fold", "Fold All"),
_cmd("config", "Configure Settings"),
]
results = fuzzy_match("fin", candidates, top_n=10)
assert len(results) > 0
assert results[0].command.id == "find"
assert results[0].score > 0.5
def test_fuzzy_match_subsequence_match():
candidates = [_cmd("x", "Find")]
results = fuzzy_match("fd", candidates, top_n=10)
assert len(results) == 1
assert results[0].command.id == "x"
def test_fuzzy_match_no_match_returns_empty():
candidates = [_cmd("x", "foo bar")]
results = fuzzy_match("xyz", candidates, top_n=10)
assert results == []
def test_fuzzy_match_top_n_limits_results():
candidates = [_cmd(f"cmd_{i}", f"Command {i}") for i in range(50)]
results = fuzzy_match("cmd", candidates, top_n=10)
assert len(results) == 10
def test_fuzzy_match_score_higher_for_exact_prefix():
candidates = [
_cmd("a", "find"),
_cmd("b", "Configure Find Settings"),
]
results = fuzzy_match("fin", candidates, top_n=10)
# "find" should rank higher than "Configure Find Settings" (exact prefix vs. word boundary)
assert results[0].command.id == "a"
```
- [ ] **Step 2.3: Run tests to confirm they fail**
```powershell
uv run pytest tests/test_command_palette.py -v
```
Expected: `ImportError` or `AttributeError` for `fuzzy_match`.
- [ ] **Step 2.4: Implement `fuzzy_match`**
Add to `src/command_palette.py`:
```python
def fuzzy_match(query: str, candidates: List[Command], top_n: int = 20) -> List[ScoredCommand]:
query_lower = query.lower()
scored: List[ScoredCommand] = []
for cmd in candidates:
title_lower = cmd.title.lower()
if not _is_subsequence(query_lower, title_lower):
continue
score = _compute_score(query_lower, title_lower)
scored.append(ScoredCommand(command=cmd, score=score))
scored.sort(key=lambda r: r.score, reverse=True)
return scored[:top_n]
def _is_subsequence(query: str, target: str) -> bool:
qi = 0
for ch in target:
if qi < len(query) and ch == query[qi]:
qi += 1
return qi == len(query)
def _compute_score(query: str, target: str) -> float:
score = 0.0
if target.startswith(query):
score += 1.0
elif _starts_at_word_boundary(query, target):
score += 0.5
if _is_contiguous(query, target):
score += 0.3
gaps = _count_gaps(query, target)
score -= 0.1 * gaps
return score
def _starts_at_word_boundary(query: str, target: str) -> bool:
if not target.startswith(query):
return False
return len(query) == 0 or not query[0].isalnum() or len(target) == len(query) or not target[len(query)].isalnum()
def _is_contiguous(query: str, target: str) -> bool:
return query in target
def _count_gaps(query: str, target: str) -> int:
qi = 0
gaps = 0
last_match = -1
for ti, ch in enumerate(target):
if qi < len(query) and ch == query[qi]:
if last_match >= 0 and ti - last_match > 1:
gaps += ti - last_match - 1
last_match = ti
qi += 1
return gaps
```
- [ ] **Step 2.5: Run tests to confirm they pass**
```powershell
uv run pytest tests/test_command_palette.py -v
```
Expected: PASS.
- [ ] **Step 2.6: Commit**
```bash
git -C C:\projects\manual_slop add src/command_palette.py tests/test_command_palette.py
git -C C:\projects\manual_slop commit -m "feat(palette): add fuzzy_match with subsequence matching and scoring"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Pure function. Subsequence match + 4-component score: exact prefix (+1.0), word boundary (+0.5), contiguous (+0.3), gap penalty (-0.1 per gap). Tested with prefix ranking, subsequence match, no-match, top_n limit, exact-prefix priority." $_ }
```
---
## Task 3: Define static commands
**Files:**
- Create: `src/commands.py`
- [ ] **Step 3.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 3.2: Create the commands file**
```python
# src/commands.py
from __future__ import annotations
from typing import TYPE_CHECKING
from src.command_palette import CommandRegistry
if TYPE_CHECKING:
from src.gui_2 import App
registry = CommandRegistry()
@registry.register
def reset_session(app: "App") -> None:
"""Reset Session — Reset the AI session and clear discussion history."""
from src import ai_client
ai_client.reset_session()
if hasattr(app, "_handle_reset_session"):
app._handle_reset_session()
@registry.register
def clear_discussion(app: "App") -> None:
"""Clear Discussion — Clear all entries in the current discussion."""
if hasattr(app, "discussion_history"):
app.discussion_history = []
@registry.register
def toggle_diagnostics(app: "App") -> None:
"""Toggle Diagnostics — Show/hide the Diagnostics panel."""
if hasattr(app, "show_diagnostics"):
app.show_diagnostics = not app.show_diagnostics
@registry.register
def add_all_files_to_context(app: "App") -> None:
"""Add All Files to Context — Add all tracked files to the context."""
if hasattr(app, "_add_all_files_to_context"):
app._add_all_files_to_context()
@registry.register
def open_project(app: "App") -> None:
"""Open Project — Open a different project TOML."""
if hasattr(app, "_show_project_picker"):
app._show_project_picker()
@registry.register
def save_project(app: "App") -> None:
"""Save Project — Save the current project state to TOML."""
if hasattr(app, "_save_project_state"):
app._save_project_state()
@registry.register
def trigger_hot_reload(app: "App") -> None:
"""Hot Reload — Reload the GUI module to pick up code changes."""
from src.hot_reloader import HotReloader
HotReloader.reload("src.gui_2", app)
@registry.register
def show_documentation(app: "App") -> None:
"""Show Documentation — Open the documentation index in the browser."""
import webbrowser
webbrowser.open("https://git.cozyair.dev/ed/manual_slop/")
@registry.register
def switch_to_dark_theme(app: "App") -> None:
"""Switch to Dark Theme."""
from src import theme_2
theme_2.apply_dark_theme()
@registry.register
def switch_to_light_theme(app: "App") -> None:
"""Switch to Light Theme."""
from src import theme_2
theme_2.apply_light_theme()
@registry.register
def switch_to_nerv_theme(app: "App") -> None:
"""Switch to NERV Theme."""
from src.theme_nerv import apply_nerv
apply_nerv()
```
- [ ] **Step 3.3: Add a test that the registry is populated**
Add to `tests/test_command_palette.py`:
```python
def test_commands_registry_has_core_commands():
from src.commands import registry
all_ids = {c.id for c in registry.all()}
assert "reset_session" in all_ids
assert "clear_discussion" in all_ids
assert "trigger_hot_reload" in all_ids
assert "show_documentation" in all_ids
```
- [ ] **Step 3.4: Run tests**
```powershell
uv run pytest tests/test_command_palette.py -v
```
Expected: All tests pass.
- [ ] **Step 3.5: Commit**
```bash
git -C C:\projects\manual_slop add src/commands.py tests/test_command_palette.py
git -C C:\projects\manual_slop commit -m "feat(palette): define 11 core commands in commands.py"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "11 commands: reset_session, clear_discussion, toggle_diagnostics, add_all_files_to_context, open_project, save_project, trigger_hot_reload, show_documentation, switch_to_dark/light/nerv_theme. Each has defensive hasattr checks for the App methods they call." $_ }
```
---
## Task 4: Implement `render_palette_modal`
**Files:**
- Modify: `src/command_palette.py`
- [ ] **Step 4.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 4.2: Add the modal render function**
Add to `src/command_palette.py`:
```python
from imgui_bundle import imgui
def render_palette_modal(app: "App", commands: List[Command]) -> None:
if not getattr(app, "show_command_palette", False):
return
viewport = imgui.get_main_viewport()
center = viewport.get_center()
imgui.set_next_window_pos((center.x - 300, center.y - 200))
imgui.set_next_window_size((600, 400))
opened = [True]
if not imgui.begin("Command Palette##manual_slop", flags=imgui.WindowFlags_.no_resize | imgui.WindowFlags_.no_collapse)[0]:
app.show_command_palette = False
imgui.end()
return
if not hasattr(app, "_command_palette_query"):
app._command_palette_query = ""
if not hasattr(app, "_command_palette_selected"):
app._command_palette_selected = 0
io = imgui.get_io()
if imgui.is_key_pressed(imgui.Key.escape):
app.show_command_palette = False
imgui.end()
return
imgui.set_next_item_width(-1)
changed, app._command_palette_query = imgui.input_text("##query", app._command_palette_query, 256)
imgui.set_keyboard_focus_here()
results = fuzzy_match(app._command_palette_query, commands, top_n=20)
if imgui.begin_child("##results", (0, -1)):
for i, scored in enumerate(results):
is_selected = (i == app._command_palette_selected)
label = f"[{scored.command.category}] {scored.command.title}"
if imgui.selectable(label, is_selected)[0]:
app._command_palette_selected = i
if is_selected:
imgui.set_item_default_focus()
if imgui.is_key_pressed(imgui.Key.enter) or imgui.is_key_pressed(imgui.Key.keypad_enter):
if scored.command.action:
scored.command.action(app)
app.show_command_palette = False
app._command_palette_query = ""
app._command_palette_selected = 0
if not results:
imgui.text_disabled("No matching commands.")
imgui.end_child()
imgui.end()
```
- [ ] **Step 4.3: Commit**
```bash
git -C C:\projects\manual_slop add src/command_palette.py
git -C C:\projects\manual_slop commit -m "feat(palette): add render_palette_modal with fuzzy search and keyboard nav"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Centered 600x400 modal. Search input with focus on open. Up/Down arrow nav via _command_palette_selected. Enter executes action and closes. Escape closes. Stores query/selected on app to persist across frames. Per delegation pattern: takes app as param." $_ }
```
---
## Task 5: Wire the palette into `App` class
**Files:**
- Modify: `src/gui_2.py`
- [ ] **Step 5.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 5.2: Add `show_command_palette` to `App.__init__`**
Use `manual-slop_py_get_skeleton` first to see the current `App.__init__` structure, then add:
```python
# In App.__init__ (find a good spot near other UI flags)
self.show_command_palette: bool = False
```
- [ ] **Step 5.3: Add the thin wrapper method to `App`**
Add a method (near other render methods):
```python
def _render_command_palette(self) -> None:
"""Thin wrapper that delegates to module-level function. Per UI delegation pattern."""
from src.command_palette import render_palette_modal
from src.commands import registry
render_palette_modal(self, registry.all())
```
- [ ] **Step 5.4: Call the wrapper from the main render loop**
Find the main render loop (look for `def render(self)` or similar) and add near the top of modal renders:
```python
# Near the top of the render method
self._render_command_palette()
```
- [ ] **Step 5.5: Add the Ctrl+Shift+P keyboard handler**
In the input handling section (look for existing keyboard handlers), add:
```python
# In the keyboard input handling block
io = imgui.get_io()
if (io.key_ctrl and io.key_shift
and not io.key_alt and not io.key_super
and imgui.is_key_pressed(imgui.Key.p)):
self.show_command_palette = not self.show_command_palette
if self.show_command_palette:
# Reset state on open
if hasattr(self, '_command_palette_query'):
self._command_palette_query = ""
if hasattr(self, '_command_palette_selected'):
self._command_palette_selected = 0
```
- [ ] **Step 5.6: Commit**
```bash
git -C C:\projects\manual_slop add src/gui_2.py
git -C C:\projects\manual_slop commit -m "feat(gui): wire Command Palette into App class with Ctrl+Shift+P"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Added show_command_palette flag, _render_command_palette thin wrapper, Ctrl+Shift+P keyboard handler. Wrapper delegates to module-level render_palette_modal. Keyboard handler resets state on open." $_ }
```
---
## Task 6: Write integration tests via `live_gui`
**Files:**
- Create: `tests/test_command_palette_sim.py`
- [ ] **Step 6.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 6.2: Create the integration test file**
```python
# tests/test_command_palette_sim.py
import time
import pytest
def test_ctrl_shift_p_opens_palette(live_gui):
"""Verify the keyboard shortcut opens the palette modal."""
client = live_gui[1]
client.press_key_combo("Ctrl+Shift+P")
time.sleep(0.5)
state = client.get_window_state("command_palette")
assert state["visible"] is True
def test_palette_filters_as_user_types(live_gui):
"""Verify the palette filters commands as the user types."""
client = live_gui[1]
client.press_key_combo("Ctrl+Shift+P")
time.sleep(0.3)
client.type_in_palette("reset")
time.sleep(0.3)
results = client.get_palette_results()
titles = [r["title"].lower() for r in results]
assert any("reset" in t for t in titles)
def test_escape_closes_palette(live_gui):
"""Verify Escape closes the palette without executing anything."""
client = live_gui[1]
client.press_key_combo("Ctrl+Shift+P")
time.sleep(0.3)
client.press_key("Escape")
time.sleep(0.3)
state = client.get_window_state("command_palette")
assert state["visible"] is False
```
- [ ] **Step 6.3: Run the tests**
```powershell
uv run pytest tests/test_command_palette_sim.py -v
```
Expected: 3 tests pass (live_gui fixture handles process lifecycle).
- [ ] **Step 6.4: Commit**
```bash
git -C C:\projects\manual_slop add tests/test_command_palette_sim.py
git -C C:\projects\manual_slop commit -m "test(palette): add live_gui integration tests for Ctrl+Shift+P, filter, escape"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "3 integration tests via live_gui: Ctrl+Shift+P opens palette, typing filters by fuzzy match, Escape closes. Each is independent; failures isolate to the specific behavior." $_ }
```
---
## Task 7: Phase Completion Verification
- [ ] **Step 7.1: Run the full palette test suite**
```powershell
uv run pytest tests/test_command_palette.py tests/test_command_palette_sim.py -v
```
Expected: All unit + integration tests pass.
- [ ] **Step 7.2: Manually verify in a running app**
```powershell
uv run sloppy.py
```
Press `Ctrl+Shift+P`. The palette should open. Type "reset". The "Reset Session" command should appear at the top. Press Enter. The palette should close. Verify the discussion history was cleared.
- [ ] **Step 7.3: Create the checkpoint commit**
```bash
git -C C:\projects\manual_slop commit --allow-empty -m "conductor(checkpoint): Command Palette (Phase 2) complete"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Command Palette feature complete. 6 atomic per-file commits. Unit tests for fuzzy_match, registry, and core commands. Integration tests for Ctrl+Shift+P, filtering, escape. Manually verified in running app." $_ }
```
---
## Self-Review
- **Spec coverage:** All design sections have a task. ✓
- **Placeholder scan:** No "TBD"/"TODO". ✓
- **Type consistency:** `Command`, `ScoredCommand`, `CommandRegistry`, `fuzzy_match` names used consistently. ✓
- **No subagent dispatch:** All tasks are single-agent. ✓
@@ -1,339 +0,0 @@
# Conductor Docs Refresh Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Sync the source-of-truth conductor docs (`conductor/product.md`, `conductor/product-guidelines.md`, `conductor/tech-stack.md`, `conductor/workflow.md`, `conductor/index.md`) to reflect the current state of the codebase after 4 months of evolution since the last docs refresh.
**Architecture:** Single-agent sequential execution. Each conductor doc is a self-contained task: audit current claims against the actual codebase, identify drift, rewrite stale sections, commit per-file, attach git note. No new conductor docs are written in this plan (those are out of scope per the spec).
**Tech Stack:** Manual Slop's MCP research tools. Git for version control. Markdown for documentation.
**Spec:** `docs/superpowers/specs/2026-06-02-comprehensive-documentation-refresh-design.md` §5 (Sub-Track 2).
**Depends on:** Sub-Track 1 (`docs_layer_refresh_20260602`) must complete first. The human-facing docs layer is the source of truth that this plan syncs to.
---
## Execution Constraints
These apply to every task in this plan:
- **No subagents.** Execute as a single agent. No Tier 3/Tier 4 delegation. No parallel workers.
- **Pre-edit checkpoint:** Before any file edit, run `git add .` to stage current state.
- **Per-file atomic commits:** One file = one commit. Never batch.
- **Commit message format:** `docs(<scope>): <imperative description>`.
- **Git note format:** Attach a 3-8 line rationale to each commit.
- **Symbol parity:** Every class, function, module, or dep name in these docs must match the source.
- **The conductor docs are the source of truth** for the rest of the project. Anything documented here must be verifiable in the codebase.
---
## Task 1: Sync `conductor/product.md`
**Files:**
- Modify: `conductor/product.md` (current size ~23.1K)
- Reference: `src/` directory (every module), `docs/Readme.md` (now updated per Sub-Track 1), `docs/guide_*.md`
**Scope:** This is the product vision / feature list document. Every feature claim must correspond to a real subsystem. Every subsystem should appear. No "planned" or "in progress" features should be listed as if they exist (unless they're in the active backlog).
- [ ] **Step 1.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 1.2: Read the current `conductor/product.md`**
Use `manual-slop_get_file_summary` first, then `manual-slop_get_file_slice` for sections.
- [ ] **Step 1.3: Inventory every src/ module**
Use `manual-slop_list_directory path="C:\projects\manual_slop\src"` to get the current module list. For each module, use `manual-slop_get_file_summary` to understand its purpose.
Map each module to a feature. Note: the modules in `src/` are the source of truth for what exists.
- [ ] **Step 1.4: Compare modules to feature claims in `product.md`**
Build a comparison:
- **Modules in `src/` not mentioned in `product.md`:** [list] — these are missing features
- **Features in `product.md` with no corresponding module:** [list] — these are stale claims
- **Features with a module that does something different than `product.md` claims:** [list] — these are stale descriptions
- [ ] **Step 1.5: Add missing features**
For each module not mentioned in `product.md`:
- Find the most relevant existing section in `product.md`
- Add a sub-bullet or paragraph describing the feature
- Use the gap analysis from Sub-Track 1's `gap_analysis.md` as a cross-reference
Pay particular attention to: RAG, Beads, Hot Reload, Discussion Metrics/Compression, Command Palette, Structural File Editor, NERV Theme, Persona Editor, Workspace Profiles, History (undo/redo), External MCP, Synced HITL Bridge, External Editor Integration.
- [ ] **Step 1.6: Remove or correct stale claims**
For each feature claim that no longer matches the code:
- If the feature was removed, delete the claim
- If the feature was renamed or restructured, update the description
- If the feature was culled (per the Culling Candidates report), remove the claim
- [ ] **Step 1.7: Update the "Primary Use Cases" section**
If real-world usage patterns have evolved (e.g., the user mentioned Claude is no longer used), update the use cases to reflect actual current usage.
- [ ] **Step 1.8: Validate all cross-doc links**
For every `[text](./relative/path)` in the modified `product.md`, verify the target exists.
- [ ] **Step 1.9: Commit per-section (one commit per logical group of changes)**
For example:
- If you added 5 new features, that's 1 commit: `docs(product): add RAG, Beads, Hot Reload, and 2 more features`
- If you corrected 3 stale claims, that's 1 commit: `docs(product): correct stale claims for X, Y, Z`
- If you removed 2 culled features, that's 1 commit: `docs(product): remove culled features X, Y`
```powershell
git -C C:\projects\manual_slop add conductor/product.md
git -C C:\projects\manual_slop commit -m "docs(product): <description>"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Sync rationale: [what was stale, what was added, what was removed]." $_ }
```
---
## Task 2: Sync `conductor/tech-stack.md`
**Files:**
- Modify: `conductor/tech-stack.md`
- Reference: `pyproject.toml`, `requirements.txt`, `src/` (every module)
**Scope:** Every entry must be a real Python dep (verify in `pyproject.toml` or `requirements.txt`) or a real `src/` module. No "we use X" claims without verification.
- [ ] **Step 2.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 2.2: Read the current `conductor/tech-stack.md`**
- [ ] **Step 2.3: Read `pyproject.toml` to get the canonical dep list**
Use `manual-slop_read_file` (it's small).
- [ ] **Step 2.4: For each dep claim in `tech-stack.md`, verify it exists in `pyproject.toml`**
Use `manual-slop_py_find_usages` or grep to confirm. Flag any claim that doesn't match.
- [ ] **Step 2.5: For each `src/` module claim in `tech-stack.md`, verify it exists**
Use `manual-slop_search_files` to confirm.
- [ ] **Step 2.6: Add missing deps and modules**
Likely missing entries (verify via Step 2.3-2.5):
- `chromadb` (RAG)
- `dolt` or any Beads dep
- `psutil` (performance monitoring)
- `pywin32` (window frame manipulation)
- `pydantic` (if used)
- New `src/` modules: `rag_engine.py`, `beads_client.py`, `hot_reloader.py`, `history.py`, `workspace_manager.py`, `theme_nerv.py`, `theme_nerv_fx.py`, `personas.py`, `presets.py`, `context_presets.py`, `tool_bias.py`, `tool_presets.py`, `synthesis_formatter.py`, `imgui_scopes.py`, etc.
- [ ] **Step 2.7: Remove or correct stale claims**
- [ ] **Step 2.8: Validate all cross-doc links**
- [ ] **Step 2.9: Commit per-section**
```powershell
git -C C:\projects\manual_slop add conductor/tech-stack.md
git -C C:\projects\manual_slop commit -m "docs(tech-stack): <description>"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Sync rationale: [added X deps/modules, removed Y stale claims]." $_ }
```
---
## Task 3: Sync `conductor/workflow.md`
**Files:**
- Modify: `conductor/workflow.md` (current size ~25.2K)
- Reference: 5-10 recent completed tracks (read their `plan.md` files), the test suite, the pre-commit/lint scripts
**Scope:** The task lifecycle, TDD protocol, phase completion verification, checkpointing. This is the document every conductor-driven agent reads. It must match actual practice.
- [ ] **Step 3.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 3.2: Read the current `conductor/workflow.md`**
- [ ] **Step 3.3: Spot-check 5 recent completed tracks for workflow compliance**
Pick 5 tracks from `conductor/archive/` or `conductor/tracks/` (use `manual-slop_list_directory`). For each:
- Read its `plan.md` and `spec.md`
- Compare the actual workflow followed (commits, tests, checkpoint) to what `workflow.md` prescribes
- Note any drift: did the track skip a phase? Add a step that isn't in `workflow.md`? Skip a verification step?
- [ ] **Step 3.4: Update `workflow.md` to match actual practice**
If a phase is being consistently skipped, either (a) update the docs to make it optional, or (b) flag the drift and ask the user. Default: (a) — update the docs to match reality, then enforce via a future lint script (which is out of scope for this track).
- [ ] **Step 3.5: Update the Phase Completion Verification section**
Verify the protocol description matches the actual steps in recent tracks. Update if needed.
- [ ] **Step 3.6: Update the Commit Guidelines section**
Verify the message format examples match recent commits. Run `git log --oneline -20` to see the recent style.
- [ ] **Step 3.7: Validate all cross-doc links**
- [ ] **Step 3.8: Commit per-section**
```powershell
git -C C:\projects\manual_slop add conductor/workflow.md
git -C C:\projects\manual_slop commit -m "docs(workflow): <description>"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Sync rationale: [drift findings and corrections]." $_ }
```
---
## Task 4: Sync `conductor/product-guidelines.md`
**Files:**
- Modify: `conductor/product-guidelines.md` (current size ~6.6K)
- Reference: `src/app_controller.py`, `src/gui_2.py`, `src/imgui_scopes.py`, `scripts/check_imgui_scopes.py`, recent track style
**Scope:** The AI-Optimized Python Style (1-space indent, no comments, type hints), Modular Controller Pattern, UI Delegation Pattern, Mandatory ImGui Verification, Structural Dependency Mapping (SDM) docstrings. These rules must match the actual code in `src/`.
- [ ] **Step 4.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 4.2: Read the current `conductor/product-guidelines.md`**
- [ ] **Step 4.3: Spot-check the style claims against actual `src/` code**
For each style rule, sample 3 random files in `src/` and verify the rule is followed:
- **1-space indentation:** Use `manual-slop_get_file_slice` on a few files, look for any 4-space or tab-indented lines
- **No comments:** Use `grep` (via `manual-slop_run_powershell`) to find `#` characters in `src/*.py` (excluding shebangs, type comments, etc.)
- **Type hints:** Sample a few public function signatures
- **Modular Controller Pattern:** Check `src/app_controller.py` for module-level functions
- **UI Delegation Pattern:** Check `src/gui_2.py` for `render_xxx` module-level functions
- **SDM docstrings:** Check 3 random functions for `[C: ...]` and `[M: ...]` tags
- [ ] **Step 4.4: Update the guidelines to match reality**
If a rule is no longer followed:
- Option (a): Update the rule to be more accurate
- Option (b): Flag the drift and ask the user
Default: (a) — but flag any major drift in the commit message so the user is aware.
- [ ] **Step 4.5: Verify the Mandatory ImGui Verification claim**
Check that `scripts/check_imgui_scopes.py` exists and works. If it's been replaced by `src/imgui_scopes.py` and a different linter, update the docs.
- [ ] **Step 4.6: Validate all cross-doc links**
- [ ] **Step 4.7: Commit per-section**
```powershell
git -C C:\projects\manual_slop add conductor/product-guidelines.md
git -C C:\projects\manual_slop commit -m "docs(guidelines): <description>"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Sync rationale: [style drift findings, corrections made]." $_ }
```
---
## Task 5: Update `conductor/index.md`
**Files:**
- Modify: `conductor/index.md` (current size 334 bytes)
- Reference: every file in `conductor/`
**Scope:** The conductor directory index. Must link to all major conductor files.
- [ ] **Step 5.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 5.2: Inventory `conductor/` directory**
Use `manual-slop_get_tree path="C:\projects\manual_slop\conductor" max_depth=2`.
- [ ] **Step 5.3: Identify any major conductor file not linked from `index.md`**
Likely candidates: `conductor/code_styleguides/`, `conductor/fix_test_suite_failures_20260516.md`, any new top-level files.
- [ ] **Step 5.4: Update `index.md` to link the missing files**
- [ ] **Step 5.5: Validate all cross-doc links**
- [ ] **Step 5.6: Commit**
```powershell
git -C C:\projects\manual_slop add conductor/index.md
git -C C:\projects\manual_slop commit -m "docs(conductor): update index with [list of newly-linked files]"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Added links to [list]." $_ }
```
---
## Task 6: Sub-Track 2 Verification
- [ ] **Step 6.1: Dep coverage audit**
For every Python dep in `pyproject.toml`, confirm it appears in `conductor/tech-stack.md`. Use:
```powershell
$deps = (Get-Content C:\projects\manual_slop\pyproject.toml | Select-String '^\s*"?[a-zA-Z][a-zA-Z0-9_-]*[><=~]' | ForEach-Object { ($_.ToString().Trim() -split '["><=~]')[0].Trim() } | Sort-Object -Unique)
foreach ($dep in $deps) { if (-not (Select-String -Path C:\projects\manual_slop\conductor\tech-stack.md -Pattern $dep -SimpleMatch -Quiet)) { Write-Host "MISSING: $dep" } }
```
- [ ] **Step 6.2: Module coverage audit**
For every `.py` file in `src/`, confirm it appears in `conductor/tech-stack.md` or `conductor/product.md`. Use:
```powershell
Get-ChildItem C:\projects\manual_slop\src\*.py | ForEach-Object { $name = $_.BaseName; if (-not (Select-String -Path C:\projects\manual_slop\conductor\tech-stack.md, C:\projects\manual_slop\conductor\product.md -Pattern $name -SimpleMatch -Quiet)) { Write-Host "MISSING: $name" } }
```
- [ ] **Step 6.3: Feature claim verification**
For 3 random features claimed in `conductor/product.md`, verify each has a corresponding module and that the module does what the claim says. Use `manual-slop_get_file_summary` on the relevant module.
- [ ] **Step 6.4: Per-file commit verification**
```powershell
git -C C:\projects\manual_slop log --oneline -- conductor/product.md conductor/product-guidelines.md conductor/tech-stack.md conductor/workflow.md conductor/index.md | head -20
```
Confirm every file has its own commit history (not batched).
- [ ] **Step 6.5: Git note verification**
```powershell
git -C C:\projects\manual_slop log --format="%H" -- conductor/ | Select-Object -First 20 | ForEach-Object { $note = git -C C:\projects\manual_slop notes show $_ 2>$null; if (-not $note) { Write-Host "MISSING NOTE: $_" } }
```
- [ ] **Step 6.6: Phase completion checkpoint**
Per the project's Phase Completion Verification protocol in `conductor/workflow.md`:
- Run the automated test suite (or at least smoke tests)
- Present the verification report to the user
- Get user sign-off
- Create the checkpoint commit: `conductor(checkpoint): Sub-Track 2 (conductor docs refresh) complete`
- Attach the verification report as a git note
---
## Self-Review (after writing this plan)
1. **Spec coverage:** Spec §5 has 6 phases (2.1-2.6). Tasks 1-5 cover 2.1-2.5. Task 6 covers 2.6 (verification). ✓ All phases covered.
2. **Placeholder scan:** No "TBD" / "TODO" markers. Specific file paths, commit commands, and verification scripts throughout. ✓
3. **Type/symbol consistency:** Module names and dep names referenced consistently. The audit scripts in Task 6 are concrete and runnable. ✓
4. **Risk check:** Pre-edit checkpoints, per-file commits, git notes, and the audit scripts mitigate drift. The optional-vs-mandatory decision in Step 3.4 is flagged for the user. ✓
@@ -1,590 +0,0 @@
# Docker & Web Frontend Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Containerize Manual Slop for Unraid deployment. Add a web frontend via the imgui-bundle web backend (per the [explorer docs](https://imgui-bundle.pages.dev/explorer/)). Preserve the existing Hook API on :8999 for agent access.
**Architecture:** Python 3.11-slim base image with `uv` for dependency management. The container runs `sloppy.py` in web mode (via a new `--web-host` / `--web-port` arg), which uses the imgui-bundle Hello ImGui web backend to render ImGui to a WebGL canvas in the browser. Two exposed ports: 8080 (web client) and 8999 (hook API). Volumes for project data and app state.
**Tech Stack:** Docker, docker-compose, Python 3.11, imgui-bundle (web backend), FastAPI/Uvicorn (existing, for hooks)
- **[checkpoint: 99a84bd]**
- **No subagents.**
- **Pre-edit checkpoint:** `git add .` before any file edit.
- **Per-file atomic commits.**
- **Commit message format:** `<type>(<scope>): <imperative description>`.
- **Git note format:** 3-8 line rationale per commit.
- **Style baseline:** 1-space indent, no comments, type hints.
---
## File Structure
| File | Action | Responsibility |
|---|---|---|
| `Dockerfile` | Create | Container build for Manual Slop |
| `docker-compose.yml` | Create | Multi-container orchestration for Unraid |
| `scripts/docker_build.sh` | Create | Build helper |
| `scripts/docker_run.sh` | Create | Run helper with env var wiring |
| `sloppy.py` | Modify | Add `--web-host` and `--web-port` args |
| `docs/guide_docker_deployment.md` | Create | Unraid setup guide |
| `tests/test_docker_build.py` | Create | Opt-in Docker build test |
| `pyproject.toml` | Modify | Add `docker` marker |
---
## Task 1: Add web args to `sloppy.py`
**Files:**
- Modify: `sloppy.py`
- [ ] **Step 1.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 1.2: Read current `sloppy.py`**
Use `manual-slop_read_file` to see current contents.
- [ ] **Step 1.3: Add the new args**
Find the existing argument parser setup and add the web args. If there isn't an argparse, look for how args are handled. Add:
```python
# In sloppy.py, after existing argument definitions
import argparse
parser = argparse.ArgumentParser(description="Manual Slop entry point")
# ... existing args ...
parser.add_argument("--web-host", default=None, help="Enable web mode and bind to this host (e.g., 0.0.0.0)")
parser.add_argument("--web-port", type=int, default=8080, help="Web mode port (default: 8080)")
parser.add_argument("--enable-test-hooks", action="store_true", help="Enable the HookServer on :8999 for external automation")
args = parser.parse_args()
```
If the existing entry point uses a different mechanism, integrate the args there.
- [ ] **Step 1.4: Add the web mode branch**
After argument parsing, before the existing main launch, add:
```python
if args.web_host is not None:
from imgui_bundle import hello_imgui
from src.api_hooks import HookServer
if args.enable_test_hooks:
hook_server = HookServer()
hook_server.start()
runner_params = hello_imgui.RunnerParams()
runner_params.app_window_params.window_title = "Manual Slop (Web)"
runner_params.app_window_params.borderless = True
runner_params.imgui_window_params.default_imgui_window_type = hello_imgui.DefaultImGuiWindowType.provide_full_screen_docker_space
runner_params.app_window_params.restore_previous_window_size = True
from src.gui_2 import App
app = App()
hello_imgui.run(runner_params, lambda: app.render_frame())
```
- [ ] **Step 1.5: Commit**
```bash
git -C C:\projects\manual_slop add sloppy.py
git -C C:\projects\manual_slop commit -m "feat(sloppy): add --web-host and --web-port args for web mode"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Adds --web-host (enables web mode) and --web-port (default 8080) args. When --web-host is set, launches via Hello ImGui web backend with full-screen docking. Preserves --enable-test-hooks for agent access via :8999." $_ }
```
---
## Task 2: Create the Dockerfile
**Files:**
- Create: `Dockerfile`
- [ ] **Step 2.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 2.2: Create the Dockerfile**
```dockerfile
FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
git curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN pip install uv
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen
COPY . .
RUN mkdir -p /projects /config
VOLUME ["/projects", "/config"]
EXPOSE 8080 8999
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD curl -f http://127.0.0.1:8999/status || exit 1
ENTRYPOINT ["uv", "run", "sloppy.py", "--enable-test-hooks", "--web-host=0.0.0.0", "--web-port=8080"]
```
- [ ] **Step 2.3: Add a `.dockerignore`**
```dockerignore
# .dockerignore
.git
__pycache__
*.pyc
.pytest_cache
.ruff_cache
.venv
.env
tests/artifacts/
tests/logs/
logs/
md_gen/
*.log
```
- [ ] **Step 2.4: Commit**
```bash
git -C C:\projects\manual_slop add Dockerfile .dockerignore
git -C C:\projects\manual_slop commit -m "feat(docker): add Dockerfile and .dockerignore for containerized deployment"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Python 3.11-slim base, uv for dep management, copies pyproject + uv.lock first for layer caching. Exposes 8080 (web) and 8999 (hooks). Volumes for /projects and /config. Healthcheck on hook status. .dockerignore excludes test artifacts and git." $_ }
```
---
## Task 3: Create the docker-compose.yml
**Files:**
- Create: `docker-compose.yml`
- [ ] **Step 3.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 3.2: Create the compose file**
```yaml
version: '3.8'
services:
manual_slop:
build: .
image: manual_slop:latest
container_name: manual_slop
ports:
- "8999:8999"
- "8080:8080"
volumes:
- /mnt/user/projects:/projects:rw
- /mnt/user/appdata/manual_slop:/config:rw
environment:
- GEMINI_API_KEY=${GEMINI_API_KEY:-}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY:-}
- MINIMAX_API_KEY=${MINIMAX_API_KEY:-}
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:8999/status"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
```
- [ ] **Step 3.3: Commit**
```bash
git -C C:\projects\manual_slop add docker-compose.yml
git -C C:\projects\manual_slop commit -m "feat(docker): add docker-compose.yml for Unraid deployment"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Two ports (8999 hooks, 8080 web), two volumes (projects, appdata config), env vars for 4 providers, healthcheck on hook status. Unraid-friendly paths." $_ }
```
---
## Task 4: Create the build/run scripts
**Files:**
- Create: `scripts/docker_build.sh`
- Create: `scripts/docker_run.sh`
- [ ] **Step 4.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 4.2: Create `scripts/docker_build.sh`**
```bash
#!/usr/bin/env bash
# scripts/docker_build.sh
# Build the Manual Slop Docker image.
set -euo pipefail
cd "$(dirname "$0")/.."
docker build -t manual_slop:latest .
```
- [ ] **Step 4.3: Create `scripts/docker_run.sh`**
```bash
#!/usr/bin/env bash
# scripts/docker_run.sh
# Run the Manual Slop container.
set -euo pipefail
cd "$(dirname "$0")/.."
docker compose up -d
```
- [ ] **Step 4.4: Make scripts executable and commit**
```bash
git -C C:\projects\manual_slop add scripts/docker_build.sh scripts/docker_run.sh
git -C C:\projects\manual_slop commit -m "feat(docker): add build and run shell scripts"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Two thin shell scripts: build (docker build -t manual_slop:latest .) and run (docker compose up -d). Will be made executable in chmod step during deployment." $_ }
```
---
## Task 5: Add the `docker` marker to `pyproject.toml`
**Files:**
- Modify: `pyproject.toml`
- [ ] **Step 5.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 5.2: Add the marker**
Find the markers list (from Task 1 of the clean-install plan) and add:
```toml
markers = [
"integration: integration tests requiring live GUI",
"strict: tests that require strict mode",
"clean_install: clean install verification (opt-in via RUN_CLEAN_INSTALL_TEST=1)",
"docker: docker build and run test (opt-in via RUN_DOCKER_TEST=1)",
]
```
- [ ] **Step 5.3: Commit**
```bash
git -C C:\projects\manual_slop add pyproject.toml
git -C C:\projects\manual_slop commit -m "test(pytest): add docker marker"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Registers the docker marker. Tests using Docker can be filtered with -m docker or -m 'not docker'." $_ }
```
---
## Task 6: Write the Docker build test
**Files:**
- Create: `tests/test_docker_build.py`
- [ ] **Step 6.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 6.2: Create the test file**
```python
# tests/test_docker_build.py
import os
import shutil
import subprocess
import time
import pytest
import requests
IMAGE_NAME = "manual_slop:test"
CONTAINER_NAME = "manual_slop_test_container"
WEB_PORT = 18080
HOOK_PORT = 18999
@pytest.mark.docker
def test_docker_image_builds(tmp_path):
"""Build the Docker image. Slow; opt-in."""
if os.environ.get("RUN_DOCKER_TEST") != "1":
pytest.skip("Set RUN_DOCKER_TEST=1 to enable")
if not _docker_available():
pytest.skip("Docker not available in this environment")
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
result = subprocess.run(
["docker", "build", "-t", IMAGE_NAME, "."],
cwd=repo_root,
capture_output=True, text=True, timeout=600,
)
assert result.returncode == 0, f"Docker build failed: {result.stderr}"
@pytest.mark.docker
def test_docker_container_starts_and_responds():
"""Run the container, verify web and hook endpoints respond."""
if os.environ.get("RUN_DOCKER_TEST") != "1":
pytest.skip("Set RUN_DOCKER_TEST=1 to enable")
if not _docker_available():
pytest.skip("Docker not available in this environment")
_cleanup_container()
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
result = subprocess.run(
[
"docker", "run", "-d",
"--name", CONTAINER_NAME,
"-p", f"{WEB_PORT}:8080",
"-p", f"{HOOK_PORT}:8999",
IMAGE_NAME,
],
cwd=repo_root,
capture_output=True, text=True, timeout=30,
)
assert result.returncode == 0, f"Docker run failed: {result.stderr}"
try:
start = time.time()
ready = False
while time.time() - start < 90:
try:
r = requests.get(f"http://127.0.0.1:{HOOK_PORT}/status", timeout=1)
if r.status_code == 200:
ready = True
break
except (requests.ConnectionError, requests.Timeout):
pass
time.sleep(1)
assert ready, "Container hook API did not respond within 90s"
r = requests.get(f"http://127.0.0.1:{WEB_PORT}/", timeout=5)
assert r.status_code == 200
body = r.content.lower()
assert b"<html" in body or b"<!doctype" in body, "Web endpoint did not return HTML"
finally:
_cleanup_container()
def _docker_available() -> bool:
return shutil.which("docker") is not None
def _cleanup_container() -> None:
subprocess.run(
["docker", "rm", "-f", CONTAINER_NAME],
capture_output=True,
)
```
- [ ] **Step 6.3: Run in skip mode**
```powershell
uv run pytest tests/test_docker_build.py -v
```
Expected: 2 skipped.
- [ ] **Step 6.4: Commit**
```bash
git -C C:\projects\manual_slop add tests/test_docker_build.py
git -C C:\projects\manual_slop commit -m "test(docker): add opt-in build and container-run tests"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Two opt-in tests: build image (RUN_DOCKER_TEST=1), run container and verify hook + web endpoints. Skipped by default. Docker daemon required." $_ }
```
---
## Task 7: Write the Unraid deployment guide
**Files:**
- Create: `docs/guide_docker_deployment.md`
- [ ] **Step 7.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 7.2: Create the guide**
```markdown
# Docker Deployment Guide (Unraid)
[Top](../README.md) | [Architecture](guide_architecture.md) | [Tools & IPC](guide_tools.md)
---
## Overview
This guide covers deploying Manual Slop on Unraid (or any Docker host) using the containerized image. The deployment provides:
- A web-accessible ImGui GUI (browser-based, no local display required)
- The Hook API on `:8999` for agent access
- Persistent volumes for projects and app state
## Prerequisites
- Unraid 6.10+ (or any Docker host with compose support)
- A project share mounted at `/mnt/user/projects` (or edit `docker-compose.yml` to match your path)
- API keys for the providers you want to use
## Building the Image
From the repo root:
```bash
docker build -t manual_slop:latest .
```
Or use the helper:
```bash
./scripts/docker_build.sh
```
## Running the Container
Edit `docker-compose.yml` to set your volume paths and provider keys (via `.env` file or environment).
```bash
# Create a .env file with your API keys
cat > .env <<EOF
GEMINI_API_KEY=your-key-here
ANTHROPIC_API_KEY=your-key-here
DEEPSEEK_API_KEY=your-key-here
MINIMAX_API_KEY=your-key-here
EOF
# Start the container
docker compose up -d
```
## Accessing the GUI
Open a browser and navigate to:
- `http://<your-unraid-ip>:8080` for the web client
- `http://<your-unraid-ip>:8999/status` for the hook API health check
The web client renders the ImGui panels via WebGL. The Hello ImGui web backend streams frame deltas over WebSocket.
## Agent Access
Agents interact with the running container via the Hook API on `:8999`. Examples:
```bash
# Check status
curl http://<your-unraid-ip>:8999/status
# Get MMA state
curl http://<your-unraid-ip>:8999/api/gui/mma_status
```
See [guide_tools.md](guide_tools.md) for the full Hook API reference.
## Volumes
- `/projects` — Mounted from `/mnt/user/projects` by default. Your project workspaces live here. The `manual_slop.toml` per project is in this directory.
- `/config` — Mounted from `/mnt/user/appdata/manual_slop` by default. App state: presets, personas, log directory, workspace profiles.
## Updating
```bash
git pull
docker build -t manual_slop:latest .
docker compose up -d
```
## Backup
Back up `/config` to preserve presets, personas, and workspace profiles. Back up `/projects/<project>/conductor/` to preserve track history.
## Troubleshooting
- **Port conflicts:** Edit `docker-compose.yml` to change the host port (e.g., `"18080:8080"` to use 18080 on the host).
- **Permission errors:** Ensure the Unraid share has write permissions for the container's UID.
- **Hook API not responding:** Check `docker logs manual_slop` for the startup output. The hook server should log "HookServer started on :8999".
```
- [ ] **Step 7.3: Commit**
```bash
git -C C:\projects\manual_slop add docs/guide_docker_deployment.md
git -C C:\projects\manual_slop commit -m "docs(docker): add Unraid deployment guide"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Unraid-specific setup guide: prerequisites, build, run via compose, web access URLs, agent access via hook API, volumes, updating, backup, troubleshooting." $_ }
```
---
## Task 8: Phase Completion Verification
- [ ] **Step 8.1: Verify all files exist and are syntactically valid**
```powershell
# Python syntax
python -c "import ast; ast.parse(open('sloppy.py').read())"
# YAML syntax (if PyYAML available)
python -c "import yaml; yaml.safe_load(open('docker-compose.yml').read())"
# Dockerfile syntax (if hadolint available)
# hadolint Dockerfile || echo "hadolint not installed, skipping"
# Markdown lint (skip if not configured)
```
- [ ] **Step 8.2: Run the test suite in skip mode**
```powershell
uv run pytest tests/test_docker_build.py -v
```
Expected: 2 skipped.
- [ ] **Step 8.3: If Docker is available, run the tests**
```powershell
RUN_DOCKER_TEST=1 uv run pytest tests/test_docker_build.py -v
```
- [ ] **Step 8.4: Create the checkpoint commit**
```bash
git -C C:\projects\manual_slop commit --allow-empty -m "conductor(checkpoint): Docker & web frontend complete"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Track complete. Dockerfile, docker-compose.yml, build/run scripts, --web-host arg, Unraid deployment guide, opt-in Docker build test. imgui-bundle web backend integration pending Hello ImGui runner config tuning." $_ }
```
---
## Self-Review
- **Spec coverage:** All design sections have tasks. ✓
- **Placeholder scan:** All code blocks are complete. ✓
- **Type consistency:** Args named consistently (`--web-host`, `--web-port`, `--enable-test-hooks`). ✓
- **Risk acknowledged:** The web backend integration is experimental and may need iteration after testing. The checkpoint note flags this. ✓
@@ -1,656 +0,0 @@
# Docs Layer Refresh Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Refresh all human-facing documentation (`Readme.md`, `docs/Readme.md`, all `docs/guide_*.md`, plus new guides for subsystems that emerged since Feb 2026) to textbook / Microsoft "purple-tomb" SDK documentation fidelity. No source code changes. No new tooling. Per-file atomic commits with git notes.
**Architecture:** Single-agent sequential execution. Each guide is a self-contained task: audit current state, identify gaps against `conductor/product.md`, rewrite affected sections in VEFontCache-Odin style (Philosophy → Architectural Boundaries → Implementation Logic → Verification), validate cross-doc links, commit per-file, attach git note. New guides are written only for subsystems explicitly listed in `conductor/product.md` that lack coverage.
**Tech Stack:** Manual Slop's MCP research tools (`manual-slop_get_file_summary`, `manual-slop_py_get_skeleton`, `manual-slop_py_get_code_outline`, `manual-slop_get_git_diff`, `manual-slop_search_files`). Git for version control. Markdown for documentation.
**Spec:** `docs/superpowers/specs/2026-06-02-comprehensive-documentation-refresh-design.md` §4 (Sub-Track 1).
---
## Execution Constraints
These apply to every task in this plan:
- **No subagents.** Execute as a single agent. No Tier 3/Tier 4 delegation. No parallel workers.
- **Pre-edit checkpoint:** Before any file edit, run `git add .` to stage current state. Protects against any subsequent `git restore` mishap.
- **Per-file atomic commits:** One file = one commit. Never batch.
- **Commit message format:** `docs(<scope>): <imperative description>` (e.g., `docs(architecture): document RAG engine integration`).
- **Git note format:** Attach a 3-8 line rationale to each commit via `git notes add -m "<note>" <sha>`. The note should list: source files cross-referenced, subsystems updated, any decisions made.
- **Style baseline:** VEFontCache-Odin pattern. Philosophy → Architectural Boundaries → Implementation Logic → Verification. No "In this section..." filler. Symbol names match source exactly.
- **Link validation:** For every `[text](./relative/path)` link in a modified file, verify the target exists via `manual-slop_search_files` or `manual-slop_list_directory`.
- **"Textbook / purple-tomb fidelity" concretely means:** for every public class/function/event mentioned, the doc states its signature, threading constraints, side effects, and at least one usage example. Internal algorithms explained step-by-step. State machines include the full transition table.
- **No new comments added to source code.** Docs live in `/docs`, not inline.
---
## Task 1: Inventory and Gap Analysis
**Files:**
- Read: `conductor/product.md`
- Read: `docs/Readme.md`
- Read: each `docs/guide_*.md` (use `manual-slop_get_file_summary` for first pass)
- Read: `docs/MMA_Support/*` (use `manual-slop_get_file_summary` for first pass)
**Output:** A gap table written into the parent track's `conductor/tracks/documentation_refresh_comprehensive_20260602/gap_analysis.md` (create the parent track directory in this task if it doesn't exist).
- [ ] **Step 1.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 1.2: Create the parent track directory**
```powershell
New-Item -ItemType Directory -Path "C:\projects\manual_slop\conductor\tracks\documentation_refresh_comprehensive_20260602" -Force
```
- [ ] **Step 1.3: Read `conductor/product.md` to get the canonical feature list**
Use `manual-slop_get_file_summary` first. If the file is summarized well, proceed to extract feature names. If you need the full list, use `manual-slop_get_file_slice` with a generous range.
Identify every distinct feature/subsystem mentioned (e.g., Beads mode, RAG, Hot Reload, 4-Tier MMA, 26 MCP tools, Multi-Provider, etc.).
- [ ] **Step 1.4: Read every `docs/guide_*.md` to map current coverage**
For each guide:
- Use `manual-slop_get_file_summary` for a quick read
- Note which subsystems from `conductor/product.md` it covers
- Note which subsystems it does NOT cover
Files to inventory:
- `docs/guide_architecture.md`
- `docs/guide_mma.md`
- `docs/guide_tools.md`
- `docs/guide_simulations.md`
- `docs/guide_context_curation.md`
- `docs/guide_shaders_and_window.md`
- `docs/guide_meta_boundary.md`
- [ ] **Step 1.5: Read `docs/Readme.md` to confirm what's indexed**
Verify which guides are linked from the documentation index. Flag any `docs/guide_*.md` that is NOT in the index (likely candidates: `guide_context_curation.md`, `guide_shaders_and_window.md`).
- [ ] **Step 1.6: Write the gap analysis**
Create `conductor/tracks/documentation_refresh_comprehensive_20260602/gap_analysis.md` with this structure:
```markdown
# Docs Layer Gap Analysis
**Date:** 2026-06-02
**Source of truth for features:** `conductor/product.md`
**Source of truth for guides:** `docs/Readme.md` and `docs/guide_*.md`
## Existing Guides and Their Coverage
| Guide | Covers | Does NOT cover (gaps) |
|---|---|---|
| `docs/guide_architecture.md` | [list] | [list] |
| `docs/guide_mma.md` | [list] | [list] |
| ... | ... | ... |
## Subsystems Without Dedicated Guides
| Subsystem | Source module(s) | Recommendation |
|---|---|---|
| RAG | `src/rag_engine.py` | NEW: `docs/guide_rag.md` |
| Beads | `src/beads_client.py` | NEW: `docs/guide_beads.md` |
| ... | ... | ... |
## Existing Guides That Are Stale
| Guide | Last meaningful update | Stale sections |
|---|---|---|
| ... | ... | ... |
## Cross-Cutting Issues
- Files in `docs/MMA_Support/*` not linked from `docs/Readme.md`: [list]
- Guides that reference each other via broken links: [list]
- Symbols in docs that don't match current source: [list, if any found during the read pass]
```
- [ ] **Step 1.7: Commit the gap analysis**
```powershell
git -C C:\projects\manual_slop add conductor/tracks/documentation_refresh_comprehensive_20260602/gap_analysis.md
git -C C:\projects\manual_slop commit -m "docs(track): docs layer gap analysis for comprehensive refresh"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Subsystem coverage map for docs layer refresh. Each existing guide's coverage and gaps listed. New guides recommended for unlisted subsystems." $_ }
```
- [ ] **Step 1.8: Record SHA in plan**
After committing, note the commit SHA. This gap analysis becomes the source of truth for all subsequent tasks in this plan.
```markdown
Gap analysis commit SHA: [paste here]
```
---
## Task 2: Update the Documentation Index (`docs/Readme.md`)
**Files:**
- Modify: `docs/Readme.md`
- Test: Manual link validation (every `[text](./relative/path)` target exists)
- [ ] **Step 2.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 2.2: Read current `docs/Readme.md`**
Use `manual-slop_read_file` (file is small enough).
- [ ] **Step 2.3: Identify missing guide links**
Cross-reference the Guides table in `docs/Readme.md` against the list of `docs/guide_*.md` files (from Task 1). Flag every guide that is NOT in the index.
- [ ] **Step 2.4: Add the missing guide entries**
For each missing guide, add a row to the Guides table:
```markdown
| [Context Curation](guide_context_curation.md) | [contents summary] |
| [Shaders & Window](guide_shaders_and_window.md) | [contents summary] |
```
The "[contents summary]" should be 1-2 lines describing the guide's scope, written in the same high-density style as the existing rows.
- [ ] **Step 2.5: If Task 1.6 recommended new guides, add placeholder rows for them too**
```markdown
| [RAG](guide_rag.md) | [contents summary — written in Task N] |
| [Beads](guide_beads.md) | [contents summary — written in Task N] |
```
Use `[contents summary — written in Task N]` as a placeholder if the new guide has not been written yet. Update the row once the new guide is complete.
- [ ] **Step 2.6: Decide fate of `docs/MMA_Support/*`**
Options:
- (a) Keep as legacy, add a "Legacy MMA Reference" section to the index that links them
- (b) Merge into `docs/guide_mma.md` and delete the MMA_Support directory
- (c) Leave as-is, not linked
Default: (a) — keep as legacy, add a "Legacy MMA Reference (Deprecated)" section at the bottom of the index. This preserves historical context without confusing new readers.
- [ ] **Step 2.7: Verify every link in the modified file resolves**
For every `[text](./relative/path)` in the updated `docs/Readme.md`:
- Use `manual-slop_search_files` to confirm the target exists
- If broken, either fix the link or remove the row
- [ ] **Step 2.8: Commit**
```powershell
git -C C:\projects\manual_slop add docs/Readme.md
git -C C:\projects\manual_slop commit -m "docs(index): link guide_context_curation and guide_shaders_and_window; add legacy MMA section"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Linked previously-unlinked guides. Added Legacy MMA Reference section per gap analysis decision. All cross-doc links verified." $_ }
```
---
## Task 3: Rewrite `docs/guide_architecture.md`
**Files:**
- Modify: `docs/guide_architecture.md` (current size ~34.5K)
- Reference: `src/gui_2.py`, `src/ai_client.py`, `src/multi_agent_conductor.py`, `src/dag_engine.py`, `src/events.py`, `src/app_controller.py`, `src/api_hooks.py`
**Scope:** Cover all threading domains, all event types, all state machines, the Execution Clutch HITL flow, multi-provider AI client architecture, caching strategies, and any new subsystems that interact with these (RAG retrieval, Beads sync, Hot Reload lifecycle hooks).
- [ ] **Step 3.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 3.2: Read the current guide**
```powershell
# Use skeleton/summary first
manual-slop_get_file_summary path="C:\projects\manual_slop\docs\guide_architecture.md"
```
Then read sections as needed. The file is large (~34.5K) so use `manual-slop_get_file_slice` with explicit ranges.
- [ ] **Step 3.3: Audit the source code for changes since the last update**
For each major source file referenced:
- Use `manual-slop_get_git_diff path="<file>" base_rev="HEAD~30"` to see what changed in the last 30 commits
- Use `manual-slop_py_get_skeleton` and `manual-slop_py_get_code_outline` to map current structure
- Note any new classes, methods, events, state machines
- [ ] **Step 3.4: Identify which sections need rewriting**
Based on the diff, mark each section as:
- **Stale:** Needs rewrite
- **Missing:** Subsystem not covered at all
- **Current:** No changes needed
If you find that a major subsystem is missing (e.g., RAG integration with the event system, Beads lifecycle hooks, Hot Reload interaction with the threading model), add a new section to the guide.
- [ ] **Step 3.5: Rewrite stale and missing sections**
Apply the VEFontCache-Odin style:
- **Philosophy:** Mental model. Why does this subsystem exist? What problem does it solve? What are the trade-offs?
- **Architectural Boundaries:** Which thread/domain owns it? What are the interfaces? What cannot cross?
- **Implementation Logic:** State machines with full transition tables. Locking strategy. Event flow with sequence. Public class/function signatures.
- **Verification:** How is it tested? What edge cases are covered? What can fail?
For "textbook / purple-tomb fidelity": for every public class/function/event mentioned, state its signature, threading constraints, side effects, and at least one usage example.
- [ ] **Step 3.6: Validate all cross-doc links**
For every `[text](./relative/path)` in the modified file:
- Use `manual-slop_search_files` to confirm target exists
- If broken, fix the link
- [ ] **Step 3.7: Spot-check 3 random symbols against the source**
Pick 3 random public class/function names mentioned in the rewritten sections. Use `manual-slop_py_find_usages` to confirm the symbol exists in the source with the same name.
- [ ] **Step 3.8: Commit**
```powershell
git -C C:\projects\manual_slop add docs/guide_architecture.md
git -C C:\projects\manual_slop commit -m "docs(architecture): refresh for RAG, Beads, Hot Reload, and post-Feb-2026 changes"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Rewrote [list specific sections]. Cross-referenced [list source files]. New sections: [list]. Decisions: [list any judgment calls]." $_ }
```
---
## Task 4: Rewrite `docs/guide_mma.md`
**Files:**
- Modify: `docs/guide_mma.md`
- Reference: `src/multi_agent_conductor.py`, `src/dag_engine.py`, `src/conductor_tech_lead.py`, `src/mma_prompts.py`, `src/models.py`
**Scope:** The 4-Tier MMA hierarchy, Ticket/Track/WorkerContext data structures, DAG engine (Kahn's algorithm, cycle detection, transitive blocking propagation), ConductorEngine execution loop, Tier 2 ticket generation, Tier 3 worker lifecycle with Context Amnesia, Tier 4 QA integration, token firewalling, model escalation. Add: persona assignment, RAG-augmented worker context, Beads-backed track state, Hot Reload integration with worker spawning.
- [ ] **Step 4.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 4.2: Read the current guide and source code**
Use `manual-slop_get_file_summary` for the guide, then `manual-slop_py_get_skeleton` for each source file.
- [ ] **Step 4.3: Audit changes since last update**
Use `manual-slop_get_git_diff` for each source file. Note: the MMA system has evolved substantially (personas, RAG, Beads, Hot Reload interaction).
- [ ] **Step 4.4: Identify stale / missing sections**
Pay particular attention to:
- **Persona integration:** How are personas assigned to MMA tiers? Where in the worker lifecycle?
- **RAG augmentation:** When does a worker receive RAG-retrieved context? How is the chunk selection made?
- **Beads integration:** Are tracks stored in Beads when Beads mode is active? How does the transition work?
- **Hot Reload interaction:** When a Tier 3 worker finishes and the GUI is reloaded, what happens to the worker's session?
- [ ] **Step 4.5: Rewrite stale and missing sections**
VEFontCache-Odin style. The DAG engine algorithms in particular should be explained step-by-step (Kahn's algorithm pseudocode, cycle detection using iterative DFS, cascade_blocks with transitive propagation).
- [ ] **Step 4.6: Validate all cross-doc links**
- [ ] **Step 4.7: Spot-check 3 random symbols against the source**
- [ ] **Step 4.8: Commit**
```powershell
git -C C:\projects\manual_slop add docs/guide_mma.md
git -C C:\projects\manual_slop commit -m "docs(mma): refresh for personas, RAG augmentation, Beads integration, Hot Reload interaction"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Rewrote [sections]. Cross-referenced [files]. New sections: [list]." $_ }
```
---
## Task 5: Rewrite `docs/guide_tools.md`
**Files:**
- Modify: `docs/guide_tools.md` (current size ~22.9K)
- Reference: `src/mcp_client.py`, `src/api_hooks.py`, `src/api_hook_client.py`, `src/shell_runner.py`
**Scope:** All 26+ MCP tools (current count), the 3-layer security model (Allowlist Construction → Path Validation → Resolution Gate), the Hook API GET/POST endpoints, `ApiHookClient` method reference, the `/api/ask` synchronous HITL protocol, the shell runner. Add: any new tools added since Feb 2026 (RAG tools, Beads tools, tree-sitter Lua/GDScript/C# tools if implemented, persona tools, structural file editor tools).
- [ ] **Step 5.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 5.2: Read the current guide**
- [ ] **Step 5.3: Audit current tool inventory in `src/mcp_client.py`**
Use `manual-slop_py_get_skeleton` and `manual-slop_py_get_code_outline` to get the current list of registered tools. Count them. Compare to the count in the guide.
- [ ] **Step 5.4: For each tool, document or update its entry**
For every tool:
- **Signature:** Exact function signature
- **Security constraints:** Which of the 3 layers it passes through
- **Side effects:** What does it mutate? What does it return?
- **Edge cases:** Empty input, missing file, permission denied, etc.
- **Usage example:** At least one example showing the call site and expected return
If a tool is missing from the guide, add a full entry. If an entry is stale, rewrite it.
- [ ] **Step 5.5: Update the Hook API endpoint reference**
Use `manual-slop_py_get_skeleton` on `src/api_hooks.py` to get the current list of routes. For each route, document request/response format.
- [ ] **Step 5.6: Update the `ApiHookClient` method reference**
Use `manual-slop_py_get_class_summary` for the `ApiHookClient` class. Document each public method.
- [ ] **Step 5.7: Validate all cross-doc links**
- [ ] **Step 5.8: Spot-check 3 random symbols against the source**
- [ ] **Step 5.9: Commit**
```powershell
git -C C:\projects\manual_slop add docs/guide_tools.md
git -C C:\projects\manual_slop commit -m "docs(tools): refresh tool inventory, Hook API, and ApiHookClient reference"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Updated tool count to [N]. Rewrote [sections]. New tool entries: [list]." $_ }
```
---
## Task 6: Rewrite `docs/guide_simulations.md`
**Files:**
- Modify: `docs/guide_simulations.md`
- Reference: `tests/conftest.py`, `simulation/sim_base.py`, `simulation/workflow_sim.py`, `simulation/user_agent.py`, `tests/mock_gemini_cli.py`, `src/api_hook_client.py`
**Scope:** The `live_gui` pytest fixture (lifecycle, readiness polling, failure path, teardown, session isolation via `reset_ai_client`), the Puppeteer pattern (8-stage MMA simulation), the mock provider strategy, the VerificationLogger, process cleanup (`kill_process_tree`), visual verification patterns. Add: any new test infrastructure added since Feb 2026 (extended simulations, async tool tests, discussion metrics tests, structural file editor tests, command palette tests).
- [ ] **Step 6.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 6.2: Read the current guide**
- [ ] **Step 6.3: Audit current simulation infrastructure**
Use `manual-slop_get_file_summary` on `tests/conftest.py` and each `simulation/*.py` file. Note any new fixtures, base classes, or patterns.
- [ ] **Step 6.4: Identify stale / missing sections**
- [ ] **Step 6.5: Rewrite stale and missing sections**
VEFontCache-Odin style. Particular attention to:
- The `live_gui` fixture's full lifecycle (startup → readiness polling → test execution → teardown)
- The mock provider's JSON-L protocol (input mechanisms, response routing, output protocol)
- Process cleanup for Windows (the `kill_process_tree` implementation)
- The new extended simulation patterns (if any)
- [ ] **Step 6.6: Validate all cross-doc links**
- [ ] **Step 6.7: Spot-check 3 random symbols against the source**
- [ ] **Step 6.8: Commit**
```powershell
git -C C:\projects\manual_slop add docs/guide_simulations.md
git -C C:\projects\manual_slop commit -m "docs(simulations): refresh test infrastructure, mock provider, and Puppeteer pattern"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Rewrote [sections]. New content: [list]." $_ }
```
---
## Task 7: Refresh `docs/guide_context_curation.md` and `docs/guide_shaders_and_window.md`
**Files:**
- Modify: `docs/guide_context_curation.md` (current size ~8K)
- Modify: `docs/guide_shaders_and_window.md` (current size ~3.4K)
- Reference: `src/file_cache.py`, `src/summarize.py`, `src/outline_tool.py`, `src/summary_cache.py`, `src/fuzzy_anchor.py`, `src/shaders.py`, `src/bg_shader.py`, `src/imgui_scopes.py`
**Scope:** For `guide_context_curation.md`: skeleton/curated/targeted view algorithms, fuzzy anchor algorithm, summary cache strategy, file aggregation pipeline, view modes. For `guide_shaders_and_window.md`: shader pipeline, custom window frame manipulation, pywin32 integration, NERV theme FX.
- [ ] **Step 7.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 7.2: Read both guides**
- [ ] **Step 7.3: Audit changes in referenced source files**
- [ ] **Step 7.4: Rewrite stale sections in `guide_context_curation.md`**
- [ ] **Step 7.5: Commit `guide_context_curation.md`**
```powershell
git -C C:\projects\manual_slop add docs/guide_context_curation.md
git -C C:\projects\manual_slop commit -m "docs(context-curation): refresh view algorithms and fuzzy anchor"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Rewrote [sections]. Cross-referenced [files]." $_ }
```
- [ ] **Step 7.6: Rewrite stale sections in `guide_shaders_and_window.md`**
- [ ] **Step 7.7: Commit `guide_shaders_and_window.md`**
```powershell
git -C C:\projects\manual_slop add docs/guide_shaders_and_window.md
git -C C:\projects\manual_slop commit -m "docs(shaders): refresh shader pipeline and window frame"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Rewrote [sections]. Cross-referenced [files]." $_ }
```
- [ ] **Step 7.8: Validate all cross-doc links in both files**
---
## Task 8: Refresh `docs/guide_meta_boundary.md`
**Files:**
- Modify: `docs/guide_meta_boundary.md` (current size ~4.6K)
- Reference: `scripts/mma_exec.py`, `scripts/claude_mma_exec.py`, `scripts/tool_call.py`, `scripts/mcp_server.py`, `mma-orchestrator/SKILL.md`, `.gemini/`, `.claude/`, `.opencode/`
**Scope:** The Application domain (Strict HITL — `gui_2.py`, `ai_client.py`, `multi_agent_conductor.py`, `dag_engine.py`) vs the Meta-Tooling domain (agent skill files and orchestration scripts). Inter-Domain Bridges (`cli_tool_bridge.py`, `claude_tool_bridge.py`). The `GEMINI_CLI_HOOK_CONTEXT` environment variable. Safety model separation.
- [ ] **Step 8.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 8.2: Read the current guide and the meta-tooling files**
- [ ] **Step 8.3: Rewrite stale sections**
Pay attention to:
- Is `claude_mma_exec.py` still in active use? (User indicated Claude is no longer the primary toolchain — may be vestigial.)
- Is `cli_tool_bridge.py` still in active use?
- Are there new meta-tooling scripts not yet documented?
- [ ] **Step 8.4: Validate all cross-doc links**
- [ ] **Step 8.5: Commit**
```powershell
git -C C:\projects\manual_slop add docs/guide_meta_boundary.md
git -C C:\projects\manual_slop commit -m "docs(meta-boundary): refresh Application vs Meta-Tooling domain separation"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Rewrote [sections]. Cross-referenced [files]." $_ }
```
---
## Task 9: Update `Readme.md` (top-level)
**Files:**
- Modify: `Readme.md` (current size ~15.5K)
- Reference: every `docs/guide_*.md` (for the new Subsystem Index)
**Scope:** Add a "Module by Domain" reference table (links to each guide), a "Subsystem Index" (cross-references each major feature to its dedicated guide), update setup prerequisites (Beads CLI, Dolt, ChromaDB, etc.), update the Tech Stack section to reflect current dependencies.
- [ ] **Step 9.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 9.2: Read the current `Readme.md`**
- [ ] **Step 9.3: Add a "Module by Domain" section**
Insert a table that links to each `docs/guide_*.md`:
```markdown
## Module by Domain
For deep dives into each subsystem, see the dedicated guide:
| Subsystem | Guide |
|---|---|
| Threading, events, AI client, HITL | [docs/guide_architecture.md](docs/guide_architecture.md) |
| 4-Tier MMA, DAG, worker lifecycle | [docs/guide_mma.md](docs/guide_mma.md) |
| MCP tools, Hook API, shell runner | [docs/guide_tools.md](docs/guide_tools.md) |
| Test infrastructure, mock provider, Puppeteer | [docs/guide_simulations.md](docs/guide_simulations.md) |
| Skeleton/curated/targeted views, fuzzy anchors | [docs/guide_context_curation.md](docs/guide_context_curation.md) |
| Shader pipeline, custom window frame | [docs/guide_shaders_and_window.md](docs/guide_shaders_and_window.md) |
| Application vs Meta-Tooling domain | [docs/guide_meta_boundary.md](docs/guide_meta_boundary.md) |
| [RAG, Beads, etc. once new guides exist] | [docs/guide_rag.md](docs/guide_rag.md) |
```
- [ ] **Step 9.4: Add a "Subsystem Index" section**
A cross-reference of every major feature to its dedicated guide. Use the gap analysis from Task 1 as the source.
- [ ] **Step 9.5: Update setup prerequisites**
Check the current `pyproject.toml` (use `manual-slop_get_file_summary`) and confirm the README's prerequisites are accurate. Add any missing dependencies (e.g., `chromadb` for RAG, `dolt` for Beads).
- [ ] **Step 9.6: Update the Tech Stack section**
Add providers (MiniMax if not already there), new SDKs (any new ones), new tooling (e.g., ChromaDB).
- [ ] **Step 9.7: Commit**
```powershell
git -C C:\projects\manual_slop add Readme.md
git -C C:\projects\manual_slop commit -m "docs(readme): add Module by Domain, Subsystem Index, and updated setup"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Added Module by Domain table. Added Subsystem Index. Updated prerequisites to include [list]. Updated Tech Stack." $_ }
```
---
## Task 10: Write New Guides for Unlisted Subsystems
**Files:**
- Create (one per new guide): `docs/guide_<name>.md`
- Reference: the relevant `src/<module>.py` and tests
**Scope:** Per the gap analysis from Task 1, write new guides for any subsystem that:
1. Is explicitly listed in `conductor/product.md`
2. Does NOT have a corresponding guide yet
3. Is substantial enough to warrant a dedicated guide (>500 lines of code in the primary module)
**Likely candidates** (confirm via gap analysis):
- RAG (`docs/guide_rag.md`) — `src/rag_engine.py`
- Beads (`docs/guide_beads.md`) — `src/beads_client.py`
- Hot Reload (`docs/guide_hot_reload.md`) — `src/hot_reloader.py`
- Discussion Metrics & Compression (`docs/guide_discussion_metrics.md`) — `src/ai_client.py` (relevant section)
- Command Palette (`docs/guide_command_palette.md`) — `src/gui_2.py` (relevant section)
- Structural File Editor (`docs/guide_structural_editor.md`) — `src/gui_2.py` (relevant section)
- NERV Theme (`docs/guide_nerv_theme.md`) — `src/theme_nerv.py`, `src/theme_nerv_fx.py`
- [ ] **Step 10.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 10.2: For each new guide (one per subsystem), follow this per-subsystem workflow**
For each subsystem in the gap analysis's "Subsystems Without Dedicated Guides" table:
**10.2.1: Read the source module(s)**
- Use `manual-slop_py_get_skeleton` and `manual-slop_py_get_code_outline` first
- Read specific sections via `manual-slop_get_file_slice` for implementation details
**10.2.2: Read any existing tests for the subsystem**
- Use `manual-slop_get_file_summary` on the relevant `test_*.py` files
- Note what's tested (gives you the public API surface)
**10.2.3: Write the new guide in VEFontCache-Odin style**
- **Philosophy:** What problem does this subsystem solve? What design alternatives were considered?
- **Architectural Boundaries:** Which thread/domain owns it? What are the public interfaces? What cannot cross?
- **Implementation Logic:** State machines, algorithms step-by-step, public class/function signatures with side effects, threading constraints
- **Verification:** How is it tested? What edge cases? What can fail?
**10.2.4: Commit per-guide**
```powershell
git -C C:\projects\manual_slop add docs/guide_<name>.md
git -C C:\projects\manual_slop commit -m "docs(<name>): new guide for [subsystem]"
git -C C:\projects\manual_slop log -1 --format="%H" | ForEach-Object { git -C C:\projects\manual_slop notes add -m "New guide covering [subsystem]. Cross-referenced [files]. Public API surface documented." $_ }
```
**10.2.5: Update `docs/Readme.md` to link the new guide**
- Go back to Task 2's modified `docs/Readme.md` and update the placeholder row for this guide with the real "[contents summary]"
- Commit as a separate small commit: `docs(index): finalize [subsystem] guide link`
- [ ] **Step 10.3: Validate all cross-doc links across all new guides**
For every `[text](./relative/path)` in any new guide, verify the target exists.
---
## Task 11: Sub-Track 1 Verification
**Files:**
- Read: every modified `docs/guide_*.md` and `Readme.md`
- [ ] **Step 11.1: Cross-link audit**
For every modified markdown file, run a manual grep for `[text](./relative/path)` and verify each target exists via `manual-slop_search_files`.
- [ ] **Step 11.2: Symbol parity spot-check**
For 3 random public symbols mentioned in each rewritten guide, use `manual-slop_py_find_usages` to confirm the symbol exists with the same name in the source.
- [ ] **Step 11.3: Subsystem coverage check**
For every feature listed in `conductor/product.md`, confirm either (a) a dedicated guide exists, or (b) the feature is covered in a related guide with a link from `Readme.md`.
- [ ] **Step 11.4: Per-file commit verification**
Run `git log --oneline conductor/tracks/documentation_refresh_comprehensive_20260602/.. -- docs/ Readme.md` and confirm every file change has its own commit.
- [ ] **Step 11.5: Git note verification**
Run `git log --format="%H" -- docs/ Readme.md | ForEach-Object { git notes show $_ }` and confirm every commit has a non-empty git note.
- [ ] **Step 11.6: Phase completion checkpoint**
Per the project's Phase Completion Verification protocol in `conductor/workflow.md`:
- Run the automated test suite (or at least the docs-related tests, if any)
- Run the API hook verification if applicable
- Present the verification report to the user
- Get user sign-off
- Create the checkpoint commit: `conductor(checkpoint): Sub-Track 1 (docs layer refresh) complete`
- Attach the verification report as a git note
---
## Self-Review (after writing this plan)
1. **Spec coverage:** Sections 4.3-4.6 of the spec (Phase 1.3-1.6) are covered by Tasks 3-11. Phase 1.1 (Inventory) is Task 1. Phase 1.2 (Index update) is Task 2. Phase 1.5 (New guides) is Task 10. Phase 1.6 (Verification) is Task 11. ✓ All phases covered.
2. **Placeholder scan:** No "TBD" / "TODO" / "similar to Task N" markers. Specific file paths and commit commands throughout. ✓
3. **Type/symbol consistency:** Tasks reference consistent file paths, module names, and symbol names throughout. The VEFontCache-Odin pattern is applied consistently. ✓
4. **Risk check:** Pre-edit checkpoints, per-file commits, and git notes mitigate the codebase-evolving-underneath risk. Manual link validation mitigates broken links. User sign-off at the end of each sub-track is enforced via the Phase Completion Verification protocol. ✓
@@ -1,427 +0,0 @@
# Test Consolidation & TOML Sandboxing Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Audit tests for real-TOML usage, migrate offenders to sandboxed patterns, consolidate similar tests where it improves clarity, and enforce the rule going forward.
**Architecture:** `scripts/check_test_toml_paths.py` is the audit tool. `tests/conftest.py` gets a new autouse fixture `enforce_no_real_toml`. Migration follows the existing `isolate_workspace` pattern (already in conftest.py) using `tmp_path` + `monkeypatch`. Consolidation is judgment-call per area.
**Tech Stack:** Python 3.11+, pytest, regex (stdlib)
**Spec:** `docs/superpowers/specs/2026-06-02-test-consolidation-design.md`
---
## Execution Constraints
- **No subagents.** Execute as a single agent.
- **Pre-edit checkpoint:** `git add .` before any file edit.
- **Per-file atomic commits:** One file = one commit.
- **Commit message format:** `<type>(<scope>): <imperative description>`.
- **Git note format:** 3-8 line rationale per commit.
- **Style baseline:** 1-space indent, no comments, type hints.
---
## File Structure
| File | Action | Responsibility |
|---|---|---|
| `scripts/check_test_toml_paths.py` | Create | Greps for direct `./<name>.toml` references in tests; CI gate |
| `tests/conftest.py` | Modify | Add `enforce_no_real_toml` autouse fixture |
| `tests/test_enforce_no_real_toml.py` | Create | Tests for the enforcer itself |
| Various `tests/test_*.py` | Modify | Migrate offenders to sandboxed pattern |
---
## Task 1: Build the audit script
**Files:**
- Create: `scripts/check_test_toml_paths.py`
- [ ] **Step 1.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 1.2: Create the audit script**
```python
#!/usr/bin/env python3
# scripts/check_test_toml_paths.py
"""Detect tests that read/write real TOML files. Used as a CI gate.
Run from repo root: python scripts/check_test_toml_paths.py
Exits 0 if all tests use sandboxed paths, 1 otherwise.
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
TOML_BASENAMES = {
"manual_slop", "config", "credentials",
"presets", "personas", "tool_presets",
"workspace_profiles", "tool_presets",
}
PATTERNS = [
re.compile(rf'Path\(["\'](?:{"|".join(TOML_BASENAMES)})\.toml["\']'),
re.compile(rf'open\(["\'](?:{"|".join(TOML_BASENAMES)})\.toml["\']'),
re.compile(rf'["\']\.{{1,2}}/(?:{"|".join(TOML_BASENAMES)})\.toml["\']'),
re.compile(rf'Path\(["\']\.\./(?:{"|".join(TOML_BASENAMES)})\.toml["\']'),
]
EXCLUDE_DIRS = {"artifacts", "logs", "__pycache__", "snapshots"}
def find_violations(tests_dir: Path) -> list[tuple[Path, int, str]]:
violations = []
for test_file in tests_dir.rglob("test_*.py"):
if any(excluded in test_file.parts for excluded in EXCLUDE_DIRS):
continue
try:
content = test_file.read_text(encoding="utf-8")
except (OSError, UnicodeDecodeError):
continue
for lineno, line in enumerate(content.splitlines(), start=1):
for pattern in PATTERNS:
if pattern.search(line):
violations.append((test_file, lineno, line.strip()))
break
return violations
def main() -> int:
repo_root = Path(__file__).resolve().parent.parent
tests_dir = repo_root / "tests"
if not tests_dir.exists():
print(f"Tests dir not found: {tests_dir}", file=sys.stderr)
return 1
violations = find_violations(tests_dir)
if not violations:
print("OK: No tests reference real TOML files.")
return 0
print(f"FAIL: {len(violations)} test(s) reference real TOML files:")
for path, lineno, line in violations:
rel = path.relative_to(repo_root)
print(f" {rel}:{lineno}: {line}")
return 1
if __name__ == "__main__":
sys.exit(main())
```
- [ ] **Step 1.3: Run the script (expect failures)**
```powershell
python scripts/check_test_toml_paths.py
```
Expected: Outputs a list of violations (since the migration hasn't happened yet).
- [ ] **Step 1.4: Commit**
```bash
git -C C:\projects\manual_slop add scripts/check_test_toml_paths.py
git -C C:\projects\manual_slop commit -m "feat(tests): add check_test_toml_paths.py audit script"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Greps tests/ for direct ./<name>.toml references. Excludes artifacts, logs, __pycache__, snapshots. Exits 0 on clean, 1 on violations. Used as CI gate." $_ }
```
---
## Task 2: Add the enforcement fixture
**Files:**
- Modify: `tests/conftest.py`
- Create: `tests/test_enforce_no_real_toml.py`
- [ ] **Step 2.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 2.2: Read current `tests/conftest.py` to find insertion point**
Use `manual-slop_py_get_code_outline` to see the existing fixtures (especially `isolate_workspace` at line 71).
- [ ] **Step 2.3: Add the `enforce_no_real_toml` fixture**
Add near the existing `isolate_workspace` fixture (around line 70-90):
```python
# In tests/conftest.py, after isolate_workspace
@pytest.fixture(autouse=True)
def enforce_no_real_toml(request, tmp_path, monkeypatch):
"""Snapshot any real TOML files in cwd, remove them for the test, restore after.
This prevents tests from accidentally reading/writing the user's real config.
Tests must use tmp_path or monkeypatch to get their TOML data.
"""
from pathlib import Path as _P
real_toml_basenames = [
"manual_slop.toml", "config.toml", "credentials.toml",
"presets.toml", "personas.toml", "tool_presets.toml",
"workspace_profiles.toml",
]
snapshots: dict[_P, bytes] = {}
cwd = _P.cwd()
for name in real_toml_basenames:
p = cwd / name
if p.exists():
snapshots[p] = p.read_bytes()
p.unlink()
try:
yield
finally:
for p, content in snapshots.items():
p.write_bytes(content)
```
- [ ] **Step 2.4: Run the existing test suite to verify the fixture doesn't break things**
```powershell
uv run pytest tests/ -x --timeout=60 -q
```
Expected: Existing tests should still pass. If a test was relying on a real TOML being present, it'll fail and need migration (next task).
- [ ] **Step 2.5: Commit**
```bash
git -C C:\projects\manual_slop add tests/conftest.py
git -C C:\projects\manual_slop commit -m "test(infra): add enforce_no_real_toml autouse fixture"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Autouse fixture in tests/conftest.py. Snapshots any real TOML in cwd before test, removes it, restores after. Tests must use tmp_path or monkeypatch to access TOML data." $_ }
```
---
## Task 3: Tests for the enforcer
**Files:**
- Create: `tests/test_enforce_no_real_toml.py`
- [ ] **Step 3.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 3.2: Create the meta-test**
```python
# tests/test_enforce_no_real_toml.py
import os
from pathlib import Path
import subprocess
import sys
def test_check_script_exits_zero_on_clean_suite():
"""The audit script returns 0 when no violations exist."""
result = subprocess.run(
[sys.executable, "scripts/check_test_toml_paths.py"],
capture_output=True, text=True,
cwd=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
)
# If we're in a clean state, this should be 0.
# If there are still violations, this documents the current state.
assert result.returncode in (0, 1), f"Unexpected exit code: {result.returncode}"
def test_enforce_fixture_runs_without_error():
"""The fixture completes without raising."""
# If we get here, the fixture ran successfully.
assert True
def test_real_toml_restored_after_test(tmp_path, monkeypatch):
"""Verify the fixture restores a real TOML after the test."""
cwd = Path.cwd()
real_path = cwd / "_enforce_test_temp.toml"
if real_path.exists():
real_path.unlink()
real_path.write_bytes(b"[test]\nkey='value'")
try:
# During this test, the fixture removes _enforce_test_temp.toml
# (it's not in the real_toml_basenames list, so it stays)
# Test that real_path still exists (fixture didn't touch it)
assert real_path.exists()
finally:
if real_path.exists():
real_path.unlink()
```
- [ ] **Step 3.3: Run the meta-tests**
```powershell
uv run pytest tests/test_enforce_no_real_toml.py -v
```
Expected: 3 tests pass.
- [ ] **Step 3.4: Commit**
```bash
git -C C:\projects\manual_slop add tests/test_enforce_no_real_toml.py
git -C C:\projects\manual_slop commit -m "test(infra): add tests for enforce_no_real_toml fixture"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Meta-tests for the enforcer: script exit codes, fixture completes, real-file state not affected by unrelated tmp files." $_ }
```
---
## Task 4: Migrate test offenders
**Files:**
- Various `tests/test_*.py` (find via `check_test_toml_paths.py`)
- [ ] **Step 4.1: Generate the offender list**
```powershell
python scripts/check_test_toml_paths.py 2>&1 | Tee-Object -FilePath scripts/_offenders.txt
```
- [ ] **Step 4.2: For each offender, migrate**
Pick one violation at a time. For each:
1. Read the test file
2. Identify the line with the violation
3. Refactor to use `tmp_path` + `monkeypatch` (or the `isolate_workspace` fixture if it's already running)
4. Run that test in isolation to verify it still passes
5. Commit the change
**Example migration pattern:**
Before:
```python
def test_load_presets():
presets_path = Path("presets.toml")
data = tomllib.loads(presets_path.read_text())
assert "default" in data
```
After:
```python
def test_load_presets(tmp_path, monkeypatch):
from src import paths
presets_path = tmp_path / "presets.toml"
presets_path.write_text("[presets]\ndefault = {}\n")
monkeypatch.setattr(paths, "get_global_presets_path", lambda: presets_path)
data = tomllib.loads(presets_path.read_text())
assert "default" in data
```
- [ ] **Step 4.3: Re-run the audit script after each batch**
```powershell
python scripts/check_test_toml_paths.py
```
Goal: 0 violations.
- [ ] **Step 4.4: Commit per-file**
```bash
git -C C:\projects\manual_slop add tests/test_<migrated_file>.py
git -C C:\projects\manual_slop commit -m "test(<scope>): migrate <file> to sandboxed TOML pattern"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Migrated <N> tests in <file> from real TOML to tmp_path + monkeypatch. Verified with full test suite." $_ }
```
(Repeat for each file.)
---
## Task 5: Consolidate similar tests (judgment call)
**Files:**
- Various `tests/test_*.py`
- [ ] **Step 5.1: Identify consolidation candidates**
Run an analysis of test files:
```powershell
Get-ChildItem C:\projects\manual_slop\tests\test_*.py | Group-Object { $_.BaseName -replace '^test_', '' -replace '_.*$', '' } | Where-Object { $_.Count -gt 2 } | Select-Object Name, Count
```
Look for groups with > 2 files (e.g., `test_ai_settings_*.py`, `test_*_provider.py`).
- [ ] **Step 5.2: For each candidate, evaluate**
For each candidate group, ask:
- Are these tests testing the same thing?
- Would merging them make the failure messages clearer?
- Would merging them make it harder to find a specific test?
- Is the test count reduction a real win, or just a number?
If the answer is "merging improves clarity," proceed. Otherwise, leave alone.
- [ ] **Step 5.3: Consolidation pattern (if proceeding)**
Before (3 files):
```
tests/test_provider_gemini_init.py
tests/test_provider_anthropic_init.py
tests/test_provider_deepseek_init.py
```
After (1 file, parametrized):
```python
# tests/test_provider_init.py
import pytest
@pytest.mark.parametrize("provider_name", ["gemini", "anthropic", "deepseek"])
def test_provider_init(provider_name, tmp_path, monkeypatch):
# Common test logic, parametrized
...
```
- [ ] **Step 5.4: Commit per consolidation**
```bash
git -C C:\projects\manual_slop add tests/test_consolidated.py tests/test_removed_file1.py tests/test_removed_file2.py
git -C C:\projects\manual_slop commit -m "test(consolidate): merge <topic> tests into parametrized single file"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Consolidated N test files into 1 parametrized test. <Rationale for clarity gain>." $_ }
```
(Only if a consolidation was actually performed.)
---
## Task 6: Phase Completion Verification
- [ ] **Step 6.1: Final audit**
```powershell
python scripts/check_test_toml_paths.py
```
Expected: "OK: No tests reference real TOML files." Exit 0.
- [ ] **Step 6.2: Full test suite**
```powershell
uv run pytest tests/ -q --timeout=60
```
Expected: All tests pass.
- [ ] **Step 6.3: Create the checkpoint commit**
```bash
git -C C:\projects\manual_slop commit --allow-empty -m "conductor(checkpoint): Test consolidation & TOML sandboxing complete"
git -C C:\projects\manual_slop log -1 --format='%H' | ForEach-Object { git -C C:\projects\manual_slop notes add -m "Track complete. Audit script passes (0 violations). enforce_no_real_toml fixture in place. N tests migrated, M consolidations performed (or 0 if none were warranted)." $_ }
```
---
## Self-Review
- **Spec coverage:** All design phases have a task. ✓
- **Placeholder scan:** Migration example is concrete. ✓
- **Type consistency:** Fixture name `enforce_no_real_toml` used consistently. ✓
- **Consolidation is opt-in:** Task 5 explicitly says "if merging improves clarity" — not forced. ✓
@@ -1,985 +0,0 @@
# UI Polish Track — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Resolve five outstanding UI quality-of-life issues: GFM table rendering, Keep Pairs input width, Log Management refresh bug, Operations Hub vendor-state panel, and Files & Media directory tree grouping.
**Architecture:** Pure rendering-layer changes. No new infrastructure, no threading, no provider API changes. Each phase is a self-contained sub-track with its own Red/Green/Commit cycle.
**Tech Stack:** Python 3.11+, imgui-bundle (`imgui_md`, `imgui.begin_table`, `imgui.tree_node_ex`), pytest, the `live_gui` fixture from `tests/conftest.py`.
**Spec:** `docs/superpowers/specs/2026-06-03-ui-polish-design.md`
---
## File Structure
| File | Status | Responsibility |
|------|--------|----------------|
| `src/markdown_table.py` | **create** | Pure GFM table parser. No ImGui imports. |
| `src/markdown_helper.py` | modify | Insert table interceptor in `MarkdownRenderer.render()`. |
| `src/vendor_state.py` | **create** | Pure vendor-state aggregator. No ImGui imports. |
| `src/app_controller.py` | modify | Add `vendor_quota` field + `set_vendor_quota()` callback. |
| `src/ai_client.py` | modify | Wire `set_vendor_quota` into quota-bearing response paths. |
| `src/gui_2.py` | modify | Five surgical edits (one per phase). |
| `tests/test_markdown_table.py` | **create** | Parser unit tests. |
| `tests/test_markdown_table_render.py` | **create** | live_gui render tests. |
| `tests/test_vendor_state.py` | **create** | Aggregator unit tests. |
| `tests/test_vendor_state_render.py` | **create** | live_gui render tests. |
| `tests/test_log_management_refresh.py` | **create** | live_gui button-press test. |
| `tests/test_files_and_media_tree.py` | **create** | live_gui directory-grouping test. |
---
## Conventions
- **1-space indentation** for all Python (per `conductor/code_styleguides/python.md`).
- **No comments** in source files (per `AGENTS.md`).
- All public functions get strict type hints.
- New SDM tags `[C: ...]` and `[M: ...]` on every new public function/method.
- Run `uv run python scripts/check_imgui_scopes.py` after every `gui_2.py` edit.
---
# Phase 1 — Markdown Table Pre-Processor
### Task 1.1: GFM Table Parser — First Failing Test
**Files:**
- Create: `tests/test_markdown_table.py`
- Create: `src/markdown_table.py` (stub with `parse_tables() -> list[TableBlock]` returning `[]`)
- [ ] **Step 1: Stage current state**
```powershell
git add .
```
- [ ] **Step 2: Write the failing test**
```python
# tests/test_markdown_table.py
from src.markdown_table import parse_tables, TableBlock
def test_parses_simple_two_column_table():
text = (
"| Name | Type |\n"
"|-------|------|\n"
"| foo | int |\n"
"| bar | str |\n"
)
blocks = parse_tables(text)
assert len(blocks) == 1
block = blocks[0]
assert block.headers == ["Name", "Type"]
assert block.rows == [["foo", "int"], ["bar", "str"]]
def test_ignores_tables_inside_code_fence():
text = (
"```\n"
"| not | a table |\n"
"| --- | ------- |\n"
"| x | y |\n"
"```\n"
)
assert parse_tables(text) == []
def test_returns_empty_for_plain_markdown():
text = "# Heading\n\nSome **bold** text.\n"
assert parse_tables(text) == []
```
- [ ] **Step 3: Run test to verify it fails**
Run: `uv run pytest tests/test_markdown_table.py -v`
Expected: FAIL with `ImportError` or `AttributeError: module 'src.markdown_table' has no attribute 'parse_tables'`.
- [ ] **Step 4: Stub the module so the import succeeds**
```python
# src/markdown_table.py
from dataclasses import dataclass
@dataclass(frozen=True)
class TableBlock:
headers: list[str]
rows: list[list[str]]
span: tuple[int, int]
def parse_tables(text: str) -> list[TableBlock]:
return []
```
- [ ] **Step 5: Run test, confirm it still fails (logic not implemented)**
Run: `uv run pytest tests/test_markdown_table.py -v`
Expected: FAIL on `assert block.headers == ["Name", "Type"]`.
- [ ] **Step 6: Commit (Red)**
```powershell
git add tests/test_markdown_table.py src/markdown_table.py
git commit -m "test(markdown): add GFM table parser failing tests"
```
---
### Task 1.2: GFM Table Parser — Implementation
**Files:**
- Modify: `src/markdown_table.py`
- [ ] **Step 1: Implement parse_tables() to pass all tests**
```python
# src/markdown_table.py
import re
from dataclasses import dataclass
_TABLE_SEPARATOR = re.compile(r"^\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)+\|?\s*$")
@dataclass(frozen=True)
class TableBlock:
headers: list[str]
rows: list[list[str]]
span: tuple[int, int]
[C: src/markdown_helper.py:MarkdownRenderer.render]
def _split_row(line: str) -> list[str]:
line = line.strip()
if line.startswith("|"): line = line[1:]
if line.endswith("|"): line = line[:-1]
return [c.strip() for c in line.split("|")]
def _is_table_at(lines: list[str], i: int) -> bool:
if i + 1 >= len(lines): return False
if "|" not in lines[i]: return False
return bool(_TABLE_SEPARATOR.match(lines[i + 1]))
def parse_tables(text: str) -> list[TableBlock]:
lines = text.splitlines()
in_fence = False
blocks: list[TableBlock] = []
i = 0
while i < len(lines):
line = lines[i]
if line.strip().startswith("```"):
in_fence = not in_fence
i += 1
continue
if in_fence:
i += 1
continue
if _is_table_at(lines, i):
headers = _split_row(lines[i])
j = i + 2
rows: list[list[str]] = []
while j < len(lines) and "|" in lines[j] and not _TABLE_SEPARATOR.match(lines[j]):
rows.append(_split_row(lines[j]))
j += 1
blocks.append(TableBlock(headers=headers, rows=rows, span=(i, j)))
i = j
continue
i += 1
return blocks
```
- [ ] **Step 2: Run test, confirm it passes**
Run: `uv run pytest tests/test_markdown_table.py -v`
Expected: 3 passed.
- [ ] **Step 3: Commit (Green)**
```powershell
git add src/markdown_table.py
git commit -m "feat(markdown): implement GFM table parser"
```
---
### Task 1.3: Table Renderer — live_gui Test
**Files:**
- Create: `tests/test_markdown_table_render.py`
- Modify: `src/markdown_table.py` (add `render_table(block: TableBlock) -> None`)
- [ ] **Step 1: Write the failing test**
```python
# tests/test_markdown_table_render.py
from imgui_bundle import imgui
from src.markdown_table import parse_tables, render_table
def test_render_table_creates_imGui_table(live_gui):
app = live_gui
text = "| A | B |\n|---|---|\n| 1 | 2 |\n"
blocks = parse_tables(text)
assert len(blocks) == 1
imgui.begin("test_window")
try:
render_table(blocks[0])
finally:
imgui.end()
assert imgui.get_io().font_default is not None
```
- [ ] **Step 2: Run test, confirm it fails (no render_table function)**
Run: `uv run pytest tests/test_markdown_table_render.py -v`
Expected: FAIL with `ImportError: cannot import name 'render_table'`.
- [ ] **Step 3: Add the render_table function stub to src/markdown_table.py**
```python
# append to src/markdown_table.py
from imgui_bundle import imgui
def render_table(block: TableBlock) -> None:
pass
```
- [ ] **Step 4: Run test, confirm it passes (trivial stub)**
Run: `uv run pytest tests/test_markdown_table_render.py -v`
Expected: PASS.
- [ ] **Step 5: Commit (Red-Green trivial)**
```powershell
git add tests/test_markdown_table_render.py src/markdown_table.py
git commit -m "test(markdown): scaffold render_table test with trivial impl"
```
---
### Task 1.4: Table Renderer — Real Implementation
**Files:**
- Modify: `src/markdown_table.py` — replace the trivial `render_table` with a real implementation.
- [ ] **Step 1: Replace render_table with a real implementation**
```python
def render_table(block: TableBlock) -> None:
n_cols = len(block.headers)
if n_cols == 0: return
flags = imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable
if not imgui.begin_table("md_table", n_cols, flags):
return
imgui.table_headers_row()
for h in block.headers:
imgui.table_next_column()
imgui.text(h)
for row in block.rows:
imgui.table_next_row()
for c in row:
imgui.table_next_column()
imgui.text(c)
imgui.end_table()
[C: src/markdown_helper.py:MarkdownRenderer.render]
```
- [ ] **Step 2: Run all markdown_table tests**
Run: `uv run pytest tests/test_markdown_table.py tests/test_markdown_table_render.py -v`
Expected: 4 passed.
- [ ] **Step 3: Run regression on existing markdown tests**
Run: `uv run pytest tests/test_markdown_helper.py -v` (or whatever the actual file is — check with `search_files`)
Expected: All existing tests still pass.
- [ ] **Step 4: Commit (Green)**
```powershell
git add src/markdown_table.py
git commit -m "feat(markdown): implement table rendering with imgui.begin_table"
```
---
### Task 1.5: Interceptor in MarkdownRenderer
**Files:**
- Modify: `src/markdown_helper.py``MarkdownRenderer.render` method.
- [ ] **Step 1: Write a failing test in tests/test_markdown_table_render.py**
Append to the file:
```python
def test_renderer_intercepts_table_blocks(live_gui):
from src.markdown_helper import MarkdownRenderer
text = "# Title\n\n| A | B |\n|---|---|\n| 1 | 2 |\n\nEnd.\n"
imgui.begin("test_window_2")
try:
MarkdownRenderer().render(text, context_id="test")
finally:
imgui.end()
```
- [ ] **Step 2: Run test, confirm it passes (interceptor not yet wired)**
Run: `uv run pytest tests/test_markdown_table_render.py -v`
Expected: PASS (interceptor absent is currently the green state).
This is a *characterization* test, not a Red-Green test. We proceed to Step 3 knowing the new test will pass either way; the value of the test is that the failure mode is now defined.
- [ ] **Step 3: Modify MarkdownRenderer.render to call parse_tables and substitute placeholders**
```python
# src/markdown_helper.py — replace the render method body
def render(self, text: str, context_id: str = "default") -> None:
if not text: return
from src.markdown_table import parse_tables, render_table
blocks = parse_tables(text)
sentinel = "\x00TBL{}\x00"
masked = text
for idx, block in enumerate(blocks):
start, end = block.span
original_block = "\n".join(masked.splitlines()[start:end])
masked = masked.replace(original_block, sentinel.format(idx), 1)
parts = re.split(r"(```[\s\S]*?```)", masked)
table_iter = iter(blocks)
block_idx = 0
for part in parts:
if part.startswith("```") and part.endswith("```"):
self._render_code_block(part, context_id, block_idx)
block_idx += 1
elif part.strip():
sub_parts = re.split(r"(\x00TBL\d+\x00)", part)
for sp in sub_parts:
if sp.startswith("\x00TBL") and sp.endswith("\x00"):
idx = int(sp[4:-1])
try: render_table(blocks[idx])
except Exception: imgui.text(sp)
else:
if sp.strip(): imgui_md.render(sp)
[C: src/theme_2.py:render_post_fx]
```
- [ ] **Step 4: Run the full markdown test suite**
Run: `uv run pytest tests/test_markdown_table.py tests/test_markdown_table_render.py tests/test_markdown_helper.py -v`
Expected: All pass.
- [ ] **Step 5: Run imgui scope linter**
Run: `uv run python scripts/check_imgui_scopes.py src/markdown_helper.py`
Expected: No errors.
- [ ] **Step 6: Commit**
```powershell
git add src/markdown_helper.py
git commit -m "feat(markdown): intercept GFM tables and render via imgui.begin_table"
```
- [ ] **Step 7: Attach git note**
```powershell
git notes add -m "Phase 1 Task 1.5: wired MarkdownRenderer.render to parse_tables + render_table. Placeholder scheme avoids nested imgui_md quirks. Regressions: 0." HEAD
```
- [ ] **Step 8: Phase 1 checkpoint**
```powershell
git commit --allow-empty -m "conductor(checkpoint): Phase 1 complete"
$env:PHASE1_SHA = (git log -1 --format="%H")
```
---
# Phase 2 — Truncate / Keep Pairs Input Width
### Task 2.1: Test the width fix
**Files:**
- Create: `tests/test_discussion_truncate_layout.py`
- Modify: `src/gui_2.py:3829`
- [ ] **Step 1: Write a layout assertion test**
```python
# tests/test_discussion_truncate_layout.py
from imgui_bundle import imgui
def test_truncate_keep_pairs_field_has_adequate_width(live_gui):
app = live_gui
imgui.begin("test_disc_truncate")
try:
imgui.text("Keep Pairs:"); imgui.same_line()
imgui.set_next_item_width(80)
_, _ = imgui.input_int("##trunc_pairs", 2, 1)
finally:
imgui.end()
# The production code at gui_2.py:3829 must use width >= 140
import inspect, src.gui_2
src_text = inspect.getsource(src.gui_2)
assert 'set_next_item_width(80)' not in src_text.split('def render_discussion_metadata')[0]
```
- [ ] **Step 2: Run test, confirm it fails**
Run: `uv run pytest tests/test_discussion_truncate_layout.py -v`
Expected: FAIL because `set_next_item_width(80)` is currently in the source.
- [ ] **Step 3: Modify `src/gui_2.py:3829` to use width 140 and switch to drag_int**
Replace:
```python
imgui.text("Keep Pairs:"); imgui.same_line(); imgui.set_next_item_width(80)
ch, app.ui_disc_truncate_pairs = imgui.input_int("##trunc_pairs", app.ui_disc_truncate_pairs, 1)
```
With:
```python
imgui.text("Keep Pairs:"); imgui.same_line(); imgui.set_next_item_width(140)
ch, app.ui_disc_truncate_pairs = imgui.drag_int("##trunc_pairs", app.ui_disc_truncate_pairs, 1, 1, 999)
if app.ui_disc_truncate_pairs < 1: app.ui_disc_truncate_pairs = 1
```
- [ ] **Step 4: Run test, confirm it passes**
Run: `uv run pytest tests/test_discussion_truncate_layout.py -v`
Expected: PASS.
- [ ] **Step 5: Run regression on discussion hub tests**
Run: `uv run pytest tests/test_gui_fast_render.py -k discussion -v`
Expected: All pass.
- [ ] **Step 6: Commit**
```powershell
git add src/gui_2.py tests/test_discussion_truncate_layout.py
git commit -m "fix(gui): widen Keep Pairs input and switch to drag_int for clearer +/-"
```
- [ ] **Step 7: Phase 2 checkpoint**
```powershell
git commit --allow-empty -m "conductor(checkpoint): Phase 2 complete"
```
---
# Phase 3 — Log Management `Refresh Registry` Bug
### Task 3.1: Failing test for refresh
**Files:**
- Create: `tests/test_log_management_refresh.py`
- Modify: `src/gui_2.py:1675`
- [ ] **Step 1: Write the failing test**
```python
# tests/test_log_management_refresh.py
import os, tempfile, toml
from pathlib import Path
from src import log_registry, paths
def test_refresh_registry_reloads_from_disk(live_gui):
app = live_gui
with tempfile.TemporaryDirectory() as tmp:
reg_path = Path(tmp) / "log_registry.toml"
# First version
toml.dump({"s1": {"start_time": "2026-01-01"}}, reg_path.open("w"))
reg = log_registry.LogRegistry(str(reg_path))
reg.load_registry()
app._log_registry = reg
# Mutate the file out-of-band
toml.dump({"s1": {"start_time": "2026-01-01"}, "s2": {"start_time": "2026-01-02"}}, reg_path.open("w"))
# Simulate the button handler
from src.gui_2 import render_log_management
# Render the panel once
import imgui_bundle
imgui_bundle.imgui.begin("Log Management")
try:
render_log_management(app)
finally:
imgui_bundle.imgui.end()
# The current code does NOT call load_registry, so s2 is not present.
# Our fix must add the call. This assertion is what we want to be True.
app._log_registry.load_registry()
assert "s2" in app._log_registry.data
```
- [ ] **Step 2: Run test, confirm it passes (handler is irrelevant, we manually call load_registry at the end)**
Run: `uv run pytest tests/test_log_management_refresh.py -v`
Expected: PASS even before the fix — this test is characterizing the registry's reload, not the button. We need a different approach.
- [ ] **Step 3: Replace test with a focused source-assertion test**
Replace the file content with:
```python
# tests/test_log_management_refresh.py
import inspect
from src import gui_2
def test_refresh_registry_button_calls_load_registry():
src = inspect.getsource(gui_2)
# The button handler MUST call .load_registry() — either in-place or after re-instantiation
snippet = src[src.find("Refresh Registry"):src.find("Refresh Registry") + 400]
assert "load_registry" in snippet, (
"Refresh Registry button must invoke load_registry(); "
"currently it only re-instantiates LogRegistry which leaves .data empty."
)
```
- [ ] **Step 4: Run test, confirm it fails**
Run: `uv run pytest tests/test_log_management_refresh.py -v`
Expected: FAIL with the assertion message.
- [ ] **Step 5: Fix `src/gui_2.py:1675`**
Replace:
```python
if imgui.button("Refresh Registry"): app._log_registry = log_registry.LogRegistry(str(paths.get_logs_dir() / "log_registry.toml"))
```
With:
```python
if imgui.button("Refresh Registry"):
if app._log_registry is not None: app._log_registry.load_registry()
```
- [ ] **Step 6: Run test, confirm it passes**
Run: `uv run pytest tests/test_log_management_refresh.py -v`
Expected: PASS.
- [ ] **Step 7: Run regression on log management tests**
Run: `uv run pytest tests/test_log_management_ui.py tests/test_log_pruner.py -v`
Expected: All pass.
- [ ] **Step 8: Commit**
```powershell
git add src/gui_2.py tests/test_log_management_refresh.py
git commit -m "fix(log): Refresh Registry button now calls load_registry() on the live instance"
```
- [ ] **Step 9: Phase 3 checkpoint**
```powershell
git commit --allow-empty -m "conductor(checkpoint): Phase 3 complete"
```
---
# Phase 4 — Operations Hub > Vendor State Panel
### Task 4.1: Vendor State Aggregator — Tests + Stub
**Files:**
- Create: `tests/test_vendor_state.py`
- Create: `src/vendor_state.py` (stub)
- [ ] **Step 1: Stage and write the failing test**
```python
# tests/test_vendor_state.py
from src.vendor_state import get_vendor_state, VendorMetric
class _StubApp:
current_provider = "Anthropic"
current_model = "claude-opus-4"
controller = None # set in fixture
def test_get_vendor_state_returns_core_metrics():
class _C:
token_tracker = type("TT", (), {"used": 78234, "limit": 200000, "cache_hits": 1200, "cache_misses": 80})()
last_error = None
vendor_quota = {"remaining_pct": 87}
app = _StubApp()
app.controller = _C()
metrics = get_vendor_state(app)
keys = {m.key for m in metrics}
assert "provider_model" in keys
assert "context_window" in keys
assert "cache" in keys
assert "quota" in keys
assert "last_error" in keys
def test_missing_data_renders_em_dash_not_crash():
class _C:
token_tracker = None
last_error = None
vendor_quota = {}
app = _StubApp()
app.controller = _C()
metrics = get_vendor_state(app)
for m in metrics:
assert m.value is not None
```
- [ ] **Step 2: Stub `src/vendor_state.py`**
```python
# src/vendor_state.py
from dataclasses import dataclass
@dataclass(frozen=True)
class VendorMetric:
key: str
label: str
value: str
state: str
tooltip: str
[C: src/gui_2.py:render_vendor_state]
def get_vendor_state(app) -> list[VendorMetric]:
return []
```
- [ ] **Step 3: Run test, confirm it fails**
Run: `uv run pytest tests/test_vendor_state.py -v`
Expected: FAIL with `assert "provider_model" in keys` (empty list).
- [ ] **Step 4: Implement get_vendor_state**
```python
def get_vendor_state(app) -> list[VendorMetric]:
out: list[VendorMetric] = []
out.append(VendorMetric(
key="provider_model",
label="Provider / Model",
value=f"{app.current_provider} / {app.current_model}",
state="info",
tooltip="The vendor and model that will handle the next request."
))
ctrl = getattr(app, "controller", None)
tt = getattr(ctrl, "token_tracker", None) if ctrl else None
if tt and getattr(tt, "limit", 0):
pct = 100.0 * getattr(tt, "used", 0) / tt.limit
state = "warn" if pct > 75 else "ok"
out.append(VendorMetric(
key="context_window",
label="Context Window",
value=f"{tt.used:,} / {tt.limit:,} ({pct:.0f}%)",
state=state,
tooltip="Used vs total context window for the current session."
))
else:
out.append(VendorMetric(
key="context_window", label="Context Window", value="", state="info",
tooltip="No token tracker attached for the current provider."
))
if tt:
hits = getattr(tt, "cache_hits", 0)
miss = getattr(tt, "cache_misses", 0)
total = hits + miss
rate = (100.0 * hits / total) if total else 0.0
out.append(VendorMetric(
key="cache", label="Cache Hit Rate",
value=f"{rate:.0f}% ({hits:,}/{total:,})",
state="ok" if rate > 50 else "info",
tooltip="Server-side prompt cache hit rate for the current session."
))
quota = (getattr(ctrl, "vendor_quota", {}) or {}) if ctrl else {}
pct_left = quota.get("remaining_pct")
if pct_left is None:
out.append(VendorMetric(
key="quota", label="Vendor Quota", value="", state="info",
tooltip="Vendor did not report quota for the current billing period."
))
else:
out.append(VendorMetric(
key="quota", label="Vendor Quota",
value=f"{pct_left}% remaining",
state="ok" if pct_left > 25 else "warn",
tooltip="Approximate quota remaining for the current billing period."
))
err = getattr(ctrl, "last_error", None) if ctrl else None
out.append(VendorMetric(
key="last_error", label="Last Error",
value=err.get("class", "none") if err else "none",
state="error" if err else "ok",
tooltip=err.get("message", "No error since session start.") if err else "No error since session start."
))
return out
```
- [ ] **Step 5: Run test, confirm it passes**
Run: `uv run pytest tests/test_vendor_state.py -v`
Expected: 2 passed.
- [ ] **Step 6: Commit**
```powershell
git add src/vendor_state.py tests/test_vendor_state.py
git commit -m "feat(vendor-state): add pure aggregator with stable metric keys"
```
---
### Task 4.2: Add vendor_quota to AppController
**Files:**
- Modify: `src/app_controller.py` — find a dataclass-style state block (search for `@dataclass` near `class AppController`)
- [ ] **Step 1: Find the AppController state block**
Run: `py_get_definition src/app_controller.py:AppController.__init__` (or `search_files` for `vendor_quota` in app_controller.py)
Expected: The state is in `__init__` of `AppController` (probably around line 100-200).
- [ ] **Step 2: Add the field and method**
Add to `AppController.__init__`:
```python
self.vendor_quota: dict[str, Any] = {}
self.last_error: dict[str, str] | None = None
```
Add a new method on `AppController`:
```python
def set_vendor_quota(self, provider: str, remaining_pct: float) -> None:
with self._state_lock:
self.vendor_quota = {"provider": provider, "remaining_pct": remaining_pct}
[C: src/ai_client.py:_*_request]
```
- [ ] **Step 3: Wire it into ai_client.py quota paths**
For each provider that returns quota-bearing responses (Anthropic, Gemini, DeepSeek, MiniMax), after a successful response, call:
```python
if hasattr(app, "controller") and app.controller is not None:
app.controller.set_vendor_quota(provider, remaining_pct)
```
The exact wire-up point depends on the existing response handler. Read the file with `py_get_skeleton` and patch at the call sites that currently write the comms log entry for a successful response.
- [ ] **Step 4: Run vendor_state tests + ai_client tests**
Run: `uv run pytest tests/test_vendor_state.py tests/test_ai_client.py -v`
Expected: All pass.
- [ ] **Step 5: Commit**
```powershell
git add src/app_controller.py src/ai_client.py
git commit -m "feat(vendor-state): add vendor_quota state and provider wire-up"
```
---
### Task 4.3: Wire Vendor State into Operations Hub
**Files:**
- Modify: `src/gui_2.py` — add `render_vendor_state` function, add new tab in `render_operations_hub`.
- [ ] **Step 1: Add render_vendor_state function (module level in gui_2.py)**
```python
def render_vendor_state(app: App) -> None:
from src.vendor_state import get_vendor_state
metrics = get_vendor_state(app)
if imgui.begin_table("vendor_state", 3, imgui.TableFlags_.row_bg | imgui.TableFlags_.borders):
imgui.table_setup_column("Metric", imgui.TableColumnFlags_.width_fixed, 180)
imgui.table_setup_column("Value", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("State", imgui.TableColumnFlags_.width_fixed, 60)
imgui.table_headers_row()
state_colors = {"ok": vec4(120, 220, 120), "warn": vec4(240, 200, 80), "error": vec4(240, 80, 80), "info": vec4(180, 180, 180)}
for m in metrics:
imgui.table_next_row()
imgui.table_next_column(); imgui.text(m.label)
imgui.table_next_column(); imgui.text(m.value)
if imgui.is_item_hovered(): imgui.set_tooltip(m.tooltip)
imgui.table_next_column()
imgui.text_colored(state_colors.get(m.state, vec4(180, 180, 180)), m.state)
imgui.end_table()
[C: src/gui_2.py:render_operations_hub]
```
- [ ] **Step 2: Add the tab to render_operations_hub**
In `render_operations_hub` (around line 4023 in current `src/gui_2.py`), after the "Workspace Layouts" tab block, add:
```python
with imscope.tab_item("Vendor State") as (exp, _):
if exp: render_vendor_state(app)
```
- [ ] **Step 3: Add a live_gui render test**
```python
# tests/test_vendor_state_render.py
from src.gui_2 import render_vendor_state
def test_render_vendor_state_creates_table(live_gui):
app = live_gui
import imgui_bundle
imgui_bundle.imgui.begin("test_vendor")
try:
render_vendor_state(app)
finally:
imgui_bundle.imgui.end()
```
- [ ] **Step 4: Run regression on Operations Hub tests**
Run: `uv run pytest tests/test_gui_fast_render.py -k operations -v`
Expected: All pass.
- [ ] **Step 5: Run imgui scope linter**
Run: `uv run python scripts/check_imgui_scopes.py src/gui_2.py`
Expected: No errors.
- [ ] **Step 6: Commit**
```powershell
git add src/gui_2.py tests/test_vendor_state_render.py
git commit -m "feat(ops-hub): add Vendor State tab with quota + context + cache"
```
- [ ] **Step 7: Phase 4 checkpoint**
```powershell
git commit --allow-empty -m "conductor(checkpoint): Phase 4 complete"
```
---
# Phase 5 — Files & Media > Files Directory Tree
### Task 5.1: Test + Refactor render_files_and_media
**Files:**
- Create: `tests/test_files_and_media_tree.py`
- Modify: `src/gui_2.py:2689-2750`
- [ ] **Step 1: Write the failing test**
```python
# tests/test_files_and_media_tree.py
import os, tempfile
from src import models
from src.gui_2 import render_files_and_media
def test_files_rendered_under_directory_grouping(live_gui):
app = live_gui
with tempfile.TemporaryDirectory() as tmp:
f1 = os.path.join(tmp, "a.py"); f2 = os.path.join(tmp, "b.py")
f3 = os.path.join(tmp, "sub", "c.py")
os.makedirs(os.path.dirname(f3), exist_ok=True)
for p in (f1, f2, f3): open(p, "w").close()
app.files = [models.FileItem(path=f1), models.FileItem(path=f2), models.FileItem(path=f3)]
import imgui_bundle
imgui_bundle.imgui.begin("Files & Media")
try:
render_files_and_media(app)
finally:
imgui_bundle.imgui.end()
# Assert no exception was raised. The directory grouping is structural.
assert len(app.files) == 3
```
- [ ] **Step 2: Run test, confirm it passes (current code is flat, not yet grouped)**
Run: `uv run pytest tests/test_files_and_media_tree.py -v`
Expected: PASS (the assertion is only about not crashing). The test is a regression guard for the refactor.
- [ ] **Step 3: Modify `src/gui_2.py:2689-2750` to wrap the inner loop in a directory group loop**
Replace the body of the `if imgui.collapsing_header("Files", ...)` block (lines ~2691-2750 in current `src/gui_2.py`) with:
```python
if imgui.collapsing_header("Files", imgui.TreeNodeFlags_.default_open):
with imscope.group():
if imgui.begin_table("files_table", 3, imgui.TableFlags_.resizable | imgui.TableFlags_.borders | imgui.TableFlags_.row_bg):
imgui.table_setup_column("Act", imgui.TableColumnFlags_.width_fixed, 60)
imgui.table_setup_column("Path", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("Status", imgui.TableColumnFlags_.width_fixed, 70)
imgui.table_headers_row()
to_remove_idx = -1
app.files.sort(key=lambda f: f.path.lower() if hasattr(f, 'path') else str(f).lower())
from src import aggregate
grouped = aggregate.group_files_by_dir(app.files)
for dir_name, g_files in sorted(grouped.items()):
with imscope.tree_node_ex(f"{dir_name}##files_dir", imgui.TreeNodeFlags_.default_open) as is_open:
if is_open:
for f_item in g_files:
i = app.files.index(f_item)
imgui.table_next_row()
imgui.table_set_column_index(0)
fpath = f_item.path if hasattr(f_item, 'path') else str(f_item)
in_context = any((cf.path if hasattr(cf, 'path') else str(cf)) == fpath for cf in app.context_files)
is_cached = any(fpath in c for c in getattr(app, '_cached_files', []))
if imgui.button(f"+##add_f_{i}"):
if not in_context:
from src import models
new_item = models.FileItem(path=fpath)
app.context_files.append(new_item)
app._populate_auto_slices(new_item)
imgui.same_line()
if imgui.button(f"x##rem_f_{i}"):
to_remove_idx = i
imgui.table_set_column_index(1)
imgui.text(fpath)
if imgui.is_item_hovered(): imgui.set_tooltip(fpath)
imgui.table_set_column_index(2)
if in_context:
imgui.text_colored(imgui.ImVec4(0.3, 0.8, 0.3, 1), "Active")
elif is_cached:
imgui.text_colored(imgui.ImVec4(0.3, 0.8, 1, 1), "Cached")
else:
imgui.text_disabled(" - ")
imgui.end_table()
if to_remove_idx != -1: app.files.pop(to_remove_idx)
imgui.dummy(imgui.ImVec2(0, 5))
if imgui.button("Add Files to Inventory"):
r = hide_tk_root(); paths = filedialog.askopenfilenames(); r.destroy()
for p in paths:
if p not in [f.path if hasattr(f, 'path') else f for f in app.files]: app.files.append(models.FileItem(path=p))
```
- [ ] **Step 4: Run all files-and-media tests**
Run: `uv run pytest tests/test_files_and_media_tree.py tests/test_gui_fast_render.py::test_render_files_and_media_fast -v`
Expected: All pass.
- [ ] **Step 5: Run imgui scope linter**
Run: `uv run python scripts/check_imgui_scopes.py src/gui_2.py`
Expected: No errors.
- [ ] **Step 6: Commit**
```powershell
git add src/gui_2.py tests/test_files_and_media_tree.py
git commit -m "feat(files-media): group files by directory using collapsible tree nodes"
```
- [ ] **Step 7: Phase 5 final checkpoint**
```powershell
git commit --allow-empty -m "conductor(checkpoint): UI Polish track complete"
```
---
## Self-Review Checklist
- [x] All five user issues have at least one task.
- [x] No `TBD`/`TODO` placeholders.
- [x] Type signatures match across tasks (`VendorMetric.key`, `TableBlock.span`, etc.).
- [x] Indentation in code blocks is 1-space.
- [x] Every `gui_2.py` edit is followed by an imgui scope linter run.
- [x] No inter-phase dependencies — phases can be reordered.
## Execution Handoff
Plan complete and saved to `docs/superpowers/plans/2026-06-03-ui-polish.md`.
**Two execution options:**
1. **Subagent-Driven (recommended)** — dispatch a fresh subagent per task, review between tasks, fast iteration.
2. **Inline Execution** — execute tasks in this session with `executing-plans`, batch execution with checkpoints.
Awaiting your choice before proceeding to implementation.
File diff suppressed because it is too large Load Diff
@@ -1,387 +0,0 @@
# Live-GUI Fragility Fixes Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Fix 3 failing live_gui tests discovered in the 2026-06-05 batched test run (269/272 → 272/272) by repairing a regression in the defer-not-catch fix for `_capture_workspace_profile`, fixing a test mock for `imscope.window`, and adding a regression unit test.
**Architecture:** Surgical 1-line fix on the production code path (the str/bytes sentinel that violated the `WorkspaceProfile.ini_content: str` contract), a 2-line fix on the prior session test mock (add missing tuple-return for `imscope.window`), and a new unit test that encodes the str/bytes contract so future regressions are caught at unit-test speed.
**Tech Stack:** Python 3.11+, pytest 9.0, imgui-bundle (`imgui.save_ini_settings_to_memory()`), tomli_w, tomllib.
---
## File Structure
| File | Change | Purpose |
|---|---|---|
| `src/gui_2.py` | Modify lines 601-609 | Fix `ini = b""``ini = ""` in defer branch + `except` handler. Add `str()` defensive wrap. |
| `tests/test_prior_session_no_pop_imbalance.py` | Modify (add 2 lines) | Add `(True, True)` tuple-return mock for `imscope.window`. |
| `tests/test_workspace_profile_serialization.py` | Create | New unit test for the `ini_content: str` round-trip contract. |
| `conductor/tracks.md` | Modify (1 line, plan updates) | Register new track. |
| `docs/superpowers/specs/2026-06-05-live-gui-fragility-fixes-design.md` | (already written) | Spec for this work. |
No new files needed in `src/`. No production-code refactoring. No changes to the workspace profile save/load architecture.
---
## Task 1: Fix `_capture_workspace_profile` str/bytes sentinel
**Files:**
- Modify: `src/gui_2.py:601-609`
- Test: deferred to Task 3 (regression unit test)
- [ ] **Step 1.1: Pre-edit checkpoint**
```powershell
cd C:\projects\manual_slop; git add .
```
- [ ] **Step 1.2: Read the current state of `_capture_workspace_profile`**
Read `src/gui_2.py:601-609` to confirm the current code.
- [ ] **Step 1.3: Apply the fix**
Replace the current `_capture_workspace_profile` defer-and-except block (lines 601-609) with:
```python
def _capture_workspace_profile(self, name: str) -> models.WorkspaceProfile:
if not getattr(self, "_ini_capture_ready", False):
self._ini_capture_ready = True
ini = ""
else:
try:
ini = str(imgui.save_ini_settings_to_memory() or "")
except Exception:
ini = ""
panel_states = {
```
Use `manual-slop_py_update_definition` with the existing function name `_capture_workspace_profile` to do the surgical replacement. The body change is:
- Line 604: `ini = b""``ini = ""`
- Line 609: `ini = b""``ini = ""`
- Line 607: `ini = imgui.save_ini_settings_to_memory()``ini = str(imgui.save_ini_settings_to_memory() or "")`
Use exactly 1-space indentation.
- [ ] **Step 1.4: Verify the file still parses**
```powershell
cd C:\projects\manual_slop; uv run python -c "import ast; ast.parse(open('src/gui_2.py').read())"
```
Expected: no error.
- [ ] **Step 1.5: Run the workspace-profile-related tests to verify the fix**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_workspace_manager.py tests/test_workspace_profiles_sim.py tests/test_auto_switch_sim.py -v --timeout=60
```
Expected:
- `test_workspace_manager.py` passes (it tests the manager's save/load semantics with mocked profiles).
- `test_workspace_profiles_sim.py` passes (it uses `live_gui`).
- `test_auto_switch_sim.py` passes (it uses `live_gui`).
If `test_workspace_profiles_sim.py` or `test_auto_switch_sim.py` fails, it should be ONLY because of session-state pollution from a prior run. The fix targets the underlying bug; the test infrastructure (live_gui fixture) is what makes these flake. Re-run individually if needed:
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_workspace_profiles_sim.py::test_workspace_profiles_restoration -v --timeout=60
```
- [ ] **Step 1.6: Commit**
```powershell
cd C:\projects\manual_slop; git add src/gui_2.py
git -C C:\projects\manual_slop commit -m "fix(gui_2): use str sentinel not bytes in _capture_workspace_profile"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "WorkspaceProfile.ini_content is str (src/models.py:799) and tomli_w rejects bytes. The d7487af4 defer fix used ini=b'' which crashed TOML serialization, so save_workspace_profile raised TypeError, profile was never saved, and load_workspace_profile became a no-op. Changed both ini=b'' to ini='' and added str() defensive wrap on the non-defer path. Fixes test_auto_switch_sim and test_workspace_profiles_restoration." $h
```
---
## Task 2: Fix prior session test mock for `imscope.window`
**Files:**
- Modify: `tests/test_prior_session_no_pop_imbalance.py` (the mock setup loop ~line 75-78, where the test sets up imscope context managers)
- [ ] **Step 2.1: Pre-edit checkpoint**
```powershell
cd C:\projects\manual_slop; git add .
```
- [ ] **Step 2.2: Read the current mock setup**
Read `tests/test_prior_session_no_pop_imbalance.py:60-95` to see the `mock_imscope` setup.
- [ ] **Step 2.3: Apply the fix**
Find the loop that sets `__enter__` and `__exit__` for all imscope context managers (it looks like this around line 70-80):
```python
for sc in [mock_imscope.style_color, mock_imscope.style_var, mock_imscope.child, mock_imscope.tab_bar, mock_imscope.tab_item, mock_imscope.tree_node_ex, mock_imscope.group, mock_imscope.indent, mock_imscope.id, mock_imscope.text_wrap, mock_imscope.tooltip, mock_imscope.menu, mock_imscope.menu_bar, mock_imscope.popup, mock_imscope.popup_modal, mock_imscope.window, mock_imscope.table]:
sc.return_value.__enter__ = MagicMock(side_effect=_scope_enter)
sc.return_value.__exit__ = MagicMock(side_effect=_scope_exit)
```
Note: `mock_imscope.window` is in this list. The default `MagicMock(side_effect=_scope_enter)` returns a bare `MagicMock` (non-iterable), but production code at `src/gui_2.py:2333` does `with imscope.window(...) as (opened, visible):` which expects a 2-tuple.
After the loop (around line 91, after the `mock_imscope.popup_modal.return_value.__enter__ = MagicMock(return_value=(True, None))` line), add:
```python
mock_imscope.window.return_value.__enter__ = MagicMock(return_value=(True, True))
```
This matches the pattern already used for `popup_modal` (which returns `(True, None)`). The `__exit__` from the loop above is preserved (returns `False`, indicating no exception).
Use `manual-slop_edit_file` with the exact `old_string` from the popup_modal mock line and the `new_string` with the additional window mock line right after it.
Use exactly 1-space indentation.
- [ ] **Step 2.4: Run the prior session test**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_prior_session_no_pop_imbalance.py -v --timeout=30
```
Expected: PASS.
- [ ] **Step 2.5: Commit**
```powershell
cd C:\projects\manual_slop; git add tests/test_prior_session_no_pop_imbalance.py
git -C C:\projects\manual_slop commit -m "test(prior_session): mock imscope.window with tuple-return matching popup_modal"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "The test's mock setup loop for imscope context managers set __enter__ to a bare MagicMock (non-iterable), but render_preset_manager_window at src/gui_2.py:2333 does 'with imscope.window(...) as (opened, visible):' which expects a 2-tuple. popup_modal already had the right setup; window was missing it. Added the tuple-return for window, matching popup_modal's pattern." $h
```
---
## Task 3: Add regression unit test for `WorkspaceProfile` str/bytes contract
**Files:**
- Create: `tests/test_workspace_profile_serialization.py`
- [ ] **Step 3.1: Pre-edit checkpoint**
```powershell
cd C:\projects\manual_slop; git add .
```
- [ ] **Step 3.2: Write the test file**
Create `tests/test_workspace_profile_serialization.py`:
```python
import io
import tomllib
import pytest
import tomli_w
from src.models import WorkspaceProfile
def test_workspace_profile_empty_ini_content_roundtrips():
"""WorkspaceProfile with ini_content='' (empty str) must round-trip through TOML.
This is the str/bytes type contract that the defer-not-catch fix in d7487af4 violated
(it used ini=b'' which tomli_w rejects with TypeError).
"""
profile = WorkspaceProfile(
name="t",
ini_content="",
show_windows={"A": True, "B": False},
panel_states={"x": 1, "y": 2.0, "z": True},
)
d = profile.to_dict()
buf = io.BytesIO()
tomli_w.dump({"t": d}, buf)
buf.seek(0)
back = tomllib.load(buf)
loaded = WorkspaceProfile.from_dict("t", back["t"])
assert loaded.ini_content == ""
assert loaded.show_windows == {"A": True, "B": False}
assert loaded.panel_states == {"x": 1, "y": 2.0, "z": True}
def test_workspace_profile_with_actual_ini_content_roundtrips():
"""WorkspaceProfile with real ini content (str) must round-trip through TOML.
This mirrors how save_ini_settings_to_memory() returns a str at runtime.
"""
profile = WorkspaceProfile(
name="real",
ini_content="[Window][Debug]\nPos=10,20\n",
show_windows={},
panel_states={},
)
d = profile.to_dict()
buf = io.BytesIO()
tomli_w.dump({"real": d}, buf)
buf.seek(0)
back = tomllib.load(buf)
loaded = WorkspaceProfile.from_dict("real", back["real"])
assert loaded.ini_content == "[Window][Debug]\nPos=10,20\n"
assert loaded.name == "real"
assert loaded.show_windows == {}
assert loaded.panel_states == {}
def test_workspace_profile_bytes_ini_content_rejected_by_toml():
"""Regression guard: a bytes ini_content must raise TypeError from tomli_w.
This documents the type contract; if tomli_w ever gains bytes support, the
contract should be revisited (e.g. by switching WorkspaceProfile.ini_content
to bytes and updating the imgui.load_ini_settings_from_memory call site).
"""
profile = WorkspaceProfile(
name="bad",
ini_content=b"", # type: ignore[arg-type]
show_windows={},
panel_states={},
)
d = profile.to_dict()
buf = io.BytesIO()
with pytest.raises(TypeError, match="bytes"):
tomli_w.dump({"bad": d}, buf)
```
Use exactly 1-space indentation. No comments per project style.
- [ ] **Step 3.3: Run the test to verify it passes (Change 1 should already be applied)**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_workspace_profile_serialization.py -v --timeout=15
```
Expected: 3 passed (one per test).
- [ ] **Step 3.4: Verify the test would catch the regression**
Temporarily revert the fix in `src/gui_2.py:604` from `ini = ""` back to `ini = b""` and re-run:
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_workspace_profile_serialization.py -v --timeout=15
```
Expected: the first two tests should still pass (they test the dataclass round-trip directly, not the defer fix), and the third test confirms the type contract. The test that catches the regression is the integration test in Task 1 (which goes through the live_gui save flow).
Restore the fix:
```powershell
cd C:\projects\manual_slop; git diff src/gui_2.py # confirm only the in-scope fix is there
```
If you reverted the fix, re-apply it via `manual-slop_edit_file` and verify the tests still pass.
- [ ] **Step 3.5: Commit**
```powershell
cd C:\projects\manual_slop; git add tests/test_workspace_profile_serialization.py
git -C C:\projects\manual_slop commit -m "test(workspace_profile): add str/bytes TOML serialization contract test"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Encodes the WorkspaceProfile.ini_content: str contract. The d7487af4 defer fix used ini=b'' which tomli_w rejects with TypeError. This test would have caught the regression at unit-test speed (no live_gui needed). 3 tests: empty str round-trips, real ini content round-trips, bytes ini_content is rejected (documents the contract)." $h
```
---
## Task 4: Verify all 3 originally-failing tests now pass
**Files:** (no file changes; verification only)
- [ ] **Step 4.1: Run the 3 originally-failing tests**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_auto_switch_sim.py tests/test_workspace_profiles_sim.py tests/test_prior_session_no_pop_imbalance.py -v --timeout=60
```
Expected: 3 passed (one file each).
- [ ] **Step 4.2: Run the regression unit test**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_workspace_profile_serialization.py -v --timeout=15
```
Expected: 3 passed.
- [ ] **Step 4.3: Run the full batched test suite**
```powershell
cd C:\projects\manual_slop; uv run python scripts/run_tests_batched.py
```
Expected: 273 files (272 + 1 new), all batches pass (273/273 = 100%).
- [ ] **Step 4.4: Commit plan update**
```powershell
cd C:\projects\manual_slop; git add conductor/tracks.md docs/superpowers/plans/2026-06-05-live-gui-fragility-fixes.md
```
Then append the following entry to `conductor/tracks.md` (under the existing `regression_fixes_20260605` entry or as a new entry):
```markdown
- [x] **Track: Live-GUI Fragility Fixes (post regression_fixes_20260605)** `[checkpoint: <sha>]`
*Link: [./tracks/live_gui_fragility_fixes_20260605/](./tracks/live_gui_fragility_fixes_20260605/), Spec: [./../../docs/superpowers/specs/2026-06-05-live-gui-fragility-fixes-design.md](./../../docs/superpowers/specs/2026-06-05-live-gui-fragility-fixes-design.md), Plan: [./../../docs/superpowers/plans/2026-06-05-live-gui-fragility-fixes.md](./../../docs/superpowers/plans/2026-06-05-live-gui-fragility-fixes.md)*
*Goal: Fix 3 remaining live_gui test failures (269/272 → 272/272). 1-line src fix in `_capture_workspace_profile` (str/bytes sentinel that broke TOML serialization), 2-line test mock fix for `imscope.window` tuple-return, 1 new regression unit test for the str/bytes contract. All atomic per-file commits. The d7487af4 defer fix had introduced a TypeError via `ini=b""`; the regression was traced to `WorkspaceProfile.ini_content: str` and tomli_w's bytes rejection.*
```
(Replace `<sha>` with the actual checkpoint SHA from the last commit.)
```powershell
cd C:\projects\manual_slop; git -c core.autocrlf=false commit -m "conductor(plan): mark live_gui_fragility_fixes track complete"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Track complete. 273/273 tests pass (was 269/272 pre-track, 272/273 mid-track). 3 atomic per-file commits: src/gui_2.py, test_prior_session_no_pop_imbalance.py, new test_workspace_profile_serialization.py." $h
```
---
## Task 5 (OPTIONAL): Doc hardening of defer-not-catch sections
> **Skip this task if time is short.** Per user review 2026-06-05, this is deferred to the end. If you've reached the end of the track with time to spare, do it; otherwise, leave for a follow-up.
**Files:**
- Modify: `docs/guide_gui_2.md` "Workspace Profile Defer-Not-Catch" section
- Modify: `docs/guide_testing.md` "Early-Render C-Level Crashes" section
- Modify: `conductor/workflow.md` "Defer-Not-Catch Pattern for Native Crashes" section
- [ ] **Step 5.1: Add a one-paragraph note to each of the three docs**
Add this note (paraphrased to fit each doc's voice) to each of the three defer-not-catch sections:
> "**Sentinel type contract.** When implementing a defer-not-catch guard, the early-return sentinel value must match the type contract of the downstream consumer. For `WorkspaceProfile.ini_content: str` (in this codebase), the sentinel must be `""` (str), not `b""` (bytes) — `tomli_w` rejects bytes, and `imgui.load_ini_settings_from_memory(ini_data: str, ...)` also expects str. A previous version of this fix used `b""` and silently broke the save flow via a `TypeError` raised by `tomli_w.dump`; tests passed unit-test-wise but failed in the live_gui save+load round-trip."
- [ ] **Step 5.2: Commit**
```powershell
cd C:\projects\manual_slop; git add docs/guide_gui_2.md docs/guide_testing.md conductor/workflow.md
git -C C:\projects\manual_slop commit -m "docs: add sentinel-type-contract note to defer-not-catch sections"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Three doc updates: guide_gui_2.md, guide_testing.md, workflow.md. Added a 'Sentinel type contract' note to each defer-not-catch section warning that the early-return sentinel must match the downstream consumer's type contract (str not bytes for WorkspaceProfile.ini_content). Prevents future regressions of the kind introduced by d7487af4." $h
```
---
## Self-Review
After writing the complete plan, check against the spec:
**1. Spec coverage:**
- Change 1 (`b""``""` fix): Task 1, Step 1.3. ✓
- Change 2 (test mock fix): Task 2, Step 2.3. ✓
- Change 3 (regression unit test): Task 3, Step 3.2. ✓
- Change 4 (doc hardening, deferred): Task 5, marked OPTIONAL. ✓
- Goals: 100% pass rate (Task 4, Step 4.3). ✓
- Non-goals respected: no workspace profile refactor, no wait-for-ready framework, no sloppy.py startup changes. ✓
**2. Placeholder scan:** No "TBD"/"TODO"/"implement later" patterns. All code blocks are complete. ✓
**3. Type consistency:** `WorkspaceProfile.ini_content: str` referenced consistently. `b""``""` change is the single source of the fix. `_ini_capture_ready` flag is preserved. `str(...) or ""` wrap is documented. ✓
---
## Execution Handoff
This plan is sized for **inline execution** (single agent, no subagents, per the user's stated preference). Execute Tasks 1-4 in order; skip Task 5 if time is short.
After each task's commit, attach the git note (the `$h` line in each task). After all tasks, run Task 4's full suite to confirm 100% pass.
If any task fails, stop and run `/conductor:implement --debug` or escalate to a Tier 4 QA analysis (per `conductor/workflow.md`).
@@ -1,369 +0,0 @@
# Live-GUI State Sync Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Eliminate the App/Controller dual-state bug for the 8 confirmed sync-bug fields. Single source of truth: the Controller. App exposes Controller fields as properties. Restore `test_auto_switch_sim`, `test_workspace_profiles_restoration`, and likely `test_undo_redo_lifecycle`.
**Architecture:** Add `@property` + `@X.setter` pairs on the `App` class for each sync-bug field. The getter reads `self.controller.X`; the setter writes `self.controller.X`. App-only fields (no Controller counterpart) remain as plain attributes. One regression test encodes the contract.
**Tech Stack:** Python 3.11+, properties (descriptor protocol), pytest 9.0.
---
## File Structure
| File | Change | Purpose |
|---|---|---|
| `src/gui_2.py` | Modify (App class only) | Add 9 property pairs (8 sync-bug fields + `ui_ai_input`) |
| `tests/test_app_controller_state_sync.py` | Create | Regression test for the delegation contract |
No new modules, no architectural refactor.
---
## Task 1: Add the property pair for `ui_ai_input`
**Files:**
- Modify: `src/gui_2.py` (App class, near other property definitions if any, or after `__init__`)
- [ ] **Step 1.1: Pre-edit checkpoint**
```powershell
cd C:\projects\manual_slop; git status --short
```
If `src/gui_2.py` has uncommitted changes, stop and ask the user.
- [ ] **Step 1.2: Read the App class around `__init__` to find a good insertion point**
Read `src/gui_2.py:130-200` to see how the App class is structured. The property should be at module/class level, ideally in a clearly delimited region. Check if there's an existing `#region: Properties` block or similar.
- [ ] **Step 1.3: Add the `ui_ai_input` property pair**
Find the existing `self.ui_ai_input = ...` line in `App.__init__` (search for it). After the `__init__` method ends, add:
```python
@property
def ui_ai_input(self) -> str:
return self.controller.ui_ai_input
@ui_ai_input.setter
def ui_ai_input(self, value: str) -> None:
self.controller.ui_ai_input = value
```
Use exactly 1-space indentation per project style. Use `manual-slop_py_update_definition` with the App class to add the property.
- [ ] **Step 1.4: Verify the file still parses**
```powershell
cd C:\projects\manual_slop; uv run python -c "import ast; ast.parse(open('src/gui_2.py', encoding='utf-8').read()); print('OK')"
```
Expected: `OK`.
- [ ] **Step 1.5: Commit (interim checkpoint)**
```powershell
cd C:\projects\manual_slop; git add src/gui_2.py
git -C C:\projects\manual_slop commit -m "fix(gui_2): add ui_ai_input property delegating to controller (sync fix #1 of 9)"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Add @property/@setter for ui_ai_input on the App class. Getter reads self.controller.ui_ai_input; setter writes self.controller.ui_ai_input. This is the first of 9 sync-bug property pairs (ui_ai_input + 7 panel_states + show_windows). The dual state was the root cause of test_undo_redo_lifecycle: snapshot read app.ui_ai_input but set_value wrote controller.ui_ai_input." $h
```
---
## Task 2: Add property pairs for `ui_separate_tier1` through `ui_separate_tier4`
**Files:**
- Modify: `src/gui_2.py` (App class)
- [ ] **Step 2.1: Add all 4 properties in a batch**
After the `ui_ai_input` property, add:
```python
@property
def ui_separate_tier1(self) -> bool:
return self.controller.ui_separate_tier1
@ui_separate_tier1.setter
def ui_separate_tier1(self, value: bool) -> None:
self.controller.ui_separate_tier1 = value
@property
def ui_separate_tier2(self) -> bool:
return self.controller.ui_separate_tier2
@ui_separate_tier2.setter
def ui_separate_tier2(self, value: bool) -> None:
self.controller.ui_separate_tier2 = value
@property
def ui_separate_tier3(self) -> bool:
return self.controller.ui_separate_tier3
@ui_separate_tier3.setter
def ui_separate_tier3(self, value: bool) -> None:
self.controller.ui_separate_tier3 = value
@property
def ui_separate_tier4(self) -> bool:
return self.controller.ui_separate_tier4
@ui_separate_tier4.setter
def ui_separate_tier4(self, value: bool) -> None:
self.controller.ui_separate_tier4 = value
```
- [ ] **Step 2.2: Verify parse + commit**
```powershell
cd C:\projects\manual_slop; uv run python -c "import ast; ast.parse(open('src/gui_2.py', encoding='utf-8').read()); print('OK')"
cd C:\projects\manual_slop; git add src/gui_2.py
git -C C:\projects\manual_slop commit -m "fix(gui_2): add ui_separate_tier1..4 property pairs (sync fix #2-5 of 9)"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Add 4 property pairs (ui_separate_tier1..4). These are the 4 fields that test_workspace_profiles_restoration and test_auto_switch_sim exercise. The save reads app.ui_separate_tier1, but set_value writes controller.ui_separate_tier1 -- the property bridges them." $h
```
---
## Task 3: Add property pairs for `ui_separate_task_dag` and `ui_separate_usage_analytics`
**Files:**
- Modify: `src/gui_2.py` (App class)
- [ ] **Step 3.1: Add both properties**
```python
@property
def ui_separate_task_dag(self) -> bool:
return self.controller.ui_separate_task_dag
@ui_separate_task_dag.setter
def ui_separate_task_dag(self, value: bool) -> None:
self.controller.ui_separate_task_dag = value
@property
def ui_separate_usage_analytics(self) -> bool:
return self.controller.ui_separate_usage_analytics
@ui_separate_usage_analytics.setter
def ui_separate_usage_analytics(self, value: bool) -> None:
self.controller.ui_separate_usage_analytics = value
```
- [ ] **Step 3.2: Verify + commit**
```powershell
cd C:\projects\manual_slop; uv run python -c "import ast; ast.parse(open('src/gui_2.py', encoding='utf-8').read()); print('OK')"
cd C:\projects\manual_slop; git add src/gui_2.py
git -C C:\projects\manual_slop commit -m "fix(gui_2): add ui_separate_task_dag, ui_separate_usage_analytics property pairs (sync fix #6-7 of 9)"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Add 2 property pairs (ui_separate_task_dag, ui_separate_usage_analytics). These complete the 6 panel_states sync-bug fields. All ui_separate_X fields with Controller settable counterparts are now properties." $h
```
---
## Task 4: Add property pair for `show_windows`
**Files:**
- Modify: `src/gui_2.py` (App class)
- [ ] **Step 4.1: Add the property (dict type)**
```python
@property
def show_windows(self) -> dict:
return self.controller.show_windows
@show_windows.setter
def show_windows(self, value: dict) -> None:
self.controller.show_windows = value
```
- [ ] **Step 4.2: Verify + commit**
```powershell
cd C:\projects\manual_slop; uv run python -c "import ast; ast.parse(open('src/gui_2.py', encoding='utf-8').read()); print('OK')"
cd C:\projects\manual_slop; git add src/gui_2.py
git -C C:\projects\manual_slop commit -m "fix(gui_2): add show_windows property pair (sync fix #8 of 9)"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Add show_windows property (dict). In-place mutations (app.show_windows['X'] = True) work because the property returns the same dict reference as the controller. Replacements (app.show_windows = new_dict) go through the setter." $h
```
---
## Task 5: Write the regression test
**Files:**
- Create: `tests/test_app_controller_state_sync.py`
- [ ] **Step 5.1: Pre-edit checkpoint**
```powershell
cd C:\projects\manual_slop; git status --short
```
- [ ] **Step 5.2: Read the App's `__init__` to find the minimum setup needed for property access**
Read `src/gui_2.py:130-180` to see App's `__init__`. We need to instantiate an App (or use `__new__` to skip `__init__`) and set up the minimum state for property access.
- [ ] **Step 5.3: Write the test file**
```python
import pytest
from src import app_controller, gui_2
def _make_minimal_app():
app = gui_2.App.__new__(gui_2.App)
app.controller = app_controller.AppController()
app.controller._app = app
return app
def test_ui_ai_input_property_delegates_to_controller():
app = _make_minimal_app()
app.controller.ui_ai_input = "Hello"
assert app.ui_ai_input == "Hello"
app.ui_ai_input = "World"
assert app.controller.ui_ai_input == "World"
def test_ui_separate_tier1_property_delegates_to_controller():
app = _make_minimal_app()
app.controller.ui_separate_tier1 = True
assert app.ui_separate_tier1 is True
app.ui_separate_tier1 = False
assert app.controller.ui_separate_tier1 is False
def test_ui_separate_tier2_through_tier4_properties_delegate():
app = _make_minimal_app()
for attr in ("ui_separate_tier2", "ui_separate_tier3", "ui_separate_tier4"):
setattr(app.controller, attr, True)
assert getattr(app, attr) is True
setattr(app, attr, False)
assert getattr(app.controller, attr) is False
def test_ui_separate_task_dag_and_usage_analytics_properties_delegate():
app = _make_minimal_app()
for attr in ("ui_separate_task_dag", "ui_separate_usage_analytics"):
setattr(app.controller, attr, True)
assert getattr(app, attr) is True
setattr(app, attr, False)
assert getattr(app.controller, attr) is False
def test_show_windows_property_delegates_to_controller():
app = _make_minimal_app()
app.controller.show_windows = {"A": True, "B": False}
assert app.show_windows == {"A": True, "B": False}
app.show_windows = {"C": True}
assert app.controller.show_windows == {"C": True}
def test_show_windows_inplace_mutation_visible_to_controller():
app = _make_minimal_app()
app.controller.show_windows = {"A": False}
app.show_windows["A"] = True
assert app.controller.show_windows["A"] is True
def test_app_only_panel_states_remain_plain_attributes():
app = _make_minimal_app()
for attr in ("ui_separate_context_preview", "ui_separate_message_panel",
"ui_separate_response_panel", "ui_separate_tool_calls_panel",
"ui_separate_external_tools", "ui_discussion_split_h"):
assert not hasattr(type(app), attr), \
f"{attr} should NOT be a property (no controller counterpart)"
```
Use exactly 1-space indentation.
- [ ] **Step 5.4: Run the test**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_app_controller_state_sync.py -v --timeout=15
```
Expected: 7 passed.
- [ ] **Step 5.5: Commit**
```powershell
cd C:\projects\manual_slop; git add tests/test_app_controller_state_sync.py
git -C C:\projects\manual_slop commit -m "test(app_controller): add state sync property regression tests"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "7 tests for the App->Controller state delegation contract. Covers ui_ai_input, ui_separate_tier1..4, ui_separate_task_dag, ui_separate_usage_analytics, show_windows (with both replacement and in-place mutation semantics). Also asserts that App-only fields (ui_separate_context_preview, etc.) are NOT properties." $h
```
---
## Task 6: Run the originally-failing tests to verify the fix
**Files:** (no file changes; verification only)
- [ ] **Step 6.1: Run the 3 originally-failing tests**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_auto_switch_sim.py tests/test_workspace_profiles_sim.py tests/test_undo_redo_sim.py -v --timeout=60
```
Expected: all pass (or at minimum: the 2 profile tests pass; undo_redo may still fail if it's a flake unrelated to sync).
- [ ] **Step 6.2: If `test_undo_redo_sim` still fails, run it in isolation**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_undo_redo_sim.py::test_undo_redo_lifecycle -v --timeout=60
```
If it passes in isolation, it's a flake. Document in the commit note and move on.
- [ ] **Step 6.3: Commit verification result**
```powershell
cd C:\projects\manual_slop; git -c core.autocrlf=false commit --allow-empty -m "verify: state sync fix unblocks test_auto_switch_sim + test_workspace_profiles_restoration"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Verified: test_auto_switch_sim and test_workspace_profiles_restoration now pass. test_undo_redo_lifecycle [passes in isolation / still fails - see other notes]. The App/Controller state sync bug is resolved via the property approach." $h
```
---
## Task 7: Update tracks.md and conductor/index.md
**Files:**
- Modify: `conductor/tracks.md` (mark v2 sub-track complete or partial)
- Modify: `conductor/index.md` (move v2 sub-track to recently-shipped or note next steps)
- [ ] **Step 7.1: Update tracks.md**
Find the live_gui_test_hardening_v2 entry and add a sub-task completion note. Or move to a dedicated entry.
- [ ] **Step 7.2: Update index.md**
- [ ] **Step 7.3: Commit**
```powershell
cd C:\projects\manual_slop; git add conductor/tracks.md conductor/index.md
git -C C:\projects\manual_slop commit -m "conductor: live_gui_state_sync sub-track complete"
```
---
## Self-Review
- **Spec coverage:** All 8 sync-bug fields + `ui_ai_input` (9 total) have property pairs (Tasks 1-4). The regression test (Task 5) covers the delegation contract. Verification (Task 6) runs the originally-failing tests.
- **Placeholders:** None.
- **Type consistency:** `bool` for `ui_separate_*`, `str` for `ui_ai_input`, `dict` for `show_windows` — matches the existing Controller type hints.
- **Risk:** Mid — 9 property pairs added to a 5532-line class. Per-field atomic commits with regression tests mitigate.
---
## Execution Handoff
This plan is sized for **inline execution** (single agent, no subagents, per the user's stated preference). Execute Tasks 1-7 in order; each task ends with an atomic commit + git note.
After all tasks, the user runs `uv run python scripts/run_tests_batched.py` to confirm 100% pass on the 273-file suite.
@@ -1,222 +0,0 @@
# prior_session_test_harden_20260605 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Rewrite `tests/test_prior_session_no_pop_imbalance.py` to call `gui_2.render_prior_session_view(app_instance)` instead of `gui_2.render_main_interface(app_instance)`. Reduce mocks from 50+ to ~30. Preserve the push/pop balance assertion.
**Architecture:** Refactor the test scope from kitchen-sink to narrow path. The `render_prior_session_view` function is ~30 lines with a finite mockable set of imgui/imscope calls.
**Tech Stack:** Python 3.11+, pytest 9.0, unittest.mock.
---
## File Structure
| File | Change | Purpose |
|---|---|---|
| `tests/test_prior_session_no_pop_imbalance.py` | Rewrite | Call narrow `render_prior_session_view`; remove 50+ kitchen-sink mocks; keep 30+ scoped mocks |
No production code changes.
---
## Task 1: Audit the mocks required by `render_prior_session_view`
**Files:**
- Read: `src/gui_2.py` (the `render_prior_session_view` function, ~30 lines)
- [ ] **Step 1.1: Read the function**
Read `src/gui_2.py:render_prior_session_view` to list every imgui/imscope/theme/markdown_helper call it makes.
- [ ] **Step 1.2: Build the required-mock list**
From the function body, list:
- `imscope.style_color`, `imscope.child`, `imscope.id` (3 context managers)
- `imgui.Col_`, `imgui.button`, `imgui.same_line`, `imgui.text_colored`, `imgui.separator`, `imgui.get_content_region_avail`, `imgui.ImVec2`, `imgui.WindowFlags_` (~8 imgui calls)
- `theme.get_color`, `theme.ai_text_style` (2 theme calls)
- `markdown_helper.render` (1 call)
**Expected mocks:** ~14 unique mock setups (with side_effects for tracking, maybe 20-25 mock assignments total).
- [ ] **Step 1.3: Document the list inline**
Create a one-line comment in the test file or in a comment at the top:
```python
# render_prior_session_view uses: imscope.{style_color, child, id}, imgui.{Col_, button, same_line, text_colored, separator, get_content_region_avail, ImVec2, WindowFlags_}, theme.{get_color, ai_text_style}, markdown_helper.render
```
This becomes the contract.
- [ ] **Step 1.4: No commit yet (informational step)**
---
## Task 2: Rewrite the test file
**Files:**
- Modify: `tests/test_prior_session_no_pop_imbalance.py` (full rewrite)
- [ ] **Step 2.1: Pre-edit checkpoint**
```powershell
cd C:\projects\manual_slop; git status --short
```
- [ ] **Step 2.2: Backup the original (optional safety)**
```powershell
cp C:\projects\manual_slop\tests\test_prior_session_no_pop_imbalance.py C:\projects\manual_slop\tests\test_prior_session_no_pop_imbalance.py.bak
```
(This is just a safety net; we won't commit the .bak.)
- [ ] **Step 2.3: Write the new test file**
Replace the entire content of `tests/test_prior_session_no_pop_imbalance.py` with:
```python
import pytest
from unittest.mock import MagicMock, patch
# render_prior_session_view uses: imscope.{style_color, child, id}, imgui.{Col_, button, same_line, text_colored, separator, get_content_region_avail, ImVec2, WindowFlags_}, theme.{get_color, ai_text_style}, markdown_helper.render
def test_no_extraneous_pop_when_prior_session_renders():
"""Verifies that imscope push/pop balance is maintained when the
prior-session render path executes. Calls render_prior_session_view
(the narrow function) instead of render_main_interface (kitchen sink).
"""
from src import gui_2
app_instance = MagicMock()
app_instance.is_viewing_prior_session = True
app_instance.perf_profiling_enabled = False
app_instance.prior_disc_entries = [
{"role": "User", "content": "test", "collapsed": False, "ts": "t1"}
]
push_count = {"n": 0}
pop_count = {"n": 0}
def _track_push(*a, **k): push_count["n"] += 1
def _track_pop(*a, **k): pop_count["n"] += 1
with patch("src.gui_2.imgui") as mock_imgui, \
patch("src.gui_2.imscope") as mock_imscope, \
patch("src.gui_2.theme") as mock_theme, \
patch("src.gui_2.markdown_helper") as mock_md:
# imscope context managers: track style_color push/pop, default for child/id
mock_imscope.style_color.return_value.__enter__.side_effect = _track_push
mock_imscope.style_color.return_value.__exit__.side_effect = lambda *a: (pop_count.__setitem__("n", pop_count["n"] + 1) or False)
mock_imscope.child.return_value.__enter__ = MagicMock()
mock_imscope.child.return_value.__exit__ = MagicMock(return_value=False)
mock_imscope.id.return_value.__enter__ = MagicMock()
mock_imscope.id.return_value.__exit__ = MagicMock(return_value=False)
# imgui calls
mock_imgui.Col_ = MagicMock()
mock_imgui.button = MagicMock(return_value=False)
mock_imgui.same_line = MagicMock()
mock_imgui.text_colored = MagicMock()
mock_imgui.separator = MagicMock()
mock_imgui.get_content_region_avail = MagicMock(return_value=MagicMock(x=800.0, y=600.0))
mock_imgui.ImVec2 = lambda *a: MagicMock(x=a[0], y=a[1])
mock_imgui.WindowFlags_ = MagicMock()
# theme calls
mock_theme.get_color = MagicMock(return_value=MagicMock())
mock_theme.ai_text_style.return_value.__enter__ = MagicMock()
mock_theme.ai_text_style.return_value.__exit__ = MagicMock(return_value=False)
# markdown helper
mock_md.render = MagicMock()
gui_2.render_prior_session_view(app_instance)
assert push_count["n"] == pop_count["n"], f"Push/pop imbalance: pushes={push_count['n']}, pops={pop_count['n']}"
```
Use exactly 1-space indentation. No comments unless the docstring is enough.
- [ ] **Step 2.4: Remove the backup**
```powershell
Remove-Item C:\projects\manual_slop\tests\test_prior_session_no_pop_imbalance.py.bak
```
- [ ] **Step 2.5: Run the test**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_prior_session_no_pop_imbalance.py -v --timeout=15
```
Expected: 1 passed.
- [ ] **Step 2.6: If it fails, diagnose the missing mock**
The test output will show the missing imgui call. Add the mock and re-run.
- [ ] **Step 2.7: Commit**
```powershell
cd C:\projects\manual_slop; git add tests/test_prior_session_no_pop_imbalance.py
git -C C:\projects\manual_slop commit -m "test(prior_session): rewrite to test narrow render_prior_session_view"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Refactor test to call render_prior_session_view (narrow ~30-line function) instead of render_main_interface (kitchen sink). Reduced mocks from 50+ to ~20. Preserved the push/pop balance assertion. The imscope.window tuple-return issue is bypassed because render_prior_session_view doesn't call imscope.window." $h
```
---
## Task 3: Verify the test runs in the full batched suite
**Files:** (no file changes; verification only)
- [ ] **Step 3.1: Run the full test_prior_session_no_pop_imbalance.py**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_prior_session_no_pop_imbalance.py -v --timeout=15
```
Expected: 1 passed.
- [ ] **Step 3.2: Commit the verification (no-op)**
```powershell
cd C:\projects\manual_slop; git -c core.autocrlf=false commit --allow-empty -m "verify: prior_session test passes in isolation"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Verified the rewritten test passes in isolation. The user will run the full batched suite to confirm 273/273 pass." $h
```
---
## Task 4: Update tracks.md
**Files:**
- Modify: `conductor/tracks.md` (note prior_session_test_harden sub-track complete)
- [ ] **Step 4.1: Add a brief note**
Find the live_gui_test_hardening_v2 entry and add: "Sub-track `prior_session_test_harden_20260605` complete: test rewritten to call narrow `render_prior_session_view` (50+ mocks → ~20 mocks)."
- [ ] **Step 4.2: Commit**
```powershell
cd C:\projects\manual_slop; git add conductor/tracks.md
git -C C:\projects\manual_slop commit -m "conductor: prior_session_test_harden sub-track complete"
```
---
## Self-Review
- **Spec coverage:** Test rewritten to call `render_prior_session_view` (Task 2). Push/pop balance assertion preserved. Mocks reduced from 50+ to ~20.
- **Placeholders:** None.
- **Type consistency:** Mocks return MagicMock() with appropriate attributes; side_effects match the tracked contract.
- **Risk:** Low — only the test file changes; production code is untouched.
---
## Execution Handoff
Inline execution. 4 tasks, atomic commits. User runs the full batched suite to confirm.
@@ -1,669 +0,0 @@
# Regression Fixes — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Fix all test failures observed in the 2026-06-05 full test suite run (272 files in 68 batches). Eleven batches failed. Includes one theme-track regression, four pre-existing non-live_gui failures, and sixteen live_gui failures (mix of startup slowness, real test bugs, and GUI crashes).
**Architecture:** Each task is a self-contained fix. Theme regression gets a test update. Pre-existing non-live_gui failures get either fixture updates or src changes. Live_gui failures need investigation of root cause (often GUI startup or session lifecycle bugs).
**Tech Stack:** Python 3.11+, pytest, imgui-bundle, FastAPI/Uvicorn (live_gui), Unittest.mock
---
## Failure Inventory
### A. Theme-Track Regression (1 test)
| Test | File | Error | Bisect Result |
|---|---|---|---|
| `test_render_mma_dashboard_progress` | `tests/test_gui_progress.py:80` | `TypeError: __eq__(): incompatible function arguments. The following argument types are supported: 1. __eq__(self, arg: imgui_bundle._imgui_bundle.imgui.ImVec4, /)` | **Theme-caused**, broke at commit `7ea52cbb` (compact TOML formatting and lift semantic colors) |
**Root cause:** Commit `7ea52cbb` changed `C_LBL` from a module-level `imgui.ImVec4` value to a function call:
```python
# Before
C_LBL: imgui.ImVec4 = vec4(180, 180, 180)
# After
def C_LBL() -> imgui.ImVec4: return theme.get_color("text_disabled")
```
The test does `mock_imgui.text_colored.assert_any_call(C_LBL(), "Completed:")`. `C_LBL()` now calls `theme.get_color("text_disabled")` which uses the **real** `imgui.ImVec4` from `src/theme_2.py` (the test only patches `src.gui_2.imgui` and `src.imgui_scopes.imgui`, not `src.theme_2.imgui`). The real `ImVec4.__eq__` rejects the MagicMock argument from `assert_any_call`.
**Fix:** Adapt the test to mock `src.theme_2.imgui` properly. Per AGENTS.md: "DO NOT CREATE MOCK PATCHES TO PSEUDO API CALLS OR HOOKS BECAUSE THE APP SOURCE WAS CHANGED. ADAPT TESTS PROPERLY."
### B. Pre-Existing Non-live_gui Failures (4 tests)
| Test | File | Error | Bisect Result |
|---|---|---|---|
| `test_track_discussion_toggle` | `tests/test_gui_phase4.py:124` | `RuntimeError: IM_ASSERT( GImGui != 0 && ...)` in `src/markdown_helper.py:147` (`imgui.spacing()`) | **Pre-existing**, fails at commit `7df65dff` (pre-theme) |
| `test_no_extraneous_pop_when_prior_session_renders` | `tests/test_prior_session_no_pop_imbalance.py:132` | `AttributeError: 'tuple' object has no attribute 'x'` in `src/shaders.py:10` | **Pre-existing**, fails at commit `7df65dff` |
| `test_load_presets_from_project_list` | `tests/test_view_presets.py:95` | `AttributeError: 'AppController' object has no attribute 'persona_manager'` in `src/app_controller.py:2851` | **Pre-existing**, fails at commit `7df65dff` |
| `test_load_presets_from_project_legacy_dict` | `tests/test_view_presets.py:112` | Same as above | **Pre-existing** |
**Root causes:**
- `test_track_discussion_toggle`: `src/markdown_helper.py:147` calls `imgui.spacing()` in `flush_md()` after `imgui_md.render()`. Test mocks `imgui_md.render` to no-op but `imgui.spacing()` is not mocked, causing IM_ASSERT when no ImGui context exists.
- `test_no_extraneous_pop_when_prior_session_renders`: `src/shaders.py:10` does `r, g, b, a = color.x, color.y, color.z, color.w` where `color` should be an `imgui.ImVec4`. Test's mock `color` is a `tuple` from `("ImVec4", a)` mock lambda.
- `test_view_presets.py x2`: Test fixture doesn't initialize `ctrl.persona_manager` even though `_refresh_from_project` calls `self.persona_manager.load_all()`.
**Fixes:** Adapt the tests to mock the necessary calls properly (no mock-patches-for-changed-API shortcuts).
### C. Live_gui Failures (16 tests)
| Test | File | Failure Mode | Pattern |
|---|---|---|---|
| `test_auto_switch_sim` | `tests/test_auto_switch_sim.py:47` | `assert client.get_value('show_windows').get('Diagnostics', False) == True` | Workspace auto-switch logic not applying Tier 3 profile (GUI starts fine, assertion fails) |
| `test_context_sim_live` | `tests/test_extended_sims.py:27` | `assert len(entries) >= 2, f"Expected at least 2 entries, found {len(entries)}"` | GUI runs, AI responds, but session entries empty |
| `test_ai_settings_sim_live` | `tests/test_extended_sims.py:35` | `assert client.wait_for_server(timeout=10)` | GUI process died after `test_context_sim_live` |
| `test_tools_sim_live` | `tests/test_extended_sims.py:49` | Same | Same |
| `test_execution_sim_live` | `tests/test_extended_sims.py:62` | Same | Same |
| `test_full_live_workflow` | `tests/test_live_workflow.py:140` | `assert success, f"AI failed to respond. Entries: {client.get_session()}, Status: {client.get_mma_status()}"` | AI never responded (status always `None`) |
| `test_mma_concurrent_tracks_execution` | `tests/test_mma_concurrent_tracks_sim.py:58` | `assert ok, f"Proposed tracks not found: {status.get('proposed_tracks')}"` | MMA epic plan never produced tracks |
| `test_mma_concurrent_tracks_stress` | `tests/test_mma_concurrent_tracks_stress_sim.py:33` | `assert client.wait_for_server(timeout=15)` | Hook server didn't start |
| `test_mma_step_mode_approval_flow` | `tests/test_mma_step_mode_sim.py:48` | `KeyError: 'tracks'` | Tracks never created after plan epic |
| `test_phase4_final_verify` | `tests/test_rag_phase4_final_verify.py:78` | `if "error" in status.lower():` raises `AttributeError: 'NoneType' object has no attribute 'lower'` | Test doesn't handle `status=None` from `state.get('ai_status')` |
| `test_rag_large_codebase_verification_sim` | `tests/test_rag_phase4_stress.py:17` | `assert client.wait_for_server(timeout=15)` | Hook server didn't start |
| `test_rag_full_lifecycle_sim` | `tests/test_rag_visual_sim.py:17` | Same | Same |
| `test_rag_settings_persistence_sim` | `tests/test_rag_visual_sim.py:81` | Same | Same |
| `test_mma_complete_lifecycle` | `tests/test_visual_sim_mma_v2.py:92` | Timeout after 100s polling | Proposed tracks never appear |
| `test_mock_malformed_json` | `tests/test_z_negative_flows.py:40` | `assert event is not None, "Did not receive terminal response event"` | Response event never received |
| `test_mock_error_result` | `tests/test_z_negative_flows.py:51` | `assert client.wait_for_server(timeout=15)` | Hook server didn't start |
| `test_mock_timeout` | `tests/test_z_negative_flows.py:93` | Same | Same |
**Pattern groups:**
1. **GUI startup slowness (LogPruner busy loop):** Tests fail with "Hook server did not start" within 15s. The `LogPruner` is in a tight loop trying to delete locked log files (file still in use by the GUI process). This blocks the main thread from starting the FastAPI hook server promptly. **Affects:** `test_mma_concurrent_tracks_stress`, `test_rag_large_codebase_verification_sim`, `test_rag_full_lifecycle_sim`, `test_rag_settings_persistence_sim`, `test_mock_error_result`, `test_mock_timeout`, and the second/third/fourth tests in `test_extended_sims.py` (which die from cascading failure after first test).
2. **Session entries not populated:** `test_context_sim_live` (and likely the extended_sims cascade). AI sends a response but no entries show up in `client.get_session()`. Could be a real bug in session/entry tracking.
3. **MMA pipeline doesn't reach "tracks" state:** `test_mma_concurrent_tracks_execution`, `test_mma_step_mode_approval_flow`, `test_mma_complete_lifecycle`. All of these use the gemini_cli mock provider, call `btn_mma_plan_epic`, and then poll for `proposed_tracks` / `tracks`. None of them get them. Could be a real bug in MMA pipeline or the mock provider.
4. **AI never responds:** `test_full_live_workflow`. The status stays `None` for 20 seconds, then the test times out.
5. **Auto-switch layout not applying:** `test_auto_switch_sim`. The test triggers an MMA state update with `active_tier='Tier 3 (Worker): task-1'`, but the workspace profile doesn't auto-apply.
6. **Test code bugs (not app bugs):** `test_rag_phase4_final_verify` doesn't handle `status=None`. `test_rag_phase4_stress` etc. depend on GUI startup being faster.
## Execution Status (2026-06-05 - Updated)
| Task | Status | Commit |
|---|---|---|
| Task 1 (theme regression) | DONE | 38abf231 |
| Task 2a (gui_phase4) | DONE | df43f158 |
| Task 2b (prior_session) | PARTIAL (test still fails deeper) | f829d1df |
| Task 2c (view_presets) | DONE | 970f198c |
| Task 3a (LogPruner) | DONE | ac08ee87 |
| Task 3b (session entries) | ROOT CAUSE FOUND (task 2b-related) | - |
| Task 3c (MMA pipeline) | DEFERRED (live GUI + C-level crash) | - |
| Task 3d (RAG NoneType) | DONE | c96bdb06 |
| Task 3e (live workflow) | DEFERRED (live GUI + C-level crash) | - |
| Task 3f (auto_switch) | DEFERRED (live GUI + C-level crash) | - |
| Task 3g (z_negative_flows) | DEFERRED (live GUI + C-level crash) | - |
### BONUS FIX: GUI Production Bug (theme-caused)
**Commit 1469ecac** - Fixed `gui_2.py:3705-3707` where `DIR_COLORS.get(direction, C_VAL())`
returned the callable function instead of calling it. This was causing
`imgui.text_colored` to receive a function instead of `ImVec4`, raising
TypeError on EVERY GUI frame in `render_comms_history_panel`. The error was
caught by `_gui_func`'s except block so the GUI continued, but the Operations
Hub comms panel was completely broken. This is the THEME-CAUSED production
bug that was masking other test failures.
### ROOT CAUSE OF REMAINING LIVE_GUI FAILURES
The remaining 12 live_gui tests fail because the `sloppy.py` subprocess
crashes with a C-level access violation (`0xc0000005`) in
`_imgui_bundle.cp311-win_amd64.pyd`. This is a native crash, not a Python
exception, so it cannot be caught or debugged from Python.
**Event Viewer log evidence:**
```
Faulting module name: _imgui_bundle.cp311-win_amd64.pyd
Exception code: 0xc0000005
Fault offset: 0x00000000011424ae
```
**Why this blocks all live_gui tests:**
- `test_gui_startup_smoke` PASSES (basic startup works)
- All more complex live_gui tests fail (the GUI process dies after a few
render frames when user input triggers deeper code paths)
- The crash is non-deterministic (different fault offsets between runs),
suggesting memory corruption from C-side state
**What's needed to unblock:**
1. Capture a full crash dump from `_imgui_bundle.cp311-win_amd64.pyd`
2. Identify the specific imgui function causing the crash
3. Find the call site in `src/gui_2.py` that triggers it
4. Fix the call (e.g., pass correct type, add null check, init context)
This requires:
- A Windows debugger (WinDbg) or crash dump analysis
- A reproducer script that crashes 100% of the time
- Familiarity with imgui-bundle's C++ internals
### DEFERRED TASKS REQUIRING ABOVE
Tasks 3b-3g all depend on the live_gui fixture, which can't survive long
enough to run the test bodies. After fixing the underlying crash, the
deferred tasks should become tractable with normal test debugging.
---
## Execution Constraints
- **No subagents.** Execute as a single agent (per user request).
- **Per-file atomic commits.**
- **Commit message format:** `<type>(<scope>): <imperative description>`.
- **Git note format:** 3-8 line rationale per commit.
- **Style baseline:** 1-space indent, no comments, type hints.
- **Tests required:** every fix must include a passing test, not just patch existing ones.
---
## File Structure
| File | Action | Responsibility |
|---|---|---|
| `tests/test_gui_progress.py` | Modify | Adapt to new `C_LBL()` function API (Task 1) |
| `tests/test_gui_phase4.py` | Modify | Mock `imgui.spacing()` in `flush_md` (Task 2) |
| `tests/test_prior_session_no_pop_imbalance.py` | Modify | Use proper ImVec4 mock OR fix `shaders.py:10` to accept tuple (Task 2) |
| `tests/test_view_presets.py` | Modify | Add `persona_manager` mock to fixture (Task 2) |
| `src/markdown_helper.py` | Modify | Defensive guard around `imgui.spacing()` in `flush_md` (optional, if test-only fix is preferred) |
| `src/shaders.py` | Modify | Defensive guard for tuple input in `draw_soft_shadow` (optional) |
| `src/app_controller.py` | Modify | Defensive `hasattr(self, 'persona_manager')` check in `_refresh_from_project` (optional) |
| `src/log_pruner.py` | Modify | Add backoff/retry to avoid blocking the main thread on locked log files (Task 3) |
| `src/...` (various) | Investigate | Live_gui test fixes (Task 3) — need investigation per failure |
---
## Task 1: Fix theme-track regression in `test_gui_progress.py`
**Files:**
- Modify: `tests/test_gui_progress.py`
- [ ] **Step 1.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 1.2: Read current test fixture**
Read `tests/test_gui_progress.py:1-30` to see the existing `with patch(...)` block.
- [ ] **Step 1.3: Add `src.theme_2.imgui` to the patch list**
In `tests/test_gui_progress.py`, locate the existing `with patch(...)` block (around line 25-28). Add `patch("src.theme_2.imgui", new=mock_imgui)` to the context manager chain so `theme.get_color()` returns the mocked `ImVec4` instead of the real one.
Current pattern (approximate):
```python
with patch('src.gui_2.imgui', mock_imgui), \
patch('src.imgui_scopes.imgui', new=mock_imgui), \
patch('src.gui_2.cost_tracker.estimate_cost', return_value=0.0):
```
Change to:
```python
with patch('src.gui_2.imgui', mock_imgui), \
patch('src.imgui_scopes.imgui', new=mock_imgui), \
patch('src.theme_2.imgui', new=mock_imgui), \
patch('src.gui_2.cost_tracker.estimate_cost', return_value=0.0):
```
- [ ] **Step 1.4: Run test to verify it passes**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_gui_progress.py::test_render_mma_dashboard_progress -v --timeout=15
```
Expected: PASS.
- [ ] **Step 1.5: Run full test_gui_progress.py to check no regressions**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_gui_progress.py -v --timeout=15
```
Expected: all tests pass.
- [ ] **Step 1.6: Commit**
```powershell
git -C C:\projects\manual_slop add tests/test_gui_progress.py
git -C C:\projects\manual_slop commit -m "test(gui_progress): patch src.theme_2.imgui for C_LBL() function API"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "The 7ea52cbb commit changed C_LBL from an ImVec4 value to a C_LBL() function that calls theme.get_color. The test patches src.gui_2.imgui but theme.get_color uses the real imgui binding from src.theme_2. Adding patch('src.theme_2.imgui', new=mock_imgui) makes theme.get_color return the mock's ImVec4, so assert_any_call can compare it." $h
```
---
## Task 2: Fix pre-existing non-live_gui test failures
**Files:**
- Modify: `tests/test_gui_phase4.py`
- Modify: `tests/test_prior_session_no_pop_imbalance.py`
- Modify: `tests/test_view_presets.py`
### Task 2a: Fix `test_track_discussion_toggle` (gui_phase4)
- [ ] **Step 2.1: Read test setup**
Read `tests/test_gui_phase4.py:80-130` to see the `mock_imgui` setup and find the `imgui_md.render` patch.
- [ ] **Step 2.2: Add `imgui_md.render` and `imgui.spacing` mocks if missing**
In the test's `with patch(...)` block, ensure the following mocks exist (most are already present per the captured traceback; verify):
- `mock_imgui_md.render` is mocked to a no-op (or use a real one with the right return)
- `mock_imgui.spacing` is mocked to a no-op (the traceback shows this is the failing call at `src/markdown_helper.py:147`)
If `imgui.spacing` is NOT already mocked, add it. The traceback shows the call is:
```python
imgui_md.render(chunk) # mocked, no-op
imgui.spacing() # NOT mocked, fails IM_ASSERT
```
Add `mock_imgui.spacing = MagicMock()` to the test fixture.
- [ ] **Step 2.3: Run test to verify it passes**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_gui_phase4.py::test_track_discussion_toggle -v --timeout=15
```
Expected: PASS.
- [ ] **Step 2.4: Run full test_gui_phase4.py**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_gui_phase4.py -v --timeout=15
```
Expected: all tests pass.
- [ ] **Step 2.5: Commit**
```powershell
git -C C:\projects\manual_slop add tests/test_gui_phase4.py
git -C C:\projects\manual_slop commit -m "test(gui_phase4): mock imgui.spacing to avoid IM_ASSERT in markdown_helper"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "markdown_helper.flush_md calls imgui_md.render then imgui.spacing. The test mocks imgui_md.render but not imgui.spacing, so the second call hits the real imgui with no context and IM_ASSERT fails. Adding mock_imgui.spacing = MagicMock() prevents the assertion." $h
```
### Task 2b: Fix `test_no_extraneous_pop_when_prior_session_renders` (prior_session)
- [ ] **Step 2.6: Investigate root cause**
Read `src/shaders.py:1-30` to see the `draw_soft_shadow` function. Confirm it does `r, g, b, a = color.x, color.y, color.z, color.w` which requires `color` to be a real `imgui.ImVec4` (not a tuple).
The test mock creates `color` as a tuple via `("ImVec4", a)` lambda. Two options:
**Option A (test fix):** Update the test mock to use `MagicMock(side_effect=lambda *a: type("ImVec4", (), {"x": a[0], "y": a[1], "z": a[2], "w": a[3]})(*a))` so the mock returns an object with `.x`/`.y`/`.z`/`.w` attributes.
**Option B (src fix):** Update `src/shaders.py:10` to accept tuple OR `ImVec4`:
```python
if hasattr(color, "x"):
r, g, b, a = color.x, color.y, color.z, color.w
elif isinstance(color, (tuple, list)) and len(color) == 4:
r, g, b, a = color
```
**Recommendation:** Option B — make the function defensive. Real `ImVec4` objects are passed at runtime; tests use tuples as a simplification. Both should work.
- [ ] **Step 2.7: Apply src fix to `src/shaders.py`**
Read current `src/shaders.py:1-15` and modify the unpacking in `draw_soft_shadow` to handle both `ImVec4` and tuple/list inputs:
```python
def draw_soft_shadow(draw_list, p_min, p_max, color, shadow_size=10.0, rounding=0.0) -> None:
if hasattr(color, "x"):
r, g, b, a = color.x, color.y, color.z, color.w
else:
r, g, b, a = color
...
```
Use 1-space indent. The rest of the function is unchanged.
- [ ] **Step 2.8: Run test to verify it passes**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_prior_session_no_pop_imbalance.py::test_no_extraneous_pop_when_prior_session_renders -v --timeout=15
```
Expected: PASS.
- [ ] **Step 2.9: Run full test_prior_session_no_pop_imbalance.py**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_prior_session_no_pop_imbalance.py -v --timeout=15
```
Expected: all tests pass.
- [ ] **Step 2.10: Commit**
```powershell
git -C C:\projects\manual_slop add src/shaders.py
git -C C:\projects\manual_slop commit -m "fix(shaders): draw_soft_shadow accepts tuple or ImVec4 color"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Tests pass tuple mocks for color but the function expected ImVec4.x/.y/.z/.w attributes. Adding a hasattr fallback to unpack from a 4-tuple makes the function more permissive without changing real-app behavior (the real call path always passes a real ImVec4)." $h
```
### Task 2c: Fix `test_view_presets.py` (missing `persona_manager`)
- [ ] **Step 2.11: Read test fixture**
Read `tests/test_view_presets.py:7-37` to see the `controller` fixture.
- [ ] **Step 2.12: Add `persona_manager` mock**
After the existing `tool_preset_manager` mock line, add:
```python
ctrl.persona_manager = type('Mock', (), {'load_all': lambda self: {}})()
```
- [ ] **Step 2.13: Run tests to verify they pass**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_view_presets.py -v --timeout=15
```
Expected: all tests pass (5 total).
- [ ] **Step 2.14: Commit**
```powershell
git -C C:\projects\manual_slop add tests/test_view_presets.py
git -C C:\projects\manual_slop commit -m "test(view_presets): mock persona_manager in fixture"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "AppController._refresh_from_project calls self.persona_manager.load_all() but the test fixture only mocks preset_manager and tool_preset_manager. Adding a minimal persona_manager mock (load_all returns empty dict) makes the test pass without requiring the full PersonaManager class." $h
```
---
## Task 3: Investigate and fix live_gui test failures
This is the largest task. The 16 failures fall into 4 pattern groups. Each needs investigation before a fix can be planned.
### Sub-Task 3a: Fix LogPruner busy loop blocking GUI startup
The "Hook server did not start" pattern occurs because `LogPruner` is in a tight retry loop on locked log files. This blocks the main GUI thread from initializing the FastAPI hook server.
**Files:**
- Modify: `src/log_pruner.py`
- [ ] **Step 3.1: Pre-edit checkpoint**
```powershell
git -C C:\projects\manual_slop add .
```
- [ ] **Step 3.2: Read current LogPruner code**
Read `src/log_pruner.py` to find the busy loop. The test output shows:
```
[LogPruner] Removing 20260605_094323 at C:\projects\manual_slop\logs\20260605_094323 (Size: 0 bytes)
[LogPruner] Error removing C:\projects\manual_slop\logs\20260605_094323: [WinError 32] The process cannot access the file...
[LogPruner] Removing 20260605_095304 at C:\projects\manual_slop\logs\20260605_095304 (Size: 0 bytes)
[LogPruner] Error removing C:\projects\manual_slop\logs\20260605_095304: [WinError 32] ...
```
Tight loop on `WinError 32` (sharing violation).
- [ ] **Step 3.3: Add exponential backoff and skip-on-lock to LogPruner**
Modify the LogPruner's `prune` method to:
1. Add a `time.sleep(0.1)` after a `WinError 32` to avoid tight-looping.
2. Skip locked files on the first pass; try again on the next prune cycle.
3. Cap the number of retry attempts per file per cycle.
Use 1-space indent.
- [ ] **Step 3.4: Run live_gui test to verify startup completes**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_auto_switch_sim.py -v --timeout=60
```
Expected: PASS (or at least: hook server starts in <15s).
- [ ] **Step 3.5: Commit**
```powershell
git -C C:\projects\manual_slop add src/log_pruner.py
git -C C:\projects\manual_slop commit -m "fix(log_pruner): avoid tight retry loop on locked log files"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "The pruner was in a tight loop on WinError 32 (file in use) trying to delete logs the GUI process still holds. Added sleep + skip-on-lock to release the main thread so the FastAPI hook server can start. This unblocks 7+ live_gui tests that were timing out at wait_for_server(timeout=15)." $h
```
### Sub-Task 3b: Investigate session entries not populated
`test_context_sim_live` runs an AI turn successfully (status: "md written: project_001.md") but no entries show in `client.get_session()`.
**Files:**
- Investigate: `src/app_controller.py`, `src/session_logger.py`
- [ ] **Step 3.6: Add debug logging to test**
Read `tests/test_extended_sims.py:27-65` to see the test flow. Add a print statement before the assertion to dump `client.get_session()` and `client.get_mma_status()` to confirm the empty entries state.
- [ ] **Step 3.7: Run test with debug output**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_extended_sims.py::test_context_sim_live -v --timeout=60 -s
```
Expected: see session structure with empty entries.
- [ ] **Step 3.8: Trace session update path**
Read `src/app_controller.py` to find where `disc_entries` gets updated after an AI turn. Verify that `self.disc_entries` is properly updated and the session endpoint returns the right structure.
- [ ] **Step 3.9: Identify and fix the bug**
(This will be determined by the investigation. Common causes: thread safety issue, missing lock, endpoint not refreshing from controller state, async task not awaited.)
- [ ] **Step 3.10: Run test to verify it passes**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_extended_sims.py::test_context_sim_live -v --timeout=60
```
Expected: PASS.
- [ ] **Step 3.11: Commit**
```powershell
git -C C:\projects\manual_slop add <modified files>
git -C C:\projects\manual_slop commit -m "fix(session): <description from investigation>"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "..." $h
```
### Sub-Task 3c: Investigate MMA pipeline not creating tracks
`test_mma_concurrent_tracks_execution`, `test_mma_step_mode_approval_flow`, `test_mma_complete_lifecycle` all call `btn_mma_plan_epic` with a mock gemini_cli provider, but `proposed_tracks` / `tracks` never appear.
**Files:**
- Investigate: `src/multi_agent_conductor.py`, `src/dag_engine.py`, `src/api_hooks.py`, `tests/mock_gemini_cli.py`
- [ ] **Step 3.12: Run one test with -s to see the full poll output**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_mma_step_mode_sim.py::test_mma_step_mode_approval_flow -v --timeout=300 -s 2>&1 | Select-String "SIM|mma|tracks|proposed" | Select-Object -First 30
```
Expected: see polling output and the failing poll condition.
- [ ] **Step 3.13: Inspect the mock gemini_cli response**
Read `tests/mock_gemini_cli.py` to verify it returns a valid track-proposal response for the epic input.
- [ ] **Step 3.14: Trace the proposal pipeline**
In `src/multi_agent_conductor.py`, find the `plan_epic` flow and verify it:
1. Calls the mock provider
2. Parses the response into `proposed_tracks`
3. Sets `self.proposed_tracks` so `get_mma_status()` returns it
- [ ] **Step 3.15: Identify and fix the bug**
(Possible causes: mock provider path not being passed correctly, response parser failing silently, thread-safety issue with `proposed_tracks` field.)
- [ ] **Step 3.16: Run tests to verify they pass**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_mma_concurrent_tracks_sim.py tests/test_mma_concurrent_tracks_stress_sim.py tests/test_mma_step_mode_sim.py -v --timeout=300
```
Expected: all PASS.
- [ ] **Step 3.17: Commit**
```powershell
git -C C:\projects\manual_slop add <modified files>
git -C C:\projects\manual_slop commit -m "fix(mma): <description from investigation>"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "..." $h
```
### Sub-Task 3d: Fix test code bugs (not app bugs)
`test_rag_phase4_final_verify::test_phase4_final_verify` has:
```python
if "error" in status.lower():
```
But `status` is `None` when polling doesn't return one. This is a test bug — the test should handle None.
**Files:**
- Modify: `tests/test_rag_phase4_final_verify.py`
- [ ] **Step 3.18: Read the test**
Read `tests/test_rag_phase4_final_verify.py:60-85` to see the poll loop.
- [ ] **Step 3.19: Add None check**
Change:
```python
if "error" in status.lower():
```
to:
```python
if status and "error" in status.lower():
```
- [ ] **Step 3.20: Run test to verify it passes**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_rag_phase4_final_verify.py -v --timeout=60
```
Expected: PASS.
- [ ] **Step 3.21: Commit**
```powershell
git -C C:\projects\manual_slop add tests/test_rag_phase4_final_verify.py
git -C C:\projects\manual_slop commit -m "test(rag_phase4): handle None status in error check"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "The poll loop doesn't always return a status string. Added a None guard before calling .lower() to prevent AttributeError when status is missing. Real app status is always set, but test should be robust." $h
```
### Sub-Task 3e: Investigate `test_full_live_workflow` AI never responding
`test_full_live_workflow` polls `ai_status` for 20s, never gets a non-None value.
**Files:**
- Investigate: `src/app_controller.py`, `src/ai_client.py`
- [ ] **Step 3.22: Run with -s to see full poll output**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_live_workflow.py::test_full_live_workflow -v --timeout=120 -s 2>&1 | Select-String "Poll|status|set_value|click" | Select-Object -First 30
```
- [ ] **Step 3.23: Trace the AI request path**
Investigate why `ai_status` is never set after `btn_gen_send`. The test sets `current_provider='gemini'`, `current_model='gemini-2.5-flash-lite'`, sends a message, then expects status to change to 'sending...' or 'streaming...'.
- [ ] **Step 3.24: Identify and fix the bug**
- [ ] **Step 3.25: Run test to verify it passes**
- [ ] **Step 3.26: Commit**
### Sub-Task 3f: Investigate `test_auto_switch_sim` workspace profile not applying
The test triggers `mma_state_update` with `active_tier='Tier 3 (Worker): task-1'` but the bound workspace profile doesn't auto-apply.
**Files:**
- Investigate: `src/workspace_manager.py`, `src/gui_2.py` (auto-switch handler)
- [ ] **Step 3.27: Read test and find auto-switch handler**
Read `tests/test_auto_switch_sim.py:30-50` and find the auto-switch handler in `src/gui_2.py` (search for `ui_auto_switch_layout` or `auto_switch`).
- [ ] **Step 3.28: Identify the bug**
(Possible causes: tier name mismatch, profile name not loading correctly, switch never fires.)
- [ ] **Step 3.29: Run test to verify it passes**
- [ ] **Step 3.30: Commit**
### Sub-Task 3g: Investigate `test_z_negative_flows` (3 tests)
`test_mock_malformed_json`, `test_mock_error_result`, `test_mock_timeout` all fail. The first fails because the response event never arrives; the others fail on hook server startup.
- [ ] **Step 3.31: Wait for Sub-Task 3a to complete (LogPruner fix)**
These tests depend on the GUI starting successfully. The "Hook server did not start" failures will likely be fixed by the LogPruner fix in 3a.
- [ ] **Step 3.32: Run the three tests to see which still fail**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_z_negative_flows.py -v --timeout=60
```
- [ ] **Step 3.33: Investigate `test_mock_malformed_json` separately**
If it still fails after 3a, investigate the response event delivery for the malformed JSON case.
- [ ] **Step 3.34: Identify and fix any remaining bugs**
- [ ] **Step 3.35: Commit**
---
## Task 4: Phase Completion Verification
- [ ] **Step 4.1: Run full test suite to verify all fixes**
```powershell
cd C:\projects\manual_slop; uv run python scripts/run_tests_batched.py
```
Expected: 0 failed batches. (Skips allowed.)
- [ ] **Step 4.2: Address any new failures**
If new failures emerge, add them to the regression list and create follow-up tasks.
- [ ] **Step 4.3: Create checkpoint commit**
```powershell
git -C C:\projects\manual_slop commit --allow-empty -m "conductor(checkpoint): Regression fixes complete"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "All 21 test failures from 2026-06-05 full suite run resolved. 1 theme-track regression, 4 pre-existing non-live_gui failures, and 16 live_gui failures (mix of environment, app bugs, and test bugs) fixed. See plan.md for individual task rationales." $h
```
---
## Self-Review
- **Spec coverage:** All 21 failures from the 11 failed batches are covered: 1 in Task 1, 4 in Task 2, 16 in Task 3.
- **Placeholder scan:** Sub-tasks 3b, 3c, 3e, 3f, 3g have investigation steps before fix steps because the root cause needs to be determined at runtime. The plan explicitly says "Identify and fix the bug" with a "commit" step that will document what was found. No TBDs.
- **Type consistency:** All tests modified keep their existing signatures. Source changes are defensive guards (no API changes).
- **Constraint compliance:** No subagents (per user request). Per-file atomic commits. Style baseline 1-space indent.
## Execution Notes for User
The user said "Don't spawn workers, you'll need todo the fixes after planning" — meaning **you will execute these tasks yourself** (not me or subagents). The plan above is structured so each task can be done by hand:
- Task 1, Task 2a, 2b, 2c: Source-level changes are small (~5 lines each), can be done with `manual-slop_edit_file` or `manual-slop_py_update_definition`.
- Task 3: Investigation-heavy. Sub-tasks 3a, 3d are deterministic (LogPruner busy loop, None check). 3b, 3c, 3e, 3f, 3g need actual debugging with the live GUI.
Run the verification batched test script at the end of each sub-task to confirm no new failures.
@@ -1,161 +0,0 @@
# undo_redo_lifecycle_fix_20260605 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Resolve the `test_undo_redo_lifecycle` failure. Phase 1: verify state-sync fix is sufficient. Phase 2: investigate snapshot mechanism if needed. Phase 3: flake-fix with polling if needed.
**Architecture:** Sequential investigation. Cheapest fix first.
**Tech Stack:** Python 3.11+, pytest 9.0.
---
## File Structure
| File | Change | Purpose |
|---|---|---|
| (Phase 1) None | | |
| (Phase 2) `src/history.py`, `src/gui_2.py`, `tests/test_undo_redo_ai_input_snapshot.py` | Possibly modify | Fix snapshot if it doesn't include ai_input |
| (Phase 3) `tests/test_undo_redo_sim.py` | Possibly modify | Replace time.sleep with polling |
---
## Task 1: Phase 1 — Run the test, see if it passes after the state-sync fix
**Files:** (no changes; verification)
- [ ] **Step 1.1: Run the test in isolation**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_undo_redo_sim.py::test_undo_redo_lifecycle -v --timeout=60
```
Expected outcomes:
- **A) PASSES** → Done. The state-sync fix is sufficient. Skip to Task 4 (documentation).
- **B) FAILS** → Proceed to Task 2 (Phase 2: investigate snapshot).
- [ ] **Step 1.2: Document the outcome**
If passes: commit a doc-only note confirming state-sync fixed it.
```powershell
cd C:\projects\manual_slop; git -c core.autocrlf=false commit --allow-empty -m "verify: undo_redo_lifecycle passes after state-sync fix"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Confirmed: the ui_ai_input property delegation in live_gui_state_sync_20260605 fixes test_undo_redo_lifecycle. The snapshot reads app.ui_ai_input (now delegated to controller.ui_ai_input where the value lives) and captures the right value. Undo restores correctly." $h
```
If fails: proceed to Task 2.
---
## Task 2: Phase 2 — Check the snapshot mechanism for `ai_input`
**Files:** (read-only; possibly modify later)
- [ ] **Step 2.1: Read `UISnapshot` definition**
Read `src/history.py` to find the `UISnapshot` dataclass. List its fields.
```powershell
cd C:\projects\manual_slop; uv run python -c "
import re
with open('src/history.py', 'r', encoding='utf-8') as f:
content = f.read()
m = re.search(r'class UISnapshot', content)
if m:
print(content[m.start():m.start()+500])
"
```
- [ ] **Step 2.2: Check if `ai_input` is a field**
- **A) `ai_input` is a field** → Task 3: check `_apply_snapshot` for restore line.
- **B) `ai_input` is NOT a field** → Add it. See Step 2.3.
- [ ] **Step 2.3: If `ai_input` is missing from UISnapshot, add it**
Add `ai_input: str = ""` to the UISnapshot dataclass.
In `src/gui_2.py:_take_snapshot` (line 551), add `ai_input=self.ui_ai_input,`.
In `src/gui_2.py:_apply_snapshot` (line 569), add `self.ui_ai_input = snapshot.ai_input`.
Commit:
```powershell
cd C:\projects\manual_slop; git add src/history.py src/gui_2.py
git -C C:\projects\manual_slop commit -m "fix(gui_2): add ai_input to UISnapshot for undo/redo round-trip"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Add ai_input field to UISnapshot (src/history.py), capture in _take_snapshot, restore in _apply_snapshot. The undo/redo system was silently dropping ai_input changes; this fixes test_undo_redo_lifecycle." $h
```
- [ ] **Step 2.4: Run the test**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_undo_redo_sim.py::test_undo_redo_lifecycle -v --timeout=60
```
Expected: 1 passed.
If still fails → proceed to Task 3 (Phase 3: flake-fix with polling).
---
## Task 3: Phase 3 — Test-ordering / flake investigation
**Files:**
- Modify: `tests/test_undo_redo_sim.py` (replace time.sleep with polling)
- [ ] **Step 3.1: Add the polling helpers (or import from wait_for_ready track)**
```python
import time
def wait_for_value(client, item, expected, timeout=5.0):
deadline = time.time() + timeout
while time.time() < deadline:
if client.get_value(item) == expected:
return
time.sleep(0.1)
raise TimeoutError(f"Item '{item}' did not become {expected!r} within {timeout}s")
```
- [ ] **Step 3.2: Replace the time.sleep calls**
- [ ] **Step 3.3: Run the test**
- [ ] **Step 3.4: Commit**
```powershell
cd C:\projects\manual_slop; git add tests/test_undo_redo_sim.py
git -C C:\projects\manual_slop commit -m "test(undo_redo): replace time.sleep with wait_for_value polling"
```
---
## Task 4: Update tracks.md
**Files:**
- Modify: `conductor/tracks.md`
- [ ] **Step 4.1: Add a note about the outcome**
```powershell
cd C:\projects\manual_slop; git add conductor/tracks.md
git -C C:\projects\manual_slop commit -m "conductor: undo_redo_lifecycle sub-track complete"
```
---
## Self-Review
- **Spec coverage:** 3-phase sequential investigation. State-sync fix may resolve it (Phase 1). If not, snapshot investigation (Phase 2). If not, flake-fix (Phase 3).
- **Placeholders:** None.
- **Type consistency:** `ai_input: str` matches the existing type.
- **Risk:** Low — only investigation + minimal source change.
---
## Execution Handoff
Inline execution. Up to 4 tasks; some may be skipped depending on the outcome of Phase 1.
@@ -1,191 +0,0 @@
# wait_for_ready_test_pattern_20260605 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Replace `time.sleep(N)` in `test_workspace_profiles_sim.py` and `test_auto_switch_sim.py` with polling helpers that wait for the operation to complete. Tests should pass consistently across machines.
**Architecture:** Inline polling helpers (or extracted to `tests/helpers.py` if 3+ tests need them). 100ms poll interval, 5s default timeout.
**Tech Stack:** Python 3.11+, pytest 9.0, time-based polling.
---
## File Structure
| File | Change | Purpose |
|---|---|---|
| `tests/test_workspace_profiles_sim.py` | Modify | Replace time.sleep with polling |
| `tests/test_auto_switch_sim.py` | Modify | Replace time.sleep with polling |
No production code changes. No new shared module (helpers are inlined for now).
---
## Task 1: Migrate `test_workspace_profiles_sim.py`
**Files:**
- Modify: `tests/test_workspace_profiles_sim.py`
- [ ] **Step 1.1: Pre-edit checkpoint**
```powershell
cd C:\projects\manual_slop; git status --short
```
- [ ] **Step 1.2: Read the test**
Read `tests/test_workspace_profiles_sim.py` to see the current `time.sleep` calls.
- [ ] **Step 1.3: Add the polling helpers at the top of the file**
After the existing imports, add:
```python
import time
def wait_for_save_completion(client, profile_name, timeout=5.0):
"""Poll until the saved profile appears in the workspace profiles."""
deadline = time.time() + timeout
while time.time() < deadline:
profiles = client.get_value('workspace_profiles') or {}
if profile_name in profiles:
return
time.sleep(0.1)
raise TimeoutError(f"Profile '{profile_name}' did not appear in workspace_profiles within {timeout}s")
def wait_for_load_completion(client, item, expected, timeout=5.0):
"""Poll until the item's value matches expected."""
deadline = time.time() + timeout
while time.time() < deadline:
if client.get_value(item) == expected:
return
time.sleep(0.1)
raise TimeoutError(f"Item '{item}' did not become {expected!r} within {timeout}s")
```
Use exactly 1-space indentation. No comments.
- [ ] **Step 1.4: Replace the `time.sleep` calls**
In the test body, replace:
- `time.sleep(2.0)` after `save_workspace_profile``wait_for_save_completion(client, "test_restore")`
- `time.sleep(2.0)` after `load_workspace_profile``wait_for_load_completion(client, 'ui_separate_tier1', True)`
- The other `time.sleep(1.0)` calls after `set_value` can stay (set_value is synchronous in the controller) OR be replaced with `wait_for_load_completion` for consistency.
**Recommended:** keep the `set_value` sleeps for now (set_value writes to controller synchronously; the sleep is for the GUI to process the change), but replace the save/load ones.
- [ ] **Step 1.5: Run the test**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_workspace_profiles_sim.py -v --timeout=30
```
Expected: 1 passed.
- [ ] **Step 1.6: Commit**
```powershell
cd C:\projects\manual_slop; git add tests/test_workspace_profiles_sim.py
git -C C:\projects\manual_slop commit -m "test(workspace_profiles): replace time.sleep with wait_for_X polling helpers"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Replaced time.sleep(2.0) with wait_for_save_completion and wait_for_load_completion polling helpers. 100ms poll interval, 5s default timeout. Per the Authoring Robust live_gui Tests rules in docs/guide_testing.md: use wait-for-ready pattern, not fixed sleeps." $h
```
---
## Task 2: Migrate `test_auto_switch_sim.py`
**Files:**
- Modify: `tests/test_auto_switch_sim.py`
- [ ] **Step 2.1: Read the test**
Read `tests/test_auto_switch_sim.py` to see the current `time.sleep` calls.
- [ ] **Step 2.2: Add the polling helpers at the top of the file**
Same as Task 1 Step 1.3 (or import from a shared location if extracted in the future).
- [ ] **Step 2.3: Replace the `time.sleep(1)` calls after each `trigger_tier(...)` call**
The test triggers a tier-2 then tier-3 transition. After each trigger, wait for `show_windows['Diagnostics']` to reach the expected value:
```python
trigger_tier('Tier 2 (Tech Lead)')
wait_for_load_completion(client, 'show_windows', {'Diagnostics': False})
assert client.get_value('show_windows').get('Diagnostics', False) == False
trigger_tier('Tier 3 (Worker): task-1')
wait_for_load_completion(client, 'show_windows', {'Diagnostics': True})
assert client.get_value('show_windows').get('Diagnostics', False) == True
```
- [ ] **Step 2.4: Run the test**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_auto_switch_sim.py -v --timeout=60
```
Expected: 1 passed.
- [ ] **Step 2.5: Commit**
```powershell
cd C:\projects\manual_slop; git add tests/test_auto_switch_sim.py
git -C C:\projects\manual_slop commit -m "test(auto_switch): replace time.sleep with wait_for_load_completion polling"
$h = git -C C:\projects\manual_slop log -1 --format='%H'
git -C C:\projects\manual_slop notes add -m "Replaced time.sleep(1) after each trigger_tier with wait_for_load_completion. The auto-switch applies a workspace profile; the test now polls until the expected show_windows state is observed." $h
```
---
## Task 3: Verify both tests pass in the full batched suite
**Files:** (no file changes; verification only)
- [ ] **Step 3.1: Run both tests**
```powershell
cd C:\projects\manual_slop; uv run pytest tests/test_workspace_profiles_sim.py tests/test_auto_switch_sim.py -v --timeout=60
```
Expected: 2 passed.
- [ ] **Step 3.2: Commit (no-op)**
```powershell
cd C:\projects\manual_slop; git -c core.autocrlf=false commit --allow-empty -m "verify: wait_for_ready migration unblocks 2 tests"
```
---
## Task 4: Update tracks.md
**Files:**
- Modify: `conductor/tracks.md`
- [ ] **Step 4.1: Add a brief note**
Find the live_gui_test_hardening_v2 entry and add: "Sub-track `wait_for_ready_test_pattern_20260605` complete: time.sleep replaced with polling helpers in test_workspace_profiles_sim and test_auto_switch_sim."
- [ ] **Step 4.2: Commit**
```powershell
cd C:\projects\manual_slop; git add conductor/tracks.md
git -C C:\projects\manual_slop commit -m "conductor: wait_for_ready_test_pattern sub-track complete"
```
---
## Self-Review
- **Spec coverage:** 2 tests migrated; polling helpers defined; fixed sleeps replaced.
- **Placeholders:** None.
- **Type consistency:** Polling helpers return None on success, raise TimeoutError on failure. Test assertions unchanged.
- **Risk:** Low — only test files change.
---
## Execution Handoff
Inline execution. 4 tasks, atomic commits. User runs the full batched suite to confirm.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff