1103 Commits

Author SHA1 Message Date
AndyMik90 7e869650d6 fix: pass archive filter through loadInsightsSession to prevent overwrite
- Add includeArchived parameter to loadInsightsSession and propagate to
  loadInsightsSessions, deleteSession, and clearSession
- Update all callers in Insights.tsx to pass showArchived through
- Remove projectId from showArchived effect deps to prevent duplicate
  loads on project switch (mount effect already handles it)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 42372141ec fix: address PR review findings for bulk delete/archive chat history
- Return success:false from IPC handlers when bulk operations have failures
- Propagate failedIds through store functions for proper error reporting
- Add session ID validation in paths.ts to prevent path traversal
- Prune selectedIds when sessions list changes to avoid stale selections
- Fix isFirstRun race condition when projectId changes in Insights
- Convert archivedAt to Date in loadSessionById for type consistency
- Fix accessibility: conditional tabIndex based on selection mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 d76368e1b9 fix: address all new PR review findings for bulk delete/archive functionality
Comprehensive fixes addressing 4 critical and accessibility issues:

**Critical Bug Fixes:**
1. Fixed UI state mismatch when active session is archived/deleted
   - Added loadInsightsSession() call after bulk operations
   - Ensures frontend stays in sync when backend auto-switches sessions

2. Fixed race condition causing flicker on projectId change
   - Added prevProjectId ref to reset skip flag per-projectId
   - Prevents duplicate loadInsightsSessions calls when showArchived is true

3. Added comprehensive error handling to all bulk handlers
   - Wrapped all async operations in try/catch blocks
   - Added detailed error logging with session IDs and context
   - Prevents unhandled promise rejections from IPC failures

**Accessibility Improvements:**
4. Fixed selection mode accessibility in SessionItem
   - Removed redundant checkbox onCheckedChange to prevent double-toggle
   - Made parent row fully interactive with role="checkbox" in selection mode
   - Added aria-checked attribute for screen reader support
   - Wired row onClick and keyboard handlers to toggle selection
   - Users can now activate selection via row click/keyboard or nested checkbox

All changes maintain backward compatibility and improve robustness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 02b40fa342 fix: address all PR review findings for bulk delete/archive functionality
Comprehensive code quality improvements addressing all review comments:

**Critical Fixes:**
- Fixed double-toggle bug where checkbox called onToggleSelect twice
- Added accessibility attributes (role, tabIndex, onKeyDown) to interactive div

**Error Handling:**
- Removed error re-throws in async event handlers to prevent unhandled rejections
- Added error logging to empty catch blocks in session-storage.ts
- Added error handling with .catch() for dropdown menu promise rejections
- Added logging for partial failures in bulk operations

**Code Quality:**
- Replaced non-null assertion (!) with explicit null check in session-manager.ts
- Removed redundant async/await wrappers in archive/unarchive handlers
- Removed duplicate loadInsightsSession calls from store functions
- Added skip-first-run guard to prevent double load on component mount
- Added clarifying comment for showArchived useEffect dependency

**Performance:**
- Eliminated redundant IPC calls by removing duplicate session reloads
- Fixed flicker issue caused by double reload with different filters

All changes maintain backward compatibility and improve user experience.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 9f3ac54844 fix: properly await async bulk operations in ChatHistorySidebar
Fixed fire-and-forget async operations that were clearing UI state
before operations completed. Changes:

- Updated prop types to return Promise<void> for async callbacks
- Made handleBulkDelete and handleBulkArchive async functions
- Added await for all async callback invocations
- Added try/catch error handling with console logging
- Updated SessionItem prop types for onArchive/onUnarchive
- Made single archive/unarchive arrow functions async

This ensures UI state is only cleared after operations complete
successfully and prevents unhandled promise rejections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 e547e96e26 fix: pass showArchived flag when reloading sessions after bulk operations (qa-requested)
Make handleArchiveSession, handleUnarchiveSession, handleDeleteSessions,
and handleArchiveSessions async, await store calls, then reload sessions
with showArchived flag to preserve archived session visibility.

QA Fix Session: 2
2026-02-18 15:05:54 +01:00
AndyMik90 2d9911cd21 auto-claude: subtask-5-3 - Update Insights.tsx to wire new ChatHistorySidebar props
Add showArchived state, import bulk store helpers (deleteSessions, archiveSession,
archiveSessions, unarchiveSession), create handler functions, add useEffect to
reload sessions when showArchived changes, and pass all new props to ChatHistorySidebar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 0f952a7b96 auto-claude: subtask-5-2 - Add selection mode, bulk actions, and archive support to ChatHistorySidebar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 1e44a909c1 auto-claude: subtask-5-1 - Add i18n translation keys for chat history bulk delete and archive
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 0bf75d1b67 auto-claude: subtask-4-1 - Add bulk delete/archive helper functions to insights-store
Add deleteSessions, archiveSession, archiveSessions, unarchiveSession helpers
and update loadInsightsSessions to accept optional includeArchived parameter.
Also update ElectronAPI types and browser mocks for new methods.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 4198bde687 auto-claude: subtask-3-3 - Extend preload InsightsAPI with bulk delete/archive methods
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 60b8a77d69 auto-claude: subtask-3-2 - Register new IPC handlers in insights-handlers.ts
Add handlers for bulk delete, archive, bulk archive, and unarchive
sessions. Update list sessions handler to accept includeArchived param.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 ffad4fc53d auto-claude: subtask-3-1 - Add delegation methods to InsightsService
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 c3a927ae54 auto-claude: subtask-2-2 - Add archive/unarchive/bulk methods to SessionManager
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 9626ba42a2 auto-claude: subtask-2-1 - Add archive/unarchive/bulk methods to SessionStorage
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 1c2918fe0b auto-claude: subtask-1-2 - Add new IPC channel constants for bulk delete and archive
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
AndyMik90 2f43b2af24 auto-claude: subtask-1-1 - Add archivedAt optional field to InsightsSession and InsightsSessionSummary
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:05:54 +01:00
Andy 40364a016d Merge branch 'develop' into auto-claude/222-refactor-roadmap-tasks-with-xstate 2026-02-18 14:49:16 +01:00
Andy 819f98d9fa fix: handle empty/greenfield projects in spec creation (#1426) (#1841)
* fix: handle empty/greenfield projects in spec creation and prevent stuck planning state (#1426)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review findings - planning_phase_ended bug, type hints, dedup (#1426)

- Convert planning_phase_ended to instance attribute self._planning_phase_ended
  so _run_phases() can mark it True after each end_phase() call, preventing
  double-end on exception propagation
- Add Path type annotation to _is_greenfield_project(spec_dir)
- Extract duplicated greenfield detection into _check_and_log_greenfield() helper
- Add TaskLogger and types.ModuleType type hints to _run_phases() signature
- Simplify redundant SystemExit handler with explanatory comment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prevent greenfield false positive on missing/corrupt project index

When get_project_index_stats() returns {} (file missing, JSON parse
error, or unrecognized format), _is_greenfield_project() now returns
False instead of incorrectly classifying the project as greenfield.
Also removes unused TYPE_CHECKING import and empty conditional block.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:49:05 +01:00
Andy 28a620079f fix: clear terminalEventSeen on task restart to prevent stuck-after-planning (#1828) (#1840)
* fix: clear terminalEventSeen on task restart to prevent stuck-after-planning (#1828)

The terminalEventSeen Set in TaskStateManager was never cleared when a task
was restarted. When spec_runner.py emits PLANNING_COMPLETE, the taskId is
added to terminalEventSeen. If the subsequent coding process (run.py) fails,
handleProcessExited() returns early because terminalEventSeen.has(taskId)
is true, silently swallowing the PROCESS_EXITED event. The XState actor
never transitions, leaving the task permanently stuck in 'coding' state.

Additionally, lastSequenceByTask from the old process would cause events
from a new process (starting at sequence 0) to be dropped as duplicates.

Fix: Add prepareForRestart(taskId) method that clears both terminalEventSeen
and lastSequenceByTask without stopping the XState actor. Call it in all 4
locations where a new agent process is started:
- TASK_START handler
- TASK_STOP handler (so subsequent restart works)
- TASK_UPDATE_STATUS auto-start path
- TASK_RECOVER_STUCK auto-restart path

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add prepareForRestart to TASK_REVIEW rejection path

Add missing prepareForRestart(taskId) call before startQAProcess() in the
TASK_REVIEW rejection handler. This is the 5th location where a new agent
process is started for an existing task, but was missed in the original fix.
Without this, if the QA fixer process crashes after a review rejection,
terminalEventSeen would cause handleProcessExited() to swallow the exit
event, leaving the task permanently stuck.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:42:01 +01:00
Andy fb3a3fbda7 fix: watch worktree path for implementation_plan.json changes (#1805) (#1842)
* fix: watch worktree path for implementation_plan.json changes (#1805)

The FileWatcher was always watching the main project's spec directory for
implementation_plan.json changes. When tasks run in a worktree, the backend
writes the plan file to the worktree directory instead, so the watcher never
detected changes and subtask progress was never sent to the UI.

Changes:
- Add getSpecDirForWatcher() helper that checks worktree path first
- Update all 3 file watcher setup locations (TASK_START, TASK_UPDATE_STATUS
  auto-start, TASK_RECOVER_STUCK auto-restart) to use worktree-aware paths
- Add re-watch logic in execution-progress handler: when a worktree appears
  after task start, automatically switch the watcher to the worktree path
- Add worktree fallback in exit handler for reading final plan state
- Add getWatchedSpecDir() method to FileWatcher for path comparison

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review findings - naming consistency, async error handling (#1805)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review findings for file watcher race condition and error handling

- Add pendingWatches guard in FileWatcher.watch() to prevent overlapping async
  calls from creating duplicate watchers (CodeRabbit critical finding)
- Add .catch() to all three fire-and-forget fileWatcher.watch() calls in
  execution-handlers.ts to prevent unhandled promise rejections
- Remove shadowed specsBaseDir re-declaration in autoRestart block, reusing
  the outer variable from the same TASK_RECOVER_STUCK handler scope

* fix: address PR review findings for file-watcher race conditions and variable shadowing

- Change pendingWatches from Set<string> to Map<string, string> (taskId->specDir)
  so re-watch calls with a different specDir are allowed through instead of silently dropped
- Add cancelledWatches Set to coordinate unwatch() with in-flight watch() calls,
  preventing watcher leaks when unwatch() runs during watch()'s await points
- Add .catch() handler to fileWatcher.unwatch() call in agent-events-handlers exit handler,
  consistent with the .catch() pattern used for all watch() calls
- Remove shadowed const mainSpecDir re-declaration inside autoRestart block in
  execution-handlers.ts, using the outer variable from the enclosing try block instead

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve FileWatcher race conditions and unhandled promise rejections

- Add supersession check in watch() after awaiting existing watcher close
  to prevent a later concurrent call from having its watcher overwritten
- Return early in unwatch() when a watch() is in-flight to prevent
  double-closing the same FSWatcher
- Cancel in-flight watch() calls in unwatchAll() by marking their taskIds
  in cancelledWatches before closing existing watchers
- Add .catch() to fileWatcher.unwatch() calls in TASK_STOP and
  TASK_RECOVER_STUCK handlers to surface errors instead of silently dropping them

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve concurrent watch() race conditions in FileWatcher

- Make finally block conditional so superseding watch() calls are not
  wiped out by the superseded call cleaning up pendingWatches
- Delete watcher from map before awaiting close() to prevent concurrent
  calls from double-closing the same FSWatcher reference
- Make cancelledWatches cleanup conditional on the call still owning the
  pendingWatches entry, preventing premature flag removal for concurrent calls
- Fix misleading comment about mainSpecDir declaration scope

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve PR review findings for FileWatcher dead code, missing guards, and test coverage

- Remove dead code in finally block: after delete(), has() is always
  false so the inner if was always true; simplify to a single delete +
  cancelledWatches.delete call (Finding 1)
- Add implementation_plan.json existence check in getSpecDirForWatcher
  before preferring the worktree path, so the watcher is started in
  the correct directory even when the plan file hasn't been written yet
  (Finding 2)
- Clear pendingWatches in unwatchAll() so in-flight watch() calls can
  no longer register new watchers after a full teardown (Finding 3)
- Also clear cancelledWatches in unwatchAll() since in-flight calls bail
  via the supersession check and won't clean up the flags themselves
- Add comprehensive concurrency tests for FileWatcher covering
  deduplication, supersession, cancellation, and unwatchAll behaviour
  (Finding 4)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use path.join() in file-watcher tests for cross-platform compatibility

Replace hardcoded forward-slash strings in getWatchedSpecDir assertions with
path.join() so expected values match on Windows (backslash) and Unix (forward
slash) alike.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove duplicate specDir declaration after rebase

The rebase on origin/develop introduced a duplicate `const specDir` declaration
that caused TypeScript and Biome CI failures. The variable was already declared
earlier in the same scope with the same value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:33:18 +01:00
Andy 76d1d3b032 fix: resolve Claude CLI not found on Windows - PATH, prompt size, cwd (#1661) (#1843)
* fix: resolve Claude CLI not found on Windows - PATH merge, prompt size cap, and cwd (#1661)

Three root causes addressed:

1. PATH overwrite: pythonEnv.PATH was overwriting the augmented PATH (with npm
   globals) in spawn env. Now merges PATH entries instead, prepending
   python-specific paths (pywin32_system32) while preserving all augmented entries.

2. System prompt size: On Windows, SDK passes system_prompt as --system-prompt
   CLI arg. Large CLAUDE.md files exceed CreateProcessW's 32,768 char limit,
   causing misleading "Claude Code not found" error. Now caps CLAUDE.md content
   on Windows to stay under the limit.

3. Cross-drive cwd: Agent processes were spawned with autoBuildSource as cwd.
   On Windows with cross-drive setups, this caused file access issues. Now uses
   projectPath as cwd since all script paths are absolute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review findings - constants, logging, CI fixes (#1661)

- Extract magic number 24000 into WINDOWS_MAX_SYSTEM_PROMPT_CHARS constant
  (set to 20000 for more conservative ~12KB CLI headroom)
- Extract truncation suffix into WINDOWS_TRUNCATION_MESSAGE constant
- Fix double-print when truncation occurs: only print "included in system
  prompt" when CLAUDE.md was NOT truncated (was_truncated flag)
- Fix CI test failures: update subprocess-spawn tests to expect projectPath
  as cwd instead of autoBuildSource (matches the #1661 CWD change)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: normalize PATH key casing and fix truncation budget on Windows

- Normalize env objects to a single uppercase 'PATH' key before merging
  to prevent duplicate PATH keys on Windows where process.env has 'Path'
  and getAugmentedEnv() writes 'PATH'. Without this, Object.keys().find()
  returns 'Path' first (insertion order), discarding augmented entries,
  and the final spread produces both 'Path' and 'PATH' keys.
  Follows the same pattern used in python-env-manager.ts. (#1661)

- Subtract WINDOWS_TRUNCATION_MESSAGE length from the truncation budget
  so the final system prompt stays within WINDOWS_MAX_SYSTEM_PROMPT_CHARS.

Addresses PR #1843 review findings NEW-001, NEW-002, NEW-003.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review findings for Windows PATH key casing and truncation budget

- Finding 1 (MEDIUM): Prefer 'PATH' key directly when present in env to avoid
  insertion-order bug where Object.keys().find() returned 'Path' first on Windows
- Finding 2 (MEDIUM): Normalization block (delete stale cased key, write 'PATH')
  already in place from previous commit; Finding 1 fix ensures envPathKey resolves
  correctly so normalization fires only when truly needed
- Finding 3 (LOW): Subtract header template overhead from max_claude_md_chars to
  prevent ~44-char overshoot in Windows command-line truncation budget (#1661)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove stale 'Path' key after PATH normalization on Windows

When getAugmentedEnv() spreads process.env on Windows, the resulting object
contains both 'Path' (from process.env spread) and 'PATH' (explicitly written
by getAugmentedEnv). The prior normalization block only removed non-'PATH' keys
when 'PATH' was absent, leaving the stale 'Path' key when both coexisted.

Add a cleanup loop to delete all case-variant PATH keys that differ from
'PATH' after the main normalization, ensuring the child process inherits a
single canonical 'PATH' entry with the fully-augmented value. (#1661)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract shared PATH normalization utilities and add unit tests

- Extract normalizeEnvPathKey() and mergePythonEnvPath() into env-utils.ts
  as shared, exported helpers to eliminate duplicated PATH key case-normalization
  logic across agent-process.ts and python-env-manager.ts (Finding 3)
- Add PATH normalization call in agent-queue.ts spawnIdeationProcess and
  spawnRoadmapProcess to fix the same Windows PATH duplicate-key issue that
  was fixed in agent-process.ts (#1661) (Finding 1)
- Add comprehensive unit tests for normalizeEnvPathKey() and mergePythonEnvPath()
  covering Windows-style 'Path' key renaming, duplicate key removal, PATH
  deduplication across merge, and Unix separator support (Finding 2)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:22:23 +01:00
Andy 3cb05781fa fix: handle planning phase crash and resume recovery (#1562) (#1844)
* fix: handle planning phase crash and resume recovery (#1562)

When spec creation crashes, the task gets stuck in "planning" state
forever because the backend never emits PLANNING_FAILED to the frontend
XState machine. Clicking Resume then also crashes because the resume
logic transitions to "coding" state, but there are no subtasks yet.

Root causes and fixes:

1. Backend orchestrator (orchestrator.py):
   - Wrap run() in try/except to emit PLANNING_FAILED on unhandled exceptions
   - Add _emit_planning_failed() calls at every early return path
   - Fix spec_dir tracking after rename_spec_dir_from_requirements()

2. XState machine (task-machine.ts):
   - Add PLANNING_STARTED transitions from error and human_review states
   - This allows tasks that crashed during planning to resume back to planning

3. Execution handlers (execution-handlers.ts):
   - Detect error state with 0 subtasks and send PLANNING_STARTED (not USER_RESUMED)
   - Check actual implementation_plan.json for subtasks instead of task.subtasks.length
   - Handles both with-actor and without-actor (app restart) code paths

Closes #1562

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review findings - reliable subtask check, spec_dir rename, empty except (#1562)

- Move planHasSubtasks calculation (reads implementation_plan.json) before
  XState handling so the crash-during-planning check uses the reliable
  file-based check instead of task.subtasks.length
- Change rename_spec_dir_from_requirements to return the new Path directly
  instead of a bool, eliminating brittle directory scanning in orchestrator
- Add descriptive comment to empty except clause to satisfy code scanning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update tests for rename_spec_dir_from_requirements return type change (#1562)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review findings for planning crash resume

- Update phase_executor.spec_dir and spec_validator after directory rename
  so subsequent phases don't use stale paths (critical bug flagged by
  sentry, coderabbitai, and Auto Claude review)
- Fix TASK_UPDATE_STATUS handler to use file-based plan check instead of
  unreliable task.subtasks.length (same #1562 bug fixed in TASK_START)
- Replace manual subtask counting with existing checkSubtasksCompletion helper
- Use safeReadFileSync instead of existsSync+readFileSync (TOCTOU fix)
- Add self.validator update to backward-compat _rename_spec_dir_from_requirements

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:14:09 +01:00
AndyMik90 4349963f39 fix: address PR #1815 review findings for roadmap XState refactor
- Remove redundant conditional in setGenerationStatus (NEW-001)
- Add comprehensive test coverage for catch-up logic (NEW-002)
- Pass persisted context to getOrCreateGenerationActor on reload (CMT-001)
- Add reverse-direction assertions for state name arrays (FNEW-003)
- Fix stale line reference in comment (NEW-003)
- Use precise no-op guard in updateFeatureLinkedSpec (NEW-004)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:23:06 +01:00
AndyMik90 6c69e71a76 fix: resolve 3 remaining PR #1815 issues
- Add vite/client types reference to fix import.meta.hot TS errors
- Add unit tests for mapGenerationStateToPhase/mapFeatureStateToStatus
  ensuring all machine states map correctly without silent fallthrough
- Restore persisted state in getOrCreateGenerationActor matching the
  feature actor pattern with resolveState()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 09:49:49 +01:00
AndyMik90 7f7e845cd5 fix: resolve 3 remaining PR #1819 review issues
- Preserve 'exited' status in setClaudeMode(false) instead of overwriting to 'running'
- Add isResumingPhase guard to SWAP_RESUME_COMPLETE for consistency with other swap events
- Clean up stale migratedSessionFlags when resumeClaudeAsync finds no terminal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 09:49:35 +01:00
AndyMik90 6f9a24176d fix: remove vacuous if/else guard in graphiti status test
Replace conditional assertions with direct assertions since
sys.modules patching deterministically makes available=True.
The else-branch was dead code with a trivially-passing assertion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 09:48:29 +01:00
AndyMik90 7fb9b10bcd fix: address PR review findings for XState roadmap refactor
- Add catch-up logic for 'error' phase in setGenerationStatus so
  GENERATION_ERROR is not silently dropped when actor is in idle/complete
- Fix progress updates being dropped for empty string message by using
  !== undefined instead of truthy check on status.message
- Improve compile-time assertion comments explaining both-direction sync
  enforcement strategy (forward via type assertion, reverse via switch
  exhaustiveness in map functions)
- Replace unnecessary dynamic import of resetActors in test afterEach
  with static import
- Document that backward transitions are intentionally unsupported in
  the forward-only generation pipeline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:38:18 +01:00
AndyMik90 faf6148f82 fix: use setClaudeMode instead of updateTerminal in onTerminalExit to prevent XState divergence
The onTerminalExit handler used store.updateTerminal() to reset isClaudeMode,
which is a plain Zustand setter that bypasses XState notification. Replace with
store.setClaudeMode() which properly checks XState state before sending events.

Since setTerminalStatus('exited') already sends SHELL_EXITED to XState (handling
the claude_active -> exited transition), the setClaudeMode(false) call here only
updates Zustand - its XState guard correctly skips sending CLAUDE_EXITED since
the machine is already in 'exited' state.

Fixes: NCR-R7-001 (HIGH) - Claude exit via updateTerminal bypasses XState machine

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:36:55 +01:00
AndyMik90 8e86bd60ae fix: address PR review findings for image pipeline
- Add defense-in-depth count and size validation in InsightsExecutor
  (NEW-005)
- Make image analysis unsupported warning more prominent with icon and
  background styling (REVIEW-001/CMT-001)
- Add inline notice on sent messages that images were not analyzed
  (CMT-001)
- Add i18n keys for image not-analyzed notice (en + fr)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:36:31 +01:00
AndyMik90 9e06b15d3d fix: address PR #1847 review findings
- Reuse SKIP_DIRS from context.constants instead of duplicating exclusion list
- Fix exception types in write error handlers (TypeError/ValueError, not JSONDecodeError)
- Add warning log when path validation bypassed due to exhausted retries
- Use existing safeReadFileSync helper for attempt_history reads

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:35:33 +01:00
AndyMik90 3bf5a1f0c3 fix: use write_json_atomic for implementation_plan.json in recovery
Replace raw json.dump with write_json_atomic when writing
implementation_plan.json in mark_subtask_stuck() to prevent file
corruption, consistent with 8+ other call sites in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:34:26 +01:00
AndyMik90 04d141f6be fix: make test_get_graphiti_status_invalid_config_sets_reason environment-independent
Avoid hard-asserting status['available'] is True, which depends on
sys.modules patching behavior. Instead check the key exists and branch
on its value, consistent with neighboring tests in the same class.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:34:24 +01:00
AndyMik90 9334b71da8 fix: cancel fallback timer in all task restart paths
The fallback safety net timer was only cancelled in the TASK_START handler,
but not in the TASK_UPDATE_STATUS auto-start path or TASK_RECOVER_STUCK
auto-restart path. This meant a stale timer could incorrectly stop a
newly restarted task if it was restarted within the 500ms window via
drag-to-in-progress or recovery auto-restart.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:34:01 +01:00
AndyMik90 4cb0a14aa7 fix: add debug log for venv fallback success in worktree setup
Add missing debug log when venv symlink health check fails and the
recreate fallback succeeds, matching the Python backend's behavior
for consistent debug output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:33:51 +01:00
AndyMik90 04618ccb08 fix: move debug log after delete to fix off-by-one count in cleanup timer
The cleanup timer debug log was computing pendingDelete.size - 1 before
the actual delete call, logging an incorrect remaining count. Moved both
delete calls before the debug log so it reports the accurate size.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:33:42 +01:00
AndyMik90 db4cd7d2da fix: extract stuck-subtask loader, use actual_output for stuck notes
- Extract duplicated stuck-subtask loading into _load_stuck_subtask_ids()
- Write stuck reason to actual_output instead of notes field for
  consistency with the QA reviewer's expectations
- Clarify progress message to mention terminal states (completed/failed/stuck)
- Update test assertions to match actual_output field

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:33:06 +01:00
AndyMik90 1f60699f38 fix: resolve QA validation deadlock when subtasks are stuck or failed
When a coding agent marks a subtask as "stuck", QA validation would never
start because is_build_complete() requires ALL subtasks to have status
"completed". This creates a deadlock: coder exits (no more subtasks to
work on), but QA never triggers.

Changes:
- Add is_build_ready_for_qa() that considers builds ready when all
  subtasks reach a terminal state (completed, failed, or stuck)
- Update mark_subtask_stuck() to also set status="failed" in
  implementation_plan.json, keeping plan file in sync with reality
- Reorder QA loop to check human feedback before build completeness,
  so QA_FIX_REQUEST.md bypasses the build gate as intended
- Replace is_build_complete() with is_build_ready_for_qa() in
  should_run_qa() and CLI qa commands
- Add 20 new tests covering is_build_ready_for_qa() edge cases and
  mark_subtask_stuck() plan update behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:33:06 +01:00
AndyMik90 c2b287c02c refactor: extract scoring logic, use indexed lookup in auto-correct
- Extract _score_and_select() from duplicated candidate scoring blocks
- Use _find_correct_path_indexed() in _auto_correct_subtask_files()
  with a shared file index built once for all missing files
- Use find_subtask_in_plan() helper instead of inline nested loop
- Add more dirs to _EXCLUDE_DIRS (.idea, .vscode, vendor, target, out)
- Narrow exception handlers from (OSError, JSONDecodeError) to OSError
  for write_json_atomic (JSON errors can't occur on write)
- Fix type annotation for missing_entries list

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:59 +01:00
AndyMik90 4a8c94538c feat: implement fuzzy file path matching and indexing for self-healing in coder pipeline
- Add functions for building a file index, finding correct paths using fuzzy matching, and auto-correcting subtask file paths.
- Introduce directory exclusion logic to optimize file searching.
- Enhance validation of file paths in implementation plans to support better error recovery.
- Add comprehensive tests for the new functionalities, covering various matching scenarios and edge cases.

This update improves the robustness of the coder pipeline by enabling it to automatically correct file paths based on existing project structure, thus enhancing overall stability and user experience.
2026-02-17 15:32:58 +01:00
AndyMik90 36b3b29d9e test: add coverage for missing graph backend scenario
Add test_get_graphiti_status_no_graph_backend to verify error handling when
graphiti_core imports successfully but neither real_ladybug nor kuzu are
available. This addresses CodeRabbit's recommendation to test the error path
in config.py lines 645-650.

Addresses CodeRabbit review comment on PR #1834.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:56 +01:00
AndyMik90 e61f4370a8 fix: address review findings for PR #1834
- Add nested try-except in config.py for clearer error messages when graph DB backend is missing
- Mock imports in test_config.py to make test environment-independent
- Ensure test passes regardless of whether graphiti_core/real_ladybug/kuzu are installed

Resolves CodeRabbit and Gemini review comments on PR #1834.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:56 +01:00
AndyMik90 aaa3b7f588 fix: remove stale FalkorDB references from migration cleanup
- Remove FalkorDB docker service reference from project_index.json (docker-compose.yml no longer exists)
- Correct line number reference in test_config.py comment (line 644 not 641)

Code review findings - no functional changes, just metadata cleanup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:56 +01:00
AndyMik90 b7739a4794 fix(memory): complete FalkorDB → LadybugDB migration in memory system
## Problem
The memory system was still checking for FalkorDB imports in `config.py`,
causing it to always report as unavailable and fall back to file-based
storage, despite LadybugDB being the configured and installed database.

Error in logs:
```
Graphiti packages not installed: falkordb is required for FalkorDri...
```

## Root Cause
In `get_graphiti_status()` at line 638, the code tried to import:
```python
from graphiti_core.driver.falkordb_driver import FalkorDriver
```
This import failed because FalkorDB was removed when migrating to LadybugDB.

## Changes Made

### Priority 1 — Critical Bug Fix
**File**: `apps/backend/integrations/graphiti/config.py` (lines 634-646)
- Replaced FalkorDB import check with LadybugDB/kuzu import check
- Now tries `real_ladybug` first (Python 3.12+), falls back to `kuzu`
- Removed unreachable pragma: no cover comment (line now executes)

### Priority 2 — Test Infrastructure Updates
1. **`conftest.py`** (lines 84-103)
   - Renamed fixture: `mock_falkor_driver` → `mock_kuzu_driver`
   - Updated docstring and patch path to reference KuzuDriver

2. **`test_config.py`** (lines 1056-1070, 1092-1094)
   - Updated test to reflect new behavior: `available=True` when packages
     installed, even with embedder validation errors (embedder is optional)
   - Updated comment from "falkordb" to "LadybugDB/kuzu"

3. **`test_memory.py`** (lines 267, 278)
   - Updated variable name: `mock_falkordb_driver` → `mock_kuzu_driver`
   - Updated sys.modules patch path to use kuzu_driver instead of falkordb_driver

### Priority 3 — Documentation Updates
4. **`test_memory_facade.py`** (line 163)
   - Updated comment: "remote FalkorDB" → "remote database"

5. **`spec_runner.py`** (line 139)
   - Updated example: "FalkorDB" → "LadybugDB"

## Testing
All 670 graphiti tests pass:
```
apps/backend/.venv/bin/pytest apps/backend/integrations/graphiti/tests/ -v
========== 670 passed, 6 skipped, 112 deselected, 4 warnings in 2.10s ==========
```

## Impact
- Memory system now correctly detects LadybugDB as available
- No more false negatives causing fallback to file-based storage
- All existing functionality preserved
- No breaking changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:56 +01:00
AndyMik90 92a3c821ed fix: address all auto-claude review findings for PR #1833
Fixed all 3 findings from the latest auto-claude review:

1. [NCR-NEW-001] MEDIUM: Fallback safety net timeout race condition
   - Store setTimeout timer reference in a Map keyed by taskId
   - Export cancelFallbackTimer() function to clear pending timers
   - Call cancelFallbackTimer() at start of TASK_START handler
   - Prevents stale timer from incorrectly stopping newly restarted tasks
   - Clean up timer reference after it fires

2. [CMT-NEW-001] LOW: Duplicate hasPlan detection logic
   - Replace inline hasPlan logic in execution-handlers.ts TASK_STOP
   - Use shared hasPlanWithSubtasks() utility from plan-file-utils.ts
   - Eliminates code duplication and ensures consistent behavior

3. [CMT-001] LOW: Misleading async keyword
   - Remove async from setTimeout callback in agent-events-handlers.ts
   - No await expressions exist in the callback
   - All operations (getCurrentState, hasPlanWithSubtasks) are synchronous

All changes maintain backward compatibility and fix the race condition
where restarting a task within 500ms could be incorrectly stopped by
the fallback timer from the previous process exit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:51 +01:00
AndyMik90 3628fba7f3 refactor: address all PR review findings for code quality
- Extract hasPlan logic into shared hasPlanWithSubtasks() utility in plan-file-utils.ts
  to eliminate duplication and centralize plan validation logic
- Make setTimeout callback async to avoid blocking readFileSync in event loop
- Replace hardcoded terminal status check with cleaner .includes() pattern
- Add status gate for final phase updates to prevent UI flicker when failed
  phase arrives after task has transitioned to human_review
- Import and use hasPlanWithSubtasks in agent-events-handlers.ts

All review comments addressed:
- Gemini: Critical/Medium - forceTransition method, magic number (already fixed in previous commit)
- Gemini: Medium - hardcoded status list (now uses .includes)
- CodeRabbit: Trivial - extract XSTATE_ACTIVE_STATES (already fixed in previous commit)
- CodeRabbit: Minor - derive hasPlan from file check (now uses shared utility)
- CodeRabbit: Minor - prevent UI flicker from final phase updates (now gates on status)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:51 +01:00
AndyMik90 b098d0d703 fix: address follow-up review findings - dynamic hasPlan and shared constants
Resolves NEW-001 and CMT-001 from the follow-up review:
- Extract XSTATE_ACTIVE_STATES to shared constant in task-state-utils.ts
- Export XSTATE_ACTIVE_STATES from state-machines index
- Replace hardcoded hasPlan: true with dynamic plan file check
- Pattern matches TASK_STOP handler (execution-handlers.ts lines 299-310)
- Tasks stuck in 'planning' state now correctly route to backlog, not human_review
- Improved logging shows hasPlan value for debugging

This ensures the fallback safety net routes tasks to the correct Kanban column
based on whether a plan file exists with subtasks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:51 +01:00
AndyMik90 4c00536dc5 fix: address all review findings for PR #1833
- Add named constant STUCK_TASK_FALLBACK_TIMEOUT_MS for magic number
- Use XState getCurrentState() instead of cached task status to avoid stale cache issues
- Check XState active states directly (planning, coding, qa_review, qa_fixing)
- Improve logging to show actual XState state name
- Add inline comments explaining the stale cache avoidance strategy

This resolves all 3 blocking issues from the Auto Claude review:
1. CRITICAL: forceTransition() method - fixed by using handleUiEvent()
2. MEDIUM: Stale closure - fixed by checking XState directly
3. MEDIUM: Magic number - fixed by using named constant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:51 +01:00
AndyMik90 cb8ed52eca fix: correct fallback timeout to use existing TaskStateManager API
The 500ms fallback safety net was calling `taskStateManager.forceTransition()`
which doesn't exist, causing TypeScript compilation errors.

Fixed to:
- Use `handleUiEvent()` with `USER_STOPPED` event (proper XState transition)
- Look up both task and project fresh (avoid stale closure references)
- Add null check for `checkProject` before proceeding

This ensures the fallback actually works when XState fails to transition
tasks out of in_progress after process exit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:51 +01:00
AndyMik90 4286152f42 fix: resolve Kanban board stuck task state synchronization
When an agent process exits with incomplete/stuck subtasks, the task gets
stuck in the Kanban UI — it spins forever, can't be stopped, and can't be
dragged back to planning.

This fix addresses 5 specific state synchronization gaps between the backend
process lifecycle and the frontend XState state machine:

1. Reset execution progress on terminal state transitions (task-store.ts)
   - When tasks reach terminal states (human_review, error, done, pr_created),
     execution progress is now reset to idle
   - Prevents stuck tasks from showing stale progress indicators in UI

2. Propagate final phase updates even after XState settles (agent-events-handlers.ts)
   - Final 'complete' or 'failed' phase updates are now sent to renderer
   - Previously these were silently dropped, causing UI to never show completion

3. Add fallback exit handler to force state transition (agent-events-handlers.ts)
   - If task remains in_progress 500ms after process exit, force to human_review
   - Safety net for when XState fails to properly handle PROCESS_EXITED event

4. Disable dragging for in_progress tasks (SortableTaskCard.tsx)
   - Prevents users from dragging tasks that are currently running or stuck
   - Adds disabled flag to useSortable hook when status is 'in_progress'

5. Allow stop button for stuck tasks (TaskCard.tsx)
   - Users can now force-stop stuck tasks using the stop button
   - Removed the isStuck check that prevented stopping stuck tasks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:32:51 +01:00