majordomus_cli/execution/mod.rs
1//! Executions: watching a capability call that takes longer than a request should be held
2//! open for.
3//!
4//! Nothing here is a second way to do anything. An execution runs a capability of the one
5//! registry, through the one executor, and the only thing it adds is that the call has an
6//! identity, a lifecycle, a stream of typed events and a cancellation flag — so that a
7//! browser, an agent or a terminal can watch it happen instead of waiting for a response.
8//!
9//! ```text
10//! capability! ──► CapabilityRegistry ──► Context::execute ──► the handler
11//! │ ▲ │
12//! │ │ │ Progress
13//! └──► ExecutionEngine ───┘ ▼
14//! │ ExecutionEvent
15//! ▼ │
16//! ExecutionStore ◄─────────────────────────┘
17//! │
18//! ┌──────────────────┼───────────────────┐
19//! ▼ ▼ ▼
20//! executions.get executions.events the WebSocket
21//! (HTTP · MCP · CLI) (HTTP · MCP · CLI) (the Cockpit)
22//! ```
23//!
24//! The layering is the point, and it runs one way. The domain produces events; the store
25//! keeps and fans them out; a transport renders them. [`crate::http::events`] is an
26//! adapter over this module and contains no execution logic, which is what makes an audit
27//! trail, a notifier or a second protocol something that subscribes rather than something
28//! that has to be threaded through the engine.
29//!
30//! One plane per checkout, not one per process. `majordomus run` executes in its own
31//! process, and when a server already serves the checkout it reports the execution there
32//! ([`ExecutionStore::adopt`], [`ExecutionStore::report`]), so the server's listing, the
33//! Cockpit and the live channel show it with the pid that ran it; a server that stops
34//! hearing from it fails it rather than showing it running for ever. With no server, the
35//! run says on stderr that nothing else can see it. A run never starts a server.
36//!
37//! What it is not: a job system. There is no durable queue, no retry, no schedule and no
38//! execution that outlives the process that accepted it. The store says so — its limits
39//! are typed and its behaviour at each limit is defined — and [`store::Limits`] is where a
40//! durable store would be substituted, because nothing above it names a `Mutex`.
41
42pub mod engine;
43pub mod event;
44pub mod model;
45pub mod redact;
46pub mod sink;
47pub mod store;
48
49pub use engine::{ExecutionEngine, SubmitError};
50pub use event::{EventPayload, ExecutionEvent, PROTOCOL_VERSION};
51pub use model::{
52 Actor, ActorKind, Execution, ExecutionDiagnostic, ExecutionError, ExecutionId, ExecutionState,
53 LogStream, ProgressView, RepositoryRef, StepState, StepView,
54};
55pub use redact::redact;
56pub use sink::Progress;
57pub use store::{
58 AdoptError, CancelOutcome, Delivery, EventPage, ExecutionStore, Filter, Limits, ReportOutcome,
59 Seed, REPORT_HEARTBEAT, REPORT_SILENCE,
60};