made_core/ports/executor.rs
1//! [`ExecutorPort`] — opaque execution of a winning proposal.
2//!
3//! After a deliberation produces a winner, the `Orchestrate` use case
4//! optionally hands the proposal off to an executor (e.g. the Underpass
5//! Runtime via gRPC, a local subprocess, a human-in-the-loop queue,
6//! …). MADE does not know what execution means beyond
7//! "adapter runs it and reports an outcome".
8
9use async_trait::async_trait;
10use serde::{Deserialize, Serialize};
11
12use crate::entities::Proposal;
13use crate::error::DomainError;
14use crate::value_objects::{Attributes, DurationMs};
15
16/// Result returned by an executor. Opaque `output` carried back for
17/// the caller to surface.
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub struct ExecutionOutcome {
20 pub execution_id: String,
21 pub succeeded: bool,
22 pub duration: DurationMs,
23 pub output: Attributes,
24}
25
26#[async_trait]
27pub trait ExecutorPort: Send + Sync {
28 /// Execute the winning proposal. Adapters are free to block or
29 /// stream, as long as the final outcome is returned.
30 async fn execute(
31 &self,
32 winner: &Proposal,
33 options: &Attributes,
34 ) -> Result<ExecutionOutcome, DomainError>;
35}