Skip to content

Commit 9202adc

Browse files
AbirAbbasclaude
andcommitted
fix: resolve 3 runtime bugs blocking swe-fast pipeline execution
- Add missing `command` to swe-fast docker-compose service (was running swe-planner entrypoint instead of swe_af.fast) - Replace **kwargs thin wrappers with explicit signatures to fix 422 schema validation errors from the control plane - Register planner/verifier modules in __init__.py so fast_plan_tasks and fast_verify reasoners are available at startup - Include execution router in swe-fast app so router.note() calls in delegated execution_agents functions don't raise RuntimeError - Fix fast_verify to adapt task_results into completed/failed/skipped split matching run_verifier's interface, use correct model param name, and qualify the target with NODE_ID Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 96b91be commit 9202adc

4 files changed

Lines changed: 119 additions & 40 deletions

File tree

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ services:
3939
build:
4040
context: .
4141
dockerfile: Dockerfile
42+
command: ["python", "-m", "swe_af.fast"]
4243
environment:
4344
- AGENTFIELD_SERVER=http://control-plane:8080
4445
- NODE_ID=swe-fast

swe_af/fast/__init__.py

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,41 +28,114 @@
2828

2929

3030
@fast_router.reasoner()
31-
async def run_git_init(**kwargs) -> dict: # type: ignore[override]
31+
async def run_git_init(
32+
repo_path: str,
33+
goal: str,
34+
artifacts_dir: str = "",
35+
model: str = "sonnet",
36+
permission_mode: str = "",
37+
ai_provider: str = "claude",
38+
previous_error: str | None = None,
39+
build_id: str = "",
40+
) -> dict:
3241
"""Thin wrapper around execution_agents.run_git_init."""
3342
import swe_af.reasoners.execution_agents as _ea # noqa: PLC0415
34-
return await _ea.run_git_init(**kwargs)
43+
return await _ea.run_git_init(
44+
repo_path=repo_path, goal=goal, artifacts_dir=artifacts_dir,
45+
model=model, permission_mode=permission_mode, ai_provider=ai_provider,
46+
previous_error=previous_error, build_id=build_id,
47+
)
3548

3649

3750
@fast_router.reasoner()
38-
async def run_coder(**kwargs) -> dict: # type: ignore[override]
51+
async def run_coder(
52+
issue: dict,
53+
worktree_path: str,
54+
feedback: str = "",
55+
iteration: int = 1,
56+
iteration_id: str = "",
57+
project_context: dict | None = None,
58+
memory_context: dict | None = None,
59+
model: str = "sonnet",
60+
permission_mode: str = "",
61+
ai_provider: str = "claude",
62+
) -> dict:
3963
"""Thin wrapper around execution_agents.run_coder."""
4064
import swe_af.reasoners.execution_agents as _ea # noqa: PLC0415
41-
return await _ea.run_coder(**kwargs)
65+
return await _ea.run_coder(
66+
issue=issue, worktree_path=worktree_path, feedback=feedback,
67+
iteration=iteration, iteration_id=iteration_id,
68+
project_context=project_context, memory_context=memory_context,
69+
model=model, permission_mode=permission_mode, ai_provider=ai_provider,
70+
)
4271

4372

4473
@fast_router.reasoner()
45-
async def run_verifier(**kwargs) -> dict: # type: ignore[override]
74+
async def run_verifier(
75+
prd: dict,
76+
repo_path: str,
77+
artifacts_dir: str,
78+
completed_issues: list[dict] | None = None,
79+
failed_issues: list[dict] | None = None,
80+
skipped_issues: list[str] | None = None,
81+
model: str = "sonnet",
82+
permission_mode: str = "",
83+
ai_provider: str = "claude",
84+
) -> dict:
4685
"""Thin wrapper around execution_agents.run_verifier."""
4786
import swe_af.reasoners.execution_agents as _ea # noqa: PLC0415
48-
return await _ea.run_verifier(**kwargs)
87+
return await _ea.run_verifier(
88+
prd=prd, repo_path=repo_path, artifacts_dir=artifacts_dir,
89+
completed_issues=completed_issues or [], failed_issues=failed_issues or [],
90+
skipped_issues=skipped_issues or [],
91+
model=model, permission_mode=permission_mode, ai_provider=ai_provider,
92+
)
4993

5094

5195
@fast_router.reasoner()
52-
async def run_repo_finalize(**kwargs) -> dict: # type: ignore[override]
96+
async def run_repo_finalize(
97+
repo_path: str,
98+
artifacts_dir: str = "",
99+
model: str = "sonnet",
100+
permission_mode: str = "",
101+
ai_provider: str = "claude",
102+
) -> dict:
53103
"""Thin wrapper around execution_agents.run_repo_finalize."""
54104
import swe_af.reasoners.execution_agents as _ea # noqa: PLC0415
55-
return await _ea.run_repo_finalize(**kwargs)
105+
return await _ea.run_repo_finalize(
106+
repo_path=repo_path, artifacts_dir=artifacts_dir,
107+
model=model, permission_mode=permission_mode, ai_provider=ai_provider,
108+
)
56109

57110

58111
@fast_router.reasoner()
59-
async def run_github_pr(**kwargs) -> dict: # type: ignore[override]
112+
async def run_github_pr(
113+
repo_path: str,
114+
integration_branch: str,
115+
base_branch: str,
116+
goal: str,
117+
build_summary: str = "",
118+
completed_issues: list[dict] | None = None,
119+
accumulated_debt: list[dict] | None = None,
120+
artifacts_dir: str = "",
121+
model: str = "sonnet",
122+
permission_mode: str = "",
123+
ai_provider: str = "claude",
124+
) -> dict:
60125
"""Thin wrapper around execution_agents.run_github_pr."""
61126
import swe_af.reasoners.execution_agents as _ea # noqa: PLC0415
62-
return await _ea.run_github_pr(**kwargs)
127+
return await _ea.run_github_pr(
128+
repo_path=repo_path, integration_branch=integration_branch,
129+
base_branch=base_branch, goal=goal, build_summary=build_summary,
130+
completed_issues=completed_issues, accumulated_debt=accumulated_debt,
131+
artifacts_dir=artifacts_dir, model=model,
132+
permission_mode=permission_mode, ai_provider=ai_provider,
133+
)
63134

64135

65136
from . import executor # noqa: E402, F401 — registers fast_execute_tasks
137+
from . import planner # noqa: E402, F401 — registers fast_plan_tasks
138+
from . import verifier # noqa: E402, F401 — registers fast_verify
66139

67140
__all__ = [
68141
"fast_router",
@@ -72,4 +145,6 @@ async def run_github_pr(**kwargs) -> dict: # type: ignore[override]
72145
"run_repo_finalize",
73146
"run_github_pr",
74147
"executor",
148+
"planner",
149+
"verifier",
75150
]

swe_af/fast/app.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929

3030
app.include_router(fast_router)
3131

32+
# Include the planner's execution router so that router.note() calls inside
33+
# the original execution_agents functions (run_coder, run_verifier, etc.)
34+
# work when delegated to via the thin wrappers.
35+
from swe_af.reasoners import router as _execution_router # noqa: E402
36+
app.include_router(_execution_router)
37+
3238

3339
def _repo_name_from_url(url: str) -> str:
3440
"""Extract repo name from a GitHub URL."""

swe_af/fast/verifier.py

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,51 +17,48 @@
1717

1818
@fast_router.reasoner()
1919
async def fast_verify(
20-
*,
21-
prd: str,
20+
prd: dict[str, Any],
2221
repo_path: str,
2322
task_results: list[dict[str, Any]],
24-
verifier_model: str,
25-
permission_mode: str,
26-
ai_provider: str,
27-
artifacts_dir: str,
28-
**kwargs: Any,
23+
verifier_model: str = "sonnet",
24+
permission_mode: str = "",
25+
ai_provider: str = "claude",
26+
artifacts_dir: str = "",
2927
) -> dict[str, Any]:
3028
"""Run a single verification pass against the built repository.
3129
32-
Calls ``run_verifier`` via a lazy import of ``swe_af.fast.app`` to
33-
avoid circular imports at module load time. No fix cycles are
34-
attempted — this is a single-pass reasoner.
35-
36-
Args:
37-
prd: The product requirements document text.
38-
repo_path: Absolute path to the repository to verify.
39-
task_results: List of task result dicts from the execution phase.
40-
verifier_model: Model name string for the verifier agent.
41-
permission_mode: Permission mode string passed to the agent runtime.
42-
ai_provider: AI provider identifier string.
43-
artifacts_dir: Path to the artifacts directory.
44-
**kwargs: Additional keyword arguments forwarded to the agent call.
45-
46-
Returns:
47-
A :class:`~swe_af.fast.schemas.FastVerificationResult` serialised
48-
as a plain dict.
30+
Adapts fast task_results into the completed/failed/skipped split that
31+
``run_verifier`` expects, then delegates to a single verification pass.
32+
No fix cycles are attempted.
4933
"""
5034
try:
5135
import swe_af.fast.app as _app # noqa: PLC0415
5236

37+
# Split task_results into completed/failed for run_verifier's interface
38+
completed_issues: list[dict] = []
39+
failed_issues: list[dict] = []
40+
for tr in task_results:
41+
entry = {
42+
"issue_name": tr.get("task_name", ""),
43+
"result_summary": tr.get("summary", ""),
44+
}
45+
if tr.get("outcome") == "completed":
46+
completed_issues.append(entry)
47+
else:
48+
failed_issues.append(entry)
49+
5350
result: dict[str, Any] = await _app.app.call(
54-
"run_verifier",
51+
f"{_app.NODE_ID}.run_verifier",
5552
prd=prd,
5653
repo_path=repo_path,
57-
task_results=task_results,
58-
verifier_model=verifier_model,
54+
artifacts_dir=artifacts_dir,
55+
completed_issues=completed_issues,
56+
failed_issues=failed_issues,
57+
skipped_issues=[],
58+
model=verifier_model,
5959
permission_mode=permission_mode,
6060
ai_provider=ai_provider,
61-
artifacts_dir=artifacts_dir,
62-
**kwargs,
6361
)
64-
# Ensure the result conforms to FastVerificationResult
6562
verification = FastVerificationResult(
6663
passed=result.get("passed", False),
6764
summary=result.get("summary", ""),

0 commit comments

Comments
 (0)