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:
316
examples/status_integration_example.py
Normal file
316
examples/status_integration_example.py
Normal file
@@ -0,0 +1,316 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Luzia Status System - Integration Example
|
||||
|
||||
This example demonstrates how to integrate the status publishing system
|
||||
into your orchestrator code. Each section shows the 7 key integration points.
|
||||
|
||||
Copy these patterns into your existing code wherever you dispatch tasks,
|
||||
monitor progress, or handle completion/failures.
|
||||
"""
|
||||
|
||||
import time
|
||||
import uuid
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Import the sync wrapper (works in both async and sync contexts)
|
||||
from luzia_status_sync_wrapper import get_sync_publisher
|
||||
|
||||
|
||||
class TaskDispatcherWithStatus:
|
||||
"""Example task dispatcher with integrated status publishing"""
|
||||
|
||||
def __init__(self):
|
||||
self.publisher = get_sync_publisher()
|
||||
|
||||
def dispatch_task(self, project: str, description: str, estimated_duration: int = 600):
|
||||
"""
|
||||
Example 1: Publish task started
|
||||
Location: In your task dispatcher when you create a new task
|
||||
"""
|
||||
task_id = f"{project}-{uuid.uuid4().hex[:8]}"
|
||||
|
||||
logger.info(f"Dispatching task: {task_id}")
|
||||
|
||||
# PUBLISHING POINT #1: Task Started
|
||||
self.publisher.publish_task_started(
|
||||
task_id=task_id,
|
||||
project=project,
|
||||
description=description,
|
||||
estimated_duration_seconds=estimated_duration
|
||||
)
|
||||
|
||||
return task_id
|
||||
|
||||
def monitor_task_progress(self, task_id: str, project: str, total_steps: int = 4):
|
||||
"""
|
||||
Example 2 & 5: Publish progress updates and warnings
|
||||
Location: In your main task execution loop
|
||||
"""
|
||||
start_time = time.time()
|
||||
step_names = ["Analyzing", "Processing", "Validating", "Finalizing"]
|
||||
|
||||
for step_num, step_name in enumerate(step_names, 1):
|
||||
logger.info(f" Step {step_num}/{total_steps}: {step_name}")
|
||||
|
||||
# Simulate work
|
||||
time.sleep(2)
|
||||
|
||||
elapsed = int(time.time() - start_time)
|
||||
progress = int((step_num / total_steps) * 100)
|
||||
|
||||
# PUBLISHING POINT #2: Progress Update
|
||||
# Do this every 30 seconds or at significant milestones
|
||||
self.publisher.publish_progress(
|
||||
task_id=task_id,
|
||||
progress_percent=progress,
|
||||
current_step=step_num,
|
||||
total_steps=total_steps,
|
||||
current_step_name=step_name,
|
||||
elapsed_seconds=elapsed,
|
||||
estimated_remaining_seconds=int((600 - elapsed) * (100 - progress) / 100)
|
||||
)
|
||||
|
||||
# PUBLISHING POINT #5: Warning (if approaching time limit)
|
||||
if elapsed > 480: # 80% of 600 second budget
|
||||
remaining = int(600 - elapsed)
|
||||
if remaining < 120: # Less than 2 minutes left
|
||||
self.publisher.publish_warning(
|
||||
task_id=task_id,
|
||||
warning_type="DURATION_EXCEEDED",
|
||||
message=f"Task approaching time limit: {remaining}s remaining",
|
||||
current_step=step_num,
|
||||
total_steps=total_steps,
|
||||
current_step_name=step_name,
|
||||
elapsed_seconds=elapsed,
|
||||
progress_percent=progress,
|
||||
recommendation="May need optimization or extension"
|
||||
)
|
||||
|
||||
def complete_task(self, task_id: str, project: str, elapsed_secs: int, findings: list):
|
||||
"""
|
||||
Example 3: Publish task completed
|
||||
Location: When task finishes successfully
|
||||
"""
|
||||
logger.info(f"Task completed: {task_id}")
|
||||
|
||||
# PUBLISHING POINT #3: Task Completed
|
||||
self.publisher.publish_task_completed(
|
||||
task_id=task_id,
|
||||
elapsed_seconds=elapsed_secs,
|
||||
findings_count=len(findings),
|
||||
recommendations_count=1, # Number of recommendations
|
||||
status="APPROVED" # or NEEDS_WORK, REJECTED
|
||||
)
|
||||
|
||||
def fail_task(self, task_id: str, error: str, elapsed_secs: int, retry_count: int):
|
||||
"""
|
||||
Example 6: Publish task failed
|
||||
Location: In your error handler
|
||||
"""
|
||||
logger.error(f"Task failed: {task_id}")
|
||||
|
||||
# PUBLISHING POINT #6: Task Failed
|
||||
self.publisher.publish_task_failed(
|
||||
task_id=task_id,
|
||||
error=error,
|
||||
elapsed_seconds=elapsed_secs,
|
||||
retry_count=retry_count,
|
||||
retriable=retry_count < 5 # Can be retried?
|
||||
)
|
||||
|
||||
|
||||
class QueueManagerWithStatus:
|
||||
"""Example queue manager with integrated status publishing"""
|
||||
|
||||
def __init__(self):
|
||||
self.publisher = get_sync_publisher()
|
||||
self.queue = []
|
||||
|
||||
def queue_task(self, task_id: str, project: str, description: str, reason: str, wait_estimate: int):
|
||||
"""
|
||||
Example 4: Publish task queued
|
||||
Location: In your queue manager when adding to queue
|
||||
"""
|
||||
queue_position = len(self.queue) + 1
|
||||
|
||||
logger.info(f"Queuing task: {task_id} (position {queue_position})")
|
||||
|
||||
# PUBLISHING POINT #4: Task Queued
|
||||
self.publisher.publish_task_queued(
|
||||
task_id=task_id,
|
||||
project=project,
|
||||
description=description,
|
||||
reason=reason, # Why it was queued
|
||||
queue_position=queue_position,
|
||||
queue_ahead=[t['id'] for t in self.queue], # Tasks ahead in queue
|
||||
estimated_wait_seconds=wait_estimate
|
||||
)
|
||||
|
||||
self.queue.append({
|
||||
'id': task_id,
|
||||
'project': project,
|
||||
'description': description
|
||||
})
|
||||
|
||||
|
||||
class SystemMonitorWithStatus:
|
||||
"""Example system monitor with integrated status publishing"""
|
||||
|
||||
def __init__(self):
|
||||
self.publisher = get_sync_publisher()
|
||||
|
||||
def check_system_health(self):
|
||||
"""
|
||||
Example 7: Publish system alert
|
||||
Location: In your system health monitor
|
||||
"""
|
||||
import psutil
|
||||
|
||||
# Check memory
|
||||
memory_percent = psutil.virtual_memory().percent
|
||||
if memory_percent > 80:
|
||||
# PUBLISHING POINT #7: System Alert
|
||||
self.publisher.publish_system_alert(
|
||||
alert_type="RESOURCE_WARNING",
|
||||
message=f"Memory usage at {memory_percent}%",
|
||||
recommendation="New tasks will be queued until memory is freed",
|
||||
severity="warning"
|
||||
)
|
||||
|
||||
# Check disk
|
||||
disk_percent = psutil.disk_usage('/').percent
|
||||
if disk_percent > 90:
|
||||
self.publisher.publish_system_alert(
|
||||
alert_type="DISK_CRITICAL",
|
||||
message=f"Disk usage at {disk_percent}%",
|
||||
recommendation="Immediate cleanup required",
|
||||
severity="critical"
|
||||
)
|
||||
|
||||
|
||||
# Example usage
|
||||
def example_task_lifecycle():
|
||||
"""
|
||||
Demonstrate the complete task lifecycle with status publishing
|
||||
|
||||
This shows all 7 integration points in action
|
||||
"""
|
||||
dispatcher = TaskDispatcherWithStatus()
|
||||
queue_manager = QueueManagerWithStatus()
|
||||
monitor = SystemMonitorWithStatus()
|
||||
|
||||
# Example 1: Dispatch a task
|
||||
task_id = dispatcher.dispatch_task(
|
||||
project="musica",
|
||||
description="Fix audio synthesis engine",
|
||||
estimated_duration=600
|
||||
)
|
||||
|
||||
try:
|
||||
# Example 2 & 5: Monitor progress (with warnings)
|
||||
dispatcher.monitor_task_progress(task_id, "musica")
|
||||
|
||||
# Example 3: Complete the task
|
||||
dispatcher.complete_task(
|
||||
task_id=task_id,
|
||||
project="musica",
|
||||
elapsed_secs=615,
|
||||
findings=["Issue A", "Issue B"]
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
# Example 6: Handle failures
|
||||
dispatcher.fail_task(
|
||||
task_id=task_id,
|
||||
error=str(e),
|
||||
elapsed_secs=300,
|
||||
retry_count=1
|
||||
)
|
||||
|
||||
|
||||
def example_queue_management():
|
||||
"""Demonstrate queuing with status publishing"""
|
||||
queue_manager = QueueManagerWithStatus()
|
||||
|
||||
# Example 4: Queue a task (when system is busy)
|
||||
queue_manager.queue_task(
|
||||
task_id="admin-code-001",
|
||||
project="admin",
|
||||
description="Code review and cleanup",
|
||||
reason="System resource limit reached",
|
||||
wait_estimate=300
|
||||
)
|
||||
|
||||
|
||||
def example_system_monitoring():
|
||||
"""Demonstrate system monitoring with alerts"""
|
||||
monitor = SystemMonitorWithStatus()
|
||||
|
||||
# Example 7: Check system health
|
||||
try:
|
||||
monitor.check_system_health()
|
||||
except ImportError:
|
||||
logger.warning("psutil not available, skipping system check")
|
||||
|
||||
|
||||
# Integration Points Summary
|
||||
"""
|
||||
To integrate into your orchestrator, add the following 7 calls:
|
||||
|
||||
1. Task Dispatcher (when creating task):
|
||||
publisher.publish_task_started(task_id, project, description, estimated_duration)
|
||||
|
||||
2. Progress Loop (every 30 seconds):
|
||||
publisher.publish_progress(task_id, progress_percent, current_step, total_steps,
|
||||
current_step_name, elapsed_seconds, estimated_remaining)
|
||||
|
||||
3. Task Completion (when task succeeds):
|
||||
publisher.publish_task_completed(task_id, elapsed_seconds, findings_count, status)
|
||||
|
||||
4. Queue Manager (when queueing task):
|
||||
publisher.publish_task_queued(task_id, project, description, reason,
|
||||
queue_position, queue_ahead, wait_estimate)
|
||||
|
||||
5. Resource Monitor (when warning threshold exceeded):
|
||||
publisher.publish_warning(task_id, warning_type, message, current_step,
|
||||
total_steps, current_step_name, elapsed_seconds,
|
||||
progress_percent, recommendation)
|
||||
|
||||
6. Error Handler (when task fails):
|
||||
publisher.publish_task_failed(task_id, error, elapsed_seconds,
|
||||
retry_count, retriable)
|
||||
|
||||
7. System Monitor (on health issues):
|
||||
publisher.publish_system_alert(alert_type, message, recommendation, severity)
|
||||
|
||||
Each call is idempotent and safe to use in production.
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n" + "=" * 60)
|
||||
print("LUZIA STATUS INTEGRATION EXAMPLES")
|
||||
print("=" * 60)
|
||||
|
||||
print("\n1. Task Lifecycle Example:")
|
||||
print("-" * 60)
|
||||
example_task_lifecycle()
|
||||
|
||||
print("\n2. Queue Management Example:")
|
||||
print("-" * 60)
|
||||
example_queue_management()
|
||||
|
||||
print("\n3. System Monitoring Example:")
|
||||
print("-" * 60)
|
||||
example_system_monitoring()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Integration complete - status events published")
|
||||
print("=" * 60)
|
||||
Reference in New Issue
Block a user