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
//! The **attempt** — one fork of a conversation from one point in its history
//! (VISION §5 V2, the Counterfactualist rung; DESIGN §5.1 #32, §11 rail).
//!
//! *"A pinned notch offers Fork from here: a goal composer seeded empty,
//! firing the ordinary fork with the pinned commit as ref."* That is the
//! whole gesture, and it is **one** gesture: an attempt. A **cohort** — V2's
//! ×N, the parallel candidates an operator compares — is N attempts fired
//! from one notch, and nothing anywhere records that they belong together.
//! Membership is [derived](crate::rail::cohort) from the notch each child
//! hangs on and the ref each forked off, both already on V1's cards, so
//! `N == 1` and `N > 1` are not two paths: they are one path walked once and
//! walked N times.
//!
//! **This is why there is no fan verb, no fan registry and no winner field.**
//! A gesture that fired "the whole fan" would have to name the fan, and a name
//! is a stored fact that the refs already imply. Firing the ordinary fork N
//! times leaves N ordinary `ops.jsonl` rows (§4.2) — N committed execution
//! facts — which is strictly more provenance than one row for N would be.
//!
//! **The upstream verb already exists** (lernie bl-a693, in the pin since `=0.0.6`):
//! `lernie dispatch <role> <ws> <parent> --goal <text> --from <ref>
//! [--pin <dest>=<src>]`. lernie's own words: *"`--from <ref>` is not a second
//! kind of dispatch … the flag reaches the existing `fork_point` field … and
//! changes nothing else"* — so yog composes an argv and adds no mechanism.
//!
//! **The three fire-time controls are three real parameters of that argv**,
//! never a fourth thing yog invents (VISION V2.2, "yog policy made visible"):
//!
//! - **config branch** is the fork point itself (`--from`). One control with
//! two kinds of value — the pinned commit ("from here", a fork with
//! ancestry) or `config/<name>` (a clean start, provenance only) — which is
//! VISION V1.3's ruling verbatim: *"'Clean vs fork' is one spawn gesture
//! with one parameter — the fork point."*
//! - **model** is the **role** (`<role>`). lernie resolves a model from
//! `roles.<name>.{provider,model}` in the `providers.yaml` of the config
//! commit governing the fork point, and from nowhere else. So the composer
//! lists the roles that ref declares **with the model each names**
//! ([`ForkPoint::roles`]): the model is shown at the point of choice and
//! cannot lie, because yog is reading the very file the run will resolve
//! against. Giving an attempt a model no config declares is a config write
//! — §9.4's [`PickModel`](crate::boundary::Action::PickModel) — not a
//! dispatch flag, and pretending otherwise would be capability theater.
//! - **skills** are pins (`--pin skills/<name>/SKILL.md=<pool>/<name>/SKILL.md`).
//! lernie's pin is documented as *"standing context a caller (a frontend, an
//! operator) pins without rewriting the goal or authoring a config commit"*,
//! and the shipped worker manifest composes `order: skills/**`, so a pinned
//! skill reaches assembled context by the config's own glob. The pool is the
//! world's `$LERNIE_HOME/skills` ([`pool`]) — the same directory the agent's
//! own `load_skill` tool copies out of, so the composer offers exactly what
//! the agent could have loaded for itself.
//!
//! **Read-only by construction** (VISION §4.10, bl-2b8c). An attempt forks the
//! *conversation* repo and nothing else; project-mutating attempts need
//! §4.10's isolation and binding (yog bl-8746) and are not reachable from
//! here. Nothing in this module can touch a project worktree.
use crate;
use crate;
use ;
/// The lernie subcommand an attempt is (ARCH §3.4).
const DISPATCH: &str = "dispatch";
const GOAL: &str = "--goal";
const FROM: &str = "--from";
const PIN: &str = "--pin";
/// The `providers.yaml` a fork point's governing config declares its roles in
/// (lernie ARCH §4.3) — the one file that binds a role to a model.
const PROVIDERS: &str = "providers.yaml";
/// The skills pool, under the world's `$LERNIE_HOME` — and the destination
/// prefix a pinned skill lands at in the child's worktree. One constant,
/// because they are one name: the pin reproduces the pool's own layout.
pub const SKILLS_DIR: &str = "skills";
/// The one file of a skill directory a pin carries: its instructions. A pin's
/// destination is one path, so a skill's `references/**` stay where the
/// agent's own `load_skill` can still fetch them whole.
const SKILL_FILE: &str = "SKILL.md";
/// The `config/` ref prefix a clean fork point wears (lernie ARCH §2.2).
pub const CONFIG_REF: &str = "config/";
/// One attempt's fire-time overrides — everything that varies between the
/// candidates of a cohort. The goal, the workspace and the dispatching parent
/// do not vary (they are what makes the candidates comparable), so they are
/// not here: they ride the [`Fork`](crate::boundary::Action::Fork) action
/// beside this.
/// One attempt, addressed: the candidate's overrides plus the three facts the
/// whole cohort shares (where, whose history, what for) and the world pool its
/// pins are drawn from. Owned and whole, so [`argv`] is a pure function of one
/// value and the executor carries no second parameter list to keep in step.
/// The argv one attempt fires: `lernie dispatch <role> <ws> <parent> --goal
/// <goal> --from <ref> [--pin skills/<s>/SKILL.md=<pool>/<s>/SKILL.md]…`.
///
/// **The goal is passed verbatim** — the same rule the start flow's fire keeps
/// (§3.3, bl-6920): what an operator wrote is what the model reads, unmutated.
/// The pin sources are absolute, so a child spawned in any cwd resolves them.
/// One skill's `<dest>=<src>` pin spec. The destination mirrors the pool's own
/// layout, so a pinned skill and a `load_skill`-loaded one land at the same
/// path and the manifest's `skills/**` glob sees both.
/// The world's skill pool: every directory under `skills_root` that carries a
/// `SKILL.md`, sorted. A directory without one is not a skill, and an absent
/// pool is no skills rather than an error — the composer then offers none,
/// which is the general path with an empty input.
/// One fork point the composer offers, with the policy it carries.
/// Everything the fork composer offers for one pinned notch: where an attempt
/// may fork from, and which skills it may carry. Derived on demand from the
/// workspace repo and the world's pool; stored nowhere.
/// Derive the composer's choices for a pinned notch: **here** (the pinned
/// commit, a fork carrying the conversation's own ancestry) followed by every
/// `config/<name>` branch (a clean start, provenance only). Each point's roles
/// are read at the config commit that governs it, which for a config head is
/// itself and for the pinned commit is its nearest `config/*` ancestor — the
/// one derivation §5.1 #17 already makes, asked at a ref instead of a tip.
///
/// A point whose config cannot be resolved carries no roles rather than
/// vanishing: the operator sees the ref and sees that it offers nothing, which
/// is a fact about the workspace and not a silence.
/// One fork point, with the roles its governing config declares.
/// The roles a ref's governing config commit declares (`providers.yaml`'s
/// `roles:` block), each with its provider row and model id. Reuses §9.4's own
/// grammar reader, so the picker and the fork composer can never disagree
/// about what a config file says.
/// The world's skills pool directory, `$LERNIE_HOME/skills` — the same path
/// lernie's own `load_skill` tool resolves. Derived from the world layout, so
/// yog's nested substrate (§16.2) and the pool it offers are one fact.