Fix #609: Windows coding phase not starting after spec/planning (#1347)

* Fix #609: Windows coding phase not starting after spec/planning

On Windows, os.execv() breaks the connection with the Electron parent
process when spec_runner.py transitions to run.py for the coding phase.

This causes the coding phase to never start and shows 'encountered unknown
error' in the UI.

Solution:
- Use subprocess.run() on Windows to maintain the parent-child connection
- Keep os.execv() on Unix/macOS (more efficient, replaces process)
- Added import subprocess

Tested on Windows 10 - coding phase now starts correctly after planning.
Unix/macOS behavior unchanged (continues using os.execv() as before).

* Add exception handling for Windows subprocess.run()

Addresses Auto Claude PR Review feedback (PR #743):

1. MEDIUM issue - Missing exception handling:
   - Added try-except for FileNotFoundError with clear error message
   - Added OSError handler for permission/system issues
   - Prevents unhelpful stack traces during coding phase startup

2. LOW issue - Misleading KeyboardInterrupt message:
   - Added specific KeyboardInterrupt handler for coding phase
   - Shows "Coding phase interrupted" instead of "Spec creation interrupted"
   - Exits with code 130 (standard for SIGINT)

These defensive programming improvements ensure graceful error handling
consistent with other subprocess.run() usage in the codebase
(e.g., apps/backend/services/orchestrator.py lines 295-328).

Implements suggestions from @AndyMik90's Auto Claude PR Review.

* Fix linting: remove f-string without placeholders

Addresses ruff F541 error on line 351:
- Changed f-string to regular string (no variable interpolation needed)
- Line 354 keeps f-string (has {e} placeholder)

Fixes CI linting check failure.

* refactor: use is_windows() from core.platform for consistency

Use the centralized platform abstraction helper instead of direct
sys.platform check for the execution path selection. The early
startup check (line 55) must remain as sys.platform since it runs
before core.platform can be imported.

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

* Address review feedback for consistency

- Use exit code 1 instead of 130 for KeyboardInterrupt (matches codebase)
- Use print_status() instead of print() for error messages (consistent UI)
- Add debug_error() logging before each error (matches existing patterns)

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

* Fix Ruff formatting: wrap long lines

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

---------

Co-authored-by: TamerineSky <TamerineSky@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
TamerineSky
2026-01-20 00:20:41 -07:00
committed by GitHub
parent 6a6247bbf2
commit 6da1b17042
+28 -2
View File
@@ -47,6 +47,7 @@ if sys.version_info < (3, 10): # noqa: UP036
import asyncio
import io
import os
import subprocess
from pathlib import Path
# Configure safe encoding on Windows BEFORE any imports that might print
@@ -104,6 +105,7 @@ from core.sentry import capture_exception, init_sentry
init_sentry(component="spec-runner")
from core.platform import is_windows
from debug import debug, debug_error, debug_section, debug_success
from phase_config import resolve_model_id
from review import ReviewState
@@ -369,8 +371,32 @@ Examples:
print(f" {muted('Running:')} {' '.join(run_cmd)}")
print()
# Execute run.py - replace current process
os.execv(sys.executable, run_cmd)
# Execute run.py - use subprocess on Windows to maintain connection with Electron
# Fix for issue #609: os.execv() breaks connection on Windows
if is_windows():
try:
result = subprocess.run(run_cmd)
sys.exit(result.returncode)
except FileNotFoundError:
debug_error(
"spec_runner",
"Could not start coding phase - executable not found",
)
print_status(
"Could not start coding phase - executable not found", "error"
)
sys.exit(1)
except OSError as e:
debug_error("spec_runner", f"Error starting coding phase: {e}")
print_status(f"Error starting coding phase: {e}", "error")
sys.exit(1)
except KeyboardInterrupt:
debug_error("spec_runner", "Coding phase interrupted by user")
print("\n\nCoding phase interrupted.")
sys.exit(1)
else:
# On Unix/macOS, os.execv() works correctly - replaces current process
os.execv(sys.executable, run_cmd)
sys.exit(0)