Skip to content

Jira Transition Actions — Activating TRIGGER_AGENT Dispatch

Feature: FEAT-265 — jiraspecialist-trigger-agent-orchestrator (follow-up to the jira webhooks feature — see sdd/specs/jiraspecialist-webhooks.spec.md) Since: ai-parrot next minor Stability: stable


Background

JiraSpecialist receives Jira webhooks via JiraWebhookHook, classifies them, and routes jira.transitioned events through handle_hook_event()_dispatch_transition() → a registry of TransitionAction entries (self._transition_actions). Each action has an action_type:

action_type Handler Behaviour
NOTIFY_CHANNEL _action_notify_channel Sends a Telegram message via the agent's TelegramAgentWrapper.
CALL_HANDLER _action_call_handler Invokes a named method on the agent instance.
LOG _action_log_transition Emits a structured log line.
TRIGGER_AGENT _action_trigger_agent Dispatches another agent with a rendered task. See below.

Activating TRIGGER_AGENT dispatch

JiraSpecialist lives in the ai-parrot (core) package and must not import the server-side AutonomousOrchestrator (ai-parrot-server's parrot.autonomous.orchestrator) — that would be a layering violation. Instead, JiraSpecialist exposes a narrow, injectable async slot:

def set_agent_dispatcher(self, dispatcher: AgentDispatcher) -> None: ...

where AgentDispatcher (parrot.bots._types.AgentDispatcher) is a structural typing.Protocol:

async def __call__(self, agent_name: str, task: str, *,
                    user_id: Optional[str] = None,
                    session_id: Optional[str] = None) -> Any: ...

AutonomousOrchestrator.execute_agent already matches this shape, so wiring it in is a single call — made in the consuming deployment project's app.py (not in this repo; agents/ here is gitignored and is not the dispatch host), once both the orchestrator and the concrete Jira agent exist:

# in the consuming project's app.py, after both objects are constructed
jira_agent.set_agent_dispatcher(orchestrator.execute_agent)

Degrade behaviour (no dispatcher wired)

If set_agent_dispatcher() is never called, TRIGGER_AGENT actions log the trigger intent at WARNING level and return {"status": "skipped", "reason": "no dispatcher wired", "agent_id", "task"} — the webhook still returns 200; nothing raises.

Return-status vocabulary

status Meaning
dispatched The dispatcher was awaited successfully; result carries a truncated string summary.
skipped No agent_id in action_config, or no dispatcher wired.
error The dispatcher raised; the exception is caught and logged (exc_info=True); the transition action loop continues for any remaining matched actions.

Latency caveat (v1: await-inline)

The dispatcher is awaited inline — the action's return dict carries the real result/error, but this ties webhook response latency to the downstream agent's runtime. Verify after rollout that slow agents don't push the webhook response past Jira's timeout; if they do, a follow-up may move this to asyncio.create_task fire-and-forget.

Example

TransitionAction(
    from_status="*",
    to_status="Ready For Deploy",
    action_type=TransitionActionType.TRIGGER_AGENT,
    action_config={
        "agent_id": "deploy_bot",
        "task_template": "Deploy {issue_key}",
    },
)

When a NAV ticket transitions to Ready For Deploy and a dispatcher is wired, this fires await orchestrator.execute_agent("deploy_bot", "Deploy NAV-1234", ...).

See packages/ai-parrot/tests/test_jira_transition_dispatch.py for the full behavioural test suite (dispatch / skip / error / template rendering / end-to-end).