Files
roboco/tests/integration/test_task_baseline_constraints.py
T

118 lines
4.1 KiB
Python
Raw Normal View History

"""TaskService.create appends the project's baseline constraints (flag-gated)."""
from __future__ import annotations
from typing import TYPE_CHECKING
from uuid import UUID, uuid4
from roboco.config import settings
from roboco.db.tables import AgentTable, ProjectTable
from roboco.models import AgentRole, AgentStatus, Complexity, Team
from roboco.models.base import TaskNature
from roboco.models.task import TaskCreateRequest, TaskType
from roboco.services.task import TaskService
if TYPE_CHECKING:
import pytest
from sqlalchemy.ext.asyncio import AsyncSession
async def _seed(db: AsyncSession) -> tuple[AgentTable, ProjectTable]:
agent = AgentTable(
id=uuid4(),
name="Dev",
slug=f"be-dev-{uuid4().hex[:8]}",
role=AgentRole.DEVELOPER,
team=Team.BACKEND,
status=AgentStatus.ACTIVE,
model_config={},
system_prompt="dev",
capabilities=[],
permissions={},
metrics={},
)
db.add(agent)
await db.flush()
project = ProjectTable(
id=uuid4(),
name="C-Proj",
slug=f"c-proj-{uuid4().hex[:8]}",
git_url="https://example.com/r.git",
assigned_cell=Team.BACKEND,
created_by=agent.id,
)
db.add(project)
await db.flush()
return agent, project
def _req(
agent: AgentTable, project: ProjectTable, description: str
) -> TaskCreateRequest:
return TaskCreateRequest(
title="A task",
description=description,
acceptance_criteria=["it works"],
team=Team.BACKEND,
created_by=UUID(str(agent.id)),
task_type=TaskType.CODE,
nature=TaskNature.TECHNICAL,
estimated_complexity=Complexity.MEDIUM,
project_id=UUID(str(project.id)),
)
async def test_baseline_attached_when_flag_on(
db_session: AsyncSession, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr(settings, "conventions_enabled", True)
agent, project = await _seed(db_session)
task = await TaskService(db_session).create(_req(agent, project, "Do the work"))
# The conventions block lives in `constraints`, not `description` (the
# 2026-07-07 fix: description is the human-authored instruction only).
assert task.description == "Do the work"
assert task.constraints is not None
assert "## Constraints" in task.constraints
assert "no lint suppressions" in task.constraints
async def test_flag_off_attaches_nothing(
db_session: AsyncSession, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr(settings, "conventions_enabled", False)
agent, project = await _seed(db_session)
task = await TaskService(db_session).create(_req(agent, project, "Do the work"))
assert task.description == "Do the work"
assert task.constraints is None
async def test_baseline_not_suppressed_by_agent_constraints_section(
db_session: AsyncSession, monkeypatch: pytest.MonkeyPatch
) -> None:
# An agent-authored ## Constraints section in the description must NOT
# suppress the mandatory server baseline — the description keeps the
# agent's block and `constraints` carries the server baseline (separate
# field, so neither suppresses the other).
monkeypatch.setattr(settings, "conventions_enabled", True)
agent, project = await _seed(db_session)
seeded = "Do the work\n\n## Constraints\n- a task-specific note"
task = await TaskService(db_session).create(_req(agent, project, seeded))
assert task.description is not None
assert "a task-specific note" in task.description
assert task.constraints is not None
assert "no lint suppressions" in task.constraints
async def test_baseline_attach_is_idempotent(
db_session: AsyncSession, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr(settings, "conventions_enabled", True)
agent, project = await _seed(db_session)
svc = TaskService(db_session)
task = await svc.create(_req(agent, project, "Do the work"))
before = task.constraints
await svc._attach_baseline_constraints(task)
assert task.constraints == before
assert task.constraints is not None
assert task.constraints.count("no lint suppressions") == 1