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
//! What a row's `protocol` column says about a yog turn (DESIGN §9.4) — the
//! two reads keyed on brazen's own `ProtocolId`, split off [`super`] at §12's
//! line budget along the seam the module already had: the parent is the table's
//! *projection* (three columns, the credential model, the rendered row), and
//! this is the dialect judgement over one of those columns.
//!
//! **`protocol` is the TOOL capability** (bl-3d22). The column carries brazen's
//! own `ProtocolId` spelling, and `ProtocolId` is a public, closed enum on
//! brazen's library surface — so [`ProviderRow::tools_blocked`] parses the
//! column back into that enum through brazen's own serde rename and answers
//! over a **total match**, which a new upstream dialect fails to compile until
//! its arm is added. That is the whole reason the judgement is here rather than
//! a table of row NAMES: `claude-code` is a name, `claude_code` is a protocol,
//! and only the second is a fact about request shape.
//!
//! It exists because row EXISTENCE never established request-shape
//! compatibility, which §9.4 used to claim it did: `/model worker claude-code
//! <id>` passed the row gate, advanced both config halves, and the next worker
//! start died at brazen's encoder before any network call — *"claude_code
//! carries no tool declarations; use the `anthropic` row for tools"*. Every yog
//! turn declares at least the `clients` tool
//! ([`crate::tool_host::Injection::tools`] returns it unconditionally, and
//! lernie splices the injection into every canonical request), so a
//! tool-less dialect can serve no lernie ROLE at all.
//!
//! **The same judgement answers the failure that already happened** (bl-5252).
//! Gating the picker never reached a config written BEFORE the gate, or written
//! by hand through the §9.1 editor, which is the operator's own authority: that
//! step still dies at encode, and the §7.3 banner offered it Dismiss and nothing
//! else, because [`crate::config_edit::fault`] keyed on lernie's `Config`-kind
//! wrapper and brazen stamps every dialect decline `ErrorKind::ParseInput`. So
//! [`dialect_decline`] reads the dead step's own words into the same match a row
//! is read into — the route, not a second table.
//!
//! **The upstream ask is already filed, and is brazen bl-5053** ("publish
//! per-row capability declines (tools, multi-turn) on the read surface"): brazen
//! projects no capability column — `--list-providers` serves `name`/`protocol`/
//! `auth`/`credential`, and the `Protocol` trait that owns the rejection is
//! crate-private, so the authoritative answer cannot be asked for. That ball was
//! filed off THIS defect seen once before, and its own words name the cost:
//! *"yog's model picker validated provider=claude-code for a tool-bearing worker
//! role (it only checks the row exists in `bz --list-providers`), and every
//! session call then died at encode"*. It was never gated on yog's side, so it
//! recurred. When brazen serves the column, the match below is deleted and the
//! column read in its place — that is the whole migration.
//!
//! **The refusal is per-ROLE, and removes nothing from the catalog.** The VISION
//! §4.9 alignment monitor's check is structurally tool-less
//! ([`crate::monitor::check`]: *"`bz` takes no tool flag, so tool-lessness here
//! is structural rather than a promise"*) and pins a MODEL rather than a provider
//! row, so a tool-less dialect is a legitimate target for it. What cannot be a
//! lernie role's row is not thereby useless.
//!
//! **The second read is a CAVEAT, not a refusal** (bl-671d, §9.4). A dialect
//! whose request declares no context size hands that number to the server, and
//! yog cannot see what the server chose — so the honest answer is the fact and
//! the remedy, at the seat where the row is picked, and never a gate. It is the
//! same discipline [`is_unknown_row`](crate::model_pick::grammar::is_unknown_row)
//! applies to a table that did not answer: no surface may refuse on the strength
//! of a question that went unanswered. `ollama_chat` is the dialect
//! ([`ProviderRow::context_caveat`]), and it is the *whole* of what the wire
//! carries: the pin's own encoder maps the output cap to `options.num_predict`
//! and emits no `options.num_ctx` at all — `tests/brazen_ollama_context.rs`
//! drives the linked brazen and asserts that body, so the sentence below is
//! true because a test says so and fails the day brazen changes it. **The
//! upstream ask is brazen bl-f19d** (a first-class context declaration, or a
//! config passthrough that composes with the typed `options` instead of being
//! dropped by it); when it lands, this read and [`CONTEXT_REMEDY`] go together.
use ProviderRow;
/// The operator's way to give an `ollama_chat` row an explicit context, in the
/// one file that authors a row (§9.1's editor) — the hover beside
/// [`ProviderRow::context_caveat`], because the fact fits a line and the recipe
/// does not.
///
/// Both halves are load-bearing and both were measured against the linked
/// brazen (`tests/brazen_ollama_context.rs`). Clearing the typed cap is what
/// lets a nested `options` object through: `encode` inserts the typed `options`
/// FIRST and folds config passthrough with `or_insert`, so a `body_defaults`
/// `options` beside a typed `max_tokens` is dropped **whole and silently** —
/// which is why the recipe restates the output cap inside the object it hands
/// over.
pub const CONTEXT_REMEDY: &str = "give the row an explicit context in this workspace's brazen \
config.toml: `unsupported_body_keys = [\"max_tokens\"]` together with \
`body_defaults = { options = { num_ctx = <your context>, num_predict = <your output cap> } }`. \
Restate the output cap inside that object — clearing the typed one is what lets the object \
reach the wire, and a nested `options` beside a typed cap is dropped whole and silently.";
/// Why a dialect can carry no lernie role, keyed on the `protocol` **spelling**
/// rather than on a row — the one match, which [`ProviderRow::tools_blocked`]
/// reads a column into and [`dialect_decline`] reads a dead step's own words
/// into. Two callers, one arm per dialect: a second table would be the thing
/// bl-3d22 refused to build.
/// Why the dialect a **failed step's own words** name can serve no role, or
/// `None` when they name none (bl-5252) — the route from a dead step back to
/// [`dialect_blocked`], which [`crate::config_edit::fault`] pairs with the §9.1
/// route.
///
/// **The signal is the dialect naming ITSELF, and that is brazen's own habit.**
/// Every decline the `claude_code` encoder writes leads with its `ProtocolId`
/// spelling — *"`claude_code` carries no tool declarations…"*, *"…cannot express
/// a tool_choice"*, *"…is single-turn"*, *"…accepts only text content in
/// {slot}"* — one family from one `reject` helper, all four stamped
/// `ErrorKind::ParseInput`, so no error KIND separates them from a malformed
/// image block. So the scan is over whole `[a-z0-9_]` tokens, judged by the
/// match above, which makes it narrow in the only way that matters: it is
/// **exact and case-sensitive on brazen's spelling**, so the row NAME
/// `claude-code` — hyphen, the thing an operator's config and every other
/// failure line carry — answers nothing, and a tool-capable dialect that names
/// itself (*"anthropic_messages requires max_tokens"*) answers nothing either.
/// A dialect this build cannot name is an unanswered question, as everywhere
/// else here.
/// A `protocol` spelling parsed back into brazen's own registry key, through
/// brazen's own serde rename — never a second table of spellings beside it.
/// `None` for a spelling this build cannot name, which is a *newer* brazen
/// or a degraded column, and in both cases an unanswered question.