Skip to content
Closed
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ services:
context: .
dockerfile: Dockerfile
command: ["python", "-m", "swe_af.fast"]
env_file: .env
environment:
- AGENTFIELD_SERVER=http://control-plane:8080
- NODE_ID=swe-fast
Expand Down
48 changes: 47 additions & 1 deletion swe_af/fast/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
import asyncio
import os
import re
import subprocess

from agentfield import Agent
from agentfield.types import AgentStatus
from swe_af.execution.envelope import unwrap_call_result as _unwrap
from swe_af.fast import fast_router
from swe_af.fast.schemas import FastBuildConfig, FastBuildResult, fast_resolve_models
Expand All @@ -29,6 +31,14 @@

app.include_router(fast_router)


@app.on_event("startup")
async def _mark_ready():
"""Promote lifecycle_status to READY after the SDK's own startup completes."""
await asyncio.sleep(3)
app._current_status = AgentStatus.READY


# Include the planner's execution router so that router.note() calls inside
# the original execution_agents functions (run_coder, run_verifier, etc.)
# work when delegated to via the thin wrappers.
Expand Down Expand Up @@ -73,7 +83,43 @@ async def build(
if not repo_path:
raise ValueError("Either repo_path or repo_url must be provided")

os.makedirs(repo_path, exist_ok=True)
git_dir = os.path.join(repo_path, ".git")
if effective_repo_url and not os.path.exists(git_dir):
app.note(f"Cloning {effective_repo_url} → {repo_path}", tags=["fast_build", "clone"])
os.makedirs(repo_path, exist_ok=True)
clone_result = subprocess.run(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

CI failure is from this PR — test_repo_url_without_repo_path_auto_derives_repo_path patches os.makedirs but not subprocess.run, so this hits real git clone against /workspaces/my-project (permission denied on CI, remote-not-found locally). Either patch swe_af.fast.app.subprocess.run in that test or short-circuit the clone branch by pre-creating the .git dir in tmp_path and pointing repo_path there.

["git", "clone", effective_repo_url, repo_path],
capture_output=True,
text=True,
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Minor: subprocess.run is synchronous and blocks the event loop for the full clone duration (can be tens of seconds on large repos). Parent swe_af/app.py has the same pattern, so matching it is fine — just flagging for a follow-up (asyncio.create_subprocess_exec or asyncio.to_thread).

if clone_result.returncode != 0:
err = clone_result.stderr.strip()
app.note(f"Clone failed: {err}", tags=["fast_build", "clone", "error"])
raise RuntimeError(f"git clone failed: {err}")
elif effective_repo_url and os.path.exists(git_dir):
default_branch = cfg.github_pr_base or "main"
app.note(
f"Repo exists at {repo_path} — resetting to origin/{default_branch}",
tags=["fast_build", "clone", "reset"],
)
subprocess.run(["git", "fetch", "origin"], cwd=repo_path, capture_output=True, text=True)
subprocess.run(["git", "checkout", "-f", default_branch], cwd=repo_path, capture_output=True, text=True)
reset = subprocess.run(
["git", "reset", "--hard", f"origin/{default_branch}"],
cwd=repo_path, capture_output=True, text=True,
)
if reset.returncode != 0:
import shutil
shutil.rmtree(repo_path, ignore_errors=True)
os.makedirs(repo_path, exist_ok=True)
clone_result = subprocess.run(
["git", "clone", effective_repo_url, repo_path],
capture_output=True, text=True,
)
if clone_result.returncode != 0:
raise RuntimeError(f"git re-clone failed: {clone_result.stderr.strip()}")
else:
os.makedirs(repo_path, exist_ok=True)

resolved = fast_resolve_models(cfg)
ai_provider = _runtime_to_provider(cfg.runtime)
Expand Down
Loading