feat: dual execution model (SSH CLI + HTTP pull)
- ExecutionMode enum: SshCli (orchestrator dispatches) | HttpPull (agent pulls) - SSH CLI executor: spawn remote agents via ssh + CLI template - Local subprocess as SSH special case (localhost) - HostConfig with capability matching and load-based selection - Dispatch loop: scan created tasks → select host → execute → update - CliAdapterConfig: CLI templates for Codex and Claude Code - Structured prompt construction (Issue → goal/constraints/validation) - Output parsers: Codex JSON, Claude Code JSON, raw fallback - TaskStatus::ReviewPending + review_count loop limit - Forgejo webhook: pull_request (opened→review_pending, merged→completed) - Forgejo webhook: push events (task/* branch → last_activity_at) - HTTP API: dequeue only returns http_pull tasks - HTTP API: status update only for http_pull mode - Token auth config for http_pull agents - Adapter module rewritten: AgentAdapter trait removed → config-driven CLI templates - New fields: execution_mode, assigned_host, branch_name, pr_title, last_activity_at, review_count - 30/30 tests pass
This commit is contained in:
parent
1bc7580ecc
commit
e39a16498c
34 changed files with 2541 additions and 1555 deletions
2
openspec/changes/dual-execution-model/.openspec.yaml
Normal file
2
openspec/changes/dual-execution-model/.openspec.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
schema: spec-driven
|
||||
created: 2026-05-12
|
||||
83
openspec/changes/dual-execution-model/design.md
Normal file
83
openspec/changes/dual-execution-model/design.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
## Context
|
||||
|
||||
`adapter-cross-machine-revision` change 设计了纯 Pull 模型(Agent 主动调 HTTP API),但分析后发现 Pull 模型不是 subprocess CLI 的跨机等价——控制权、上下文传递、生命周期管理都不同。
|
||||
|
||||
真正的跨机等价是 **SSH + CLI**:Orchestrator 通过 SSH 在远程主机上 spawn Agent CLI,与本地 subprocess 完全一致的执行模型。
|
||||
|
||||
同时,HTTP Pull 对外部 Agent(OpenClaw/Jeeves、Hermes)仍有价值。因此需要双执行模型。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- SSH + CLI 作为主执行模式(Orchestrator 主动调度、构造上下文、控制生命周期)
|
||||
- HTTP API 保留给外部 Agent 自主接入
|
||||
- 每种 Agent 类型定义为 CLI 模板 + 输出解析器
|
||||
- 远程主机管理(SSH 连接、CLI 可用性检查)
|
||||
|
||||
**Non-Goals:**
|
||||
- 不实现 Agent daemon(Phase 1 用 SSH + CLI 足够)
|
||||
- 不实现动态主机发现(Phase 1 静态配置)
|
||||
- 不实现容器化执行(不用 Docker/Kubernetes)
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1: SSH + CLI 是 subprocess 的跨机等价
|
||||
|
||||
**选择**: Orchestrator 通过 SSH 执行远程 Agent CLI
|
||||
|
||||
**理由**:
|
||||
- 控制流与本地 subprocess 完全一致
|
||||
- 上下文由 Orchestrator 构造,通过 CLI 参数传入
|
||||
- Agent 不需要预运行,不需要 daemon
|
||||
- 唯一前提:SSH 免密登录 + 目标机器装了 CLI
|
||||
|
||||
**替代方案**:
|
||||
- HTTP Pull:控制权反转,上下文传递难解决
|
||||
- Agent daemon:复杂度高,每台机器多一个服务
|
||||
- 容器化:更复杂,需要 container runtime
|
||||
|
||||
### Decision 2: HTTP Pull 作为补充模式
|
||||
|
||||
**选择**: 保留 HTTP API 给外部 Agent
|
||||
|
||||
**理由**:
|
||||
- OpenClaw/Jeeves、Hermes 有自己的调度,不需要 Orchestrator 启动
|
||||
- 它们只需要查询/更新任务状态
|
||||
- 两种模式通过 `execution_mode` 字段区分
|
||||
|
||||
### Decision 3: Adapter = CLI 模板 + 输出解析器
|
||||
|
||||
**选择**: 不实现 AgentAdapter trait,而是配置驱动
|
||||
|
||||
**理由**:
|
||||
- CLI 模板可以通过配置文件定义,无需编译
|
||||
- 不同 Agent 的差异主要在 CLI 参数和输出格式
|
||||
- 新增 Agent 类型只需要加配置,不需要写代码
|
||||
|
||||
### Decision 4: 本地执行作为 SSH 的特例
|
||||
|
||||
**选择**: Orchestrator 所在机器的 Agent 用本地 subprocess,不经过 SSH
|
||||
|
||||
**理由**:
|
||||
- 避免 SSH loopback 的开销和配置
|
||||
- subprocess 是 SSH 的本地特例,逻辑统一
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[SSH 密钥管理] 需要配置免密 SSH** → 用 SSH agent forwarding 或 deploy key,标准运维实践
|
||||
- **[长时间运行] SSH 连接可能超时** → 用 `ssh -o ServerAliveInterval=60`,或改用 SSH multiplexing
|
||||
- **[CLI 版本差异] 不同机器可能装不同版本** → health check 时验证版本
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 新增 `src/execution/` 模块(SSH executor、CLI template、output parser)
|
||||
2. Task 模型添加 `execution_mode`、`assigned_host` 字段
|
||||
3. 新增 `[hosts]` 配置 section
|
||||
4. 实现 Orchestrator dispatch loop(ssh_cli → SSH 执行,http_pull → 等待 Agent dequeue)
|
||||
5. 保留并调整 HTTP API 端点(dequeue 仅限 http_pull 任务)
|
||||
6. 更新测试
|
||||
|
||||
## Open Questions
|
||||
|
||||
- SSH 库选择:`ssh2` crate vs `tokio::process::Command` + 系统 `ssh` 命令?
|
||||
- 是否需要支持 SSH jump host(通过跳板机连接目标机器)?
|
||||
46
openspec/changes/dual-execution-model/proposal.md
Normal file
46
openspec/changes/dual-execution-model/proposal.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
## Why
|
||||
|
||||
当前 `adapter-cross-machine-revision` change 设计了纯 Pull 模型(Agent 主动调 HTTP API 拉取任务),但这不是 subprocess CLI 的真正跨机等价:
|
||||
|
||||
- **subprocess CLI**:Orchestrator 主动拉起 Agent,构造上下文,控制生命周期。Agent 跑完退出。
|
||||
- **HTTP Pull**:Agent 必须自己已经在运行,自己来拉任务,自己管生命周期。控制权反转。
|
||||
|
||||
Pull 模型无法解决:
|
||||
1. **上下文传递**:Agent 拉到 task JSON 后要自己构建 prompt、加载仓库、决定怎么执行
|
||||
2. **Agent 不在运行**:Orchestrator 无法启动远程机器上的 Agent
|
||||
3. **生命周期管理**:Orchestrator 无法控制 Agent 启停
|
||||
|
||||
而 **SSH + CLI 才是 subprocess 的真正跨机等价**:控制流、上下文传递、生命周期管理与本地 spawn 完全一致。
|
||||
|
||||
同时,HTTP Pull 模式对**外部 Agent**(OpenClaw/Jeeves、Hermes 等)仍有价值——这些 Agent 有自己的调度和运行时,只需要通过 API 查询/更新状态。
|
||||
|
||||
因此需要设计**双执行模型**。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 新增 **SSH + CLI 执行模式**:Orchestrator 通过 SSH 在远程机器上 spawn Agent CLI,传入结构化 prompt,收集输出
|
||||
- **保留 HTTP API**:供外部 Agent(OpenClaw/Jeeves、Hermes 等)自主接入
|
||||
- Task 模型新增 `execution_mode` 字段:`ssh_cli` | `http_pull`
|
||||
- Orchestrator 根据执行模式选择调度策略:
|
||||
- `ssh_cli`:Orchestrator 主动 spawn → 等待完成 → 解析输出 → 生成 receipt
|
||||
- `http_pull`:Agent 自主 dequeue → 自行执行 → 提交 receipt
|
||||
- 新增 **SSH host 配置**:每台远程机器的 SSH 连接信息
|
||||
- 新增 **CLI 命令模板**:每种 Agent 类型的 CLI 调用模板(claude、codex、opencode)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `ssh-cli-execution`: Orchestrator 通过 SSH + CLI 在远程机器上执行 Agent,是 subprocess 的跨机等价
|
||||
- `host-management`: 远程主机管理(SSH 连接、Agent CLI 可用性检查)
|
||||
|
||||
### Modified Capabilities
|
||||
- `task-assignment-protocol`: 补充双执行模式——`ssh_cli`(Orchestrator 主动调度)和 `http_pull`(Agent 自主拉取)
|
||||
- `agent-adapter`: Adapter 定义为 CLI 命令模板 + 输出解析器,不再是 trait 或 protocol 描述
|
||||
- `notification-via-forgejo`: 通知机制在两种模式下都适用(SSH CLI 模式的 Agent 也会创建 PR)
|
||||
|
||||
## Impact
|
||||
|
||||
- **代码**:新增 `src/execution/` 模块(SSH executor + CLI template + output parser)
|
||||
- **配置**:新增 `[hosts]` section(远程机器 SSH 信息)和 agent CLI 模板
|
||||
- **Task 模型**:新增 `execution_mode` 字段
|
||||
- **依赖**:新增 `ssh2` 或 `tokio-process` + SSH 相关 crate
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Agent adapter as CLI template
|
||||
每个 Agent 类型 SHALL 定义为 CLI 命令模板 + 输出解析器,而非代码 trait。Orchestrator 根据模板构造命令、通过 SSH 执行、解析输出。
|
||||
|
||||
#### Scenario: Codex CLI adapter definition
|
||||
- **WHEN** Agent 类型为 `codex-cli`
|
||||
- **THEN** adapter 定义 SHALL 包含:
|
||||
- `cli_template`: `codex exec --json '{prompt}'`
|
||||
- `work_dir`: `{repo_path}`
|
||||
- `output_format`: `json`
|
||||
- `timeout`: 3600
|
||||
- `output_parser`: codex_json_parser
|
||||
|
||||
#### Scenario: Claude Code adapter definition
|
||||
- **WHEN** Agent 类型为 `claude-code`
|
||||
- **THEN** adapter 定义 SHALL 包含:
|
||||
- `cli_template`: `claude -p '{prompt}' --output-format json --dangerously-skip-permissions`
|
||||
- `work_dir`: `{repo_path}`
|
||||
- `output_format`: `json`
|
||||
- `timeout`: 3600
|
||||
- `output_parser`: claude_json_parser
|
||||
|
||||
#### Scenario: Custom adapter with custom template
|
||||
- **WHEN** 用户定义新的 Agent 类型
|
||||
- **THEN** SHALL 能通过配置文件指定 CLI 模板和输出格式
|
||||
|
||||
#### Scenario: http_pull mode — no adapter needed
|
||||
- **WHEN** Agent 使用 http_pull 模式(如 OpenClaw/Jeeves)
|
||||
- **THEN** 不需要 CLI adapter 定义,Agent 通过 HTTP API 自行交互
|
||||
|
||||
### Requirement: Adapter configuration
|
||||
Agent 实例配置 SHALL 关联到具体主机,包含连接信息和执行参数。
|
||||
|
||||
#### Scenario: Remote Codex on host-worker-02
|
||||
- **WHEN** 配置 host-worker-02 上的 Codex
|
||||
- **THEN** 配置 SHALL 包含:`{host: "host-worker-02", agent_type: "codex-cli", max_concurrency: 2, model: "gpt-5.5", capabilities: ["code:rust"]}`
|
||||
|
||||
#### Scenario: Local Codex on same machine
|
||||
- **WHEN** Agent 运行在 Orchestrator 同一台机器
|
||||
- **THEN** SSH 可替换为本地 subprocess,无需 SSH 开销
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Remote host configuration
|
||||
Orchestrator SHALL 支持配置多台远程主机,每台主机包含 SSH 连接信息和可用 Agent 列表。
|
||||
|
||||
#### Scenario: Host configuration format
|
||||
- **WHEN** 配置远程主机
|
||||
- **THEN** 配置 SHALL 包含:`{host_id, hostname, ssh_user, ssh_port, ssh_key_path, work_dir, agents: [{agent_type, max_concurrency}]}`
|
||||
|
||||
#### Scenario: Host with multiple agents
|
||||
- **WHEN** 一台主机配置了多个 Agent(例如同时有 Codex 和 Claude Code)
|
||||
- **THEN** Orchestrator SHALL 跟踪每个 Agent 的并发数,不超过 max_concurrency
|
||||
|
||||
### Requirement: Host health check
|
||||
Orchestrator SHALL 能检查远程主机的 SSH 连通性和 Agent CLI 可用性。
|
||||
|
||||
#### Scenario: SSH connectivity check
|
||||
- **WHEN** Orchestrator 检查 host-worker-02
|
||||
- **THEN** SHALL 尝试 SSH 连接并执行 `echo ok`
|
||||
- **AND** 连接失败时标记主机为 `unreachable`
|
||||
|
||||
#### Scenario: Agent CLI availability check
|
||||
- **WHEN** Orchestrator 检查 host-worker-02 上的 Codex
|
||||
- **THEN** SHALL 执行 `which codex` 或 `codex --version`
|
||||
- **AND** CLI 不存在时标记该 Agent 为 `unavailable`
|
||||
|
||||
### Requirement: Host selection for task assignment
|
||||
当任务的执行模式为 `ssh_cli` 时,Orchestrator SHALL 选择合适的主机执行。
|
||||
|
||||
#### Scenario: Select host by capability and availability
|
||||
- **WHEN** 任务需要 `code:rust` 能力
|
||||
- **THEN** SHALL 选择配置了对应 Agent 且当前并发数未满的主机
|
||||
- **AND** 多个候选主机时优先选择负载最低的
|
||||
|
||||
#### Scenario: No available host
|
||||
- **WHEN** 没有可用主机匹配任务需求
|
||||
- **THEN** 任务保持 `created` 状态,等待主机可用
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Git branch as task execution unit
|
||||
每个任务 SHALL 关联一个 Git 分支,无论哪种执行模式。Agent 在该分支上工作,通过 PR 提交结果。
|
||||
|
||||
#### Scenario: ssh_cli mode — Orchestrator creates branch
|
||||
- **WHEN** ssh_cli 模式任务开始执行
|
||||
- **THEN** Orchestrator SHALL 在目标仓库创建分支 `task/{task_id}`(通过 SSH 在远程主机执行 `git checkout -b`)
|
||||
- **AND** 将分支名传入 Agent prompt
|
||||
|
||||
#### Scenario: http_pull mode — Agent creates branch
|
||||
- **WHEN** http_pull 模式任务被 Agent 领取
|
||||
- **THEN** Agent SHALL 自行创建分支 `task/{task_id}`
|
||||
|
||||
### Requirement: PR webhook as completion notification
|
||||
无论哪种执行模式,Agent 完成任务后 SHALL 通过 Forgejo PR 触发状态更新。
|
||||
|
||||
#### Scenario: ssh_cli mode — Agent creates PR via CLI
|
||||
- **WHEN** ssh_cli 模式的 Agent 执行完成
|
||||
- **THEN** Agent(或 Orchestrator 通过 SSH) SHALL push 到 task 分支并创建 PR
|
||||
- **AND** Forgejo PR webhook 触发状态更新
|
||||
|
||||
#### Scenario: http_pull mode — same flow
|
||||
- **WHEN** http_pull 模式的 Agent 执行完成
|
||||
- **THEN** Agent SHALL push 到 task 分支并创建 PR
|
||||
- **AND** Forgejo PR webhook 触发状态更新(与 ssh_cli 模式相同)
|
||||
|
||||
### Requirement: Push events as progress tracking
|
||||
无论哪种执行模式,Forgejo push webhook SHALL 作为进度信号。
|
||||
|
||||
#### Scenario: ssh_cli mode — push detected
|
||||
- **WHEN** ssh_cli 模式执行中,Agent push 到 task 分支
|
||||
- **THEN** Orchestrator 更新 `last_activity_at`(与 http_pull 模式相同)
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: SSH CLI execution mode
|
||||
Orchestrator SHALL 支持通过 SSH 在远程主机上执行 Agent CLI 命令。这是 subprocess CLI 的跨机等价:Orchestrator 构造上下文、主动启动 Agent、等待完成、收集输出。
|
||||
|
||||
#### Scenario: Execute Codex on remote host
|
||||
- **WHEN** 任务分配给 host-worker-02 上的 Codex Agent
|
||||
- **THEN** Orchestrator SHALL 通过 SSH 连接 host-worker-02
|
||||
- **AND** 执行 `codex exec --json '{structured_prompt}'`
|
||||
- **AND** 等待命令完成,解析 JSON 输出为 receipt
|
||||
|
||||
#### Scenario: Execute Claude Code on remote host
|
||||
- **WHEN** 任务分配给 host-worker-03 上的 Claude Code Agent
|
||||
- **THEN** Orchestrator SHALL 通过 SSH 连接 host-worker-03
|
||||
- **AND** 执行 `claude -p '{structured_prompt}' --output-format json --dangerously-skip-permissions`
|
||||
- **AND** 等待命令完成,解析 JSON 输出为 receipt
|
||||
|
||||
#### Scenario: SSH connection fails
|
||||
- **WHEN** Orchestrator 无法 SSH 连接到目标主机
|
||||
- **THEN** 任务 SHALL 标记为 `failed`,记录连接错误
|
||||
- **AND** 如果 retry_count < max_retries, SHALL 自动重试
|
||||
|
||||
#### Scenario: Agent CLI returns non-zero exit code
|
||||
- **WHEN** 远程 CLI 命令返回非零退出码
|
||||
- **THEN** 任务 SHALL 标记为 `failed`,记录 stderr 输出
|
||||
|
||||
### Requirement: Structured prompt construction
|
||||
Orchestrator SHALL 为每个任务构造结构化 prompt,通过 CLI 参数传入 Agent。Prompt 内容包括:任务目标、约束条件、影响文件范围、验证命令。
|
||||
|
||||
#### Scenario: Prompt for code task
|
||||
- **WHEN** 任务为代码实现类型
|
||||
- **THEN** prompt SHALL 包含:Issue 标题和描述、任务约束(从 Issue labels 提取)、预期输出格式、验证命令(`cargo test` / `npm test`)
|
||||
|
||||
#### Scenario: Prompt for review task
|
||||
- **WHEN** 任务为代码审查类型
|
||||
- **THEN** prompt SHALL 包含:PR diff、审查要点、审查结果格式要求
|
||||
|
||||
### Requirement: CLI command templates
|
||||
每种 Agent 类型 SHALL 有可配置的 CLI 命令模板。模板支持变量替换:`{prompt}`、`{work_dir}`、`{task_id}`、`{branch}`。
|
||||
|
||||
#### Scenario: Codex CLI template
|
||||
- **WHEN** Agent 类型为 `codex-cli`
|
||||
- **THEN** 命令模板 SHALL 为 `codex exec --json '{prompt}'`
|
||||
- **AND** 在 `{work_dir}` 目录下执行
|
||||
|
||||
#### Scenario: Custom template
|
||||
- **WHEN** 用户配置自定义 CLI 模板
|
||||
- **THEN** SHALL 支持变量替换:`{prompt}`、`{work_dir}`、`{task_id}`、`{branch}`
|
||||
|
||||
### Requirement: Output parsing
|
||||
Orchestrator SHALL 解析 Agent CLI 的 JSON 输出为 receipt。
|
||||
|
||||
#### Scenario: Parse Codex JSON output
|
||||
- **WHEN** Codex CLI 输出 JSON
|
||||
- **THEN** SHALL 提取:status(completed/failed)、summary、artifacts(changed files、PR URL)、duration
|
||||
|
||||
#### Scenario: Parse Claude Code JSON output
|
||||
- **WHEN** Claude Code CLI 输出 JSON
|
||||
- **THEN** SHALL 提取:status、summary、artifacts、duration
|
||||
|
||||
#### Scenario: Malformed output
|
||||
- **WHEN** Agent 输出无法解析为有效 JSON
|
||||
- **THEN** 任务 SHALL 标记为 `failed`,记录原始输出
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Dual execution mode
|
||||
Orchestrator SHALL 支持两种任务执行模式:
|
||||
|
||||
1. **ssh_cli**:Orchestrator 主动通过 SSH 在远程主机执行 Agent CLI。控制权在 Orchestrator,上下文由 Orchestrator 构造并传入。
|
||||
2. **http_pull**:Agent 自主通过 HTTP API 拉取任务、执行、提交 receipt。控制权在 Agent,适用于外部 Agent(OpenClaw/Jeeves、Hermes 等)。
|
||||
|
||||
每个任务 SHALL 有 `execution_mode` 字段,由任务来源决定(默认 `ssh_cli`)。
|
||||
|
||||
#### Scenario: Forgejo Issue → ssh_cli task
|
||||
- **WHEN** 任务从 Forgejo Issue 创建
|
||||
- **THEN** execution_mode SHALL 为 `ssh_cli`
|
||||
- **AND** Orchestrator SHALL 选择主机并主动执行
|
||||
|
||||
#### Scenario: External Agent using http_pull
|
||||
- **WHEN** 外部 Agent(Jeeves)需要执行任务
|
||||
- **THEN** 任务 execution_mode SHALL 为 `http_pull`
|
||||
- **AND** Agent 通过 `POST /api/v1/tasks/dequeue` 拉取并自行执行
|
||||
|
||||
### Requirement: Agent task dequeue (pull model — for http_pull mode only)
|
||||
`POST /api/v1/tasks/dequeue` SHALL 仅适用于 `http_pull` 模式的任务。`ssh_cli` 模式的任务 SHALL 由 Orchestrator 直接调度,不经过 dequeue。
|
||||
|
||||
#### Scenario: Agent dequeues an http_pull task
|
||||
- **WHEN** Agent 发送 `POST /api/v1/tasks/dequeue`
|
||||
- **THEN** SHALL 仅返回 execution_mode = `http_pull` 的任务
|
||||
- **AND** 排除已被 Orchestrator 调度的 ssh_cli 任务
|
||||
|
||||
### Requirement: Agent task status update (for http_pull mode)
|
||||
`POST /api/v1/tasks/{task_id}/status` SHALL 适用于 `http_pull` 模式。`ssh_cli` 模式的状态 SHALL 由 Orchestrator 直接管理。
|
||||
|
||||
#### Scenario: ssh_cli task status managed by Orchestrator
|
||||
- **WHEN** 任务 execution_mode = `ssh_cli`
|
||||
- **THEN** 状态变更 SHALL 由 Orchestrator 在 SSH 执行过程中自动更新
|
||||
- **AND** Agent 不需要调用 status update API
|
||||
|
||||
### Requirement: Single task detail query
|
||||
Orchestrator SHALL 提供 `GET /api/v1/tasks/{task_id}` 返回单个任务详情,两种执行模式通用。
|
||||
|
||||
#### Scenario: Query task detail
|
||||
- **WHEN** 发送 `GET /api/v1/tasks/org/repo#42`
|
||||
- **THEN** 返回任务完整信息 JSON,包含 execution_mode、assigned_host(ssh_cli)或 assigned_agent_id(http_pull)
|
||||
|
||||
### Requirement: Agent authentication (http_pull mode)
|
||||
`http_pull` 模式的 Agent 调用 API 时 SHALL 携带 token。`ssh_cli` 模式不需要 Agent 认证(由 Orchestrator 直接管理)。
|
||||
|
||||
#### Scenario: ssh_cli mode — no Agent auth needed
|
||||
- **WHEN** 任务 execution_mode = `ssh_cli`
|
||||
- **THEN** Agent 不参与 API 调用,无需认证
|
||||
|
||||
#### Scenario: http_pull mode — token required
|
||||
- **WHEN** 任务 execution_mode = `http_pull`
|
||||
- **THEN** Agent SHALL 携带有效 token 调用 API
|
||||
|
||||
### Requirement: Non-PR task completion endpoint
|
||||
对于不产生 PR 的任务(research、review 等),无论哪种执行模式,都 SHALL 可通过 `POST /api/v1/tasks/{task_id}/complete` 显式完成。
|
||||
|
||||
#### Scenario: ssh_cli mode — auto-complete from CLI output
|
||||
- **WHEN** 任务 execution_mode = `ssh_cli` 且 Agent CLI 输出包含成功 receipt
|
||||
- **THEN** Orchestrator SHALL 自动解析输出并完成任务
|
||||
|
||||
#### Scenario: http_pull mode — Agent calls complete
|
||||
- **WHEN** 任务 execution_mode = `http_pull`
|
||||
- **THEN** Agent SHALL 调用 `POST /api/v1/tasks/{task_id}/complete` + receipt
|
||||
|
||||
### Requirement: Review loop limit
|
||||
无论哪种执行模式,任务的 review 循环 SHALL 有最大次数限制。
|
||||
|
||||
#### Scenario: ssh_cli review loop
|
||||
- **WHEN** ssh_cli 模式下 review 不通过
|
||||
- **THEN** Orchestrator SHALL 自动重新调度 Agent 修复
|
||||
- **AND** 超过 max_retries 时标记 failed
|
||||
62
openspec/changes/dual-execution-model/tasks.md
Normal file
62
openspec/changes/dual-execution-model/tasks.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
## 1. 数据模型扩展
|
||||
|
||||
- [ ] 1.1 Task 模型新增 `execution_mode` 字段(`ssh_cli` | `http_pull`,默认 `ssh_cli`)
|
||||
- [ ] 1.2 Task 模型新增 `assigned_host` 字段(ssh_cli 模式下的目标主机 ID)
|
||||
- [ ] 1.3 Task 模型新增 `branch_name`、`pr_title`、`last_activity_at`、`review_count` 字段
|
||||
- [ ] 1.4 TaskStatus 新增 `review_pending` 状态
|
||||
|
||||
## 2. 主机管理
|
||||
|
||||
- [ ] 2.1 新增 `HostConfig` struct(host_id, hostname, ssh_user, ssh_port, ssh_key_path, agents)
|
||||
- [ ] 2.2 `config.toml` 新增 `[[hosts]]` section
|
||||
- [ ] 2.3 实现 SSH 连通性检查(`ssh {host} echo ok`)
|
||||
- [ ] 2.4 实现 Agent CLI 可用性检查(`ssh {host} which codex`)
|
||||
|
||||
## 3. SSH CLI 执行器
|
||||
|
||||
- [ ] 3.1 创建 `src/execution/mod.rs` 模块
|
||||
- [ ] 3.2 实现 `SshExecutor`:通过 SSH 执行远程 CLI 命令,处理超时和错误
|
||||
- [ ] 3.3 实现 `CliTemplate`:命令模板 + 变量替换(`{prompt}`, `{work_dir}`, `{task_id}`, `{branch}`)
|
||||
- [ ] 3.4 实现结构化 prompt 构造:Issue 内容 → 结构化 prompt(目标、约束、文件范围、验证命令)
|
||||
- [ ] 3.5 实现 output parser:解析 Codex JSON 输出 → Receipt
|
||||
- [ ] 3.6 实现 output parser:解析 Claude Code JSON 输出 → Receipt
|
||||
- [ ] 3.7 支持本地 subprocess 作为 SSH 的特例(hostname = localhost 时)
|
||||
|
||||
## 4. 调度循环
|
||||
|
||||
- [ ] 4.1 实现 dispatch loop:扫描 created 状态的 ssh_cli 任务 → 选择主机 → SSH 执行 → 更新状态
|
||||
- [ ] 4.2 主机选择逻辑:按能力匹配 + 并发数限制 + 负载最低优先
|
||||
- [ ] 4.3 执行结果处理:成功 → assigned → running → review_pending/completed;失败 → failed + retry
|
||||
- [ ] 4.4 Review 循环:review_pending + PR feedback → 重新调度 → 检查 review_count ≤ max_retries
|
||||
|
||||
## 5. HTTP API 调整
|
||||
|
||||
- [ ] 5.1 `POST /api/v1/tasks/dequeue` 仅返回 execution_mode = `http_pull` 的任务
|
||||
- [ ] 5.2 `POST /api/v1/tasks/{task_id}/status` 仅 http_pull 模式可用
|
||||
- [ ] 5.3 `GET /api/v1/tasks/{task_id}` 返回 execution_mode 和 assigned_host
|
||||
- [ ] 5.4 `POST /api/v1/tasks/{task_id}/complete` 两种模式通用
|
||||
- [ ] 5.5 Token 认证中间件(仅 http_pull 模式的 API 需要)
|
||||
|
||||
## 6. Adapter 模块重写
|
||||
|
||||
- [ ] 6.1 重写 `src/adapters/mod.rs`:移除 `AgentAdapter` trait 和 `AdapterRunner`
|
||||
- [ ] 6.2 保留 `AdapterKind`,新增 `CliAdapterConfig`(cli_template, output_format, timeout, output_parser)
|
||||
- [ ] 6.3 内置 Codex 和 Claude Code 的默认 CLI 模板
|
||||
|
||||
## 7. Forgejo webhook 扩展
|
||||
|
||||
- [ ] 7.1 支持 `pull_request` 事件(opened → review_pending, merged → completed + auto receipt)
|
||||
- [ ] 7.2 支持 `push` 事件(task/* 分支 → last_activity_at 更新)
|
||||
|
||||
## 8. 测试与验证
|
||||
|
||||
- [ ] 8.1 `cargo check` 通过
|
||||
- [ ] 8.2 `cargo test` 全部通过
|
||||
- [ ] 8.3 SSH executor 测试(mock SSH 或本地 localhost 测试)
|
||||
- [ ] 8.4 CLI template 变量替换测试
|
||||
- [ ] 8.5 Output parser 测试(Codex JSON、Claude Code JSON、malformed)
|
||||
- [ ] 8.6 Prompt 构造测试
|
||||
- [ ] 8.7 主机选择逻辑测试
|
||||
- [ ] 8.8 Dispatch loop 测试(ssh_cli 调度流程、http_pull 排除)
|
||||
- [ ] 8.9 Review 循环 limit 测试
|
||||
- [ ] 8.10 Forgejo PR/push webhook 测试
|
||||
Loading…
Add table
Add a link
Reference in a new issue