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
//! **The learning loop's operator half, at the boundary** (§9.6; REMOTE §9.22,
//! bl-dd88): the staged proposals a reviewer left, one of them whole, and the
//! settle that accepts or rejects one.
//!
//! litany's reviewer stages a real config patch on `proposal/<reviewer-id>`
//! (litany `docs/DESIGN_LEARNING_LOOP.md` §3) and the loop's whole design turns
//! on a person being able to see what was learned and veto it. Adopting the
//! loop was already two gestures a seat could make; reading, accepting and
//! rejecting were `litany proposal` and nothing else, so from any seat — the
//! window, the phone, `yog gesture` — a staged patch was invisible. On a server
//! install (DESIGN §10.1) that made the veto an `ssh` and a container `exec`,
//! which is exactly what the §8.5 boundary exists to make unnecessary.
//!
//! **The read is yog's and the act is litany's, and the split is not
//! arbitrary.** yog already reads this workspace's other two ref namespaces
//! itself — `agents/*` for the §7.1 tree, `config/*` for the §9.3 browse — so a
//! listing over the third is that same derivation through the same scrubbed
//! doorway ([`git_tree`](crate::git_tree)'s named vocabulary), and it answers
//! typed rows rather than a rendered table a seat would have to parse back. The
//! **settle** is not read arithmetic: accepting is a compare-and-swap
//! fast-forward whose expected old value is the freshness the listing showed,
//! and re-implementing that here would be a second home for a rule whose
//! failure mode is a lost race. So it stays `litany proposal`, spawned through
//! the §8.2 workspace seam like every other litany verb and leaving that
//! family's ops row.
//!
//! **Freshness is derived at read time and never stored.** `fresh` is two
//! commits compared at the moment it is asked, and the accept re-derives the
//! same test as git's own `update-ref <head> <new> <old>` — so a proposal
//! cannot be listed fresh and then accepted on a stale field, and a race is
//! refused by git rather than by a check that could lose to it.
use Path;
use crate;
/// The `proposal/` ref-namespace prefix (litany ARCH §2.3's third namespace).
/// Mirrored here for the reason `REPO_DIR` and the `config/` prefix already
/// are: yog reads these refs and does not link the module that names them.
const PREFIX: &str = "proposal/";
/// One staged proposal as a seat reads it. Every field is derived at the moment
/// it is asked; nothing here is stored anywhere.
/// What a proposals read answers: the listing, and — when the read named one —
/// that proposal **whole**, message and diff.
///
/// **A listing and one entry's bytes are one question asked at two depths**,
/// which is [`Query::Files`](crate::boundary::Query::Files)' shape and taken
/// for its reason: the seat that names an id has already been answered the row
/// it names, so a second query would be a second derivation of one subject.
/// `whole` is `None` for the bare read, which is the general path with no input.
/// **The operator's verdict on one staged proposal** (litany's §3 *operator
/// verb*): take it onto its lineage, or delete it.
///
/// Two words and no third: there is no *defer*, because a proposal nobody
/// settles is exactly the state the listing already shows, and no *merge*,
/// because a stale proposal's reviewer read a config that no longer governs —
/// litany refuses to merge one forward, and the remedy is a reject plus the
/// next checkpoint.
/// One settle: which workspace, which proposal, and which way.
///
/// **The id is required and the verdict is explicit.** A settle that took "the
/// only one" would do something different the day a second proposal was staged,
/// which is the class of surprise a destructive act must not have; litany's own
/// verb refuses it for that reason and this does not soften it.
/// Every staged proposal of one workspace, and — when `id` names one — that
/// proposal whole. A workspace with none answers an empty listing, which is the
/// general path with no input rather than a refusal.
///
/// An `id` naming no staged proposal refuses in git's own words. That is the
/// same split [`config::read`](crate::boundary::config) already draws: a
/// listing is total, and a read that names one thing must not answer emptiness
/// for a thing that is not there.
pub
/// The read itself, in git's own error type — [`read`]'s body, so the one
/// `map_err` sits at the boundary rather than at every call.
/// The listing, in `for-each-ref` order — which is the ids' own, so two engines
/// render one workspace identically without sharing ordering state (I9).
/// One proposal's row. Its parent is the commit the reviewer read and staged
/// on, and `fresh` is whether any config lineage still stands there — which is
/// exactly the set an accept's fast-forward would move.
/// Every config lineage and the oid its head stands at — the set `fresh` is
/// derived against, read once per listing rather than once per row.
/// The answer's JSON spelling, both directions — beside the type that owns it.