Skip to main content

made_api/
ceremony_engine_api.rs

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