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)
This commit is contained in:
parent
a18cb2824e
commit
48c93e2ce9
13 changed files with 639 additions and 13 deletions
75
openspec/changes/dynamic-execution-mode/design.md
Normal file
75
openspec/changes/dynamic-execution-mode/design.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
## Context
|
||||
|
||||
当前任务创建时硬编码 `execution_mode = SshCli`。这在只有 ssh_cli agent 时可行,但在混合环境(ssh_cli + http_pull)中会阻塞 http_pull agent 接任务。
|
||||
|
||||
核心洞察:**执行模式是调度决策,不是任务属性**。任务只描述"需要什么能力",由调度器决定"怎么执行"。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 任务创建时不预设执行模式
|
||||
- Dispatch loop 根据注册的 agent 动态决定
|
||||
- Coordinator 可以显式指派任务给特定 agent
|
||||
- http_pull agent 能 dequeue 到未被 ssh_cli 认领的任务
|
||||
|
||||
**Non-Goals:**
|
||||
- 不实现智能调度(负载均衡、亲和性等)——Phase 2 再考虑
|
||||
- 不改变 receipt 验证流程
|
||||
- 不改变 Forgejo webhook 格式
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1: ExecutionMode 新增 Undecided
|
||||
|
||||
**选择**: 新增 `Undecided` 变体作为默认值
|
||||
|
||||
**理由**:
|
||||
- 向后兼容:已有的 `SshCli`/`HttpPull` 任务不受影响
|
||||
- 语义清晰:`Undecided` 表示"等待调度器决定"
|
||||
- dispatch loop 只处理 `Undecided` 任务,已决定的不再改变
|
||||
|
||||
**替代方案**:
|
||||
- 用 `Option<ExecutionMode>`(None 表示未决定)—— 语义等价但 enum 更明确
|
||||
- 去掉 execution_mode 字段,纯靠 runtime 状态—— 太激进,改太大
|
||||
|
||||
### Decision 2: 两阶段 dispatch
|
||||
|
||||
**选择**: dispatch loop 分两阶段:
|
||||
|
||||
1. **ssh_cli 阶段**:扫描 `Undecided` 任务,查找匹配的 ssh_cli host → 找到则标记 `SshCli` 并执行
|
||||
2. **http_pull 阶段**:剩余的 `Undecided` 任务标记为 `HttpPull`,等待 agent dequeue
|
||||
|
||||
**理由**:
|
||||
- ssh_cli 是主动调度(orchestrator 控制),优先级高于被动等待
|
||||
- http_pull agent 通过 dequeue 自行认领,不需要 orchestrator 主动分配
|
||||
- 两阶段简单清晰,不需要复杂的调度算法
|
||||
|
||||
### Decision 3: Coordinator 显式指派
|
||||
|
||||
**选择**: 新增 `POST /api/v1/tasks/{id}/assign` 端点
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_id": "hermes-worker-01",
|
||||
"execution_mode": "http_pull" // optional, auto-detect if omitted
|
||||
}
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- coordinator(Jeeves)可能比自动调度更了解哪个 agent 适合
|
||||
- 支持跨任务指派(如"这个文档任务给 Hermes")
|
||||
- 指派后任务不再是 `Undecided`,直接进入执行
|
||||
|
||||
### Decision 4: Dequeue 查询条件
|
||||
|
||||
**选择**: dequeue 查询改为 `execution_mode IN ('http_pull', 'undecided')`
|
||||
|
||||
**理由**:
|
||||
- 纯 http_pull 任务直接匹配
|
||||
- 被自动标记为 http_pull 的任务也能匹配
|
||||
- 如果调度器还没来得及处理 `Undecided`,agent 也能直接拉走(降级为 http_pull)
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[竞争条件]** ssh_cli dispatch 和 http_pull dequeue 可能同时抢同一个 `Undecided` 任务 → 用 SQLite 事务保证原子性,dequeue 用 `UPDATE ... RETURNING` 原子操作
|
||||
- **[调度延迟]** Undecided 任务可能等一个 dispatch cycle 才被标记为 http_pull → dequeue 直接查 Undecided 可以缓解
|
||||
Loading…
Add table
Add a link
Reference in a new issue