hopefully done refining
This commit is contained in:
@@ -2,38 +2,27 @@
|
||||
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 1: Foundation & Research
|
||||
Focus: Verify existing infrastructure and set up panel structure
|
||||
## Phase 1: Verify Existing Infrastructure
|
||||
Focus: Confirm ai_client.get_gemini_cache_stats() works
|
||||
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- Run `activate_skill mma-orchestrator` before starting
|
||||
|
||||
- [ ] Task 1.2: Verify ai_client.get_gemini_cache_stats()
|
||||
- WHERE: `src/ai_client.py` lines ~200-230
|
||||
- WHAT: Confirm function exists and returns expected dict structure
|
||||
- [ ] Task 1.2: Verify get_gemini_cache_stats()
|
||||
- WHERE: `src/ai_client.py`
|
||||
- WHAT: Confirm function exists and returns expected dict
|
||||
- HOW: Use `manual-slop_py_get_definition` on `get_gemini_cache_stats`
|
||||
- OUTPUT: Document exact return dict structure in task notes
|
||||
|
||||
- [ ] Task 1.3: Verify ai_client.cleanup()
|
||||
- WHERE: `src/ai_client.py` line ~220
|
||||
- WHAT: Confirm function clears `_gemini_cache`
|
||||
- HOW: Use `manual-slop_py_get_definition` on `cleanup`
|
||||
- SAFETY: Note that cleanup() also clears other resources - document side effects
|
||||
|
||||
## Phase 2: Panel Implementation
|
||||
Focus: Create the GUI panel structure
|
||||
Focus: Create cache panel in GUI
|
||||
|
||||
- [ ] Task 2.1: Add cache panel state variables
|
||||
- WHERE: `src/gui_2.py` in `App.__init__` (around line 170)
|
||||
- WHAT: Add minimal state if needed (likely none - read directly from ai_client)
|
||||
- HOW: Follow existing state initialization pattern
|
||||
- CODE STYLE: 1-space indentation
|
||||
- SAFETY: No locks needed - read-only access to ai_client globals
|
||||
- [ ] Task 2.1: Add cache panel state (if needed)
|
||||
- WHERE: `src/gui_2.py` `App.__init__`
|
||||
- WHAT: Minimal state for display
|
||||
- HOW: Likely none needed - read directly from ai_client
|
||||
|
||||
- [ ] Task 2.2: Create _render_cache_panel() method
|
||||
- WHERE: `src/gui_2.py` after other `_render_*_panel()` methods
|
||||
- WHAT: New method that displays cache statistics
|
||||
- HOW:
|
||||
- WHERE: `src/gui_2.py` after other render methods
|
||||
- WHAT: Display cache statistics
|
||||
- HOW:
|
||||
```python
|
||||
def _render_cache_panel(self) -> None:
|
||||
if self.current_provider != "gemini":
|
||||
@@ -41,104 +30,50 @@ Focus: Create the GUI panel structure
|
||||
if not imgui.collapsing_header("Cache Analytics"):
|
||||
return
|
||||
stats = ai_client.get_gemini_cache_stats()
|
||||
# Render stats...
|
||||
if not stats.get("cache_exists"):
|
||||
imgui.text("No active cache")
|
||||
return
|
||||
imgui.text(f"Age: {self._format_age(stats.get('cache_age_seconds', 0))}")
|
||||
imgui.text(f"TTL: {stats.get('ttl_remaining', 0):.0f}s remaining")
|
||||
# Progress bar for TTL
|
||||
ttl_pct = stats.get('ttl_remaining', 0) / stats.get('ttl_seconds', 3600)
|
||||
imgui.progress_bar(ttl_pct)
|
||||
```
|
||||
|
||||
- [ ] Task 2.3: Add helper for age formatting
|
||||
- WHERE: `src/gui_2.py`
|
||||
- HOW:
|
||||
```python
|
||||
def _format_age(self, seconds: float) -> str:
|
||||
if seconds < 60:
|
||||
return f"{seconds:.0f}s"
|
||||
elif seconds < 3600:
|
||||
return f"{seconds/60:.0f}m {seconds%60:.0f}s"
|
||||
else:
|
||||
return f"{seconds/3600:.0f}h {(seconds%3600)/60:.0f}m"
|
||||
```
|
||||
- CODE STYLE: 1-space indentation, NO COMMENTS
|
||||
|
||||
- [ ] Task 2.3: Integrate panel into main GUI
|
||||
- WHERE: `src/gui_2.py` in `_gui_func()` method
|
||||
- WHAT: Call `_render_cache_panel()` in appropriate location
|
||||
- HOW: Add near token budget panel or in settings area
|
||||
- SAFETY: Ensure not called during modal dialogs
|
||||
## Phase 3: Manual Controls
|
||||
Focus: Add cache clear button
|
||||
|
||||
## Phase 3: Statistics Display
|
||||
Focus: Implement the visual statistics rendering
|
||||
|
||||
- [ ] Task 3.1: Implement cache status display
|
||||
- WHERE: `src/gui_2.py` in `_render_cache_panel()`
|
||||
- WHAT: Show cache existence and age
|
||||
- HOW:
|
||||
- `imgui.text(f"Cache Active: {stats['cache_exists']}")`
|
||||
- `imgui.text(f"Age: {format_age(stats['cache_age_seconds'])}")`
|
||||
- HELPER: Create `format_age(seconds: float) -> str` helper
|
||||
- CODE STYLE: 1-space indentation
|
||||
|
||||
- [ ] Task 3.2: Implement TTL countdown display
|
||||
- WHERE: `src/gui_2.py` in `_render_cache_panel()`
|
||||
- WHAT: Show remaining TTL with percentage
|
||||
- HOW:
|
||||
- `remaining = stats['ttl_remaining']`
|
||||
- `percentage = (remaining / stats['ttl_seconds']) * 100`
|
||||
- Use `imgui.progress_bar()` for visual
|
||||
- VISUAL: Warning color when percentage < 20% (use `vec4` colors defined at top of file)
|
||||
|
||||
- [ ] Task 3.3: Implement requests counter (optional enhancement)
|
||||
- WHERE: `src/gui_2.py` in `_render_cache_panel()`
|
||||
- WHAT: Show estimated "requests while cache active"
|
||||
- HOW: Add `self._cache_request_count: int = 0` state, increment on send
|
||||
- SAFETY: This requires hooking into send flow - may skip if complex
|
||||
|
||||
## Phase 4: Manual Controls
|
||||
Focus: Implement the cache clear functionality
|
||||
|
||||
- [ ] Task 4.1: Add clear cache button
|
||||
- WHERE: `src/gui_2.py` in `_render_cache_panel()`
|
||||
- WHAT: Button that clears the Gemini cache
|
||||
- [ ] Task 3.1: Add clear cache button
|
||||
- WHERE: `src/gui_2.py` `_render_cache_panel()`
|
||||
- HOW:
|
||||
```python
|
||||
if imgui.button("Clear Cache"):
|
||||
ai_client.cleanup()
|
||||
# Optionally set a flag to show "Cache cleared" message
|
||||
self._cache_cleared = True
|
||||
if getattr(self, '_cache_cleared', False):
|
||||
imgui.text_colored(vec4(100, 255, 100, 255), "Cache cleared - will rebuild on next request")
|
||||
```
|
||||
- SAFETY: `cleanup()` clears ALL caches, not just Gemini - document this
|
||||
|
||||
- [ ] Task 4.2: Add clear confirmation/feedback
|
||||
- WHERE: `src/gui_2.py` in `_render_cache_panel()`
|
||||
- WHAT: Show feedback after clear
|
||||
- HOW: Add `self._cache_just_cleared: bool = False` flag, show message, auto-clear after 3s
|
||||
- CODE STYLE: 1-space indentation
|
||||
## Phase 4: Integration
|
||||
Focus: Add panel to main GUI
|
||||
|
||||
- [ ] Task 4.1: Integrate panel into layout
|
||||
- WHERE: `src/gui_2.py` `_gui_func()`
|
||||
- WHAT: Call `_render_cache_panel()` in settings or token budget area
|
||||
|
||||
## Phase 5: Testing
|
||||
Focus: Verify all functionality works correctly
|
||||
|
||||
- [ ] Task 5.1: Write unit tests for cache panel
|
||||
- WHERE: `tests/test_cache_panel.py` (new file)
|
||||
- WHAT: Test panel visibility, stats display, clear button
|
||||
- HOW: Use `mock_app` or `app_instance` fixture from `conftest.py`
|
||||
- PATTERN: Follow `test_gui_phase4.py` as reference
|
||||
- CODE STYLE: 1-space indentation
|
||||
|
||||
- [ ] Task 5.2: Write integration test with live_gui
|
||||
- WHERE: `tests/test_cache_panel.py`
|
||||
- WHAT: Test with actual Gemini provider (or mock)
|
||||
- HOW: Use `live_gui` fixture, set provider to 'gemini', verify panel shows
|
||||
- ARTIFACTS: Write to `tests/artifacts/`
|
||||
|
||||
- [ ] Task 5.3: Conductor - Phase Verification
|
||||
- Run targeted tests: `uv run pytest tests/test_cache_panel.py -v`
|
||||
- Verify no lint errors in modified files
|
||||
- Manual visual verification in GUI
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Thread Safety Analysis
|
||||
- `ai_client.get_gemini_cache_stats()` reads `_gemini_cache` and `_gemini_cache_created_at`
|
||||
- These are set on the asyncio worker thread during `_send_gemini()`
|
||||
- Reading from GUI thread is safe (atomic types) but values may be slightly stale
|
||||
- No locks required for display purposes
|
||||
|
||||
### Code Style Checklist
|
||||
- [ ] 1-space indentation throughout
|
||||
- [ ] CRLF line endings on Windows
|
||||
- [ ] No comments unless explicitly documenting complex logic
|
||||
- [ ] Type hints on all new functions
|
||||
- [ ] Follow existing `vec4` color naming pattern
|
||||
|
||||
### Files Modified
|
||||
- `src/gui_2.py`: Add `_render_cache_panel()`, integrate into `_gui_func()`
|
||||
- `tests/test_cache_panel.py`: New test file
|
||||
|
||||
### Dependencies
|
||||
- Existing: `src/ai_client.py::get_gemini_cache_stats()`
|
||||
- Existing: `src/ai_client.py::cleanup()`
|
||||
- Existing: `imgui_bundle::imgui`
|
||||
- [ ] Task 5.1: Write unit tests
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,29 +1,66 @@
|
||||
# Implementation Plan: Kill/Abort Workers (kill_abort_workers_20260306)
|
||||
# Implementation Plan: Kill/Abort Running Workers (kill_abort_workers_20260306)
|
||||
|
||||
## Phase 1: Kill Mechanism
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Add thread termination
|
||||
- WHERE: src/multi_agent_conductor.py
|
||||
- WHAT: Kill specific worker thread
|
||||
- HOW: threading.Event or similar
|
||||
- SAFETY: Clean resource release
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 2: UI Integration
|
||||
- [ ] Task: Add kill buttons
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Button per worker
|
||||
- HOW: In worker panel
|
||||
- [ ] Task: Add confirmation dialog
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Confirm before kill
|
||||
- HOW: imgui popup
|
||||
## Phase 1: Thread Tracking
|
||||
Focus: Track active worker threads
|
||||
|
||||
## Phase 3: Status Updates
|
||||
- [ ] Task: Update worker status
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Reflect killed state
|
||||
- HOW: Status variable
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Add worker tracking dict to ConductorEngine
|
||||
- WHERE: `src/multi_agent_conductor.py` `ConductorEngine.__init__`
|
||||
- WHAT: Dict to track active workers
|
||||
- HOW:
|
||||
```python
|
||||
self._active_workers: dict[str, threading.Thread] = {}
|
||||
self._abort_events: dict[str, threading.Event] = {}
|
||||
```
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Task: Test kill functionality
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
## Phase 2: Abort Mechanism
|
||||
Focus: Add abort signal to workers
|
||||
|
||||
- [ ] Task 2.1: Create abort event per ticket
|
||||
- WHERE: `src/multi_agent_conductor.py` before spawning worker
|
||||
- WHAT: Create threading.Event for abort
|
||||
- HOW: `self._abort_events[ticket.id] = threading.Event()`
|
||||
|
||||
- [ ] Task 2.2: Check abort in worker lifecycle
|
||||
- WHERE: `src/multi_agent_conductor.py` `run_worker_lifecycle()`
|
||||
- WHAT: Check abort event between operations
|
||||
- HOW:
|
||||
```python
|
||||
abort_event = engine._abort_events.get(ticket.id)
|
||||
if abort_event and abort_event.is_set():
|
||||
ticket.status = "killed"
|
||||
return
|
||||
```
|
||||
|
||||
## Phase 3: Kill Button UI
|
||||
Focus: Add kill button to GUI
|
||||
|
||||
- [ ] Task 3.1: Add kill button per worker
|
||||
- WHERE: `src/gui_2.py` MMA dashboard
|
||||
- WHAT: Button to kill specific worker
|
||||
- HOW:
|
||||
```python
|
||||
for ticket_id, thread in engine._active_workers.items():
|
||||
if thread.is_alive():
|
||||
if imgui.button(f"Kill {ticket_id}"):
|
||||
engine.kill_worker(ticket_id)
|
||||
```
|
||||
|
||||
- [ ] Task 3.2: Implement kill_worker method
|
||||
- WHERE: `src/multi_agent_conductor.py`
|
||||
- WHAT: Set abort event and wait for termination
|
||||
- HOW:
|
||||
```python
|
||||
def kill_worker(self, ticket_id: str) -> None:
|
||||
if ticket_id in self._abort_events:
|
||||
self._abort_events[ticket_id].set()
|
||||
if ticket_id in self._active_workers:
|
||||
self._active_workers[ticket_id].join(timeout=2.0)
|
||||
del self._active_workers[ticket_id]
|
||||
```
|
||||
|
||||
## Phase 4: Testing
|
||||
- [ ] Task 4.1: Write unit tests
|
||||
- [ ] Task 4.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,32 +1,58 @@
|
||||
# Implementation Plan: Manual Block/Unblock Control (manual_block_control_20260306)
|
||||
|
||||
## Phase 1: Block Mechanism
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Add manual block field
|
||||
- WHERE: src/models.py
|
||||
- WHAT: Add manual_block_reason to Ticket
|
||||
- HOW: Optional[str]
|
||||
- [ ] Task: Add block/unblock methods
|
||||
- WHERE: src/models.py
|
||||
- WHAT: mark_blocked, mark_unblocked
|
||||
- HOW: Set/unset flag
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 2: UI Controls
|
||||
- [ ] Task: Add block button
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Block selected ticket
|
||||
- HOW: Button with input
|
||||
- [ ] Task: Add unblock button
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Remove block
|
||||
- HOW: Button
|
||||
## Phase 1: Add Manual Block Fields
|
||||
Focus: Add manual_block flag to Ticket
|
||||
|
||||
## Phase 3: Visualization
|
||||
- [ ] Task: Show block status
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Visual indicator for blocked
|
||||
- HOW: Different color/icon
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Add manual_block field to Ticket
|
||||
- WHERE: `src/models.py` `Ticket` dataclass
|
||||
- WHAT: Add `manual_block: bool = False`
|
||||
- HOW:
|
||||
```python
|
||||
manual_block: bool = False
|
||||
```
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Test block/unblock
|
||||
- [ ] Conductor - Phase Verification
|
||||
- [ ] Task 1.3: Add mark_manual_block method
|
||||
- WHERE: `src/models.py` `Ticket`
|
||||
- WHAT: Method to set manual block with reason
|
||||
- HOW:
|
||||
```python
|
||||
def mark_manual_block(self, reason: str) -> None:
|
||||
self.status = "blocked"
|
||||
self.blocked_reason = f"[MANUAL] {reason}"
|
||||
self.manual_block = True
|
||||
```
|
||||
|
||||
## Phase 2: Block/Unblock UI
|
||||
Focus: Add block buttons to ticket display
|
||||
|
||||
- [ ] Task 2.1: Add block button
|
||||
- WHERE: `src/gui_2.py` ticket rendering
|
||||
- WHAT: Button to block with reason input
|
||||
- HOW: Modal with text input for reason
|
||||
|
||||
- [ ] Task 2.2: Add unblock button
|
||||
- WHERE: `src/gui_2.py` ticket rendering
|
||||
- WHAT: Button to clear manual block
|
||||
- HOW:
|
||||
```python
|
||||
if ticket.manual_block and ticket.status == "blocked":
|
||||
if imgui.button("Unblock"):
|
||||
ticket.status = "todo"
|
||||
ticket.blocked_reason = None
|
||||
ticket.manual_block = False
|
||||
```
|
||||
|
||||
## Phase 3: Cascade Integration
|
||||
Focus: Trigger cascade on block/unblock
|
||||
|
||||
- [ ] Task 3.1: Call cascade_blocks after manual block
|
||||
- WHERE: `src/gui_2.py` or `src/multi_agent_conductor.py`
|
||||
- WHAT: Update downstream tickets
|
||||
- HOW: `self.dag.cascade_blocks()`
|
||||
|
||||
## Phase 4: Testing
|
||||
- [ ] Task 4.1: Write unit tests
|
||||
- [ ] Task 4.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,202 +1,141 @@
|
||||
# Implementation Plan: Manual Skeleton Context Injection (manual_skeleton_injection_20260306)
|
||||
|
||||
## Phase 1: File Selection UI
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Implement file picker
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Browse files for skeleton
|
||||
- HOW: imgui.file_picker or custom
|
||||
- SAFETY: Path validation
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 2: Skeleton Preview
|
||||
- [ ] Task: Generate preview
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Show skeleton before inject
|
||||
- HOW: Call skeleton generation
|
||||
- SAFETY: Handle large files
|
||||
- [ ] Task: Implement inject button
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Inject into discussion
|
||||
- HOW: Append to context
|
||||
## Phase 1: UI Foundation
|
||||
Focus: Add file injection button and state
|
||||
|
||||
## Phase 3: Full Read Option
|
||||
- [ ] Task: Add full read toggle
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Option for full file
|
||||
- HOW: Checkbox/switch
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- Run `activate_skill mma-orchestrator` before starting
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Task: Test injection flow
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
## Phase 1: Skeleton Preview
|
||||
- [ ] Task: Implement inject button
|
||||
- WHERE: src/gui_2.py discussion panel
|
||||
- how: Call ASTParser.get_skeleton() for preview
|
||||
- Display preview
|
||||
- on preview change, regenerate skeleton via `get_skeleton()`
|
||||
|
||||
- Show "Skeleton" or "Full" toggle in preview
|
||||
- on "Inject", append to discussion input
|
||||
- [ ] Task: Add toggle logic
|
||||
- where: src/gui_2.py
|
||||
- how: imgui.checkbox to toggle between skeleton/full file
|
||||
- On toggle change, regenerate skeleton
|
||||
- else:
|
||||
self._skeleton_mode = True
|
||||
self._render_full_content = True
|
||||
- code style: 1-space indentation
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
- Run targeted tests: `uv run pytest tests/test_manual_skeleton_injection.py -v`
|
||||
- Verify skeleton preview works
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
- Run: `uv run pytest tests/test_manual_skeleton_injection.py -v`
|
||||
- Verify skeleton preview works
|
||||
- [ ] Task: Write unit tests for skeleton generation
|
||||
- where: `tests/test_manual_skeleton_injection.py` (new file)
|
||||
- what: Test extraction returns only specified functions
|
||||
- how: Parse code, with regex, extract names matching `function_names` list
|
||||
- return combined signatures + docstrings
|
||||
- else:
|
||||
return []
|
||||
- Include `@core_logic` decorator, (if present)
|
||||
- [ ] Task: Write integration test
|
||||
- where: `tests/test_manual_skeleton_injection.py` (new file)
|
||||
- what: Test targeted extraction returns only specified functions
|
||||
- how: Use existing `AST.parse via `tree_sitter_python`
|
||||
- Return extracted nodes
|
||||
- Also add curated view (hot paths) and
|
||||
- In `run_worker_lifecycle`,: check if ticket is in context_requirements and call `get_curated_view()` for full content (hot paths).
|
||||
- [ ] Task: Write integration test
|
||||
- where: `tests/test_context_pruning.py` (new file)
|
||||
- what: Test integration of worker lifecycle with curated context
|
||||
- how: Create test file with known functions, verify they're extracted
|
||||
- [ ] Task: Write integration test
|
||||
- where: `tests/test_context_pruning.py` (new file)
|
||||
- what: Test integration of curated view
|
||||
- how: Use `aggregate.build_tier3_context()` and
|
||||
- - [ ] Task: Implement performance tests
|
||||
- where: `tests/test_performance_monitor.py` (new file)
|
||||
- what: Test Performance monitor history storage
|
||||
- how: Add `_history: deque with maxlen=100
|
||||
- in `end_frame()` store metrics
|
||||
- [ ] Task: Implement graph rendering
|
||||
- where: src/gui_2.py diagnostics panel
|
||||
- how: Get CPU/RAM data from `performance_monitor.get_history()`
|
||||
- Render graphs using imgui.plot_lines() or imgui.plot_histogram()
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
- Run: `uv run pytest tests/test_manual_skeleton_injection.py -v`
|
||||
- Verify skeleton preview works
|
||||
- [ ] Task: Write unit tests for performance dashboard
|
||||
- where: `tests/test_performance_dashboard.py` (new file)
|
||||
- what: Test performance dashboard
|
||||
- how: Test history storage limits
|
||||
- [x] Performance_monitor.add_history() if value is None: self._history = deque(maxlen=100)
|
||||
self._history = data
|
||||
imgui.plot_lines("CPU", data)
|
||||
imgui.plot_histogram("Frame Time", data)
|
||||
imgui.text("History: N points")
|
||||
imgui.end()
|
||||
imgui.text("Session Stats")
|
||||
imgui.text(f"Total: ${self._session_cost_total:.2f}")
|
||||
imgui.end_child()
|
||||
|
||||
# Token timeline
|
||||
time.sleep(0.5) # Check for slow operations
|
||||
time.sleep(0.5)
|
||||
for i, range(len(tickets):
|
||||
t = t. completed
|
||||
t.status = "completed"
|
||||
else:
|
||||
t.status = "in_progress"
|
||||
|
||||
# Session stats header
|
||||
if imgui.collapsing_header("Session Statistics"):
|
||||
imgui.text_wrapped(f"Tokens: {sum(t['input'] + t['output']):,}")
|
||||
imgui.text(f"Cost projection: ${cost_tracker.estimate_cost(t['model'], t['input'], t['output']) * 1.0:.4f}")
|
||||
|
||||
# Cost projection
|
||||
burn_rate = tokens_per minute * sum(t['input'] / t['output'] * 60)
|
||||
time.sleep =(session - now)
|
||||
projected_remaining = time = now() - remaining_time
|
||||
- [ ] Task 1.2: Add injection state variables
|
||||
- WHERE: `src/gui_2.py` `App.__init__`
|
||||
- WHAT: State for injection UI
|
||||
- HOW:
|
||||
```python
|
||||
self._inject_file_path: str = ""
|
||||
self._inject_mode: str = "skeleton" # "skeleton" | "full"
|
||||
self._inject_preview: str = ""
|
||||
self._show_inject_modal: bool = False
|
||||
```
|
||||
- CODE STYLE: 1-space indentation
|
||||
|
||||
- [ ] Task 1.3: Add inject button to discussion panel
|
||||
- WHERE: `src/gui_2.py` discussion panel
|
||||
- WHAT: Button to open injection modal
|
||||
- HOW:
|
||||
```python
|
||||
if imgui.button("Inject File"):
|
||||
self._show_inject_modal = True
|
||||
```
|
||||
|
||||
## Phase 2: File Selection
|
||||
Focus: File picker and path validation
|
||||
|
||||
- [ ] Task 2.1: Create file selection modal
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Modal for selecting project file
|
||||
- HOW:
|
||||
```python
|
||||
if self._show_inject_modal:
|
||||
imgui.open_popup("Inject File")
|
||||
if imgui.begin_popup_modal("Inject File"):
|
||||
# File list from project files
|
||||
for file_path in self.project.get("files", {}).get("paths", []):
|
||||
if imgui.selectable(file_path, self._inject_file_path == file_path):
|
||||
self._inject_file_path = file_path
|
||||
self._update_inject_preview()
|
||||
imgui.end_popup()
|
||||
```
|
||||
|
||||
- [ ] Task 2.2: Validate selected path
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Ensure path is within project
|
||||
- HOW: Check against `files.base_dir`
|
||||
|
||||
## Phase 3: Preview Generation
|
||||
Focus: Generate and display skeleton/full preview
|
||||
|
||||
- [ ] Task 3.1: Implement preview update function
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Generate preview based on mode
|
||||
- HOW:
|
||||
```python
|
||||
def _update_inject_preview(self) -> None:
|
||||
if not self._inject_file_path:
|
||||
self._inject_preview = ""
|
||||
return
|
||||
base_dir = self.project.get("files", {}).get("base_dir", ".")
|
||||
full_path = Path(base_dir) / self._inject_file_path
|
||||
try:
|
||||
content = full_path.read_text(encoding="utf-8")
|
||||
if self._inject_mode == "skeleton":
|
||||
parser = ASTParser("python")
|
||||
self._inject_preview = parser.get_skeleton(content)
|
||||
else:
|
||||
imgui.text(f"Projected cost: ${projected_cost(t['model'], t['input'], t['output']) * 1.0:.4f}")
|
||||
|
||||
imgui.end_child()
|
||||
|
||||
# Efficiency Score
|
||||
efficiency_score = tokens_per useful change ratio (if > 0 else 0)
|
||||
efficiency_text = summary
|
||||
imgui.text_wrapped(f"Efficiency Score: {efficiency_score:.2f}")
|
||||
imgui.end_child()
|
||||
|
||||
# Session summary
|
||||
if imgui.collapsing_header("Session Summary"):
|
||||
imgui.text_wrapped("## Session")
|
||||
- **Total**:** tokens
|
||||
- **Completed:** status: {len(completed)} tickets
|
||||
- **Blocked:** tickets marked as blocked with reason: {reason}
|
||||
for t_status in self.track.tickets:
|
||||
if t.blocked_reason:
|
||||
imgui.text_wrapped(f" Blocked: {reason}")
|
||||
imgui.text(f"Tickets blocked: {len(blocked)} tickets")
|
||||
imgui.end()
|
||||
|
||||
# Controls
|
||||
imgui.separator()
|
||||
imgui.text("Controls")
|
||||
|
||||
# Per-ticket block controls
|
||||
imgui.combo with options: block, execute, skip
|
||||
block
|
||||
else:
|
||||
imgui.text("Unblocked")
|
||||
imgui.indent_same_indent()
|
||||
|
||||
# Priority field
|
||||
imgui.combo("priority", options: high/medium/low)
|
||||
if imgui.begin_combo("Priority", item=0):
|
||||
imgui.end_combo()
|
||||
elif ticket.status == "completed":
|
||||
imgui.text_wrapped(f" Completed")
|
||||
elif ticket.status == "blocked":
|
||||
imgui.text_wrapped(f" blocked: {reason}")
|
||||
imgui.indent_same_indent()
|
||||
imgui.end()
|
||||
|
||||
# Footer
|
||||
imgui.text_wrapped("Session Stats")
|
||||
|
||||
# Tier Usage table (if imgui.begin_table("Tier Usage"))
|
||||
{
|
||||
imgui.table_next_row()
|
||||
for tier in ["Tier 1", "Tier 2", "Tier 3", "Tier 4"]:
|
||||
imgui.table_set_column_index(1)
|
||||
imgui.text(f"Tier {tier}")
|
||||
imgui.table_next_row()
|
||||
for ticket in tickets:
|
||||
imgui.text(f" {ticket.id}")
|
||||
imgui.text(f" {ticket.status}")
|
||||
imgui.text(f" {ticket.priority}")
|
||||
imgui.text("")
|
||||
imgui.end_table()
|
||||
|
||||
imgui.text_wrapped(f"Session total: ${self._session_cost_total:.2f}")
|
||||
imgui.end_child()
|
||||
t.end_table()
|
||||
|
||||
imgui.text_wrapped("## Ticket Queue Management")
|
||||
imgui.text("Priority")
|
||||
imgui.combo("priority", options: high/medium/low)
|
||||
imgui.same_line()
|
||||
imgui.text_wrapped(f" {priority}")
|
||||
imgui.end()
|
||||
# Drag-drop reordering
|
||||
imgui.combo("Reorder", options: top/bottom/after")
|
||||
imgui.text("Top")
|
||||
imgui.text("Bottom")
|
||||
else
|
||||
imgui.text("Bulk Actions")
|
||||
imgui.text("Apply to: execute/skip/block")
|
||||
imgui.end_child()
|
||||
self._inject_preview = content
|
||||
# Truncate to 500 lines
|
||||
lines = self._inject_preview.split("\n")[:500]
|
||||
self._inject_preview = "\n".join(lines)
|
||||
if len(lines) >= 500:
|
||||
self._inject_preview += "\n... (truncated)"
|
||||
except Exception as e:
|
||||
self._inject_preview = f"Error reading file: {e}"
|
||||
```
|
||||
|
||||
- [ ] Task 3.2: Add mode toggle
|
||||
- WHERE: `src/gui_2.py` inject modal
|
||||
- WHAT: Radio buttons for skeleton/full
|
||||
- HOW:
|
||||
```python
|
||||
if imgui.radio_button("Skeleton", self._inject_mode == "skeleton"):
|
||||
self._inject_mode = "skeleton"
|
||||
self._update_inject_preview()
|
||||
imgui.same_line()
|
||||
if imgui.radio_button("Full File", self._inject_mode == "full"):
|
||||
self._inject_mode = "full"
|
||||
self._update_inject_preview()
|
||||
```
|
||||
|
||||
- [ ] Task 3.3: Display preview
|
||||
- WHERE: `src/gui_2.py` inject modal
|
||||
- WHAT: Scrollable preview area
|
||||
- HOW:
|
||||
```python
|
||||
imgui.begin_child("preview", height=300)
|
||||
imgui.text_wrapped(self._inject_preview)
|
||||
imgui.end_child()
|
||||
```
|
||||
|
||||
## Phase 4: Inject Action
|
||||
Focus: Append to discussion input
|
||||
|
||||
- [ ] Task 4.1: Implement inject button
|
||||
- WHERE: `src/gui_2.py` inject modal
|
||||
- WHAT: Button to inject content
|
||||
- HOW:
|
||||
```python
|
||||
if imgui.button("Inject"):
|
||||
formatted = f"\n## File: {self._inject_file_path}\n```python\n{self._inject_preview}\n```\n"
|
||||
self.ui_input_text += formatted
|
||||
self._show_inject_modal = False
|
||||
imgui.close_current_popup()
|
||||
imgui.same_line()
|
||||
if imgui.button("Cancel"):
|
||||
self._show_inject_modal = False
|
||||
imgui.close_current_popup()
|
||||
```
|
||||
|
||||
## Phase 5: Testing
|
||||
Focus: Verify all functionality
|
||||
|
||||
- [ ] Task 5.1: Write unit tests
|
||||
- WHERE: `tests/test_skeleton_injection.py` (new file)
|
||||
- WHAT: Test preview generation, truncation
|
||||
- HOW: Create test files, verify skeleton output
|
||||
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
- Run: `uv run pytest tests/test_skeleton_injection.py -v`
|
||||
- Manual: Verify inject modal works in GUI
|
||||
# Footer
|
||||
imgui.end_table()
|
||||
|
||||
@@ -1,36 +1,113 @@
|
||||
# Track Specification: Manual Skeleton Context Injection (manual_skeleton_injection_20260306)
|
||||
|
||||
## Overview
|
||||
Add UI controls to manually flag files for skeleton injection in discussions. Allow agent to request full file reads or specific def/class definitions on-demand.
|
||||
Add UI controls to manually inject file skeletons into discussions. Allow user to preview skeleton content before sending to AI, with option to toggle between skeleton and full file.
|
||||
|
||||
## Current State Audit
|
||||
|
||||
### Already Implemented (DO NOT re-implement)
|
||||
- **`file_cache.ASTParser.get_skeleton()`**: Returns Python skeleton
|
||||
- **`mcp_client.py_get_skeleton()`**: MCP tool for skeleton generation
|
||||
- **`aggregate.py`**: Builds file items context
|
||||
|
||||
### Gaps to Fill
|
||||
- No UI to flag files for skeleton vs full
|
||||
- No preview of skeleton before injection
|
||||
- No on-demand definition lookup
|
||||
#### ASTParser (src/file_cache.py)
|
||||
- **`ASTParser` class**: Uses tree-sitter for Python parsing
|
||||
- **`get_skeleton(code: str) -> str`**: Returns file skeleton (signatures/docstrings preserved, function bodies replaced with `...`)
|
||||
- **`get_curated_view(code: str) -> str`**: Returns curated view preserving `@core_logic` and `# [HOT]` decorated function bodies
|
||||
|
||||
#### MCP Tools (src/mcp_client.py)
|
||||
- **`py_get_skeleton(path, language)`**: Tool #15 - generates skeleton
|
||||
- **`py_get_definition(path, name)`**: Tool #18 - gets specific definition
|
||||
- **Both available to AI during discussion**
|
||||
|
||||
#### Context Building (src/aggregate.py)
|
||||
- **`build_file_items()`**: Creates file items from project config
|
||||
- **`build_tier*_context()`**: Tier-specific context builders already use skeleton logic
|
||||
|
||||
### Gaps to Fill (This Track's Scope)
|
||||
- No UI for manual skeleton preview/injection
|
||||
- No toggle between skeleton and full file
|
||||
- No inject-to-discussion button
|
||||
|
||||
## Architectural Constraints
|
||||
|
||||
### Non-Blocking Preview
|
||||
- Skeleton generation MUST NOT block UI
|
||||
- Use existing `ASTParser.get_skeleton()` - already fast (<100ms)
|
||||
|
||||
### Preview Size Limit
|
||||
- Truncate preview at 500 lines
|
||||
- Show "... (truncated)" notice if exceeded
|
||||
|
||||
## Architecture Reference
|
||||
|
||||
### Key Integration Points
|
||||
|
||||
| File | Lines | Purpose |
|
||||
|------|-------|---------|
|
||||
| `src/gui_2.py` | ~1300-1400 | Discussion panel - add injection UI |
|
||||
| `src/file_cache.py` | 30-80 | `ASTParser.get_skeleton()` |
|
||||
| `src/aggregate.py` | 119-145 | `build_file_items()` |
|
||||
|
||||
### UI Integration Pattern
|
||||
```python
|
||||
# In discussion panel:
|
||||
if imgui.button("Inject File"):
|
||||
# Open file picker
|
||||
self._inject_file_path = selected_path
|
||||
self._inject_mode = "skeleton" # or "full"
|
||||
# Preview in child window
|
||||
preview = ASTParser("python").get_skeleton(content) if skeleton_mode else content
|
||||
# Inject button appends to input text
|
||||
```
|
||||
|
||||
## Functional Requirements
|
||||
- File picker UI in discussion panel
|
||||
- Skeleton preview before injection
|
||||
- Toggle: skeleton vs full file
|
||||
- Uses existing `py_get_skeleton()` tool
|
||||
|
||||
## Key Integration Points
|
||||
| File | Purpose |
|
||||
|-----|---------|
|
||||
| `src/gui_2.py` | File picker, injection UI |
|
||||
| `src/mcp_client.py` | `py_get_skeleton()` |
|
||||
| `src/file_cache.py` | `ASTParser` |
|
||||
### FR1: File Selection
|
||||
- Button "Inject File" in discussion panel
|
||||
- Opens file browser limited to project files
|
||||
- Path validation against project's `files.base_dir`
|
||||
|
||||
### FR2: Mode Toggle
|
||||
- Radio buttons: "Skeleton" / "Full File"
|
||||
- Default: Skeleton
|
||||
- Switching regenerates preview
|
||||
|
||||
### FR3: Preview Display
|
||||
- Child window showing preview content
|
||||
- Monospace font
|
||||
- Scrollable, max 500 lines displayed
|
||||
- Line numbers optional
|
||||
|
||||
### FR4: Inject Action
|
||||
- Button "Inject to Discussion"
|
||||
- Appends content to input text area
|
||||
- Format: `## File: {path}\n\`\`\`python\n{content}\n\`\`\``
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
| Requirement | Constraint |
|
||||
|-------------|------------|
|
||||
| Preview Time | <100ms for typical file |
|
||||
| Memory | Preview limited to 50KB |
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
### Unit Tests
|
||||
- Test skeleton generation for sample files
|
||||
- Test truncation at 500 lines
|
||||
|
||||
### Integration Tests
|
||||
- Inject file, verify appears in discussion
|
||||
- Toggle modes, verify preview updates
|
||||
|
||||
## Out of Scope
|
||||
- Definition lookup (separate track: on_demand_def_lookup)
|
||||
- Multi-file injection
|
||||
- Custom skeleton configuration
|
||||
|
||||
## Acceptance Criteria
|
||||
- [ ] File picker UI functional
|
||||
- [ ] Skeleton preview displays
|
||||
- [ ] Manual refresh button works
|
||||
- [ ] Full read option available
|
||||
- [ ] 1-space indentation
|
||||
- [ ] "Inject File" button in discussion panel
|
||||
- [ ] File browser limits to project files
|
||||
- [ ] Skeleton/Full toggle works
|
||||
- [ ] Preview displays correctly
|
||||
- [ ] Inject appends to input
|
||||
- [ ] Large file truncation works
|
||||
- [ ] 1-space indentation maintained
|
||||
|
||||
@@ -1,31 +1,63 @@
|
||||
# Implementation Plan: MMA Multi-Worker Visualization (mma_multiworker_viz_20260306)
|
||||
|
||||
## Phase 1: Layout Design
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Design multi-pane layout
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Split view for workers
|
||||
- HOW: imgui.columns or child windows
|
||||
- SAFETY: Handle 0 workers
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 2: Worker Tracking
|
||||
- [ ] Task: Track worker status
|
||||
- WHERE: src/multi_agent_conductor.py
|
||||
- WHAT: Per-worker status
|
||||
- HOW: Dictionary of worker states
|
||||
- SAFETY: Thread-safe updates
|
||||
## Phase 1: Stream Structure Enhancement
|
||||
Focus: Extend existing mma_streams for per-worker tracking
|
||||
|
||||
## Phase 3: Controls
|
||||
- [ ] Task: Add kill buttons
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Button per worker to kill
|
||||
- HOW: Signal worker thread
|
||||
- SAFETY: Graceful termination
|
||||
- [ ] Task: Add restart buttons
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Respawn killed worker
|
||||
- HOW: Re-submit ticket
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Review existing mma_streams structure
|
||||
- WHERE: `src/app_controller.py` line 142
|
||||
- WHAT: Current is `Dict[str, str]` - stream_id -> accumulated text
|
||||
- NOTE: Keep this structure, add per-worker metadata separately
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Task: Test multi-worker display
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
## Phase 2: Worker Status Tracking
|
||||
Focus: Track worker status separately
|
||||
|
||||
- [ ] Task 2.1: Add worker status dict
|
||||
- WHERE: `src/app_controller.py`
|
||||
- WHAT: Track status per worker
|
||||
- HOW:
|
||||
```python
|
||||
self._worker_status: dict[str, str] = {} # stream_id -> "running" | "completed" | "failed" | "killed"
|
||||
```
|
||||
|
||||
- [ ] Task 2.2: Update status on worker events
|
||||
- WHERE: `src/app_controller.py` `_process_pending_gui_tasks()`
|
||||
- WHAT: Update status based on mma events
|
||||
- HOW: On "response" event, set status to "completed"
|
||||
|
||||
## Phase 3: Multi-Pane Display
|
||||
Focus: Display all active streams
|
||||
|
||||
- [ ] Task 3.1: Iterate all Tier 3 streams
|
||||
- WHERE: `src/gui_2.py` `_render_tier_stream_panel()`
|
||||
- WHAT: Show all workers in split view
|
||||
- HOW:
|
||||
```python
|
||||
tier3_keys = [k for k in self.mma_streams if "Tier 3" in k]
|
||||
for key in tier3_keys:
|
||||
status = self._worker_status.get(key, "unknown")
|
||||
imgui.text(f"{key}: {status}")
|
||||
if imgui.begin_child(f"stream_{key}", height=150):
|
||||
imgui.text_wrapped(self.mma_streams.get(key, ""))
|
||||
imgui.end_child()
|
||||
```
|
||||
|
||||
## Phase 4: Stream Pruning
|
||||
Focus: Limit memory per stream
|
||||
|
||||
- [ ] Task 4.1: Prune stream on append
|
||||
- WHERE: `src/app_controller.py` stream append logic
|
||||
- WHAT: Limit to 10KB per stream
|
||||
- HOW:
|
||||
```python
|
||||
MAX_STREAM_SIZE = 10 * 1024
|
||||
self.mma_streams[stream_id] += text
|
||||
if len(self.mma_streams[stream_id]) > MAX_STREAM_SIZE:
|
||||
self.mma_streams[stream_id] = self.mma_streams[stream_id][-MAX_STREAM_SIZE:]
|
||||
```
|
||||
|
||||
## Phase 5: Testing
|
||||
- [ ] Task 5.1: Write unit tests
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,27 +1,107 @@
|
||||
# Implementation Plan: Native Orchestrator (native_orchestrator_20260306)
|
||||
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 1: Plan File Operations
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Implement plan.md read/write
|
||||
- WHERE: src/orchestrator_pm.py or new module
|
||||
- WHAT: Parse and write plan.md
|
||||
- HOW: toml/tomllib for parsing
|
||||
- SAFETY: Preserve formatting
|
||||
Focus: Native plan.md read/write
|
||||
|
||||
## Phase 2: Metadata Management
|
||||
- [ ] Task: Implement metadata.json operations
|
||||
- WHERE: src/orchestrator_pm.py
|
||||
- WHAT: Read/write track metadata
|
||||
- HOW: json load/dump
|
||||
- SAFETY: Atomic writes
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Implement read_plan function
|
||||
- WHERE: `src/orchestrator_pm.py` or new `src/native_orchestrator.py`
|
||||
- WHAT: Parse plan.md content
|
||||
- HOW:
|
||||
```python
|
||||
def read_plan(track_id: str, base_dir: str = ".") -> str:
|
||||
plan_path = Path(base_dir) / "conductor" / "tracks" / track_id / "plan.md"
|
||||
if not plan_path.exists():
|
||||
return ""
|
||||
return plan_path.read_text(encoding="utf-8")
|
||||
```
|
||||
|
||||
## Phase 3: Tier Delegation
|
||||
- [ ] Task: Implement in-process delegation
|
||||
- WHERE: src/multi_agent_conductor.py
|
||||
- WHAT: Replace subprocess calls with direct function calls
|
||||
- HOW: Import and call tier functions directly
|
||||
- SAFETY: Proper error propagation
|
||||
- [ ] Task 1.3: Implement write_plan function
|
||||
- WHERE: Same as above
|
||||
- WHAT: Write plan.md content
|
||||
- HOW:
|
||||
```python
|
||||
def write_plan(track_id: str, content: str, base_dir: str = ".") -> None:
|
||||
plan_path = Path(base_dir) / "conductor" / "tracks" / track_id / "plan.md"
|
||||
plan_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
plan_path.write_text(content, encoding="utf-8")
|
||||
```
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Task: Test plan operations
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
- [ ] Task 1.4: Parse task checkboxes
|
||||
- WHERE: Same as above
|
||||
- WHAT: Extract task status from plan
|
||||
- HOW:
|
||||
```python
|
||||
def parse_plan_tasks(content: str) -> list[dict]:
|
||||
tasks = []
|
||||
for line in content.split("\n"):
|
||||
if line.strip().startswith("- ["):
|
||||
checked = "[x]" in line
|
||||
tasks.append({"text": line, "completed": checked})
|
||||
return tasks
|
||||
```
|
||||
|
||||
## Phase 2: Metadata Operations
|
||||
Focus: Native metadata.json management
|
||||
|
||||
- [ ] Task 2.1: Implement read_metadata
|
||||
- WHERE: Same as above
|
||||
- HOW:
|
||||
```python
|
||||
def read_metadata(track_id: str, base_dir: str = ".") -> dict:
|
||||
meta_path = Path(base_dir) / "conductor" / "tracks" / track_id / "metadata.json"
|
||||
if not meta_path.exists():
|
||||
return {}
|
||||
return json.loads(meta_path.read_text(encoding="utf-8"))
|
||||
```
|
||||
|
||||
- [ ] Task 2.2: Implement write_metadata
|
||||
- WHERE: Same as above
|
||||
- HOW:
|
||||
```python
|
||||
def write_metadata(track_id: str, data: dict, base_dir: str = ".") -> None:
|
||||
meta_path = Path(base_dir) / "conductor" / "tracks" / track_id / "metadata.json"
|
||||
meta_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
meta_path.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
||||
```
|
||||
|
||||
## Phase 3: In-Process Tier Delegation
|
||||
Focus: Replace subprocess calls with direct function calls
|
||||
|
||||
- [ ] Task 3.1: Create NativeOrchestrator class
|
||||
- WHERE: `src/native_orchestrator.py` (new file)
|
||||
- WHAT: Class with tier methods
|
||||
- HOW:
|
||||
```python
|
||||
class NativeOrchestrator:
|
||||
def __init__(self, base_dir: str = "."):
|
||||
self.base_dir = Path(base_dir)
|
||||
|
||||
def generate_tickets(self, brief: str) -> list[Ticket]:
|
||||
return conductor_tech_lead.generate_tickets(brief, ...)
|
||||
|
||||
def execute_ticket(self, ticket: Ticket, context: str) -> str:
|
||||
return ai_client.send(context, ticket.description, ...)
|
||||
|
||||
def analyze_error(self, error: str) -> str:
|
||||
return ai_client.run_tier4_analysis(error)
|
||||
```
|
||||
|
||||
- [ ] Task 3.2: Integrate with ConductorEngine
|
||||
- WHERE: `src/multi_agent_conductor.py`
|
||||
- WHAT: Use NativeOrchestrator instead of subprocess
|
||||
- HOW: Import and call methods directly
|
||||
|
||||
## Phase 4: CLI Fallback
|
||||
Focus: Maintain mma_exec.py compatibility
|
||||
|
||||
- [ ] Task 4.1: Update mma_exec.py to use NativeOrchestrator
|
||||
- WHERE: `scripts/mma_exec.py`
|
||||
- WHAT: Thin wrapper around native module
|
||||
- HOW: Import NativeOrchestrator and call methods
|
||||
|
||||
## Phase 5: Testing
|
||||
- [ ] Task 5.1: Write unit tests
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,30 +1,67 @@
|
||||
# Implementation Plan: On-Demand Definition Lookup (on_demand_def_lookup_20260306)
|
||||
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 1: Symbol Parsing
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Implement @syntax parsing
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Detect @symbol in input
|
||||
- HOW: Regex or string match
|
||||
- SAFETY: Handle edge cases
|
||||
Focus: Parse @symbol syntax from user input
|
||||
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Implement @symbol regex parser
|
||||
- WHERE: `src/gui_2.py` in `_send_callback()`
|
||||
- WHAT: Extract @SymbolName patterns
|
||||
- HOW:
|
||||
```python
|
||||
import re
|
||||
def parse_symbols(text: str) -> list[str]:
|
||||
return re.findall(r'@(\w+(?:\.\w+)*)', text)
|
||||
```
|
||||
|
||||
## Phase 2: Definition Retrieval
|
||||
- [ ] Task: Integrate py_get_definition
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Get definition from outline_tool
|
||||
- HOW: Call existing function
|
||||
- SAFETY: Handle not found
|
||||
Focus: Use existing MCP tool to get definitions
|
||||
|
||||
- [ ] Task 2.1: Integrate py_get_definition
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Call MCP tool for each symbol
|
||||
- HOW:
|
||||
```python
|
||||
from src import mcp_client
|
||||
def get_symbol_definition(symbol: str, files: list[str]) -> tuple[str, str] | None:
|
||||
for file_path in files:
|
||||
result = mcp_client.py_get_definition(file_path, symbol)
|
||||
if result and "not found" not in result.lower():
|
||||
return (file_path, result)
|
||||
return None
|
||||
```
|
||||
|
||||
## Phase 3: Inline Display
|
||||
- [ ] Task: Render inline
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Show definition in discussion
|
||||
- HOW: imgui.text with formatting
|
||||
- [ ] Task: Implement navigation
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Click to jump to source
|
||||
- HOW: Store file/line refs
|
||||
Focus: Display definition in discussion
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Task: Test lookup flow
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
- [ ] Task 3.1: Inject definition as context
|
||||
- WHERE: `src/gui_2.py` `_send_callback()`
|
||||
- WHAT: Append definition to message
|
||||
- HOW:
|
||||
```python
|
||||
symbols = parse_symbols(user_message)
|
||||
for symbol in symbols:
|
||||
result = get_symbol_definition(symbol, self.project_files)
|
||||
if result:
|
||||
file_path, definition = result
|
||||
user_message += f"\n\n[Definition: {symbol} from {file_path}]\n```python\n{definition}\n```"
|
||||
```
|
||||
|
||||
## Phase 4: Click Navigation
|
||||
Focus: Allow clicking definition to open file
|
||||
|
||||
- [ ] Task 4.1: Store file/line metadata with definition
|
||||
- WHERE: Discussion entry structure
|
||||
- WHAT: Track source location
|
||||
- HOW: Add to discussion entry dict
|
||||
|
||||
- [ ] Task 4.2: Add click handler
|
||||
- WHERE: `src/gui_2.py` discussion rendering
|
||||
- WHAT: On click, scroll to definition
|
||||
- HOW: Use selectable text with callback
|
||||
|
||||
## Phase 5: Testing
|
||||
- [ ] Task 5.1: Write unit tests for parsing
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,32 +1,82 @@
|
||||
# Implementation Plan: Per-Ticket Model Override (per_ticket_model_20260306)
|
||||
|
||||
## Phase 1: Model Field
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Add override field
|
||||
- WHERE: src/models.py
|
||||
- WHAT: model_override on Ticket
|
||||
- HOW: Optional[str]
|
||||
- [ ] Task: Update ticket creation
|
||||
- WHERE: src/conductor_tech_lead.py
|
||||
- WHAT: Pass override to ticket
|
||||
- HOW: Include in ticket dict
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 2: UI Controls
|
||||
- [ ] Task: Add model dropdown
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Select model per ticket
|
||||
- HOW: imgui.combo with model list
|
||||
- [ ] Task: Add override indicator
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Show override is active
|
||||
- HOW: Different style
|
||||
## Phase 1: Model Override Field
|
||||
Focus: Add field to Ticket dataclass
|
||||
|
||||
## Phase 3: Execution Integration
|
||||
- [ ] Task: Use override at runtime
|
||||
- WHERE: src/multi_agent_conductor.py
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Add model_override to Ticket
|
||||
- WHERE: `src/models.py` `Ticket` dataclass
|
||||
- WHAT: Add optional model override field
|
||||
- HOW:
|
||||
```python
|
||||
@dataclass
|
||||
class Ticket:
|
||||
# ... existing fields ...
|
||||
model_override: Optional[str] = None
|
||||
```
|
||||
|
||||
- [ ] Task 1.3: Update serialization
|
||||
- WHERE: `src/models.py` `Ticket.to_dict()` and `from_dict()`
|
||||
- WHAT: Include model_override
|
||||
- HOW: Add field to dict conversion
|
||||
|
||||
## Phase 2: Model Dropdown UI
|
||||
Focus: Add model selection to ticket display
|
||||
|
||||
- [ ] Task 2.1: Get available models list
|
||||
- WHERE: `src/gui_2.py` or from cost_tracker
|
||||
- WHAT: List of available models
|
||||
- HOW:
|
||||
```python
|
||||
AVAILABLE_MODELS = ["gemini-2.5-flash-lite", "gemini-2.5-flash", "gemini-3.1-pro-preview", "claude-3-5-sonnet", "deepseek-v3"]
|
||||
```
|
||||
|
||||
- [ ] Task 2.2: Add dropdown to ticket UI
|
||||
- WHERE: `src/gui_2.py` ticket rendering
|
||||
- WHAT: Combo for model selection
|
||||
- HOW:
|
||||
```python
|
||||
current_model = ticket.model_override or "Default"
|
||||
if imgui.begin_combo("Model", current_model):
|
||||
if imgui.selectable("Default", ticket.model_override is None):
|
||||
ticket.model_override = None
|
||||
for model in AVAILABLE_MODELS:
|
||||
if imgui.selectable(model, ticket.model_override == model):
|
||||
ticket.model_override = model
|
||||
imgui.end_combo()
|
||||
```
|
||||
|
||||
## Phase 3: Visual Indicator
|
||||
Focus: Show when override is active
|
||||
|
||||
- [ ] Task 3.1: Color-code override tickets
|
||||
- WHERE: `src/gui_2.py` ticket rendering
|
||||
- WHAT: Visual distinction for override
|
||||
- HOW:
|
||||
```python
|
||||
if ticket.model_override:
|
||||
imgui.text_colored(vec4(255, 200, 100, 255), f"[{ticket.model_override}]")
|
||||
```
|
||||
|
||||
## Phase 4: Execution Integration
|
||||
Focus: Use override in worker execution
|
||||
|
||||
- [ ] Task 4.1: Check override in ConductorEngine.run()
|
||||
- WHERE: `src/multi_agent_conductor.py` `run()`
|
||||
- WHAT: Use ticket.model_override if set
|
||||
- HOW: Check and apply
|
||||
- HOW:
|
||||
```python
|
||||
if ticket.model_override:
|
||||
model_name = ticket.model_override
|
||||
else:
|
||||
# Use existing escalation logic
|
||||
models = ["gemini-2.5-flash-lite", "gemini-2.5-flash", "gemini-3.1-pro-preview"]
|
||||
model_idx = min(ticket.retry_count, len(models) - 1)
|
||||
model_name = models[model_idx]
|
||||
```
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Test override works
|
||||
- [ ] Conductor - Phase Verification
|
||||
## Phase 5: Testing
|
||||
- [ ] Task 5.1: Write unit tests
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,26 +1,87 @@
|
||||
# Implementation Plan: Performance Dashboard (performance_dashboard_20260306)
|
||||
|
||||
## Phase 1: Metrics Collection
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Verify performance_monitor
|
||||
- WHERE: src/performance_monitor.py
|
||||
- WHAT: Check existing metrics
|
||||
- HOW: Review get_metrics() output
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 2: Historical Data
|
||||
- [ ] Task: Implement metrics history
|
||||
- WHERE: src/performance_monitor.py
|
||||
## Phase 1: Historical Data Storage
|
||||
Focus: Add history buffer to PerformanceMonitor
|
||||
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Add history deque to PerformanceMonitor
|
||||
- WHERE: `src/performance_monitor.py` `PerformanceMonitor.__init__`
|
||||
- WHAT: Rolling window of metrics
|
||||
- HOW: deque with maxlen
|
||||
- SAFETY: Memory bounded
|
||||
- HOW:
|
||||
```python
|
||||
from collections import deque
|
||||
self._history: deque = deque(maxlen=100)
|
||||
```
|
||||
|
||||
## Phase 3: Visualization
|
||||
- [ ] Task: Render graphs
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Line graphs for CPU/RAM/frame time
|
||||
- HOW: imgui.plot_lines or custom
|
||||
- SAFETY: 60fps during rendering
|
||||
- [ ] Task 1.3: Store metrics each frame
|
||||
- WHERE: `src/performance_monitor.py` `end_frame()`
|
||||
- WHAT: Append current metrics to history
|
||||
- HOW:
|
||||
```python
|
||||
def end_frame(self) -> None:
|
||||
# ... existing code ...
|
||||
self._history.append({
|
||||
"fps": self._fps, "frame_time_ms": self._frame_time_ms,
|
||||
"cpu_percent": self._cpu_percent, "input_lag_ms": self._input_lag_ms
|
||||
})
|
||||
```
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Task: Test graph rendering
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
- [ ] Task 1.4: Add get_history method
|
||||
- WHERE: `src/performance_monitor.py`
|
||||
- HOW:
|
||||
```python
|
||||
def get_history(self) -> list[dict]:
|
||||
return list(self._history)
|
||||
```
|
||||
|
||||
## Phase 2: CPU Graph
|
||||
Focus: Render CPU usage over time
|
||||
|
||||
- [ ] Task 2.1: Extract CPU values from history
|
||||
- WHERE: `src/gui_2.py` diagnostics panel
|
||||
- WHAT: Get CPU% array for plotting
|
||||
- HOW:
|
||||
```python
|
||||
history = self.perf_monitor.get_history()
|
||||
cpu_values = [h["cpu_percent"] for h in history]
|
||||
```
|
||||
|
||||
- [ ] Task 2.2: Render line graph
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: imgui.plot_lines for CPU
|
||||
- HOW:
|
||||
```python
|
||||
if imgui.collapsing_header("CPU Usage"):
|
||||
imgui.plot_lines("##cpu", cpu_values, scale_min=0, scale_max=100)
|
||||
imgui.text(f"Current: {cpu_values[-1]:.1f}%" if cpu_values else "N/A")
|
||||
```
|
||||
|
||||
## Phase 3: Frame Time Histogram
|
||||
Focus: Show frame time distribution
|
||||
|
||||
- [ ] Task 3.1: Bucket frame times
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Categorize into 0-16ms, 16-33ms, 33+ms
|
||||
- HOW:
|
||||
```python
|
||||
buckets = [0, 0, 0] # <16ms, 16-33ms, 33+ms
|
||||
for h in history:
|
||||
ft = h["frame_time_ms"]
|
||||
if ft < 16: buckets[0] += 1
|
||||
elif ft < 33: buckets[1] += 1
|
||||
else: buckets[2] += 1
|
||||
```
|
||||
|
||||
- [ ] Task 3.2: Render histogram
|
||||
- WHERE: `src/gui_2.py`
|
||||
- HOW:
|
||||
```python
|
||||
imgui.plot_histogram("##frametime", buckets)
|
||||
imgui.text("<16ms: {} 16-33ms: {} >33ms: {}".format(*buckets))
|
||||
```
|
||||
|
||||
## Phase 4: Testing
|
||||
- [ ] Task 4.1: Write unit tests
|
||||
- [ ] Task 4.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,32 +1,68 @@
|
||||
# Implementation Plan: Pipeline Pause/Resume (pipeline_pause_resume_20260306)
|
||||
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 1: Pause Mechanism
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Add pause state
|
||||
- WHERE: src/multi_agent_conductor.py
|
||||
- WHAT: Global pause flag
|
||||
- HOW: threading.Event
|
||||
- [ ] Task: Implement pause
|
||||
- WHERE: src/multi_agent_conductor.py
|
||||
- WHAT: Stop worker spawning
|
||||
- HOW: Check pause flag
|
||||
Focus: Add pause event to ConductorEngine
|
||||
|
||||
## Phase 2: Resume Mechanism
|
||||
- [ ] Task: Implement resume
|
||||
- WHERE: src/multi_agent_conductor.py
|
||||
- WHAT: Continue from pause
|
||||
- HOW: Clear flag, restart workers
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Add pause event to ConductorEngine
|
||||
- WHERE: `src/multi_agent_conductor.py` `ConductorEngine.__init__`
|
||||
- WHAT: Threading event for pause control
|
||||
- HOW:
|
||||
```python
|
||||
self._pause_event: threading.Event = threading.Event()
|
||||
```
|
||||
|
||||
## Phase 3: UI
|
||||
- [ ] Task: Add pause button
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Toggle pause state
|
||||
- HOW: imgui.button
|
||||
- [ ] Task: Add visual indicator
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Show paused status
|
||||
- HOW: Banner or icon
|
||||
- [ ] Task 1.3: Check pause in run loop
|
||||
- WHERE: `src/multi_agent_conductor.py` `run()`
|
||||
- WHAT: Wait while paused
|
||||
- HOW:
|
||||
```python
|
||||
while True:
|
||||
if self._pause_event.is_set():
|
||||
time.sleep(0.5)
|
||||
continue
|
||||
# Normal processing...
|
||||
```
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Test pause/resume
|
||||
- [ ] Conductor - Phase Verification
|
||||
## Phase 2: Pause/Resume Methods
|
||||
Focus: Add control methods
|
||||
|
||||
- [ ] Task 2.1: Add pause method
|
||||
- WHERE: `src/multi_agent_conductor.py`
|
||||
- HOW: `self._pause_event.set()`
|
||||
|
||||
- [ ] Task 2.2: Add resume method
|
||||
- WHERE: `src/multi_agent_conductor.py`
|
||||
- HOW: `self._pause_event.clear()`
|
||||
|
||||
## Phase 3: UI Controls
|
||||
Focus: Add pause/resume buttons
|
||||
|
||||
- [ ] Task 3.1: Add pause/resume button
|
||||
- WHERE: `src/gui_2.py` MMA dashboard
|
||||
- WHAT: Toggle button for pause state
|
||||
- HOW:
|
||||
```python
|
||||
is_paused = engine._pause_event.is_set()
|
||||
label = "Resume" if is_paused else "Pause"
|
||||
if imgui.button(label):
|
||||
if is_paused:
|
||||
engine.resume()
|
||||
else:
|
||||
engine.pause()
|
||||
```
|
||||
|
||||
- [ ] Task 3.2: Add visual indicator
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Banner or color when paused
|
||||
- HOW:
|
||||
```python
|
||||
if engine._pause_event.is_set():
|
||||
imgui.text_colored(vec4(255, 200, 100, 255), "PIPELINE PAUSED")
|
||||
```
|
||||
|
||||
## Phase 4: Testing
|
||||
- [ ] Task 4.1: Write unit tests
|
||||
- [ ] Task 4.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,28 +1,94 @@
|
||||
# Implementation Plan: Session Insights (session_insights_20260306)
|
||||
|
||||
## Phase 1: Session Data
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Review session_logger
|
||||
- WHERE: src/session_logger.py
|
||||
- WHAT: Check existing data
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 2: Metrics Calculation
|
||||
- [ ] Task: Implement token timeline
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Token usage over time
|
||||
- HOW: Graph from history
|
||||
- [ ] Task: Implement cost projection
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Estimate remaining budget
|
||||
- HOW: Linear extrapolation
|
||||
## Phase 1: Token Timeline Data
|
||||
Focus: Collect token usage over session
|
||||
|
||||
## Phase 3: UI
|
||||
- [ ] Task: Render insights panel
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Timeline, projection, summary
|
||||
- HOW: imgui widgets
|
||||
- SAFETY: Handle zero session
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Add token history state
|
||||
- WHERE: `src/gui_2.py` or `src/app_controller.py`
|
||||
- WHAT: Track tokens per API call
|
||||
- HOW:
|
||||
```python
|
||||
self._token_history: list[dict] = [] # [{"time": t, "input": n, "output": n, "model": s}, ...]
|
||||
```
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Task: Test insights
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
- [ ] Task 1.3: Record on each API response
|
||||
- WHERE: `src/gui_2.py` in response handler
|
||||
- WHAT: Append to history
|
||||
- HOW:
|
||||
```python
|
||||
# After API call
|
||||
self._token_history.append({
|
||||
"time": time.time(), "input": input_tokens, "output": output_tokens, "model": model
|
||||
})
|
||||
```
|
||||
|
||||
## Phase 2: Token Timeline Visualization
|
||||
Focus: Render token usage graph
|
||||
|
||||
- [ ] Task 2.1: Extract cumulative tokens
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Calculate running totals
|
||||
- HOW:
|
||||
```python
|
||||
cumulative_input = 0
|
||||
cumulative_output = 0
|
||||
input_values = []
|
||||
output_values = []
|
||||
for entry in self._token_history:
|
||||
cumulative_input += entry["input"]
|
||||
cumulative_output += entry["output"]
|
||||
input_values.append(cumulative_input)
|
||||
output_values.append(cumulative_output)
|
||||
```
|
||||
|
||||
- [ ] Task 2.2: Render timeline
|
||||
- WHERE: `src/gui_2.py` session insights panel
|
||||
- HOW:
|
||||
```python
|
||||
if imgui.collapsing_header("Token Timeline"):
|
||||
imgui.plot_lines("Input", input_values)
|
||||
imgui.plot_lines("Output", output_values)
|
||||
imgui.text(f"Total: {cumulative_input + cumulative_output:,} tokens")
|
||||
```
|
||||
|
||||
## Phase 3: Cost Projection
|
||||
Focus: Estimate remaining cost
|
||||
|
||||
- [ ] Task 3.1: Calculate burn rate
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Tokens per minute
|
||||
- HOW:
|
||||
```python
|
||||
if len(self._token_history) >= 2:
|
||||
elapsed = self._token_history[-1]["time"] - self._token_history[0]["time"]
|
||||
total_tokens = sum(e["input"] + e["output"] for e in self._token_history)
|
||||
burn_rate = total_tokens / (elapsed / 60) if elapsed > 0 else 0
|
||||
```
|
||||
|
||||
- [ ] Task 3.2: Display projection
|
||||
- WHERE: `src/gui_2.py`
|
||||
- HOW:
|
||||
```python
|
||||
imgui.text(f"Burn rate: {burn_rate:.0f} tokens/min")
|
||||
imgui.text(f"Session cost: ${session_cost:.4f}")
|
||||
```
|
||||
|
||||
## Phase 4: Efficiency Score
|
||||
Focus: Calculate tokens per useful change
|
||||
|
||||
- [ ] Task 4.1: Define efficiency metric
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Ratio of tokens to completed tickets
|
||||
- HOW:
|
||||
```python
|
||||
completed_tickets = sum(1 for t in self.track.tickets if t.status == "completed")
|
||||
total_tokens = sum(e["input"] + e["output"] for e in self._token_history)
|
||||
efficiency = total_tokens / completed_tickets if completed_tickets > 0 else 0
|
||||
```
|
||||
|
||||
## Phase 5: Testing
|
||||
- [ ] Task 5.1: Write unit tests
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,33 +1,131 @@
|
||||
# Implementation Plan: Manual Ticket Queue Management (ticket_queue_mgmt_20260306)
|
||||
|
||||
## Phase 1: UI Framework
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Add drag-drop support
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Enable ticket reordering via drag
|
||||
- HOW: imgui drag-drop or custom handling
|
||||
- SAFETY: Validate after drop
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 2: Priority System
|
||||
- [ ] Task: Add priority field
|
||||
- WHERE: src/models.py
|
||||
- WHAT: Add priority to Ticket
|
||||
- HOW: Enum (high/med/low)
|
||||
- [ ] Task: Add priority UI
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Dropdown or buttons
|
||||
- HOW: imgui.combo
|
||||
## Phase 1: Priority Field
|
||||
Focus: Add priority to Ticket model
|
||||
|
||||
## Phase 3: Bulk Operations
|
||||
- [ ] Task: Implement multi-select
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Checkbox per ticket
|
||||
- HOW: imgui.checkbox
|
||||
- [ ] Task: Implement bulk actions
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Execute/skip/block buttons
|
||||
- HOW: Apply to selected
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- Run `activate_skill mma-orchestrator` before starting
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Task: Test operations
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
- [ ] Task 1.2: Add priority field to Ticket
|
||||
- WHERE: `src/models.py` `Ticket` dataclass
|
||||
- WHAT: Add `priority: str = "medium"` field
|
||||
- HOW:
|
||||
```python
|
||||
@dataclass
|
||||
class Ticket:
|
||||
# ... existing fields ...
|
||||
priority: str = "medium" # "high" | "medium" | "low"
|
||||
```
|
||||
- CODE STYLE: 1-space indentation
|
||||
|
||||
- [ ] Task 1.3: Update Ticket serialization
|
||||
- WHERE: `src/models.py` `Ticket.to_dict()` and `from_dict()`
|
||||
- WHAT: Include priority in serialization
|
||||
- HOW: Add `priority` to dict conversion
|
||||
|
||||
## Phase 2: Priority UI
|
||||
Focus: Add priority dropdown to ticket display
|
||||
|
||||
- [ ] Task 2.1: Add priority dropdown
|
||||
- WHERE: `src/gui_2.py` ticket rendering
|
||||
- WHAT: Dropdown for priority selection
|
||||
- HOW:
|
||||
```python
|
||||
priorities = ["high", "medium", "low"]
|
||||
current_idx = priorities.index(ticket.priority) if ticket.priority in priorities else 1
|
||||
if imgui.begin_combo("Priority", priorities[current_idx]):
|
||||
for i, p in enumerate(priorities):
|
||||
if imgui.selectable(p, i == current_idx):
|
||||
ticket.priority = p
|
||||
imgui.end_combo()
|
||||
```
|
||||
|
||||
- [ ] Task 2.2: Add color coding
|
||||
- WHERE: `src/gui_2.py` ticket rendering
|
||||
- WHAT: Color-code priority display
|
||||
- HOW:
|
||||
```python
|
||||
priority_colors = {"high": vec4(255, 100, 100, 255), "medium": vec4(255, 200, 100, 255), "low": vec4(150, 150, 150, 255)}
|
||||
imgui.text_colored(priority_colors.get(ticket.priority, vec4(200, 200, 200, 255)), f"[{ticket.priority.upper()}]")
|
||||
```
|
||||
|
||||
## Phase 3: Multi-Select
|
||||
Focus: Enable ticket selection for bulk operations
|
||||
|
||||
- [ ] Task 3.1: Add selection state
|
||||
- WHERE: `src/gui_2.py` or `src/app_controller.py`
|
||||
- WHAT: Track selected ticket IDs
|
||||
- HOW:
|
||||
```python
|
||||
self._selected_tickets: set[str] = set()
|
||||
```
|
||||
|
||||
- [ ] Task 3.2: Add checkbox per ticket
|
||||
- WHERE: `src/gui_2.py` ticket list rendering
|
||||
- WHAT: Checkbox for selection
|
||||
- HOW:
|
||||
```python
|
||||
selected = ticket.id in self._selected_tickets
|
||||
if imgui.checkbox(f"##select_{ticket.id}", selected):
|
||||
if selected:
|
||||
self._selected_tickets.discard(ticket.id)
|
||||
else:
|
||||
self._selected_tickets.add(ticket.id)
|
||||
imgui.same_line()
|
||||
```
|
||||
|
||||
- [ ] Task 3.3: Add select all/none buttons
|
||||
- WHERE: `src/gui_2.py` ticket list header
|
||||
- WHAT: Buttons to select/deselect all
|
||||
- HOW:
|
||||
```python
|
||||
if imgui.button("Select All"):
|
||||
self._selected_tickets = {t.id for t in self.track.tickets}
|
||||
imgui.same_line()
|
||||
if imgui.button("Select None"):
|
||||
self._selected_tickets.clear()
|
||||
```
|
||||
|
||||
## Phase 4: Bulk Actions
|
||||
Focus: Execute bulk operations on selected tickets
|
||||
|
||||
- [ ] Task 4.1: Add bulk action buttons
|
||||
- WHERE: `src/gui_2.py` ticket list area
|
||||
- WHAT: Execute, Skip, Block buttons
|
||||
- HOW:
|
||||
```python
|
||||
if imgui.button("Bulk Execute"):
|
||||
for tid in self._selected_tickets:
|
||||
self.engine.approve_task(tid)
|
||||
imgui.same_line()
|
||||
if imgui.button("Bulk Skip"):
|
||||
for tid in self._selected_tickets:
|
||||
self.engine.update_task_status(tid, "completed")
|
||||
imgui.same_line()
|
||||
if imgui.button("Bulk Block"):
|
||||
for tid in self._selected_tickets:
|
||||
self.engine.update_task_status(tid, "blocked")
|
||||
```
|
||||
|
||||
## Phase 5: Drag-Drop (Optional)
|
||||
Focus: Allow ticket reordering
|
||||
|
||||
- [ ] Task 5.1: Implement drag-drop reordering
|
||||
- WHERE: `src/gui_2.py` ticket list
|
||||
- WHAT: Drag tickets to reorder
|
||||
- HOW: Use imgui drag-drop API
|
||||
- SAFETY: Validate DAG after reorder (no dependency violations)
|
||||
|
||||
## Phase 6: Testing
|
||||
Focus: Verify all functionality
|
||||
|
||||
- [ ] Task 6.1: Write unit tests
|
||||
- WHERE: `tests/test_ticket_queue.py` (new file)
|
||||
- WHAT: Test priority serialization, bulk operations
|
||||
- HOW: Create mock tickets, verify state changes
|
||||
|
||||
- [ ] Task 6.2: Conductor - Phase Verification
|
||||
- Run: `uv run pytest tests/test_ticket_queue.py -v`
|
||||
- Manual: Verify UI controls work
|
||||
|
||||
@@ -1,41 +1,112 @@
|
||||
# Track Specification: Manual Ticket Queue Management (ticket_queue_mgmt_20260306)
|
||||
|
||||
## Overview
|
||||
Allow user to manually reorder, prioritize, or requeue tickets. Add drag-drop, priority tags, bulk selection.
|
||||
Allow user to manually reorder, prioritize, or requeue tickets in the DAG. Add drag-drop reordering, priority tags, and bulk selection for execute/skip/block operations.
|
||||
|
||||
## Current State Audit
|
||||
|
||||
### Already Implemented
|
||||
- **`models.Ticket`**: No priority field
|
||||
- **`dag_engine.py`**: `get_ready_tasks()` returns in order
|
||||
- **GUI**: Linear ticket list display
|
||||
### Already Implemented (DO NOT re-implement)
|
||||
|
||||
### Gaps to Fill
|
||||
- No priority field on Ticket
|
||||
- No drag-drop reordering
|
||||
- No bulk selection/operations
|
||||
#### Ticket Model (src/models.py)
|
||||
- **`Ticket` dataclass**: Has `status`, `depends_on`, but no `priority` field
|
||||
- **`mark_blocked(reason)`**: Sets status to blocked with reason
|
||||
- **`mark_complete()`**: Sets status to completed
|
||||
|
||||
## Functional Requirements
|
||||
- Add `priority: str = "medium"` to Ticket (high/medium/low)
|
||||
- Drag-drop reordering in ticket list
|
||||
- Multi-select for bulk operations
|
||||
- Bulk execute/skip/block
|
||||
#### DAG Engine (src/dag_engine.py)
|
||||
- **`TrackDAG`**: Manages ticket dependency graph
|
||||
- **`get_ready_tasks()`**: Returns tasks with satisfied dependencies
|
||||
- **`update_task_status()`**: Updates ticket status
|
||||
- **`has_cycle()`**: Validates DAG
|
||||
|
||||
## Key Integration Points
|
||||
| File | Purpose |
|
||||
|-----|---------|
|
||||
| `src/models.py` | Add priority field |
|
||||
| `src/gui_2.py` | Drag-drop, bulk UI |
|
||||
| `src/dag_engine.py` | Priority-aware ordering |
|
||||
### Gaps to Fill (This Track's Scope)
|
||||
- No `priority` field on Ticket
|
||||
- No drag-drop reordering in GUI
|
||||
- No multi-select for bulk operations
|
||||
- No bulk execute/skip/block actions
|
||||
|
||||
## Architectural Constraints
|
||||
- DAG validity maintained after reorder
|
||||
- Dependency order cannot be violated
|
||||
|
||||
### DAG Validity
|
||||
- Reordering MUST NOT violate dependencies
|
||||
- Cannot move ticket before its dependencies
|
||||
- `depends_on` relationships preserved
|
||||
|
||||
### Atomic Operations
|
||||
- Bulk operations apply to all selected tickets atomically
|
||||
- Partial failure rolls back all changes
|
||||
|
||||
## Architecture Reference
|
||||
|
||||
### Key Integration Points
|
||||
|
||||
| File | Lines | Purpose |
|
||||
|------|-------|---------|
|
||||
| `src/models.py` | 30-50 | `Ticket` - add priority field |
|
||||
| `src/gui_2.py` | 2650-2750 | Ticket display - add drag-drop |
|
||||
| `src/dag_engine.py` | 50-80 | Status updates |
|
||||
|
||||
### Proposed Ticket Enhancement
|
||||
```python
|
||||
@dataclass
|
||||
class Ticket:
|
||||
# ... existing fields ...
|
||||
priority: str = "medium" # "high" | "medium" | "low"
|
||||
```
|
||||
|
||||
## Functional Requirements
|
||||
|
||||
### FR1: Priority Field
|
||||
- Add `priority: str = "medium"` to Ticket dataclass
|
||||
- Values: "high", "medium", "low"
|
||||
- Persist in track state
|
||||
|
||||
### FR2: Priority UI
|
||||
- Dropdown or button group per ticket
|
||||
- Color-coded: high=red, medium=yellow, low=gray
|
||||
- Save to state on change
|
||||
|
||||
### FR3: Drag-Drop Reordering
|
||||
- Drag ticket to reorder in list
|
||||
- Drop validates DAG (no dependency violation)
|
||||
- Show error if invalid position
|
||||
|
||||
### FR4: Multi-Select
|
||||
- Checkbox per ticket for selection
|
||||
- Select all / deselect all buttons
|
||||
- Track selected ticket IDs
|
||||
|
||||
### FR5: Bulk Actions
|
||||
- Execute: Mark all selected as ready
|
||||
- Skip: Mark all selected as completed
|
||||
- Block: Mark all selected as blocked
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
| Requirement | Constraint |
|
||||
|-------------|------------|
|
||||
| Response Time | <100ms for drag-drop validation |
|
||||
| Persistence | Priority saved to state.toml |
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
### Unit Tests
|
||||
- Test priority field serialization
|
||||
- Test DAG validation on reorder
|
||||
|
||||
### Integration Tests
|
||||
- Drag-drop tickets, verify order changes
|
||||
- Bulk block tickets, verify all blocked
|
||||
|
||||
## Out of Scope
|
||||
- Automatic priority assignment
|
||||
- Priority-based auto-scheduling
|
||||
- Cross-track ticket movement
|
||||
|
||||
## Acceptance Criteria
|
||||
- [ ] Drag-drop reordering works
|
||||
- [ ] Priority tags display and save
|
||||
- [ ] Multi-select functional
|
||||
- [ ] Bulk actions apply correctly
|
||||
- [ ] DAG validity maintained
|
||||
- [ ] 1-space indentation
|
||||
- [ ] Priority field added to Ticket
|
||||
- [ ] Priority dropdown works in UI
|
||||
- [ ] Drag-drop reordering functional
|
||||
- [ ] DAG validity enforced on drop
|
||||
- [ ] Multi-select with checkboxes
|
||||
- [ ] Bulk execute/skip/block works
|
||||
- [ ] 1-space indentation maintained
|
||||
|
||||
@@ -1,25 +1,123 @@
|
||||
# Implementation Plan: Tier 4 Auto-Patching (tier4_auto_patching_20260306)
|
||||
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 1: Patch Generation
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Extend Tier 4 to generate patches
|
||||
- WHERE: src/multi_agent_conductor.py
|
||||
- WHAT: Add patch generation on test failure
|
||||
- HOW: Use difflib to generate unified diff
|
||||
- SAFETY: Only generate, don't apply
|
||||
Focus: Generate unified diff on test failure
|
||||
|
||||
## Phase 2: Diff Viewer
|
||||
- [ ] Task: Implement GUI diff viewer
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Side-by-side diff display
|
||||
- HOW: Custom ImGui rendering or third-party
|
||||
- SAFETY: Read-only preview
|
||||
- [ ] Task: Implement apply button
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Apply patch to working directory
|
||||
- HOW: subprocess.run(['patch', '-p1'])
|
||||
- SAFETY: Backup before apply
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Extend Tier 4 prompt for patch generation
|
||||
- WHERE: `src/mma_prompts.py` or inline in `ai_client.py`
|
||||
- WHAT: Prompt to generate unified diff
|
||||
- HOW:
|
||||
```python
|
||||
TIER4_PATCH_PROMPT = """
|
||||
Analyze the error and generate a unified diff patch to fix it.
|
||||
Output format:
|
||||
--- a/path/to/file.py
|
||||
+++ b/path/to/file.py
|
||||
@@ -line,count +line,count @@
|
||||
context
|
||||
-removed line
|
||||
+added line
|
||||
context
|
||||
"""
|
||||
```
|
||||
|
||||
## Phase 3: Tests & Verification
|
||||
- [ ] Task: Write patch tests
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
- [ ] Task 1.3: Add patch generation function
|
||||
- WHERE: `src/ai_client.py`
|
||||
- WHAT: Generate patch from error
|
||||
- HOW:
|
||||
```python
|
||||
def run_tier4_patch_generation(error: str, file_context: str) -> str:
|
||||
prompt = TIER4_PATCH_PROMPT + f"\n\nError:\n{error}\n\nFiles:\n{file_context}"
|
||||
return send(prompt, model="gemini-2.5-flash-lite")
|
||||
```
|
||||
|
||||
## Phase 2: Diff Viewer UI
|
||||
Focus: Display side-by-side diff
|
||||
|
||||
- [ ] Task 2.1: Parse unified diff
|
||||
- WHERE: `src/gui_2.py` or new `src/diff_viewer.py`
|
||||
- WHAT: Parse diff into hunks
|
||||
- HOW:
|
||||
```python
|
||||
def parse_diff(diff_text: str) -> list[dict]:
|
||||
hunks = []
|
||||
current_hunk = None
|
||||
for line in diff_text.split("\n"):
|
||||
if line.startswith("@@"):
|
||||
if current_hunk: hunks.append(current_hunk)
|
||||
current_hunk = {"header": line, "lines": []}
|
||||
elif current_hunk:
|
||||
current_hunk["lines"].append(line)
|
||||
if current_hunk: hunks.append(current_hunk)
|
||||
return hunks
|
||||
```
|
||||
|
||||
- [ ] Task 2.2: Render diff viewer
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Color-coded diff display
|
||||
- HOW:
|
||||
```python
|
||||
for hunk in hunks:
|
||||
for line in hunk["lines"]:
|
||||
if line.startswith("+"):
|
||||
imgui.text_colored(vec4(100, 255, 100, 255), line)
|
||||
elif line.startswith("-"):
|
||||
imgui.text_colored(vec4(255, 100, 100, 255), line)
|
||||
else:
|
||||
imgui.text(line)
|
||||
```
|
||||
|
||||
## Phase 3: Patch Application
|
||||
Focus: Apply patch with backup
|
||||
|
||||
- [ ] Task 3.1: Create backup before apply
|
||||
- WHERE: `src/gui_2.py` or `src/mcp_client.py`
|
||||
- WHAT: Backup file to .backup
|
||||
- HOW:
|
||||
```python
|
||||
import shutil
|
||||
backup_path = file_path.with_suffix(file_path.suffix + ".backup")
|
||||
shutil.copy(file_path, backup_path)
|
||||
```
|
||||
|
||||
- [ ] Task 3.2: Apply patch
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Use patch command or difflib
|
||||
- HOW:
|
||||
```python
|
||||
import subprocess
|
||||
result = subprocess.run(["patch", "-p1"], input=diff_text, capture_output=True, text=True)
|
||||
```
|
||||
|
||||
- [ ] Task 3.3: Restore on failure
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Restore from backup if patch fails
|
||||
- HOW: `shutil.copy(backup_path, file_path)`
|
||||
|
||||
## Phase 4: Modal UI
|
||||
Focus: Approval modal for patches
|
||||
|
||||
- [ ] Task 4.1: Create patch approval modal
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Modal with diff preview and Apply/Reject buttons
|
||||
- HOW:
|
||||
```python
|
||||
if self._show_patch_modal:
|
||||
imgui.open_popup("Apply Patch?")
|
||||
if imgui.begin_popup_modal("Apply Patch?"):
|
||||
# Render diff
|
||||
if imgui.button("Apply"):
|
||||
self._apply_current_patch()
|
||||
imgui.close_current_popup()
|
||||
imgui.same_line()
|
||||
if imgui.button("Reject"):
|
||||
imgui.close_current_popup()
|
||||
imgui.end_popup()
|
||||
```
|
||||
|
||||
## Phase 5: Testing
|
||||
- [ ] Task 5.1: Write unit tests
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
|
||||
@@ -1,25 +1,107 @@
|
||||
# Implementation Plan: Tool Usage Analytics (tool_usage_analytics_20260306)
|
||||
|
||||
## Phase 1: Data Collection
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Verify tool_log_callback
|
||||
- WHERE: src/ai_client.py
|
||||
- WHAT: Check existing logging
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 2: Aggregation
|
||||
- [ ] Task: Implement usage aggregation
|
||||
- WHERE: src/gui_2.py or new module
|
||||
- WHAT: Count tools, avg times, failures
|
||||
- HOW: Process tool_log entries
|
||||
- SAFETY: Efficient data structures
|
||||
## Phase 1: Data Collection
|
||||
Focus: Add tool execution tracking
|
||||
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- Run `activate_skill mma-orchestrator` before starting
|
||||
|
||||
- [ ] Task 1.2: Add tool stats state
|
||||
- WHERE: `src/app_controller.py` or `src/gui_2.py`
|
||||
- WHAT: Add `_tool_stats: dict[str, dict]` state
|
||||
- HOW:
|
||||
```python
|
||||
self._tool_stats: dict[str, dict] = {}
|
||||
# Structure: {tool_name: {"count": 0, "total_time_ms": 0, "failures": 0}}
|
||||
```
|
||||
- CODE STYLE: 1-space indentation
|
||||
|
||||
- [ ] Task 1.3: Hook into tool execution
|
||||
- WHERE: `src/ai_client.py` in tool execution path
|
||||
- WHAT: Track tool name, time, success/failure
|
||||
- HOW:
|
||||
```python
|
||||
start_time = time.time()
|
||||
try:
|
||||
result = mcp_client.dispatch(name, args)
|
||||
success = True
|
||||
except Exception:
|
||||
success = False
|
||||
finally:
|
||||
elapsed_ms = (time.time() - start_time) * 1000
|
||||
# Update stats via callback or direct update
|
||||
```
|
||||
- SAFETY: Don't impact tool execution performance
|
||||
|
||||
## Phase 2: Aggregation Logic
|
||||
Focus: Calculate derived metrics
|
||||
|
||||
- [ ] Task 2.1: Implement stats update function
|
||||
- WHERE: `src/app_controller.py`
|
||||
- WHAT: Function to update tool stats
|
||||
- HOW:
|
||||
```python
|
||||
def _update_tool_stats(self, tool_name: str, elapsed_ms: float, success: bool) -> None:
|
||||
if tool_name not in self._tool_stats:
|
||||
self._tool_stats[tool_name] = {"count": 0, "total_time_ms": 0.0, "failures": 0}
|
||||
self._tool_stats[tool_name]["count"] += 1
|
||||
self._tool_stats[tool_name]["total_time_ms"] += elapsed_ms
|
||||
if not success:
|
||||
self._tool_stats[tool_name]["failures"] += 1
|
||||
```
|
||||
|
||||
- [ ] Task 2.2: Calculate average time and failure rate
|
||||
- WHERE: `src/gui_2.py` in render function
|
||||
- WHAT: Derive avg_time and failure_rate from stats
|
||||
- HOW:
|
||||
```python
|
||||
for tool, stats in self._tool_stats.items():
|
||||
count = stats["count"]
|
||||
avg_time = stats["total_time_ms"] / count if count > 0 else 0
|
||||
failure_rate = (stats["failures"] / count * 100) if count > 0 else 0
|
||||
```
|
||||
|
||||
## Phase 3: Visualization
|
||||
- [ ] Task: Render analytics
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Charts and tables
|
||||
- HOW: imgui tables, plot_lines
|
||||
- SAFETY: Handle empty data
|
||||
Focus: Display analytics in GUI
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Task: Test analytics
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
- [ ] Task 3.1: Add analytics panel
|
||||
- WHERE: `src/gui_2.py` in MMA Dashboard or Operations
|
||||
- WHAT: Table showing tool stats
|
||||
- HOW:
|
||||
```python
|
||||
if imgui.collapsing_header("Tool Usage Analytics"):
|
||||
if imgui.begin_table("tool_stats", 4):
|
||||
imgui.table_setup_column("Tool")
|
||||
imgui.table_setup_column("Count")
|
||||
imgui.table_setup_column("Avg Time (ms)")
|
||||
imgui.table_setup_column("Failure %")
|
||||
imgui.table_headers_row()
|
||||
for tool, stats in sorted(self._tool_stats.items(), key=lambda x: -x[1]["count"]):
|
||||
imgui.table_next_row()
|
||||
imgui.table_set_column_index(0)
|
||||
imgui.text(tool)
|
||||
# ... other columns
|
||||
imgui.end_table()
|
||||
```
|
||||
|
||||
## Phase 4: Reset on Session Clear
|
||||
Focus: Clear stats on new session
|
||||
|
||||
- [ ] Task 4.1: Clear stats on session reset
|
||||
- WHERE: `src/gui_2.py` or `src/app_controller.py` reset handler
|
||||
- WHAT: Clear `_tool_stats` dict
|
||||
- HOW: `self._tool_stats.clear()`
|
||||
|
||||
## Phase 5: Testing
|
||||
Focus: Verify all functionality
|
||||
|
||||
- [ ] Task 5.1: Write unit tests
|
||||
- WHERE: `tests/test_tool_analytics.py` (new file)
|
||||
- WHAT: Test stats accumulation, avg calculation
|
||||
- HOW: Mock tool execution, verify stats update
|
||||
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
- Run: `uv run pytest tests/test_tool_analytics.py -v`
|
||||
- Manual: Verify analytics panel displays in GUI
|
||||
|
||||
@@ -1,35 +1,99 @@
|
||||
# Track Specification: Tool Usage Analytics (tool_usage_analytics_20260306)
|
||||
|
||||
## Overview
|
||||
Analytics panel showing most-used tools, average execution time, failure rates.
|
||||
Analytics panel showing most-used tools, average execution time, and failure rates. Uses existing tool execution data from ai_client.
|
||||
|
||||
## Current State Audit
|
||||
|
||||
### Already Implemented
|
||||
- **`ai_client.tool_log_callback`**: Called when tool executes
|
||||
- **`mcp_client.dispatch()`**: Routes tool calls
|
||||
- **No aggregation or storage**
|
||||
### Already Implemented (DO NOT re-implement)
|
||||
|
||||
### Gaps to Fill
|
||||
- No tool usage tracking
|
||||
- No execution time tracking
|
||||
#### Tool Execution (src/ai_client.py)
|
||||
- **Tool dispatch in `_execute_tool_calls_concurrently()`**: Executes tools via `mcp_client.dispatch()`
|
||||
- **`pre_tool_callback`**: Optional callback before tool execution
|
||||
- **No built-in tracking or aggregation**
|
||||
|
||||
#### MCP Client (src/mcp_client.py)
|
||||
- **`dispatch(name, args)`**: Routes tool calls to implementations
|
||||
- **26 tools available** (run_powershell, read_file, py_get_skeleton, etc.)
|
||||
- **`MUTATING_TOOLS`**: Set of tools that modify files
|
||||
|
||||
### Gaps to Fill (This Track's Scope)
|
||||
- No tool usage tracking (count per tool)
|
||||
- No execution time tracking per tool
|
||||
- No failure rate tracking
|
||||
- No analytics display in GUI
|
||||
|
||||
## Architectural Constraints
|
||||
|
||||
### Efficient Aggregation
|
||||
- Track tool stats in lightweight data structure
|
||||
- Don't impact tool execution performance
|
||||
- Use dict: `{tool_name: {count, total_time, failures}}`
|
||||
|
||||
### Memory Bounds
|
||||
- Only track stats, not full history
|
||||
- Reset on session reset
|
||||
|
||||
## Architecture Reference
|
||||
|
||||
### Key Integration Points
|
||||
|
||||
| File | Lines | Purpose |
|
||||
|------|-------|---------|
|
||||
| `src/ai_client.py` | ~500-550 | Tool execution - add tracking |
|
||||
| `src/gui_2.py` | ~2700-2800 | Analytics panel |
|
||||
|
||||
### Proposed Tracking Structure
|
||||
```python
|
||||
# In AppController or App:
|
||||
self._tool_stats: dict[str, dict] = {}
|
||||
# Structure: {"read_file": {"count": 10, "total_time_ms": 150, "failures": 0}, ...}
|
||||
```
|
||||
|
||||
## Functional Requirements
|
||||
- Track tool name, execution time, success/failure
|
||||
- Aggregate by tool name
|
||||
- Display ranking by usage count
|
||||
- Show average time per tool
|
||||
- Show failure percentage
|
||||
|
||||
## Key Integration Points
|
||||
| File | Purpose |
|
||||
|-----|---------|
|
||||
| `src/ai_client.py` | Hook into tool_log_callback |
|
||||
| `src/gui_2.py` | Analytics panel rendering |
|
||||
### FR1: Tool Usage Tracking
|
||||
- Track tool name, execution time, success/failure
|
||||
- Store in `_tool_stats` dict
|
||||
- Update on each tool execution
|
||||
|
||||
### FR2: Aggregation by Tool
|
||||
- Count total calls per tool
|
||||
- Calculate average execution time
|
||||
- Track failure count and rate
|
||||
|
||||
### FR3: Analytics Display
|
||||
- Table showing tool name, count, avg time, failure rate
|
||||
- Sort by usage count (most used first)
|
||||
- Show in MMA Dashboard or Operations panel
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
| Requirement | Constraint |
|
||||
|-------------|------------|
|
||||
| Tracking Overhead | <1ms per tool call |
|
||||
| Memory | <1KB for stats dict |
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
### Unit Tests
|
||||
- Test tracking updates correctly
|
||||
- Test failure rate calculation
|
||||
|
||||
### Integration Tests
|
||||
- Execute tools, verify stats accumulate
|
||||
- Reset session, verify stats cleared
|
||||
|
||||
## Out of Scope
|
||||
- Historical analytics across sessions
|
||||
- Export to file
|
||||
- Per-ticket tool breakdown
|
||||
|
||||
## Acceptance Criteria
|
||||
- [ ] Tool ranking displayed
|
||||
- [ ] Average times accurate
|
||||
- [ ] Failure rates tracked
|
||||
- [ ] 1-space indentation
|
||||
- [ ] Tool execution tracked
|
||||
- [ ] Count per tool accurate
|
||||
- [ ] Average time calculated
|
||||
- [ ] Failure rate shown
|
||||
- [ ] Display in GUI panel
|
||||
- [ ] Reset on session clear
|
||||
- [ ] 1-space indentation maintained
|
||||
|
||||
@@ -1,32 +1,95 @@
|
||||
# Implementation Plan: Track Progress Visualization (track_progress_viz_20260306)
|
||||
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 1: Progress Calculation
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Implement progress calculation
|
||||
- WHERE: src/gui_2.py or app_controller
|
||||
- WHAT: Calculate % from ticket states
|
||||
- HOW: Count completed vs total
|
||||
Focus: Calculate progress metrics from ticket states
|
||||
|
||||
## Phase 2: Visualization
|
||||
- [ ] Task: Render progress bar
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Visual progress bar
|
||||
- HOW: imgui.progress_bar
|
||||
- [ ] Task: Render percentage text
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Numeric % display
|
||||
- HOW: imgui.text
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- Run `activate_skill mma-orchestrator` before starting
|
||||
|
||||
## Phase 3: Additional Features
|
||||
- [ ] Task: Implement ticket counts
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Completed/remaining counts
|
||||
- HOW: Text display
|
||||
- [ ] Task: Implement ETA
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Time estimate
|
||||
- HOW: Average time per ticket
|
||||
- [ ] Task 1.2: Implement progress calculation function
|
||||
- WHERE: `src/gui_2.py` or helper in `src/project_manager.py`
|
||||
- WHAT: Calculate completion percentage from tickets
|
||||
- HOW:
|
||||
```python
|
||||
def calculate_track_progress(tickets: list[Ticket]) -> dict:
|
||||
total = len(tickets)
|
||||
if total == 0:
|
||||
return {"percentage": 0, "completed": 0, "total": 0, "in_progress": 0, "blocked": 0, "todo": 0}
|
||||
completed = sum(1 for t in tickets if t.status == "completed")
|
||||
in_progress = sum(1 for t in tickets if t.status == "in_progress")
|
||||
blocked = sum(1 for t in tickets if t.status == "blocked")
|
||||
todo = sum(1 for t in tickets if t.status == "todo")
|
||||
percentage = (completed / total) * 100
|
||||
return {"percentage": percentage, "completed": completed, "total": total,
|
||||
"in_progress": in_progress, "blocked": blocked, "todo": todo}
|
||||
```
|
||||
|
||||
## Phase 4: Verification
|
||||
- [ ] Task: Test progress display
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
## Phase 2: Progress Bar Rendering
|
||||
Focus: Display visual progress bar
|
||||
|
||||
- [ ] Task 2.1: Add progress bar to MMA Dashboard
|
||||
- WHERE: `src/gui_2.py` `_render_mma_dashboard()`
|
||||
- WHAT: Visual progress bar with percentage
|
||||
- HOW:
|
||||
```python
|
||||
progress = calculate_track_progress(self.track.tickets)
|
||||
imgui.text(f"Progress: {progress['completed']}/{progress['total']} tickets")
|
||||
imgui.progress_bar(progress['percentage'] / 100.0, size=(300, 20))
|
||||
imgui.same_line()
|
||||
imgui.text(f"{progress['percentage']:.1f}%")
|
||||
```
|
||||
- SAFETY: Handle empty ticket list
|
||||
|
||||
## Phase 3: Ticket Breakdown Display
|
||||
Focus: Show status breakdown
|
||||
|
||||
- [ ] Task 3.1: Add status breakdown text
|
||||
- WHERE: `src/gui_2.py` `_render_mma_dashboard()`
|
||||
- WHAT: Show counts per status
|
||||
- HOW:
|
||||
```python
|
||||
imgui.text(f"Completed: {progress['completed']}")
|
||||
imgui.text(f"In Progress: {progress['in_progress']}")
|
||||
imgui.text(f"Blocked: {progress['blocked']}")
|
||||
imgui.text(f"Todo: {progress['todo']}")
|
||||
```
|
||||
|
||||
## Phase 4: ETA Estimation
|
||||
Focus: Estimate time remaining
|
||||
|
||||
- [ ] Task 4.1: Track ticket completion times
|
||||
- WHERE: `src/gui_2.py` or `src/app_controller.py`
|
||||
- WHAT: Track average time per completed ticket
|
||||
- HOW:
|
||||
```python
|
||||
self._ticket_start_times: dict[str, float] = {}
|
||||
self._avg_ticket_time: float = 0.0
|
||||
self._completed_count: int = 0
|
||||
# On ticket start: self._ticket_start_times[ticket.id] = time.time()
|
||||
# On ticket complete: elapsed = time.time() - start; update average
|
||||
```
|
||||
|
||||
- [ ] Task 4.2: Calculate and display ETA
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Show estimated time remaining
|
||||
- HOW:
|
||||
```python
|
||||
remaining = progress['total'] - progress['completed']
|
||||
eta_seconds = self._avg_ticket_time * remaining
|
||||
eta_minutes = int(eta_seconds / 60)
|
||||
imgui.text(f"ETA: ~{eta_minutes}m ({remaining} tickets remaining)")
|
||||
```
|
||||
|
||||
## Phase 5: Testing
|
||||
Focus: Verify all functionality
|
||||
|
||||
- [ ] Task 5.1: Write unit tests for progress calculation
|
||||
- WHERE: `tests/test_progress_viz.py` (new file)
|
||||
- WHAT: Test percentage calculation, edge cases
|
||||
- HOW: Create mock tickets with various statuses
|
||||
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
- Run: `uv run pytest tests/test_progress_viz.py -v`
|
||||
- Manual: Verify progress bar displays correctly
|
||||
|
||||
@@ -1,34 +1,111 @@
|
||||
# Track Specification: Track Progress Visualization (track_progress_viz_20260306)
|
||||
|
||||
## Overview
|
||||
Progress bars and percentage completion for active tracks and tickets.
|
||||
Progress bars and percentage completion for active tracks and tickets. Better visualization of DAG execution state.
|
||||
|
||||
## Current State Audit
|
||||
|
||||
### Already Implemented
|
||||
- **`models.Track`**: Has tickets list
|
||||
- **`project_manager.get_all_tracks()`**: Returns track progress
|
||||
- **GUI**: Shows ticket status but no progress bar
|
||||
### Already Implemented (DO NOT re-implement)
|
||||
|
||||
### Gaps to Fill
|
||||
- No visual progress bar
|
||||
#### Track Model (src/models.py)
|
||||
- **`Track` dataclass**: Has `tickets: list[Ticket]` field
|
||||
- **`Ticket.status`**: "todo" | "in_progress" | "completed" | "blocked"
|
||||
|
||||
#### Track Listing (src/project_manager.py)
|
||||
- **`get_all_tracks()`**: Returns list of track metadata with progress
|
||||
- **Progress calculation exists**: Counts completed vs total tickets
|
||||
|
||||
#### DAG Engine (src/dag_engine.py)
|
||||
- **`TrackDAG`**: Manages ticket dependency graph
|
||||
- **Status tracking via `update_task_status()`**
|
||||
|
||||
### Gaps to Fill (This Track's Scope)
|
||||
- No visual progress bar in GUI
|
||||
- No percentage completion display
|
||||
- No ETA estimation
|
||||
- No ticket breakdown display
|
||||
|
||||
## Architectural Constraints
|
||||
|
||||
### Accurate State
|
||||
- Progress MUST reflect actual ticket status
|
||||
- Count completed, in_progress, blocked, todo separately
|
||||
|
||||
### Efficient Updates
|
||||
- Status changes trigger immediate UI update
|
||||
- No polling - event-driven via MMA state updates
|
||||
|
||||
## Architecture Reference
|
||||
|
||||
### Key Integration Points
|
||||
|
||||
| File | Lines | Purpose |
|
||||
|------|-------|---------|
|
||||
| `src/gui_2.py` | 2650-2750 | MMA Dashboard - add progress display |
|
||||
| `src/project_manager.py` | 289-320 | `get_all_tracks()` - progress data |
|
||||
| `src/dag_engine.py` | 50-80 | Status tracking |
|
||||
|
||||
### Progress Calculation
|
||||
```python
|
||||
def calculate_progress(tickets: list[Ticket]) -> dict:
|
||||
total = len(tickets)
|
||||
completed = sum(1 for t in tickets if t.status == "completed")
|
||||
in_progress = sum(1 for t in tickets if t.status == "in_progress")
|
||||
blocked = sum(1 for t in tickets if t.status == "blocked")
|
||||
todo = sum(1 for t in tickets if t.status == "todo")
|
||||
percentage = (completed / total * 100) if total > 0 else 0
|
||||
return {
|
||||
"total": total, "completed": completed, "in_progress": in_progress,
|
||||
"blocked": blocked, "todo": todo, "percentage": percentage
|
||||
}
|
||||
```
|
||||
|
||||
## Functional Requirements
|
||||
- Progress bar showing % complete
|
||||
- Ticket count: X/Y completed
|
||||
- ETA estimation based on average time per ticket
|
||||
|
||||
## Key Integration Points
|
||||
| File | Purpose |
|
||||
|-----|---------|
|
||||
| `src/gui_2.py` | Progress bar rendering |
|
||||
| `src/dag_engine.py` | Completion calculation |
|
||||
### FR1: Progress Bar
|
||||
- Visual progress bar using `imgui.progress_bar()`
|
||||
- Show 0-100% completion
|
||||
- Color based on progress (red < 25%, yellow < 75%, green >= 75%)
|
||||
|
||||
### FR2: Percentage Text
|
||||
- Display "X% complete" below bar
|
||||
- Show "X/Y tickets completed"
|
||||
|
||||
### FR3: Ticket Breakdown
|
||||
- Show counts: completed, in_progress, blocked, todo
|
||||
- Use colored indicators per status
|
||||
|
||||
### FR4: ETA Estimation
|
||||
- Track average time per completed ticket
|
||||
- Estimate remaining time: `avg_time * remaining_tickets`
|
||||
- Display as "ETA: ~Xm"
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
| Requirement | Constraint |
|
||||
|-------------|------------|
|
||||
| Update Latency | <100ms after status change |
|
||||
| Memory | <100 bytes for ETA state |
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
### Unit Tests
|
||||
- Test progress calculation accuracy
|
||||
- Test ETA estimation logic
|
||||
|
||||
### Integration Tests
|
||||
- Complete tickets, verify progress updates
|
||||
- Verify ETA recalculates
|
||||
|
||||
## Out of Scope
|
||||
- Historical progress tracking
|
||||
- Progress export
|
||||
- Multi-track comparison
|
||||
|
||||
## Acceptance Criteria
|
||||
- [ ] Progress bar renders
|
||||
- [ ] Percentage accurate
|
||||
- [ ] Counts match actual tickets
|
||||
- [ ] ETA calculation works
|
||||
- [ ] 1-space indentation
|
||||
- [ ] Progress bar renders correctly
|
||||
- [ ] Percentage accurate (X/Y completed)
|
||||
- [ ] Ticket breakdown displayed
|
||||
- [ ] ETA estimation works
|
||||
- [ ] Updates on ticket status change
|
||||
- [ ] 1-space indentation maintained
|
||||
|
||||
@@ -1,29 +1,102 @@
|
||||
# Implementation Plan: Visual DAG Ticket Editing (visual_dag_ticket_editing_20260306)
|
||||
|
||||
> **Reference:** [Spec](./spec.md) | [Architecture Guide](../../../docs/guide_architecture.md)
|
||||
|
||||
## Phase 1: Node Editor Setup
|
||||
- [ ] Task: Initialize MMA Environment
|
||||
- [ ] Task: Verify ImGui Bundle node editor available
|
||||
- WHERE: requirements.txt
|
||||
- WHAT: Ensure imgui-bundle with node editor
|
||||
- HOW: pip install
|
||||
Focus: Verify ImGui Bundle node editor
|
||||
|
||||
## Phase 2: Implementation
|
||||
- [ ] Task: Implement ticket node rendering
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Render tickets as nodes
|
||||
- HOW: imgui.node_editor with custom drawing
|
||||
- SAFETY: 60fps target
|
||||
- [ ] Task: Implement drag-drop
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Handle node drag and dependency lines
|
||||
- HOW: Node editor callbacks
|
||||
- SAFETY: Validate DAG after edit
|
||||
- [ ] Task: Implement status display
|
||||
- WHERE: src/gui_2.py
|
||||
- WHAT: Color-coded status per node
|
||||
- HOW: Node colors based on ticket status
|
||||
- SAFETY: Sync with backend state
|
||||
- [ ] Task 1.1: Initialize MMA Environment
|
||||
- [ ] Task 1.2: Verify imgui_bundle node editor available
|
||||
- WHERE: Check imports
|
||||
- HOW: `import imgui_bundle.node_editor as ed`
|
||||
|
||||
## Phase 3: Tests & Verification
|
||||
- [ ] Task: Write node editor tests
|
||||
- [ ] Task: Conductor - Phase Verification
|
||||
## Phase 2: Basic Node Rendering
|
||||
Focus: Render tickets as nodes
|
||||
|
||||
- [ ] Task 2.1: Create node editor context
|
||||
- WHERE: `src/gui_2.py` MMA dashboard
|
||||
- WHAT: Begin/end node editor
|
||||
- HOW:
|
||||
```python
|
||||
ed.begin("Ticket DAG")
|
||||
for ticket in self.track.tickets:
|
||||
ed.begin_node(ticket.id)
|
||||
imgui.text(ticket.id)
|
||||
imgui.text(ticket.status)
|
||||
ed.end_node()
|
||||
ed.end()
|
||||
```
|
||||
|
||||
- [ ] Task 2.2: Set node positions
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Position nodes in grid
|
||||
- HOW:
|
||||
```python
|
||||
# Auto-layout: grid positions
|
||||
col = i % 4
|
||||
row = i // 4
|
||||
ed.set_node_position(ticket.id, col * 200, row * 150)
|
||||
```
|
||||
|
||||
- [ ] Task 2.3: Add status colors
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Color nodes by status
|
||||
- HOW:
|
||||
```python
|
||||
status_colors = {"todo": vec4(150, 150, 150, 255), "in_progress": vec4(255, 200, 100, 255),
|
||||
"completed": vec4(100, 255, 100, 255), "blocked": vec4(255, 100, 100, 255)}
|
||||
color = status_colors.get(ticket.status, vec4(200, 200, 200, 255))
|
||||
ed.push_style_color(ed.StyleColor.node_bg, color)
|
||||
```
|
||||
|
||||
## Phase 3: Dependency Links
|
||||
Focus: Draw lines between nodes
|
||||
|
||||
- [ ] Task 3.1: Create links for dependencies
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Draw lines from dependency to dependent
|
||||
- HOW:
|
||||
```python
|
||||
for ticket in self.track.tickets:
|
||||
for dep_id in ticket.depends_on:
|
||||
# Create link: dep_id -> ticket.id
|
||||
ed.link(f"link_{dep_id}_{ticket.id}", dep_id, ticket.id)
|
||||
```
|
||||
|
||||
## Phase 4: Interactive Editing
|
||||
Focus: Allow creating/removing dependencies
|
||||
|
||||
- [ ] Task 4.1: Handle link creation
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Detect new links and update Ticket
|
||||
- HOW:
|
||||
```python
|
||||
# Check for new links
|
||||
if ed.begin_create():
|
||||
created = ed.get_created_link()
|
||||
if created:
|
||||
# Add dependency to ticket
|
||||
pass
|
||||
ed.end_create()
|
||||
```
|
||||
|
||||
- [ ] Task 4.2: Handle link deletion
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Detect deleted links
|
||||
- HOW: `ed.begin_delete()`, `ed.get_deleted_link()`
|
||||
|
||||
- [ ] Task 4.3: Validate DAG after edit
|
||||
- WHERE: `src/gui_2.py`
|
||||
- WHAT: Check for cycles
|
||||
- HOW:
|
||||
```python
|
||||
from src.dag_engine import TrackDAG
|
||||
temp_dag = TrackDAG(updated_tickets)
|
||||
if temp_dag.has_cycle():
|
||||
imgui.open_popup("Cycle Detected!")
|
||||
# Revert change
|
||||
```
|
||||
|
||||
## Phase 5: Testing
|
||||
- [ ] Task 5.1: Write unit tests
|
||||
- [ ] Task 5.2: Conductor - Phase Verification
|
||||
|
||||
Reference in New Issue
Block a user