feat: agent coordination system (workflows, locks, components, messages)

Adds /api/coord/* endpoints for real-time cross-session coordination:
- coord_workflows: named units of work per project
- coord_work_items: tasks within workflows with dependency chains
- coord_session_locks: exclusive resource locks with auto-expiry (TTL)
- coord_component_states: live component state per project (upsert)
- coord_messages: cross-session messaging and broadcasts
- /api/coord/status: cross-project snapshot endpoint

Replaces PROJECT_STATE.md as the coordination layer for Claude sessions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 08:25:33 -07:00
parent bd88398297
commit 63975284f4
24 changed files with 1565 additions and 0 deletions

View File

@@ -5,6 +5,11 @@ This package contains all database models and their base classes.
"""
from api.models.api_audit_log import ApiAuditLog
from api.models.coord_workflow import CoordWorkflow
from api.models.coord_work_item import CoordWorkItem
from api.models.coord_session_lock import CoordSessionLock
from api.models.coord_component_state import CoordComponentState
from api.models.coord_message import CoordMessage
from api.models.backup_log import BackupLog
from api.models.base import Base, TimestampMixin, UUIDMixin
from api.models.billable_time import BillableTime
@@ -47,6 +52,11 @@ from api.models.work_item_tag import WorkItemTag
__all__ = [
"ApiAuditLog",
"CoordWorkflow",
"CoordWorkItem",
"CoordSessionLock",
"CoordComponentState",
"CoordMessage",
"BackupLog",
"Base",
"BillableTime",

View File

@@ -0,0 +1,57 @@
"""Coordination component state model."""
from typing import Optional
from sqlalchemy import PrimaryKeyConstraint, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from .base import Base, TimestampMixin
class CoordComponentState(Base, TimestampMixin):
"""Current state of a named component within a project."""
__tablename__ = "coord_component_states"
project_key: Mapped[str] = mapped_column(
String(200),
nullable=False,
primary_key=True,
doc="Project namespace"
)
component: Mapped[str] = mapped_column(
String(200),
nullable=False,
primary_key=True,
doc="Component name, e.g. 'server', 'agent', 'dashboard', 'database'"
)
state: Mapped[str] = mapped_column(
String(50),
nullable=False,
doc="State: deployed, building, stable, broken, unknown"
)
version: Mapped[Optional[str]] = mapped_column(
String(100),
doc="Version string or git SHA"
)
notes: Mapped[Optional[str]] = mapped_column(
Text,
doc="Freeform notes about current state"
)
updated_by: Mapped[str] = mapped_column(
String(200),
nullable=False,
doc="Session that last updated this record"
)
__table_args__ = (
PrimaryKeyConstraint("project_key", "component", name="pk_coord_component_states"),
)
def __repr__(self) -> str:
return f"<CoordComponentState(project_key='{self.project_key}', component='{self.component}', state='{self.state}')>"

View File

@@ -0,0 +1,56 @@
"""Coordination inter-session message model."""
from datetime import datetime
from typing import Optional
from sqlalchemy import DateTime, Index, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from .base import Base, TimestampMixin, UUIDMixin
class CoordMessage(Base, UUIDMixin, TimestampMixin):
"""A message sent from one session to another (or broadcast)."""
__tablename__ = "coord_messages"
from_session: Mapped[str] = mapped_column(
String(200),
nullable=False,
doc="Sending session, e.g. 'DESKTOP-0O8A1RL/Claude'"
)
to_session: Mapped[Optional[str]] = mapped_column(
String(200),
doc="Recipient session; NULL means broadcast to all"
)
project_key: Mapped[Optional[str]] = mapped_column(
String(200),
doc="Optional project context for the message"
)
subject: Mapped[str] = mapped_column(
String(500),
nullable=False,
doc="Message subject line"
)
body: Mapped[str] = mapped_column(
Text,
nullable=False,
doc="Message body, markdown ok"
)
read_at: Mapped[Optional[datetime]] = mapped_column(
DateTime,
doc="NULL means unread"
)
__table_args__ = (
Index("idx_coord_messages_to_read", "to_session", "read_at"),
Index("idx_coord_messages_from", "from_session"),
)
def __repr__(self) -> str:
return f"<CoordMessage(from='{self.from_session}', to='{self.to_session}', subject='{self.subject}')>"

View File

@@ -0,0 +1,64 @@
"""Coordination session lock model."""
from datetime import datetime
from typing import Optional
from sqlalchemy import DateTime, Index, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.sql import func
from .base import Base, TimestampMixin, UUIDMixin
class CoordSessionLock(Base, UUIDMixin, TimestampMixin):
"""An exclusive lock held by a session on a resource path."""
__tablename__ = "coord_session_locks"
project_key: Mapped[str] = mapped_column(
String(200),
nullable=False,
doc="Project namespace this lock applies to"
)
session_id: Mapped[str] = mapped_column(
String(200),
nullable=False,
doc="Session holding the lock, e.g. 'DESKTOP-0O8A1RL/Claude'"
)
resource: Mapped[str] = mapped_column(
String(500),
nullable=False,
doc="Resource path being locked, e.g. 'server/src/', 'migrations/'"
)
description: Mapped[Optional[str]] = mapped_column(
Text,
doc="Why this lock was acquired"
)
acquired_at: Mapped[datetime] = mapped_column(
DateTime,
nullable=False,
server_default=func.now(),
doc="When the lock was claimed"
)
expires_at: Mapped[Optional[datetime]] = mapped_column(
DateTime,
doc="NULL means no expiry; otherwise the lock expires at this time"
)
released_at: Mapped[Optional[datetime]] = mapped_column(
DateTime,
doc="NULL means still held; set when lock is explicitly released"
)
__table_args__ = (
Index("idx_coord_locks_project_resource", "project_key", "resource"),
Index("idx_coord_locks_session", "session_id"),
)
def __repr__(self) -> str:
return f"<CoordSessionLock(project_key='{self.project_key}', resource='{self.resource}', session_id='{self.session_id}')>"

View File

@@ -0,0 +1,87 @@
"""Coordination work item model."""
from datetime import datetime
from typing import Optional
from sqlalchemy import CHAR, CheckConstraint, ForeignKey, Index, Integer, String, Text, DateTime
from sqlalchemy.orm import Mapped, mapped_column
from .base import Base, TimestampMixin, UUIDMixin
class CoordWorkItem(Base, UUIDMixin, TimestampMixin):
"""A discrete task within a coordination workflow."""
__tablename__ = "coord_work_items"
workflow_id: Mapped[str] = mapped_column(
CHAR(36),
ForeignKey("coord_workflows.id", ondelete="CASCADE"),
nullable=False,
doc="Parent workflow"
)
project_key: Mapped[str] = mapped_column(
String(200),
nullable=False,
doc="Denormalized project key for filtering without join"
)
title: Mapped[str] = mapped_column(
String(500),
nullable=False,
doc="Short title"
)
description: Mapped[Optional[str]] = mapped_column(
Text,
doc="Full description, markdown ok — store design specs, schemas, etc."
)
status: Mapped[str] = mapped_column(
String(20),
nullable=False,
default="pending",
doc="Status: pending, in_progress, blocked, completed, cancelled"
)
priority: Mapped[int] = mapped_column(
Integer,
nullable=False,
default=0,
doc="Higher value = more urgent"
)
assigned_session: Mapped[Optional[str]] = mapped_column(
String(200),
doc="Session currently working this item"
)
depends_on_id: Mapped[Optional[str]] = mapped_column(
CHAR(36),
ForeignKey("coord_work_items.id", ondelete="SET NULL"),
doc="Blocking predecessor item"
)
started_at: Mapped[Optional[datetime]] = mapped_column(
DateTime,
doc="When work began"
)
completed_at: Mapped[Optional[datetime]] = mapped_column(
DateTime,
doc="When item reached a terminal state"
)
__table_args__ = (
CheckConstraint(
"status IN ('pending', 'in_progress', 'blocked', 'completed', 'cancelled')",
name="ck_coord_work_items_status"
),
Index("idx_coord_work_items_workflow", "workflow_id"),
Index("idx_coord_work_items_project_status", "project_key", "status"),
Index("idx_coord_work_items_assigned", "assigned_session"),
)
def __repr__(self) -> str:
return f"<CoordWorkItem(title='{self.title}', status='{self.status}')>"

View File

@@ -0,0 +1,61 @@
"""Coordination workflow model."""
from datetime import datetime
from typing import Optional
from sqlalchemy import CHAR, CheckConstraint, Index, String, Text, DateTime
from sqlalchemy.orm import Mapped, mapped_column
from .base import Base, TimestampMixin, UUIDMixin
class CoordWorkflow(Base, UUIDMixin, TimestampMixin):
"""A named unit of work spanning one or more sessions."""
__tablename__ = "coord_workflows"
project_key: Mapped[str] = mapped_column(
String(200),
nullable=False,
doc="Project namespace slug, e.g. 'gururmm', 'client/acme-corp'"
)
name: Mapped[str] = mapped_column(
String(200),
nullable=False,
doc="Short identifier, e.g. 'discovery-feature'"
)
description: Mapped[Optional[str]] = mapped_column(
Text,
doc="Freeform description, markdown ok"
)
status: Mapped[str] = mapped_column(
String(20),
nullable=False,
default="planning",
doc="Status: planning, active, blocked, completed, cancelled"
)
created_by: Mapped[str] = mapped_column(
String(200),
nullable=False,
doc="Session that created this workflow, e.g. 'DESKTOP-0O8A1RL/Claude'"
)
completed_at: Mapped[Optional[datetime]] = mapped_column(
DateTime,
doc="When the workflow reached a terminal state"
)
__table_args__ = (
CheckConstraint(
"status IN ('planning', 'active', 'blocked', 'completed', 'cancelled')",
name="ck_coord_workflows_status"
),
Index("idx_coord_workflows_project_status", "project_key", "status"),
)
def __repr__(self) -> str:
return f"<CoordWorkflow(project_key='{self.project_key}', name='{self.name}', status='{self.status}')>"