mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
[F113] collapse WorkSession creation to the validated service path
_create_work_session_if_needed constructed WorkSessionTable directly, duplicating WorkSessionService.create's validation (existing-active check, single-active-per-task supersede, project/task existence). The two sites had drifted. Route through WorkSessionService.create instead, mapping ConflictError to the idempotent 'if needed' None. Remove the now-dead _supersede_other_active_sessions (create's supersede_active_sessions_for_task replaces it). Fix three pre-existing RED tests surfaced by the sweep (all confirmed failing on the F110 commit before this change): - test_fail_qa_work_session_fallback_excludes_qa_session: inserted two ACTIVE work_sessions per task, violating uq_work_sessions_one_active _per_task (migration 047). The QA session is now ABANDONED — still in the fallback query's result set (the query filters by task_id + agent_id, not status), so the exclude filter (agent_id != qa_id) is still exercised and the dev is resolved. - test_ceo_reject_routes_coordination_task_to_main_pm / test_ceo_reject_routes_batch_umbrella_to_main_pm: ceo_reject emits an audit row keyed to CEO_AGENT_ID, but the tests never seeded the CEO agent row (fk_audit_log_agent_id_agents). Seed the CEO agent (get-or- create, mirroring test_ceo_reject_writes_handoff_journal).
This commit is contained in:
@@ -36,12 +36,13 @@ from roboco.models.base import (
|
||||
)
|
||||
from roboco.models.permissions import AgentContext
|
||||
from roboco.models.task import TaskCreateRequest
|
||||
from roboco.models.work_session import WorkSessionStatus
|
||||
from roboco.models.work_session import WorkSessionCreate, WorkSessionStatus
|
||||
from roboco.services.task import (
|
||||
SoftBlockInfo,
|
||||
SoftBlockInput,
|
||||
TaskService,
|
||||
)
|
||||
from roboco.services.work_session import WorkSessionService
|
||||
from sqlalchemy import select
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -448,6 +449,53 @@ async def test_create_work_session_no_project_returns_none(
|
||||
assert out is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_work_session_delegates_to_service_create(
|
||||
task_setup: dict, db_session: AsyncSession, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""F113: the claim path must create the WorkSession through the validated
|
||||
``WorkSessionService.create`` (the single source of truth), not construct a
|
||||
``WorkSessionTable`` directly. Two divergent creation sites had drifted and
|
||||
bypassed the service-layer validation (existing-active check, supersede
|
||||
invariant, project/task existence). Routing through ``create`` collapses
|
||||
them to one validated path. The derived target_branch (parent branch for
|
||||
subtasks, project default for roots) is passed in via ``WorkSessionCreate``.
|
||||
"""
|
||||
svc = task_setup["svc"]
|
||||
task = await svc.create(_req(task_setup))
|
||||
task.branch_name = "feature/backend/delegate"
|
||||
await db_session.flush()
|
||||
|
||||
captured: dict[str, Any] = {}
|
||||
original_create = WorkSessionService.create
|
||||
|
||||
async def _spy_create(_self: Any, data: WorkSessionCreate) -> Any:
|
||||
# Record the WorkSessionCreate the claim path handed to the service,
|
||||
# then run the real create so the row persists (the FK on
|
||||
# tasks.work_session_id requires a real work_sessions row).
|
||||
captured["data"] = data
|
||||
return await original_create(_self, data)
|
||||
|
||||
monkeypatch.setattr(WorkSessionService, "create", _spy_create)
|
||||
|
||||
out = await svc._create_work_session_if_needed(
|
||||
task, task_setup["agent_id"], "developer"
|
||||
)
|
||||
|
||||
assert out is not None
|
||||
assert "data" in captured
|
||||
sent = captured["data"]
|
||||
assert isinstance(sent, WorkSessionCreate)
|
||||
assert sent.project_id == task_setup["project_id"]
|
||||
assert sent.task_id == task.id
|
||||
assert sent.agent_id == task_setup["agent_id"]
|
||||
assert sent.branch_name == "feature/backend/delegate"
|
||||
# Root task targets the project default branch.
|
||||
assert sent.target_branch == sent.base_branch
|
||||
# The claim path links the session back onto the task.
|
||||
assert task.work_session_id == out.id
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# unclaim_for_reaper / unclaim_for_agent — work-session abandon paths
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user