substrate_core/cloud_dispatch_port.rs
1//! CloudDispatchPort — submit remote cloud-agent tasks and harvest PR results.
2//!
3//! Core defines the port contract and value types; `cloud-*` adapter crates
4//! implement it against Cursor Cloud Agents, Kilo gateway-backed dispatch, etc.
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8
9use crate::error::Result;
10
11/// Opaque handle returned by [`CloudDispatchPort::submit_task`].
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct CloudTaskHandle {
14 /// Adapter-local task identifier (stable for poll/harvest).
15 pub id: String,
16}
17
18/// Observed lifecycle state of a cloud-dispatched task.
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub enum CloudTaskStatus {
21 /// Task accepted but not yet executing.
22 Queued,
23 /// Remote agent is actively working.
24 Running,
25 /// Task completed successfully; [`CloudDispatchPort::harvest`] may be called.
26 Succeeded,
27 /// Task failed; optional adapter message for diagnostics.
28 Failed {
29 /// Human-readable failure reason when available.
30 message: Option<String>,
31 },
32}
33
34/// Normalized harvest payload after a successful cloud run.
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct CloudResult {
37 /// Pull-request URL when the adapter opened one.
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub pr_url: Option<String>,
40 /// Branch containing the agent's work.
41 pub branch: String,
42 /// Short summary of changes (commit message, agent reply, or diff stat).
43 pub diff_summary: String,
44}
45
46/// Outbound port for harness-agnostic cloud agent dispatch.
47///
48/// Lifecycle: [`submit_task`](CloudDispatchPort::submit_task) enqueues work and
49/// returns a handle; [`poll_status`](CloudDispatchPort::poll_status) observes
50/// progress; [`harvest`](CloudDispatchPort::harvest) collects the PR outcome
51/// once status is [`CloudTaskStatus::Succeeded`].
52#[async_trait]
53pub trait CloudDispatchPort: Send + Sync {
54 /// Submit a remote task against `repo` on base ref `branch` with `prompt`.
55 async fn submit_task(&self, repo: &str, branch: &str, prompt: &str) -> Result<CloudTaskHandle>;
56
57 /// Poll the current status for `handle`.
58 async fn poll_status(&self, handle: &CloudTaskHandle) -> Result<CloudTaskStatus>;
59
60 /// Harvest the normalized result for a succeeded task.
61 async fn harvest(&self, handle: &CloudTaskHandle) -> Result<CloudResult>;
62}