Docs

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,
    },
}