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
//! **What a gesture addresses** (§8.2, REMOTE §8): the two tables over
//! [`Action`] that answer *which workspace* and *which project* it names. Its
//! own file at §12's 300-line cap (bl-dc0c, widened to both nouns by bl-f5f6);
//! each was always a query on the enum rather than a part of it.
//!
//! Both answer **names**, because that is what the boundary now carries: a
//! path is meaningless across machines and a disclosure besides (REMOTE §8).
//! One table per noun means the resolution stands **once, ahead of the
//! dispatch table** ([`dispatch`](super::dispatch::dispatch)) instead of being
//! re-derived inside twenty arms — and it is the same table the frame's
//! after-verb refresh reads, so "which project did that touch" has one answer
//! wherever it is asked.
use super::{Action, Gesture, Query};
impl Gesture {
/// The workspace this gesture names, whichever half it is — the two tables
/// below, read as one (bl-8bbc).
///
/// The wire's scoped intake asks it: a gesture's address is what
/// authorization is decided over (REMOTE §4), and asking it here means the
/// scope and the dispatch chokepoint read the **same** table rather than
/// two that could disagree about which workspace a variant names.
pub fn workspace(&self) -> Option<String> {
match self {
Gesture::Act(action) => action.workspace(),
Gesture::Ask(query) => query.workspace(),
}
}
}
impl Action {
/// The **workspace** this gesture names (§3.1), or `None` when it names
/// none — the ack, the trail clear, and the two `bl`-only families. The
/// nested payloads answer through it: a deferred prompt and a fan both
/// name the workspace their [`Prepared`](crate::start::Prepared) was
/// prepared in, and the monitor/fleet verbs name their own.
pub fn workspace(&self) -> Option<String> {
match self {
Action::Message { workspace, .. }
| Action::Stop { workspace, .. }
| Action::Interrupt { workspace, .. }
| Action::Scan { workspace }
| Action::Nudge { workspace, .. }
| Action::Retarget { workspace, .. }
| Action::Fork { workspace, .. }
| Action::Prepare { workspace, .. }
| Action::DeleteWorkspace { workspace, .. }
| Action::DeleteAgent { workspace, .. }
| Action::MarkSeen { workspace, .. }
| Action::SetMarks { workspace, .. }
| Action::PickModel { workspace, .. }
| Action::AnswerHold { workspace, .. }
| Action::Floor { workspace, .. } => Some(workspace.clone()),
Action::Prompt { prepared, .. }
| Action::Fan(crate::fan::Verb::Spread { prepared, .. }) => {
Some(prepared.workspace.clone())
}
Action::Monitor(verb) => Some(verb.workspace()),
Action::Fleet(verb) => Some(verb.workspace()),
// An advertisement names its CLIENT, never a workspace (REMOTE §5,
// bl-4e08): a tool set is a fact about the machine, and which
// workspaces see it is the registration listing that already exists.
// The routing leg's two, for the advertisement's reason exactly
// (bl-024b): a call addresses a MACHINE, and the queue of calls to
// one is a fact about that machine, not about a workspace.
Action::Advertise { .. }
| Action::Route(_)
// The §9 config family answers through its destination instead
// ([`config::ConfigFile::workspace`](super::config::ConfigFile)):
// two of the five name a wall and three name no world at all, so
// the table would have to read the file to answer here anyway.
| Action::ApplyConfig { .. }
| Action::Close { .. }
| Action::Assign { .. }
| Action::Release { .. }
| Action::Move { .. }
| Action::Create { .. }
| Action::Update { .. }
| Action::Fan(
crate::fan::Verb::Retire { .. } | crate::fan::Verb::Deliver { .. },
)
| Action::Ack
| Action::ClearTrail => None,
}
}
/// The project a `bl`-family action mutates — the §8.2 after-verb ball
/// refresh target. `None` for the lernie/workspace families.
pub fn project(&self) -> Option<String> {
match self {
Action::Close { project, .. }
| Action::Assign { project, .. }
| Action::Release { project, .. }
| Action::Move { project, .. }
| Action::Create { project, .. }
| Action::Update { project, .. } => Some(project.clone()),
// A fan claims nothing, a retirement delivers nothing, and a
// delivery closes nothing — but all three act in a project's refs,
// and the §3.5 projection reads that project's board, so all three
// refresh it.
Action::Fan(
crate::fan::Verb::Spread { obligation, .. }
| crate::fan::Verb::Retire { obligation, .. }
| crate::fan::Verb::Deliver { obligation, .. },
) => Some(obligation.project.clone()),
Action::Prepare { payload, .. } => payload.project(),
// `SetMarks` named a project until the per-agent ruling re-keyed
// it to the agent (§16.3): it now repoints one agent's OWN space,
// which is a different clone bundle from the one the §3.5
// projection reads, so no board row can move because of it.
Action::SetMarks { .. }
| Action::Message { .. }
| Action::Stop { .. }
| Action::Interrupt { .. }
| Action::Scan { .. }
| Action::Nudge { .. }
| Action::Retarget { .. }
| Action::Prompt { .. }
| Action::DeleteWorkspace { .. }
| Action::DeleteAgent { .. }
| Action::Monitor(_)
// Arming writes one config entry and claims nothing; the loop's own
// spawns and reaps are ordinary `bl` actions and refresh on their own.
| Action::Fleet(_)
| Action::AnswerHold { .. }
| Action::Floor { .. }
| Action::Fork { .. }
| Action::Ack
| Action::MarkSeen { .. }
| Action::ClearTrail
| Action::ApplyConfig { .. }
| Action::Advertise { .. }
| Action::Route(_)
| Action::PickModel { .. } => None,
}
}
}
impl Query {
/// The **workspace** this read is aimed at (§3.1), or `None` for the reads
/// that span the world (the roster, the board, the trail, search) and the
/// one whose subject is the interface (help). The §9 config family answers
/// through its destination instead
/// ([`ConfigFile`](super::config::ConfigFile)), for the reason
/// [`Action::workspace`] gives.
///
/// The mirror of the action table above, and it exists for the same reason:
/// one resolution stands ahead of [`answer`](super::answer::answer)'s table
/// rather than being re-derived inside a dozen arms.
pub fn workspace(&self) -> Option<String> {
match self {
Query::Conversations { workspace }
| Query::WorkDiff { workspace, .. }
| Query::Lineages { workspace }
| Query::Models { workspace, .. }
| Query::Marks { workspace }
| Query::Transcript { workspace, .. }
| Query::Steps { workspace, .. }
| Query::Step { workspace, .. }
| Query::Files { workspace, .. }
| Query::Governing { workspace, .. }
| Query::Rail { workspace, .. }
| Query::Inbox { workspace, .. }
| Query::Agent { workspace, .. }
| Query::Providers { workspace }
| Query::WorkspaceBalls { workspace }
| Query::Clients { workspace } => Some(workspace.clone()),
Query::Workspaces
| Query::Balls
| Query::Board
| Query::Attention
| Query::Ops { .. }
| Query::Search { .. }
| Query::Help { .. }
| Query::ReadConfig { .. }
// The routing leg's two reads: one is answered to the intake's own
// identity, the other to a handle — neither names a world.
| Query::Invocations
| Query::Capture { .. } => None,
}
}
}