Static agents IDs

This commit is contained in:
Renn F
2025-12-19 17:13:54 +01:00
parent a3058e9e02
commit eb9109a855
5 changed files with 179 additions and 45 deletions
+9 -12
View File
@@ -5,7 +5,6 @@ Functions to populate the database with initial data.
Separates database operations from bootstrap orchestration.
"""
import contextlib
from uuid import UUID as UUIDType
import structlog
@@ -75,12 +74,13 @@ async def create_channels(session: AsyncSession) -> dict[str, str]:
async def create_agents(session: AsyncSession) -> dict[str, str]:
"""Create default agents. Returns agent_id (slug) -> db_id mapping."""
"""Create default agents. Returns slug -> db_id (UUID) mapping."""
agent_ids = {}
for agent_data in DEFAULT_AGENTS:
slug = agent_data["agent_id"]
slug = agent_data["slug"]
static_id = agent_data["id"] # Static UUID from initial_data.py
# Check if exists
result = await session.execute(
@@ -90,7 +90,7 @@ async def create_agents(session: AsyncSession) -> dict[str, str]:
if existing:
agent_ids[slug] = str(existing.id)
logger.info("Agent exists", agent_id=slug)
logger.info("Agent exists", slug=slug, id=str(existing.id))
continue
# Map role string to enum
@@ -101,15 +101,12 @@ async def create_agents(session: AsyncSession) -> dict[str, str]:
team_str = agent_data.get("team")
team = Team(team_str) if team_str else None
# If slug is a valid UUID, use it as the database ID
# (important for CEO so X-Agent-ID header matches the DB id)
explicit_id: UUIDType | None = None
with contextlib.suppress(ValueError):
explicit_id = UUIDType(slug)
# Use the static UUID from initial_data.py
agent_uuid = UUIDType(static_id)
# Create agent using ORM
# Create agent using ORM with static UUID
agent = AgentTable(
id=explicit_id, # Will use slug as ID if it's a valid UUID
id=agent_uuid,
name=agent_data["name"],
slug=slug,
role=role,
@@ -121,7 +118,7 @@ async def create_agents(session: AsyncSession) -> dict[str, str]:
await session.flush()
agent_ids[slug] = str(agent.id)
logger.info("Agent created", agent_id=slug)
logger.info("Agent created", slug=slug, id=str(agent.id))
return agent_ids
+3 -2
View File
@@ -44,8 +44,9 @@ class TaskReflectionInput(BaseModel):
class DecisionOption(BaseModel):
"""A decision option with pros/cons."""
option: str
pros_cons: str
name: str = Field(..., description="Option name/title")
pros: str = Field(default="", description="Pros/advantages of this option")
cons: str = Field(default="", description="Cons/disadvantages of this option")
class DecisionLogInput(BaseModel):
+33
View File
@@ -1084,7 +1084,40 @@ async def _handle_task_complete(task_id: str, agent_id: str) -> dict[str, Any]:
async def _handle_agent_idle(agent_id: str) -> dict[str, Any]:
"""Handle agent going idle (no work available)."""
headers = _get_agent_headers(agent_id)
async with httpx.AsyncClient(timeout=30.0) as client:
# First, check if agent has any in-progress tasks
try:
agent_uuid = await _resolve_agent_uuid(agent_id, headers)
if agent_uuid:
scan_resp = await client.get(
f"{settings.internal_api_url}/tasks",
params={"assigned_to": agent_uuid, "status": "in_progress"},
headers=headers,
)
if scan_resp.status_code == status.HTTP_200_OK:
data = scan_resp.json()
tasks = data.get("items", [])
if tasks:
# Agent has in-progress tasks - they must handle them first
task_info = [
{"id": t.get("id"), "title": t.get("title")}
for t in tasks
]
return _format_error_response(
"TASKS_IN_PROGRESS",
(
"You have in-progress tasks. Handle them before going "
"idle using: roboco_task_pause (to pause), "
"roboco_task_submit_qa (if done), or "
"roboco_task_complete (if approved)."
),
{"tasks": task_info},
)
except Exception:
# If check fails, continue to mark idle (fail open)
pass
try:
# Signal to orchestrator that this agent is idle
resp = await client.post(
+4
View File
@@ -5,7 +5,9 @@ Contains initial data definitions for bootstrapping the RoboCo system.
"""
from roboco.seeds.initial_data import (
AGENT_UUIDS,
AUDITOR_SILENT_ACCESS,
CEO_AGENT_ID,
CHANNEL_MEMBERSHIPS,
DEFAULT_AGENTS,
DEFAULT_CHANNELS,
@@ -13,7 +15,9 @@ from roboco.seeds.initial_data import (
)
__all__ = [
"AGENT_UUIDS",
"AUDITOR_SILENT_ACCESS",
"CEO_AGENT_ID",
"CHANNEL_MEMBERSHIPS",
"DEFAULT_AGENTS",
"DEFAULT_CHANNELS",
+130 -31
View File
@@ -87,84 +87,183 @@ DEFAULT_CHANNELS = [
# =============================================================================
# DEFAULT AGENTS
#
# All agents have static UUIDs for consistent mapping between:
# - Database records
# - Task assignments
# - Container orchestration
#
# UUID scheme:
# - 0000-0000: CEO (human)
# - 0001-000X: Backend cell
# - 0002-000X: Frontend cell
# - 0003-000X: UX/UI cell
# - 0004-000X: Board/Management
# =============================================================================
# Static agent UUIDs - NEVER change these after initial deployment
AGENT_UUIDS = {
# CEO (Human)
"ceo": "00000000-0000-0000-0000-000000000001",
# Backend Cell
"be-dev-1": "00000000-0000-0000-0001-000000000001",
"be-dev-2": "00000000-0000-0000-0001-000000000002",
"be-qa": "00000000-0000-0000-0001-000000000003",
"be-pm": "00000000-0000-0000-0001-000000000004",
"be-doc": "00000000-0000-0000-0001-000000000005",
# Frontend Cell
"fe-dev-1": "00000000-0000-0000-0002-000000000001",
"fe-dev-2": "00000000-0000-0000-0002-000000000002",
"fe-qa": "00000000-0000-0000-0002-000000000003",
"fe-pm": "00000000-0000-0000-0002-000000000004",
"fe-doc": "00000000-0000-0000-0002-000000000005",
# UX/UI Cell
"ux-dev": "00000000-0000-0000-0003-000000000001",
"ux-qa": "00000000-0000-0000-0003-000000000002",
"ux-pm": "00000000-0000-0000-0003-000000000003",
"ux-doc": "00000000-0000-0000-0003-000000000004",
# Board / Management
"main-pm": "00000000-0000-0000-0004-000000000001",
"product-owner": "00000000-0000-0000-0004-000000000002",
"head-marketing": "00000000-0000-0000-0004-000000000003",
"auditor": "00000000-0000-0000-0004-000000000004",
}
DEFAULT_AGENTS: list[dict[str, Any]] = [
# Backend Cell
{
"agent_id": "be-dev-1",
"id": AGENT_UUIDS["be-dev-1"],
"slug": "be-dev-1",
"name": "Backend Developer 1",
"role": "developer",
"team": "backend",
},
{
"agent_id": "be-dev-2",
"id": AGENT_UUIDS["be-dev-2"],
"slug": "be-dev-2",
"name": "Backend Developer 2",
"role": "developer",
"team": "backend",
},
{"agent_id": "be-qa", "name": "Backend QA", "role": "qa", "team": "backend"},
{"agent_id": "be-pm", "name": "Backend PM", "role": "cell_pm", "team": "backend"},
{
"agent_id": "be-doc",
"id": AGENT_UUIDS["be-qa"],
"slug": "be-qa",
"name": "Backend QA",
"role": "qa",
"team": "backend",
},
{
"id": AGENT_UUIDS["be-pm"],
"slug": "be-pm",
"name": "Backend PM",
"role": "cell_pm",
"team": "backend",
},
{
"id": AGENT_UUIDS["be-doc"],
"slug": "be-doc",
"name": "Backend Documenter",
"role": "documenter",
"team": "backend",
},
# Frontend Cell
{
"agent_id": "fe-dev-1",
"id": AGENT_UUIDS["fe-dev-1"],
"slug": "fe-dev-1",
"name": "Frontend Developer 1",
"role": "developer",
"team": "frontend",
},
{
"agent_id": "fe-dev-2",
"id": AGENT_UUIDS["fe-dev-2"],
"slug": "fe-dev-2",
"name": "Frontend Developer 2",
"role": "developer",
"team": "frontend",
},
{"agent_id": "fe-qa", "name": "Frontend QA", "role": "qa", "team": "frontend"},
{"agent_id": "fe-pm", "name": "Frontend PM", "role": "cell_pm", "team": "frontend"},
{
"agent_id": "fe-doc",
"id": AGENT_UUIDS["fe-qa"],
"slug": "fe-qa",
"name": "Frontend QA",
"role": "qa",
"team": "frontend",
},
{
"id": AGENT_UUIDS["fe-pm"],
"slug": "fe-pm",
"name": "Frontend PM",
"role": "cell_pm",
"team": "frontend",
},
{
"id": AGENT_UUIDS["fe-doc"],
"slug": "fe-doc",
"name": "Frontend Documenter",
"role": "documenter",
"team": "frontend",
},
# UX/UI Cell
{
"agent_id": "ux-dev",
"id": AGENT_UUIDS["ux-dev"],
"slug": "ux-dev",
"name": "UX/UI Developer",
"role": "developer",
"team": "ux_ui",
},
{"agent_id": "ux-qa", "name": "UX/UI QA", "role": "qa", "team": "ux_ui"},
{"agent_id": "ux-pm", "name": "UX/UI PM", "role": "cell_pm", "team": "ux_ui"},
{
"agent_id": "ux-doc",
"id": AGENT_UUIDS["ux-qa"],
"slug": "ux-qa",
"name": "UX/UI QA",
"role": "qa",
"team": "ux_ui",
},
{
"id": AGENT_UUIDS["ux-pm"],
"slug": "ux-pm",
"name": "UX/UI PM",
"role": "cell_pm",
"team": "ux_ui",
},
{
"id": AGENT_UUIDS["ux-doc"],
"slug": "ux-doc",
"name": "UX/UI Documenter",
"role": "documenter",
"team": "ux_ui",
},
# Board / Management
{"agent_id": "main-pm", "name": "Main PM", "role": "main_pm", "team": None},
{
"agent_id": "product-owner",
"id": AGENT_UUIDS["main-pm"],
"slug": "main-pm",
"name": "Main PM",
"role": "main_pm",
"team": None,
},
{
"id": AGENT_UUIDS["product-owner"],
"slug": "product-owner",
"name": "Product Owner",
"role": "product_owner",
"team": None,
},
{
"agent_id": "head-marketing",
"id": AGENT_UUIDS["head-marketing"],
"slug": "head-marketing",
"name": "Head of Marketing",
"role": "head_marketing",
"team": None,
},
{"agent_id": "auditor", "name": "Auditor", "role": "auditor", "team": None},
{
"id": AGENT_UUIDS["auditor"],
"slug": "auditor",
"name": "Auditor",
"role": "auditor",
"team": None,
},
# CEO (Human)
{
"agent_id": "00000000-0000-0000-0000-000000000001",
"id": AGENT_UUIDS["ceo"],
"slug": "ceo",
"name": "Renzo",
"role": "ceo",
"team": None,
@@ -176,30 +275,30 @@ DEFAULT_AGENTS: list[dict[str, Any]] = [
# CHANNEL MEMBERSHIP
# =============================================================================
CEO_AGENT_ID = "00000000-0000-0000-0000-000000000001"
CEO_AGENT_ID = AGENT_UUIDS["ceo"]
CHANNEL_MEMBERSHIPS = {
# Cell channels - cell members + CEO
"backend-cell": ["be-dev-1", "be-dev-2", "be-qa", "be-pm", "be-doc", CEO_AGENT_ID],
"frontend-cell": ["fe-dev-1", "fe-dev-2", "fe-qa", "fe-pm", "fe-doc", CEO_AGENT_ID],
"uxui-cell": ["ux-dev", "ux-qa", "ux-pm", "ux-doc", CEO_AGENT_ID],
"backend-cell": ["be-dev-1", "be-dev-2", "be-qa", "be-pm", "be-doc", "ceo"],
"frontend-cell": ["fe-dev-1", "fe-dev-2", "fe-qa", "fe-pm", "fe-doc", "ceo"],
"uxui-cell": ["ux-dev", "ux-qa", "ux-pm", "ux-doc", "ceo"],
# Role channels + CEO
"dev-all": ["be-dev-1", "be-dev-2", "fe-dev-1", "fe-dev-2", "ux-dev", CEO_AGENT_ID],
"qa-all": ["be-qa", "fe-qa", "ux-qa", CEO_AGENT_ID],
"pm-all": ["be-pm", "fe-pm", "ux-pm", "main-pm", CEO_AGENT_ID],
"doc-all": ["be-doc", "fe-doc", "ux-doc", CEO_AGENT_ID],
"dev-all": ["be-dev-1", "be-dev-2", "fe-dev-1", "fe-dev-2", "ux-dev", "ceo"],
"qa-all": ["be-qa", "fe-qa", "ux-qa", "ceo"],
"pm-all": ["be-pm", "fe-pm", "ux-pm", "main-pm", "ceo"],
"doc-all": ["be-doc", "fe-doc", "ux-doc", "ceo"],
# Management channels + CEO
"main-pm-board": [
"main-pm",
"product-owner",
"head-marketing",
"auditor",
CEO_AGENT_ID,
"ceo",
],
"board-private": ["product-owner", "head-marketing", "auditor", CEO_AGENT_ID],
"board-private": ["product-owner", "head-marketing", "auditor", "ceo"],
# Broadcast channels - everyone (CEO included via DEFAULT_AGENTS)
"announcements": [a["agent_id"] for a in DEFAULT_AGENTS],
"all-hands": [a["agent_id"] for a in DEFAULT_AGENTS],
"announcements": [a["slug"] for a in DEFAULT_AGENTS],
"all-hands": [a["slug"] for a in DEFAULT_AGENTS],
}
# Auditor has silent read access to cell/role channels