pub enum EventBody {
SessionOpened {
info: SessionInfo,
},
SessionClosed {
session: String,
},
ClaimAcquired {
claim: String,
session: String,
scope: Vec<String>,
intent: Option<String>,
mode: ClaimMode,
issue: Option<String>,
},
ClaimReleased {
claim: String,
},
HandoverPublished {
handover: HandoverBody,
},
HandoverConsumed {
handover: String,
session: String,
},
ReviewRequested {
review: String,
session: String,
subject: String,
scope: Vec<String>,
issue: Option<String>,
reviewer: Option<String>,
body: Option<String>,
},
ReviewAnswered {
request: String,
session: String,
verdict: String,
note: Option<String>,
},
ReviewWithdrawn {
request: String,
session: String,
note: Option<String>,
},
}Expand description
What an event says. One vocabulary for every runtime; an event of a kind this executable does not know is stored and relayed but not interpreted, so a newer peer never breaks an older one’s replication.
The vocabulary is small on purpose: everything the mesh coordinates is a session, a
claim, a handover or a review, and an event that would need a ninth kind is a feature
that has not been agreed. The kind tag is what goes on the wire, so it is protocol —
two executables of different ages read the same word for the same event.
use majordomus_cli::mesh::journal::{EventBody, SessionInfo, KNOWN_KINDS};
let opened = EventBody::SessionOpened { info: SessionInfo::named("s1", "cli") };
assert_eq!(opened.kind(), "session_opened");
assert!(KNOWN_KINDS.contains(&opened.kind()));
assert_eq!(serde_json::to_value(&opened).unwrap()["kind"], "session_opened");Variants§
SessionOpened
A session began, or said something new about itself (the latest wins).
Fields
info: SessionInfoThe session.
SessionClosed
A session ended. Its claims end with it.
ClaimAcquired
A session claimed a scope.
Fields
ClaimReleased
A claim was released by its holder.
HandoverPublished
A handover was published for other runtimes to consume.
Fields
handover: HandoverBodyThe handover.
HandoverConsumed
A session consumed a handover (on any runtime).
Fields
ReviewRequested
A session asked for a review.
Fields
body: Option<String>What the reviewer needs beyond the subject line: the evidence, the commands, the
question. Up to REVIEW_BODY_MAX bytes. A runtime older than the field reads
the request without it.
ReviewAnswered
A session answered a review request (on any runtime).
Fields
ReviewWithdrawn
The runtime that asked for a review ended its request: done, void, or superseded. Only the asking runtime writes it — from any of its sessions — so it is never a verdict; its note is the requester’s own result, shown with the request.
Implementations§
Source§impl EventBody
impl EventBody
Sourcepub fn kind(&self) -> &'static str
pub fn kind(&self) -> &'static str
The kind’s wire word: the same snake-case spelling the serialized event carries, so a counter, a log line and a peer’s JSON all name an event the same way. It is written out rather than derived from the variant name, because the word is protocol and renaming a variant must not change what another runtime reads.
use majordomus_cli::mesh::journal::{EventBody, KNOWN_KINDS};
let released = EventBody::ClaimReleased { claim: "c1".into() };
assert_eq!(released.kind(), "claim_released");
assert_eq!(serde_json::to_value(&released).unwrap()["kind"], released.kind());
assert!(KNOWN_KINDS.contains(&released.kind()));Sourcepub fn validate(&self) -> Result<(), String>
pub fn validate(&self) -> Result<(), String>
Check the body’s bounds: short identifiers, repository-relative scopes, a bounded handover. A body out of bounds is refused before it is signed or stored.
It runs on both sides: on the writer, so a runtime never signs something its peers
will refuse, and on the reader, so nothing a peer sends is stored unchecked. The
bounds are not arbitrary — a scope must be repository-relative because a claim over
/etc means nothing on another machine, a handover’s id must be the digest of its
body or identity would be forgeable, and the fields that become front matter where
a handover is consumed must be single lines.
use majordomus_cli::mesh::journal::{EventBody, HandoverBody, SessionInfo};
assert!(EventBody::SessionOpened { info: SessionInfo::named("s1", "cli") }.validate().is_ok());
// a scope that is not repository-relative is refused
let escaping = EventBody::ClaimAcquired { claim: "c1".into(), session: "s1".into(),
scope: vec!["../../etc".into()], intent: None, mode: Default::default(), issue: None };
assert!(escaping.validate().is_err());
// and a handover whose id is not the digest of its body is not that handover
let body = "# Objective\nship\n".to_string();
let lying = EventBody::HandoverPublished { handover: HandoverBody {
id: HandoverBody::digest_of("something else"), task: None, issue: None,
milestone: None, branch: None, head: None, created_at: None, name: None, body } };
assert!(lying.validate().is_err());Trait Implementations§
Source§impl<'de> Deserialize<'de> for EventBody
impl<'de> Deserialize<'de> for EventBody
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for EventBody
Source§impl JsonSchema for EventBody
impl JsonSchema for EventBody
Source§fn schema_id() -> Cow<'static, str>
fn schema_id() -> Cow<'static, str>
Source§fn json_schema(generator: &mut SchemaGenerator) -> Schema
fn json_schema(generator: &mut SchemaGenerator) -> Schema
Source§fn inline_schema() -> bool
fn inline_schema() -> bool
$ref keyword. Read moreimpl StructuralPartialEq for EventBody
Auto Trait Implementations§
impl Freeze for EventBody
impl RefUnwindSafe for EventBody
impl Send for EventBody
impl Sync for EventBody
impl Unpin for EventBody
impl UnsafeUnpin for EventBody
impl UnwindSafe for EventBody
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.