From 502b55b5a960d2b17b4c5299a1aae001a5e541e0 Mon Sep 17 00:00:00 2001 From: markov Date: Fri, 27 Feb 2026 23:55:56 +0100 Subject: [PATCH] Auto-assign as project option (default off) - Project.auto_assign boolean field (default False) - create_task only auto-assigns if project.auto_assign=True - PATCH /projects/{id} accepts auto_assign - ProjectOut includes auto_assign --- src/tracker/api/projects.py | 4 ++++ src/tracker/api/schemas.py | 1 + src/tracker/api/tasks.py | 4 ++-- src/tracker/models/project.py | 3 ++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/tracker/api/projects.py b/src/tracker/api/projects.py index febb44a..869fd4b 100644 --- a/src/tracker/api/projects.py +++ b/src/tracker/api/projects.py @@ -29,6 +29,7 @@ class ProjectUpdate(BaseModel): description: str | None = None repo_urls: list[str] | None = None status: str | None = None + auto_assign: bool | None = None class ProjectMemberAdd(BaseModel): @@ -70,6 +71,7 @@ async def _project_out(p: Project, db: AsyncSession) -> ProjectOut: status=p.status, task_counter=p.task_counter, chat_id=str(chat.id) if chat else None, + auto_assign=p.auto_assign, ) @@ -126,6 +128,8 @@ async def update_project(project_id: str, req: ProjectUpdate, db: AsyncSession = project.repo_urls = req.repo_urls if req.status is not None: project.status = req.status + if req.auto_assign is not None: + project.auto_assign = req.auto_assign await db.commit() return await _project_out(project, db) diff --git a/src/tracker/api/schemas.py b/src/tracker/api/schemas.py index cf2c049..f628fbb 100644 --- a/src/tracker/api/schemas.py +++ b/src/tracker/api/schemas.py @@ -102,6 +102,7 @@ class ProjectOut(BaseModel): status: str task_counter: int chat_id: str | None = None + auto_assign: bool = False class Config: from_attributes = True diff --git a/src/tracker/api/tasks.py b/src/tracker/api/tasks.py index 1b45f30..14298dd 100644 --- a/src/tracker/api/tasks.py +++ b/src/tracker/api/tasks.py @@ -282,8 +282,8 @@ async def create_task( db.add(task) await db.flush() - # Auto-assign by capabilities if no assignee set - if not assignee_id and req.labels: + # Auto-assign by capabilities if enabled and no assignee set + if not assignee_id and req.labels and project.auto_assign: from ..models import AgentConfig, ProjectMember agents_q = await db.execute( select(Member, AgentConfig) diff --git a/src/tracker/models/project.py b/src/tracker/models/project.py index 6893e7f..0eda74b 100644 --- a/src/tracker/models/project.py +++ b/src/tracker/models/project.py @@ -3,7 +3,7 @@ import uuid from typing import TYPE_CHECKING -from sqlalchemy import ForeignKey, Integer, String, Text, UniqueConstraint +from sqlalchemy import Boolean, ForeignKey, Integer, String, Text, UniqueConstraint from sqlalchemy.dialects.postgresql import ARRAY, UUID from sqlalchemy.orm import Mapped, mapped_column, relationship @@ -25,6 +25,7 @@ class Project(Base): repo_urls: Mapped[list[str]] = mapped_column(ARRAY(String), default=list) status: Mapped[str] = mapped_column(String(20), default=ProjectStatus.ACTIVE) # active | archived task_counter: Mapped[int] = mapped_column(Integer, default=0) # for sequential task numbers + auto_assign: Mapped[bool] = mapped_column(Boolean, default=False) # auto-assign tasks by labels ↔ capabilities tasks: Mapped[list["Task"]] = relationship(back_populates="project", cascade="all, delete-orphan") chats: Mapped[list["Chat"]] = relationship(back_populates="project", cascade="all, delete-orphan")