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
//! Where a **config-kind** failure is fixed (§9.1, bl-dd7f) — §8.3 rule 5's
//! sibling on the other kind of fault.
//!
//! An auth-shaped step failure has offered its remedy since bl-8e34: the
//! sentence names the row and Login is one click away
//! ([`crate::login::auth`]). A **config**-shaped one offered nothing. A
//! dispatch through a provider row brazen does not resolve dies with litany's
//! own words —
//!
//! ```text
//! litany prompt: provider error (Config): unknown provider `openai-chatgpt`
//! ```
//!
//! — and the §7.3 banner painted exactly that, with a Dismiss beside it and no
//! way out at all. Dismiss puts the sentence down; it does not fix the file.
//! The one thing that does is the §9.1 raw-TOML editor, which is where a
//! provider row is authored, and it was never named.
//!
//! **The judgement happens at the first dispatch, against a wall that exists.**
//! §9.2 once gated this at *birth* and the gate was retired (bl-00ee) for
//! judging a workspace's providers against a wall that did not exist yet. This
//! reads a failure that already happened, so there is nothing to pre-judge and
//! nothing to resurrect: the row's existence is brazen's fact, resolved at call
//! time (litany ARCH §4.1), and this is that answer arriving.
//!
//! **It classifies, it never re-words.** brazen's and litany's sentences stay
//! verbatim on the banner (INV-2 / §7.3); what is added is one sentence saying
//! which file holds the fault, and the control that opens it.
//!
//! **The class is wider than the wrapper says, and a marker cannot find all of
//! it** (bl-5252). A dispatch through a row whose *dialect* cannot carry a yog
//! turn dies at brazen's encoder, not at its config resolution, and the whole
//! `claude_code` decline family — no tools, no `tool_choice`, no multi-turn
//! transcript, no non-text block — is stamped `ErrorKind::ParseInput` by one
//! `reject` helper. So litany wraps it `provider error (ParseInput) …`: no
//! config-kind word appears anywhere in it, and the banner offered Dismiss for a
//! failure whose only remedy is a config file. The second way in
//! ([`dialect_remedy`]) is therefore keyed on the dialect the decline NAMES, not
//! on a phrase and not on the error kind — the kind cannot tell that family from
//! a malformed image block, and the widest marker that could (`parseinput`)
//! would claim every one of them.
/// Case-insensitive markers of a **config**-shaped failure — the class whose
/// remedy is a file, not a credential and not a retry.
///
/// Deliberately narrow, the discipline [`looks_auth`](crate::login::auth::looks_auth)
/// keeps: each token is a phrase brazen or litany writes for a configuration
/// fault and for nothing else — which is why they are not the whole class, and
/// [`dialect_remedy`] is the other way in. `provider error (config)` is litany's own
/// wrapper around brazen's `ErrorKind::Config`; `unknown provider` is brazen's
/// `ConfigError::UnknownProvider`, the one an operator actually meets. A bare
/// `config` is **excluded** — it appears in every path yog prints.
const CONFIG_MARKERS: & = &;
use crateProviderRow;
/// Does `text` look config-shaped? Either a [`CONFIG_MARKERS`] hit or a
/// **request-shape decline** ([`dialect_remedy`]) — two ways in, because brazen
/// spells the second one with no config-kind word in it at all.
/// A [`CONFIG_MARKERS`] hit. Pure, case-insensitive substring match — the same
/// shape, and the same reasons, as
/// [`looks_auth`](crate::login::auth::looks_auth).
/// The remedy for the **other** config-shaped failure: a step that died because
/// its row's *dialect* cannot carry a yog turn at all (bl-5252).
///
/// No marker can reach this family. brazen's four `claude_code` declines come
/// from one `reject` helper stamping `ErrorKind::ParseInput`, so litany wraps
/// them `provider error (ParseInput) …` and the words above match nothing —
/// while the only remedy there IS a config file. The judgement is not
/// re-derived here either: `dialect_decline` reads the dialect out of the
/// failure's own words and answers off brazen's own `tools` column, the same
/// one [`tools_blocked`](crate::config_edit::brazen::ProviderRow::tools_blocked)
/// gives the picker, so the banner and the picker cannot disagree about
/// why the row is unusable. What this adds is the operator's next move, and the
/// route is the same one an `unknown provider` gets: the §9.1 editor authors a
/// row, §9.4's picker chooses between them.
///
/// **`rows` is the judgement arriving, not the tree being joined** (bl-b6c9).
/// The no-join rule this module keeps is about the failing ROW — the name is
/// read out of the sentence because brazen already quoted it, and a second
/// derivation could only disagree. The dialect's tool capability was never in
/// the sentence: it was a total match here until brazen published it as a
/// column, and reading a published answer is the opposite of re-deriving one.
/// An empty table answers nothing and classifies nothing, which is the same
/// rule [`plan`](crate::model_pick::plan) keeps.
/// The sentence painted beside a config-kind failure, or `None` when the
/// failure is not one. It names the row when the failing text names one, for
/// the reason bl-8e34 gave the auth affordance its row: a remedy that says
/// "edit the file" without saying *what to look for in it* is half an answer.
///
/// The row is **read out of the failure's own words**, never joined from the
/// tree: brazen quotes the name it could not resolve, so the fact is already
/// in the sentence being classified, and a second derivation could only
/// disagree with it.
/// The provider row a config-kind failure names, when it names one: the word
/// brazen quotes in backticks (`unknown provider `openai-chatgpt``), or the
/// word litany quotes after `on provider row` when it wrapped the decline with
/// the row it routed to. `None` when neither shape is present, or when the
/// quote never closes — a half-parsed name is worse than none.