clef-mcp 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,24 +1,22 @@
1
+ <div align="center">
2
+
3
+ <img src="./assets/demo.svg" alt="clef_decide — probability distributions for a production incident" width="880"/>
4
+
1
5
  # clef-mcp
2
6
 
3
- Local [MCP](https://modelcontextprotocol.io) server that gives coding agents (Codex, Claude Code, Cursor, …) access to the **Clef decision model** through a single structured tool: `clef_decide`.
7
+ [![npm version](https://img.shields.io/npm/v/clef-mcp.svg)](https://www.npmjs.com/package/clef-mcp)
8
+ [![npm downloads](https://img.shields.io/npm/dm/clef-mcp.svg)](https://www.npmjs.com/package/clef-mcp)
9
+ [![CI](https://github.com/HighlyLoadedEgo/ClefMCP/actions/workflows/ci.yml/badge.svg)](https://github.com/HighlyLoadedEgo/ClefMCP/actions/workflows/ci.yml)
10
+ [![Official MCP Registry](https://img.shields.io/badge/MCP_Registry-io.github.HighlyLoadedEgo-blue)](https://registry.modelcontextprotocol.io)
11
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
4
12
 
5
- ```text
6
- AI Agent
7
- ↓ MCP (stdio)
8
- clef-mcp
9
- ↓ POST /v1/systemone
10
- llama.cpp (llama-server, local)
11
- ↓
12
- Clef-Flash → probability distribution over your answer options
13
- ```
13
+ **A "reflex" for AI coding agents: structured decisions with probabilities — not prose.**
14
14
 
15
- Clef does **not** generate prose. It reads a `state` plus a set of typed questions and returns, in a single forward pass, a probability for every allowed answer — ideal for "what should the agent do next?" decisions.
15
+ Local [MCP](https://modelcontextprotocol.io) server that gives agents (Claude Code, Codex, Cursor, ZCode, …) access to the **Clef-Flash** decision model (9B, Apache-2.0 by Cloudflare) through a single tool: `clef_decide`. Pass a `state` and typed questions, get a **probability distribution over your options** in one forward pass. Fully local, offline, no tokens burned.
16
16
 
17
- ## Requirements
17
+ </div>
18
18
 
19
- - macOS (Apple Silicon or x64), Linux (x64/arm64) or Windows x64
20
- - Node.js ≥ 20
21
- - ≥ 16 GB of memory (Clef-Flash 9B @ 4-bit); more headroom enables higher precision
19
+ ---
22
20
 
23
21
  ## Quick start
24
22
 
@@ -33,65 +31,28 @@ npx clef-mcp setup
33
31
  npx clef-mcp
34
32
  ```
35
33
 
36
- `clef-mcp` (step 3) never downloads anything. If the model is missing, tool calls return a structured `MODEL_NOT_INSTALLED` error with a hint. Steps 1 and 2 can be combined: `npx clef-mcp install --setup`.
37
-
38
- ### Register with your MCP client
34
+ `clef-mcp` (step 3) never downloads anything. If the model is missing, tool calls return a structured `MODEL_NOT_INSTALLED` error with a hint. Steps 1 and 2 combine: `npx clef-mcp install --setup`.
39
35
 
40
- **Claude Code**
36
+ ## Why not just ask the LLM?
41
37
 
42
- ```bash
43
- claude mcp add clef-mcp -- clef-mcp
44
- # or, without a global install:
45
- claude mcp add clef-mcp -- npx -y clef-mcp
46
- ```
38
+ | | Chat LLM | `clef_decide` |
39
+ |---|---|---|
40
+ | Output | prose, you parse it | strict JSON: probability per option |
41
+ | Determinism | varies per run | single forward pass, no sampling |
42
+ | Latency (1 decision) | seconds of generation | ~0.5 s local |
43
+ | Context cost | grows with every decision | fixed, small schema |
44
+ | Privacy | depends on provider | 100% on-device, works offline |
45
+ | Calibration | vibes | softmax over trained option scores |
47
46
 
48
- **Codex** — `~/.codex/config.toml`:
49
-
50
- ```toml
51
- [mcp_servers.clef-mcp]
52
- command = "clef-mcp"
53
- args = []
54
- ```
55
-
56
- **Cursor** — `.cursor/mcp.json`:
57
-
58
- ```json
59
- {
60
- "mcpServers": {
61
- "clef-mcp": {
62
- "command": "clef-mcp",
63
- "args": []
64
- }
65
- }
66
- }
67
- ```
68
-
69
- **ZCode** — `~/.zcode/cli/config.json` (user scope; servers auto-connect in new sessions):
70
-
71
- ```json
72
- {
73
- "mcp": {
74
- "servers": {
75
- "clef-mcp": { "command": "clef-mcp", "args": [], "type": "stdio" }
76
- }
77
- }
78
- }
79
- ```
80
-
81
- **Generic MCP client** — any client that speaks MCP over stdio: run `clef-mcp` as the server command.
82
-
83
- Ready-made snippets live in [`examples/`](examples/).
47
+ Sweet spot: **decision points inside agent loops** — next action, routing, classification, severity, yes/no judgment — asked dozens of times per task.
84
48
 
85
49
  ## The `clef_decide` tool
86
50
 
87
- ### Input
88
-
89
51
  ```json
90
52
  {
91
53
  "state": {
92
54
  "task": "Fix failing tests",
93
- "error": "TypeError: Cannot read properties of undefined",
94
- "git_diff": "..."
55
+ "error": "TypeError: Cannot read properties of undefined"
95
56
  },
96
57
  "questions": {
97
58
  "next_action": {
@@ -109,59 +70,141 @@ Ready-made snippets live in [`examples/`](examples/).
109
70
  "instructions": "How confident are you in this decision?",
110
71
  "criteria": ["very_low", "low", "medium", "high", "very_high"]
111
72
  },
112
- "is_outage": {
113
- "type": "noul",
114
- "instructions": "Is a service down?"
115
- }
73
+ "is_outage": { "type": "noul", "instructions": "Is a service down?" }
116
74
  }
117
75
  }
118
76
  ```
119
77
 
120
- | Type | Criteria | Meaning |
121
- |------|----------|---------|
122
- | `choice` | map of `option id → description`, or a plain list of options (ids = the strings) | Pick among named options |
123
- | `score` | ordered list of descriptions | Ordered scale (index = score) |
124
- | `noul` | optional `{"true": "...", "false": "..."}` | Yes/no question |
125
-
126
- ### Output
78
+ | Type | Criteria | Answer |
79
+ |------|----------|--------|
80
+ | `choice` | map `option id → description`, or a plain list | probability per option |
81
+ | `score` | ordered list (index = score) | probability per level |
82
+ | `noul` | optional `{"true": "...", "false": "..."}` | `{"true": p, "false": 1-p}` |
127
83
 
128
- Strictly structured — the server never turns the result into prose:
84
+ Response — strictly structured, never prose:
129
85
 
130
86
  ```json
131
87
  {
132
88
  "model": "clef-flash",
133
89
  "decisions": {
134
- "next_action": {
135
- "answer": { "inspect": 0.72, "modify": 0.12, "test": 0.14, "ask_user": 0.02 }
136
- },
137
- "confidence": {
138
- "answer": { "very_low": 0.01, "low": 0.04, "medium": 0.18, "high": 0.61, "very_high": 0.16 }
139
- },
140
- "is_outage": {
141
- "answer": { "true": 0.9, "false": 0.1 }
90
+ "next_action": { "answer": { "inspect": 0.72, "modify": 0.12, "test": 0.14, "ask_user": 0.02 } },
91
+ "confidence": { "answer": { "very_low": 0.01, "low": 0.04, "medium": 0.18, "high": 0.61, "very_high": 0.16 } },
92
+ "is_outage": { "answer": { "true": 0.9, "false": 0.1 } }
93
+ }
94
+ }
95
+ ```
96
+
97
+ Batch up to **64 questions per call** — they are scored in one forward pass. `state` is treated strictly as **data**: never executed, never interpreted as instructions for the server.
98
+
99
+ ## Prompts & resources
100
+
101
+ The server ships four MCP **prompts** (canned, decision-shaped asks — your client lists them via `prompts/list`):
102
+
103
+ | Prompt | Purpose |
104
+ |---|---|
105
+ | `incident-triage` | action + severity + user-impact questions for a production incident |
106
+ | `next-action` | what the coding agent should do next + confidence |
107
+ | `ticket-routing` | classify a message into a team + urgency |
108
+ | `security-review` | vulnerability yes/no, risk scale, first mitigation |
109
+
110
+ And three **resources** (read-only, no model needed):
111
+
112
+ | URI | Contents |
113
+ |---|---|
114
+ | `clef-mcp://capabilities` | live JSON: model, runtime, limits, error codes |
115
+ | `clef-mcp://evals/schema` | how to write eval cases |
116
+ | `clef-mcp://evals/dataset` | the bundled 30-case dataset |
117
+
118
+ ## Measured, not marketed
119
+
120
+ Apple M4 Pro, Clef-Flash Q4_K_M (6 GB), single request through the full MCP stdio path:
121
+
122
+ | Scenario | Latency |
123
+ |---|---|
124
+ | Cold start (incl. model load, once per session) | ~4.4 s |
125
+ | 1 question | ~0.5 s |
126
+ | 10 questions, one call | ~2.9 s |
127
+ | 64 questions, one call | ~18.6 s |
128
+
129
+ Quality gate: a 30-case evaluation dataset (coding / security / classification / routing / yes-no) — **86.7% pass** on the live model. Run it yourself: `clef-mcp evals`.
130
+
131
+ ## Register with your MCP client
132
+
133
+ <details open>
134
+ <summary><b>Claude Code</b></summary>
135
+
136
+ ```bash
137
+ claude mcp add clef-mcp -- clef-mcp
138
+ # or, without a global install:
139
+ claude mcp add clef-mcp -- npx -y clef-mcp
140
+ ```
141
+ </details>
142
+
143
+ <details>
144
+ <summary><b>Codex</b> — <code>~/.codex/config.toml</code></summary>
145
+
146
+ ```toml
147
+ [mcp_servers.clef-mcp]
148
+ command = "clef-mcp"
149
+ args = []
150
+ ```
151
+ </details>
152
+
153
+ <details>
154
+ <summary><b>Cursor</b> — <code>.cursor/mcp.json</code></summary>
155
+
156
+ ```json
157
+ {
158
+ "mcpServers": {
159
+ "clef-mcp": { "command": "clef-mcp", "args": [] }
160
+ }
161
+ }
162
+ ```
163
+ </details>
164
+
165
+ <details>
166
+ <summary><b>ZCode</b> — <code>~/.zcode/cli/config.json</code> (user scope, auto-connect)</summary>
167
+
168
+ ```json
169
+ {
170
+ "mcp": {
171
+ "servers": {
172
+ "clef-mcp": { "command": "clef-mcp", "args": [], "type": "stdio" }
142
173
  }
143
174
  }
144
175
  }
145
176
  ```
177
+ </details>
146
178
 
147
- `state` is treated strictly as **data**: it is never interpreted as instructions for the MCP server itself (see [Security](#security--data-handling)).
179
+ Ready-made snippets: [`examples/`](examples/).
148
180
 
149
181
  ## Teach your agent (skill)
150
182
 
151
- The tool schema tells the client *what* `clef_decide` accepts; agents also need to know *when* to reach for it and *how* to frame decisions. The bundled skill covers that: decision patterns (next action / routing / classification / noul / score), batching up to 64 questions into one forward pass, writing mutually exclusive criteria, interpreting distributions (argmax vs flat), thresholds for safety-adjacent calls, and error recovery.
152
-
153
- Install it for your agent(s):
183
+ The schema tells the client *what* `clef_decide` accepts; agents also need to know *when* to reach for it and *how* to frame decisions. The bundled [`clef-decisions`](skills/clef-decisions/SKILL.md) skill covers decision patterns, batching, criteria writing, distribution interpretation and error recovery:
154
184
 
155
185
  ```bash
156
- # automatic (registers the MCP server too)
157
- clef-mcp setup
186
+ clef-mcp setup # automatic
187
+ cp -r skills/clef-decisions ~/.agents/skills/ # manual, from repo
188
+ cp -r "$(npm root -g)/clef-mcp/skills/clef-decisions" ~/.agents/skills/ # from npm package
189
+ ```
158
190
 
159
- # or copy the skill manually
160
- cp -r skills/clef-decisions ~/.agents/skills/ # from this repo
161
- cp -r "$(npm root -g)/clef-mcp/skills/clef-decisions" ~/.agents/skills/ # from the npm package
191
+ ## Architecture
192
+
193
+ ```mermaid
194
+ flowchart LR
195
+ subgraph clients [MCP clients]
196
+ CC[Claude Code]
197
+ CX[Codex]
198
+ CU[Cursor]
199
+ ZC[ZCode]
200
+ end
201
+ clients -- MCP stdio --> S[clef-mcp<br/>validation · limits · structured errors]
202
+ S -- SystemOne adapter --> R[ClefRuntime<br/>llama.cpp subprocess<br/>127.0.0.1]
203
+ R -- single forward pass --> M[("Clef-Flash<br/>9B · GGUF · local")]
204
+ M -. probabilities .-> S -. strict JSON .-> clients
162
205
  ```
163
206
 
164
- `~/.agents/skills/` is the shared location for ZCode / Claude Code / Codex-style agents.
207
+ The `ClefRuntime` interface (`load / decide / unload / health`) isolates the engine: MLX or remote runtimes plug in without changing the MCP API. The wire format is `POST /v1/systemone` — the same contract across llama.cpp and other Clef runtimes.
165
208
 
166
209
  ## CLI
167
210
 
@@ -176,44 +219,44 @@ clef-mcp uninstall # remove the model (and optionally the managed runtime)
176
219
  clef-mcp evals # run the evaluation dataset against the installed model
177
220
  ```
178
221
 
179
- Useful flags: `install --quant Q8_0 --yes --skip-probe`, `install --setup` (runs setup right after install), `setup --clients zcode,cursor --no-skill`, `doctor --deep` (re-hashes the model file), `uninstall --runtime --yes`.
222
+ Flags: `install --quant Q8_0 --yes --skip-probe`, `install --setup`, `setup --clients zcode,cursor --no-skill`, `doctor --deep` (re-hash the model file), `uninstall --runtime --yes`.
223
+
224
+ ## Runtimes: llama.cpp and MLX
180
225
 
181
- ## Configuration (environment variables)
226
+ Two local runtimes behind the same `ClefRuntime` interface:
227
+
228
+ | | `llama-cpp` (default) | `mlx` |
229
+ |---|---|---|
230
+ | Platforms | macOS, Linux, Windows | macOS / Apple Silicon only |
231
+ | Model | GGUF from `ggml-org/Clef-Flash-GGUF` | MLX 4-bit from `mlx-community/clef-flash-4bit` |
232
+ | Extras | none | [uv](https://docs.astral.sh/uv/) on PATH (managed Python env) |
233
+ | Install | `clef-mcp install` | `clef-mcp install --runtime mlx` |
234
+
235
+ Switch at runtime with `CLEF_RUNTIME=mlx` (must be set for the MCP server process — e.g. in the client's `env` block). Both speak the same `POST /v1/systemone` contract. The MLX snapshot is fetched into `CLEF_HOME` via a uv-managed `huggingface_hub` (no global Python state) at a pinned revision.
236
+
237
+ ## Configuration
182
238
 
183
239
  | Variable | Default | Meaning |
184
240
  |----------|---------|---------|
185
- | `CLEF_MODEL` | `clef-flash` | Model id (`clef_decide` also accepts a per-call `model`) |
241
+ | `CLEF_MODEL` | `clef-flash` | Model id (per-call `model` also accepted) |
186
242
  | `CLEF_HOME` | `~/.cache/clef-mcp` | Cache/model home |
187
- | `CLEF_RUNTIME` | `llama-cpp` | Inference runtime (v0.1: only llama-cpp) |
188
- | `CLEF_LOG_LEVEL` | `error` | `error` \| `warn` \| `info` \| `debug` (logs go to **stderr**) |
189
- | `CLEF_LLAMA_BIN` | – | Explicit path to a `llama-server` binary |
190
- | `CLEF_LLAMA_RELEASE_TAG` | latest nightly | Pin the managed llama.cpp build, e.g. `b11378` |
243
+ | `CLEF_RUNTIME` | `llama-cpp` | `llama-cpp` \| `mlx` |
244
+ | `CLEF_LOG_LEVEL` | `error` | `error` \| `warn` \| `info` \| `debug` (stderr only) |
245
+ | `CLEF_LLAMA_BIN` | – | Explicit `llama-server` binary path (llama-cpp runtime) |
246
+ | `CLEF_LLAMA_RELEASE_TAG` | latest nightly | Pin the managed llama.cpp build |
247
+ | `CLEF_LLAMA_BATCH` | `8192` | llama.cpp physical batch (multi-question requests) |
248
+ | `CLEF_MLX_UV` | `uv` on PATH | Explicit uv binary (MLX runtime) |
191
249
  | `CLEF_MAX_QUESTIONS` | `64` | Max questions per call |
192
250
  | `CLEF_MAX_STATE_BYTES` | `1048576` | Max serialized `state` size |
193
251
  | `CLEF_MAX_INSTRUCTION_CHARS` | `10000` | Max chars per question instructions |
194
252
 
195
- Runtime selection order: `CLEF_LLAMA_BIN` → managed binary in `CLEF_HOME/runtime` → `llama-server` on `PATH`.
196
-
197
- ## Storage layout
198
-
199
- ```text
200
- ~/.cache/clef-mcp/ # or $CLEF_HOME
201
- ├── models/
202
- │ └── clef-flash/
203
- │ ├── 4bit/ # Q4_K_M
204
- │ │ ├── Clef-Flash-Q4_K_M.gguf
205
- │ │ └── manifest.json # repo, revision, sha256, license
206
- │ ├── 8bit/ # Q8_0
207
- │ └── bf16/ # BF16
208
- └── runtime/
209
- └── llama.cpp/darwin-arm64/llama-server
210
- ```
253
+ Runtime resolution: `CLEF_LLAMA_BIN` → managed binary in `CLEF_HOME/runtime` → `llama-server` on `PATH`.
211
254
 
212
- The model is downloaded from the pinned official GGUF conversion ([ggml-org/Clef-Flash-GGUF](https://huggingface.co/ggml-org/Clef-Flash-GGUF), revision-pinned) and **sha256-verified** against Hugging Face's content hash. It is never repackaged by clef-mcp.
255
+ Storage: `CLEF_HOME/models/<model>/<quant>/` (model + `manifest.json` with repo/revision/sha256/license) and `CLEF_HOME/runtime/llama.cpp/`.
213
256
 
214
- ## Error handling
257
+ The model is downloaded from the pinned official GGUF conversion ([ggml-org/Clef-Flash-GGUF](https://huggingface.co/ggml-org/Clef-Flash-GGUF)) and **sha256-verified** against Hugging Face's content hash. It is never repackaged by clef-mcp. Also listed in the [official MCP Registry](https://registry.modelcontextprotocol.io) as `io.github.HighlyLoadedEgo/clef-mcp`.
215
258
 
216
- Tool errors are structured JSON with a machine-readable code:
259
+ ## Error handling
217
260
 
218
261
  ```json
219
262
  {
@@ -225,42 +268,28 @@ Tool errors are structured JSON with a machine-readable code:
225
268
  }
226
269
  ```
227
270
 
228
- Codes: `MODEL_NOT_INSTALLED`, `MODEL_LOAD_FAILED`, `RUNTIME_NOT_FOUND`, `RUNTIME_INIT_FAILED`, `RUNTIME_NOT_SUPPORTED`, `INVALID_INPUT`, `CLEF_INFERENCE_FAILED`, `UNSUPPORTED_PLATFORM`, `OUT_OF_MEMORY`, `CHECKSUM_MISMATCH`, `DOWNLOAD_FAILED`.
229
-
230
- Input exceeding the model's 16k-token context is rejected by the runtime with a hint to reduce the state.
231
-
232
- ## Architecture
233
-
234
- ```text
235
- MCP interface (stdio) → validation, structured errors
236
- ↓
237
- Clef adapter → clef_decide ⇄ SystemOne wire format (request/response mapping)
238
- ↓
239
- ClefRuntime (interface)
240
- ↓
241
- LocalClefRuntime → llama.cpp llama-server subprocess
242
- ```
243
-
244
- The `ClefRuntime` interface (`load / decide / unload / health`) isolates the engine: future MLX or remote runtimes plug in without changing the MCP API.
271
+ Codes: `MODEL_NOT_INSTALLED`, `MODEL_LOAD_FAILED`, `RUNTIME_NOT_FOUND`, `RUNTIME_INIT_FAILED`, `RUNTIME_NOT_SUPPORTED`, `INVALID_INPUT`, `CLEF_INFERENCE_FAILED`, `UNSUPPORTED_PLATFORM`, `OUT_OF_MEMORY`, `CHECKSUM_MISMATCH`, `DOWNLOAD_FAILED`. Input exceeding the 16k-token model context is rejected with a hint to reduce the state.
245
272
 
246
273
  ## Security & data handling
247
274
 
248
275
  - No network servers, no telemetry, no accounts; everything runs locally.
249
- - The model downloads only on an explicit `install`, over HTTPS, with checksum verification.
276
+ - The model downloads only on an explicit `install`, over HTTPS, checksum-verified.
250
277
  - `state` content is passed to the model as data; the server never executes or instruction-interprets it.
251
278
  - Filesystem access is limited to `CLEF_HOME` (plus reading standard MCP client config paths in `doctor`).
252
- - The managed runtime is the official llama.cpp nightly build; pin it with `CLEF_LLAMA_RELEASE_TAG` if you want reproducibility.
279
+ - The managed runtime is the official llama.cpp build; pin it with `CLEF_LLAMA_RELEASE_TAG`.
280
+
281
+ See [SECURITY.md](SECURITY.md) for the full policy.
253
282
 
254
283
  ## Development
255
284
 
256
285
  ```bash
257
286
  npm install
258
287
  npm run build
259
- npm test # unit + integration (uses a fake llama-server, no model needed)
260
- npm run evals # needs an installed model; ~30 cases, exit code reflects pass rate
288
+ npm test # unit + integration (fake llama-server, no model needed)
289
+ npm run evals # needs an installed model; exit code reflects pass rate
261
290
  ```
262
291
 
263
- See [CONTRIBUTING.md](CONTRIBUTING.md) and [`tests/evals/dataset.jsonl`](tests/evals/dataset.jsonl) for the eval format.
292
+ See [CONTRIBUTING.md](CONTRIBUTING.md) and [`tests/evals/dataset.jsonl`](tests/evals/dataset.jsonl).
264
293
 
265
294
  ## License
266
295
 
@@ -0,0 +1,70 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="880" height="420" viewBox="0 0 880 420" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace">
2
+ <defs>
3
+ <style>
4
+ .bg { fill: #0d1117; }
5
+ .chrome { fill: #161b22; }
6
+ .title { fill: #8b949e; font-size: 12px; }
7
+ .lbl { fill: #c9d1d9; font-size: 13px; }
8
+ .dim { fill: #8b949e; font-size: 12px; }
9
+ .val { font-size: 13px; font-weight: bold; }
10
+ .json { fill: #7ee787; font-size: 12px; }
11
+ .key { fill: #79c0ff; }
12
+ </style>
13
+ </defs>
14
+
15
+ <rect class="bg" width="880" height="420" rx="10"/>
16
+ <rect class="chrome" width="880" height="36" rx="10"/>
17
+ <rect class="chrome" y="18" width="880" height="18"/>
18
+ <circle cx="22" cy="18" r="6" fill="#ff5f57"/>
19
+ <circle cx="42" cy="18" r="6" fill="#febc2e"/>
20
+ <circle cx="62" cy="18" r="6" fill="#28c840"/>
21
+ <text class="title" x="440" y="23" text-anchor="middle">clef_decide — production incident</text>
22
+
23
+ <!-- request -->
24
+ <text class="dim" x="28" y="66">state: "prod returns 500 for 40% of requests after a deploy"</text>
25
+ <text class="dim" x="28" y="84">questions: action (choice) · urgent (noul)</text>
26
+
27
+ <!-- question 1: action -->
28
+ <text class="key" x="28" y="116">action</text>
29
+ <text class="dim" x="92" y="116">"What should the coding agent do next?" — choice</text>
30
+
31
+ <text class="lbl" x="28" y="142">rollback</text>
32
+ <rect x="120" y="132" width="527" height="12" rx="3" fill="#3fb950" opacity="0.25"/>
33
+ <rect x="120" y="132" width="497" height="12" rx="3" fill="#3fb950"/>
34
+ <text class="val" x="655" y="143" fill="#3fb950">93.9%</text>
35
+
36
+ <text class="lbl" x="28" y="170">hotfix</text>
37
+ <rect x="120" y="160" width="527" height="12" rx="3" fill="#d29922" opacity="0.25"/>
38
+ <rect x="120" y="160" width="17" height="12" rx="3" fill="#d29922"/>
39
+ <text class="val" x="655" y="171" fill="#d29922">3.2%</text>
40
+
41
+ <text class="lbl" x="28" y="198">investigate</text>
42
+ <rect x="120" y="188" width="527" height="12" rx="3" fill="#58a6ff" opacity="0.25"/>
43
+ <rect x="120" y="188" width="9" height="12" rx="3" fill="#58a6ff"/>
44
+ <text class="val" x="655" y="199" fill="#58a6ff">1.8%</text>
45
+
46
+ <text class="lbl" x="28" y="226">wait</text>
47
+ <rect x="120" y="216" width="527" height="12" rx="3" fill="#8b949e" opacity="0.25"/>
48
+ <rect x="120" y="216" width="6" height="12" rx="3" fill="#8b949e"/>
49
+ <text class="val" x="655" y="227" fill="#8b949e">1.2%</text>
50
+
51
+ <!-- question 2: urgent -->
52
+ <text class="key" x="28" y="262">urgent</text>
53
+ <text class="dim" x="92" y="262">"Roll back right now?" — noul</text>
54
+
55
+ <text class="lbl" x="28" y="288">true</text>
56
+ <rect x="120" y="278" width="527" height="12" rx="3" fill="#f85149" opacity="0.25"/>
57
+ <rect x="120" y="278" width="416" height="12" rx="3" fill="#f85149"/>
58
+ <text class="val" x="655" y="289" fill="#f85149">78.7%</text>
59
+
60
+ <text class="lbl" x="28" y="316">false</text>
61
+ <rect x="120" y="306" width="527" height="12" rx="3" fill="#8b949e" opacity="0.25"/>
62
+ <rect x="120" y="306" width="112" height="12" rx="3" fill="#8b949e"/>
63
+ <text class="val" x="655" y="317" fill="#8b949e">21.3%</text>
64
+
65
+ <!-- raw json note -->
66
+ <text class="json" x="28" y="356">{ "model": "clef-flash",</text>
67
+ <text class="json" x="28" y="374"> "<tspan class="key">decisions</tspan>": { "action": { "answer": { "rollback": 0.939, ... } },</text>
68
+ <text class="json" x="28" y="392"> "urgent": { "answer": { "true": 0.787, "false": 0.213 } } } }</text>
69
+ <text class="dim" x="28" y="410">one forward pass · no prose · no sampling · offline</text>
70
+ </svg>