feat: add board goals, memory, approvals, onboarding models

This commit is contained in:
Abhimanyu Saharan
2026-02-05 14:39:34 +05:30
parent 78e89427ba
commit 89d2428252
9 changed files with 239 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
"""board lead orchestration
Revision ID: 3b9b2f1a6c2d
Revises: 9f2c1a7b0d3e
Create Date: 2026-02-05 14:45:00.000000
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "3b9b2f1a6c2d"
down_revision = "9f2c1a7b0d3e"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column("boards", sa.Column("board_type", sa.String(), server_default="goal", nullable=False))
op.add_column("boards", sa.Column("objective", sa.Text(), nullable=True))
op.add_column("boards", sa.Column("success_metrics", sa.JSON(), nullable=True))
op.add_column("boards", sa.Column("target_date", sa.DateTime(), nullable=True))
op.add_column(
"boards",
sa.Column("goal_confirmed", sa.Boolean(), server_default=sa.text("false"), nullable=False),
)
op.add_column("boards", sa.Column("goal_source", sa.Text(), nullable=True))
op.add_column(
"agents",
sa.Column("is_board_lead", sa.Boolean(), server_default=sa.text("false"), nullable=False),
)
op.add_column(
"tasks",
sa.Column("auto_created", sa.Boolean(), server_default=sa.text("false"), nullable=False),
)
op.add_column("tasks", sa.Column("auto_reason", sa.Text(), nullable=True))
op.create_table(
"board_memory",
sa.Column("id", sa.Uuid(), nullable=False),
sa.Column("board_id", sa.Uuid(), nullable=False),
sa.Column("content", sa.Text(), nullable=False),
sa.Column("tags", sa.JSON(), nullable=True),
sa.Column("source", sa.Text(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["board_id"], ["boards.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_index("ix_board_memory_board_id", "board_memory", ["board_id"], unique=False)
op.create_table(
"approvals",
sa.Column("id", sa.Uuid(), nullable=False),
sa.Column("board_id", sa.Uuid(), nullable=False),
sa.Column("agent_id", sa.Uuid(), nullable=True),
sa.Column("action_type", sa.String(), nullable=False),
sa.Column("payload", sa.JSON(), nullable=True),
sa.Column("confidence", sa.Integer(), nullable=False),
sa.Column("rubric_scores", sa.JSON(), nullable=True),
sa.Column("status", sa.String(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("resolved_at", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(["agent_id"], ["agents.id"]),
sa.ForeignKeyConstraint(["board_id"], ["boards.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_index("ix_approvals_board_id", "approvals", ["board_id"], unique=False)
op.create_index("ix_approvals_agent_id", "approvals", ["agent_id"], unique=False)
op.create_index("ix_approvals_status", "approvals", ["status"], unique=False)
op.create_table(
"board_onboarding_sessions",
sa.Column("id", sa.Uuid(), nullable=False),
sa.Column("board_id", sa.Uuid(), nullable=False),
sa.Column("session_key", sa.String(), nullable=False),
sa.Column("status", sa.String(), nullable=False),
sa.Column("messages", sa.JSON(), nullable=True),
sa.Column("draft_goal", sa.JSON(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["board_id"], ["boards.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
"ix_board_onboarding_sessions_board_id",
"board_onboarding_sessions",
["board_id"],
unique=False,
)
op.create_index(
"ix_board_onboarding_sessions_status",
"board_onboarding_sessions",
["status"],
unique=False,
)
op.create_table(
"task_fingerprints",
sa.Column("id", sa.Uuid(), nullable=False),
sa.Column("board_id", sa.Uuid(), nullable=False),
sa.Column("fingerprint_hash", sa.String(), nullable=False),
sa.Column("task_id", sa.Uuid(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["board_id"], ["boards.id"]),
sa.ForeignKeyConstraint(["task_id"], ["tasks.id"]),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
"ix_task_fingerprints_board_hash",
"task_fingerprints",
["board_id", "fingerprint_hash"],
unique=True,
)
def downgrade() -> None:
op.drop_index("ix_task_fingerprints_board_hash", table_name="task_fingerprints")
op.drop_table("task_fingerprints")
op.drop_index(
"ix_board_onboarding_sessions_status", table_name="board_onboarding_sessions"
)
op.drop_index(
"ix_board_onboarding_sessions_board_id", table_name="board_onboarding_sessions"
)
op.drop_table("board_onboarding_sessions")
op.drop_index("ix_approvals_status", table_name="approvals")
op.drop_index("ix_approvals_agent_id", table_name="approvals")
op.drop_index("ix_approvals_board_id", table_name="approvals")
op.drop_table("approvals")
op.drop_index("ix_board_memory_board_id", table_name="board_memory")
op.drop_table("board_memory")
op.drop_column("tasks", "auto_reason")
op.drop_column("tasks", "auto_created")
op.drop_column("agents", "is_board_lead")
op.drop_column("boards", "goal_source")
op.drop_column("boards", "goal_confirmed")
op.drop_column("boards", "target_date")
op.drop_column("boards", "success_metrics")
op.drop_column("boards", "objective")
op.drop_column("boards", "board_type")

View File

@@ -1,15 +1,23 @@
from app.models.activity_events import ActivityEvent from app.models.activity_events import ActivityEvent
from app.models.agents import Agent from app.models.agents import Agent
from app.models.approvals import Approval
from app.models.board_memory import BoardMemory
from app.models.board_onboarding import BoardOnboardingSession
from app.models.boards import Board from app.models.boards import Board
from app.models.gateways import Gateway from app.models.gateways import Gateway
from app.models.tasks import Task from app.models.tasks import Task
from app.models.task_fingerprints import TaskFingerprint
from app.models.users import User from app.models.users import User
__all__ = [ __all__ = [
"ActivityEvent", "ActivityEvent",
"Agent", "Agent",
"Approval",
"BoardMemory",
"BoardOnboardingSession",
"Board", "Board",
"Gateway", "Gateway",
"Task", "Task",
"TaskFingerprint",
"User", "User",
] ]

View File

@@ -27,5 +27,6 @@ class Agent(SQLModel, table=True):
delete_requested_at: datetime | None = Field(default=None) delete_requested_at: datetime | None = Field(default=None)
delete_confirm_token_hash: str | None = Field(default=None, index=True) delete_confirm_token_hash: str | None = Field(default=None, index=True)
last_seen_at: datetime | None = Field(default=None) last_seen_at: datetime | None = Field(default=None)
is_board_lead: bool = Field(default=False, index=True)
created_at: datetime = Field(default_factory=datetime.utcnow) created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow)

View File

@@ -0,0 +1,22 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID, uuid4
from sqlalchemy import JSON, Column
from sqlmodel import Field, SQLModel
class Approval(SQLModel, table=True):
__tablename__ = "approvals"
id: UUID = Field(default_factory=uuid4, primary_key=True)
board_id: UUID = Field(foreign_key="boards.id", index=True)
agent_id: UUID | None = Field(default=None, foreign_key="agents.id", index=True)
action_type: str
payload: dict[str, object] | None = Field(default=None, sa_column=Column(JSON))
confidence: int
rubric_scores: dict[str, int] | None = Field(default=None, sa_column=Column(JSON))
status: str = Field(default="pending", index=True)
created_at: datetime = Field(default_factory=datetime.utcnow)
resolved_at: datetime | None = None

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID, uuid4
from sqlalchemy import JSON, Column
from sqlmodel import Field, SQLModel
class BoardMemory(SQLModel, table=True):
__tablename__ = "board_memory"
id: UUID = Field(default_factory=uuid4, primary_key=True)
board_id: UUID = Field(foreign_key="boards.id", index=True)
content: str
tags: list[str] | None = Field(default=None, sa_column=Column(JSON))
source: str | None = None
created_at: datetime = Field(default_factory=datetime.utcnow)

View File

@@ -0,0 +1,20 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID, uuid4
from sqlalchemy import JSON, Column
from sqlmodel import Field, SQLModel
class BoardOnboardingSession(SQLModel, table=True):
__tablename__ = "board_onboarding_sessions"
id: UUID = Field(default_factory=uuid4, primary_key=True)
board_id: UUID = Field(foreign_key="boards.id", index=True)
session_key: str
status: str = Field(default="active", index=True)
messages: list[dict[str, object]] | None = Field(default=None, sa_column=Column(JSON))
draft_goal: dict[str, object] | None = Field(default=None, sa_column=Column(JSON))
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
from uuid import UUID, uuid4 from uuid import UUID, uuid4
from sqlalchemy import JSON, Column
from sqlmodel import Field from sqlmodel import Field
from app.models.tenancy import TenantScoped from app.models.tenancy import TenantScoped
@@ -15,5 +16,11 @@ class Board(TenantScoped, table=True):
name: str name: str
slug: str = Field(index=True) slug: str = Field(index=True)
gateway_id: UUID | None = Field(default=None, foreign_key="gateways.id", index=True) gateway_id: UUID | None = Field(default=None, foreign_key="gateways.id", index=True)
board_type: str = Field(default="goal", index=True)
objective: str | None = None
success_metrics: dict[str, object] | None = Field(default=None, sa_column=Column(JSON))
target_date: datetime | None = None
goal_confirmed: bool = Field(default=False)
goal_source: str | None = None
created_at: datetime = Field(default_factory=datetime.utcnow) created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow)

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from datetime import datetime
from uuid import UUID, uuid4
from sqlmodel import Field, SQLModel
class TaskFingerprint(SQLModel, table=True):
__tablename__ = "task_fingerprints"
id: UUID = Field(default_factory=uuid4, primary_key=True)
board_id: UUID = Field(foreign_key="boards.id", index=True)
fingerprint_hash: str = Field(index=True)
task_id: UUID = Field(foreign_key="tasks.id")
created_at: datetime = Field(default_factory=datetime.utcnow)

View File

@@ -23,6 +23,8 @@ class Task(TenantScoped, table=True):
created_by_user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True) created_by_user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
assigned_agent_id: UUID | None = Field(default=None, foreign_key="agents.id", index=True) assigned_agent_id: UUID | None = Field(default=None, foreign_key="agents.id", index=True)
auto_created: bool = Field(default=False)
auto_reason: str | None = None
created_at: datetime = Field(default_factory=datetime.utcnow) created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow)