wire/command/step.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//! The values a Command carries beside its scalars, and the closed sets: which scope a halt applies to, which governed document is being written,
7//! what is being done to a pursuit, and what a new session keeps from
8//! the one before it.
9//!
10//! Each of them is the protocol's own vocabulary with no upstream
11//! owner, which is what separates them from the carried names in
12//! `wire::carried_name`: those defer to whoever owns the value set,
13//! and these have no one to defer to.
14
15use kernel::event::record::SliceVerdict;
16use kernel::{Address, B3Hash, Effort, IdemKey, KeepWarm};
17use serde::{Deserialize, Serialize};
18
19/// A page's save of one document: edits made on the version `baseline`,
20/// refused once that version has moved (`crates/wire/spec/Command/Step.lean` §8-72).
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
23pub struct RangeWrite {
24 pub doc: Address,
25 pub baseline: B3Hash,
26 pub edits: Vec<documents::TextEdit>,
27 pub idem: IdemKey,
28}
29
30/// A display name for the session of `room` that began at `began`; an
31/// empty name takes it back (`crates/wire/spec/Answer/Sessions.lean` D27).
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
34pub struct SessionNaming {
35 pub room: Address,
36 pub began: kernel::Seq,
37 pub name: String,
38 pub idem: IdemKey,
39}
40
41/// A room's new run policy, which the run under way takes at its next
42/// safe point (`crates/wire/spec/Answer/Sessions.lean` D27).
43#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
45pub struct PolicyChange {
46 pub room: Address,
47 pub policy: kernel::model::RunPolicy,
48 pub idem: IdemKey,
49}
50
51/// What `OpenRemoteDoor` asks for: the remote door open for `lasting_ms`, one minute to seven
52/// days. The request does nothing by itself; the city prints a code at its own console and
53/// refuses the frame with `E_APPROVAL_PENDING` (`crates/wire/spec/Command/Kind.lean`,
54/// remote_access D4).
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
56#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
57pub struct DoorOpening {
58 pub lasting_ms: u64,
59 pub idem: IdemKey,
60}
61
62/// What `ConfirmRemoteDoor` carries: the code the city's console printed, read however it was
63/// retyped. Every answer, right or wrong, ends the request it answers.
64#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
66pub struct DoorAnswer {
67 pub code: String,
68 pub idem: IdemKey,
69}
70
71/// A door verb with nothing to say but its key: `ReplaceCityKey`, guarded the way opening is,
72/// and `CloseRemoteDoor`, unguarded because closing only takes access away (remote_access D5).
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
75pub struct DoorStep {
76 pub idem: IdemKey,
77}
78
79/// A person's decision on proposal cards of one document, landed as one
80/// save (`crates/wire/spec/Answer/Proposals.lean` §8-73).
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
82#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
83pub struct ProposalDecisions {
84 pub doc: Address,
85 pub decisions: Vec<ProposalDecision>,
86 pub idem: IdemKey,
87}
88
89/// One card and the verdicts on its changed sentences; a sentence left
90/// unnamed is rejected.
91#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
92#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
93pub struct ProposalDecision {
94 pub proposal: B3Hash,
95 pub verdicts: Vec<SliceVerdict>,
96}
97
98/// A building's whole `RULES.toml` from a page, and the text the page
99/// read: the city evaluates `body` before it lands, and only over `base`
100/// (`crates/wire/spec/Command/Step.lean` §8-60).
101#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
102#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
103pub struct RulesWrite {
104 pub building: Address,
105 pub base: String,
106 pub body: String,
107 pub idem: IdemKey,
108}
109
110/// The city's own layer from the settings page: `None` leaves a key as
111/// it is (`crates/wire/spec/Command/Step.lean` §8-61).
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
113#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
114pub struct CitySettings {
115 pub keep_warm: Option<KeepWarm>,
116 pub effort: Option<Effort>,
117 pub idem: IdemKey,
118}
119
120/// What a new session at an address keeps from the previous one.
121///
122/// An enum rather than a flag: the two states are named actions with
123/// different results on disk, and `carry: true` at a call site says
124/// neither of them. `Nothing` is the first variant and the default,
125/// because that is what a person means by starting a new session — a
126/// new one, here, not a continuation (`crates/sprawling/Spec.lean` §8-82). The
127/// handoff is the exception a person states.
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
129#[serde(rename_all = "snake_case")]
130#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
131pub enum Carry {
132 /// Forget the shape the last session froze, and carry no summary.
133 #[default]
134 Nothing,
135 /// Forget the shape, keep the summary the last session wrote.
136 ///
137 /// The shape still goes, which is the point of the verb: a person
138 /// who carries the summary changed the model, and a room that kept
139 /// its frozen shape could not dispatch at all.
140 Handoff,
141}
142
143/// What a Halt, Release or Autonomy change applies to. Unlike modes and
144/// providers, this set is the protocol's own and has no upstream owner.
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
146#[serde(rename_all = "snake_case")]
147#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
148pub enum HaltScope {
149 City,
150 Building(Address),
151 Workshop(Address),
152}
153
154/// Which of the three documents that govern a city a `PutDocument`
155/// frame carries.
156///
157/// A closed set rather than a path, because where these files live is
158/// the city's answer and not the sender's: all three sit in the city's
159/// own reserved subtree, which no write domain reaches. A frame naming
160/// its own path would be a way to write anywhere inside the one place a
161/// resident may not edit.
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
163#[serde(rename_all = "snake_case")]
164#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
165pub enum GovernedDocument {
166 /// Who the Mayor is: `MAYOR.md`.
167 Mayor,
168 /// What the clerk answers by: `CLERK.md`.
169 Clerk,
170 /// How this person wants their city run: `PREFERENCES.md`. It
171 /// belongs to no resident, which is why it sits beside the other two
172 /// rather than at somebody's address.
173 Preferences,
174}
175
176/// One identity card's values (`crates/wire/spec/Answer/Identity.lean` §8-59).
177///
178/// Each key is the card's own: `None` removes it, which puts the default
179/// name back. What a name may be is the city's answer (`city::Naming`),
180/// so a value here is the text the person typed.
181#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
182#[serde(rename_all = "snake_case")]
183#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
184pub enum IdentityCard {
185 /// `PREFERENCES.md`: what the city calls the person, the host the
186 /// name was imported from, and, when `about` is `Some`, the body
187 /// that says what every agent should know about them.
188 Person {
189 user_id: Option<String>,
190 imported_from: Option<String>,
191 about: Option<String>,
192 },
193 /// `MAYOR.md`: what the Mayor is called. The body under the area is
194 /// left as it is.
195 Mayor { name: Option<String> },
196}
197
198/// Which of a building's own spine documents a write carries.
199///
200/// Named rather than addressed, for the reason [`GovernedDocument`]
201/// gives: where these files live is the city's answer and not the
202/// sender's. Unlike the three documents that govern a city, these have
203/// a second writer — a resident reaches `Roadmap.md` through `plan` and
204/// the others through `edit` — so a write to one of them is a write that
205/// can lose a race, and the caller says which text it started from.
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
207#[serde(rename_all = "snake_case")]
208#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
209pub enum SpineDocument {
210 /// The plan and the only source of progress in a building.
211 Roadmap,
212 /// The notepad: whatever needs recording and has no other home.
213 Memo,
214 /// What the next session needs and cannot get from the files.
215 Handoff,
216 /// What this project is and the decisions it holds.
217 Spec,
218}
219
220/// What a `Pursue` command does to a pursuit.
221///
222/// `Clear` and `Pause` are different actions and both exist: pausing
223/// keeps the goal so it can be taken up again, and clearing throws it
224/// away. Cancelling a *run* is a third thing again, and it has its own
225/// command.
226#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
227#[serde(rename_all = "snake_case")]
228#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
229pub enum PursuitStep {
230 /// Declare one, replacing any goal this building already had.
231 Set {
232 goal: String,
233 },
234 Pause,
235 Resume,
236 Clear,
237}