Skip to content

Task Engine

Task lifecycle management lives in an external PM tool (Jira, Plane, GitHub/GitLab issues). The engine uses ExecutionTracker — a thin orchestration layer that tracks which agent is working on which issue and the dependency graph between issues — the orchestration concerns the PM tool doesn’t cover.


The ExecutionTracker is a passive data structure — it emits no events and enforces no transitions. Events originate from webhooks via the NotificationService.

What it tracks: bidirectional agent ↔ issue mappings and a dependency graph between issues.

What it does NOT track: task status, transitions, storage, or lifecycle — all of that lives in the PM tool.

Interface:

MethodPurpose
track(issue_key, agent_id)Record that an agent is working on an issue (webhook assigned)
untrack(issue_key)Remove tracking (webhook resolved)
get_agent(issue_key)Look up which agent is on an issue
get_issue(issue_key)Get the full tracked issue metadata
get_issues(agent_id)List all issues an agent is working on
add_dependency(a, blocked_by_b)Record that issue A is blocked by issue B
dependencies_met(issue_key)Check if all blocking issues are resolved

PM-tool webhooks do not become dedicated task events. Every webhook is parsed by the NotificationService into an ExternalNotification delivered to the routed agents’ inboxes — the assignee, watchers, @-mentioned agents, or the project lead as a fallback (see Jira Integration). The woken agent then acts on the PM tool through its own MCP tools.

EnginePM tool (Jira/Plane/GitLab)EnginePM tool (Jira/Plane/GitLab)NotificationService parses + routes →ExternalNotification to the project lead's inbox(fallback routing) → lead agent turn→ ExternalNotification to the assignee's inbox → agent turn→ ExternalNotification to watchers, assignee,and @-mentioned agentsAgent creates subtask, transitions ticket, postscomment — all through MCP tools, same as a human wouldTicket created (webhook)Ticket assigned (webhook)Comment added (webhook)MCP tool call

The TaskAssigned event type exists for engine-internal work injection — the Scheduler fires it for cron-style recurring tasks — not for the PM-tool webhook pipeline. The ExecutionTracker itself is passive plumbing exposed to extensions (ExtensionContext.execution_tracker) and the turn context; the engine’s only built-in mutation is untracking a removed role’s issues during org hot-reload.


See Organization Model for how unit leads and rosters are configured.

Task assignment is not an algorithmic strategy — it is a team lead agent’s reasoning decision. When a task appears (via webhook or builtin tool), the engine notifies the team lead. The lead reasons about each member’s backstory, skills, workload, and knowledge scopes, then assigns the task — either via a builtin tool or by setting the assignee in the PM tool via MCP. The engine then wakes the assigned agent.

A human can also assign directly in the PM tool — the same webhook fires, the same agent wakes up. For top-level tasks (no team lead above), the founder assigns directly in the PM tool, or a C-level agent role acts as the top-level assigner.


There is no special escalation mechanism in Crewlet. When an agent is blocked or out of its depth, it hands off the same way a human would:

  • The agent reaches its manager during Execute with the colleague-surface tool that fits where the work lives — a Jira comment, a Slack mention, or a2a_ask for tight-loop sync. If the blocker only becomes clear at Review, Review returns decision="self_iterate" with a note telling Plan to add that outreach step, and the next pass makes the call.
  • The getting-unstuck tool skill (see examples/tool-skills/getting-unstuck.md) teaches the agent the discipline: include what you tried, options you see, your recommendation, and urgency. Never hand a naked problem.
  • The agent’s identity prompt names its manager, so the handoff target is always resolvable.

When the engine itself can’t continue — stall guard fires, max-iter exhausted, unhandled exception, LLM unavailable — it publishes a turn.guard_breach (or llm_unavailable) event and terminates the turn as failed. The dashboard derives an afk state from the latest failure event and surfaces a cause-specific status line so the founder sees what happened.


PM Tool (Jira)Issue created: 'Build auth API'Webhook fires → NotificationService

EngineExternalNotification (project-lead fallback routing)

EventQueue → Team Lead inboxa human lead has no inbox — the task falls through to thetarget role's own agents; the human sees it in the PM tool

Team lead reads task, queries knowledge.Creates subtasks in Jira via MCP tools:'Design auth endpoints' → Senior Engineer'Implement JWT middleware' → Senior Engineer'Write auth tests' → Junior Engineer

Jira webhooks fire for each assignment

ExternalNotification routed to each assignee's inbox

Agents work in parallel, transition tickets via MCP

Transition webhooks → watchers (incl. the lead) notified

Lead reviews results, transitions parent ticket

Transition webhook → manager notified (watcher/mention)

Engine-driven failure (stall / max-iter / exception / LLM down)

TurnGuardBreach (or LLMUnavailable)

Dashboard shows agent as 'afk' with a cause-specific quip.Founder investigates via the events panel.

Agent-detected blocker

Agent (e.g. Junior Engineer)working on task, encounters blocker

Execute calls the colleague-surface tool for the manager(or Review self_iterates so Plan adds that outreach step),targeting the surface that fits where the work lives

Colleague-surface tool fires(slack / jira / confluence / a2a)

Manager sees the mention on the same surface they alreadyuse for human teammates; their next turn fires when they reply

Generated from crewlet/crewlet v0.1.0 at b40ea18.