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. Separates database operations from bootstrap orchestration.
""" """
import contextlib
from uuid import UUID as UUIDType from uuid import UUID as UUIDType
import structlog import structlog
@@ -75,12 +74,13 @@ async def create_channels(session: AsyncSession) -> dict[str, str]:
async def create_agents(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 = {} agent_ids = {}
for agent_data in DEFAULT_AGENTS: 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 # Check if exists
result = await session.execute( result = await session.execute(
@@ -90,7 +90,7 @@ async def create_agents(session: AsyncSession) -> dict[str, str]:
if existing: if existing:
agent_ids[slug] = str(existing.id) agent_ids[slug] = str(existing.id)
logger.info("Agent exists", agent_id=slug) logger.info("Agent exists", slug=slug, id=str(existing.id))
continue continue
# Map role string to enum # 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_str = agent_data.get("team")
team = Team(team_str) if team_str else None team = Team(team_str) if team_str else None
# If slug is a valid UUID, use it as the database ID # Use the static UUID from initial_data.py
# (important for CEO so X-Agent-ID header matches the DB id) agent_uuid = UUIDType(static_id)
explicit_id: UUIDType | None = None
with contextlib.suppress(ValueError):
explicit_id = UUIDType(slug)
# Create agent using ORM # Create agent using ORM with static UUID
agent = AgentTable( agent = AgentTable(
id=explicit_id, # Will use slug as ID if it's a valid UUID id=agent_uuid,
name=agent_data["name"], name=agent_data["name"],
slug=slug, slug=slug,
role=role, role=role,
@@ -121,7 +118,7 @@ async def create_agents(session: AsyncSession) -> dict[str, str]:
await session.flush() await session.flush()
agent_ids[slug] = str(agent.id) 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 return agent_ids
+3 -2
View File
@@ -44,8 +44,9 @@ class TaskReflectionInput(BaseModel):
class DecisionOption(BaseModel): class DecisionOption(BaseModel):
"""A decision option with pros/cons.""" """A decision option with pros/cons."""
option: str name: str = Field(..., description="Option name/title")
pros_cons: str pros: str = Field(default="", description="Pros/advantages of this option")
cons: str = Field(default="", description="Cons/disadvantages of this option")
class DecisionLogInput(BaseModel): 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]: async def _handle_agent_idle(agent_id: str) -> dict[str, Any]:
"""Handle agent going idle (no work available).""" """Handle agent going idle (no work available)."""
headers = _get_agent_headers(agent_id) headers = _get_agent_headers(agent_id)
async with httpx.AsyncClient(timeout=30.0) as client: 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: try:
# Signal to orchestrator that this agent is idle # Signal to orchestrator that this agent is idle
resp = await client.post( 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 ( from roboco.seeds.initial_data import (
AGENT_UUIDS,
AUDITOR_SILENT_ACCESS, AUDITOR_SILENT_ACCESS,
CEO_AGENT_ID,
CHANNEL_MEMBERSHIPS, CHANNEL_MEMBERSHIPS,
DEFAULT_AGENTS, DEFAULT_AGENTS,
DEFAULT_CHANNELS, DEFAULT_CHANNELS,
@@ -13,7 +15,9 @@ from roboco.seeds.initial_data import (
) )
__all__ = [ __all__ = [
"AGENT_UUIDS",
"AUDITOR_SILENT_ACCESS", "AUDITOR_SILENT_ACCESS",
"CEO_AGENT_ID",
"CHANNEL_MEMBERSHIPS", "CHANNEL_MEMBERSHIPS",
"DEFAULT_AGENTS", "DEFAULT_AGENTS",
"DEFAULT_CHANNELS", "DEFAULT_CHANNELS",
+130 -31
View File
@@ -87,84 +87,183 @@ DEFAULT_CHANNELS = [
# ============================================================================= # =============================================================================
# DEFAULT AGENTS # 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]] = [ DEFAULT_AGENTS: list[dict[str, Any]] = [
# Backend Cell # Backend Cell
{ {
"agent_id": "be-dev-1", "id": AGENT_UUIDS["be-dev-1"],
"slug": "be-dev-1",
"name": "Backend Developer 1", "name": "Backend Developer 1",
"role": "developer", "role": "developer",
"team": "backend", "team": "backend",
}, },
{ {
"agent_id": "be-dev-2", "id": AGENT_UUIDS["be-dev-2"],
"slug": "be-dev-2",
"name": "Backend Developer 2", "name": "Backend Developer 2",
"role": "developer", "role": "developer",
"team": "backend", "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", "name": "Backend Documenter",
"role": "documenter", "role": "documenter",
"team": "backend", "team": "backend",
}, },
# Frontend Cell # Frontend Cell
{ {
"agent_id": "fe-dev-1", "id": AGENT_UUIDS["fe-dev-1"],
"slug": "fe-dev-1",
"name": "Frontend Developer 1", "name": "Frontend Developer 1",
"role": "developer", "role": "developer",
"team": "frontend", "team": "frontend",
}, },
{ {
"agent_id": "fe-dev-2", "id": AGENT_UUIDS["fe-dev-2"],
"slug": "fe-dev-2",
"name": "Frontend Developer 2", "name": "Frontend Developer 2",
"role": "developer", "role": "developer",
"team": "frontend", "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", "name": "Frontend Documenter",
"role": "documenter", "role": "documenter",
"team": "frontend", "team": "frontend",
}, },
# UX/UI Cell # UX/UI Cell
{ {
"agent_id": "ux-dev", "id": AGENT_UUIDS["ux-dev"],
"slug": "ux-dev",
"name": "UX/UI Developer", "name": "UX/UI Developer",
"role": "developer", "role": "developer",
"team": "ux_ui", "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", "name": "UX/UI Documenter",
"role": "documenter", "role": "documenter",
"team": "ux_ui", "team": "ux_ui",
}, },
# Board / Management # 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", "name": "Product Owner",
"role": "product_owner", "role": "product_owner",
"team": None, "team": None,
}, },
{ {
"agent_id": "head-marketing", "id": AGENT_UUIDS["head-marketing"],
"slug": "head-marketing",
"name": "Head of Marketing", "name": "Head of Marketing",
"role": "head_marketing", "role": "head_marketing",
"team": None, "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) # CEO (Human)
{ {
"agent_id": "00000000-0000-0000-0000-000000000001", "id": AGENT_UUIDS["ceo"],
"slug": "ceo",
"name": "Renzo", "name": "Renzo",
"role": "ceo", "role": "ceo",
"team": None, "team": None,
@@ -176,30 +275,30 @@ DEFAULT_AGENTS: list[dict[str, Any]] = [
# CHANNEL MEMBERSHIP # CHANNEL MEMBERSHIP
# ============================================================================= # =============================================================================
CEO_AGENT_ID = "00000000-0000-0000-0000-000000000001" CEO_AGENT_ID = AGENT_UUIDS["ceo"]
CHANNEL_MEMBERSHIPS = { CHANNEL_MEMBERSHIPS = {
# Cell channels - cell members + CEO # Cell channels - cell members + CEO
"backend-cell": ["be-dev-1", "be-dev-2", "be-qa", "be-pm", "be-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_AGENT_ID], "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_AGENT_ID], "uxui-cell": ["ux-dev", "ux-qa", "ux-pm", "ux-doc", "ceo"],
# Role channels + CEO # Role channels + CEO
"dev-all": ["be-dev-1", "be-dev-2", "fe-dev-1", "fe-dev-2", "ux-dev", 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_AGENT_ID], "qa-all": ["be-qa", "fe-qa", "ux-qa", "ceo"],
"pm-all": ["be-pm", "fe-pm", "ux-pm", "main-pm", CEO_AGENT_ID], "pm-all": ["be-pm", "fe-pm", "ux-pm", "main-pm", "ceo"],
"doc-all": ["be-doc", "fe-doc", "ux-doc", CEO_AGENT_ID], "doc-all": ["be-doc", "fe-doc", "ux-doc", "ceo"],
# Management channels + CEO # Management channels + CEO
"main-pm-board": [ "main-pm-board": [
"main-pm", "main-pm",
"product-owner", "product-owner",
"head-marketing", "head-marketing",
"auditor", "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) # Broadcast channels - everyone (CEO included via DEFAULT_AGENTS)
"announcements": [a["agent_id"] for a in DEFAULT_AGENTS], "announcements": [a["slug"] for a in DEFAULT_AGENTS],
"all-hands": [a["agent_id"] for a in DEFAULT_AGENTS], "all-hands": [a["slug"] for a in DEFAULT_AGENTS],
} }
# Auditor has silent read access to cell/role channels # Auditor has silent read access to cell/role channels