Skip to main content

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}