fix: address medium severity issues from code review

- Fix inconsistent null handling in state-store (use ?? instead of ||)
- Add JSON.stringify error handling with specific messages
- Add error handling for SSE init event in app.js
- Fix spawn orchestrator silent failures (add logging)
- Fix statSync race condition in subagent-watcher
- Fix unbounded _taskNumberToContent Map in ralph-tracker
- Fix YAML pattern case sensitivity in ralph-config

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-26 13:58:34 +01:00
co-authored by Claude Opus 4.5
parent e81dc4665a
commit 3c2e89f427
9 changed files with 106 additions and 34 deletions
+20
View File
@@ -217,6 +217,9 @@ const TASK_DONE_PATTERN = /(?:task|item|todo)\s*(?:#?\d+|"\s*[^"]+\s*")?\s*(?:is
*/
const ANSI_ESCAPE_PATTERN = /\x1b\[[0-9;]*[A-Za-z]/g;
/** Maximum number of task number to content mappings to track */
const MAX_TASK_MAPPINGS = 100;
// ========== Event Types ==========
/**
@@ -1159,6 +1162,7 @@ export class RalphTracker extends EventEmitter {
const content = match[2].trim();
if (content.length >= 5) {
this._taskNumberToContent.set(taskNum, content);
this.enforceTaskMappingLimit();
this.upsertTodo(content, 'pending');
updated = true;
}
@@ -1173,6 +1177,7 @@ export class RalphTracker extends EventEmitter {
// Only register if not already known from a "created" line
if (!this._taskNumberToContent.has(taskNum)) {
this._taskNumberToContent.set(taskNum, content);
this.enforceTaskMappingLimit();
}
this.upsertTodo(this._taskNumberToContent.get(taskNum) || content, 'pending');
updated = true;
@@ -1456,6 +1461,21 @@ export class RalphTracker extends EventEmitter {
this.emit('loopUpdate', this.loopState);
}
/**
* Enforce size limit on _taskNumberToContent map.
* Removes lowest task numbers (oldest tasks) when limit exceeded.
*/
private enforceTaskMappingLimit(): void {
if (this._taskNumberToContent.size <= MAX_TASK_MAPPINGS) return;
// Sort keys and remove lowest (oldest) task numbers
const sortedKeys = Array.from(this._taskNumberToContent.keys()).sort((a, b) => a - b);
const keysToRemove = sortedKeys.slice(0, this._taskNumberToContent.size - MAX_TASK_MAPPINGS);
for (const key of keysToRemove) {
this._taskNumberToContent.delete(key);
}
}
/**
* Clear all state and disable the tracker.
*