pub struct LedgerEvent { /* private fields */ }Expand description
One event on its way into the ledger: the name, the payload, and the envelope the writer computes.
The envelope — ts, event, head, branch, by, and session when an episode is
open — is computed and never authored, exactly as share/events.yaml says. This type
takes the computed values from the caller rather than reaching for git itself, because
a domain that shells out to git while holding a lock on the ledger is a domain that
holds the lock for a hundred milliseconds on a loaded machine.
use majordomus_cli::session::{Envelope, LedgerEvent, Vocabulary};
let v = Vocabulary::parse(
"version: 1\nevents:\n - id: session.closed\n requires: [outcome, session_path]\n public: true\n",
).unwrap();
let envelope = Envelope {
ts: "2026-09-11T11:02:35Z".into(),
head: "1a651e644".into(),
branch: "feature/the-session-domain-is-typed".into(),
by: "majordomus/0.5.0".into(),
session: Some("s-20260910205542-e2a6".into()),
};
// the payload its declaration requires
let ok = LedgerEvent::new("session.closed")
.with("outcome", "closed")
.with("session_path", ".ai/repo/sessions/x.md");
let line = ok.render(&v, &envelope).expect("a line");
assert!(line.starts_with('{') && line.ends_with('}'));
assert!(line.contains("\"event\":\"session.closed\""));
assert!(line.contains("\"session\":\"s-20260910205542-e2a6\""));
// one key short, and it is refused before anything is written
let short = LedgerEvent::new("session.closed").with("outcome", "closed");
assert!(short.render(&v, &envelope).is_err());
// a name the vocabulary does not declare is refused too
assert!(LedgerEvent::new("session.clsoed").render(&v, &envelope).is_err());Implementations§
Source§impl LedgerEvent
impl LedgerEvent
Sourcepub fn new(name: impl Into<String>) -> Self
pub fn new(name: impl Into<String>) -> Self
An event with no payload yet. The name is not checked here: it is checked against
the vocabulary at LedgerEvent::render, which is the last moment before a line
could become durable.
use majordomus_cli::session::LedgerEvent;
assert_eq!(LedgerEvent::new("session.closed").name(), "session.closed");Sourcepub fn with(self, key: &str, value: impl Into<String>) -> Self
pub fn with(self, key: &str, value: impl Into<String>) -> Self
Add one payload key. Strings are the common case and have their own method so that
the ordinary call site carries no json!.
use majordomus_cli::session::{Envelope, LedgerEvent, Vocabulary};
let v = Vocabulary::parse("version: 1\nevents:\n - id: session.closed\n requires: [outcome]\n").unwrap();
let envelope = Envelope {
ts: "t".into(), head: "h".into(), branch: "b".into(), by: "m".into(), session: None,
};
let line = LedgerEvent::new("session.closed").with("outcome", "closed").render(&v, &envelope).unwrap();
assert!(line.contains("\"outcome\":\"closed\""));Sourcepub fn with_value(self, key: &str, value: Value) -> Self
pub fn with_value(self, key: &str, value: Value) -> Self
Add one payload key whose value is not a string — a count, a flag, a list.
use majordomus_cli::session::{Envelope, LedgerEvent, Vocabulary};
let v = Vocabulary::parse("version: 1\nevents:\n - id: ledger.rotated\n requires: [kept]\n").unwrap();
let envelope = Envelope {
ts: "t".into(), head: "h".into(), branch: "b".into(), by: "m".into(), session: None,
};
let line = LedgerEvent::new("ledger.rotated")
.with_value("kept", serde_json::json!(120))
.render(&v, &envelope)
.unwrap();
assert!(line.contains("\"kept\":120"));Sourcepub fn name(&self) -> &str
pub fn name(&self) -> &str
The event’s name, as offered. It is a declared name only once the vocabulary has
been asked, which LedgerEvent::render does.
use majordomus_cli::session::LedgerEvent;
assert_eq!(LedgerEvent::new("session.started").name(), "session.started");Sourcepub fn render(
&self,
vocabulary: &Vocabulary,
envelope: &Envelope,
) -> Result<String, LedgerError>
pub fn render( &self, vocabulary: &Vocabulary, envelope: &Envelope, ) -> Result<String, LedgerError>
Render the line, refusing an unregistered name or a missing required key. The refusal happens here — before the lock is taken and before anything is written — so that a malformed event never becomes a durable line.
use majordomus_cli::session::{Envelope, LedgerEvent, Vocabulary};
let v = Vocabulary::parse(
"version: 1\nevents:\n - id: session.closed\n requires: [outcome, session_path]\n",
).unwrap();
let envelope = Envelope {
ts: "2026-09-11T11:02:35Z".into(), head: "1a651e64".into(),
branch: "master".into(), by: "majordomus/0.5.0".into(),
session: Some("s-20260910205542-e2a6".into()),
};
let line = LedgerEvent::new("session.closed")
.with("outcome", "closed")
.with("session_path", ".ai/repo/sessions/x.md")
.render(&v, &envelope)
.expect("a line");
assert!(line.contains("\"event\":\"session.closed\""));
assert!(line.contains("\"session\":\"s-20260910205542-e2a6\""));
// one key short, and nothing is rendered at all
assert!(LedgerEvent::new("session.closed").with("outcome", "closed").render(&v, &envelope).is_err());Trait Implementations§
Source§impl Clone for LedgerEvent
impl Clone for LedgerEvent
Source§fn clone(&self) -> LedgerEvent
fn clone(&self) -> LedgerEvent
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for LedgerEvent
impl Debug for LedgerEvent
Source§impl PartialEq for LedgerEvent
impl PartialEq for LedgerEvent
Source§fn eq(&self, other: &LedgerEvent) -> bool
fn eq(&self, other: &LedgerEvent) -> bool
self and other values to be equal, and is used by ==.