AI Task System
The AI task system is the mechanism by which OpenPR assigns work to AI coding agents. It bridges the gap between a human creating an issue in a project tracker and an AI agent autonomously writing code to resolve it.
Overview
Section titled “Overview”The system has four components:
- API — Creates tasks, manages state, receives callbacks from agents
- Worker — Background process that polls for pending tasks and dispatches them via webhooks
- Webhook — HTTP POST to the agent’s endpoint with task details
- Agent — The AI coding tool (Codex, Claude Code, OpenCode) that executes the task and reports back
Issue assigned to bot | v API creates ai_task (status: pending) | v Worker picks up task (status: processing) | v Worker dispatches via webhook POST | v Agent executes (writes code, runs tests) | v Agent calls API callback | v Task marked completed or failedTask Lifecycle
Section titled “Task Lifecycle”State Transitions
Section titled “State Transitions”pending --> processing --> completed ^ | | v +------- failed (if retries remain, back to pending)| Status | Meaning |
|---|---|
pending | Task is queued, waiting for the worker to pick it up |
processing | Worker has claimed the task and dispatched it to an agent |
completed | Agent reported successful completion |
failed | Agent reported failure or all retry attempts exhausted |
cancelled | Task was manually cancelled |
Retry Mechanism
Section titled “Retry Mechanism”Each task has a configurable max_attempts (default: 3). When a dispatch fails or an agent reports failure:
- If
attempts < max_attempts, the task returns topendingstatus with anext_retry_attimestamp - Retry backoff is linear:
attempts * 30 seconds, capped at 600 seconds (10 minutes) - If all attempts are exhausted, the task moves to
failedand anai.task_failedwebhook is fired
Event Log
Section titled “Event Log”Every state transition is recorded in the ai_task_events table with a timestamped event type and payload:
| Event Type | When |
|---|---|
created | Task is initially created |
picked_up | Worker claims the task for processing |
progress | Agent reports intermediate progress |
completed | Agent reports successful completion |
retried | Task is returned to pending after a failure |
failed | Task exhausts all retry attempts |
Task Types
Section titled “Task Types”The system supports four task types:
| Type | Description | Reference Type |
|---|---|---|
issue_assigned | An issue has been assigned to the bot for implementation | work_item |
review_requested | A code review or proposal review is requested | proposal |
comment_requested | The bot is asked to comment on an issue or proposal | comment |
vote_requested | The bot is asked to vote on a governance proposal | proposal |
Bot Users and Authentication
Section titled “Bot Users and Authentication”Bot User Accounts
Section titled “Bot User Accounts”AI agents are represented as users with entity_type = 'bot'. They are registered in the users table like any other user but authenticate differently.
Bot Tokens
Section titled “Bot Tokens”Bot tokens use the opr_ prefix and are workspace-scoped. They are stored in the workspace_bots table as SHA-256 hashes.
Authorization: Bearer opr_0a5bc81ea108dad8077decc880abced0d923aa873b9ff774575ec152aecf15d5Bot tokens carry:
| Field | Description |
|---|---|
workspace_id | Workspace the token is scoped to |
permissions | Array of permission strings (read, write, admin) |
is_active | Whether the token is currently valid |
expires_at | Optional expiration timestamp |
last_used_at | Automatically updated on each API call |
AI Participants
Section titled “AI Participants”Bot users are registered as AI participants in specific projects via the ai_participants table. Each participant has:
| Field | Description |
|---|---|
name | Display name (e.g., “Claude Agent”) |
model | Model identifier (e.g., “claude-opus-4”) |
provider | Provider name (e.g., “anthropic”) |
capabilities | JSON object describing what the agent can do |
max_domain_level | Autonomy level: observer, advisor, voter, vetoer, autonomous |
can_veto_human_consensus | Whether the agent can override human votes |
reason_min_length | Minimum length for vote justifications |
is_active | Whether the participant is enabled |
Worker Process
Section titled “Worker Process”The worker is a standalone Rust binary that runs as a background service. It polls the ai_tasks table every 5 seconds for pending tasks.
Task Pickup
Section titled “Task Pickup”The worker uses PostgreSQL FOR UPDATE SKIP LOCKED to safely claim tasks in a concurrent environment:
- Query for pending tasks where
next_retry_atis null or in the past - Atomically update their status to
processingand incrementattempts - Record a
picked_upevent
Tasks are ordered by priority DESC, created_at ASC — higher-priority tasks are processed first, with older tasks breaking ties.
Dispatch
Section titled “Dispatch”For each claimed task, the worker:
- Looks up the active webhook for the bot user in the task’s project
- Constructs a dispatch payload with task details
- Sends an HTTP POST to the webhook URL
- If the POST fails, records the failure and either retries or marks the task as failed
Dispatch Payload
Section titled “Dispatch Payload”The webhook POST body sent to the agent contains:
{ "task_id": "uuid", "project_id": "uuid", "ai_participant_id": "uuid", "task_type": "issue_assigned", "reference_type": "work_item", "reference_id": "uuid", "payload": { "issue_title": "Fix authentication flow", "issue_description": "The login endpoint returns 500..." }, "attempts": 1, "max_attempts": 3}Concurrency
Section titled “Concurrency”The worker accepts a --concurrency flag (default: 4) that controls how many tasks are picked up per polling cycle. The actual batch size is concurrency * 10.
Agent Callback API
Section titled “Agent Callback API”After executing a task, the agent reports results back to OpenPR through the REST API.
Complete a Task
Section titled “Complete a Task”POST /api/projects/:project_id/ai-tasks/:task_id/completeAuthorization: Bearer opr_...Content-Type: application/json
{ "summary": "Fixed the authentication flow by...", "files_changed": ["src/auth.rs", "src/middleware.rs"], "commit_sha": "abc123"}The request body is stored as the task’s result field. The task moves to completed status and an ai.task_completed webhook event is fired.
Fail a Task
Section titled “Fail a Task”POST /api/projects/:project_id/ai-tasks/:task_id/failAuthorization: Bearer opr_...Content-Type: application/json
{ "error_message": "Test suite failed with 3 errors", "payload": { "test_output": "..." }}If the task has remaining retry attempts, it returns to pending with a backoff delay. Otherwise, it moves to failed and an ai.task_failed webhook event is fired.
Report Progress
Section titled “Report Progress”POST /api/projects/:project_id/ai-tasks/:task_id/progressAuthorization: Bearer opr_...Content-Type: application/json
{ "step": "running tests", "progress_pct": 75}Progress reports are recorded as progress events in ai_task_events but do not change the task status.
Creating Tasks
Section titled “Creating Tasks”Automatic (via Issue Assignment)
Section titled “Automatic (via Issue Assignment)”When an issue is assigned to a bot user and a webhook with matching bot_user_id exists, the issue assignment triggers the creation of an issue_assigned AI task.
Automatic (via Governance)
Section titled “Automatic (via Governance)”When a proposal enters the voting phase, vote_requested tasks are automatically created for all active AI participants in the project. This is handled by queue_vote_requested_tasks_for_project which iterates over active bot users and creates idempotent tasks.
Manual (via API)
Section titled “Manual (via API)”POST /api/projects/:project_id/ai-tasksAuthorization: Bearer <jwt_or_bot_token>Content-Type: application/json
{ "ai_participant_id": "bot-user-uuid", "task_type": "issue_assigned", "reference_type": "work_item", "reference_id": "issue-uuid", "priority": 5, "payload": { "instructions": "..." }, "max_attempts": 3, "idempotency_key": "unique-key-to-prevent-duplicates"}The idempotency_key prevents duplicate task creation. If a task with the same key already exists, the API returns a 409 Conflict.
Permissions
Section titled “Permissions”| Operation | Who Can Do It |
|---|---|
| Create task | Project admin/owner or system admin |
| Complete/fail task | The assigned AI participant or system admin |
| Report progress | The assigned AI participant or system admin |
| List tasks | Any project member |
Related
Section titled “Related”- OpenPR Overview — Architecture and deployment
- MCP Server — 34 tools for agent interaction
- Webhooks — Event types including
ai.task_completedandai.task_failed - Governance — How
vote_requestedtasks are auto-created - Architecture Overview — Full pipeline data flow