Refactor cockpit to use DockerTmuxController pattern
Based on claude-code-tools TmuxCLIController, this refactor: - Added DockerTmuxController class for robust tmux session management - Implements send_keys() with configurable delay_enter - Implements capture_pane() for output retrieval - Implements wait_for_prompt() for pattern-based completion detection - Implements wait_for_idle() for content-hash-based idle detection - Implements wait_for_shell_prompt() for shell prompt detection Also includes workflow improvements: - Pre-task git snapshot before agent execution - Post-task commit protocol in agent guidelines Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
369
docs/DISPATCHER-INTEGRATION-GUIDE.md
Normal file
369
docs/DISPATCHER-INTEGRATION-GUIDE.md
Normal file
@@ -0,0 +1,369 @@
|
||||
# Dispatcher Integration Guide - Luzia CLI Enhancement
|
||||
|
||||
## Summary of Changes
|
||||
|
||||
The Responsive Dispatcher improves Luzia CLI responsiveness by:
|
||||
|
||||
1. **Eliminating blocking** during task dispatch - CLI returns immediately with job_id
|
||||
2. **Adding background monitoring** - Jobs progress tracked asynchronously
|
||||
3. **Implementing status feedback** - Live progress updates without blocking
|
||||
4. **Enabling concurrent management** - Multiple tasks tracked independently
|
||||
5. **Providing responsive CLI** - Always responsive after dispatch
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
### Before (Blocking Dispatch)
|
||||
```
|
||||
User: luzia overbits "task"
|
||||
↓ [BLOCKS HERE - CLI waits for agent startup]
|
||||
(3-5 seconds of blocking)
|
||||
↓
|
||||
Output: job_id
|
||||
Result: CLI frozen during dispatch
|
||||
```
|
||||
|
||||
### After (Non-Blocking Dispatch)
|
||||
```
|
||||
User: luzia overbits "task"
|
||||
↓ [RETURNS IMMEDIATELY]
|
||||
(<100ms)
|
||||
↓
|
||||
Output: job_id
|
||||
Result: CLI responsive, task runs in background
|
||||
```
|
||||
|
||||
### Metrics
|
||||
- **Dispatch latency**: <100ms (vs 3-5s before)
|
||||
- **Throughput**: 434 tasks/second
|
||||
- **Status retrieval**: <1ms (cached) or <50µs (fresh)
|
||||
- **Memory per job**: ~2KB
|
||||
|
||||
## New Modules
|
||||
|
||||
### 1. `lib/responsive_dispatcher.py`
|
||||
Core non-blocking dispatcher engine.
|
||||
|
||||
**Key Classes:**
|
||||
- `ResponseiveDispatcher` - Main dispatcher with:
|
||||
- `dispatch_task()` - Returns immediately with job_id
|
||||
- `get_status()` - Poll job status with caching
|
||||
- `update_status()` - Update job progress (used by monitor)
|
||||
- `list_jobs()` - Get job history
|
||||
- `wait_for_job()` - Block until completion (optional)
|
||||
- `start_background_monitor()` - Start monitor thread
|
||||
|
||||
**Features:**
|
||||
- Atomic status file operations
|
||||
- Intelligent caching (1-second TTL)
|
||||
- Background monitoring queue
|
||||
- Job history persistence
|
||||
|
||||
### 2. `lib/cli_feedback.py`
|
||||
Pretty-printed CLI feedback and status display.
|
||||
|
||||
**Key Classes:**
|
||||
- `CLIFeedback` - Responsive output formatting:
|
||||
- `job_dispatched()` - Show dispatch confirmation
|
||||
- `show_status()` - Display job status with progress
|
||||
- `show_jobs_list()` - List all jobs
|
||||
- `show_concurrent_jobs()` - Summary view
|
||||
|
||||
- `Colors` - ANSI color codes
|
||||
- `ProgressBar` - ASCII progress bar renderer
|
||||
- `ResponseiveOutput` - Context manager for operations
|
||||
|
||||
### 3. `lib/dispatcher_enhancements.py`
|
||||
Integration layer connecting dispatcher to existing Luzia code.
|
||||
|
||||
**Key Classes:**
|
||||
- `EnhancedDispatcher` - Wrapper combining responsive dispatcher + feedback
|
||||
- `dispatch_and_report()` - Dispatch with automatic feedback
|
||||
- `get_status_and_display()` - Get and display status
|
||||
- `show_jobs_summary()` - Show jobs for a project
|
||||
- `show_concurrent_summary()` - Show all jobs
|
||||
|
||||
**Integration Functions:**
|
||||
- `enhanced_spawn_claude_agent()` - Replacement for existing spawn
|
||||
- `track_existing_job()` - Retroactive tracking
|
||||
- `show_job_status_interactive()` - Interactive monitoring
|
||||
- `start_background_monitoring()` - Start monitor thread
|
||||
|
||||
## Integration Steps
|
||||
|
||||
### Step 1: Import New Modules
|
||||
|
||||
In `bin/luzia`, add at the top:
|
||||
|
||||
```python
|
||||
from lib.responsive_dispatcher import ResponseiveDispatcher
|
||||
from lib.cli_feedback import CLIFeedback
|
||||
from lib.dispatcher_enhancements import EnhancedDispatcher, get_enhanced_dispatcher
|
||||
```
|
||||
|
||||
### Step 2: Enhanced Project Task Handler
|
||||
|
||||
Replace the existing `route_project_task` handler:
|
||||
|
||||
```python
|
||||
def route_project_task(config: dict, args: list, kwargs: dict) -> int:
|
||||
"""Handler: luzia <project> <task> (with responsive dispatch)"""
|
||||
|
||||
# ... existing validation code ...
|
||||
|
||||
project = args[0]
|
||||
task = " ".join(args[1:])
|
||||
|
||||
# ... existing setup code ...
|
||||
|
||||
# Use enhanced dispatcher for responsive dispatch
|
||||
enhanced = get_enhanced_dispatcher()
|
||||
|
||||
# Dispatch and show feedback
|
||||
job_id, status = enhanced.dispatch_and_report(
|
||||
project=project,
|
||||
task=task,
|
||||
show_details=not is_command, # Show details for natural language only
|
||||
show_feedback=VERBOSE
|
||||
)
|
||||
|
||||
# Output job_id for tracking
|
||||
print(f"agent:{project}:{job_id}")
|
||||
return 0
|
||||
```
|
||||
|
||||
### Step 3: Add Job Status Commands
|
||||
|
||||
Add new route for `luzia jobs`:
|
||||
|
||||
```python
|
||||
def route_jobs(config: dict, args: list, kwargs: dict) -> int:
|
||||
"""Handler: luzia jobs [job_id]"""
|
||||
|
||||
enhanced = get_enhanced_dispatcher()
|
||||
|
||||
if not args:
|
||||
# Show all jobs
|
||||
enhanced.show_jobs_summary()
|
||||
return 0
|
||||
|
||||
job_id = args[0]
|
||||
|
||||
if "--watch" in args:
|
||||
# Interactive monitoring
|
||||
from lib.dispatcher_enhancements import show_job_status_interactive
|
||||
show_job_status_interactive(job_id)
|
||||
else:
|
||||
# Show status
|
||||
enhanced.get_status_and_display(job_id, show_full=True)
|
||||
|
||||
return 0
|
||||
```
|
||||
|
||||
### Step 4: Start Background Monitor
|
||||
|
||||
Add to main startup:
|
||||
|
||||
```python
|
||||
def main():
|
||||
# ... existing code ...
|
||||
|
||||
# Start background monitoring
|
||||
enhanced = get_enhanced_dispatcher()
|
||||
enhanced.dispatcher.start_background_monitor()
|
||||
|
||||
# ... rest of main ...
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
New files created:
|
||||
|
||||
```
|
||||
/opt/server-agents/orchestrator/
|
||||
├── lib/
|
||||
│ ├── responsive_dispatcher.py # Core dispatcher
|
||||
│ ├── cli_feedback.py # CLI feedback system
|
||||
│ └── dispatcher_enhancements.py # Integration layer
|
||||
├── tests/
|
||||
│ └── test_responsive_dispatcher.py # Test suite (11 tests)
|
||||
├── examples/
|
||||
│ └── demo_concurrent_tasks.py # Live demonstration
|
||||
└── docs/
|
||||
├── RESPONSIVE-DISPATCHER.md # User guide
|
||||
└── DISPATCHER-INTEGRATION-GUIDE.md (this file)
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Dispatch (Non-blocking)
|
||||
|
||||
```bash
|
||||
$ luzia overbits "fix the login button"
|
||||
✓ Dispatched
|
||||
Job ID: 113754-a2f5
|
||||
Project: overbits
|
||||
|
||||
Use: luzia jobs to view status
|
||||
luzia jobs 113754-a2f5 for details
|
||||
|
||||
$ # CLI is responsive immediately!
|
||||
$ luzia jobs # Check status without waiting
|
||||
```
|
||||
|
||||
### Monitor Multiple Jobs
|
||||
|
||||
```bash
|
||||
$ luzia overbits "task 1" & luzia musica "task 2" & luzia dss "task 3" &
|
||||
agent:overbits:113754-a2f5
|
||||
agent:musica:113754-8e4b
|
||||
agent:dss:113754-9f3c
|
||||
|
||||
$ # All 3 running concurrently
|
||||
$ luzia jobs
|
||||
Task Summary:
|
||||
Running: 3
|
||||
Pending: 0
|
||||
```
|
||||
|
||||
### Watch Job Progress
|
||||
|
||||
```bash
|
||||
$ luzia jobs 113754-a2f5 --watch
|
||||
|
||||
Monitoring job: 113754-a2f5
|
||||
|
||||
starting [░░░░░░░░░░░░░░░░░░░░] 5%
|
||||
running [██████░░░░░░░░░░░░░░] 30%
|
||||
running [████████████░░░░░░░░] 65%
|
||||
completed [██████████████████████] 100%
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Run the test suite:
|
||||
|
||||
```bash
|
||||
python3 tests/test_responsive_dispatcher.py
|
||||
```
|
||||
|
||||
All 11 tests should pass:
|
||||
- ✓ Immediate dispatch
|
||||
- ✓ Status retrieval
|
||||
- ✓ Status updates
|
||||
- ✓ Concurrent jobs
|
||||
- ✓ Cache behavior
|
||||
- ✓ CLI feedback
|
||||
- ✓ Progress bar
|
||||
- ✓ Background monitoring
|
||||
- ✓ Enhanced dispatcher dispatch
|
||||
- ✓ Enhanced dispatcher display
|
||||
- ✓ Enhanced dispatcher summary
|
||||
|
||||
## Demo
|
||||
|
||||
Run the live demo:
|
||||
|
||||
```bash
|
||||
python3 examples/demo_concurrent_tasks.py
|
||||
```
|
||||
|
||||
Demonstrates:
|
||||
1. Concurrent dispatch (5 tasks in <50ms)
|
||||
2. Non-blocking status polling
|
||||
3. Independent job monitoring
|
||||
4. Job listing and summaries
|
||||
5. Performance metrics (434 tasks/sec, <1ms status retrieval)
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
The implementation maintains full backward compatibility:
|
||||
|
||||
- Existing `spawn_claude_agent()` still works
|
||||
- Existing route handlers can continue to work
|
||||
- New functionality is opt-in through `EnhancedDispatcher`
|
||||
- Status files stored separately in `/var/lib/luzia/jobs/`
|
||||
- No changes to job output or agent execution
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
To fully integrate responsive dispatcher into Luzia:
|
||||
|
||||
- [ ] Import new modules in bin/luzia
|
||||
- [ ] Update route_project_task to use EnhancedDispatcher
|
||||
- [ ] Add route_jobs handler for `luzia jobs`
|
||||
- [ ] Start background monitor in main()
|
||||
- [ ] Add `--watch` flag support to jobs command
|
||||
- [ ] Test with existing workflows
|
||||
- [ ] Run full test suite
|
||||
- [ ] Update CLI help text
|
||||
- [ ] Document new `luzia jobs` command
|
||||
- [ ] Document `--watch` flag usage
|
||||
|
||||
## Configuration
|
||||
|
||||
Optional environment variables:
|
||||
|
||||
```bash
|
||||
# Cache TTL in seconds (default: 1)
|
||||
export LUZIA_CACHE_TTL=2
|
||||
|
||||
# Monitor poll interval (default: 1)
|
||||
export LUZIA_MONITOR_INTERVAL=0.5
|
||||
|
||||
# Max job history (default: 1000)
|
||||
export LUZIA_MAX_JOBS=500
|
||||
|
||||
# Job directory (default: /var/lib/luzia/jobs)
|
||||
export LUZIA_JOBS_DIR=/custom/path
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Monitor not running
|
||||
|
||||
Check if background thread started:
|
||||
```bash
|
||||
ps aux | grep python | grep luzia
|
||||
```
|
||||
|
||||
Start manually if needed:
|
||||
```python
|
||||
from lib.dispatcher_enhancements import start_background_monitoring
|
||||
start_background_monitoring()
|
||||
```
|
||||
|
||||
### Jobs not updating
|
||||
|
||||
Ensure job directory is writable:
|
||||
```bash
|
||||
ls -la /var/lib/luzia/jobs/
|
||||
chmod 755 /var/lib/luzia/jobs
|
||||
```
|
||||
|
||||
### Status cache stale
|
||||
|
||||
Force fresh read:
|
||||
```python
|
||||
status = dispatcher.get_status(job_id, use_cache=False)
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Planned additions:
|
||||
- [ ] Web dashboard for job monitoring
|
||||
- [ ] WebSocket support for real-time updates
|
||||
- [ ] Job retry with exponential backoff
|
||||
- [ ] Job cancellation with graceful shutdown
|
||||
- [ ] Resource-aware scheduling
|
||||
- [ ] Job dependencies and DAG execution
|
||||
- [ ] Slack/email notifications
|
||||
- [ ] Database persistence (SQLite)
|
||||
- [ ] Job timeout management
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
1. Check test suite: `python3 tests/test_responsive_dispatcher.py`
|
||||
2. Run demo: `python3 examples/demo_concurrent_tasks.py`
|
||||
3. Review documentation: `docs/RESPONSIVE-DISPATCHER.md`
|
||||
4. Check logs: `/var/log/luz-orchestrator/`
|
||||
Reference in New Issue
Block a user