Skip to content

Commit 4831820

Browse files
author
Test User
committed
fix: standardize worker agent task dict interface
Fix interface mismatch between LeadAgent and worker agents where: - LeadAgent was passing minimal dict with only id/title/description - FrontendWorkerAgent and TestWorkerAgent expected Task objects Changes: - LeadAgent now uses task.to_dict() for complete task data - FrontendWorkerAgent.execute_task accepts Dict[str, Any] - TestWorkerAgent.execute_task accepts Dict[str, Any] - Updated all tests to use dict input instead of Task objects This fixes AttributeError when workers accessed task.project_id, task.task_number, etc. for git workflow operations.
1 parent 2da47ba commit 4831820

File tree

8 files changed

+635
-186
lines changed

8 files changed

+635
-186
lines changed

codeframe/agents/frontend_worker_agent.py

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,33 @@ def _build_system_prompt(self) -> str:
8888
- Ensure proper TypeScript typing (no 'any' types)
8989
"""
9090

91-
async def execute_task(self, task: Task, project_id: int = 1) -> Dict[str, Any]:
91+
async def execute_task(self, task: Dict[str, Any], project_id: int = 1) -> Dict[str, Any]:
9292
"""
9393
Execute frontend task: generate React component.
9494
9595
Args:
96-
task: Task to execute with component specification
96+
task: Task dictionary with id, project_id, task_number, title, description, etc.
9797
project_id: Project ID for WebSocket broadcasts
9898
9999
Returns:
100100
Execution result with status and output
101101
"""
102-
self.current_task = task
102+
# Extract commonly used fields from task dict
103+
task_id = task["id"]
104+
task_title = task["title"]
105+
task_description = task["description"]
106+
107+
# Create Task object for self.current_task (used by blocker creation)
108+
self.current_task = Task(
109+
id=task_id,
110+
project_id=task.get("project_id"),
111+
issue_id=task.get("issue_id"),
112+
task_number=task.get("task_number", ""),
113+
parent_issue_number=task.get("parent_issue_number", ""),
114+
title=task_title,
115+
description=task_description,
116+
assigned_to=task.get("assigned_to"),
117+
)
103118

104119
try:
105120
# Broadcast task started
@@ -110,18 +125,18 @@ async def execute_task(self, task: Task, project_id: int = 1) -> Dict[str, Any]:
110125
await broadcast_task_status(
111126
self.websocket_manager,
112127
project_id,
113-
task.id,
128+
task_id,
114129
"in_progress",
115130
agent_id=self.agent_id,
116131
progress=0,
117132
)
118133
except Exception as e:
119134
logger.debug(f"Failed to broadcast task status: {e}")
120135

121-
logger.info(f"Frontend agent {self.agent_id} executing task {task.id}: {task.title}")
136+
logger.info(f"Frontend agent {self.agent_id} executing task {task_id}: {task_title}")
122137

123138
# Parse task description for component spec
124-
component_spec = self._parse_component_spec(task.description)
139+
component_spec = self._parse_component_spec(task_description)
125140

126141
# Generate component code
127142
component_code = await self._generate_react_component(component_spec)
@@ -151,29 +166,29 @@ async def execute_task(self, task: Task, project_id: int = 1) -> Dict[str, Any]:
151166
await broadcast_task_status(
152167
self.websocket_manager,
153168
project_id,
154-
task.id,
169+
task_id,
155170
"completed",
156171
agent_id=self.agent_id,
157172
progress=100,
158173
)
159174
except Exception as e:
160175
logger.debug(f"Failed to broadcast task status: {e}")
161176

162-
logger.info(f"Frontend agent {self.agent_id} completed task {task.id}")
177+
logger.info(f"Frontend agent {self.agent_id} completed task {task_id}")
163178

164179
# T076: Auto-commit task changes after successful completion
165180
if hasattr(self, "git_workflow") and self.git_workflow and file_paths:
166181
try:
167182
# Convert file_paths dict to list of paths
168183
files_modified = [path for path in file_paths.values()]
169184

170-
# Convert Task object to dict for git_workflow
185+
# Task is already a dict, extract fields for git_workflow
171186
task_dict = {
172-
"id": task.id,
173-
"project_id": task.project_id,
174-
"task_number": task.task_number,
175-
"title": task.title,
176-
"description": task.description,
187+
"id": task_id,
188+
"project_id": task.get("project_id"),
189+
"task_number": task.get("task_number", ""),
190+
"title": task_title,
191+
"description": task_description,
177192
}
178193

179194
commit_sha = self.git_workflow.commit_task_changes(
@@ -182,11 +197,11 @@ async def execute_task(self, task: Task, project_id: int = 1) -> Dict[str, Any]:
182197

183198
# T082: Record commit SHA in database
184199
if commit_sha and self.db:
185-
self.db.update_task_commit_sha(task.id, commit_sha)
186-
logger.info(f"Task {task.id} committed with SHA: {commit_sha[:7]}")
200+
self.db.update_task_commit_sha(task_id, commit_sha)
201+
logger.info(f"Task {task_id} committed with SHA: {commit_sha[:7]}")
187202
except Exception as e:
188203
# T080: Graceful degradation - log warning but don't block task completion
189-
logger.warning(f"Auto-commit failed for task {task.id} (non-blocking): {e}")
204+
logger.warning(f"Auto-commit failed for task {task_id} (non-blocking): {e}")
190205

191206
return {
192207
"status": "completed",
@@ -196,7 +211,7 @@ async def execute_task(self, task: Task, project_id: int = 1) -> Dict[str, Any]:
196211
}
197212

198213
except Exception as e:
199-
logger.error(f"Frontend agent {self.agent_id} failed task {task.id}: {e}")
214+
logger.error(f"Frontend agent {self.agent_id} failed task {task_id}: {e}")
200215

201216
# Broadcast failure
202217
if self.websocket_manager:
@@ -206,12 +221,12 @@ async def execute_task(self, task: Task, project_id: int = 1) -> Dict[str, Any]:
206221
await broadcast_task_status(
207222
self.websocket_manager,
208223
project_id,
209-
task.id,
224+
task_id,
210225
"failed",
211226
agent_id=self.agent_id,
212227
)
213-
except Exception as e:
214-
logger.debug(f"Failed to broadcast task status: {e}")
228+
except Exception as broadcast_err:
229+
logger.debug(f"Failed to broadcast task status: {broadcast_err}")
215230

216231
return {"status": "failed", "output": str(e), "error": str(e)}
217232

@@ -438,7 +453,7 @@ def _update_imports_exports(self, component_name: str, file_paths: Dict[str, str
438453
)
439454

440455
async def _run_and_check_linting(
441-
self, task: Task, file_paths: Dict[str, str], project_id: int
456+
self, task: Dict[str, Any], file_paths: Dict[str, str], project_id: int
442457
) -> None:
443458
"""
444459
Run linting on created files and create blocker if critical errors found (T112).
@@ -447,13 +462,14 @@ async def _run_and_check_linting(
447462
stores results in the database, and creates a blocker if critical errors are found.
448463
449464
Args:
450-
task: Task object
465+
task: Task dictionary with id, project_id, task_number, title, description, etc.
451466
file_paths: Dict of created file paths
452467
project_id: Project ID for broadcasts
453468
454469
Raises:
455470
ValueError: If linting fails with critical errors (blocker created)
456471
"""
472+
task_id = task["id"]
457473
if not file_paths:
458474
logger.debug("No files created, skipping linting")
459475
return
@@ -478,7 +494,7 @@ async def _run_and_check_linting(
478494
lint_runner = LintRunner(self.web_ui_root)
479495

480496
logger.info(
481-
f"Running linting on {len(files_to_lint)} TypeScript files for task {task.id}"
497+
f"Running linting on {len(files_to_lint)} TypeScript files for task {task_id}"
482498
)
483499

484500
# Run linting
@@ -488,7 +504,7 @@ async def _run_and_check_linting(
488504
if self.db:
489505
for result in lint_results:
490506
self.db.create_lint_result(
491-
task_id=task.id,
507+
task_id=task_id,
492508
linter=result.linter,
493509
error_count=result.error_count,
494510
warning_count=result.warning_count,
@@ -508,10 +524,10 @@ async def _run_and_check_linting(
508524
blocker_type="SYNC",
509525
title=f"Linting failed: {total_errors} critical errors",
510526
description=blocker_description,
511-
blocking_task_id=task.id,
527+
blocking_task_id=task_id,
512528
)
513529

514-
logger.error(f"Task {task.id} blocked by {total_errors} lint errors")
530+
logger.error(f"Task {task_id} blocked by {total_errors} lint errors")
515531

516532
# Broadcast lint failure via WebSocket (T119)
517533
if self.websocket_manager:
@@ -523,7 +539,7 @@ async def _run_and_check_linting(
523539
project_id,
524540
{
525541
"type": "lint_failed",
526-
"task_id": task.id,
542+
"task_id": task_id,
527543
"error_count": total_errors,
528544
"timestamp": datetime.utcnow().isoformat(),
529545
},
@@ -536,7 +552,7 @@ async def _run_and_check_linting(
536552
# Log warnings (non-blocking)
537553
total_warnings = sum(r.warning_count for r in lint_results)
538554
if total_warnings > 0:
539-
logger.warning(f"Task {task.id}: {total_warnings} lint warnings (non-blocking)")
555+
logger.warning(f"Task {task_id}: {total_warnings} lint warnings (non-blocking)")
540556

541557
# Broadcast lint success via WebSocket (T119)
542558
if self.websocket_manager:
@@ -548,7 +564,7 @@ async def _run_and_check_linting(
548564
project_id,
549565
{
550566
"type": "lint_completed",
551-
"task_id": task.id,
567+
"task_id": task_id,
552568
"error_count": 0,
553569
"warning_count": total_warnings,
554570
"timestamp": datetime.utcnow().isoformat(),

codeframe/agents/lead_agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,8 +2198,8 @@ async def _assign_and_execute_task(self, task: Task, retry_counts: Dict[int, int
21982198
7. Broadcast task status changes
21992199
"""
22002200
try:
2201-
# Determine agent type
2202-
task_dict = {"id": task.id, "title": task.title, "description": task.description}
2201+
# Determine agent type - use task.to_dict() for complete task data
2202+
task_dict = task.to_dict()
22032203
agent_type = self.agent_assigner.assign_agent_type(task_dict)
22042204

22052205
logger.info(f"Assigning task {task.id} ({task.title}) to {agent_type}")

0 commit comments

Comments
 (0)