Files
T
2026-07-06 23:52:53 +08:00

57 lines
4.9 KiB
Markdown

## ADDED Requirements
### Requirement: Task submission enqueues a scheduled task
The system SHALL allow a caller to submit a task (a goal string, or a reference to a `WorkflowDefinition`, plus optional device constraints: `driver_type`, required capability tags) and SHALL enqueue it as a `ScheduledTask` with status `queued`, returning a stable task id the caller can poll.
#### Scenario: Successful submission
- **WHEN** a caller submits a task with a goal and no constraints
- **THEN** the scheduler creates a `ScheduledTask` with status `queued`, assigns it a unique id, and returns that id to the caller without blocking for a device to become available
#### Scenario: Queue depth limit reached
- **WHEN** a caller submits a task while the queue already holds `config.max_queue_depth` queued tasks
- **THEN** the scheduler rejects the submission with a clear error rather than accepting an unbounded backlog
### Requirement: Assignment matches a queued task to an idle, constraint-matching device
The system SHALL assign a queued `ScheduledTask` to an idle `PooledDevice` (as reported by the `device-pool` capability) whose `driver_type` and capability tags satisfy the task's constraints, using a named, registrable `AssignmentStrategy`.
#### Scenario: Matching idle device available
- **WHEN** `assign()` runs and at least one idle `PooledDevice` matches the head-of-queue task's constraints
- **THEN** the scheduler selects one such device via the configured `AssignmentStrategy`, transitions the task to status `assigned`, and records the chosen `device_id`/`host_id`
#### Scenario: No matching device available
- **WHEN** `assign()` runs and no idle `PooledDevice` matches the head-of-queue task's constraints
- **THEN** the task remains `queued` (not failed), and `assign()` returns without error, ready to be retried on a later call
#### Scenario: Unknown assignment strategy configured
- **WHEN** `TaskScheduler` is configured with an `AssignmentStrategy` name that is not registered
- **THEN** the scheduler raises a clear configuration error at startup/first-assign rather than silently falling back to a default strategy
### Requirement: Assignment strategies are pluggable by name
The system SHALL provide an `AssignmentStrategy` registry mapping a strategy name to an implementation, with a default `fifo_match` strategy (oldest-queued matching task first, first matching idle device), and SHALL allow a new strategy to be added by registering a name without modifying `TaskScheduler`'s control flow.
#### Scenario: Default FIFO strategy orders by submission time
- **WHEN** two tasks with satisfiable, overlapping constraints are queued in order A then B, and one matching idle device exists
- **THEN** the default `fifo_match` strategy assigns the device to task A, leaving task B queued
#### Scenario: Adding a new strategy requires no scheduler edit
- **WHEN** a new `AssignmentStrategy` implementation is registered under a new name
- **THEN** `TaskScheduler` can be configured to use it by name alone, with no change to `scheduler.py`'s assignment control flow
### Requirement: Local dispatch executes an assignment via existing runners
The system SHALL provide a `TaskDispatcher` that, for an assignment whose device is owned by the local process's own host, executes the assigned task by composing the existing `agent-runtime` task-execution entry point (for a goal-based submission) or the `workflow-orchestration` workflow-execution entry point (for a workflow-based submission), without reimplementing planning/execution/retry logic.
#### Scenario: Dispatching a goal-based assignment
- **WHEN** `TaskDispatcher.dispatch()` is called with an assignment for a goal-based `ScheduledTask` whose device is local
- **THEN** the dispatcher constructs and runs a `Task` through the existing task-execution entry point, and updates the `ScheduledTask`'s status to `done` or `failed` based on the resulting task's outcome
#### Scenario: Dispatching a workflow-based assignment
- **WHEN** `TaskDispatcher.dispatch()` is called with an assignment referencing a `WorkflowDefinition` whose device is local
- **THEN** the dispatcher runs the definition through the existing workflow-execution entry point and updates the `ScheduledTask`'s status based on the resulting workflow run's outcome
### Requirement: Remote assignments are rejected explicitly, not silently ignored
The system SHALL raise a distinct, typed error when `TaskDispatcher.dispatch()` is called for an assignment whose device is owned by a host other than the dispatching process's own host, rather than attempting execution or silently no-op'ing.
#### Scenario: Assignment targets a remote host's device
- **WHEN** `TaskDispatcher.dispatch()` is called with an assignment whose `host_id` does not match the local process's own host id
- **THEN** the dispatcher raises a `RemoteDispatchNotSupportedError` and leaves the `ScheduledTask`'s status unchanged from `assigned`