mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 20:49:41 +02:00
Phase 1 Ralph enhancement implementation: - Parse RALPH_STATUS blocks from Claude output (status, tasks, files, tests) - Circuit breaker state machine (CLOSED → HALF_OPEN → OPEN) for stuck detection - Dual-condition exit gate (completion indicators >= 2 AND EXIT_SIGNAL) - Frontend: circuit breaker badge (yellow warning, red stuck), status block display - API: circuit breaker reset endpoint, ralph status endpoint - Notifications when circuit breaker opens or exit gate is met Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
13 KiB
13 KiB
Ralph Tracker Phase 1 Implementation Plan
Overview
This plan details how to enhance the existing RalphTracker with RALPH_STATUS block parsing, circuit breaker pattern, and dual-condition exit gate.
1. Current State Analysis
What RalphTracker Already Does Well
- Todo Detection: Supports 5 formats (checkboxes, indicators, status in parentheses, native TodoWrite, checkmark-based)
- Completion Phrases: Detects
<promise>PHRASE</promise>with occurrence-based logic (1st = store, 2nd = complete) - Loop State Tracking: Tracks active/inactive, iteration counts, max iterations, elapsed hours, cycle counts
- Auto-Enable: Disabled by default, auto-enables when Ralph patterns detected
- Event System: Emits
loopUpdate,todoUpdate,completionDetected,enabledevents - SSE Integration: Events forwarded via
session:ralphLoopUpdate,session:ralphTodoUpdate,session:ralphCompletionDetected - Debouncing: EVENT_DEBOUNCE_MS (50ms) for rapid updates to prevent UI jitter
- Cleanup: MAX_TODO_ITEMS (50), TODO_EXPIRY_MS (1 hour), throttled cleanup
Current Limitations
| Feature | Status |
|---|---|
| RALPH_STATUS block parsing | Missing |
| Circuit breaker pattern | Missing |
| Priority-based todos (P0/P1/P2) | Missing |
| Dual-condition exit gate | Missing |
| Files modified tracking | Missing |
| Tests status tracking | Missing |
| Work type classification | Missing |
2. New Type Definitions (types.ts)
// ========== RALPH_STATUS Block Types ==========
export type RalphStatusValue = 'IN_PROGRESS' | 'COMPLETE' | 'BLOCKED';
export type RalphTestsStatus = 'PASSING' | 'FAILING' | 'NOT_RUN';
export type RalphWorkType = 'IMPLEMENTATION' | 'TESTING' | 'DOCUMENTATION' | 'REFACTORING';
/**
* Parsed RALPH_STATUS block from Claude output.
*/
export interface RalphStatusBlock {
status: RalphStatusValue;
tasksCompletedThisLoop: number;
filesModified: number;
testsStatus: RalphTestsStatus;
workType: RalphWorkType;
exitSignal: boolean;
recommendation: string;
parsedAt: number;
}
// ========== Circuit Breaker Types ==========
export type CircuitBreakerState = 'CLOSED' | 'HALF_OPEN' | 'OPEN';
export type CircuitBreakerReason =
| 'normal_operation'
| 'no_progress_warning'
| 'no_progress_open'
| 'same_error_repeated'
| 'tests_failing_too_long'
| 'progress_detected'
| 'manual_reset';
export interface CircuitBreakerStatus {
state: CircuitBreakerState;
consecutiveNoProgress: number;
consecutiveSameError: number;
consecutiveTestsFailure: number;
lastProgressIteration: number;
reason: string;
reasonCode: CircuitBreakerReason;
lastTransitionAt: number;
lastErrorMessage: string | null;
}
// ========== Priority Todo Types ==========
export type RalphTodoPriority = 'P0' | 'P1' | 'P2' | null;
// ========== Helper Functions ==========
export function createInitialCircuitBreakerStatus(): CircuitBreakerStatus {
return {
state: 'CLOSED',
consecutiveNoProgress: 0,
consecutiveSameError: 0,
consecutiveTestsFailure: 0,
lastProgressIteration: 0,
reason: 'Initial state',
reasonCode: 'normal_operation',
lastTransitionAt: Date.now(),
lastErrorMessage: null,
};
}
3. New Regex Patterns (ralph-tracker.ts)
// ---------- RALPH_STATUS Block Patterns ----------
const RALPH_STATUS_START_PATTERN = /^---RALPH_STATUS---\s*$/;
const RALPH_STATUS_END_PATTERN = /^---END_RALPH_STATUS---\s*$/;
const RALPH_STATUS_FIELD_PATTERN = /^STATUS:\s*(IN_PROGRESS|COMPLETE|BLOCKED)\s*$/i;
const RALPH_TASKS_COMPLETED_PATTERN = /^TASKS_COMPLETED_THIS_LOOP:\s*(\d+)\s*$/i;
const RALPH_FILES_MODIFIED_PATTERN = /^FILES_MODIFIED:\s*(\d+)\s*$/i;
const RALPH_TESTS_STATUS_PATTERN = /^TESTS_STATUS:\s*(PASSING|FAILING|NOT_RUN)\s*$/i;
const RALPH_WORK_TYPE_PATTERN = /^WORK_TYPE:\s*(IMPLEMENTATION|TESTING|DOCUMENTATION|REFACTORING)\s*$/i;
const RALPH_EXIT_SIGNAL_PATTERN = /^EXIT_SIGNAL:\s*(true|false)\s*$/i;
const RALPH_RECOMMENDATION_PATTERN = /^RECOMMENDATION:\s*(.+)$/i;
// ---------- Completion Indicator Patterns ----------
const COMPLETION_INDICATOR_PATTERNS = [
/all\s+(?:tasks?|items?|work)\s+(?:are\s+)?(?:completed?|done|finished)/i,
/(?:completed?|finished)\s+all\s+(?:tasks?|items?|work)/i,
/nothing\s+(?:left|remaining)\s+to\s+do/i,
/no\s+more\s+(?:tasks?|items?|work)/i,
/everything\s+(?:is\s+)?(?:completed?|done)/i,
];
// ---------- Priority Pattern ----------
const TODO_PRIORITY_PATTERN = /^\s*(?:\[.\])?\s*(?:Critical:|Blocker:|Feature:|Improvement:)?\s*\(?(P[012])\)?:?\s*/i;
4. New State Properties (ralph-tracker.ts)
// Add to RalphTracker class
// Circuit breaker state tracking
private _circuitBreaker: CircuitBreakerStatus;
// RALPH_STATUS block parsing state
private _statusBlockBuffer: string[] = [];
private _inStatusBlock: boolean = false;
private _lastStatusBlock: RalphStatusBlock | null = null;
// Dual-condition exit tracking
private _completionIndicators: number = 0;
private _exitGateMet: boolean = false;
// Cumulative tracking
private _totalFilesModified: number = 0;
private _totalTasksCompleted: number = 0;
5. New Methods to Implement
5.1 RALPH_STATUS Block Parsing
private processStatusBlockLine(line: string): void {
const trimmed = line.trim();
if (RALPH_STATUS_START_PATTERN.test(trimmed)) {
this._inStatusBlock = true;
this._statusBlockBuffer = [];
return;
}
if (this._inStatusBlock && RALPH_STATUS_END_PATTERN.test(trimmed)) {
this._inStatusBlock = false;
this.parseStatusBlock(this._statusBlockBuffer);
this._statusBlockBuffer = [];
return;
}
if (this._inStatusBlock) {
this._statusBlockBuffer.push(trimmed);
}
}
private parseStatusBlock(lines: string[]): void {
const block: Partial<RalphStatusBlock> = { parsedAt: Date.now() };
for (const line of lines) {
// Parse each field...
}
if (block.status !== undefined) {
this._lastStatusBlock = fullBlock;
this.handleStatusBlock(fullBlock);
}
}
private handleStatusBlock(block: RalphStatusBlock): void {
this._totalFilesModified += block.filesModified;
this._totalTasksCompleted += block.tasksCompletedThisLoop;
const hasProgress = block.filesModified > 0 || block.tasksCompletedThisLoop > 0;
this.updateCircuitBreaker(hasProgress, block.testsStatus, block.status);
if (block.status === 'COMPLETE') {
this._completionIndicators++;
}
if (block.exitSignal && this._completionIndicators >= 2) {
this._exitGateMet = true;
this.emit('exitGateMet', { completionIndicators: this._completionIndicators, exitSignal: true });
}
this.emit('statusBlockDetected', block);
}
5.2 Circuit Breaker Logic
private updateCircuitBreaker(
hasProgress: boolean,
testsStatus: RalphTestsStatus,
status: RalphStatusValue
): void {
const prevState = this._circuitBreaker.state;
if (hasProgress) {
this._circuitBreaker.consecutiveNoProgress = 0;
this._circuitBreaker.lastProgressIteration = this._loopState.cycleCount;
if (this._circuitBreaker.state === 'HALF_OPEN') {
this._circuitBreaker.state = 'CLOSED';
this._circuitBreaker.reasonCode = 'progress_detected';
}
} else {
this._circuitBreaker.consecutiveNoProgress++;
if (this._circuitBreaker.state === 'CLOSED') {
if (this._circuitBreaker.consecutiveNoProgress >= 3) {
this._circuitBreaker.state = 'OPEN';
this._circuitBreaker.reasonCode = 'no_progress_open';
} else if (this._circuitBreaker.consecutiveNoProgress >= 2) {
this._circuitBreaker.state = 'HALF_OPEN';
this._circuitBreaker.reasonCode = 'no_progress_warning';
}
}
}
if (prevState !== this._circuitBreaker.state) {
this._circuitBreaker.lastTransitionAt = Date.now();
this.emit('circuitBreakerUpdate', { ...this._circuitBreaker });
}
}
resetCircuitBreaker(): void {
this._circuitBreaker = createInitialCircuitBreakerStatus();
this._circuitBreaker.reasonCode = 'manual_reset';
this.emit('circuitBreakerUpdate', { ...this._circuitBreaker });
}
5.3 Update processLine Method
private processLine(line: string): void {
const trimmed = line.trim();
if (!trimmed) return;
// NEW: Check for RALPH_STATUS block
this.processStatusBlockLine(trimmed);
// NEW: Check for completion indicators
this.detectCompletionIndicators(trimmed);
// EXISTING: Rest of the detection methods...
this.detectCompletionPhrase(trimmed);
this.detectAllTasksComplete(trimmed);
this.detectTaskCompletion(trimmed);
this.detectLoopStatus(trimmed);
this.detectTodoItems(trimmed);
}
6. New Events to Add
export interface RalphTrackerEvents {
// Existing events
loopUpdate: (state: RalphTrackerState) => void;
todoUpdate: (todos: RalphTodoItem[]) => void;
completionDetected: (phrase: string) => void;
enabled: () => void;
// New events
statusBlockDetected: (block: RalphStatusBlock) => void;
circuitBreakerUpdate: (status: CircuitBreakerStatus) => void;
exitGateMet: (data: { completionIndicators: number; exitSignal: boolean }) => void;
}
7. Server Integration (server.ts)
// Add new SSE event handlers in setupSessionListeners()
session.on('ralphStatusBlockDetected', (block: RalphStatusBlock) => {
this.broadcast('session:ralphStatusUpdate', { sessionId: session.id, block });
});
session.on('ralphCircuitBreakerUpdate', (status: CircuitBreakerStatus) => {
this.broadcast('session:circuitBreakerUpdate', { sessionId: session.id, status });
});
session.on('ralphExitGateMet', (data) => {
this.broadcast('session:exitGateMet', { sessionId: session.id, ...data });
});
// Add API endpoint for circuit breaker reset
this.app.post('/api/sessions/:id/ralph-circuit-breaker/reset', async (req) => {
const session = this.sessions.get(req.params.id);
if (!session) return { success: false, error: 'Session not found' };
session.ralphTracker?.resetCircuitBreaker();
return { success: true };
});
8. Frontend Changes (app.js)
New SSE Event Listeners
this.eventSource.addEventListener('session:ralphStatusUpdate', (e) => {
const data = JSON.parse(e.data);
this.updateRalphStatusBlock(data.sessionId, data.block);
});
this.eventSource.addEventListener('session:circuitBreakerUpdate', (e) => {
const data = JSON.parse(e.data);
this.updateCircuitBreaker(data.sessionId, data.status);
});
New Rendering Methods
updateRalphStatusBlock(sessionId, block) {
// Store and render status block
}
renderRalphStatusBlock(block) {
// Render STATUS, WORK_TYPE, TESTS_STATUS, RECOMMENDATION
}
updateCircuitBreaker(sessionId, status) {
// Store and render circuit breaker state
}
renderCircuitBreaker(status) {
// Render badge: green (CLOSED), yellow (HALF_OPEN), red (OPEN)
}
9. Implementation Order
| Step | Task | Time |
|---|---|---|
| 1 | Add type definitions to types.ts |
30 min |
| 2 | Add regex patterns to ralph-tracker.ts |
30 min |
| 3 | Add state properties to RalphTracker class | 15 min |
| 4 | Implement RALPH_STATUS parsing methods | 1.5 hr |
| 5 | Implement circuit breaker logic | 1 hr |
| 6 | Implement completion indicators | 30 min |
| 7 | Update events interface | 15 min |
| 8 | Add server SSE handlers and API endpoint | 45 min |
| 9 | Add frontend event listeners and rendering | 1 hr |
| 10 | Add CSS styles | 30 min |
| 11 | Update HTML structure | 15 min |
| 12 | Write unit tests | 1.5 hr |
Total: ~8 hours
10. Files to Modify
| File | Changes |
|---|---|
src/types.ts |
Add RalphStatusBlock, CircuitBreakerStatus, helper functions |
src/ralph-tracker.ts |
Add patterns, state, parsing methods, circuit breaker |
src/web/server.ts |
Add SSE handlers, circuit breaker reset endpoint |
src/web/public/app.js |
Add event listeners, rendering methods |
src/web/public/styles.css |
Add status block and circuit breaker styles |
src/web/public/index.html |
Add UI elements to Ralph panel |
test/ralph-tracker.test.ts |
Add tests for new functionality |
11. Test Cases to Add
-
RALPH_STATUS Parsing
- Parse valid status block with all fields
- Parse block with missing optional fields
- Ignore malformed blocks
- Handle multiple blocks in sequence
-
Circuit Breaker State Transitions
- CLOSED → HALF_OPEN on 2 no-progress
- HALF_OPEN → OPEN on 3 no-progress
- HALF_OPEN → CLOSED on progress
- Manual reset from OPEN
-
Dual-Condition Exit Gate
- Exit when indicators >= 2 AND exitSignal = true
- No exit when indicators >= 2 but exitSignal = false
- No exit when exitSignal = true but indicators < 2
-
Integration Tests
- SSE events broadcast correctly
- UI updates on status block detection
- Circuit breaker badge updates