Skip to content

Bug: Agentic fix doesn't push commits when exiting early at Step 2 #419

@Serhan-Asad

Description

@Serhan-Asad

Summary

When the agentic e2e fix workflow exits early (at Step 2 with ALL_TESTS_PASS), commits created by the LLM agent in Step 1 are not pushed to the remote repository, despite the workflow reporting success.

Steps to Reproduce

  1. Run pdd fix https://github.com/promptdriven/pdd/issues/409 (or any issue where tests pass immediately after Step 1 fix)
  2. Observe workflow exits early at Step 2: ALL_TESTS_PASS detected in Step 2. Exiting loop.
  3. Check local commits: git log shows commit exists
  4. Check remote: git log origin/fix/issue-409 shows commit is NOT pushed
  5. Observe git status: Your branch is ahead of 'origin/fix/issue-409' by 1 commit

Expected Behavior

When the workflow completes successfully (success=True), ALL commits created during the workflow should be pushed to the remote, regardless of whether the workflow exits early or runs all 9 steps.

Actual Behavior

Scenario 1: Full workflow (all 9 steps, like issue #411)

  • ✅ Commits are created
  • ✅ Commits are pushed
  • ✅ Output: Committed and pushed 7 file(s)

Scenario 2: Early exit (Step 2, like issue #409)

  • ✅ Commit is created in Step 1
  • ❌ Commit is NOT pushed
  • ❌ Output: No changes to commit

Root Cause

File: pdd/agentic_e2e_fix_orchestrator.py
Function: _commit_and_push() (lines 198-273)

The bug occurs at lines 237-238:

def _commit_and_push(...):
    # Get current file hashes
    current_hashes = _get_file_hashes(cwd)  # Only detects uncommitted files
    
    # Find files that changed during workflow
    files_to_commit: List[str] = []
    for filepath, current_hash in current_hashes.items():
        if filepath not in initial_file_hashes:
            files_to_commit.append(filepath)
        elif initial_file_hashes[filepath] != current_hash:
            files_to_commit.append(filepath)
    
    if not files_to_commit:
        return True, "No changes to commit"  # ❌ BUG: Exits without pushing!
    
    # ... stage files ...
    # ... create commit ...
    # ... git push ...  ← Never reached!

Why it fails:

  1. Line 346 (before workflow): initial_file_hashes = _get_file_hashes(cwd) returns {} (no uncommitted files)
  2. Step 1 (LLM agent): Modifies pdd/commands/generate.py and creates commit 94f3bab5
  3. Step 2: Detects ALL_TESTS_PASS, exits early
  4. Line 521 (after workflow): Calls _commit_and_push()
  5. Inside _commit_and_push:
    • current_hashes = _get_file_hashes(cwd) calls git diff --name-only HEAD
    • Returns {} because working tree is clean (changes already committed)
    • files_to_commit is empty
    • Returns "No changes to commit" without executing git push

The issue: _get_file_hashes() only detects uncommitted changes, but the LLM agent already committed the fix.

Evidence

Issue #409 example:

# Local commits
$ git log --oneline -2
94f3bab5 Fix issue #409: Remove os.environ.update() causing environment variable pollution
0d61f1a8 Add failing tests for issue #409: Environment Variable Pollution

# Remote commits  
$ git log origin/fix/issue-409 --oneline -2
0d61f1a8 Add failing tests for issue #409: Environment Variable Pollution
88a37d5d Bump version

# Status
$ git status
Your branch is ahead of 'origin/fix/issue-409' by 1 commit.
  (use "git push" to publish your local commits)

Workflow output:

E2E fix complete
   Total cost: $0.9906
   Cycles used: 1/5
   Files changed: 
   Dev units fixed: 
   No changes to commit  ← Should say "Pushed 1 commit" or similar

Suggested Fix

Option 1: Always push at the end

def _commit_and_push(...):
    # ... detect files changed ...
    
    if not files_to_commit:
        # No new changes to commit, but push any unpushed commits
        push_result = subprocess.run(["git", "push"], cwd=cwd, ...)
        if push_result.returncode == 0:
            return True, "Pushed existing commits"
        else:
            return True, "No changes to push (up to date)"
    
    # ... stage, commit, and push new changes ...

Option 2: Separate commit and push logic

# In run_agentic_e2e_fix_orchestrator() after success:
if success:
    # Commit any uncommitted changes
    if has_uncommitted_changes():
        commit_changes()
    
    # Always push (whether we made a new commit or not)
    push_result = subprocess.run(["git", "push"], cwd=cwd, ...)

Test Case

Create a test that:

  1. Runs agentic fix on an issue where tests pass immediately
  2. Verifies that the local commit exists
  3. Verifies that the commit was pushed to remote

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions