Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions apps/backend/agents/coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,9 @@
task_logger.set_session(iteration)
else:
# Switch to coding phase after planning
just_transitioned_from_planning = False
if is_planning_phase:
just_transitioned_from_planning = True
is_planning_phase = False
current_log_phase = LogPhase.CODING
emit_phase(ExecutionPhase.CODING, "Starting implementation")
Expand All @@ -338,8 +340,35 @@
print_status("Phase transition synced to main project", "success")

if not next_subtask:
print("No pending subtasks found - build may be complete!")
break
# FIX for Issue #495: Race condition after planning phase
# The implementation_plan.json may not be fully flushed to disk yet,
# or there may be a brief delay before subtasks become available.
# Retry with exponential backoff before giving up.
if just_transitioned_from_planning:
print_status(
"Waiting for implementation plan to be ready...", "progress"
)
for retry_attempt in range(3):
delay = (retry_attempt + 1) * 2 # 2s, 4s, 6s
Comment on lines +351 to +352
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This retry logic is a good improvement. To make it even better, I have two suggestions:

  1. Exponential vs. Linear Backoff: The comment on line 346 indicates 'exponential backoff', but the delay calculation on line 352 is linear. It would be better to align the implementation with the comment by using an actual exponential backoff.

  2. Magic Numbers: The retry count 3 and the delay base 2 are hardcoded. Extracting these into named constants (e.g., PLANNING_TRANSITION_RETRIES, RETRY_DELAY_BASE_SECONDS) at a higher scope would improve readability and maintainability.

Here's a suggestion that implements exponential backoff. The constants for retry count and delay base can be defined elsewhere.

Suggested change
for retry_attempt in range(3):
delay = (retry_attempt + 1) * 2 # 2s, 4s, 6s
for retry_attempt in range(3):
delay = 2 ** (retry_attempt + 1) # Exponential backoff: 2s, 4s, 8s

await asyncio.sleep(delay)
next_subtask = get_next_subtask(spec_dir)
if next_subtask:
# Update subtask_id and phase_name after successful retry
subtask_id = next_subtask.get("id")
phase_name = next_subtask.get("phase_name")

Check warning

Code scanning / CodeQL

Variable defined multiple times Warning

This assignment to 'phase_name' is unnecessary as it is
redefined
before this value is used.
print_status(
f"Found subtask {subtask_id} after {delay}s delay",
"success",
)
break
print_status(
f"Retry {retry_attempt + 1}/3: No subtask found yet...",
"warning",
)

if not next_subtask:
print("No pending subtasks found - build may be complete!")
break

# Get attempt count for recovery context
attempt_count = recovery_manager.get_attempt_count(subtask_id)
Expand Down
Loading