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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
//! What a read asks for, stated once (§16, F-34).
//!
//! Three qualifiers appear on every read surface in this crate — the lineage,
//! the valid-time instant, the transaction-time instant — and until 0.15.9
//! each surface spelled them itself. [`TraversalBuilder`] carries three
//! fields, `query_as_of_edges_on` takes two positional arguments and cannot
//! express the third at all, and the Python binding repeats the set as
//! keywords on five entry points. Nothing was wrong with any one of them; what
//! was wrong is that "read `exp` as it stood on Tuesday, under what we believed
//! in March" was a sentence the crate could not hold as a value, so it could
//! not be passed, stored, compared, or given a default.
//!
//! [`ReadPlan`] is that value. It is caller-facing and deliberately dumb: it
//! holds four `Option`s and knows no SQL. The lowering that turns a read into
//! CTEs lives in `graph::plan` and stays crate-private, which is why there are
//! two modules called `plan` and only one of them is a public path. The
//! division is the useful one — this module is *what was asked*, that one is
//! *how it is answered*, and a caller who never reads SQL should never meet
//! the second.
//!
//! # The fourth qualifier, and the promise 0.15.9 made about it
//!
//! `limit` was in [the 0.16.0 plan]'s sketch of this struct and was left out of
//! 0.15.9 on the grounds that a public knob that silently does nothing is the
//! one failure mode a plan value has that three loose arguments do not — a
//! caller can *see* an argument go unused at a call site and cannot see a field
//! go unread. `#[non_exhaustive]` made it additive on the day it meant
//! something, and 0.15.10 ([D-252]) is that day: it bounds the walk from
//! inside the recursive CTE, and every surface that takes a plan reads it.
//!
//! It is not a fourth *temporal* qualifier and it does not compose like one.
//! The other three narrow which rows are true; this one says how much of the
//! answer to pay for, so two reads under the same plan can differ. That is
//! stated on [`ReadPlan::limit()`] rather than smoothed over, and it is why
//! the walk reports whether the ceiling bit
//! ([`WalkOutcome`](crate::graph::WalkOutcome)) where it never had to report
//! on an instant.
//!
//! [the 0.16.0 plan]: ../../docs/Macrame%20Update%20Plan%20v0.16.0.md
//! [D-252]: ../../docs/architecture/s13-decision-register.md#d-252
//! [`TraversalBuilder`]: crate::graph::TraversalBuilder
use crateBranchId;
use crateResult;
use crate;
use crate;
use crateEdgeBelief;
/// The lineage, the two instants, and the ceiling a read is taken under.
///
/// Every field is `None` by default and `None` means the same thing on all of
/// them: **the ordinary read**. No branch is the trunk, no valid instant is
/// now, no recorded instant is current belief, no limit is the whole answer. A
/// default [`ReadPlan`] and no plan at all are the same read, which is what
/// lets [`TraversalBuilder::plan`](crate::graph::TraversalBuilder::plan) be
/// additive over the setters it composes rather than another way to configure a
/// traversal.
///
/// # Why the branch is a [`BranchId`] and the instants are `String`
///
/// Not an oversight, and not symmetry withheld for its own sake. A branch name
/// is validated at construction — length, control characters, surrounding
/// whitespace — and [`BranchId`] is the type that has already asked those
/// questions, so taking a `String` here would move a refusal out of the
/// caller's `BranchId::new` and into somewhere inside a read. A timestamp has
/// no such type in this crate: the canonical form is enforced at the boundary
/// (`util::timestamps`) and carried as a string everywhere below it, and
/// inventing an instant newtype for one struct would give the crate two
/// answers to what a stamp is. So [`Self::on`] cannot fail and neither can
/// [`Self::valid_at`]; a malformed stamp is refused where every other stamp in
/// the crate is refused.
///
/// # Errors, when this is executed
///
/// A plan is inert and returns nothing. The refusals belong to the read that
/// takes one: [`DbError::UnknownBranch`](crate::DbError::UnknownBranch) for a
/// lineage that was never registered, and
/// [`DbError::RecordedInstantUnreachable`](crate::DbError::RecordedInstantUnreachable)
/// for a transaction-time instant the hot log no longer answers for
/// ([D-247](../../docs/architecture/s13-decision-register.md#d-247)).
///
/// ```no_run
/// use macrame::prelude::*;
///
/// # async fn f(db: &Database) -> macrame::Result<()> {
/// let plan = ReadPlan::new()
/// .on(BranchId::new("exp")?)
/// .valid_at("2026-01-06T00:00:00.000000Z")
/// .recorded_at("2026-03-01T00:00:00.000000Z");
///
/// // The same qualifiers, on a whole-ledger read and on a walk.
/// let edges = db.edges(plan.clone()).await?;
/// let reached = TraversalBuilder::new("a")
/// .plan(plan)
/// .execute_ids(db.read_conn(), "2026-06-01T00:00:00.000000Z")
/// .await?;
/// # let _ = (edges, reached);
/// # Ok(())
/// # }
/// ```
/// Where the branch binds in [`edges_at`]'s statement, when the shape has one.
///
/// `?1` is the valid instant, which every shape binds. This is the
/// [`TraversalBuilder`](crate::graph::TraversalBuilder)'s layout with four
/// slots removed — no start node, no depth, no weight floor — and it is a named
/// constant for the same reason it is one there: the SQL and the parameter
/// vector must agree exactly, and D-030's failure mode is two places agreeing
/// by comment.
const BRANCH_SLOT: usize = 2;
/// Every edge one plan names, as the ledger held them.
///
/// The whole projection filtered to an instant — this is not a neighbourhood
/// read and there is no budget on it. `load_subgraph_with` is the bounded one.
///
/// **The order is unspecified**, as it is for
/// [`query_as_of_edges`](crate::temporal::query_as_of_edges), whose statement
/// this is. Adding an `ORDER BY` would put a sort on the largest read in the
/// crate to make its result look tidy; a caller who needs an order knows which
/// one, and sorting a `Vec` they already own is cheaper than sorting a relation
/// SQLite has to spill.
///
/// `limit` is a plain `LIMIT` on that projection, and it interacts with the
/// unspecified order exactly as badly as it sounds: the rows kept are whichever
/// the engine reaches first. It is offered anyway because the alternative —
/// leaving [`ReadPlan::limit`] unread on this surface — is the silently
/// ignored field 0.15.9 refused to ship, and because bounding a whole-ledger
/// read is worth having even when the sample is arbitrary. Unlike the walk it
/// needs nothing to report truncation: nothing drops rows after the limit
/// applies, so `out.len() == n` is exact.
pub async