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
//! The attention model (DESIGN §6, §15 Y10): the derived per-agent predicate,
//! its per-signal detail for badges, the workspace/strip rollups, the
//! jump-to-next-attention control, and the roster sort.
//!
//! Everything here is a **pure function** of injected snapshots — the
//! [`git_tree::Agent`](crate::git_tree::Agent) views plus a `seen`-lookup
//! closure over the `ui.json` watermarks (§4.1). The narrowest coupling: the
//! module needs exactly one query from `ui_state` — "is this evidence oid
//! acknowledged?" ([`UiState::is_seen`](crate::ui_state::UiState::is_seen)) —
//! so it takes that one closure, never the whole document nor a `Clock`.
//!
//! # The predicate (DESIGN §6)
//!
//! [`attention`] is true when any signal fires:
//!
//! 1. **notify** — `notify_oid` present and unseen.
//! 2. **stopped** — the agent is **at rest** (`Quiescent | Stopped`), **not**
//! abandoned (`abandoned_oid` absent), **nobody dispatched it**, and its
//! branch tip oid is unseen (the §6/§4.1 evidence for a rest is the branch
//! tip). Ruled bl-2194: the strip is a **turn queue**, so rule 2 fires on
//! rest, not on the wound — a clean turn-end and a failed one differ in the
//! state badge, never in whether your turn has come. The field, the
//! [`AttentionKind`] and the `ui.json` key keep the historical name
//! `stopped`; the watermark's identity is the tip oid, unchanged, which is
//! what makes the widening migration-free.
//! Since bl-3592 rule 2 also asks **whose** turn the rest is: a
//! conversation somebody else dispatched comes to rest into that one's
//! inbox, so the turn is its parent's and never the operator's
//! ([`rest_is_the_operators`]). DESIGN §6 rule 2 carries the argument and
//! what it costs; this does not restate it.
//! 3. **budget** — `budget_oid` present and unseen.
//! 4. **conflicted** — `conflicted_oid` present and unseen.
//! 5. **mail** — a non-empty `pending` listing **and** the lock is definitely `Free`
//! (a driver-absence stall). Signals 1–4 are seen-gated on `ui.json`; **mail
//! is not** — it self-clears when a driver drains the inbox (§6 rule 5).
//! Since bl-b43b it also says **which way** the rest came about: a rest whose
//! latest response was refused at the provider rung earns
//! [`AttentionKind::Refused`] instead, which is one firing said in the word
//! that is true of it and never a second signal beside it.
//! 6. **held** — the capability control parked a tool invocation before it
//! executed (`refs/litany/held/<id>`, §8.6). **Not seen-gated**, on mail's
//! own precedent and for a stronger reason: a park costs the drone no
//! process and no tokens and *nothing but an answer releases it*, so a
//! watermark could only hide a conversation that cannot move. It self-clears
//! when litany lifts the mark — which happens exactly when the answer lands
//! and the branch re-adjudicates.
//!
//! Signals 1–4 "re-arm" automatically: the watermark is an oid, so a moved ref
//! (new oid ≠ the seen one) fires again (§4.1 "A moved ref re-notifies").
/// **The signal vocabulary** — which signals exist and the sentence each says
/// (§6), cut off this file at §12's budget on `roster`'s own seam: a word is
/// not a derivation, and only one of the two changes when a seat needs the rule
/// stated rather than badged.
pub use ;
pub use AttentionKind;
pub use row_says;
use crate;
use crateSeenKind;
// The seen-lookup every predicate here takes — does `(kind, ws, agent, oid)`
// carry an acknowledgement watermark in `ui.json` (§6)? Threaded as a bare
// `&dyn Fn(..)` (a type alias `dyn Fn` would bake in `'static` and reject the
// shell's `self`-capturing closure; `&dyn` defaults to the reference's own,
// elided lifetime). Production passes `&|k, w, a, o| ui_state.is_seen(k, w, a, o)`.
/// The per-agent attention detail: which of the six signals fire. The bare
/// predicate is [`Attention::any`]; [`Attention::kinds`] lists the firing
/// kinds for badge rendering.
/// The §6 per-agent predicate over injected snapshots. `ws` is the workspace's
/// seen-key path (§4.1 `seen[ws][agent]`).
///
/// **`siblings` is the workspace's whole agent set**, and rule 2 is why: whose
/// turn a rest is depends on whether anybody dispatched this conversation, and
/// that is a question about the set rather than about one row (module doc).
/// Every caller already holds the set — the rank sort, both rollups and the
/// roster walk each iterate it — so the parameter costs nothing but its name.
/// **Whose turn this conversation's rest is** (§6 rule 2 as amended, bl-3592):
/// the operator's only when nobody dispatched it.
///
/// A conversation forked by another — a compactor, a reviewer, a subagent, a
/// fan candidate — comes to rest **into its dispatcher's inbox** (litany ARCH
/// §2.6), so that rest is the parent's to take. **No turn is lost by the
/// suppression**: either the parent's driver takes the deposit, and nothing
/// needed the operator, or it does not, and the parent is at rest with mail
/// nobody is driving — rule 5, on the row that can act. Nothing but rule 2 is
/// suppressed, because nothing but a rest is a turn a parent can take.
///
/// The membership rule is the descent tree's, asked rather than restated
/// ([`parent_index`](crate::git_tree::parent_index)): a root id, an id outside
/// litany's grammar and a descendant whose dispatcher holds no ref all read as
/// nobody's child here exactly as they render at depth 0 there. DESIGN §6 rule
/// 2 carries why this is not a role test.
/// The §6 **at rest** state class: a conversation that is not executing —
/// `Quiescent` (it came to rest cleanly) or `Stopped` (it came to rest wounded).
/// Rest is the general condition rule 2 fires on; *which way* it came to rest is
/// the state badge's job, never attention's (ruled bl-2194).
/// The §6 rule-2 evidence for `agent`: the branch tip oid it is resting at, or
/// `None` when it is still running or has been abandoned (`refs/litany/abandoned`
/// is the will-not-retry assertion that suppresses the rule). **The one home for
/// rule 2's non-watermark gate** — the predicate here and the acknowledgement in
/// `app::focus` both call it, so the two can never drift into two answers.
/// The present acknowledgement evidence for one agent (§6): every signal oid
/// that exists right now — notify, the rest tip (unless abandoned), budget,
/// conflicted. Recording these as seen is what quiets attention; a later moved
/// ref is a different oid and re-arms (§4.1).
///
/// **The one definition**, read by both entries that acknowledge: the window's
/// focus tick ([`AppModel::focus_agent`](crate::AppModel::focus_agent)) and the
/// boundary's `seen` action
/// ([`queue::mark_seen`](crate::boundary::answer::queue::mark_seen)). Rules 5
/// (mail) and 6 (held) have no oid and appear here by design — each self-clears
/// when the world moves (a driver drains the inbox; litany lifts the hold mark
/// on the answer's re-adjudication), and no watermark may pretend to answer
/// them.
/// The §6 rule-5 driver-absence condition: the executor lock is definitely
/// `Free`, not merely `Unknown`. The classifier (`git_tree::state`) collapses a
/// `Free` probe to a framing state (the [`at_rest`] pair) with the uncertainty
/// flag *clear*, whereas `Unknown` yields the same framing state with the flag
/// *set* (DESIGN §10). So `Free ⟺ at-rest ∧ ¬uncertain` — a `Live` / `InFlight`
/// agent (lock `Held`) is never mail-stalled; an `Unknown` one is hidden, never
/// a false stall.