lernie 0.1.56

lernie: the operator seat — the window and wire client for a yog server
Documentation
//! **The census of what an engine can answer** — the kinds this window draws,
//! and the captured run three of the ops on this surface come back as.
//!
//! Split from [`super`] at the 300-line cap on the seam that module's own doc
//! already draws: [`super`] is *the reading* — the three outcomes one frame
//! can be, the four-rung policy every decoder obeys, and the module list — and
//! this is *what an answer turned out to be*. The first changes when the
//! policy does, which is almost never; the second every time a pane lands, and
//! that asymmetry is the whole reason a seam is here.
//!
//! **[`Reply`] is the one census** and DESIGN §4.9 holds the ledger of what a
//! later pane adds: a kind nothing renders is a kind nobody has to carry, and
//! the ball that lands a pane is the ball that adds its kind.

use super::{
    agent, balls, board, clients, config, convs, diff, enrolled, files, governing, help, inbox,
    lineages, login, ops, proposals, providers, queue, rail, roles, roster, science, search, start,
    step, steps, stream, transcript,
};

/// **The kinds the window draws.** Thirty-three, and each is here because a
/// surface paints it; DESIGN §4.9 holds the ledger of what a later pane adds.
#[derive(Debug, Clone, PartialEq)]
pub enum Reply {
    /// A short verb's captured run — what a deposit, a stop or a ball verb
    /// earns. `ok` is `exit == 0` and is read off the exit code alone.
    Outcome(Outcome),
    /// The detached advance was launched. It carries nothing because there is
    /// nothing yet: what the model does with the turn arrives on the
    /// transcript, at its own pace, and a receipt that guessed at it would be
    /// a receipt that lied.
    Nudged,
    /// The enumerated workspaces with their rollups — the roster, and how
    /// current the derivation behind it is.
    Workspaces(roster::Workspaces),
    /// One workspace's conversation list.
    Conversations(Vec<convs::ConvRow>),
    /// **What one workspace's roles are set to** — the read the tuning pane
    /// opens on, and the read back of what its own three writes landed. It is
    /// a listing rather than a map: the engine's order is the config's order,
    /// and a seat that re-sorted it would be holding a second opinion about a
    /// file it did not write.
    Roles(Vec<roles::RoleRow>),
    /// One conversation's committed entries with the live tail folded on —
    /// the whole of what the chat pane paints.
    Transcript(transcript::Transcript),
    /// **Everything waiting on the operator**, across every workspace the
    /// engine can see — the decision queue's rows, standing while its pane is
    /// open (bl-f0ef). It is the answer to two ops rather than one: `attention`
    /// asks for the whole queue and `seen` answers with the queue that remains,
    /// so a reading of it is never a receipt to discard.
    Attention(Vec<queue::QueueRow>),
    /// **The parked invocation was answered** (§4.34, bl-bce2): which call the
    /// answer landed on, the verdict written, and whether the release actually
    /// drove the conversation on.
    ///
    /// It answers with the **held invocation** rather than with the queue that
    /// remains, which is upstream's own decision and the reason the seat can
    /// say anything at all: the mark lifts only once the re-adjudication runs,
    /// so a queue read here would still show the park it just answered.
    Answered {
        tool: String,
        tool_use: String,
        /// The verdict written, carried verbatim ([`super`]'s rung 3).
        verdict: String,
        /// **How far the answer stands** (PROTOCOL 18): the held call, the
        /// conversation and its descent, or the workspace. Read back rather
        /// than assumed from what was asked — the engine narrows a wide answer
        /// on a destructive or credential-reaching call, so the receipt is the
        /// only place the reach the boundary actually took is stated. Carried
        /// verbatim on rung 3, like the verdict beside it.
        scope: String,
        /// Whether the releasing advance was launched. `hold` never launches
        /// one, and that is the operator saying *stay parked*.
        advanced: bool,
    },
    /// **The spread's product** (§4.36): one staged body per candidate, each
    /// rebound to its own attempt worktree and ready for the ordinary
    /// `prompt`. It is a listing of exactly the value
    /// [`Prepared`](Self::Prepared) carries one of, because upstream encodes
    /// it with the same encoder — so a candidate and a single staged start are
    /// one type here, and firing n is firing one, n times.
    Fanned(Vec<start::Prepared>),
    /// **A candidate was accepted** (§4.36): the identities its delivery acted
    /// on. It is a receipt and never a stored winner — the standing fact is
    /// the tagged squash the target's history now carries — so it is four
    /// scalars and no type of its own, exactly as
    /// [`Answered`](Self::Answered) is.
    ///
    /// **Two of the four are optional and each absence is a fact**, upstream's
    /// own: no `source` is a source ref that was not there, and no `commit` is
    /// a delivery that landed nothing. The target and the pinned base it
    /// validated against are always said, because a delivery is about them
    /// whether or not it moved anything.
    Delivered {
        base: String,
        target: String,
        source: Option<String>,
        commit: Option<String>,
    },
    /// **A candidate's worktree was released** (§4.36): `discarded` says
    /// whether this project's declared retention also took the source ref.
    ///
    /// It reports what the policy DID and never what was asked, which is why
    /// the seat paints it rather than predicting it: an undeclared retention
    /// keeps the ref, and a seat that said so on its own would be a second
    /// authority on a file it has not read.
    Retired { discarded: bool },
    /// **A capability floor was written** (§4.34, bl-bce2): whether one
    /// **stands** over the conversation now.
    ///
    /// Re-derived from the engine's trail after the write rather than echoed
    /// back from the direction that was asked, and the two differ exactly
    /// where it matters: restoring a conversation whose ancestor is still
    /// floored leaves it floored, and a receipt saying otherwise would lie.
    /// It is one field, so it is carried as one.
    Floored { standing: bool },
    /// **A flag was raised**, and the row it lands on arrives on the next
    /// [`Attention`](Self::Attention). It carries nothing for the reason
    /// [`Nudged`](Self::Nudged) carries nothing — what changed is on the queue,
    /// and a receipt that restated it would be this end predicting a listing.
    Flagged,
    /// **The steps the selected conversation's loop has taken** — one half of
    /// what the records pane paints, standing while it is open (bl-2cf7).
    Steps(steps::Steps),
    /// **What the selected conversation's worktree holds** — the other half,
    /// on the same standing (bl-2cf7).
    Files(files::Files),
    /// **The selected conversation's spine** — every operable commit it has
    /// and the children dispatched off them, on the records pane's standing
    /// (§4.28, bl-b52c). It is the read the `fork` control's one argument is
    /// discoverable off, which is why the two landed together.
    Rail(rail::Rail),
    /// **The conversation's own row, whole** — the header the records pane
    /// opens with, on that pane's standing (§4.32, bl-3257). The largest shape
    /// on this surface, and the one the seat reads most of a conversation from.
    ///
    /// **Boxed**, and it is the only variant here that is: at twenty-one
    /// fields over five nested objects it is several times the size of every
    /// other, so carrying it inline would widen `Reply` — and therefore every
    /// `Read` this window moves across the lock — to the size of the largest
    /// answer the seat has ever learned to paint.
    Agent(Box<agent::Agent>),
    /// **One step's records** — the drill-in under the steps list, addressed
    /// by the `seq` those rows paint (§4.32, bl-3257). POSTED rather than
    /// standing: it is asked about one row, on a control, and re-asked freely.
    ///
    /// **Boxed**, beside [`Agent`](Self::Agent) and for the same reason: four
    /// records, a stream, a tool listing and two bounded logs is the second
    /// of the two shapes on this surface big enough to widen every `Read`
    /// this window moves across the lock.
    Step(Box<step::Step>),
    /// **The undelivered mail** waiting in that conversation's inbox — the
    /// records pane's sixth read, on the same standing (§4.32, bl-3257).
    Inbox(Vec<inbox::Row>),
    /// **The config commit that conversation resolves its policy from** — the
    /// spine's other half, on the same standing (§4.28, bl-b52c). Its `oid`
    /// changed meaning at PROTOCOL 5 under an unchanged spelling, and
    /// `governing`'s own module doc is where that is written down.
    Governing(governing::Governing),
    /// **One engine's own verb table** — what the commands pane paints, and
    /// the same rows the parity roster is generated from (bl-40ec). It names
    /// no workspace, so it is one channel's answer and the pane is the union.
    Help(Vec<help::HelpRow>),
    /// **What a needle found**, across everything one engine can see — the
    /// find pane's answer, on the same fanned terms (bl-40ec).
    Found(search::Found),
    /// **The whole box's ball⇄workspace binding table** — the ball pane's
    /// widest read (bl-d2af). It names no workspace, so it is one channel's
    /// answer and the pane is the union.
    Balls(Vec<balls::BallRow>),
    /// **One engine's fleet board** — every live ball in its column, and the
    /// armed loops running them, which is the only place a loop's own facts
    /// are answered at all (bl-d2af). It names no workspace either.
    Board(board::Board),
    /// **The balls one wall holds**, with what each has cost — the ball pane's
    /// aimed half, standing on the pane exactly as the roles read does.
    WorkspaceBalls(Vec<balls::BoundBall>),
    /// **The branch a wall tracks its tasks on**, re-read. It is one field, so
    /// it is carried as one rather than wrapped in a struct with nothing else
    /// in it.
    Marks { branch: String },
    /// **The trail one engine keeps** — every action that crossed its boundary,
    /// standing while the trail pane is open (bl-4c48). It names no workspace,
    /// so it is one channel's answer and the pane is the union.
    Ops(Vec<ops::OpRow>),
    /// **What this wall can sign in to** — the login pane's first read,
    /// standing while it is open (bl-e3c5). It is the engine's listing order,
    /// which is brazen's own routing order, and a seat that re-sorted it would
    /// be holding a second opinion about a table it does not own.
    Providers(Vec<providers::ProviderRow>),
    /// **The machines registered in one workspace** — the clients pane's one
    /// read, standing while it is open (bl-e53c). Presence is answered at the
    /// moment it is asked and the advertised set is what that machine last
    /// presented, which are two lifetimes on one row and are painted as two.
    Clients(Vec<clients::ClientRow>),
    /// **One config file's bytes, and the settings its schema found in them**
    /// — the config pane's second read, standing on the destination it is
    /// pointed at (bl-5c53; DESIGN §4.30).
    Config(config::Config),
    /// **The config lineages one workspace holds** — that pane's first, and
    /// the listing its two pickers are filled from.
    Lineages(Vec<lineages::Lineage>),
    /// **What a reviewer has staged for this workspace's config**, and — when
    /// the read named one — that proposal whole (REMOTE §9.22, PROTOCOL 18).
    /// The learning loop's operator half: a candidate config commit is the
    /// same subject `lineages` browses and `config` writes, one branch away
    /// from governing anything.
    Proposals(proposals::Proposals),
    /// **The watermark landed** (bl-b8f7). It carries nothing for the reason
    /// [`Flagged`](Self::Flagged) carries nothing: what changed is on the
    /// trail, and the standing read answers `acked` on the rows that were
    /// standing — this end predicts neither.
    Acked,
    /// **The trail was truncated**, on exactly those terms. What the new trail
    /// holds is its own first row, and the standing read is what says so.
    TrailCleared,
    /// **Whether a standing thing is now standing** — the receipt `fleet`,
    /// `disband`, `arm` and `disarm` all answer with (bl-a43a). It is ONE kind
    /// for two families — the fleet loop and the alignment monitor — so a seat
    /// cannot tell which it answers from the reply and must read the `op` back
    /// (`crate::ui::model::absorb::Model::receipt`, DESIGN §4.33).
    Armed(bool),
    /// **Every delivery attempt of one workspace** — the fleet pane's first
    /// read, standing while it is open (bl-a43a). It is derived when asked, so
    /// the same row a minute later is a different statement.
    Science(Vec<science::Attempt>),
    /// **What one workspace's agents changed** — the fleet pane's second read,
    /// on the same standing. It is the rows and nothing else: the `patch` a
    /// named file would answer with rides through unread, because no gesture
    /// this build composes can ask for one.
    Work(Vec<diff::Diff>),
    /// **What one provider row is offering** — the same pane, one depth down,
    /// and posted rather than standing: a model list is fixed for the life of
    /// a provider's own answer, so a standing read would spend a round trip a
    /// beat forever on something that cannot change under the operator.
    Models(Vec<String>),
    /// **One sign-in run**, whether it is the act's own receipt or a frame of
    /// the lane that follows it — upstream answers both with this kind,
    /// because they are the same value at the same moment (REMOTE §8.3).
    Login(login::Signin),
    /// One frame of the live tail, and the whole accumulated stream rather
    /// than a delta: a frame **replaces** what a seat holds, so nothing has to
    /// be reassembled and a follow lane needs no second parser.
    Follow(stream::Stream),
    /// **A start, staged.** The fire-time parameters as the engine settled
    /// them, which the next act hands straight back — the one reply on this
    /// surface a seat has to hold between two gestures.
    Prepared(start::Prepared),
    /// **A new box's material** — the one reply on this surface that carries a
    /// secret, held while a symbol is on screen and written down nowhere
    /// (REMOTE §8.4; DESIGN §3).
    Enrolled(enrolled::Enrolled),
    /// **A start, fired**, and the name the engine minted for it. It carries
    /// nothing else for the reason [`Nudged`](Self::Nudged) carries nothing:
    /// what the model does with the turn arrives on the transcript. What is
    /// new is the name, and the name is an address the reply just made
    /// answerable.
    Started { conversation: String },
}

/// A captured run: what the child said and how it ended.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Outcome {
    /// The child's exit status.
    pub exit: i32,
    pub stdout: String,
    pub stderr: String,
}

impl Outcome {
    /// Whether the run succeeded. **Derived from the exit code**, never read
    /// off the `ok` beside it: one fact with one home, and a second copy could
    /// only ever disagree with it.
    pub fn ok(&self) -> bool {
        self.exit == 0
    }
}