1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
//! The query chokepoint (§8.5): every boundary read, over the published
//! [`Snapshot`] + the durable `ui.json` ([`UiState`]) + the caller's clock —
//! **the snapshot derivation run without a frame** (VISION §4.8). The frame's
//! view-models ([`AppModel`](crate::AppModel)) delegate to these same
//! functions, which is the parity discipline: one implementation, two
//! serializations — the GUI renders the returned rows, the headless transport
//! encodes them ([`super::reply`]).
//!
//! **Most queries are pure over the snapshot; the §9 config family's three
//! (bl-0164) are not** — a destination's bytes, the §16.3 knob and brazen's
//! provider table are read from the world at the moment they are asked,
//! exactly as their write already is (§8.5's "asked, never stored"), so
//! [`answer`] takes the same [`Deps`] [`dispatch`](super::dispatch::dispatch)
//! does rather than the bare snapshot alone — `Deps` already carries one
//! ([`Deps::snapshot`]). That is also why this can refuse: a config read can
//! fail exactly as its write can (an unreadable file, an unprimed project).
//!
//! Also home to the §3.6 confirmation derivation both dispatch and the dialog
//! read — the delete gate is one derivation wherever it is asked.
use crate::ui_state::UiState;
use super::dispatch::Deps;
use super::reply::Reply;
use super::{Query, config, help, login};
/// One conversation as a seat sees it (REMOTE §9.4, bl-1eb0) — the
/// [`Agent`](crate::git_tree::Agent)'s wire projection.
pub mod agent;
/// One workspace's bound balls with their §3.5 figures (REMOTE §9.7, bl-b4b5).
pub mod balls;
/// The §11 altitude-0 chrome: the enumeration, its §6 rollups, and how current
/// the derivation behind them is.
mod chrome;
/// The §3.6 unmaking's own derivations — what a delete would destroy, read by
/// the dialog and by the dispatch gate alike.
mod confirm;
/// The §11 conversation list of one workspace and the two reads that hang off
/// it — split off at §12's budget (bl-c088): everything left here is the
/// `Query` table, and these three are a derivation it calls.
pub mod convs;
/// The §11 inspector family (bl-6233): one conversation's transcript, steps,
/// files, spine and mail — the derivations the frame's view-models delegate to.
pub mod inspector;
/// The §6 decision queue: the roster both frontends walk, the queue it filters
/// to, and the acknowledgement that answers one row (VISION §5 V5.2).
pub mod queue;
pub use chrome::{workspace_stats, workspaces, ws_rows};
pub use confirm::{agent_confirmation_of, confirmation_of};
pub use convs::{conv_ball, conversations, names_in};
/// Answer one query (§8.5). Total over [`Query`]; `now_unix` is the caller's
/// wall clock (minted at the process boundary, so the derivation stays
/// clock-free and deterministic under test). `Err` is a refusal — the same
/// class [`dispatch`](super::dispatch::dispatch) returns, and for the same
/// reason: the three config reads can fail exactly as their writes can.
pub fn answer(query: &Query, deps: &Deps, ui: &UiState, now_unix: i64) -> Result<Reply, String> {
let snap = &deps.snapshot;
// **The one resolution** (REMOTE §8, bl-f5f6). Every read that names a
// workspace names it by *name*; this turns that name into the path the
// derivations below read, once, ahead of the table — never once per arm.
// A query that names no workspace resolves to nothing and no arm reads it:
// the general path with no input, not a case of its own.
let ws: &std::path::Path = &match query.workspace() {
Some(name) => snap.ws_path(&name)?,
None => std::path::PathBuf::new(),
};
// **And the conversation's** (bl-49bc), on the same terms one noun down:
// the §11 inspector family and the seat's own read are aimed at an agent
// **id**, and a `Started` receipt hands back a *name* — so the resolution
// stands here too, once, ahead of the table. A read naming no conversation
// resolves to nothing and no arm reads it.
let agent: &str = &super::address::resolve_agent(snap, ws, query.agent())?;
// The workspace's pre-walked bills (bl-9dd4), empty for one the derivation
// has not billed — what the two priced reads below join their money off.
let bills: &[crate::budgets::StepBill] = snap.bills.get(ws).map_or(&[], Vec::as_slice);
Ok(match query {
Query::Workspaces => Reply::Workspaces(workspaces(snap, ui, now_unix)),
Query::Conversations { .. } => Reply::Conversations(conversations(snap, ui, ws, now_unix)),
Query::Balls => Reply::Balls(snap.join_rows.clone()),
// The same binding facts one workspace deep, with each ball's figure
// (REMOTE §9.7, bl-b4b5) — the §11 balls section's whole content.
Query::WorkspaceBalls { .. } => Reply::WorkspaceBalls(balls::ws_balls(snap, ui, ws)),
// The V4 board — the same snapshot, one altitude up. The window's
// `AppModel::board` is this same call (§8.5's parity discipline).
Query::Board => Reply::Board(crate::board::build(snap, ui, now_unix)),
// The §6 attention strip made addressable (VISION §5 V5.2) — the same
// predicate the window counts, listed.
Query::Attention => Reply::Attention(queue::queue(snap, ui, now_unix)),
// The one query with no world to read (§8.5): its subject is the
// interface. The consumer answers it exactly as any seat does — same
// function, so the deposited spelling and the typed one cannot differ.
Query::Help { verb } => Reply::Help(help::rows(verb.as_deref())),
// The one query whose subject is the world's bytes rather than this
// snapshot's derivations (§8.5): the snapshot says where everything is
// and [`search::run`] re-reads it. Answered straight through here
// because every seat that reaches this function is already off-frame —
// the deposit consumer's thread, or a `yog gesture` process with
// nothing else to do, so nothing supersedes it and nothing waits.
// The window's seat is [`AppModel::answer`](crate::AppModel::answer),
// which asks its searcher instead and renders the landed answer.
Query::Search { text } => Reply::Search(crate::search::run(snap, text, &|| true)),
// The other world-bytes query (§5.1 #32): the snapshot says which
// balls this workspace claims, the project repos say what changed.
// Answered straight through for the same reason search is — every
// seat reaching here is already off-frame.
Query::WorkDiff { file, .. } => {
// The claim rows come off the snapshot; the fan's candidate rows
// (bl-c2bd) come off the same two yog-owned facts the §8.6
// writable root reads — the trail's claim row and its fire rows —
// so the trail is read here, where the question is asked.
let entries = crate::opslog::tail(&deps.state_root, usize::MAX);
// The world, not one layout: an attempt's balls paths resolve
// through **its own project's** space (§16.2, bl-262a), and one
// workspace's attempts may sit in two bundles.
let attempts = crate::workdiff::read(snap, ws, &entries, &deps.world);
let patch = file
.as_ref()
.and_then(|f| crate::workdiff::patch(snap, &attempts, f));
Reply::WorkDiff { attempts, patch }
}
// The projection over those same attempts (§3.9, bl-40ab): the diff
// rows joined with the conversation each attempt was bound to. It reads
// the trail for the same reason the work diff does — the binding
// pointer is a fire row — and the step-record columns off the
// snapshot's own pre-walked bills, so the join costs no second pass.
Query::Science { .. } => {
let entries = crate::opslog::tail(&deps.state_root, usize::MAX);
Reply::Science(crate::science::project(
snap,
ws,
&entries,
&deps.world,
&ui.prices(),
))
}
// The §11 inspector family (bl-6233, REMOTE §9 step 1): the
// conversation's own reads, which had no headless spelling at all —
// so no seat but the window could read a chat. World-bytes queries
// like the two above, answered straight through for the same reason,
// over the derivations in [`inspector`] the frame delegates to.
Query::Transcript { .. } => {
Reply::Transcript(inspector::transcript(snap, ws, agent, now_unix))
}
// The follow lane's read, answered **once** (REMOTE §3, bl-73e7): this
// is the chokepoint every intake shares, and most intakes cannot hold a
// connection open — so what they get is the tail as of now, which is
// the general path with one frame rather than a degraded answer. The
// holding is `follow::Follow`, driven by the one intake that can, and
// its frames come off this same fold.
// The tool window rides the same one look (bl-5305), and on the lane's
// own two liveness questions: the prose only while a model call
// streams, the window for as long as a driver holds the lease — which
// is the span the step's tools run in. A conversation nobody is working
// has neither, which is an empty frame rather than a case of its own.
Query::Follow { .. } => Reply::Follow(super::follow::once(snap, ws, agent)),
// The sign-in lane's read, answered **once** (REMOTE §8.3, bl-c285) —
// the `Follow` shape exactly: this chokepoint answers the standing as
// of now, and the intake that can hold a connection drives
// `login::Lane`, whose frames come off this same buffer.
Query::LoginTail { provider, .. } => login::standing(deps, ws, provider),
// The rows priced off the snapshot's walk (§3.5, bl-53d1): the list is
// read live, the money is the worker's, and the join is one filter.
Query::Steps { .. } => Reply::Steps(inspector::steps(snap, ws, agent, now_unix).priced(
bills,
agent,
&ui.prices(),
)),
Query::Step { seq, .. } => Reply::Step(crate::steps_view::detail(ws, agent, seq)?),
// The listing, plus **where this conversation's work actually lands**
// when that is not the worktree the listing walked (bl-1015): a path
// or ball rung seeds litany's cwd mark at creation, so every tool step
// runs at the target and the agent worktree holds none of the work.
// The listing alone promised work products and named none of that.
Query::Files { path, at, .. } => {
let (view, preview) = inspector::files(ws, agent, path.as_deref(), at.as_deref());
Reply::Files {
view,
preview,
working_dir: inspector::working_dir(ws, agent),
}
}
Query::Rail { .. } => {
let steps =
inspector::steps(snap, ws, agent, now_unix).priced(bills, agent, &ui.prices());
let tx = inspector::transcript(snap, ws, agent, now_unix);
Reply::Rail(inspector::rail(snap, ws, agent, &steps, &tx))
}
Query::Inbox { .. } => Reply::Inbox(crate::inboxview::list_inbox(ws, agent)),
// Which config governs (VISION V1.2, bl-13f9; follow-the-tip,
// bl-e654): the §5.1 #17 derivation asked at whichever commit the seat
// named, the agent's tip when it named none. It **refuses** where its
// siblings answer absent, because its walk is the workspace's own git
// and a conversation with no policy at all is not a reading (the
// `Lineages` shape).
// The §9.4 workflow mark rides beside it (bl-b680), read live off the
// agent's descent whatever `at` named: a mark is standing state now,
// not a fact of the commit a pin selects.
Query::Governing { at, .. } => {
return Ok(Reply::Governing {
config: inspector::governing(snap, ws, agent, at.as_deref())?,
workflow_mark: crate::config_edit::branch::workflow_mark::read(ws, agent)
.map_err(|e| e.to_string())?,
});
}
// The seat's own read of its selection (REMOTE §9.4, bl-1eb0) — pure
// over the snapshot, unlike the five above, because everything it says
// was already derived when the tree was.
Query::Agent { .. } => Reply::Agent(agent::agent(snap, ui, ws, agent, now_unix)),
// The §4.2 trail, each row **with its standing** (§7.3, bl-4d81): §6's
// retirement projection folded with the ack watermark, computed here
// rather than by every seat. The slice is taken first and the fold runs
// over it, which changes no answer — retirement reads only later rows,
// and an ack line dropped with the prefix leaves every remaining row
// after it, which is what those rows already were.
Query::Ops { max } => {
let skip = snap.ops.len().saturating_sub(*max);
let tail: Vec<crate::opslog::OpRow> = snap.ops.iter().skip(skip).cloned().collect();
Reply::Ops(crate::opslog::standings(&tail))
}
// The §9 config family's reads (§8.5, bl-0164): asked of the world at
// the moment they are asked, exactly as the writes beside them are.
// One arm since bl-719a, six members one level down: the §9.3 browse
// and the §9.4 roster are on the same terms as the three beside them —
// asked of the world, this workspace's git and this wall's brazen, at
// the moment they are asked, and answered straight through because
// every seat here is already off-frame.
Query::Config(read) => {
return match read {
config::Read::File { file } => config::file(deps, ws, file),
config::Read::Marks { .. } => Ok(config::read_marks(deps, ws)),
config::Read::Providers { .. } => Ok(config::providers(deps, ws)),
config::Read::Lineages { .. } => config::lineages(ws),
config::Read::Models { provider, .. } => config::models(deps, ws, provider),
config::Read::Roles { .. } => Ok(config::roles(ws)),
config::Read::Proposals { id, .. } => config::proposals(ws, id.as_deref()),
};
}
// REMOTE §5's roster (bl-4e08): the §4.1 registration listing, the
// wire's presence RAM and each client's advertised set, joined at the
// moment they are asked. It reads the *name* rather than the resolved
// path, because a registration is keyed by name — and the resolution
// above still stands, so an unregistered workspace refuses in the
// resolver's own words before this runs.
Query::Clients { workspace } => Reply::Clients(crate::registry::roster::roster(
&deps.state_root,
&deps.caller.presence,
workspace,
)),
// **Is this box wired up?** (bl-28f4) — every check at once, each row
// carrying what was read and, when it fails, the act that fixes it. It
// takes the name AND the path because its rows say both: the wall gate
// reads the path, and the sentence names the workspace the operator
// typed. A doctor that named no workspace answers the engine's own
// checks alone, which is the state the box it exists for is in.
Query::Doctor { workspace } => Reply::Doctor(crate::doctor::report(
deps,
workspace.as_deref().map(|name| (name, ws)),
)),
// REMOTE §3's routing leg (bl-024b): the follow-class read that waits
// for this client's next work, and the asker's poll for what one
// captured. Neither names a world, so neither reads the resolution
// above.
Query::Invocations => return super::routing::invocations(deps),
Query::Capture { invocation } => return super::routing::capture(deps, invocation),
// The price table, the ceiling and the world's ledger (§3.5, bl-53d1)
// — world facts, addressed to no workspace, answered by the module
// that also writes them so a read and a receipt are one derivation.
Query::Prices => Reply::Prices(super::spend::view(deps, ui, None)),
})
}
#[cfg(test)]
mod tests;