Skip to main content

wire/frames/
query.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//! Everything a page may ask, and the name table the schema hash is
7//! built from; `frames` says what every Query shares.
8
9use kernel::{Address, B3Hash, GitOid, Locator, NodeId, RunId, Seq};
10use serde::{Deserialize, Serialize};
11
12use crate::named_frames::named_frames;
13
14named_frames! {
15/// A read of the city; the module documentation says what all of them share.
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "snake_case")]
18#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
19pub enum Query {
20    /// A bounded slice of the one history, ending just before `before`
21    /// or at the tail when that is absent.
22    ///
23    /// The server broadcasts what happens next and never what happened,
24    /// so a page that only listened would see a city that had been
25    /// running for a month as an empty one. Bounded because the whole ledger is not a
26    /// thing to put on a socket, and paged backwards because what a
27    /// reader wants first is the end.
28    History {
29        before: Option<Seq>,
30        limit: u32,
31    },
32    /// The same slice, narrowed to one session.
33    ///
34    /// [`Query::History`] carries no run, so a client watching four
35    /// sessions divides one bounded slice between them and a session
36    /// that started before the tab did is not in it at all.
37    /// [`Query::RunView`] says whether a run exists and where it got to,
38    /// not what happened in it. Answered with [`HistoryAnswer`], because
39    /// "a page of history" already has a shape.
40    RunHistory {
41        run: RunId,
42        before: Option<Seq>,
43        limit: u32,
44    },
45    /// The records of the one history between two sequence numbers, both
46    /// ends included: the question a page asks when the event stream tells
47    /// it what it skipped.
48    ///
49    /// [`Query::History`] walks backwards from a cursor, which answers
50    /// "what came before this" and cannot answer "what happened between
51    /// these two". A gap has a near end as well as a far one, and a page
52    /// that walked to the gap's near end from the tail would pay for every
53    /// record in between.
54    ///
55    /// Answered with [`HistoryRangeAnswer`](crate::HistoryRangeAnswer)
56    /// rather than with [`HistoryAnswer`](crate::HistoryAnswer): the
57    /// question carries no cursor a reader holds on to, so the answer has
58    /// to name the slice it is, and a page filling a gap while its record
59    /// view is open is asking both questions at once. A range the Ledger
60    /// holds more records for than `limit` allows is answered up to the
61    /// limit, and the answer says where to ask next.
62    HistoryRange {
63        from: Seq,
64        to: Seq,
65        limit: u32,
66    },
67    /// The stretches of one room, newest first (`crates/wire/spec/Answer/Sessions.lean` §8-71).
68    Sessions {
69        room: Address,
70    },
71    /// What moved between two checkpoints: paths and counts, never patch
72    /// text.
73    ///
74    /// The caller names both ends because it already knows them - a
75    /// checkpoint's oid is in the `checkpoint_committed` payload the
76    /// client folded - and computing the pair a second time on the
77    /// server would be a second answer to "which checkpoints belong to this
78    /// session". Both oids are immutable, so the answer is cacheable
79    /// forever by anybody who wants to.
80    ///
81    /// `head` absent means the working tree: a wave still running has
82    /// written files no checkpoint holds yet, and a list that ignored
83    /// them would describe the session as it was one checkpoint ago.
84    Changes {
85        base: GitOid,
86        head: Option<GitOid>,
87    },
88    /// The patch text of one file between two checkpoints.
89    ///
90    /// **A separate frame from [`Query::Changes`], and that is the
91    /// point.** `Changes` costs what the number of changed files costs
92    /// and answers which files moved; this costs what one file costs
93    /// and answers what moved inside it, the request of its own that
94    /// `storage::changes` says a hunk needs.
95    ///
96    /// One path per frame; there is no spelling that asks for all of
97    /// them. A line the credential scan matched is not echoed: the
98    /// answer reports its line number and the reason, because printing
99    /// the bytes to prove a leak is the leak.
100    Hunks {
101        oid_a: GitOid,
102        oid_b: GitOid,
103        path: String,
104    },
105    /// Which run wrote one commit the city made.
106    ///
107    /// Every commit this city makes carries five git trailers naming the
108    /// run, the resident, the model, the effort and the city itself.
109    /// Those trailers are a projection for readers outside the city, so
110    /// this query is answered from the Ledger and never from git: a city
111    /// exported and restored elsewhere, with no `.git` beside it, still
112    /// answers.
113    ///
114    /// An oid this city never wrote is [`Answer::Unavailable`], for the
115    /// reason [`Query::Changes`] gives: "I did not write it" and "it
116    /// changed nothing" are different answers.
117    Commit {
118        oid: GitOid,
119    },
120    RunView {
121        run: RunId,
122    },
123    CityView,
124    ApprovalQueue,
125    InboxView {
126        addr: Address,
127    },
128    Metrics,
129    CostView,
130    ArchiveSearch {
131        needle: String,
132    },
133    RegistryView,
134    DiscardView,
135    /// What is attached and what is chosen: the settings page's read.
136    EndpointView,
137    /// The vendors this city knows by host, and the base URL of each
138    /// face they document, so a person picks a vendor rather than
139    /// pasting its address.
140    KnownHosts,
141    /// The official harnesses, the command that starts each as an ACP
142    /// agent, and whether this machine can run it: the harness page.
143    Harnesses,
144    /// One building's own files and its archive - the pages an agent
145    /// writes for the next agent, which are also the pages a person
146    /// reads to know what happened in there.
147    BuildingView {
148        addr: Address,
149    },
150    /// What the city calls the person and the Mayor, read from the two
151    /// identity areas at the moment of asking (`crates/wire/spec/Answer/Identity.lean` §8-59).
152    Identity,
153    /// The schedule and the watch table, read at the moment of asking:
154    /// shown on the page, written by hand (`crates/wire/spec/Answer/Automation.lean` §8-62).
155    Automation,
156    /// A candidate user id: gh's login for a host (`crates/wire/spec/Answer/Github.lean` §8-67).
157    GithubLogin(Option<String>),
158    /// The first-run guide's progress (`crates/wire/spec/Guide.lean` §8-68).
159    Guide,
160    /// Who answers for this city, and what has been answered on the
161    /// person's behalf.
162    ///
163    /// Both halves in one answer because a reader needs both to make
164    /// sense of either: a list of decisions with nobody named beside it
165    /// does not say whether the person delegated them, and a delegation
166    /// with nothing decided under it does not say whether it has ever
167    /// been used.
168    Governance,
169    /// One session, folded into the rounds a person reads.
170    ///
171    /// Answered server-side, because the wire is the whole API
172    /// (ARCHITECTURE.md section 8): a second client must be able to
173    /// draw a session without reimplementing the fold.
174    ///
175    /// Bounded by [`HISTORY_MAX`](crate::HISTORY_MAX) records, which is
176    /// the same slice [`Query::RunHistory`] answers: where the fold runs
177    /// must not change how much of a session it can see.
178    Rounds {
179        run: RunId,
180    },
181    /// What one run left that somebody can check it by: the screenshots
182    /// it stored and the completions it closed plan nodes with.
183    ///
184    /// Locators, never bytes. Fetching a picture is the asset
185    /// endpoint's, for the reason [`Query::Hunks`] is separate from
186    /// [`Query::Changes`]: one question must not carry the cost of
187    /// every answer somebody might go on to want.
188    Evidence {
189        run: RunId,
190    },
191    /// What one plan node has cost, and which runs spent it.
192    ///
193    /// A node nobody claimed answers zero, not `Unavailable`: "no run
194    /// has held this node" is a true answer. The money comes from
195    /// `storage::attribution` and is priced nowhere but `gateway::cost`.
196    CostOf {
197        node: NodeId,
198    },
199    /// One directory of the city, one level deep; `None` is the root.
200    ///
201    /// The tree is the product - a building is a directory and a room
202    /// is one inside it - and this is the frame that opens a room. One level per question, so looking at a room
203    /// never pays for the ledger segments beside it.
204    Listing {
205        at: Option<Address>,
206    },
207    /// Files under `under` whose name holds `text` (`crates/wire/spec/Answer/Find.lean` §8-82).
208    Find { under: Address, text: String },
209    /// One file of the city as a version, with its first window
210    /// (`crates/wire/spec/Answer/Document.lean` §8-69). The path is an `Address`, so it cannot leave the
211    /// city root; the reserved subtree is readable on purpose, because
212    /// this door answers the person and not a resident.
213    Document {
214        at: Address,
215    },
216    /// The proposal cards still open on one document (`crates/wire/spec/Answer/Proposals.lean` §8-73).
217    Proposals(Address),
218    /// Every proposal card still open in the city, by document and
219    /// offer time (`crates/wire/spec/Answer/Proposals.lean` §8-73).
220    OpenProposals,
221    /// One window of a stored version, by its version (`crates/wire/spec/Answer/Range.lean` §8-70).
222    Range { version: B3Hash, range: documents::Span },
223    /// One document's versions, newest first (`crates/wire/spec/Answer/DocumentVersions.lean` §8-83).
224    Versions { at: Address },
225    /// One window of a stored object's bytes (`crates/wire/spec/Answer/DocumentBytes.lean` §8-80).
226    Bytes { version: B3Hash, range: documents::Span },
227    /// A stored Markdown version as one HTML file (`crates/wire/spec/Answer/DocumentBytes.lean` §8-81).
228    Export { at: Address, version: B3Hash },
229    /// One window of a stored Markdown version, laid out (`crates/wire/spec/Answer/Preview.lean` §8-74).
230    Preview { version: B3Hash, viewport: documents::Span },
231    /// A reply's text, laid out by the preview's grammar (`crates/wire/spec/Answer/Preview.lean` §8-75).
232    Reply {
233        text: String,
234        state: documents::ReplyState,
235    },
236    /// The commits this city made, newest first, a page at a time.
237    ///
238    /// `building` keeps the commits whose actor worked at that address
239    /// or under it - the actor is the authority and a session is its
240    /// projection, so filtering by session would lose the runs nobody
241    /// opened a session for. `before` is exclusive, as in
242    /// [`Query::History`]; the next page asks with the last `seq` it was
243    /// handed. Only commits this city wrote are listed: an oid a person
244    /// rewrote into trunk by hand is not this city's, and it says so by
245    /// leaving it out rather than reading a trailer back.
246    Commits {
247        building: Option<Address>,
248        before: Option<Seq>,
249        limit: u32,
250    },
251    /// What this machine has, and what this city still needs of it.
252    ///
253    /// Answered from what the city found when it started, not from a
254    /// fresh look: every item is a program asked its version, and a
255    /// query that started a dozen processes would hold the one thread
256    /// that answers every other read. A city that has not looked
257    /// answers [`Answer::Unavailable`], which is what a worker driven
258    /// one command at a time is.
259    Doctor,
260    /// The system prompt one run was frozen with: four segments, their
261    /// text, and what each was assembled from.
262    ///
263    /// Answered from the run's own `prompt_assembled` record joined to
264    /// the store, never by assembling a prefix again: what a person
265    /// needs to see is the bytes that were sent, and a second assembly
266    /// taken now would read files that have moved since. A segment the
267    /// store no longer holds says so by name rather than by an empty
268    /// string, because "this segment was empty" and "these bytes are
269    /// gone" are different answers.
270    Prefix {
271        run: RunId,
272    },
273    /// One object of the content store, bounded, with the cut stated.
274    ///
275    /// The general read behind [`Query::Prefix`], and the door every
276    /// other `cas:` reference a page is shown can be opened through -
277    /// an approval's artifact, a discarded file's way back, a norm on a
278    /// handoff's must-read list. Only the `cas:` scheme is answered: a
279    /// `file:` locator names a path in the tree, which is
280    /// [`Query::Document`]'s question and must not have a second answer
281    /// here.
282    Content {
283        locator: Locator,
284    },
285    /// What one building can do, and where each of those came from.
286    ///
287    /// Every shelf in one answer because a reader needs them together
288    /// to make sense of any: a row carries [`crate::SkillShelf`], which
289    /// names the shelves and where each row sits on one. Which runs
290    /// pinned a skill is folded from `run_started`, so a shelf nothing
291    /// has ever used says so instead of looking unused because nobody
292    /// wrote it down.
293    Skills { building: Address },
294    /// What is uncommitted in one building right now: the branch, how
295    /// far it has drifted from its upstream, the files that moved, and
296    /// the last checkpoint the city checkpointed there.
297    ///
298    /// Read from git at the moment of asking, which is the one answer
299    /// here that is about the disk rather than about the history: a
300    /// working tree is what a person is looking at, and the Ledger
301    /// records checkpoints rather than edits. The checkpoint beside it comes
302    /// from the history, for the reason [`Query::Commit`] gives.
303    GitStatus { building: Address },
304    /// Whether each tool server one address reaches is answering, and what it offers, handshaken
305    /// once at the moment of asking (`crates/wire/spec/Answer/McpHealth.lean` §8-34).
306    McpHealth {
307        addr: Address,
308    },
309    /// Which outside applications the broker offers, and where each one stands for this city
310    /// (`crates/wire/spec/Answer/Toolkits.lean` §8-35b).
311    Toolkits,
312    /// Which release this city is running, and which ones npm and crates.io offer
313    /// (`crates/wire/spec/Answer/Release.lean` §8-36).
314    NewestRelease,
315    /// The host's privacy controls, their current values and what this
316    /// app's history discloses to the account asking
317    /// (`crates/wire/spec/Privacy.lean` §8-88). Answered by the city's
318    /// listener, never through a remote device.
319    Privacy,
320    /// Everything this person settled about their own reading of the
321    /// city: the language, the appearance, the chords they rebound.
322    ///
323    /// The whole table in one answer, because a browser that cached
324    /// these row by row had a dozen names for them and read three of
325    /// those names back with its own idea of what a missing one means.
326    /// The file is the authority and this is the only door to it.
327    Preferences,
328    /// What one address is actually governed by, value by value, with
329    /// the file each value came from.
330    ///
331    /// **The layer is half the answer.** The three files form a ladder
332    /// - the city's, the building's, the room's - and a page told only
333    /// the resolved figure cannot say whether it is looking at
334    /// something this address states or something it inherited, so it
335    /// would have to read all three and climb the ladder a second
336    /// time. Two climbs of one ladder is two answers to one question.
337    Config {
338        addr: Address,
339    },
340    /// What each named run has been billed, for the runs a cost view's
341    /// `by_run` leaves out. At most
342    /// [`RUN_COSTS_MAX`](crate::RUN_COSTS_MAX) runs are answered per
343    /// question and the rest go unanswered, so a directory that names
344    /// its runs newest first shows the cost of the newest cold runs; the
345    /// money is folded from the Ledger for a run the city no longer
346    /// holds warm, so the cost is proportional to the runs asked about.
347    RunCosts {
348        runs: Vec<RunId>,
349    },
350    /// The newest release of one requirement-table item, asked of its
351    /// publisher (`crates/sprawling/Spec.lean` §8-120).
352    ///
353    /// Asked by the dependency page for each item once the page is open,
354    /// which is the person asking whether their tools are behind; the
355    /// city asks nothing on its own schedule. One item per question, so
356    /// a slow publisher holds up its own row and no other. A source that
357    /// cannot be reached is [`DoctorNewest::Refused`](crate::DoctorNewest::Refused)
358    /// rather than [`Answer::Unavailable`](crate::Answer::Unavailable).
359    UpstreamVersion {
360        item: String,
361    },
362    /// Every skill's contents, audit and uses by day, folded from the whole ledger when the
363    /// skill page opens (`crates/wire/spec/Reading.lean` D28, D33); `skill` narrows it to one.
364    SkillUsage { skill: Option<String> },
365    /// The same table per tool server and tool; `server` narrows it to one.
366    McpUsage { server: Option<String> },
367    /// Every use as one JSONL or CSV row for the User to download; the city writes no file.
368    UsageExport { what: crate::UsageKind, format: crate::ExportFormat },
369    /// Every shell interpreter's calls and failures by class, folded from the whole ledger in
370    /// the same pass as the usage tables (`crates/wire/spec/Reading.lean` D48).
371    Shells,
372}
373
374/// The Query surface, in declaration order — the order the handshake
375/// hash mixes these names in.
376pub const QUERY_NAMES;
377}