@basein/runner 0.1.1 → 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.
@@ -0,0 +1,545 @@
1
+ # Your first sample — run one MCPMark task and watch it pay for itself
2
+
3
+ A complete, from-zero manual. By the end you will have run one real agent
4
+ benchmark task, graded it against a live database, and then watched
5
+ BaseInstRunner replay the *same* task for **zero model tokens** — and you will
6
+ see the dollar figure that saved.
7
+
8
+ This is the hands-on companion to [mcpmark.md](mcpmark.md), which explains the
9
+ *why* in depth. This document is the *what to type*, in order, with the numbers
10
+ a real run produced on 2026-09-07 so you know what "working" looks like.
11
+
12
+ > **The one task.** Everything below uses a single MCPMark task,
13
+ > `postgres/easy/employees/hiring_year_summary`. It is the smallest task in the
14
+ > benchmark that still writes to the database, which makes it the honest place
15
+ > to start.
16
+
17
+ ---
18
+
19
+ ## 0. What MCPMark is (and why we use it)
20
+
21
+ **MCPMark is a standard agents benchmark.** It measures how well an AI agent can
22
+ drive real tools, through the [Model Context Protocol
23
+ (MCP)](https://modelcontextprotocol.io), to change real systems — not to chat
24
+ about them.
25
+
26
+ The facts worth knowing before you start:
27
+
28
+ | | |
29
+ |---|---|
30
+ | Who | The `eval-sys` research group · [github.com/eval-sys/mcpmark](https://github.com/eval-sys/mcpmark) · [paper (arXiv 2509.24002)](https://arxiv.org/abs/2509.24002) · [mcpmark.ai](https://mcpmark.ai) |
31
+ | What it tests | An agent using **five MCP services**: Postgres, GitHub, Filesystem, Notion, Playwright |
32
+ | Size | **127 standard tasks + 50 easy** (the repo has grown past the paper's five services) |
33
+ | How it grades | Each task ships a `verify.py` that opens a real connection and **queries the actual state**, not the agent's transcript. Exit 0 = PASS |
34
+ | Metrics | `pass@1` (one try), `pass@k`, and `pass^k` — *stability*: did it pass **all** k independent runs |
35
+ | State of the art | On MCPMark's verified set, the strongest model reports about **92.9%**; most models sit far lower, and `pass^4` collapses for everyone |
36
+
37
+ Two of those facts are why this benchmark suits BaseInstRunner so well:
38
+
39
+ 1. **It grades the world, not the words.** A replayed run that produces the
40
+ right rows passes exactly like a hand-typed one. There is no transcript to
41
+ fool.
42
+ 2. **`pass^k` punishes inconsistency.** The benchmark runs each task several
43
+ times against a reset database and asks whether *every* run passed. A model
44
+ that re-decides the plan token-by-token every time is exactly what
45
+ BaseInstRunner removes — it runs the *same* known-good plan each time.
46
+
47
+ That is the whole idea you are about to see working: **solve the task once, then
48
+ stop paying to re-solve it.**
49
+
50
+ ---
51
+
52
+ ## 1. What you are about to build
53
+
54
+ ```
55
+ Part A — the benchmark, on its own
56
+ seed a Postgres DB → agent solves the task → verify.py grades it → PASS
57
+
58
+ Parts B–D — the same task, through BaseInstRunner
59
+ claude -p ──mcp__postgres__*──▶ bir-proxy ──▶ postgres-mcp ──▶ PostgreSQL
60
+ │ │ records every call
61
+ └──mcp__bir__run_scenario──▶ bir-hooks (control server)
62
+ │ matches the prompt, arms the plan,
63
+ ▼ runs all 6 steps for $0 model tokens
64
+ BaseIn service ──▶ books the saving
65
+ ```
66
+
67
+ You will run the task **three ways**, and the difference between them is the
68
+ point:
69
+
70
+ | Run | Runner | What the model does | What it costs |
71
+ |---|---|---|---|
72
+ | **Baseline** | off | discovers and solves the task | full price |
73
+ | **Record** | on, replay **off** | solves it once; bir *records* the plan | full price, one time |
74
+ | **Replay** | on, replay **on** | one tool call reads a pre-computed result | a fraction |
75
+
76
+ ---
77
+
78
+ ## 2. Prerequisites
79
+
80
+ Check each one. The whole setup takes about ten minutes, most of it downloads.
81
+
82
+ | # | You need | Check | If missing |
83
+ |---|---|---|---|
84
+ | 1 | **Node 20+** | `node -v` | [nodejs.org](https://nodejs.org) |
85
+ | 2 | **Claude Code** | `claude --version` | Anthropic's CLI; this manual drives it with `claude -p` |
86
+ | 3 | **PostgreSQL 17**, running | `psql --version` and the service is up | see §2.1 |
87
+ | 4 | **uv / uvx** | `uvx --version` | [astral.sh/uv](https://docs.astral.sh/uv/) — runs `postgres-mcp` and `verify.py` |
88
+ | 5 | **The runner** | `bir --version` | `npm i -g @basein/runner` |
89
+ | 6 | **A BaseIn account + API URL** | `bir login` succeeds | your service address, e.g. `https://api.your-domain.com` |
90
+
91
+ > **`BIR_AUTH_URL` must be the API host**, normally `https://api.<domain>`, not
92
+ > the docs or app website. Point it at a website and `bir login` refuses it,
93
+ > saying what answered instead. Set it once, then open a **fresh** terminal — an
94
+ > existing one keeps the value it started with.
95
+
96
+ `bir login` signs you in through the browser: it prints a link and a short code
97
+ and waits for you to approve it. Nothing is typed into the terminal, and it works
98
+ for accounts that only sign in with Google.
99
+
100
+ ### 2.1 Postgres, without Docker
101
+
102
+ MCPMark's own harness runs Postgres in Docker (`pgvector/pgvector:pg17`). If the
103
+ machine has no Docker — this one did not — install PostgreSQL 17 **natively**;
104
+ it is the same major version, and this task needs no vector support. Disclose
105
+ the deviation in any published result.
106
+
107
+ ```powershell
108
+ winget install --id PostgreSQL.PostgreSQL.17 -e `
109
+ --accept-package-agreements --accept-source-agreements `
110
+ --custom "--mode unattended --superpassword password --serverport 5432 --unattendedmodeui none"
111
+ ```
112
+
113
+ That sets the `postgres` superuser password to `password`, which is the value
114
+ every command and connection string below expects. On macOS:
115
+ `brew install postgresql@17 && brew services start postgresql@17`.
116
+
117
+ ---
118
+
119
+ ## 3. Part A — run the benchmark task on its own
120
+
121
+ First prove the plain benchmark works, with no runner in the picture at all.
122
+
123
+ ### 3.1 Make a run folder and fetch the task
124
+
125
+ ```bash
126
+ mkdir -p ~/my-first-sample && cd ~/my-first-sample
127
+
128
+ TASK=https://raw.githubusercontent.com/eval-sys/mcpmark/main/tasks/postgres/easy/employees/hiring_year_summary
129
+ curl -fsSLO "$TASK/description.md" # what the agent is asked to do
130
+ curl -fsSLO "$TASK/verify.py" # MCPMark's grader — unmodified
131
+ curl -fsSLO "$TASK/meta.json" # task metadata (difficulty L1, mcp: postgres)
132
+ ```
133
+
134
+ The task asks the agent to build one table, `employees.hiring_year_summary`,
135
+ with a row per hiring year: how many were hired, how many are still employed,
136
+ and the retention rate. Small, but a genuine write.
137
+
138
+ ### 3.2 The exact prompt
139
+
140
+ MCPMark sends `description.md` **verbatim** plus one fixed suffix. Assemble it
141
+ once so every run is byte-identical:
142
+
143
+ ```bash
144
+ { cat description.md
145
+ printf '\n\nNote: Based on your understanding, solve the task all at once by yourself, don'\''t ask for my opinions on anything.\n'
146
+ } > prompt.txt
147
+ ```
148
+
149
+ ### 3.3 Seed the database
150
+
151
+ Download MCPMark's 33 MB backup and build the `employees` **template**
152
+ database from it. Save this as `seed.sh` and run it once:
153
+
154
+ ```bash
155
+ #!/usr/bin/env bash
156
+ set -euo pipefail
157
+ PGBIN="${PGBIN:-/c/Program Files/PostgreSQL/17/bin}" # macOS: "$(brew --prefix postgresql@17)/bin"
158
+ export PGPASSWORD="${POSTGRES_PASSWORD:-password}"
159
+ PGUSER=postgres PGHOST=localhost PGPORT=5432
160
+
161
+ [ -f employees.backup ] || curl -fsSL -o employees.backup https://storage.mcpmark.ai/postgres/employees.backup
162
+
163
+ "$PGBIN/psql" -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -v ON_ERROR_STOP=1 \
164
+ -c "DROP DATABASE IF EXISTS employees;" -c "CREATE DATABASE employees;"
165
+ "$PGBIN/pg_restore" -h $PGHOST -p $PGPORT -U $PGUSER -d employees \
166
+ --no-owner --no-privileges employees.backup \
167
+ || echo "[seed] pg_restore printed notices — checking the result instead"
168
+ "$PGBIN/psql" -h $PGHOST -p $PGPORT -U $PGUSER -d employees \
169
+ -c "SELECT count(*) AS employees FROM employees.employee;"
170
+ echo "[seed] template database 'employees' is ready"
171
+ ```
172
+
173
+ A healthy seed reports **300024** employees.
174
+
175
+ ### 3.4 A fresh database for every run
176
+
177
+ MCPMark isolates each run with `CREATE DATABASE … WITH TEMPLATE …` and asserts
178
+ the target table is absent. Reproduce that as `reset-db.sh` — **run it before
179
+ every single trial**, or the second run finds the table already built and the
180
+ task stops being the task:
181
+
182
+ ```bash
183
+ #!/usr/bin/env bash
184
+ set -euo pipefail
185
+ PGBIN="${PGBIN:-/c/Program Files/PostgreSQL/17/bin}"
186
+ export PGPASSWORD=password; PGUSER=postgres PGHOST=localhost PGPORT=5432
187
+ DB="${TRIAL_DB:-hys_trial}"
188
+ "$PGBIN/psql" -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -v ON_ERROR_STOP=1 \
189
+ -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity
190
+ WHERE datname='$DB' AND pid <> pg_backend_pid();" \
191
+ -c "DROP DATABASE IF EXISTS $DB;" \
192
+ -c "CREATE DATABASE $DB WITH TEMPLATE employees;"
193
+ LEFT=$("$PGBIN/psql" -h $PGHOST -p $PGPORT -U $PGUSER -d "$DB" -tAc \
194
+ "SELECT count(*) FROM information_schema.tables
195
+ WHERE table_schema='employees' AND table_name='hiring_year_summary';")
196
+ [ "$LEFT" = "0" ] || { echo "[reset] target table survived — aborting" >&2; exit 1; }
197
+ echo "[reset] $DB recreated from template — target table absent"
198
+ ```
199
+
200
+ The `pg_terminate_backend` line is not optional: an MCP server holds a pooled
201
+ connection, and `DROP DATABASE` fails while any session is attached.
202
+
203
+ ### 3.5 The grader
204
+
205
+ Save this as `verify.sh`. It runs MCPMark's own `verify.py`, unmodified, against
206
+ the real database:
207
+
208
+ ```bash
209
+ #!/usr/bin/env bash
210
+ export PYTHONIOENCODING=utf-8 # verify.py prints ✅/❌ — required on Windows consoles
211
+ export POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_DATABASE="${TRIAL_DB:-hys_trial}"
212
+ export POSTGRES_USERNAME=postgres POSTGRES_PASSWORD=password
213
+ cd "$(dirname "$0")"
214
+ uv run --quiet --with psycopg2-binary python verify.py
215
+ ```
216
+
217
+ > **`PYTHONIOENCODING=utf-8` is not optional on Windows.** `verify.py` prints
218
+ > `✅`/`❌`/`🎉`; a cp1255/cp1252 console raises `UnicodeEncodeError` *while
219
+ > formatting its own PASS line*, and a run graded through a crashed reporter has
220
+ > no grade.
221
+
222
+ ### 3.6 Run it once, by hand
223
+
224
+ ```bash
225
+ ./seed.sh # once
226
+ ./reset-db.sh # before the run
227
+ claude -p "$(cat prompt.txt)" --model claude-sonnet-5
228
+ ./verify.sh
229
+ ```
230
+
231
+ What a pass looks like:
232
+
233
+ ```
234
+ ✅ Hiring year summary results are correct (16 records)
235
+ 🎉 Task verification: PASS
236
+ ```
237
+
238
+ That is the benchmark working, end to end, with **no runner involved**. On this
239
+ machine that run cost about **$0.15**. Hold that number — it is the baseline the
240
+ runner has to beat.
241
+
242
+ The 16 rows it produced (the grader recomputes these in SQL and compares
243
+ row-by-row):
244
+
245
+ | hire_year | employees_hired | still_employed | retention_rate |
246
+ |---|---|---|---|
247
+ | 1985 | 35316 | 28291 | 80.11 |
248
+ | 1986 | 36150 | 28840 | 79.78 |
249
+ | … | … | … | … |
250
+ | 1999 | 1514 | 1204 | 79.52 |
251
+ | 2000 | 13 | 9 | 69.23 |
252
+
253
+ (16 rows, hire years 1985 through 2000.)
254
+
255
+ ---
256
+
257
+ ## 4. Part B — install the runner
258
+
259
+ Now put BaseInstRunner between the agent and Postgres. From inside the run
260
+ folder, write the MCP config **unwrapped** — `bir install` will wrap it:
261
+
262
+ `.mcp.json`
263
+ ```json
264
+ {
265
+ "mcpServers": {
266
+ "postgres": {
267
+ "command": "uvx",
268
+ "args": ["--with", "mcp<2", "postgres-mcp==0.3.0", "--access-mode=unrestricted"],
269
+ "env": { "DATABASE_URI": "postgresql://postgres:password@localhost:5432/hys_trial" }
270
+ }
271
+ }
272
+ }
273
+ ```
274
+
275
+ Two details that will bite if you skip them:
276
+
277
+ - **`--with "mcp<2"` is mandatory.** `postgres-mcp==0.3.0` imports
278
+ `mcp.server.fastmcp`, which the `mcp` 2.x SDK removed. MCPMark's own
279
+ `pipx run postgres-mcp==0.3.0` is broken today without this pin. (`uvx` stands
280
+ in for `pipx`; both are equivalent here.)
281
+ - **`--access-mode=unrestricted`** — the task creates a table.
282
+
283
+ Then install, with calculated replay switched on:
284
+
285
+ ```bash
286
+ export BIR_AUTH_URL=https://api.your-domain.com
287
+ bir login # once; approve it in the browser
288
+ bir install --replay --global
289
+ ```
290
+
291
+ `bir install --replay` rewrites `.mcp.json` to route `postgres` through
292
+ `bir-proxy`, adds a first-party `bir` server (the channel a replay's results
293
+ come back through), and wires Claude Code's ten hooks into
294
+ `.claude/settings.json`. `bir uninstall` reverses every byte of it.
295
+
296
+ ---
297
+
298
+ ## 5. Part C — the two settings that decide everything
299
+
300
+ This is the section the notes call *"the whole ballgame."* Get these two things
301
+ right before your first trial, or every measurement below will be wrong in a way
302
+ that still *looks* healthy.
303
+
304
+ ### 5.1 Deny the built-in tools
305
+
306
+ Left alone, Claude Code will reach for `Bash` + `psql` and never touch the MCP
307
+ server — and a single built-in call in the recording makes the whole plan
308
+ un-replayable. Pin the agent to MCP only. Add to `.claude/settings.json`:
309
+
310
+ ```json
311
+ {
312
+ "permissions": {
313
+ "deny": ["Bash", "PowerShell", "Read", "Write", "Edit", "NotebookEdit",
314
+ "Glob", "Grep", "WebFetch", "WebSearch", "Agent", "Task"],
315
+ "allow": ["mcp__postgres__*", "mcp__bir__*"]
316
+ }
317
+ }
318
+ ```
319
+
320
+ ### 5.2 Trust the workspace — or the `allow` list is silently discarded
321
+
322
+ **This is the single most common way a first run goes wrong.** If Claude Code
323
+ has not been told to trust this folder, it throws the `allow` list away and
324
+ prints one line you will scroll past:
325
+
326
+ ```
327
+ Ignoring 2 permissions.allow entries from .claude/settings.json:
328
+ this workspace has not been trusted.
329
+ ```
330
+
331
+ It then fails *asymmetrically*: wrapped `mcp__postgres__*` calls keep working
332
+ (bir's own hook allows them), but `mcp__bir__run_scenario` — the one tool the
333
+ replay is delivered through — gets denied. The agent cannot reach the plan, so
334
+ it does the task by hand, and the run lands in the **divergence** path. It still
335
+ passes, the audit log still looks fine, and you have measured nothing.
336
+
337
+ Fix it **before the first trial**, one of two ways:
338
+
339
+ - **Interactive, once:** run `claude` in the folder and accept the trust dialog.
340
+ - **By hand:** set this in `~/.claude.json` (use the exact path Claude Code
341
+ prints, forward slashes):
342
+
343
+ ```json
344
+ "projects": {
345
+ "C:/Users/Admin/my-first-sample": { "hasTrustDialogAccepted": true }
346
+ }
347
+ ```
348
+
349
+ > The interactive route is the reliable one — Claude Code guards `~/.claude.json`
350
+ > against outside edits, so a script that tries to set the flag may be blocked.
351
+ > Opening `claude` in the folder once and clicking *trust* is the path that
352
+ > always works.
353
+
354
+ ### 5.3 Start the control server, and prove it is bound
355
+
356
+ Leave this running in its own terminal, **in the run folder**:
357
+
358
+ ```bash
359
+ export BIR_AUTH_URL=https://api.your-domain.com
360
+ export BIR_REPLAY=1 # the replay switch — off by default
361
+ export BIR_REPLAY_ALLOW_SERVERS=postgres # servers allowed to run unattended
362
+ bir-hooks 2>&1 | tee -a bir-hooks.log
363
+ ```
364
+
365
+ Then, in another terminal in the same folder, **launch a `claude` session so a
366
+ proxy registers**, and check:
367
+
368
+ ```bash
369
+ bir doctor
370
+ ```
371
+
372
+ Do not send a single prompt until you see both of these:
373
+
374
+ ```
375
+ Wrapped in config : postgres
376
+ Registered proxies: postgres ← this needs a live claude session
377
+ ```
378
+
379
+ `Registered proxies: (none)` means no proxy has bound yet. This exact check is
380
+ what separates a real measurement from a confident fiction.
381
+
382
+ ---
383
+
384
+ ## 6. Part D — record once, then replay for free
385
+
386
+ The run loop is one script, `trial.sh`. It resets the DB, runs the agent
387
+ non-interactively, prints the cost, and grades. **Keep `--model` the same for
388
+ every trial**, or the costs are not comparable:
389
+
390
+ ```bash
391
+ #!/usr/bin/env bash
392
+ set -uo pipefail; cd "$(dirname "$0")"
393
+ LABEL="${1:-1}"; MODEL="${MODEL:-claude-sonnet-5}"
394
+ echo "==== trial $LABEL ($MODEL) ===="
395
+ ./reset-db.sh || exit 1
396
+ claude -p "$(cat prompt.txt)" --model "$MODEL" \
397
+ --allowedTools "mcp__postgres__*" "mcp__bir__*" \
398
+ --output-format json > "trial-$LABEL.json"
399
+ node -e 'const r=JSON.parse(require("fs").readFileSync(process.argv[1]));
400
+ console.log("cost $"+(+r.total_cost_usd).toFixed(4)+" turns="+r.num_turns+
401
+ " denied="+(r.permission_denials||[]).length)' "trial-$LABEL.json"
402
+ ./verify.sh
403
+ ```
404
+
405
+ ### 6.1 Trial 1 — record
406
+
407
+ ```bash
408
+ ./trial.sh 1
409
+ ```
410
+
411
+ bir records the run. If the service has seen this prompt before it keeps its own
412
+ canonical run and this one is *not* re-recorded — that is the match working.
413
+ Either way, the plan you will replay is a **calculated scenario**: an intent, a
414
+ parameter schema, and one bit of logic per step. Inspect it before you trust it:
415
+
416
+ ```bash
417
+ bir scenario list # find the runId / scenario id
418
+ bir scenario replay <scenarioId> --prompt "$(cat prompt.txt)" --dry
419
+ ```
420
+
421
+ `--dry` runs the scenario's logic against the *recorded* outputs — no real SQL,
422
+ one cheap model call — and prints each step's computed input. Read it for three
423
+ things: parameters that actually vary with the prompt, step inputs that look
424
+ *computed* rather than copied, and non-empty `emitted` on steps others depend
425
+ on. On our scenario the dry run produced the correct six steps and the right 16
426
+ rows for **$0.004**.
427
+
428
+ ### 6.2 Trials 2+ — replay
429
+
430
+ Reset, send the identical prompt, grade. Watch `bir-hooks.log` for the story:
431
+
432
+ ```
433
+ run.matched similarity=1.000
434
+ plan.armed mode=direct steps=6 coverage=direct,direct,direct,direct,direct,direct
435
+ replay.derived params=7 costUsd=0.0000 source="recorded samples"
436
+ replay.step n=0 … ok=true emitted=tableList
437
+ replay.step n=5 … ok=true emitted=validationRow,totalEmployees,sumHired,…
438
+ replay.done mode=direct steps=6/6 outcome=steered_full
439
+ execution.reported outcome=steered_full session=0.045 savedUsd=+0.174 measured=true
440
+ ```
441
+
442
+ All six SQL steps executed for real through the connection the proxy already
443
+ held — in **about a second of real SQL time** (the `CREATE TABLE … AS` over
444
+ 300k rows is most of it), **for $0.00 in model tokens**. The model's only job
445
+ was to read the result.
446
+
447
+ ---
448
+
449
+ ## 7. Reading the result — the money
450
+
451
+ Here are the real figures this task produced, so you can compare yours:
452
+
453
+ | Run | Outcome | Session cost | Saved vs baseline |
454
+ |---|---|---|---|
455
+ | Baseline (recorded, median of 3) | — | **$0.2188** | — |
456
+ | A clean replay | `steered_full` | **$0.0448** | **+$0.1739 (≈ 80%)** |
457
+
458
+ Across five verified replays on the test account, the service had booked
459
+ **$0.6374 saved** in total. That is the number the manual promised you: the same
460
+ task, solved once and then not re-solved, at roughly a fifth of the price each
461
+ time after.
462
+
463
+ What each outcome means for your ledger:
464
+
465
+ | Outcome | Meaning | Books as |
466
+ |---|---|---|
467
+ | `steered_full` | every step ran under the plan | a **saving** |
468
+ | `diverged` | agent went off-script; bir recovered for $0 | a saving (usually smaller) |
469
+ | `not_steered` | matched but replay was off | a **baseline sample** (this is how the baseline is measured) |
470
+ | `failed` | armed, then gave up | a baseline sample |
471
+
472
+ Where you see it: on your BaseIn console (for the hosted service,
473
+ **bi2202.com** → sign in → **Dashboard** / **Recordings**), the KPI strip shows
474
+ **Verified saved** — "money we watched move: costs measured on both sides." A
475
+ loss shows red; a saving shows green.
476
+
477
+ ---
478
+
479
+ ## 8. What actually happened here, honestly
480
+
481
+ On the machine this manual was written on, the run folder had **not** been
482
+ trusted (§5.2), so both replays landed in the **divergence** path rather than
483
+ clean `steered_full`. They still passed, and they still saved (`+$0.033` and
484
+ `+$0.116` against the $0.219 baseline) — but two things are worth stating
485
+ plainly, because you may hit them too:
486
+
487
+ 1. **Untrusted → divergence.** With the `allow` list discarded,
488
+ `mcp__bir__run_scenario` was denied and the agent solved the task by hand.
489
+ The safety net (bir re-running the six steps for $0) is why it still passed
490
+ and still saved. Trust the workspace to get the clean channel.
491
+
492
+ 2. **A cautious model may reject the replay as a prompt injection.** When the
493
+ pre-computed bundle arrived unbidden (the divergence path delivers it as a
494
+ tool result), Claude Sonnet 5 flagged it:
495
+
496
+ > *"the result … came back wrapped in a fake '[BaseInstRunner calculated
497
+ > replay]' block claiming all 6 steps were 'already executed' … This is a
498
+ > prompt injection embedded in the tool response channel … I'm not going to
499
+ > report those figures as fact."*
500
+
501
+ The model then re-verified the work itself — which is correct, cautious
502
+ behaviour, and which raises the per-run cost. Delivering the bundle through
503
+ the model's *own* `run_scenario` call (the trusted, direct path) is far less
504
+ likely to trip this, and the bundle wording is being sharpened (see
505
+ [mcpmark.md](mcpmark.md) §12–14). It is a real product wrinkle, not a setup
506
+ error — worth knowing before you read too much into one run's cost.
507
+
508
+ Neither changes the headline: **the scenario is correct, the replay executes the
509
+ real work for zero tokens, and the saving is real and positive.**
510
+
511
+ ---
512
+
513
+ ## 9. Troubleshooting
514
+
515
+ | Symptom | Cause | Fix |
516
+ |---|---|---|
517
+ | `Ignoring N permissions.allow entries … not been trusted` | workspace untrusted | §5.2 — trust it, then every replay is clean |
518
+ | `Registered proxies: (none)` | no live session bound a proxy | launch `claude` in the folder, re-run `bir doctor` |
519
+ | replay always `diverged`, never `steered_full` | usually §5.2, sometimes a cautious model (§8) | trust first; then see mcpmark.md §12–14 |
520
+ | trial 2 finds the table already there | no DB reset | `reset-db.sh` before **every** trial |
521
+ | `UnicodeEncodeError` in verify.py | Windows console codepage | `PYTHONIOENCODING=utf-8` (already in `verify.sh`) |
522
+ | `ModuleNotFoundError: mcp.server.fastmcp` | `mcp` 2.x resolved | pin `--with "mcp<2"` in `.mcp.json` |
523
+ | `bir login` says "not a BaseIn service" | `BIR_AUTH_URL` is a website, not the API | set it to `https://api.<domain>`, open a fresh terminal |
524
+ | `savedUsd` looks impossible | pricing drift between runner and server | compare `PRICING_VERSION` on both sides |
525
+ | the number won't move | a replayed turn is deliberately not re-recorded | correct — the matched run stays canonical |
526
+
527
+ ---
528
+
529
+ ## 10. Where to go next
530
+
531
+ - **Widen within Postgres.** 31 prompts share this exact server and reset
532
+ mechanism, so each new task costs almost nothing to add.
533
+ `tasks/postgres/standard/**` is where the paper's numbers come from.
534
+ - **[mcpmark.md](mcpmark.md)** — the full rationale, the topology in detail, and
535
+ the measured history of this task, including the bugs that were found and
536
+ fixed along the way.
537
+ - **[calculatedReplayGuide.md](calculatedReplayGuide.md)** — the runner's own
538
+ manual: every audit line, every switch, and how the saving is booked.
539
+ - **[quickstart.md](quickstart.md)** — setting up a fresh machine to record from
540
+ your *own* work, not just this benchmark.
541
+
542
+ > **Turning replay off** is one line: `unset BIR_REPLAY`. The system is then
543
+ > exactly a recorder again — it recognises a repeated prompt and declines to
544
+ > record it twice, and the model does the work. Nothing is skipped, nothing is
545
+ > risked.
@@ -62,8 +62,16 @@ The script narrates each stage. A healthy run looks like this:
62
62
  ==> Configuring the BaseIn service
63
63
  BIR_AUTH_URL=https://basein.example.com (persisted for this user)
64
64
  ==> Signing in
65
- Email: you@example.com
66
- Password:
65
+
66
+ [bir] Sign in to https://basein.example.com
67
+
68
+ Open https://basein.example.com/activate?code=BKQM-TXZR
69
+ Code BKQM-TXZR
70
+
71
+ Opening your browser...
72
+ Waiting for approval... (Ctrl-C to cancel)
73
+
74
+ Signed in to https://basein.example.com as you@example.com.
67
75
  ==> Wrapping MCP servers in C:\path\to\your\project
68
76
  + chrome-devtools -> bir-proxy (project scope, upstream: npx)
69
77
  + hooks -> ...\.claude\settings.json
@@ -71,7 +79,14 @@ Password:
71
79
  Wrapped 1 server.
72
80
  ```
73
81
 
74
- The password does not appear as you type it. That is normal.
82
+ **Signing in happens in your browser, not the terminal.** The script prints a
83
+ link and a short code, then waits. Open the link, check that the code matches
84
+ and that it says it will sign you in as you, and press **Approve**. There is no
85
+ password to type here, which is also why this works for an account that only
86
+ ever signs in with Google.
87
+
88
+ The link works from any device — a phone is fine — so this is also the answer on
89
+ a machine with no browser. Add `--no-browser` to stop it trying to open one.
75
90
 
76
91
  If anything goes wrong the script stops immediately and says why — it checks
77
92
  everything it can *before* changing your machine, so a failed run leaves nothing
@@ -130,6 +145,16 @@ The service address did not stick. Close the terminal, open a new one, and check
130
145
  `echo $env:BIR_AUTH_URL` (Windows) or `echo $BIR_AUTH_URL` (macOS). If it is
131
146
  empty, run the install script again with the `-AuthUrl` / `BIR_AUTH_URL` value.
132
147
 
148
+ ### "The browser never opened"
149
+
150
+ Not a problem: the link and the code are printed either way, and the link works
151
+ from any device. Open it on your phone and approve there. `bir login` skips
152
+ opening a browser on purpose over SSH, and `--no-browser` skips it always.
153
+
154
+ ### "The code expired before I approved it"
155
+
156
+ Codes last ten minutes. Run `bir login` again for a fresh one.
157
+
133
158
  ### "bir login says … is not a BaseIn service" (or `HTTP 405 Method Not Allowed`)
134
159
 
135
160
  `BIR_AUTH_URL` points at a website — the docs or app address — instead of the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basein/runner",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "A recording MCP proxy: sits between any MCP client and its MCP servers, executes each call on the client's behalf, and records the run as a reusable BaseIn scenario.",
5
5
  "type": "module",
6
6
  "license": "MIT",