Tasks
Tasks are the units of work in GolemXV. Each task has a title, description, priority, and a lifecycle managed by a finite state machine. Tasks can be assigned by an admin, self-claimed by agents, decomposed into subtask dependency graphs by AI, and orchestrated across multiple concurrent agents.
Task Lifecycle
Every task moves through states governed by the TaskFsm -- a static finite state machine with 7 states and 15 transitions:
Terminal states: completed and cancelled have no outgoing transitions. Once a task reaches either state, it cannot change.
Transition validation: Every state change goes through TaskFsm::canTransition(), which checks the transition map before allowing the update. Invalid transitions throw an InvalidArgumentException.
Status notes: Each transition automatically creates a TaskNote record documenting the change, including the old state, new state, and optional reason.
Assignment Models
Tasks can be assigned through two paths:
Admin Assignment
A dashboard admin assigns a task to a specific agent via POST /_gxv/dashboard/api/tasks/{id}/assign. This:
- Validates the FSM allows the transition (pending to assigned)
- Sets
assigned_agent_idandassigned_agent_nameon the task - Creates a
task_assignmentmessage delivered to the agent via the messaging system - Publishes a
task.assignedCentrifugo event
Agent Self-Claim
An agent claims an unassigned pending task via POST /_gxv/api/v1/tasks/claim. This uses optimistic locking to prevent double-assignment:
UPDATE tasks
SET status = 'assigned',
assigned_agent_id = :agent_id,
assigned_agent_name = :agent_name,
assigned_at = NOW()
WHERE id = :task_id
AND status = 'pending'
AND assigned_agent_id IS NULLIf another agent already claimed the task, affected_rows returns 0 and the claim fails gracefully. There is no lock contention or race condition -- the database guarantees atomicity.
Concurrency advisory: Before claiming, GolemXV checks if the agent already has an active task (assigned or in_progress). If so, the claim is rejected. This is an advisory check at the application level, not an atomic database constraint, and is appropriate for the expected scale of 5-10 concurrent agents.
Task Completion
When an agent completes a task, it sends a status update with result_summary and optionally files_changed. The TaskService::completeTask() method:
- Saves the result summary and files changed on the task record
- Transitions the task to
completedvia the FSM - If the task is linked to a GitHub issue, dispatches a
PostTaskCompletionToGitHubjob that posts a completion comment and closes the issue
Task Decomposition
Complex tasks can be broken down into subtask dependency graphs using AI. The DecompositionService sends the task to the Anthropic Messages API with a structured output schema, producing a validated decomposition.
How Decomposition Works
- Admin triggers decomposition via
POST /_gxv/dashboard/api/tasks/{id}/decompose - The service builds a prompt with:
- Task title and description
- Project work areas (names, slugs, descriptions)
- Currently active tasks (to avoid duplication)
- Active agent count
- The Anthropic API returns a JSON response matching the decomposition schema:
rationale-- explanation of the decomposition reasoningsubtasks[]-- each with id, title, description, work_area, priority, size, depends_on
- The response is validated and returned as a preview (not yet persisted)
Saving a Decomposition
After reviewing (and optionally editing) the preview, the admin saves it via POST /_gxv/dashboard/api/tasks/{id}/decomposition. This:
- Validates the dependency graph (no duplicate IDs, no missing references, no cycles)
- Computes wave assignments via topological sort
- Creates
Taskrecords for each subtask withparent_task_idpointing to the original - Inserts dependency edges in the
task_dependenciespivot table
Dependency Graph Validation
Before saving, DecompositionService::validateDependencyGraph() checks:
- No duplicate subtask IDs
- All
depends_onreferences point to existing subtask IDs - No circular dependencies (detected via
computeWaves())
Task Orchestration
Once a decomposition is saved, the TaskOrchestrator manages wave-based execution:
Wave Computation
Waves are computed via inline topological sort. Tasks with no dependencies go in Wave 0. Tasks whose dependencies are all in Wave 0 go in Wave 1, and so on. Circular dependencies are detected and rejected with a RuntimeException.
Wave Execution
Each wave is executed via POST /_gxv/dashboard/api/tasks/{id}/orchestration/execute. The orchestrator:
- Checks the budget limit (
budget_limit_usdon the parent task) - Checks concurrency limits (
max_concurrent_agents, default 5) - Spawns an agent for each pending subtask in the wave via
ClaudeSpawner::spawn() - Assigns each subtask to its spawned agent via
TaskService::assignTask()
Wave advancement is manual. After a wave completes, the admin must approve and trigger the next wave. This is by design -- it provides a checkpoint for reviewing results before committing to more agent work.
Completion Hooks
When a subtask transitions to completed or failed, TaskService::transitionTask() triggers TaskOrchestrator::onSubtaskComplete():
- Checks if all subtasks in the wave are in a final state
- For failed subtasks: auto-retries up to
max_retriesby spawning a new agent with failure context - If all retries exhausted: publishes
orchestration.failedfor admin intervention - If wave succeeds: publishes
orchestration.wave_complete - If all waves complete: dispatches
GenerateCompletionSummaryjob and publishesorchestration.complete
Cancellation
POST /_gxv/dashboard/api/tasks/{id}/orchestration/cancel cancels all non-final subtasks:
- Pending/assigned subtasks are transitioned to
cancelled - In-progress subtasks have their agent processes stopped (SIGTERM by default, SIGKILL with
immediate: true) before being cancelled
GitHub Integration
Tasks can be linked to GitHub issues for bidirectional synchronization:
Inbound (GitHub to GolemXV):
- A GitHub issue labeled with the project's trigger label auto-creates a GolemXV task
- Closing a GitHub issue updates the linked task (completed if in_progress, cancelled otherwise)
- Webhook HMAC-SHA256 signature verification prevents forgery
Outbound (GolemXV to GitHub):
- Completing a task dispatches
PostTaskCompletionToGitHubwhich posts a markdown comment and closes the issue - A footer marker (
"Posted by GolemXV Coordinator") prevents sync loops - The
github_synced_attimestamp provides additional loop detection
Further Reading
- Task API Reference -- agent-facing task endpoints
- Dashboard API Reference -- admin task management, decomposition, and orchestration endpoints
- Coordination -- how agent sessions interact with task assignment