Skip to main content

lifeloop/router/
seams.rs

1//! Trait seams for follow-up router stages.
2//!
3//! The skeleton router (issue #7) defines these traits but does **not**
4//! implement them. They name the integration shape future issues will
5//! plug into:
6//!
7//! * [`NegotiationStrategy`] — capability negotiation against an
8//!   [`AdapterManifest`] before dispatch.
9//! * [`CallbackInvoker`] — invoke a client callback for a routed
10//!   event. Issue #3 (parallel branch, protocol renderers) will
11//!   provide a renderer-backed implementation; the trait method
12//!   signature is shaped so a renderer can plug in cleanly without
13//!   this module growing a `protocol` dependency.
14//! * [`ReceiptEmitter`] — persist or forward the
15//!   [`crate::LifecycleReceipt`] produced by a dispatch.
16//! * [`FailureMapper`] — turn a [`super::RouteError`] (or a
17//!   downstream stage's error) into the [`crate::FailureClass`] /
18//!   [`crate::RetryClass`] pair the receipt carries.
19//!
20//! Methods either return `todo!()` in the skeleton's tests or are
21//! left without default implementations — implementers in follow-up
22//! issues are expected to provide them.
23
24use crate::{
25    CallbackResponse, FailureClass, LifecycleReceipt, NegotiationOutcome, PayloadEnvelope,
26    RetryClass,
27};
28
29use super::plan::RoutingPlan;
30use super::validation::RouteError;
31
32/// Capability negotiation seam.
33///
34/// A future router issue will resolve `acceptable_placements` against
35/// the manifest's placement claims, identity-correlation requirements
36/// against `session_identity`, and so on. The skeleton defines the
37/// trait shape only.
38pub trait NegotiationStrategy {
39    /// Negotiate the routing plan against the adapter manifest it
40    /// already carries. Implementations decide whether the request
41    /// is satisfied, must be degraded, is unsupported, or requires
42    /// operator intervention.
43    fn negotiate(&self, plan: &RoutingPlan) -> NegotiationOutcome;
44}
45
46/// Callback invocation seam.
47///
48/// Issue #3 owns the protocol renderer side of this seam; the
49/// signature is shaped so a renderer-backed implementation can plug
50/// in without this module knowing anything about a `protocol`
51/// module.
52///
53/// The argument shape mirrors the renderer-input tuple coordinated
54/// with issue #3:
55/// `(lifecycle_event, adapter_id, adapter_version, integration_mode,
56///   frame_ctx, payload_envelope, placement_class, receipt_meta)`.
57/// All of those fields are reachable from a [`RoutingPlan`] plus the
58/// optional [`PayloadEnvelope`]s the caller is delivering, so the
59/// trait method takes those two and lets the implementation
60/// destructure.
61///
62/// The [`PayloadEnvelope::body`] is treated as opaque bytes/value —
63/// implementations must not parse it.
64pub trait CallbackInvoker {
65    /// Error produced by the callback transport. Kept generic so a
66    /// renderer-backed impl, an in-process impl, and a future
67    /// network impl can each carry their own diagnostic shape.
68    type Error;
69
70    /// Invoke the client callback for a routed event.
71    ///
72    /// `payloads` carries any [`PayloadEnvelope`]s the caller is
73    /// delivering alongside the event. The router does not inspect
74    /// payload bodies; the invoker may, but is not required to.
75    fn invoke(
76        &self,
77        plan: &RoutingPlan,
78        payloads: &[PayloadEnvelope],
79    ) -> Result<CallbackResponse, Self::Error>;
80}
81
82/// Receipt emission seam.
83///
84/// A future router issue will synthesize the
85/// [`crate::LifecycleReceipt`] and route it to a ledger or
86/// notification surface. The skeleton defines the trait shape only.
87pub trait ReceiptEmitter {
88    /// Error produced by the receipt sink.
89    type Error;
90
91    /// Emit (persist, forward, or otherwise hand off) a
92    /// [`LifecycleReceipt`].
93    fn emit(&self, receipt: &LifecycleReceipt) -> Result<(), Self::Error>;
94}
95
96/// Failure-class mapping seam.
97///
98/// A future router issue will map [`RouteError`] (and downstream
99/// stage errors) onto the
100/// [`FailureClass`] / [`RetryClass`] pair carried on a
101/// `status=failed` receipt. The skeleton defines the trait shape
102/// only.
103pub trait FailureMapper {
104    /// Map a [`RouteError`] to a `(failure_class, retry_class)`
105    /// pair. Implementations must be deterministic — the same
106    /// [`RouteError`] variant must always map to the same pair so
107    /// receipt ledgers replay consistently.
108    fn map_route_error(&self, err: &RouteError) -> (FailureClass, RetryClass);
109}