lifeloop/router/mod.rs
1//! Lifecycle router skeleton: pre-dispatch validation and adapter resolution.
2//!
3//! Lifeloop owns lifecycle event routing as a neutral mechanism. Clients
4//! own product behavior: a client receives the routed event via a
5//! callback and decides what (if anything) to deliver. The router is the
6//! seam that turns a `CallbackRequest` plus the adapter manifest registry
7//! into a typed [`RoutingPlan`] that downstream stages — capability
8//! negotiation, callback invocation, receipt emission, failure mapping —
9//! consume without re-validating wire-level invariants.
10//!
11//! # Boundary (issue #7, skeleton slice)
12//!
13//! This module owns:
14//! * pre-dispatch validation of [`crate::CallbackRequest`] envelopes
15//! (schema version, non-empty identifiers, frame-context invariants,
16//! payload-ref structure);
17//! * adapter manifest resolution by both `adapter_id` *and*
18//! `adapter_version` (the two are distinguished failure classes —
19//! wrong id is not the same as wrong version of a known id);
20//! * synthesis of a typed [`RoutingPlan`] that names the resolved
21//! adapter and preserves opaque `PayloadRef`s for downstream stages;
22//! * the trait *seams* through which later issues plug in negotiation,
23//! callback invocation, receipt emission, and failure mapping.
24//!
25//! This module does **not** own:
26//! * capability negotiation — see [`negotiate`] / [`NegotiatedPlan`];
27//! * callback invocation against renderers (issue #3 will wire a
28//! protocol renderer into [`CallbackInvoker`]) — see
29//! [`CallbackInvoker`];
30//! * receipt persistence/emission (future router issue) — see
31//! [`ReceiptEmitter`];
32//! * failure-class mapping for receipt synthesis (future router
33//! issue) — see [`FailureMapper`];
34//! * payload body inspection. The router treats
35//! [`crate::PayloadRef`] and [`crate::PayloadEnvelope`] bodies as opaque: it does
36//! not parse, transform, or pattern-match on payload contents.
37//!
38//! # Client compatibility
39//!
40//! This module has none. It is provider-neutral by construction. Any
41//! historic host-adapter flow that fused lifecycle routing with
42//! client product semantics is replaced by Lifeloop routing plus
43//! client callbacks — every client (including the first one) becomes
44//! a consumer of these seams, not a peer of the router.
45
46mod callbacks;
47mod failure_mapping;
48mod negotiation;
49mod plan;
50mod receipts;
51mod seams;
52mod subprocess;
53mod validation;
54
55pub use callbacks::{ClientCallback, DefaultCallbackInvoker, FnClientCallback, synthesize_request};
56pub use failure_mapping::{
57 LifeloopFailureMapper, TransportError, classes_for_negotiation_outcome,
58 failure_class_for_receipt_error, failure_class_for_route_error, failure_class_for_transport,
59 retry_class_for, validate_receipt_eligible,
60};
61pub use negotiation::{
62 CapabilityKind, CapabilityRequest, CapabilityRequirement, DefaultNegotiationStrategy,
63 NegotiatedPlan, PayloadPlacementDecision, PlacementRejection, negotiate,
64};
65pub use plan::{RoutingPlan, route};
66pub use receipts::{
67 IdempotencyKey, IdempotencyStore, InMemoryIdempotencyStore, LifeloopReceiptEmitter,
68 ReceiptContext, ReceiptError, SequenceGenerator, StoreOutcome,
69};
70pub use seams::{CallbackInvoker, FailureMapper, ReceiptEmitter};
71pub use subprocess::{
72 SubprocessCallbackInvoker, SubprocessInvokerConfig, SubprocessInvokerError,
73 failure_class_for_subprocess_error, transport_error_for,
74};
75pub use validation::{AdapterRegistry, AdapterResolution, BuiltinAdapterRegistry, RouteError};