Skip to main content

wire/
answer.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! What a Query comes back as: one shape per view, and the closed set
7//! of them.
8//!
9//! These are read shapes and nothing else. Every one is derived from the
10//! city's own state by a projection above this crate, so nothing here
11//! decides anything; what this module owns is that a page and a server
12//! agree on the field names, and that `Unavailable` is a real answer —
13//! a view this build does not evaluate yet says so by name rather than
14//! returning an empty result a reader would mistake for an empty city.
15
16use kernel::{
17    Address, ApprovalItem, Autonomy, ClusterKey, FileChange, GitOid, Restoration, Ruling, Seq,
18    TimeMs,
19};
20use serde::{Deserialize, Serialize};
21
22use crate::command::HaltScope;
23use crate::guide::GuideProgress;
24use crate::preference::PreferencesAnswer;
25
26mod automation;
27mod building;
28mod commits;
29mod config;
30mod cost;
31mod cost_of;
32mod doctor;
33mod document;
34mod document_bytes;
35mod document_versions;
36mod endpoints;
37mod evidence;
38mod find;
39mod git_status;
40mod github;
41mod harnesses;
42mod history;
43mod hunks;
44mod identity;
45mod known_hosts;
46mod listing;
47mod mcp_health;
48mod metrics;
49mod model_facts;
50mod note;
51mod prefix;
52mod preview;
53mod proposals;
54mod range;
55mod release;
56mod rounds;
57mod run_summary;
58mod scanning;
59mod sessions;
60mod skills;
61mod toolkits;
62mod usage;
63
64pub use automation::{AutomationAnswer, Cadence, ScheduledJob, WatchedSource};
65pub use building::{ArchiveLine, BlockedLine, BuildingAnswer, BuildingDoc};
66pub use building::{BuildingProgress, PlanRow, PursuitLine};
67pub use commits::{CommitAnswer, CommitAt, CommitsAnswer};
68pub use config::{
69    ConfigAnswer, ConfigLayer, SecondDomain, SettledEffort, SettledSearch, SettledSecond,
70    SupplierAccounts, TuningDefaults,
71};
72pub use cost::{CostAnswer, UnpricedCalls};
73pub use cost_of::{CostOfAnswer, RUN_COSTS_MAX, RunCostsAnswer};
74pub use doctor::{DoctorAbsence, DoctorAnswer, DoctorCore, DoctorFault, DoctorInstall, DoctorItem};
75pub use doctor::{DoctorCoverage, DoctorCustody, DoctorCustodyLifetime, DoctorCustodyStore};
76pub use doctor::{DoctorGuarantee, DoctorGuaranteeAxis, DoctorSandbox, DoctorSandboxArm};
77pub use doctor::{DoctorNeed, DoctorState, DoctorTier, DoctorVerdict, DoctorVersion};
78pub use doctor::{DoctorNewest, DoctorPack, DoctorUnread, DoctorUpstream};
79pub use doctor::{DoctorSandboxMissing, SandboxArm};
80pub use document::{Coverage, DocumentAnswer, DocumentBody, DocumentState, HeldDocument};
81pub use document_bytes::{BYTES_WINDOW_MAX, BytesAnswer, ExportAnswer};
82pub use document_versions::{DocumentVersion, VERSIONS_MAX, VersionSource, VersionsAnswer};
83pub use endpoints::{AccountStatus, ChosenSummary, EndpointSummary, EndpointsAnswer, KeyState};
84pub use evidence::{EvidenceAnswer, EvidenceItem, EvidenceKind, Picture};
85pub use find::{FIND_MAX, FIND_WALK_MAX, FindAnswer, Walked};
86pub use git_status::{Drift, GitStatusAnswer};
87pub use github::{GithubLoginAnswer, GithubReading};
88pub use harnesses::{HarnessLine, HarnessState, HarnessesAnswer};
89pub use history::{HistoryAnswer, HistoryRangeAnswer};
90pub use hunks::{HunksAnswer, PatchLine, Withheld};
91pub use identity::{IdentityAnswer, StatedIdentity};
92pub use known_hosts::{KnownFace, KnownHost, KnownHostsAnswer};
93pub use listing::{Entry, EntryKind, ListingAnswer};
94pub use mcp_health::{McpHealthAnswer, McpServerHealth, McpState, McpToolLine};
95pub use metrics::MetricsAnswer;
96pub use model_facts::ModelFactsSummary;
97pub use note::{HandbackNote, Note, ReplyEnd, ReplyEnded, Speaker};
98pub use prefix::{ContentAnswer, PrefixAnswer, PrefixSegment, PrefixSlot, PrefixSource};
99pub use preview::PreviewAnswer;
100pub use proposals::{OfferedCard, OpenProposalsAnswer, ProposalCard, ProposalsAnswer};
101pub use range::RangeAnswer;
102pub use release::{InstallChannel, Registry, RegistryNewest, RegistryReading};
103pub use release::{ReleaseAnswer, ReleaseLine, UpdateHint};
104pub use rounds::{
105    Call, Closing, FrozenNames, Opening, Outcome, Output, RoundsAnswer, Timing, Turn, Used,
106};
107pub use run_summary::{RunSummary, Waiting};
108pub use scanning::{DoctorDrive, DoctorExclusion, DoctorScanning, DoctorUntold};
109pub use sessions::{SESSION_PREVIEW_MAX, SESSIONS_MAX, SessionLine, SessionStart, SessionsAnswer};
110pub use skills::{SkillLine, SkillShelf, SkillsAnswer};
111pub use toolkits::{Standing, ToolkitLine, ToolkitsAnswer};
112pub use usage::VersionAuthor;
113pub use usage::{
114    DayCount, ExportFormat, HeldSkill, McpServerUsage, McpToolUsage, McpUsageAnswer, McpUse,
115};
116pub use usage::{ShellCalls, ShellsAnswer, UsageExportAnswer, UsageKind, UseOutcome};
117pub use usage::{SkillAudit, SkillUsageAnswer, SkillUsageLine, SkillUse, SkillVersion};
118
119/// What moved between two checkpoints, one row per file, path order.
120///
121/// Path order rather than size order: somebody looking for one file finds
122/// it in the same place every time, and a list that reorders itself as
123/// the numbers change cannot be scanned twice.
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
125#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
126pub struct ChangesAnswer {
127    pub base: GitOid,
128    /// Absent when the comparison ran against the working tree.
129    pub head: Option<GitOid>,
130    pub files: Vec<FileChange>,
131}
132
133/// The most records one `History` answer may carry. A page asking for
134/// more gets this many; the whole ledger is not a thing to put on a
135/// socket, and a limit the caller cannot exceed is one fewer way for a
136/// client to make the server do unbounded work.
137pub const HISTORY_MAX: u32 = 500;
138
139#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
140#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
141pub struct CityAnswer {
142    /// Every active run and the latest frozen few, in RunId order;
143    /// `frozen` counts every frozen run, listed or not.
144    pub runs: Vec<RunSummary>,
145    pub active: u64,
146    pub frozen: u64,
147    /// One entry per building whose roadmap the city could read.
148    pub buildings: Vec<BuildingProgress>,
149    /// The standing goals this city is working towards, if any.
150    pub pursuits: Vec<PursuitLine>,
151    /// The scopes shut by `halt` and not yet released, in the shape a
152    /// `halt` frame names them. A page that has just opened has no
153    /// other way to learn that the city is stopped.
154    ///
155    /// The same type the command carries, not the ledger's string form:
156    /// a reader that had to take `building:<addr>` apart would be
157    /// writing a second copy of a grammar that already has one, and the
158    /// first client to get it wrong reported every shut building as
159    /// running.
160    pub halted: Vec<HaltScope>,
161    /// The last record of a history proved whole, when the proof has
162    /// found it so; absent while a served city is still proving it, or
163    /// after the proof found it broken (`crates/wire/spec/Answer.lean` §8-63).
164    pub proved: Option<Seq>,
165}
166
167/// What is waiting for a person, as the Ledger recorded it.
168///
169/// The whole item travels rather than a summary of it. An interface that
170/// groups identical questions needs the cluster key, and one that leads
171/// with the longest wait needs the arrival time; a summary type that
172/// dropped both forced the page to render every item as its own group
173/// under a sentence nobody wrote. `tainted` needs no special carriage
174/// here for the same reason - it is a field of the item itself.
175#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
176#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
177pub struct ApprovalsAnswer {
178    pub items: Vec<ApprovalItem>,
179}
180
181/// What a query returns. `Unavailable` is a real answer: a view this
182/// build does not evaluate yet says so by name, rather than returning an
183/// empty result a reader would mistake for an empty city.
184///
185/// `Eq` went when `History` arrived: a record's payload is arbitrary
186/// JSON, and JSON has no total equality. Nothing compared two answers
187/// for equality outside a test.
188#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
189#[serde(rename_all = "snake_case")]
190#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
191pub enum Answer {
192    History(Box<HistoryAnswer>),
193    HistoryRange(Box<HistoryRangeAnswer>),
194    Sessions(SessionsAnswer),
195    Changes(ChangesAnswer),
196    Commit(Box<CommitAnswer>),
197    City(CityAnswer),
198    Run(Option<Box<RunSummary>>),
199    Approvals(ApprovalsAnswer),
200    Cost(Box<CostAnswer>),
201    Endpoints(EndpointsAnswer),
202    KnownHosts(KnownHostsAnswer),
203    Harnesses(HarnessesAnswer),
204    Building(Box<BuildingAnswer>),
205    Inbox(InboxAnswer),
206    Discards(DiscardAnswer),
207    Registry(RegistryAnswer),
208    Archive(ArchiveAnswer),
209    Metrics(Box<MetricsAnswer>),
210    Governance(GovernanceAnswer),
211    Hunks(Box<HunksAnswer>),
212    Rounds(Box<RoundsAnswer>),
213    Evidence(EvidenceAnswer),
214    CostOf(CostOfAnswer),
215    RunCosts(RunCostsAnswer),
216    Listing(ListingAnswer),
217    Find(FindAnswer),
218    Document(Box<DocumentAnswer>),
219    Proposals(Box<ProposalsAnswer>),
220    OpenProposals(OpenProposalsAnswer),
221    Range(Box<RangeAnswer>),
222    Versions(Box<VersionsAnswer>),
223    Bytes(Box<BytesAnswer>),
224    Export(Box<ExportAnswer>),
225    Preview(Box<PreviewAnswer>),
226    /// A reply's text laid out (`crates/wire/spec/Answer/Preview.lean` §8-75): its closed blocks, and
227    /// the bytes they cover.
228    Reply(Box<documents::Laid>),
229    Commits(CommitsAnswer),
230    Doctor(Box<DoctorAnswer>),
231    Upstream(Box<DoctorUpstream>),
232    Prefix(Box<PrefixAnswer>),
233    Content(Box<ContentAnswer>),
234    Skills(Box<SkillsAnswer>),
235    GitStatus(Box<GitStatusAnswer>),
236    McpHealth(Box<McpHealthAnswer>),
237    SkillUsage(Box<SkillUsageAnswer>),
238    McpUsage(Box<McpUsageAnswer>),
239    UsageExport(Box<UsageExportAnswer>),
240    Shells(Box<ShellsAnswer>),
241    Toolkits(Box<ToolkitsAnswer>),
242    Release(Box<ReleaseAnswer>),
243    Preferences(Box<PreferencesAnswer>),
244    Config(Box<ConfigAnswer>),
245    Identity(Box<IdentityAnswer>),
246    Automation(Box<AutomationAnswer>),
247    GithubLogin(GithubLoginAnswer),
248    Guide(GuideProgress),
249    Privacy(Box<crate::PrivacyAnswer>),
250    /// `reason` is the text of what stopped a view that tried to look
251    /// (`crates/wire/spec/Server.lean` D47); absent when the view names
252    /// the query alone.
253    Unavailable {
254        query: String,
255        #[serde(default, skip_serializing_if = "Option::is_none")]
256        reason: Option<String>,
257    },
258}
259
260/// Who answers for this city, and what was answered on the person's
261/// behalf.
262///
263/// One shape for both halves: a list of decisions with nobody named
264/// beside it does not say whether the person delegated them, and a
265/// delegation with nothing under it does not say whether it was ever
266/// used.
267#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
268#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
269pub struct GovernanceAnswer {
270    pub autonomy: Autonomy,
271    /// Every approval this city has answered, oldest first — the order
272    /// the ledger wrote them and the order a fold expects.
273    ///
274    /// The person's own answers are here too. A list holding only what
275    /// somebody else decided would make "I answered this" and "nobody
276    /// ever answered this" look the same, and who may answer is the
277    /// other field of this same answer.
278    pub decided: Vec<Decision>,
279}
280
281/// One approval, as it was answered.
282#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
283#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
284pub struct Decision {
285    /// The `ApprovalId` that was answered.
286    pub item: String,
287    pub verdict: Ruling,
288    /// What the person was shown when they answered: the answer covers
289    /// the group, not the one row.
290    pub cluster: ClusterKey,
291    pub at: TimeMs,
292}
293
294/// What waits in one room, without taking it.
295///
296/// Folded from the ledger rather than read off the queue: a queue that
297/// is read by being consumed cannot also be looked at, and a view that
298/// consumed what it showed would be a view that changes the thing it
299/// reports on.
300#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
301#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
302pub struct InboxAnswer {
303    pub addr: Address,
304    pub waiting: Vec<SignalLine>,
305}
306
307#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
308#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
309pub struct SignalLine {
310    pub id: String,
311    pub kind: String,
312    pub from: String,
313    pub at: TimeMs,
314    /// The first line of the signal's `text`; `None` when its payload
315    /// carries no text (`crates/wire/spec/Reading.lean` D39).
316    #[serde(default)]
317    pub first_line: Option<String>,
318}
319
320/// The Recycle Bin: what was discarded and how each row gets back.
321///
322/// A row without a way back cannot be constructed upstream, so every
323/// row here states one; `restored` says whether somebody already took
324/// it.
325#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
326#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
327pub struct DiscardAnswer {
328    pub rows: Vec<DiscardLine>,
329}
330
331#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
332#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
333pub struct DiscardLine {
334    pub path: String,
335    /// The way back, as the record kept it - the plan itself, not a
336    /// sentence about it.
337    ///
338    /// Composing the sentence is the interface's job and it already has
339    /// one authority for it (`client/src/views/record/bin.svelte`); a server that
340    /// also rendered the plan into words would be the second. `None`
341    /// means the record names a scheme this build cannot read, which the
342    /// interface shows as a row it will not invent an action for - the
343    /// row is never dropped, because hiding a discarded thing is worse
344    /// than admitting the plan is unreadable.
345    pub restoration: Option<Restoration>,
346    pub at: TimeMs,
347    pub restored: bool,
348}
349
350/// What this city has decided is worth keeping.
351#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
352#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
353pub struct RegistryAnswer {
354    pub assets: Vec<RegistryLine>,
355}
356
357#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
358#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
359pub struct RegistryLine {
360    pub addr: Address,
361    pub kind: String,
362    pub subject: String,
363    pub at: TimeMs,
364}
365
366/// Archive hits across every building, read from the shelves at the
367/// moment of asking. The files are the authority; an index kept beside
368/// them would be a second copy of what the disk says.
369#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
370#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
371pub struct ArchiveAnswer {
372    pub needle: String,
373    pub hits: Vec<ArchiveHit>,
374}
375
376#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
377#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
378pub struct ArchiveHit {
379    pub building: Address,
380    pub kind: String,
381    pub day: u64,
382    pub subject: String,
383}