Skip to main content

made_api/
ceremony_engine_api.rs

1use crate::{
2    ApiCapabilities, ApiError, BudgetLimits, BudgetReport, BudgetReservation, CeremonySummary,
3    DefinitionAnalysisView, PublishedDefinitionView, RaiseInterventionRequest,
4    RespondToInterventionRequest, StartCeremonyRequest,
5};
6
7/// What a consuming product may ask of the embedded engine.
8///
9/// Reads, plus one mutation: starting an instance from a **published**
10/// definition. Advancing and publishing stay behind the engine's own surfaces,
11/// where their transactionality and audit live; the contract grows by adding
12/// named capabilities, never by widening what an existing one means (ADR-004).
13/// A consumer checks the capability report before relying on any of this — and
14/// keeps working, with a stub, when the engine is absent.
15#[async_trait::async_trait]
16pub trait CeremonyEngineApi: Send + Sync {
17    /// What this implementation is and what it can do. Checked by consumers at
18    /// startup, before anything is at stake.
19    fn capabilities(&self) -> ApiCapabilities;
20
21    /// Every ceremony instance the engine holds.
22    ///
23    /// The consumer filters by its own context keys; the engine does not know
24    /// what they mean and is not asked to.
25    async fn ceremonies(&self) -> Result<Vec<CeremonySummary>, ApiError>;
26
27    /// One ceremony instance by identity.
28    async fn ceremony(&self, ceremony_id: &str) -> Result<CeremonySummary, ApiError>;
29
30    /// Start an instance from a published definition. Capability
31    /// `start_ceremony`.
32    ///
33    /// `CeremonyNotFound` when nothing is published under that name and
34    /// version — publishing is the remedy, not retrying. A taken instance
35    /// identity is `Refused`: an identity is one instance forever, and the
36    /// answer is a new identity, never a restart of someone else's.
37    async fn start_ceremony(
38        &self,
39        request: StartCeremonyRequest,
40    ) -> Result<CeremonySummary, ApiError>;
41
42    /// Start a published ceremony and open one durable budget account shared by its descendants.
43    async fn start_budgeted_ceremony(
44        &self,
45        request: StartCeremonyRequest,
46        limits: BudgetLimits,
47    ) -> Result<CeremonySummary, ApiError>;
48
49    /// Current durable budget balance for a ceremony tree.
50    async fn budget_report(&self, ceremony_id: &str) -> Result<BudgetReport, ApiError>;
51
52    /// Bounded global recovery view of reservations awaiting a terminal receipt.
53    async fn pending_budget_reservations(
54        &self,
55        after_reservation_id: Option<&str>,
56        limit: usize,
57    ) -> Result<Vec<BudgetReservation>, ApiError>;
58
59    /// Put a question, investigation or proposed action to the table.
60    /// Capability `raise_intervention`.
61    async fn raise_intervention(
62        &self,
63        request: RaiseInterventionRequest,
64    ) -> Result<CeremonySummary, ApiError>;
65
66    /// Answer an open intervention. Capability `respond_to_intervention`.
67    ///
68    /// A closed intervention refuses: the answer arrived after the table moved
69    /// on, and recording it as if it had been heard would misstate the
70    /// conversation the audit trail exists to keep.
71    async fn respond_to_intervention(
72        &self,
73        request: RespondToInterventionRequest,
74    ) -> Result<CeremonySummary, ApiError>;
75
76    /// Analyze a definition draft, reporting every defect at once.
77    /// Capability `analyze_definition`.
78    ///
79    /// A draft that does not even parse is `Refused` — it is not a defective
80    /// definition, it is not a definition. Anything that parses gets the full
81    /// report, publishable or not.
82    async fn analyze_definition(
83        &self,
84        definition_yaml: &str,
85    ) -> Result<DefinitionAnalysisView, ApiError>;
86
87    /// Publish a definition, immutably. Capability `publish_definition`.
88    ///
89    /// Idempotent on identical content: republishing the same bytes under the
90    /// same name and version answers `already_published` rather than refusing,
91    /// which is what makes a retry safe. A version taken by *different*
92    /// content is `Refused` — a published version is immutable, and the
93    /// answer is a new version, never an overwrite.
94    async fn publish_definition(
95        &self,
96        definition_yaml: &str,
97    ) -> Result<PublishedDefinitionView, ApiError>;
98}