pub struct HandoffMode { /* private fields */ }Implementations§
Source§impl HandoffMode
impl HandoffMode
Sourcepub fn new(evaluator: Arc<dyn PolicyEvaluator>) -> HandoffMode
pub fn new(evaluator: Arc<dyn PolicyEvaluator>) -> HandoffMode
Construct the mode with an injected governance policy evaluator.
Trait Implementations§
Source§impl Mode for HandoffMode
impl Mode for HandoffMode
Source§fn validate_client_envelope(
&self,
session: &Session,
env: &Envelope,
) -> Result<(), MacpError>
fn validate_client_envelope( &self, session: &Session, env: &Envelope, ) -> Result<(), MacpError>
RFC-MACP-0010 §5.1(3): a client-submitted HandoffAccept carrying
implicit = true MUST be rejected, and the synthetic accept’s
message_id namespace is reserved.
Gated to semantics_rev >= 2 so rev <= 1 wire behavior is
byte-identical: a legacy session’s client could send an
implicit-accept:-prefixed id and be accepted, and its history must
stay replayable under the semantics it was accepted with.
Two rules, in this order:
- any
message_idin the reserved namespace ->InvalidEnvelope(checked first, and for every message type: the squat works throughSessionStart,CommitmentandHandoffContexttoo); HandoffAcceptwhose payload decodes withimplicit = true->InvalidPayload— the same codehandle_messagereturns for the same envelope today, so the rev-2 error surface does not shift.
Rule 2 is what the mode itself will stop being able to enforce once
the runtime synthesizes implicit accepts: at rev >= 2 a well-formed
implicit accept with the deterministic message_id is exactly what
dispatch must accept on replay, and the mode cannot tell client
provenance from runtime provenance. This boundary can.
Source§fn due_synthetic_envelope(
&self,
session: &Session,
now_ms: i64,
) -> Option<Envelope>
fn due_synthetic_envelope( &self, session: &Session, now_ms: i64, ) -> Option<Envelope>
RFC-MACP-0010 §5.1(2)-(3): the synthetic implicit HandoffAccept, due
once the outstanding offer’s implicit_accept_timeout_ms has elapsed in
unsuspended session time.
Gated to semantics_rev >= 2. Rev <= 1 sessions get None forever and
keep resolving through the interim in-Commitment path, so their
histories replay to the outcome they were accepted with.
§Decision vs. timestamp — two different functions, on purpose
The decision (“has the timeout elapsed?”) is the scalar
implicit_accept_elapsed_ms, which is exact for this purpose:
elapsed(T) >= timeout iff T >= D, because every pause banked since
the offer lies inside [offered_at, T].
Only the timestamp needs the interval walk
Session::unsuspended_deadline. §5.1(3) fixes it at “offer acceptance
time + timeout + suspended time within the window”, which is not
the naive offered_at + timeout + banked_since_offer: that form counts
pauses beginning after the deadline — fully reachable, since
SuspendSession/ResumeSession are RPCs and need no session-scoped
message — and it would make a permanently-recorded timestamp depend on
when it happened to be observed.
The two are consistent in the safe direction: an under-recorded
suspension_intervals vec (see unsuspended_deadline’s under-count
invariant) can only move D earlier, never later, so the
debug_assert!(D <= now_ms) below holds even on a legacy rev-2
snapshot.
§Cost
One mode_state decode per call for handoff sessions with a bound
timeout, and an allocation only when an envelope is actually due. A
cached “next deadline” flag on the session was considered and rejected
as premature: it would need a writer on the resume path, which is not
mode-dispatched at all.
fn on_session_start( &self, session: &Session, _env: &Envelope, ) -> Result<ModeResponse, MacpError>
fn on_message( &self, session: &Session, env: &Envelope, ) -> Result<ModeResponse, MacpError>
Source§fn on_message_at(
&self,
session: &Session,
env: &Envelope,
ctx: &MessageContext,
) -> Result<ModeResponse, MacpError>
fn on_message_at( &self, session: &Session, env: &Envelope, ctx: &MessageContext, ) -> Result<ModeResponse, MacpError>
on_message plus the runtime’s
macp_core::mode::MessageContext (acceptance clock). Defaulted to plain
on_message so most modes ignore it; modes that need a trustworthy
time source (Handoff) override this instead of reading the forgeable
Envelope.timestamp_unix_ms. The runtime and replay always call this,
with the same clock value that the log entry records.Auto Trait Implementations§
impl !RefUnwindSafe for HandoffMode
impl !UnwindSafe for HandoffMode
impl Freeze for HandoffMode
impl Send for HandoffMode
impl Sync for HandoffMode
impl Unpin for HandoffMode
impl UnsafeUnpin for HandoffMode
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request