"""Dev dispatch sequence pacing — no doomed spawn for a sequence-held task.

``_spawn_pending_dev`` boots a full dev container for a pre-assigned pending
task. Before this guard, a task held only by the assignee-blind sequence
guard (a non-terminal lower-sequence same-parent sibling, not a declared
dependency) passed every pre-spawn gate — ``_blocked_by_earlier_lane_sibling``
is narrower (same dev's lane) and ``_validate_task_for_spawn`` checks declared
dependencies, not sequence siblings — so the container spawned, the agent's
first claim hit ``_claim_blocked_by_sequence`` at the chokepoint and was
refused, and the agent exited only to be re-spawned next tick. Mirror the PM
path's ``_pending_claim_blocked`` prefilter so a sequence-held task is skipped
pre-spawn instead of churned.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any, cast
from unittest.mock import AsyncMock, patch
from uuid import uuid4

import pytest
from roboco.runtime.orchestrator import AgentOrchestrator

if TYPE_CHECKING:
    import httpx


def _orch() -> AgentOrchestrator:
    orch = object.__new__(AgentOrchestrator)
    orch._instances = {}
    return orch


def _pending_dev_task(**over: Any) -> dict[str, Any]:
    base: dict[str, Any] = {
        "id": str(uuid4()),
        "status": "pending",
        "team": "backend",
        "task_type": "code",
        "title": "Sequenced code leaf",
        "assigned_to": str(uuid4()),
    }
    base.update(over)
    return base


@pytest.mark.asyncio
async def test_sequence_held_dev_task_not_spawned() -> None:
    """A sequence-held task skips spawn entirely — no container, no churn."""
    orch = _orch()
    task = _pending_dev_task()
    client = cast("httpx.AsyncClient", object())
    with (
        patch.object(orch, "_is_agent_active", return_value=False),
        patch.object(orch, "_pending_claim_blocked", new=AsyncMock(return_value=True)),
        patch.object(orch, "_blocked_by_earlier_lane_sibling", new=AsyncMock()) as lane,
        patch.object(orch, "_pm_respawn_should_gate", new=AsyncMock()) as respawn,
        patch.object(orch, "spawn_agent", new=AsyncMock()) as spawn,
    ):
        await orch._spawn_pending_dev(client, task, "be-dev-1")

    spawn.assert_not_awaited()
    # Sequence guard is broader and checked first — narrower per-dev probe never runs.
    lane.assert_not_awaited()
    respawn.assert_not_awaited()


@pytest.mark.asyncio
async def test_ready_dev_task_still_spawns() -> None:
    """A clear task (prefilter False, lane clear, respawn gate clear, valid)
    proceeds to spawn unchanged."""
    orch = _orch()
    task = _pending_dev_task()
    client = cast("httpx.AsyncClient", object())
    with (
        patch.object(orch, "_is_agent_active", return_value=False),
        patch.object(orch, "_pending_claim_blocked", new=AsyncMock(return_value=False)),
        patch.object(
            orch, "_blocked_by_earlier_lane_sibling", new=AsyncMock(return_value=False)
        ),
        patch.object(
            orch, "_pm_respawn_should_gate", new=AsyncMock(return_value=False)
        ),
        patch.object(
            orch, "_validate_task_for_spawn", new=AsyncMock(return_value=None)
        ),
        patch.object(
            orch, "_get_prompt_for_agent", new=AsyncMock(return_value="prompt")
        ),
        patch.object(orch, "_task_git_context", return_value=None),
        patch.object(orch, "spawn_agent", new=AsyncMock()) as spawn,
    ):
        await orch._spawn_pending_dev(client, task, "be-dev-1")

    spawn.assert_awaited_once()
