channels-actor
Source: crates/lvcs-action/src/channels/mod.rs.
/// A query the channels actor asks a document, echoed by its answer.
/// Chosen by the actor, in order.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct QueryId(pub u64);
/// The channels actor's events, generated by its sml table: its mailbox and
/// journal type (decision 286).
///
/// Its events, each the payload type its variant is named for:
/// - [`ChannelsConfig`]: First in every incarnation, and again whenever main's `lvcs.toml`
/// approved heads move (decision 127).
/// - [`Restore`]: Second: the approved heads `.lvcs/approved.cbor` holds.
/// - [`Entries`]: A channel document rendered (`DocOutput::ChannelRendered`).
/// - [`ApprovalsEntries`]: The approvals document rendered (its
/// `DocOutput::ChannelRendered`'s approvals).
/// - [`Bundles`]: The registry rendered new bundle records.
/// - [`Baseline`]: A protected file document opened: its baseline heads.
/// - [`Descended`]: A document answered a `Descends` query.
/// - [`Overlapped`]: A document answered an `Overlap` query.
/// - [`Runnable`]: `run` asks whether a gated action may run on a frontier (decision 206),
/// answered by [`ChannelsOutput::Runnable`].
pub use machine::ChannelsEvents;
/// The policy from main's `lvcs.toml` `[[actions]]` and `[channels]` at
/// its approved heads (decisions 127, 141, 245, and 297, row 180).
///
/// `change` is every change action's channel but the generator's (the
/// change set), each with the ids of the actions an attestation in it
/// names and the attestations it counts a frontier at
/// (`Actions::change_channels`); `gated` every gated action;
/// `combinations` each `[channels]` combination's members; `generator` the
/// generator's id, quorum, and channel, when an action has `writes`; and
/// `held` whether a source of the set refused something (decision 245,
/// stage C): no change channel's value then moves and no frontier is
/// eligible for a run, though approvals still count from the change
/// channels' attestations, so the fix can be approved.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ChannelsConfig {
/// The change set's channels, each with its actions and threshold.
pub change: BTreeMap<ChannelName, ChangeChannel>,
/// Every gated action.
pub gated: Vec<Action>,
/// Each combination's members.
pub combinations: BTreeMap<ChannelName, BTreeSet<ChannelName>>,
/// The generator, when an action has `writes`.
pub generator: Option<Generator>,
/// Whether every change channel is held.
pub held: bool,
}
/// The generator's id, the records its generations need, and the channel
/// they are put in.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Generator {
/// The generator's id: the generations of it count.
pub id: ActionId,
/// The records with agreeing digests a generation needs to count.
pub quorum: u32,
/// Its channel, its name: only the generations there count.
pub channel: ChannelName,
}
/// Each protected path's approved heads, by address, from
/// `.lvcs/approved.cbor` (empty when it is missing).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Restore {
/// Each protected path's approved heads.
#[serde(with = "lvcs_doc::cbor::head_map")]
pub approved: BTreeMap<String, Vec<ChangeHash>>,
}
/// The entries channel `channel` added since its last render.
///
/// Every one on its incarnation's first render (`full`), and only new ones
/// after. Every value at each new key is listed, so concurrent puts at one key
/// both appear.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Entries {
/// The channel.
pub channel: ChannelName,
/// The entries added.
pub added: Vec<ChannelEntry>,
/// Whether `added` is every entry.
pub full: bool,
}
/// The approvals the approvals document added since its last render
/// (decision 297).
///
/// Every one on its incarnation's first render (`full`), and only new ones
/// after, as [`Entries`] lists a channel's.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApprovalsEntries {
/// The approvals added.
pub added: Vec<Approval>,
/// Whether `added` is every approval.
pub full: bool,
}
/// The bundle records the registry render added since the last (all of
/// them the first time, `full`), computed by the shell from consecutive
/// `RegistryRendered` records.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Bundles {
/// The records added.
pub added: Vec<BundleRecord>,
/// Whether `added` is every record.
pub full: bool,
}
/// One bundle's merge record: each document it lists, with its side.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BundleRecord {
/// The bundle.
pub bundle: BundleId,
/// Each document it lists, by address, with its side.
pub sides: BTreeMap<String, BundleSide>,
}
/// A protected file document's `Baseline` answer.
///
/// The document at `address` opened, or changed while its baseline was
/// its genesis heads, and answered `heads`: after genesis and the owner's
/// import change, or genesis's own.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Baseline {
/// The document's address.
pub address: String,
/// Its baseline heads.
#[serde(with = "lvcs_doc::cbor::heads")]
pub heads: Vec<ChangeHash>,
}
/// Protected path `address` follows its live `heads` (decision 314): while
/// the policy in force has no change action, the shell found every change
/// beyond the path's approved heads signed by a holder of `Approve` on it
/// (the owner always), so they become its approved heads. Dropped once the
/// policy has a change action, when approvals alone move them.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Followed {
/// The document's address.
pub address: String,
/// Its live heads.
#[serde(with = "lvcs_doc::cbor::heads")]
pub heads: Vec<ChangeHash>,
}
/// The answer to `Descends` query `req`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Descended {
/// The query answered.
pub req: QueryId,
/// The answer.
pub outcome: Descent,
}
/// The answer to `Overlap` query `req`: whether the two bundles are
/// concurrent in that document, and whether their ranges overlap there.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Overlapped {
/// The query answered.
pub req: QueryId,
/// Whether the two bundles are concurrent in the document.
pub concurrent: bool,
/// Whether their ranges overlap there.
pub overlapping: bool,
}
/// `run`'s question (decision 206, the triggers row): may the gated
/// action that moves `channel` run on `frontier`, or, with none named, on
/// what? The shell's `req` is echoed by the answer. A change action's
/// channel has no gated action, so it is answered `NoAction`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Runnable {
/// The shell's request, echoed.
pub req: RequestId,
/// The gated action's channel, which is its name.
pub channel: ChannelName,
/// The frontier to run on; `None` for the one frontier every `after`
/// channel of the action holds.
pub frontier: Option<FrontierId>,
/// The shell's clock when it asked, in milliseconds since the Unix
/// epoch: the run request's date is decided from it (decision
/// 269).
pub now_ms: i64,
}
/// The answer to a [`Runnable`] question: row 141's refusal rule for a
/// manual run, from the actor's own state (decision 206).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RunVerdict {
/// No gated action at approved heads moves the channel.
NoAction,
/// The frontier is neither eligible for the channel's gated action nor
/// named by a passed outcome the channel holds; or none was named and
/// the action's `after` channels' values share no eligible frontier,
/// or several.
NotEligible,
/// The action may run on `frontier`, under a run request dated
/// `time_ms`: the greater of the question's `now_ms` and one more than
/// the greatest time the channel's value ranks by (its run requests'
/// `time_ms` and its passed trigger runs' ranks, the least `time_ms` of
/// each one's passed outcomes), so it ranks above every run request and
/// trigger run the channel holds, whatever clock a trigger run's runner
/// read (decision 269).
Runnable {
/// The frontier it runs on.
frontier: FrontierId,
/// The run request's date.
time_ms: i64,
},
/// The action may run, but that date is more than `REQUEST_SKEW_MS`
/// past `now_ms`: `run` is refused `retry_after` `wait_ms`, until the
/// clock is within the skew of it, so no daemon signs a far-future
/// change.
Ahead {
/// How long until the clock is within the skew of that date.
wait_ms: u64,
},
/// The frontier is behind the channel's value, which supersedes it,
/// and the channel never passed a run of it (so it is no rollback):
/// a gated channel only moves forward (decision 306).
Superseded {
/// The channel's value.
value: FrontierId,
},
/// Whether the channel's value supersedes the frontier is not known
/// yet: the documents were asked, and `run` is refused `retry_after`.
Undecided,
}
/// A gated channel's pending run (decision 141, the triggers row): the
/// run of its greatest run request `run request`, or of its trigger channel's
/// frontier under the trigger origin `run request`, of `frontier`, keyed
/// `run`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct PendingRun {
/// The run key, `HASH([ACTION_DOMAIN, action, frontier id, run request])`.
pub run: RunKey,
/// The frontier the run runs on.
pub frontier: FrontierId,
/// The run request it serves, by entry id, or its trigger origin,
/// `HASH([TRIGGER_DOMAIN, frontier id])`.
pub origin: EntryId,
}
/// What the channels actor asks the shell to do.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChannelsOutput {
/// Ask the document at `address` whether `heads` descends from `from`
/// (`DocEvent::Descends`), answered by `Descended`; asked only for
/// documents whose heads differ, and with `from` empty to ask whether
/// the document holds `heads` at all. The shell answers every one,
/// `Unknown` when the document refuses or is missing.
Descends {
/// The query.
req: QueryId,
/// The document's address.
address: String,
/// The heads that may descend.
#[serde(with = "lvcs_doc::cbor::heads")]
heads: Vec<ChangeHash>,
/// The heads they may descend from.
#[serde(with = "lvcs_doc::cbor::heads")]
from: Vec<ChangeHash>,
},
/// Ask the document at `address` whether bundles `a` and `b` are
/// concurrent and overlap there (`DocEvent::CheckOverlap`), answered by
/// `Overlapped`. The shell answers every one, a refused check as
/// concurrent and overlapping.
Overlap {
/// The query.
req: QueryId,
/// The document's address.
address: String,
/// The first bundle's side there.
a: BundleSide,
/// The second bundle's side there.
b: BundleSide,
},
/// Channel `channel`'s value moved (decision 297): for a change
/// action's channel, its counted frontiers that no other counted
/// frontier of it supersedes, sorted by id; for the generator's, the
/// shown generation's frontier, zero or one; for a gated action's, the
/// frontier of its greatest satisfied request, zero or one (decision
/// 141); for a combination, the frontiers it holds that no other it
/// holds supersedes, sorted by id. Never re-sent unchanged (decision
/// 145).
Value {
/// The channel.
channel: ChannelName,
/// Its value.
frontiers: Vec<FrontierId>,
},
/// Protected path `path`'s approved heads moved to `heads`; the shell
/// persists them (decision 127).
Approved {
/// The protected path.
path: String,
/// Its approved heads.
#[serde(with = "lvcs_doc::cbor::heads")]
heads: Vec<ChangeHash>,
},
/// gated channel `channel`'s pending run changed: the run of its
/// greatest run request, or none (decision 141). For the runner actor.
Candidates {
/// The channel.
channel: ChannelName,
/// Its pending run.
run: Option<PendingRun>,
},
/// gated channel `channel` newly holds an outcome, passed or
/// failed, of each run in `runs` (decision 142). For the runner actor.
Outcomes {
/// The channel.
channel: ChannelName,
/// The runs, sorted.
runs: Vec<RunKey>,
},
/// What `channel.read` reports of gated gated channel `channel`'s
/// value `frontier` moved (decision 141): `runners`, the runners with a
/// passed outcome naming it, and `run requests`, the run requests naming
/// it. Sent after the `Value` that set `frontier`, and never re-sent
/// unchanged. Sent for gated channels only: the shell counts a change
/// channel's attestations itself from its renders and the
/// `ChannelsConfig` it sent (decision 297).
Counts {
/// The channel.
channel: ChannelName,
/// Its value.
frontier: FrontierId,
/// The runners with a passed outcome naming it.
runners: u32,
/// The run requests naming it.
requests: u32,
},
/// The answer to [`ChannelsEvents::Runnable`] `req`.
Runnable {
/// The shell's request, echoed.
req: RequestId,
/// The answer.
verdict: RunVerdict,
},
/// A generation of derivation `derive` changed (row 180). With
/// `Counted`, it is the generation main's visible frontier should now
/// show: among the counted generations, one whose frontier no other's
/// frontier supersedes, ties to the greater `InputsHash` bytes. With
/// `Diverged` or `Failed`, its inputs just came to that state, so they
/// never count, and a shown generation that came to it is shown no
/// more. `frontier` and `outputs` are the standing record's
/// (`policy::generation`). Never re-sent unchanged.
Generated {
/// The generator.
action: ActionId,
/// The generation's `InputsHash`.
inputs: InputsHash,
/// The standing record's frontier.
frontier: FrontierId,
/// The standing record's outputs.
outputs: BTreeMap<String, GeneratedOutput>,
/// What its records come to.
state: GenerationState,
},
/// No generation is shown any more (row 180): the generator went, or
/// no counted generation stays one the view contains.
Ungenerated,
/// Derivation inputs `inputs` newly have a counted generation (row
/// 180), shown or not. Named only from a count that needs no answer
/// still missing, after that count's shown generation, so a runner that
/// hears its inputs counted reads the shown generation that count
/// chose. Runners under different toolchain hashes count different
/// inputs, and only one of them is shown.
Counted {
/// The counted inputs.
inputs: InputsHash,
},
/// Derivation inputs `inputs`, named `Counted` before, count no more
/// (a later differing or failed record, a raised threshold, or the
/// generator gone), named at once.
Uncounted {
/// The inputs.
inputs: InputsHash,
},
}