mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
Fixing QA, Documenter and PM issues with tasks.
This commit is contained in:
@@ -113,6 +113,7 @@ class DocumenterAgent(Agent, PhaseEngine[DocTaskPhase, DocContext]):
|
|||||||
"""
|
"""
|
||||||
MONITOR phase: Watch for documentation requests.
|
MONITOR phase: Watch for documentation requests.
|
||||||
|
|
||||||
|
- Check for pending tasks directly assigned by PM
|
||||||
- Check for tasks awaiting documentation
|
- Check for tasks awaiting documentation
|
||||||
- Check for documentation notifications
|
- Check for documentation notifications
|
||||||
"""
|
"""
|
||||||
@@ -121,12 +122,36 @@ class DocumenterAgent(Agent, PhaseEngine[DocTaskPhase, DocContext]):
|
|||||||
if self._pending_docs:
|
if self._pending_docs:
|
||||||
return self._pending_docs.pop(0)
|
return self._pending_docs.pop(0)
|
||||||
|
|
||||||
|
# Priority 1: Check for pending tasks directly assigned to this documenter
|
||||||
|
# This handles cases where PM assigns a task directly to documenter
|
||||||
|
pending_task = await self._find_pending_assigned_to_me()
|
||||||
|
if pending_task:
|
||||||
|
self.log.info(
|
||||||
|
"Found pending task assigned to me", task_id=str(pending_task)
|
||||||
|
)
|
||||||
|
return pending_task
|
||||||
|
|
||||||
|
# Priority 2: Check for tasks awaiting documentation (normal workflow)
|
||||||
task_id = await self._find_awaiting_documentation()
|
task_id = await self._find_awaiting_documentation()
|
||||||
if task_id:
|
if task_id:
|
||||||
return task_id
|
return task_id
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
async def _find_pending_assigned_to_me(self) -> UUID | None:
|
||||||
|
"""Find pending tasks directly assigned to this documenter."""
|
||||||
|
try:
|
||||||
|
result = await self._api_call(
|
||||||
|
"GET",
|
||||||
|
"/tasks",
|
||||||
|
params={"status": "pending", "assigned_to": str(self.id)},
|
||||||
|
)
|
||||||
|
tasks = result.get("items", [])
|
||||||
|
return UUID(tasks[0]["id"]) if tasks else None
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning("Failed to find pending assigned task", error=str(e))
|
||||||
|
return None
|
||||||
|
|
||||||
async def execute_task(self, task_id: UUID) -> bool:
|
async def execute_task(self, task_id: UUID) -> bool:
|
||||||
"""
|
"""
|
||||||
Execute documentation through lifecycle phases.
|
Execute documentation through lifecycle phases.
|
||||||
|
|||||||
+88
-26
@@ -189,18 +189,44 @@ class CellPMAgent(Agent, CyclicPhaseRunner[CellPMPhase]):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
async def _find_assigned_task(self) -> UUID | None:
|
async def _find_assigned_task(self) -> UUID | None:
|
||||||
"""Find tasks assigned to this PM that are in progress."""
|
"""Find tasks assigned to this PM that need work.
|
||||||
try:
|
|
||||||
result = await self._api_call(
|
Checks for tasks in priority order:
|
||||||
"GET",
|
1. pending - newly assigned, needs claiming
|
||||||
"/tasks",
|
2. claimed - claimed but not started
|
||||||
params={"status": "in_progress", "assigned_to": str(self.id)},
|
3. in_progress - active work
|
||||||
)
|
4. awaiting_pm_review - tasks ready for PM approval
|
||||||
tasks = result.get("items", result) if isinstance(result, dict) else result
|
"""
|
||||||
return UUID(tasks[0]["id"]) if tasks else None
|
statuses_to_check = ["pending", "claimed", "in_progress", "awaiting_pm_review"]
|
||||||
except Exception as e:
|
|
||||||
self.log.warning("Failed to find assigned task", error=str(e))
|
for status in statuses_to_check:
|
||||||
return None
|
try:
|
||||||
|
result = await self._api_call(
|
||||||
|
"GET",
|
||||||
|
"/tasks",
|
||||||
|
params={"status": status, "assigned_to": str(self.id)},
|
||||||
|
)
|
||||||
|
tasks = (
|
||||||
|
result.get("items", result)
|
||||||
|
if isinstance(result, dict)
|
||||||
|
else result
|
||||||
|
)
|
||||||
|
if tasks:
|
||||||
|
task_id = tasks[0]["id"]
|
||||||
|
self.log.info(
|
||||||
|
"Found assigned task",
|
||||||
|
task_id=str(task_id),
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
|
return UUID(task_id) if isinstance(task_id, str) else task_id
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning(
|
||||||
|
"Failed to find assigned task",
|
||||||
|
status=status,
|
||||||
|
error=str(e),
|
||||||
|
)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
async def execute_task(self, task_id: UUID) -> bool:
|
async def execute_task(self, task_id: UUID) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -258,7 +284,12 @@ class CellPMAgent(Agent, CyclicPhaseRunner[CellPMPhase]):
|
|||||||
return await self._handle_in_progress_task(task_id, task)
|
return await self._handle_in_progress_task(task_id, task)
|
||||||
|
|
||||||
if status == "paused":
|
if status == "paused":
|
||||||
await self._mark_completed(task_id)
|
# Paused tasks need to resume before completion
|
||||||
|
# Lifecycle: paused → in_progress → completed
|
||||||
|
await self._api_call("POST", f"/tasks/{task_id}/resume")
|
||||||
|
self.log.info("PM resumed paused task", task_id=str(task_id))
|
||||||
|
# Now complete via proper endpoint (validates PM role, checks subtasks)
|
||||||
|
await self._api_call("POST", f"/tasks/{task_id}/complete")
|
||||||
self.log.info("PM completed task", task_id=str(task_id))
|
self.log.info("PM completed task", task_id=str(task_id))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1038,18 +1069,44 @@ class MainPMAgent(Agent, CyclicPhaseRunner[MainPMPhase]):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
async def _find_assigned_task(self) -> UUID | None:
|
async def _find_assigned_task(self) -> UUID | None:
|
||||||
"""Find tasks assigned to this PM that are in progress."""
|
"""Find tasks assigned to this Main PM that need work.
|
||||||
try:
|
|
||||||
result = await self._api_call(
|
Checks for tasks in priority order:
|
||||||
"GET",
|
1. pending - newly assigned, needs claiming
|
||||||
"/tasks",
|
2. claimed - claimed but not started
|
||||||
params={"status": "in_progress", "assigned_to": str(self.id)},
|
3. in_progress - active work
|
||||||
)
|
4. awaiting_pm_review - tasks ready for PM approval
|
||||||
tasks = result.get("items", result) if isinstance(result, dict) else result
|
"""
|
||||||
return UUID(tasks[0]["id"]) if tasks else None
|
statuses_to_check = ["pending", "claimed", "in_progress", "awaiting_pm_review"]
|
||||||
except Exception as e:
|
|
||||||
self.log.warning("Failed to find assigned task", error=str(e))
|
for status in statuses_to_check:
|
||||||
return None
|
try:
|
||||||
|
result = await self._api_call(
|
||||||
|
"GET",
|
||||||
|
"/tasks",
|
||||||
|
params={"status": status, "assigned_to": str(self.id)},
|
||||||
|
)
|
||||||
|
tasks = (
|
||||||
|
result.get("items", result)
|
||||||
|
if isinstance(result, dict)
|
||||||
|
else result
|
||||||
|
)
|
||||||
|
if tasks:
|
||||||
|
task_id = tasks[0]["id"]
|
||||||
|
self.log.info(
|
||||||
|
"Found assigned task",
|
||||||
|
task_id=str(task_id),
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
|
return UUID(task_id) if isinstance(task_id, str) else task_id
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning(
|
||||||
|
"Failed to find assigned task",
|
||||||
|
status=status,
|
||||||
|
error=str(e),
|
||||||
|
)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
async def execute_task(self, task_id: UUID) -> bool:
|
async def execute_task(self, task_id: UUID) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -1111,7 +1168,12 @@ class MainPMAgent(Agent, CyclicPhaseRunner[MainPMPhase]):
|
|||||||
return await self._handle_main_pm_in_progress(task_id, task)
|
return await self._handle_main_pm_in_progress(task_id, task)
|
||||||
|
|
||||||
if status == "paused":
|
if status == "paused":
|
||||||
await self._mark_completed(task_id)
|
# Paused tasks need to resume before completion
|
||||||
|
# Lifecycle: paused → in_progress → completed
|
||||||
|
await self._api_call("POST", f"/tasks/{task_id}/resume")
|
||||||
|
self.log.info("Main PM resumed paused task", task_id=str(task_id))
|
||||||
|
# Now complete via proper endpoint (validates PM role, checks subtasks)
|
||||||
|
await self._api_call("POST", f"/tasks/{task_id}/complete")
|
||||||
self.log.info("Main PM completed task", task_id=str(task_id))
|
self.log.info("Main PM completed task", task_id=str(task_id))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
+25
-1
@@ -110,6 +110,7 @@ class QAAgent(Agent, PhaseEngine[QATaskPhase, ReviewContext]):
|
|||||||
"""
|
"""
|
||||||
MONITOR phase: Watch for tasks ready for review.
|
MONITOR phase: Watch for tasks ready for review.
|
||||||
|
|
||||||
|
- Check for pending tasks directly assigned by PM
|
||||||
- Check for tasks flagged as awaiting_qa
|
- Check for tasks flagged as awaiting_qa
|
||||||
- Check for PM notifications
|
- Check for PM notifications
|
||||||
"""
|
"""
|
||||||
@@ -119,13 +120,36 @@ class QAAgent(Agent, PhaseEngine[QATaskPhase, ReviewContext]):
|
|||||||
if self._pending_reviews:
|
if self._pending_reviews:
|
||||||
return self._pending_reviews.pop(0)
|
return self._pending_reviews.pop(0)
|
||||||
|
|
||||||
# Query for tasks awaiting QA
|
# Priority 1: Check for pending tasks directly assigned to this QA agent
|
||||||
|
# This handles cases where PM assigns a task directly to QA
|
||||||
|
pending_task = await self._find_pending_assigned_to_me()
|
||||||
|
if pending_task:
|
||||||
|
self.log.info(
|
||||||
|
"Found pending task assigned to me", task_id=str(pending_task)
|
||||||
|
)
|
||||||
|
return pending_task
|
||||||
|
|
||||||
|
# Priority 2: Query for tasks awaiting QA (normal workflow)
|
||||||
task_id = await self._find_awaiting_qa()
|
task_id = await self._find_awaiting_qa()
|
||||||
if task_id:
|
if task_id:
|
||||||
return task_id
|
return task_id
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
async def _find_pending_assigned_to_me(self) -> UUID | None:
|
||||||
|
"""Find pending tasks directly assigned to this QA agent."""
|
||||||
|
try:
|
||||||
|
result = await self._api_call(
|
||||||
|
"GET",
|
||||||
|
"/tasks",
|
||||||
|
params={"status": "pending", "assigned_to": str(self.id)},
|
||||||
|
)
|
||||||
|
tasks = result.get("items", [])
|
||||||
|
return UUID(tasks[0]["id"]) if tasks else None
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning("Failed to find pending assigned task", error=str(e))
|
||||||
|
return None
|
||||||
|
|
||||||
async def execute_task(self, task_id: UUID) -> bool:
|
async def execute_task(self, task_id: UUID) -> bool:
|
||||||
"""
|
"""
|
||||||
Execute review through QA lifecycle phases.
|
Execute review through QA lifecycle phases.
|
||||||
|
|||||||
@@ -140,7 +140,8 @@ async def validate_task_claimable(
|
|||||||
"""
|
"""
|
||||||
task_status = task.get("status")
|
task_status = task.get("status")
|
||||||
claimable_statuses = {
|
claimable_statuses = {
|
||||||
"qa": ["awaiting_qa"],
|
# QA: pending (direct QA tasks from PM) or awaiting_qa (normal workflow)
|
||||||
|
"qa": ["pending", "awaiting_qa"],
|
||||||
# Documenters: pending (direct docs tasks) or awaiting_documentation (workflow)
|
# Documenters: pending (direct docs tasks) or awaiting_documentation (workflow)
|
||||||
"documenter": ["pending", "awaiting_documentation"],
|
"documenter": ["pending", "awaiting_documentation"],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,13 @@ def _get_valid_claim_statuses(
|
|||||||
role = agent.role.value if hasattr(agent.role, "value") else str(agent.role)
|
role = agent.role.value if hasattr(agent.role, "value") else str(agent.role)
|
||||||
|
|
||||||
if role == "qa":
|
if role == "qa":
|
||||||
return {TaskStatus.AWAITING_QA}
|
# QA can claim:
|
||||||
|
# - PENDING: when PM assigns a QA task directly
|
||||||
|
# - AWAITING_QA: normal workflow after dev verification
|
||||||
|
statuses = {TaskStatus.PENDING, TaskStatus.AWAITING_QA}
|
||||||
|
if allow_reassign:
|
||||||
|
statuses.add(TaskStatus.CLAIMED)
|
||||||
|
return statuses
|
||||||
elif role == "documenter":
|
elif role == "documenter":
|
||||||
# Documenters can claim:
|
# Documenters can claim:
|
||||||
# - PENDING: when PM assigns a docs task directly
|
# - PENDING: when PM assigns a docs task directly
|
||||||
|
|||||||
Reference in New Issue
Block a user