Skip to main content

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 person's decision on proposal cards of one document, landed as one
31/// save (`crates/wire/spec/Answer/Proposals.lean` §8-73).
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
34pub struct ProposalDecisions {
35    pub doc: Address,
36    pub decisions: Vec<ProposalDecision>,
37    pub idem: IdemKey,
38}
39
40/// One card and the verdicts on its changed sentences; a sentence left
41/// unnamed is rejected.
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
44pub struct ProposalDecision {
45    pub proposal: B3Hash,
46    pub verdicts: Vec<SliceVerdict>,
47}
48
49/// A building's whole `RULES.toml` from a page, and the text the page
50/// read: the city evaluates `body` before it lands, and only over `base`
51/// (`crates/wire/spec/Command/Step.lean` §8-60).
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
54pub struct RulesWrite {
55    pub building: Address,
56    pub base: String,
57    pub body: String,
58    pub idem: IdemKey,
59}
60
61/// The city's own layer from the settings page: `None` leaves a key as
62/// it is (`crates/wire/spec/Command/Step.lean` §8-61).
63#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
64#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
65pub struct CitySettings {
66    pub keep_warm: Option<KeepWarm>,
67    pub effort: Option<Effort>,
68    pub idem: IdemKey,
69}
70
71/// What a new session at an address keeps from the previous one.
72///
73/// An enum rather than a flag: the two states are named actions with
74/// different results on disk, and `carry: true` at a call site says
75/// neither of them. `Nothing` is the first variant and the default,
76/// because that is what a person means by starting a new session — a
77/// new one, here, not a continuation (`crates/sprawling/Spec.lean` §8-82). The
78/// handoff is the exception a person states.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
80#[serde(rename_all = "snake_case")]
81#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
82pub enum Carry {
83    /// Forget the shape the last session froze, and carry no summary.
84    #[default]
85    Nothing,
86    /// Forget the shape, keep the summary the last session wrote.
87    ///
88    /// The shape still goes, which is the point of the verb: a person
89    /// who carries the summary changed the model, and a room that kept
90    /// its frozen shape could not dispatch at all.
91    Handoff,
92}
93
94/// What a Halt, Release or Autonomy change applies to. Unlike modes and
95/// providers, this set is the protocol's own and has no upstream owner.
96#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
97#[serde(rename_all = "snake_case")]
98#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
99pub enum HaltScope {
100    City,
101    Building(Address),
102    Workshop(Address),
103}
104
105/// Which of the three documents that govern a city a `PutDocument`
106/// frame carries.
107///
108/// A closed set rather than a path, because where these files live is
109/// the city's answer and not the sender's: all three sit in the city's
110/// own reserved subtree, which no write domain reaches. A frame naming
111/// its own path would be a way to write anywhere inside the one place a
112/// resident may not edit.
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
114#[serde(rename_all = "snake_case")]
115#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
116pub enum GovernedDocument {
117    /// Who the Mayor is: `MAYOR.md`.
118    Mayor,
119    /// What the clerk answers by: `CLERK.md`.
120    Clerk,
121    /// How this person wants their city run: `PREFERENCES.md`. It
122    /// belongs to no resident, which is why it sits beside the other two
123    /// rather than at somebody's address.
124    Preferences,
125}
126
127/// One identity card's values (`crates/wire/spec/Answer/Identity.lean` §8-59).
128///
129/// Each key is the card's own: `None` removes it, which puts the default
130/// name back. What a name may be is the city's answer (`city::Naming`),
131/// so a value here is the text the person typed.
132#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
133#[serde(rename_all = "snake_case")]
134#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
135pub enum IdentityCard {
136    /// `PREFERENCES.md`: what the city calls the person, the host the
137    /// name was imported from, and, when `about` is `Some`, the body
138    /// that says what every agent should know about them.
139    Person {
140        user_id: Option<String>,
141        imported_from: Option<String>,
142        about: Option<String>,
143    },
144    /// `MAYOR.md`: what the Mayor is called. The body under the area is
145    /// left as it is.
146    Mayor { name: Option<String> },
147}
148
149/// Which of a building's own spine documents a write carries.
150///
151/// Named rather than addressed, for the reason [`GovernedDocument`]
152/// gives: where these files live is the city's answer and not the
153/// sender's. Unlike the three documents that govern a city, these have
154/// a second writer — a resident reaches `Roadmap.md` through `plan` and
155/// the others through `edit` — so a write to one of them is a write that
156/// can lose a race, and the caller says which text it started from.
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
158#[serde(rename_all = "snake_case")]
159#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
160pub enum SpineDocument {
161    /// The plan and the only source of progress in a building.
162    Roadmap,
163    /// The notepad: whatever needs recording and has no other home.
164    Memo,
165    /// What the next session needs and cannot get from the files.
166    Handoff,
167    /// What this project is and the decisions it holds.
168    Spec,
169}
170
171/// What a `Pursue` command does to a pursuit.
172///
173/// `Clear` and `Pause` are different actions and both exist: pausing
174/// keeps the goal so it can be taken up again, and clearing throws it
175/// away. Cancelling a *run* is a third thing again, and it has its own
176/// command.
177#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
178#[serde(rename_all = "snake_case")]
179#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
180pub enum PursuitStep {
181    /// Declare one, replacing any goal this building already had.
182    Set {
183        goal: String,
184    },
185    Pause,
186    Resume,
187    Clear,
188}