feat: add smart respawn integration and priority todos (Phase 2)

Smart Respawn Integration (2.5):
- Check circuit breaker OPEN state before respawn (blocks cycle)
- Check RALPH_STATUS EXIT_SIGNAL=true to stop respawn
- Check STATUS=BLOCKED to pause respawn for human intervention
- Use RECOMMENDATION from RALPH_STATUS in update prompt
- Emit respawn:blocked SSE event with reason and details
- Show notification when respawn is blocked
- Visual blocked state with pulsing red indicator

Priority-based Todos (part of 2.3):
- Add RalphTodoPriority type (P0/P1/P2/null)
- Parse priority from todo content (P0:, Critical:, High Priority, etc.)
- Add priority field to RalphTodoItem interface
- Update frontend to display priority badges with colors
- Sort todos by priority (P0 > P1 > P2) then by status
- Add CSS for priority badges and highlighted cards

Bump version to 0.1391

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-27 00:54:02 +01:00
co-authored by Claude Opus 4.5
parent 59b22337a5
commit 925ea09cca
8 changed files with 217 additions and 23 deletions
+47 -3
View File
@@ -2315,6 +2315,38 @@ export class RespawnController extends EventEmitter {
`tokensStable=${status.tokensStable}, ` +
`noWorking=${status.workingPatternsAbsent}`);
// ========== RALPH_STATUS Integration ==========
// Check circuit breaker status - if OPEN, pause respawn
const circuitBreaker = this.session.ralphTracker.circuitBreakerStatus;
if (circuitBreaker.state === 'OPEN') {
this.log(`Respawn blocked - Circuit breaker OPEN: ${circuitBreaker.reason}`);
this.logAction('ralph', `Circuit breaker OPEN: ${circuitBreaker.reason}`);
this.emit('respawnBlocked', { reason: 'circuit_breaker_open', details: circuitBreaker.reason });
this.setState('watching');
// Don't restart timers - wait for manual reset or circuit breaker resolution
return;
}
// Check RALPH_STATUS EXIT_SIGNAL - if true, loop is complete
const statusBlock = this.session.ralphTracker.lastStatusBlock;
if (statusBlock?.exitSignal) {
this.log(`Respawn paused - RALPH_STATUS EXIT_SIGNAL=true`);
this.logAction('ralph', `Exit signal detected: ${statusBlock.recommendation || 'Task complete'}`);
this.emit('respawnBlocked', { reason: 'exit_signal', details: statusBlock.recommendation || 'Task complete' });
this.setState('watching');
// Don't restart timers - loop is complete
return;
}
// Check if STATUS=BLOCKED - trigger circuit breaker
if (statusBlock?.status === 'BLOCKED') {
this.log(`Respawn blocked - RALPH_STATUS reports BLOCKED`);
this.logAction('ralph', `Claude reported BLOCKED: ${statusBlock.recommendation || 'Needs human intervention'}`);
this.emit('respawnBlocked', { reason: 'status_blocked', details: statusBlock.recommendation || 'Needs human intervention' });
this.setState('watching');
return;
}
// Reset detection state
this.completionMessageTime = null;
this.cancelCompletionConfirm();
@@ -2344,6 +2376,7 @@ export class RespawnController extends EventEmitter {
/**
* Send the update docs prompt (first step of cycle).
* Uses RALPH_STATUS RECOMMENDATION if available, otherwise falls back to configured prompt.
* @fires stepSent - With step 'update'
*/
private sendUpdateDocs(): void {
@@ -2356,10 +2389,21 @@ export class RespawnController extends EventEmitter {
this.config.interStepDelayMs,
() => {
this.stepTimer = null;
const input = this.config.updatePrompt + '\r'; // \r triggers Enter in Ink/Claude CLI
this.logAction('command', `Sending: "${this.config.updatePrompt.substring(0, 50)}..."`);
// Use RALPH_STATUS RECOMMENDATION if available, otherwise fall back to config
const statusBlock = this.session.ralphTracker.lastStatusBlock;
let updatePrompt = this.config.updatePrompt;
if (statusBlock?.recommendation) {
// Append RECOMMENDATION to the update prompt for context
updatePrompt = `${this.config.updatePrompt}\n\nClaude's last recommendation: ${statusBlock.recommendation}`;
this.logAction('ralph', `Using RECOMMENDATION: ${statusBlock.recommendation.substring(0, 50)}...`);
}
const input = updatePrompt + '\r'; // \r triggers Enter in Ink/Claude CLI
this.logAction('command', `Sending: "${updatePrompt.substring(0, 50)}..."`);
this.session.writeViaScreen(input);
this.emit('stepSent', 'update', this.config.updatePrompt);
this.emit('stepSent', 'update', updatePrompt);
this.setState('waiting_update');
this.promptDetected = false;
this.workingDetected = false;