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
//! The `models.yaml` half of the §9.4 block grammar: declaring a model the
//! operator picked from a live `bz --list-models` roster, and **reading back**
//! what the file declares so a dead entry can be caught (§9.2 Apply gate,
//! §9.4 role rows).
//!
//! Its whole reason to exist is lernie's cross-check — a role naming a model
//! `models.yaml` does not declare is a hard load error, so the picker writes
//! this half FIRST and the assignment second (§9.4). The two facts brazen does
//! not publish are written as **declared defaults, not discoveries**, under a
//! comment that says so.
//!
//! The reader is the same anchored grammar as [`roles`](super::roles), applied
//! to the other file: `provider:` on a model entry is a brazen provider-row
//! NAME, so it is checkable against brazen's own effective table
//! ([`BzRunner::providers`](crate::config_edit::brazen::BzRunner::providers))
//! and, since bl-53be, is checked at both the §9.2 write and the §9.4 read.
use ;
/// The context window yog declares for a model discovered from `bz`
/// (§9.4). brazen publishes one only for the providers that serve it on their
/// list GET (Google today; Anthropic, OpenAI and Ollama serve none), so for the
/// rows the picker actually writes this is a **declared default, not a
/// discovery** — deliberately conservative, because under-stating a window
/// degrades to early compaction where over-stating it overflows the request.
///
/// **It is no longer unread.** Since bl-a48b the declared window is the
/// denominator of §5.1 #35's context-fullness figure
/// ([`context_windows`]), so a wrong default now shows up as a wrong
/// percentage — which is the operator's cue to correct the line the generated
/// note already tells them to edit. Seeding it from brazen's own
/// `Model.context_window` wherever the roster carries one is the honest
/// follow-up, and it belongs to the picker's write path, not to the reader.
pub const DEFAULT_CONTEXT_WINDOW: u32 = 200_000;
/// The comment yog writes above a generated entry, so the two declared-default
/// fields are never mistaken for facts brazen published (§9.4).
const GENERATED_NOTE: &str = " # added by yog's model picker from `bz --list-models`.\n \
# brazen publishes no capabilities or context window, so the two lines\n \
# below are declared defaults, not discoveries — edit them here.";
/// The `models.yaml` entry for a model discovered live, at two-space indent.
/// Declare `model` on `provider` under the global `models:` block.
/// `Ok(None)` means nothing to write — the id is already declared **on this
/// same row**, and the operator's own capabilities/context window stand
/// untouched.
///
/// An id already declared on a *different* row has only its one `provider:`
/// line rewritten (bl-bd89). lernie refuses a config whose
/// `models.<m>.provider` differs from the `roles.<r>.provider` naming it, so
/// leaving a stale row here would brick the workspace just as surely as leaving
/// the model undeclared — the two are one fact, and the picker writes it once.
/// Everything else in the entry is the operator's and is preserved.
///
/// The entry is inserted **directly after the `models:` line**, not at EOF, so
/// a file that carries a later top-level key (`adapter:`) stays valid. A file
/// with no `models:` key at all gets one appended; an inline `models: {}` is
/// refused rather than transformed.
/// One `models:` entry as the file declares it: the model id and the brazen
/// provider-row name it points at (§4.1 of lernie's own header — "`provider:`
/// on each model is a brazen provider-row NAME").
/// Every model the file declares, in file order — the mirror of
/// [`roles`](super::roles) over the other file. An entry with no `provider:`
/// line is omitted: it names no row, so it can name no *wrong* row, and
/// lernie's own loader is the authority on a half-written entry.
/// Every declared model's **wire id** paired with the `context_window` its
/// entry declares (§9.2's own field, §5.1 #35) — the denominator of the
/// context-fullness figure, and the one home for it.
///
/// Keyed on `model_id`, falling back to the entry key when the entry declares
/// none, because the id a step's `request.json` names is the wire id lernie
/// sent — not the alias the entry is filed under. An entry with no
/// `context_window:` line, an unparseable one, or a zero is **absent from the
/// map**: a window nobody declared is unknown, and a percentage against a
/// fabricated denominator is exactly the capability theater the figure exists
/// to avoid (brazen's Usage zero-vs-unknown principle, applied one field over).
///
/// **Why the declaration and not brazen's discovery.** brazen carries
/// `Model.context_window` on `--list-models` for the providers that serve one
/// (Google), and `None` for the ones that do not (Anthropic, OpenAI, Ollama) —
/// its own empty-set rule: *"a harness hand-configures only what no provider
/// serves"*. `models.yaml` **is** that hand-configuration: lernie's declared
/// field, written by the §9.4 picker, edited by the §9.5 form. One home, one
/// number, operator-correctable — where reading brazen's cache *as well* would
/// be two representations of one fact, drifting the moment either moves.
/// **The** provider-row judgement: does `provider` name no row in brazen's
/// effective table? Every site that asks it asks it here — the §9.2 Apply gate
/// over `models.yaml` ([`unknown_rows`]), the §9.4 pick gate
/// ([`plan`](crate::model_pick::plan)), and the §9.5 pane's provider control
/// over both files ([`crate::config_edit::form`]) — so the three can never
/// disagree. Every one of them judges a file against the wall of the workspace
/// that holds it; the retired birth gate (bl-c3a9, retired bl-00ee) is the one
/// site that asked it where no wall existed yet.
///
/// `providers` is `bz --list-providers`' answer (built-ins included, which a
/// scan of `config.toml` would miss). An **empty** table is no answer rather
/// than an empty one — brazen could not be asked — so it judges nothing: no
/// surface may refuse on the strength of a question that went unanswered.
/// The declared entries whose `provider:` names no row in brazen's effective
/// table — the §9.2 Apply gate's whole judgement, one [`is_unknown_row`] per
/// entry.
///
/// A file with no `models:` block declares nothing and is therefore always
/// clean, which is why the gate can run over every §9.2 file (a
/// `workflows/*.yaml` simply has nothing to check) instead of branching on
/// which file is open.
/// Why `model` cannot be fired as `models.yaml` stands, or `None` when it is
/// usable (§9.4 role rows). Two faults, because lernie refuses the config for
/// either: the id is not declared at all, or it is declared on a provider row
/// brazen's table does not have.