agent-fleet/openspec/changes/dynamic-execution-mode/specs/task-lifecycle/spec.md
Zer4tul 48c93e2ce9 feat: dynamic execution mode — Undecided tasks, two-phase dispatch, assign API
- ExecutionMode enum adds Undecided variant (default for new tasks)
- Webhook creates tasks as Undecided instead of hardcoded SshCli
- Dispatch loop: Phase 1 matches ssh_cli hosts, Phase 2 marks remaining as HttpPull
- Dequeue now returns http_pull AND undecided tasks (atomic claim)
- New endpoint: POST /api/v1/tasks/{id}/assign for coordinator explicit assignment
- Backward compatible: existing SshCli/HttpPull tasks unaffected
- 37 tests passing (6 new)
2026-05-13 05:29:12 +08:00

4 KiB

ADDED Requirements

Requirement: ExecutionMode enum includes Undecided variant

ExecutionMode enum SHALL include an Undecided variant as the default for newly created tasks.

Scenario: Task created via Forgejo webhook

  • WHEN a Forgejo Issue webhook creates a task
  • THEN execution_mode SHALL be Undecided
  • AND the task SHALL be eligible for both ssh_cli dispatch and http_pull dequeue

Scenario: Task created via API

  • WHEN a task is created via direct API call without specifying execution_mode
  • THEN execution_mode SHALL default to Undecided

Requirement: Two-phase dispatch loop

The dispatch loop SHALL use a two-phase approach to handle Undecided tasks.

Scenario: Undecided task with matching ssh_cli host

  • GIVEN an Undecided task with labels ["agent:code"]
  • AND a registered ssh_cli host with agent capabilities matching ["agent:code"]
  • WHEN the dispatch loop runs
  • THEN the task SHALL be assigned execution_mode = SshCli
  • AND the task SHALL be dispatched via SSH for execution

Scenario: Undecided task with no matching ssh_cli host

  • GIVEN an Undecided task with labels ["agent:review", "agent:document"]
  • AND no registered ssh_cli host with matching capabilities
  • WHEN the dispatch loop runs
  • THEN the task SHALL be assigned execution_mode = HttpPull
  • AND the task SHALL become available for http_pull dequeue

Scenario: Undecided task with matching ssh_cli host but agent offline

  • GIVEN an Undecided task with matching ssh_cli host
  • AND the ssh_cli host is unreachable or agent is offline
  • WHEN the dispatch loop runs
  • THEN the task SHALL remain Undecided (retry next cycle)
  • AND the task SHALL also be available for http_pull dequeue (fallback)

Requirement: Coordinator explicit assignment

The API SHALL provide an endpoint for coordinators to explicitly assign tasks to specific agents.

Scenario: Coordinator assigns task to specific agent

  • GIVEN a task in Created or Undecided status
  • WHEN coordinator calls POST /api/v1/tasks/{id}/assign with {"agent_id": "hermes-worker-01"}
  • THEN the task SHALL be assigned to the specified agent
  • AND execution_mode SHALL be auto-detected from the agent's registration type (http_pull for registered agents, ssh_cli for configured hosts)
  • AND the task status SHALL transition to Assigned

Scenario: Coordinator assigns to non-existent agent

  • WHEN coordinator calls assign with an unknown agent_id
  • THEN the API SHALL return 404 Not Found

Scenario: Coordinator assigns already-running task

  • WHEN coordinator calls assign on a task in Running or Completed status
  • THEN the API SHALL return 400 Bad Request

Requirement: Dequeue accepts Undecided tasks

The dequeue endpoint SHALL return tasks with execution_mode of either HttpPull or Undecided.

Scenario: Agent dequeues Undecided task

  • GIVEN an Undecided task matching the agent's capabilities
  • WHEN an http_pull agent calls dequeue
  • THEN the task SHALL be returned
  • AND execution_mode SHALL be atomically updated to HttpPull
  • AND the task SHALL be assigned to the dequeuing agent

Scenario: No race condition between dispatch and dequeue

  • GIVEN an Undecided task
  • WHEN both ssh_cli dispatch and http_pull dequeue attempt to claim it simultaneously
  • THEN exactly one SHALL succeed (atomic claim via DB transaction)
  • AND the other SHALL get no task / skip the task

Requirement: Backward compatibility

Existing tasks with execution_mode = SshCli or HttpPull SHALL continue to work without changes.

Scenario: Pre-existing SshCli task

  • WHEN a task already has execution_mode = SshCli
  • THEN the dispatch loop SHALL process it as before (no change)

Scenario: Pre-existing HttpPull task

  • WHEN a task already has execution_mode = HttpPull
  • THEN the dequeue endpoint SHALL return it as before (no change)