The Basic2202 Handbook
Basic2202 Record · Calculate · Replay Handbook, revision 11

Stop paying twice
for the same run.

Your agent worked this out yesterday. Today it paid full price to work it out again. Basic2202 records the run, derives the plan, runs the plan next time — and then measures what that actually saved, rather than claiming it.

The distinction the whole product is built on — example figures
Verified $412.80
Projected $190.15

Verified is money we watched move: costs measured on both sides and reported by the process that spent them. Projected is booked from costs nobody measured end to end — real rows, honest arithmetic, partial inputs. The console leads with the first number and never adds the second into it.

One

What is Basic2202?

An agent given a task works out a plan, calls tools, reads what comes back, and answers. Give it the same task tomorrow with one value changed — a different region, a different customer, a different week — and it works the same plan out again from nothing.

The tokens, the latency and the tool calls are all spent a second time to arrive at a conclusion the first run already reached. Basic2202 records those runs, derives the plan from one of them, and runs the plan directly the next time the same question comes round.

The loop

Three steps. Each is a thing you can see in the console, and each can be inspected before you trust the next.

Step one Record

The agent works normally. Every tool call, its arguments, what came back, how long it took and what it cost is stored — along with the prompt that started it and the answer it reached.

Produces · a run
Step two Calculate

The transcript becomes logic: an intent, a parameter schema naming what was incidental to that particular run, and one body per step that recomputes that step's arguments.

Produces · a scenario
Step three Replay

A prompt that means the same thing as a recorded one runs the logic instead of being solved again. The tools are really called; only the rediscovery is skipped.

Produces · a saving

Record

The runner sits in the agent's execution path and stores each run. Nothing about the model's behaviour changes — the recorder executes the calls the model was going to make anyway and returns the results unaltered.

A run with three tool actions or fewer is not listed as a recording: it is never embedded, matches nothing, and is eventually pruned. Below that threshold there is no plan to derive, only a single call and its answer. Each finished run gets a title of at most 40 characters, written by a model from its transcript, so the list reads as a list of tasks rather than a list of ids.

Claude Code answering: check the eu-west-1 fleet and tell me which device is most at risk
A run, as the agent saw it. Claude Code is given a task, calls the fleet tool, and answers. Nothing about the agent changed; the runner recorded it from the side, and a run like this is what a recording is made of.

Calculate

A recorded run is a transcript: what happened, once, with those exact values. A calculated scenario is what Basic2202 derives from it — an intent, a parameter schema, and one logic body per step.

Press Calculate yourself, or let the service choose: whenever a prompt comes back it takes the recording with the most to save — among those whose prompt has come back at least three times and that passed end to end, the run finishing without an error and no tool call in it failing — and calculates it. It never replaces a scenario you already have. Would rather it were a different recording? Prefer that one: it is calculated at once, taking the place of an automatic scenario (or, after you confirm, one you calculated by hand), and nothing evicts it in turn until you withdraw the preference.

The distinction

The transcript says what the agent did. The scenario says what the agent was doing, which is the only half worth running again.

The Calculated scenario panel on a recording's page: the Prefer and Switch off buttons, the intent with its parameters, and the last execution
A scenario, as the console shows it. The intent names the task, not the fleet. The parameters — fleet and id — are what may change next time. The last execution says what one reuse cost and what it saved.
One step of a scenario from an earlier eu-west-1 recording: its description, the recorded input, the input logic and the output logic
One step's logic. An example from an earlier recording, on the eu-west-1 fleet. return { fleet: parameters.fleet } sends whatever fleet the new prompt names; in this example the output logic sorts what comes back by risk and hands the worst device to the next step. Nothing is remembered — everything is recomputed. A plan that had memorised one device id would look identical today and be wrong the day the data changed.

Replay

Matching is on the prompt text, embedded and compared by similarity — not the transcript and not the answer. Above the detection threshold (default 0.9) the service recognises a match; above the steering threshold (default 0.92) the runner acts on it.

  • Fully wrapped — every step is a tool the proxy already holds a connection to. The sequence runs through those connections with zero model tokens; the model's only job is to read the results.
  • Mixed — some steps are built-in tools the proxy cannot execute. The live session is steered instead: each step runs as normal, with its arguments pinned to what the scenario computed.

The savings ledger, and why it is careful

The headline is verified saved. It is deliberately smaller than the number Basic2202 could report, and the gap is the point.

Verified

Money we watched move. Costs measured on both sides — what the agent's run cost, what the replay cost — reported by the process that spent them. This is the number the dashboard leads with.

Projected

Booked from costs nobody measured end to end. Real rows, honest arithmetic, partial inputs. Shown beside the headline and never added into it.

The three headline tiles: Verified saved, Calculated scenarios, Calculated runs
The headline, as the console shows it. The verified figure leads, with the runs it is measured over underneath. Beside it: how many calculated scenarios the account holds, and how many times a scenario has done the work. Here: $0.1747 verified over 2 measured runs, 1 calculated scenario, 2 calculated runs.

The distinction matters because of how the arithmetic fails when a cost is missing. An unmeasured replay usually has only its parameter-derivation call priced, so baseline − cost books nearly the whole baseline as profit. A ledger that summed those would report a large, confident, wrong number.

Two more rules in the same spirit:

  • A baseline drawn from one run is labelled as one run. Every saving on a row is measured against what the agent costs on that task. If that figure comes from a single sample it is a guess, and the console says so rather than presenting a statistic.
  • A scenario can lose money, and it will say so. If recent replays cost more than the agent does, the row is flagged as not paying off. Savings are not clamped at zero — a scenario quietly costing you money is exactly the thing you need to be told.

Two run types are counted but save nothing, and are reported separately. A dry replay evaluates stored logic against recorded outputs — a check, not work. A not-steered hit is a matched prompt the runner declined to replay, which becomes another baseline sample instead.

Scenario caching, not semantic caching

AI gateways such as TrueFoundry's and Gravitee's offer semantic caching. The gateway keeps the model's reply to each prompt, keyed by an embedding of that prompt. When a new prompt is close enough to one it has seen, above a similarity threshold you set, it returns the stored reply and the model is never called. For what it is built for it is a good tool: the same question asked many times in slightly different words, where the same answer is still right. A help desk answering the password-reset question is the textbook case.

Basic2202 starts the same way and then does something different. It also embeds the request and looks for a close match among your earlier ones, with a stricter threshold and more checks before anything runs. But what it keeps and reuses is not the answer. It is the scenario: the plan the agent worked out, with the values that vary taken out of it, and a rule per step for recomputing that step's arguments from the new request and from what the previous step returned. On a match the tools are really called, against today's data, and the model reads the results and writes today's answer. What is skipped is the working-out, not the work. The honest name for it is scenario caching: it caches the plan.

Semantic cacheBasic2202: scenario caching
What is storedThe model's reply, keyed by the prompt's embeddingThe plan: an intent, the parameters, one rule per step
What a hit returnsThe stored reply, without calling the modelA fresh run of the plan: live tool calls, a new answer
Right whenThe same question deserves the same answerThe same task comes back with different values: another fleet, another customer, another week
Wrong whenThe world moved since the reply was stored, or the near-match was a different questionThe task is exploratory, so there is no plan to reuse
Where it sitsIn front of the model, in the gatewayBeside the agent, on the path to its tools
What it savesThe whole model call, on questions that repeat word for word or nearlyThe planning turns, on tasks that repeat in shape
What it reportsA hit rateA saving: what the agent costs on the task, minus what the replay measurably cost, with verified kept apart from projected
The failure to watch forA hit that should have been a miss returns the wrong answer, confidently. Both vendors' own documentation warns of itA step that remembers a value instead of computing it. The free dry run exists to catch that

The two are not rivals for the same request. A question with a fixed answer belongs in a semantic cache, and Basic2202 would save nothing on it: there are no tool calls to plan. A task that calls tools, whose answer depends on what those tools return today, cannot be cached at all. Only its plan can, and that is what Basic2202 caches.

What Basic2202 is not

Not thisBecause
A caching layer, or a semantic cache A cache returns a stored answer. A scenario re-runs the work with new arguments — the tools are really called, against live data, and the answer is about today. The side-by-side is under Scenario caching, not semantic caching, above.
A prompt library Nothing here changes what the model is told. Tool descriptions are never edited to steer the model's choices.
An observability product It records runs and you can read them, but the recording exists to become a scenario. If you only want traces there are cheaper things to run.
Automatic replay A run is recorded automatically, and a scenario is calculated automatically once its prompt keeps coming back — or because you asked, or preferred it. A replay runs only in a project you have set the runner up in, and only through the servers you leave on its allow-list (bir replay allow); bir replay off stops it for that project.
Two

Who should use it?

Basic2202 pays off on a narrow, common shape of work: a task you give an agent more than once, where what changes between runs is the values and not the plan.

If that describes something you do weekly, read on. If it does not, the honest answer is that Basic2202 will record your work faithfully and save you nothing — and you should know that now rather than in three weeks.

The three-question test

All three have to be yes.

  1. Will I run this again?

    Not "could someone"; will you. A task run once is a task whose plan is worth nothing to derive.

  2. Does the plan stay the same when the values change?

    "Check the eu-west-1 fleet and tell me which device is most at risk" and "check us-east-2" are one plan with a parameter moved. "Figure out why the build broke" is a different investigation every time, even when the words are identical.

  3. Is it more than three tool calls?

    Below four actions there is no plan to capture, and the service does not list the run as a recording. A one-call task is already as cheap as it gets.

The shapes that work — and the ones that don't

Worth recording

  • Scheduled sweeps and reports. Fleet health, cost reports, open-PR triage, error-budget checks. Same steps, new window, run on a cadence.
  • Per-entity investigations on a fixed path. The customer changes; the four tools and their order do not.
  • Fan-out over a list. Record once against one repository, region or account; the other thirty-nine are the same scenario with a different parameter. This is where the arithmetic gets loud.
  • Runbooks with a stable spine. Nobody should be paying a model to rediscover the opening moves at 3am.

Not worth it

  • Genuinely exploratory work. Debugging, code review, research — anywhere the second tool call depends on what the first said in a way you could not have predicted. The plan is the work.
  • Non-MCP tools without hooks. You get a partial recording, correctly labelled as one.
  • Anything that must not run unattended. If that sentence makes you uneasy about a particular tool, do not put it on the allow-list — that instinct is correct.
  • High-variance, low-frequency work. Something you run twice a year, where the environment has moved on by the time it comes round again, so the plan the scenario holds is no longer the plan.

Who, in job terms

You areWhat you get out of it
Platform / SREScheduled sweeps and runbook openings. The highest-yield case: fixed spine, changing scope, run constantly
Data / analytics engineeringReport pipelines where the shape is stable and the window moves
Support & success engineeringPer-customer investigations that always start the same way
Agent and tooling teamsA measured answer to "is this agent worth what it costs", from the ledger, before you touch replay at all
Engineering leadershipThe verified-savings figure — deliberately conservative, and therefore quotable
Note the fourth row

Recording is useful on its own, and it is the whole of what runs out of the box. A team that never calculates a single scenario still ends up with a priced, searchable record of what its agents did and what each run cost. For some teams that is the entire value, with replay as a later question.

What it costs you to find out

An afternoon, and no money. Recording, calculating and replaying are all free, there is no limit on how many scenarios you hold, and none of them expires. Take the task you repeat most, record it, calculate it (or repeat it three times and let the service calculate it for you), dry-replay it, and read the ledger after a week. If the number is unconvincing on your best candidate, it will not improve on your second-best.

If it is convincing, the question that follows is usually not about price but about where the recordings live. That is what running it in-house is for.

The honest limits

None of these is a bug to be fixed later.

  • A saving is only counted when it is measured. The verified figure needs the runner to report what a run cost. A session that ends without reporting loses you the measurement, and the saving lands under projected instead — the ledger stays conservative either way.
  • Calculation can be paused. Working a scenario out costs us several model calls, so the free service calculates one scenario at a time per account and stops for the day if the whole service has spent its budget. An ordinary account never meets the second one.
  • A match is a match on the prompt. Phrase the first prompt of a reusable task as though someone else will have to phrase it again next month — because that is exactly what has to happen.
  • Standalone recording is partial. Without the recorder — bir-hooks, which bir setup starts for the folder and the SessionStart hook keeps started — there is no prompt, no final answer and no built-in steps, so there is nothing for a match to compare against and replay cannot arm.
  • Calculation needs a model. Deriving a scenario, and deriving parameters at replay time, both call Anthropic. A service without a key configured will refuse to calculate.
Three

Using the console

The console is where recordings, scenarios and money are visible. What follows is a tour of the screens and, more usefully, of what each number is actually claiming.

Getting an account

  1. Register

    With an email and password, or sign in with Google.

  2. Verify the address

    Registration does not create a session; the link in the email does. That link is the proof of the inbox, and it may be opened on a different device from the one you registered on.

  3. Sign in

    You land on the dashboard. If a verification link has gone stale, ask for another from the sign-in screen — a new link supersedes the old one.

The sign-in page at bi2202.com: the headline, and the sign-in card
The front door. bi2202.com, signed out. The card is where you sign in; Create account is top right and under the card. The grey note at the top of the card only appears when a long-idle session was signed out.

A new account sees Getting started on the dashboard where the tiles would be. The next step is to install the runner — about ten minutes — and the first recording lands there.

Getting around

The menu is on the left and stays there. It is grouped, because the destinations are about different things.

The dashboard: Welcome back, three headline numbers, then the Getting started and Recent recordings cards
The dashboard. Welcome back, the three numbers, and the Getting started and Recent recordings cards. The picture is cropped to the middle of the page, so the menu on the left is not in it. Each ⓘ opens one sentence saying exactly what its number counts; when in doubt, click the ⓘ.
SectionScreens
—Dashboard, Recordings — and, while you are inside Recordings, its saved views — then Set up the runner
AccountAccount, Sign-in history
AdminUsers, Invoices — admin accounts only. Each row of Users has a Recordings link that opens that account's recordings, read-only

Pinned to the bottom of the rail:

  • Documentation — this handbook, where the deployment has been given a docs URL.
  • The account chip — name and role, with account settings, the light/dark toggle and sign out.

⌘K opens search from anywhere. It searches the menu, and offers one row that hands your query to the recordings search. It does not search across everything — there is no index behind that, and a palette pretending otherwise would be worse than one that is clear about its scope.

At the foot of every page: API status, Help and support, Feedback. The status light is a single call on load — it does not poll, because a status indicator that hammers a service during an incident is a second outage stacked on the first.

Help and support opens a page of its own: is the API up, is the runner recording, the usual problems with their fixes, and a Write to us button whose email already carries your account address and the console version, so a first reply can be an answer.

Recordings — the columns

The list, and the only screen with thirteen columns. Everything about it lives in the URL — sort, page, search, filters — so a view worth building can be bookmarked or sent to somebody.

The Recordings page: the three tiles, the note on automatic calculating, search, then a table with one recorded task
Recordings. One row per task the agent did — this account has one, Check us-east-2 fleet devices; rows with a green left edge have a calculated scenario. The sentence above the table says that the service also calculates recordings by itself once a prompt has come back at least three times.
The table header and its one row of the recordings list, up close
The header and its row, up close. The agent's run cost $0.2490, a replay costs $0.161655, so each reuse is 35.1% cheaper; the prompt has come back five times, the scenario has done the work twice, and $0.1747 was saved.
ColumnReading
RunThe generated title, ≤40 characters. Falls back to the first line of the prompt before a title exists. Calculated rows carry a green edge
StartedWhen the agent ran
StepsActions the agent took. Not every stored row — a tool's response and the recorder's own bookkeeping are not steps the agent took
Statusdone, failed, or running
LLM $/runThe baseline: what this task costs when the agent does it. Its ⓘ says how many runs the baseline is drawn from
Scenario $/runWhat a replay costs. Marked est. when it is a pre-flight estimate rather than an observed mean — "we measured this" and "we guessed this" must not look alike
Saved %/runHow much cheaper one calculated run is than one agent run: (LLM $/run − Scenario $/run) ÷ LLM $/run, from the two columns beside it. The bar under the figure fills to the same percentage, in a green that deepens as the saving grows; red only when a replay costs more than the agent. Marked est. while the scenario cost is still an estimate; — without a ready scenario or a baseline
Original timeWall-clock for the agent's run
Calculated?yes, no, calculating… or failed. A scenario whose recent replays cost more than the agent is flagged not paying off here too. auto beside the badge means the service calculated it by itself. The ★ Preferred and ⊘ Switched off badges under a title are the two things you can tell the service about a recording
Calculated runsReplays of this scenario that did work — the same count as the tile above. Its ⓘ breaks out unmeasured, dry and not-steered runs
Similar hitsPrompts matched against this recording
Saved $Verified savings for this row, signed. — until a measured replay has actually happened. Projected savings, when there are any, sit beneath it and are never added in

Every column with a figure in it sorts, Saved %/run included. Calculated recordings sort to the top of every column by default; the note above the table says so and lets you turn it off. Runs with three actions or fewer are hidden, with a link saying how many — hidden rather than deleted, because they are also below the threshold for being listed at all.

Recordings — the detail page

Everything stored for one run: the prompt, each step with its arguments and output, the final answer, and the scenario if there is one. This is where you press Calculate, read what calculation produced, and run the dry replay that is worth doing before you trust anything. There is no Recalculate button: a calculation that failed is run again by preferring the recording, which resets it and starts a fresh job. From the command line it is still bir scenario calc --force.

A recording's page: the prompt, the agent's answer, the steps, and the scenario on the right
A recording, opened. Check us-east-2 fleet devices, top to bottom: the name and when it ran; the exact prompt, check the us-east-2 fleet; the agent's answer; each tool call with what it sent and what came back. On the right, the scenario calculated from it, pictured up close in section One.

It is also where you overrule the service's pick, in either direction. ☆ Prefer this recording calculates this one straight away, and takes nothing from anybody: you may hold as many scenarios as you like, so nothing has to be replaced to make room. A preferred recording is also first in line whenever the service looks for something to calculate on its own, whatever its hit count, and is kept however short it is.

Switch off calculation and replay is the opposite instruction: the service never picks that recording on its own, however often its prompt comes back and however much it has at stake — and while it is off, a matching prompt is not replayed from its scenario either: the agent runs again and nothing is saved. The scenario is kept, and comes back when you switch calculation and replay on again. The two are one switch, so each button is offered only while the other flag is not set: a recording is preferred, switched off, or neither. Two badges beside them are about the server rather than the recording — server cannot calculate when the deployment has no Anthropic key and nothing can be calculated on it at all, no automatic calculation when the key is there and only the picking is switched off, which leaves Prefer working.

Steps changed by hand (not released yet). You, or Claude working for you, can correct one step of a scenario with bir — see Let Claude fix a step. The detail page shows it:

  • The scenario's details say edited by hand, and how many steps.
  • A note names those steps. It says the service does not rebuild this plan on its own while they are there. It also gives you the commands: bir scenario edits shows the history; bir scenario undo takes back the newest edit, and run again it goes one further back. Once every edit is taken back, the service can rebuild the plan as usual. To recalculate now and drop the edits: bir scenario calc <scenario id> --force --discard-edits. An administrator sees the note without the commands.
  • The step's own card has an edited by hand badge: its code, its mark, or both were changed. It has accepted on purpose as well when the change was saved with --force although its check against the recording did not pass. Hold the pointer over a badge to read more.
  • A step marked needs a judgement says how to fix it with bir scenario edit. A step you marked yourself says marked by hand.
  • In a plan with hand edits, a step that keeps failing is not repaired on its own. Its label says not repaired: hand edits, and the text under it names the ways out: fix the step, take the edits back with bir scenario undo once per edit, or recalculate and drop them.
  • Recalculate on a sub-task with hand edits is refused. The message names the same ways out, with the sub-task's own id.

The console only shows edits: making one, reading the history and undoing it are done with bir.

What it costs

The service here is free. Recording, calculating, replaying, as many scenarios as you like, and none of them expires. There is no card, no balance and no fee. Your recordings are stored on our server in Germany, and the page on security and privacy says exactly what that means.

That last sentence is why the one paid product exists. A recording holds the request you typed, every tool call, and everything those tools returned — file contents, query rows, whatever a browser fetched. Plenty of companies cannot keep that on somebody else’s server, whatever the terms say. For them there is Basic2202 In-house: the same service, running in the company’s own AWS account. It is the only thing we charge for, and the price is one fifth of what it saves you. You keep the other four fifths, and a month that saves nothing costs nothing. Only the verified saving counts, after what calculating scenarios cost; projected savings are never billed. There are no seats, no minimum and no trial to run out.

Our aim is to save you as much money as we can. A share of the saving is the price that says so: we earn only when you save, and we earn more only when you save more.

One thing to be exact about, because it is the part people assume away: an in-house instance still sends data out, to AWS services in your own account. Claude, through Amazon Bedrock, sees the whole recording when it calculates a scenario, a short excerpt when it writes a recording’s title, and the new request and the scenario’s parameters when a replay works out what to pass. And every hour one number goes to AWS Marketplace — the dollars the instance saved you — which is how you are billed. Nothing goes to us.

In-house is not available yet. To talk about running it, write to support@bi2202.com.

Reading the numbers honestly

  1. Lead with verified

    It is the smaller number and the defensible one. If you need the larger one for a slide, say which half is projected.

  2. Check the baseline's sample count

    Before quoting a saving. One run is a guess, and every saving on that row is measured against it.

  3. Believe a negative

    A scenario flagged as not paying off is costing you money on its recent runs. The two useful responses are to retire it (delete the recording) or to replace it by preferring a different recording; both beat looking away.

Four

How to record

Everything starts with a recorded run. The recorder is BaseInstRunner — bir — which lives in its own repository, BaseInstRunnerMCP. Getting started installs it in about ten minutes.

Three files there are the source of truth for everything below: README.md (the operator's half), docs/BaseInstRunner.md (the design), and docs/calculatedReplayGuide.md (the replay runbook in more detail than this page goes into). Where they disagree with this page, they are right — they sit next to the code.

Getting started — install the runner

One line in a terminal. It installs what is missing — Node, the runner, Claude Code — signs the runner in as you, wires the project you paste it in and starts the recorder in the background. Nothing has to stay open. Then claude, as always. A few minutes, most of them downloads; nothing here needs a copy of the runner's source code. Signed in to the console, the same steps are on its Set up the runner page, with the line ready to copy, your account filled in, and a line that turns green when your first recording arrives.

The earlier, by-hand install, done once on a Windows machine. Filmed before the one-line path existed: a small project to record, the task run by hand, then npm install, bir install, bir login, the recorder in a window of its own, and the first verified saving. The install and the sign-in look the same today; the rest is what the line now does for you, and the recorder no longer needs a window. 7 min 54 s, silent; the step being done is named in the corner. The desktop is wide, so watch it full screen.
  1. 0:00The handbook
  2. 0:06A small project to record
  3. 0:38The task, before the runner
  4. 1:34Install the runner (npm install)
  5. 1:48Wrap the project (bir install)
  6. 2:03Sign in (bir login)
  7. 3:24Record (bir-hooks, the earlier way)
  8. 4:10The run in Recordings
  9. 5:31Calculated
  10. 6:52Replay
  11. 7:33Verified saving
Before you start

Two things. The line does the rest.

  • You have a Basic2202 account, and you are signed in to the console. Register at bi2202.com, open the link in the verification email, and sign in once. The line on the console's Set up the runner page carries a one-time setup token tied to that account — single use, good for ten minutes — and that is how the runner learns which account it belongs to. There is no account name or identifier to type anywhere.
  • A project folder — the one you start Claude Code in. Best with an MCP server in it, a .mcp.json in the folder or servers listed in ~/.claude.json: those are what a calculated scenario can run directly. Without one, recording works all the same and a scenario steers the session instead. No project with an MCP server? That is fine: step 2 builds you a small, real one in about a minute.

Node and Claude Code are each installed for you only when missing or unusable: Node when the machine has none, one older than 20, one a version manager, Homebrew, snap or Scoop put there, or one with no npm beside it; Claude Code when claude is not found. On Windows use PowerShell or cmd, whichever your terminal is; step 1 has a line for each.

  1. Paste one line into a terminal in your project folder

    Open a terminal in the project folder you start Claude Code in, and paste this. The console's Set up the runner page shows it with the token filled in — copy it from there, not from here.

    $env:BIR_SETUP_TOKEN="<token>"; irm https://api.bi2202.com/install.ps1 | iex
    curl -fsSL https://api.bi2202.com/install.sh | BIR_SETUP_TOKEN="<token>" sh

    A Windows terminal that is cmd.exe rather than PowerShell — the prompt does not begin with PS — takes the same line, wrapped: powershell -NoProfile -Command "$env:BIR_SETUP_TOKEN='<token>'; irm https://api.bi2202.com/install.ps1 | iex". The console's Windows (cmd) tab has it, beside Windows (PowerShell) and macOS / Linux.

    The token is single use and good for ten minutes; the line lands in your shell's history, which is why it dies at first use. Expired, or not at the console? The same line without the BIR_SETUP_TOKEN part still installs and wires everything; bir setup then prints a link and a short code and waits for you to approve it in the browser, from any device. On an in-house instance, its own address stands in for https://api.bi2202.com; the line its console shows already has it.

    You should see ==> lines as it goes through Node, the runner, Claude Code and the sign-in, then a summary that starts with Done.:

    Done. Recording and replay are on for C:\path\to\your\project
      account    you@example.com  (https://api.bi2202.com)
      wrapped    fleet
      replay     on — direct execution allowed for: all wrapped (narrow it with `bir replay allow fleet`)
      recorder   started in the background (pid 1234) — it starts itself with every Claude Code session
      log        C:\Users\you\.baseinstrunner\logs\3f9c1a2b7d4e5f60.log
    
    Now:  claude
      first time in this folder: say Yes to "trust this folder"
      just installed Claude Code? it asks you to sign in to Claude (it needs a Claude subscription)
      give it a task that takes 4+ tool calls — it appears at https://bi2202.com/recordings within a minute of finishing
    Any time:  bir doctor     bir investigate     bir down (stop)     bir uninstall (undo)

    What it did, in order, each step under a ==> line of its own: installed Node if the machine had no usable one — none, older than 20, in a version manager's, Homebrew's, snap's or Scoop's path, or without npm beside it — as a private copy under ~/.baseinstrunner, never on your PATH; installed the runner; installed Claude Code if claude was missing, which can take a minute; then ran bir setup, which signed in, stored the service address, wrapped the project's MCP servers, wired Claude Code's hooks into .claude/settings.local.json, added and pre-approved the bir scenario server, and started the recorder in the background. In the summary, wrapped names the project's MCP servers, each now routed through the runner under its old name; replay says which of them a calculated scenario may call unattended (narrow it with bir replay allow <names>); recorder is the background process, and log is where its audit lines go.

    A project with no MCP servers says steer mode only on the replay line; recording works all the same. Pasted it in your home folder, or the root of a drive (or disk), by mistake? The line stops before downloading anything and says so: nothing is installed, nobody is signed in, and the token has not been used. Open a terminal in your project folder (cd there) and paste the same line again.

    If it goes wrong, the usual cases are under If something is wrong, after the steps.

  2. Make something to record

    Skip this if the folder you pasted the line in already has an MCP server — a .mcp.json in it, or servers listed in ~/.claude.json — and go on to step 3. If it has none, build a small one now. It takes a minute and gives you a task worth recording rather than a toy that records nothing; the Sample section below uses the same folder. It needs a node on your PATH (node -v): the private copy the line may have installed is not on it, so if that fails, install the LTS build from nodejs.org first.

    mkdir fleet-demo
    cd fleet-demo

    Save this into it as fleet-server.js:

    #!/usr/bin/env node
    /**
     * A tiny, real MCP server over stdio — the thing `bir-proxy` will wrap.
     *
     * Two tools, chosen so a scenario built from them has to thread state:
     *   list_devices(fleet)  -> the fleet's devices, with risk scores
     *   device_detail(id)    -> one device's detail
     *
     * Step 2 of a scenario can only compute its `id` argument from step 1's output,
     * which is exactly what `respParams` threading is for.
     */
    
    const FLEETS = {
      "eu-west-1": [
        { id: "dev_88f1", name: "edge-01", risk: 0.94, firmware: "3.1.2" },
        { id: "dev_2c30", name: "edge-02", risk: 0.31, firmware: "3.2.0" },
        { id: "dev_9a05", name: "edge-03", risk: 0.62, firmware: "3.1.9" },
      ],
      "us-east-2": [
        { id: "dev_4411", name: "core-01", risk: 0.77, firmware: "2.9.4" },
        { id: "dev_7b22", name: "core-02", risk: 0.12, firmware: "3.2.0" },
      ],
    };
    
    const TOOLS = [
      {
        name: "list_devices",
        description: "List the devices in a fleet, with their risk scores.",
        inputSchema: {
          type: "object",
          properties: { fleet: { type: "string", description: "Fleet id, e.g. eu-west-1" } },
          required: ["fleet"],
        },
      },
      {
        name: "device_detail",
        description: "Full detail for one device.",
        inputSchema: {
          type: "object",
          properties: { id: { type: "string", description: "Device id, e.g. dev_88f1" } },
          required: ["id"],
        },
      },
    ];
    
    function callTool(name, args) {
      if (name === "list_devices") {
        const devices = FLEETS[args.fleet] ?? [];
        const worst = [...devices].sort((a, b) => b.risk - a.risk)[0];
        return {
          fleet: args.fleet,
          count: devices.length,
          devices,
          worstDeviceId: worst ? worst.id : null,
        };
      }
      if (name === "device_detail") {
        for (const devices of Object.values(FLEETS)) {
          const found = devices.find((d) => d.id === args.id);
          if (found) return { ...found, lastSeen: "2026-08-31T09:12:00Z", status: "online" };
        }
        return { error: `unknown device: ${args.id}` };
      }
      throw new Error(`unknown tool: ${name}`);
    }
    
    // ── minimal MCP stdio plumbing (newline-delimited JSON-RPC) ─────────────────
    let buf = "";
    process.stdin.on("data", (chunk) => {
      buf += chunk.toString("utf8");
      let nl;
      while ((nl = buf.indexOf("\n")) >= 0) {
        const line = buf.slice(0, nl).trim();
        buf = buf.slice(nl + 1);
        if (line) handle(JSON.parse(line));
      }
    });
    
    const send = (msg) => process.stdout.write(JSON.stringify(msg) + "\n");
    const reply = (id, result) => send({ jsonrpc: "2.0", id, result });
    
    function handle(msg) {
      const { id, method, params } = msg;
      if (id === undefined || id === null) return; // notification
      switch (method) {
        case "initialize":
          return reply(id, {
            protocolVersion: params?.protocolVersion ?? "2025-06-18",
            capabilities: { tools: {} },
            serverInfo: { name: "fleet", version: "1.0.0" },
          });
        case "tools/list":
          return reply(id, { tools: TOOLS });
        case "ping":
          return reply(id, {});
        case "tools/call": {
          try {
            const out = callTool(params.name, params.arguments ?? {});
            // A normal MCP result: a text block holding JSON.
            return reply(id, { content: [{ type: "text", text: JSON.stringify(out) }], isError: false });
          } catch (err) {
            return reply(id, { content: [{ type: "text", text: String(err.message) }], isError: true });
          }
        }
        default:
          return send({ jsonrpc: "2.0", id, error: { code: -32601, message: `no method ${method}` } });
      }
    }
    
    // Diagnostics go to stderr. A stray byte on stdout corrupts the JSON-RPC stream.
    process.stderr.write("fleet-server: ready\n");

    That is a real MCP server, not a mock: 111 lines, no dependencies, speaking newline-delimited JSON-RPC over stdio exactly as any other MCP server does. It holds two fleets of pretend devices and offers two tools:

    list_devices(fleet) → the fleet's devices, with risk scores
    device_detail(id)   → one device's detail

    Two tools chosen for one reason: step 2 of a scenario can only work out its id argument from step 1's output. That makes it the smallest honest test of whether scenario calculation really works, rather than the smallest test that passes.

    Now tell Claude Code the server is there. Save this as .mcp.json in the same folder:

    {
      "mcpServers": {
        "fleet": {
          "command": "node",
          "args": ["fleet-server.js"]
        }
      }
    }

    You should end up with those two files and nothing else. Do not start the server yourself — Claude Code launches it when it needs it, and bir sits in between. Then, still in that folder, wire it. The sign-in from step 1 is kept on this computer, so no token and no address are needed:

    bir setup

    It ends with the same Done. summary as step 1, with fleet on the wrapped line. This is also how you record any other project later: cd there, bir setup.

  3. Then: claude

    In the same terminal, same folder — from a cmd window, a new one in the same folder: the line ran in a child PowerShell, and its PATH change does not reach the window you pasted in — start Claude Code as you always do. The first time in this folder it asks whether to trust it: say Yes, or the hooks do not run. Then give it a real task, one that takes four or more tool calls.

    claude

    If you built the demo in step 2, ask for "check the eu-west-1 fleet and tell me which device is most at risk". In the sample data the answer is dev_88f1 — edge-01, risk 0.94, on firmware 3.1.2: two tool calls, plus the model's turns around them. That sits near the four-call minimum, so ask a second thing in the same session ("and check us-east-2 as well") and it clears the threshold comfortably. A run with three tool calls or fewer is not listed as a recording, because there is no plan worth reusing in a couple of calls.

    You should see nothing new in the terminal: the recorder runs in the background and starts itself with every Claude Code session in this folder — tomorrow, and after a reboot, the same way. Nothing else to start, and nothing to leave open. The run appears in Recordings at bi2202.com within a minute of finishing, with its title, its step count and what it cost; on the console's Set up the runner page, the line at the bottom turns green when it does. The session itself looks like the run pictured under Record, in section One. What the recorder sends, and what it strips out first, is under Security and privacy, section Five.

    Just installed Claude Code? It asks you to sign in to Claude the first time. That is a Claude subscription, separate from your Basic2202 account.

  4. Check it

    In the project folder, any time:

    bir doctor

    This is the one command worth remembering. It does not read a settings file and tell you what should happen; it asks the running system what is happening — which service and account it records to, that the recorder runs in the background, with its pid and its log file, what replay is set to and where that setting came from — and says so loudly when something is broken. bir status shows the configuration without needing anything running.

    bir doctor with the recorder running: recording tier bound (Tier 1), recording yes, one problem listed
    Healthy: bound (Tier 1) and Recording: yes. The one problem listed only says that no agent session was open at that moment; start claude and it goes away.
    bir doctor with no recorder running: control server not running, standalone (Tier 2)
    No recorder up: not running and standalone (Tier 2). Before the first session, or after bir down, that is normal — the recorder starts with the next Claude Code session, or bir up starts it now. If it still reads this way during a session, tool calls are being recorded but your prompt is not, and nothing could ever match; the table below has it.

    For a turn that did not do what you expected — a scenario that did not run, a saving that was not made — bir investigate reads the journal and the service and says why. Ask why, below, walks through it.

If something is wrong

bir doctor first. It names the broken link in the chain, which is most of the answer. Then:

What you seeWhat to do
This is your home folder / the root of a drive (or disk)The line stopped before downloading anything: nothing is installed, nobody is signed in, and the token in it has not been used. A terminal opened from the Start menu begins in the home folder, which is where a first paste often lands. cd to your project folder and paste the same line again. (It is bir setup run by hand there, with the runner already installed, that keeps the sign-in and prints the two commands left: cd to the project, then bir setup, no token needed.)
not a BaseIn serviceThe address in the line is not the service's API: the website, or a stale BIR_AUTH_URL in a shell profile (bir setup warns when the two disagree). Copy the line from the console's Set up the runner page again — it points at https://api.bi2202.com, or at your own instance's API — and take the variable out of the profile.
control.port_busyAnother program (or the system) holds the recorder's port — on Windows, Hyper-V and WSL reserve ranges that 53411 can fall inside. In the project folder: bir setup --port 53412 (any free port). It keeps the self-starting hook; nothing to restart by hand.
recorder.disabled · no BaseIn sessionThe runner is not signed in on this computer. Run bir setup again in the project folder; without a token it signs in through the browser.
The run is not in RecordingsThree tool calls or fewer are not listed as a recording: give it a bigger task. Still nothing: the runner is signed in as another account (bir doctor names it on its recording to line). Switch it: bir logout, sign in to the console as the other account, and paste the line from its Set up the runner page again.
Tools are recorded, but not what you typedNo recorder was running for this folder, so the proxies recorded on their own (Tier 2). bir doctor says whether one runs now; bir up starts it, and the next Claude Code session would have anyway.
Claude Code asks to sign inIt needs a Claude subscription, and that sign-in is separate from your Basic2202 account. Sign in there once; from then on it starts signed in.
command not found: claude or bir (macOS / Linux) · 'bir' is not recognized (Windows cmd)The terminal you pasted in cannot see what was just installed. macOS / Linux: paste the export PATH=… line the script printed, or open a fresh terminal in the same folder. Windows cmd: the line ran in a child PowerShell, and its PATH change does not reach the cmd window; open a new cmd window in the same folder, or run set PATH=%USERPROFILE%\.baseinstrunner\bin;%USERPROFILE%\.local\bin;%PATH% in this one. (A PowerShell window is updated in place: bir works there at once, and in every new terminal.)
bir doctor: the SessionStart hook … points at a Node that is goneThe Node the hook names was removed, or moved by a version manager. bir setup in the project folder rewires the hook to the current Node.
Anything elseRun bir doctor and keep the output. Paste it into Claude Code, or into a mail to support from the console's Help and support page.
Typing bir before the install: 'bir' is not recognized as an internal or external command
The command not found row, as it looks. A window that was open before the install does not know the word yet. A PowerShell window the line was pasted in does; a cmd window has to be a new one, because the line ran in a child PowerShell; on macOS and Linux, paste the export PATH=… line it printed.

The same, by hand

Two commands, if you already have Node 20 or newer (node -v prints v20. or higher): install the package, then bir setup. Below them, what bir setup does and the pieces it is made of, for when you would rather run each one yourself. Working on the runner itself, from a checkout of BaseInstRunnerMCP? The Guide below has that.

npm install -g @basein/runner
npm install -g @basein/runner, answered with: added 1 package in 1s
You should see one line, added 1 package. That is the whole install. There is no address to set and no terminal to reopen: bir setup stores the address.

Then, in the project folder you start Claude Code in, one command, with the address of the service (your own instance's, on an in-house one):

cd C:\path\to\your\project
bir setup --auth-url https://api.bi2202.com
cd ~/path/to/your/project
bir setup --auth-url https://api.bi2202.com
==> Setting up the runner (0.2.9)
    service   https://api.bi2202.com
    signing in through the browser (a setup token from the console skips this)
  Open   https://bi2202.com/activate?code=FQSB-JCNQ
  Code   FQSB-JCNQ
  Opening your browser…
  Waiting for approval…  (Ctrl-C to cancel)
    account   you@example.com
==> Wiring the project
==> Starting the recorder in the background

Done. Recording and replay are on for …

You should see your browser open the console on Approve a sign-in. Check that the code on the page is the one in the terminal and press Approve; the terminal goes on by itself. What it does, in order:

  • Signs in as the account this browser is signed in with, and keeps the session on that computer under ~/.baseinstrunner, in a file only your user can read. You are not asked again.
  • Stores the address, so every command and every recorder from now on knows where the service is. No environment variable to set.
  • Wraps the project: routes its MCP servers through the runner, each under its old name, and wires Claude Code's hooks into .claude/settings.local.json, which stays out of git. It keeps a copy of every file it touches.
  • Pre-approves the bir scenario server, so Claude Code shows no approval dialog for it.
  • Starts the recorder in the background, and from then on the recorder starts itself with every Claude Code session in that folder.
The sign-in in the terminal, and the browser page it opens: Approve a sign-in, showing the same code
The code in the terminal and the code on the page are the same — that is the check. Press Approve and the terminal finishes by itself. The terminal in the picture ran bir login, the sign-in on its own; bir setup opens the same page. The blurred line is the machine's public address, shown so you can tell your own request from someone else's; if you did not just start a sign-in, press Deny.

This is the moment the runner is tied to your account. There is no account name or identifier to type anywhere: whichever account is signed in in the browser that presses Approve is the account this computer signs in as. The page says so, under Will sign in as, and the terminal confirms it on its account line. You never type a password into the terminal, which is also why this works for an account that only signs in with Google. From then on every recording from this computer goes to that account, until you run bir logout. If the page names the wrong account, press Deny, sign out of the site, sign in as the right one, and run bir setup again.

If it goes wrong. This is your home folder: unlike the line, which stops before installing anything, bir setup keeps the sign-in, and the two commands left are cd to the project and bir setup again. not a BaseIn service: the address is the website, not the API; use https://api.bi2202.com. On a machine with no browser — a server over SSH, a container — add --no-browser: it prints the address and the code, and you approve from your laptop. Prefer no browser step at all? The console's Set up the runner page makes a one-time setup token already tied to your account: bir setup --token <the token from the page>, within ten minutes.

To record another project, cd there and run bir setup again; the sign-in is kept, so the address is not needed either.

Or the pieces, one at a time. bir setup is these three, in this order. Run them yourself to see each one's output, or to redo one of them.

bir login                        # the sign-in on its own; takes --no-browser and --token too
bir install --global --replay    # wrap the project, wire the hooks, add the scenario server
bir up                           # the recorder, in the background — now, rather than with the next session

bir install rewrites the project's MCP entries in place — every server keeps its name, so nothing the model sees changes — and wires Claude Code's hooks. Without --replay the project is recorded and nothing is ever replayed. It keeps a copy of every file it touches, and bir uninstall puts them back byte for byte. A folder with no MCP servers is wired all the same: the hooks and the scenario server go in, and the summary says steer mode only.

bir install --global, with an earlier runner: fleet already wrapped, hooks written to .claude/settings.json, and what to do next
The same command on a project wrapped once before, with an earlier runner, so fleet reads = already wrapped and the count is 0; on a first run that line starts with + and the count is 1. Today the hooks line names .claude/settings.local.json, with --replay a line before it adds the bir scenario server, and what came after Next: — run bir-hooks in a window of its own — now reads start your session here as usual. The recorder starts itself with it.

The switches. A replayed step runs without the permission prompt you would otherwise see, so bir replay allow names the servers you are content to have called unattended, using the names on the wrapped line; left unset, every wrapped server is eligible. The setting is kept for this project, so a recorder started by a hook keeps it; a change is picked up by the next recorder, or now with bir up --restart. Read Turning replay on in the Guide before using replay on a project that matters.

bir replay allow fleet,github    # the servers replay may call unattended (default: all wrapped)
bir replay status                # what is stored for this project (bir doctor: what the recorder runs with, and its source)
bir replay off                   # record only; no scenario answers a matching prompt
bir down                         # stop the recorder; it starts again with the next Claude Code session
bir uninstall                    # put every file the install touched back

What it changed on your machine

PathWhat it holds
~/.baseinstrunner/config.jsonthe service address; BIR_AUTH_URL in the environment overrides it
~/.baseinstrunner/credentials.jsonthe cached session, bound to the service that issued it
~/.baseinstrunner/installed.jsonthe original text of every file it rewrote, and each project's port, token and replay switches
~/.baseinstrunner/logs/<key>.logthe recorder's audit log, one file per project
~/.baseinstrunner/node/, npm/, bin/the line's own Node (only if the machine had no usable one; not on PATH), the runner package, and the bir and bir-hooks launchers, first on your user PATH. An install by hand puts the runner wherever npm does instead
<project>/.claude/settings.local.jsonClaude Code's hooks for this project and the pre-approved bir scenario server. Yours, not the team's: it carries this project's token and an absolute Node path
<project>/.mcp.json (or ~/.claude.json)each MCP entry rewritten to run through bir-proxy, same keys; plus the bir entry
<project>/.git/info/excludeone line, .claude/settings.local.json, so it never shows in git status

Each project gets its own control port (from 53411 up) and its own token, so one project's settings file cannot reach another project's recorder.

Updating. Paste the line from the console again — the very same line is fine, used token and all: bir setup says the setup token was already used; keeping the sign-in from before (you@example.com) and goes on. It installs the newest version and runs bir setup, which re-wraps any entry whose runner moved. Not npm install -g over a one-line install: that puts a second copy where the launchers never look. Installed by hand in the first place? Then npm install -g @basein/runner again, and bir setup in each project. A recorder of the older version is replaced by bir setup, bir up or the next session's SessionStart hook, provided no session is mid-run just then; nothing watches for a session to end. bir setup says so when one is open (it restarts to the new version with the next session), and bir up --restart does it now.

Undoing it. In the project folder, bir down stops the recorder and bir uninstall puts every file it touched back exactly as it was; the sign-in stays, so a later bir setup needs no token. To remove the software as well: bir logout first, so the session is revoked on the service rather than only forgotten here; then npm rm -g @basein/runner if you installed by hand, or delete ~/.baseinstrunner — the runner, its private Node, the launchers, the session and the logs — and take ~/.baseinstrunner/bin off your PATH.

Guide

What the recorder is

bir is an MCP proxy. It registers under your existing MCP server keys, so the model still emits mcp__chrome-devtools__navigate_page and still gets exactly what chrome-devtools-mcp would have returned. The difference is that bir executed the call on the client's behalf, and wrote down what happened.

The guarantee

Interception is a configuration fact, not a prompting one. The model is never asked to cooperate, and tool descriptions are never edited to steer its choices.

The limit

An MCP server sees only MCP traffic addressed to it. Bash, Read, Edit, Grep are not MCP. Neither is your prompt, the model's reasoning, or the final answer.

Which is why there are two halves. bir-proxy records MCP calls; bir-hooks is the recorder: a hook receiver that records everything else and owns the run. bir setup starts it in the background, and the SessionStart hook (bir-hooks ensure) starts it again with every Claude Code session, so nothing has to stay open.

The two tiers

TierWhenWhat ends up in the recording
1 — BoundA recorder (bir-hooks) is running for this working directory — the SessionStart hook starts one with each sessionBuilt-ins and MCP, in one ordered step stream, with the prompt and the final answer
2 — StandaloneNo control server found within five secondsMCP calls only. No prompt, no final answer, no built-in steps

Tier 2 is not a degraded bug — it is the honest ceiling of what a proxy alone can observe, and a run records which tier produced it so nothing downstream mistakes a partial trace for a complete one. For anything beyond raw recording you want Tier 1: a match is a match on the prompt, and a standalone proxy never sees one.

Install

The beginner's path is Getting started above: the one line from the console's Set up the runner page, or by hand npm install -g @basein/runner and then bir setup --auth-url https://api.bi2202.com in the project (the address is stored from then on, so a second project needs only bir setup). Working on the runner itself? From a checkout of BaseInstRunnerMCP, build it and run the same setup from the checkout. It points the install at the checkout's proxy instead of the published package (--local, decided for you), and a later flag-less bir install keeps that shape:

npm install; npm run build
cd C:\path\to\the\project\you\record
node C:\path\to\BaseInstRunnerMCP\dist\bin\bir.js setup --auth-url https://api.bi2202.com    # sign in, wire it, start the recorder
npm install && npm run build
cd ~/path/to/the/project/you/record
node ~/path/to/BaseInstRunnerMCP/dist/bin/bir.js setup --auth-url https://api.bi2202.com    # sign in, wire it, start the recorder

The line in Getting started is the runner repository's own installers, scripts/install.ps1 and scripts/install.sh, served by the API with its address filled in. For a fleet of machines — packaging, offline installs, updates — see its docs/quickstart.md and docs/installRun.md.

bir install, the middle of bir setup, rewrites your MCP entries in place, keeping every key — so mcp__<server>__<tool> names never change — and wires Claude Code's hooks into .claude/settings.local.json, the file Claude Code keeps out of git. It stashes the original text of every file first, and bir uninstall puts it back byte for byte.

Then your session, now recorded:

claude

Nothing else to start. The SessionStart hook is bir-hooks ensure: it starts the recorder for the project in the background when none is running. bir up starts it by hand, bir down stops it. Whenever you want to know where you stand:

bir status    # what is wrapped, which config scope won, and where the audit log is
bir doctor    # is it working right now — exits non-zero if not

doctor is the one to trust. status tells you what is configured; doctor tells you whether a proxy can currently reach a control server — which is the difference between Tier 1 and Tier 2 — and which service and account the recorder records to (recording to … as …), with its pid and its log. recorder not running before the first session is normal: the next session starts it.

For other MCP clients, nothing in bir knows where Cursor or Codex keep their settings, and it does not guess: bir install --config <path>, or bir wrap --server-name my-server -- node my-server.js to print an entry to paste. Those clients have no hooks, so they record at Tier 2.

Record a run worth keeping

Work normally. Two server-side facts decide whether the run becomes reusable, and both bite quietly.

Rule one

A run needs more than three tool actions. Below that it is not embedded, does not enter the similarity pool, does not appear in the list, and is eventually pruned. A two-tool run will never match anything however often you repeat it.

Rule two

The prompt is what gets embedded — the text you typed, not the transcript and not the answer. Write the first prompt of a reusable task as though someone else will have to phrase it again next month, because that is exactly what has to happen.

Watch it land in the audit log:

[bir] …  run.start   run=run_… sess=birsess_… tier=bound
[bir] …  run.finish  run=run_… steps=14 durationMs=48210 lossy=false recorded=true

recorded=false means the service declined the run — usually because it matched something you already have, which is similarity detection working. Every decision is one key=value line, and the background recorder writes them to ~/.baseinstrunner/logs/<key>.log, one file per project; bir status and bir doctor name the file, and so did the log line of bir setup's summary. There is no terminal to watch, so follow the file:

Get-Content -Wait -Tail 20 $HOME\.baseinstrunner\logs\<key>.log
tail -f ~/.baseinstrunner/logs/<key>.log

A bir-hooks run by hand in a terminal, which still works, prints the same lines there instead — on stderr, never stdout, which belongs to the host's JSON-RPC stream.

Calculate, then check

bir scenario list             # what you have
bir scenario calc run_5f3a…   # start the background job
bir scenario show run_5f3a…   # poll until state leaves `calculating`

failed carries an error field; fix what it points at and recalculate with --force. Or do all of this from the console's recording detail page, which is the same API with the results laid out — with one exception: the console has no Recalculate button, so a failed calculation is run again there by preferring the recording.

Or do nothing. Once a prompt has come back three times (SCENARIO_AUTO_MIN_HITS) and the run it keeps matching passed end to end — it finished without an error and no tool call in it failed — the service calculates the recording with the most to save by itself, and the console marks it auto. A run has to have been quiet for ten minutes first (SCENARIO_AUTO_SETTLE_MS): a session that has just finished is often still sending steps. It never deletes anything and never retries a failed calculation, and it works through one calculation at a time per account. Or say which ones to leave alone: press Switch off the whole scenario on a recording's page and the picker never chooses it, however often its prompt comes back, and a matching prompt is not replayed from it either — though you can still calculate it yourself. Or say which one you want: press ☆ Prefer this recording on its page. It is calculated straight away and takes nothing from anything else you hold. SCENARIO_AUTO_CALCULATE=false leaves calculation entirely to the button.

Do this every time

Dry-replay before you trust it. It derives parameters from a new prompt and evaluates the stored logic against the recorded outputs — no real tool is called, nothing is billed, and it is the cheapest way to find out that step 3 computes an argument you did not expect.

bir scenario replay scn_9c1b… --prompt "check the us-east-2 fleet" --dry

If the dry replay shows a step working out the wrong thing, you do not have to recalculate the whole scenario. That one step can be corrected: see Let Claude fix a step, below (not released yet).

Turning replay on

Recording is the default. Calculated replay is installed and on as well, once bir setup has run: it added the bir scenario server to .mcp.json, pre-approved it in .claude/settings.local.json, and said so at the end of its wiring step:

Wrapped 1 server.
Next: start your session here as usual. The recorder starts itself with it;
`bir up` starts it now, `bir down` stops it, `bir doctor` checks it.

Calculated replay is INSTALLED and ON. `bir replay off` turns it off here, and
`bir replay allow <servers>` narrows what it may call unattended — read
docs/calculatedReplayGuide.md §5.1 first: a replayed step is auto-approved,
and a directly executed one never reaches the permission system at all.

The switches are per project, stored so a recorder started by the hook keeps them; a running recorder reads a change when it restarts:

bir replay allow fleet,github   # the servers replay may call unattended (default: all wrapped); `allow all` widens it again
bir replay off                  # record only; `bir replay on` brings it back
bir replay status               # what is stored for this project
bir up --restart                # apply a change now, rather than with the next session

By hand, on a project wired without it: bir install --replay adds the server and the pre-approval (a flag-less bir install keeps the way the project was installed — global, local or npx), then bir up --restart.

Read this before leaving it on anywhere that matters

A scenario is a pre-approved list of tool calls with computed arguments. A steered step is answered permissionDecision: "allow", so a pinned Bash or Edit runs without the prompt you would otherwise have seen. A directly executed step never reaches the permission system at all.

Run bir replay allow with the servers you are content to have called unattended. Leaving it unset means every wrapped server is eligible, and that is not the setting you want. Where a wrapped server does things you would not want done unattended, bir replay off until you have dry-replayed and read the scenarios.

Configuration

The switches above are stored per project and are the reliable way. The variables below override them where the recorder is started from — for one started by the SessionStart hook that is Claude Code's environment, not your terminal's, so a variable set in one terminal is not seen. bir doctor says which won (source=env, sidecar or default).

VariableEffect
BIR_AUTH_URLThe Basic2202 service's API address. Overrides the one bir setup stored in ~/.baseinstrunner/config.json. Neither set means nothing is recorded — everything else still works
BIR_CONTROL_PORTPreferred control-server port (default 53411). Each project has its own; bir setup --port <n> is the way to change it
BIR_CORRELATION_DECISIONallow (default) or ask. Correlation auto-approves wrapped MCP calls; ask keeps the prompt, at the cost of lossier matching
BIR_HOMEWhere state lives (default ~/.baseinstrunner)
BIR_VERBOSE=1Per-message detail in the audit log
BIR_REPLAY=0Turn replay off. It is on once installed; bir replay off is the per-project way, and this overrides it
BIR_REPLAY_ALLOW_SERVERSServer keys eligible for direct execution; overrides bir replay allow. Unset means the stored list, else every wrapped server
BIR_MIN_STEER_SIMILARITYMinimum match similarity to replay (default 0.92)
ANTHROPIC_API_KEYOptional. Working out what a new request is about — which fleet, which week — is done for you by the service on its own key while the runner is signed in. Set this only to keep that step on your machine, on your key

When it goes wrong

The governing rule is that a host session never fails because of BaseInstRunner. Every failure degrades to passthrough or to not-recording.

SymptomCauseFix
Nothing appears in the consoleNot signed in, or no service address storedbir setup in the project folder (or bir login on its own), then bir up --restart; check bir doctor
tier: standaloneNo recorder running for this cwd — the hook starts one with each session, so this is a session that began before bir setup, or a recorder that was stoppedbir up in the project folder, or start a fresh session; bir doctor says whether one runs
Recorded, but not in the listThree actions or fewerNothing to fix — below the threshold on purpose
recorded=falseThe service matched it to an existing runWorking as intended
No prompt ever matchesSimilarity detection off, or the prompts really are differentCheck SIMILARITY_DETECTION_ENABLED; re-read the prompt you wrote
Calculate returns 503No Anthropic key on the serverServer-side configuration

Sample — the fleet demo

A complete run-through against a real MCP server, on your machine, with no cloud resources and no money spent. About ten minutes. It uses the fleet-demo folder you built in step 2 of Getting started: a tiny but genuine MCP server over stdio, with two tools.

list_devices(fleet) → the fleet's devices, with risk scores
device_detail(id)   → one device's detail
Why these two tools

Step 2 can only compute its id argument from step 1's output. That makes this the smallest honest test of whether scenario calculation actually works, rather than the smallest test that passes.

The task: "check the eu-west-1 fleet and tell me which device is most at risk" — which resolves, in the sample data, to dev_88f1: edge-01, risk 0.94, firmware 3.1.2.

  1. Set up the project

    This is the fleet-demo folder from step 2. If you have not built it yet, go back and do that first — two files and a minute. If you ran bir setup in it there, it is already wired: skip to step 2. Otherwise, in the folder:

    cd fleet-demo
    bir setup

    The sign-in from Getting started is kept on this computer, so no token and no address are needed. bir setup rewrites .mcp.json so the fleet server runs through bir-proxy, writes Claude Code's hooks into .claude/settings.local.json (kept out of git), adds and pre-approves the bir scenario server, and starts the recorder in the background; both files are generated for your machine, absolute paths and control-server port included. It keeps a copy of everything it touches, and bir uninstall puts it all back byte for byte. Replay is on from here, so step 7 needs no second install.

  2. Check the recorder

    Nothing to start: bir setup started the recorder, and the SessionStart hook starts it again with every Claude Code session in this folder. In the same folder, bir doctor should say bound (Tier 1) and Recording: yes; recorder not running before the first session is normal — the session starts it, or bir up now. If it reads standalone during a session, the recorder and the session disagree about either the working directory or the port — the only two things discovery matches on.

  3. Record the run

    In the same terminal, same folder, start Claude Code and ask it the task:

    claude

    Ask for "check the eu-west-1 fleet and tell me which device is most at risk". The two tool calls land in the audit log — ~/.baseinstrunner/logs/<key>.log, the file bir doctor names — then a run.finish line.

  4. Look at it in the console

    Recordings holds the run with its generated title, step count and cost. This is the baseline: what the task costs when the agent does it.

  5. Calculate the scenario — and read it

    bir scenario list                 # find the run id
    bir scenario calc run_…
    bir scenario show run_…

    Read what comes back before going further. You are looking for three things:

    • an intent that describes the task, not the specific fleet;
    • fleet as a parameter with eu-west-1 as its sample value — if the region is baked into a step's logic, the scenario only ever answers one question;
    • step 2's logic reading step 1's output to pick the highest-risk id. A scenario that hard-codes dev_88f1 looks identical on a green run and is wrong the moment the data moves.
  6. Dry-replay against a different fleet

    bir scenario replay scn_… --prompt "check the us-east-2 fleet" --dry

    The derived parameter should be us-east-2. Since a dry run evaluates against recorded outputs, step 2 still picks from the eu-west-1 devices — correct behaviour, and the reason those lines are marked Result (recorded):. What you are checking is the parameter derivation and the step logic, not the answer.

  7. Replay it for real

    Replay has been on since bir setup. Narrow it to the fleet server — the only one this scenario needs — and let the recorder read the change:

    bir replay allow fleet
    bir up --restart

    Then start claude again and ask for "check the us-east-2 fleet and tell me which device is most at risk".

    Now the tools are really called. us-east-2's highest-risk device is dev_4411 — core-01, risk 0.77. That is the proof the scenario computed the answer rather than remembering it.

  8. Read the ledger

    Back in the console, check three things: Saved $ is populated and the baseline's ⓘ says how many runs it is drawn from (on a first pass, one — and one sample is a guess); the headline moved under verified, not projected; and the scenario is not flagged as not paying off. Saved %/run is the same verdict per run at a glance — a green bar is a cut, a red one is a loss. On a two-step task against a local server the arithmetic is tight — on real work with real latency it is not.

The second sample. There is another demo of the same shape with a longer chain — list_services, service_metrics, error_logs, recent_deploys, dependency_graph. It lives in the runner repository, which is not public, so it is not something you can fetch today; the point of it survives without it. Extend the fleet server above until it has five tools, at least one of them needing an argument computed from an earlier step's output. That is worth doing once the fleet demo works, because a five-step scenario is where the interesting failures start: a step whose logic reads the wrong earlier output looks fine on two steps and falls apart on five.

Ask why — did my task use its calculated scenario?

Once a task has been recorded and replayed a few times, three questions come up: did this run use the calculated scenario, and if not, why? How much did it save, and why not more? What happened, step by step? The runner answers all three with one command, bir investigate, and Claude Code can run it for you and explain the answer in plain words. Every finding names the problem, the cause and the fix. Steps 1 to 4 below only read. Step 5 lets Claude fix one step of the scenario, after your yes (not released yet).

Where the answer comes from

The recorder keeps a journal of every decision it makes in a folder — which recording a prompt matched, which gate declined and why, whether the plan ran in direct or steer mode, what the run cost. The service adds the recording, the scenario, the baseline and the ledger. You see your own data; an admin sees everyone's.

  1. Do a task twice

    In a folder the runner is set up in, ask Claude Code for a task, let it finish, then ask for the same task again. The second time the audit log (~/.baseinstrunner/logs/<key>.log) gets a run.matched line, and either plan.armed or replay.decision verdict=no-steer. That second turn is what you are about to investigate.

  2. Ask Claude Code why

    Do this. In the same Claude Code session, in the same folder, paste:

    Run `bir investigate` in this folder and explain in plain words:
    did my last task run its calculated scenario, and if not, why?
    If it did, how much did it save and why not more?
    List the fixes it suggests. Do not run any fix — just report.

    You should see Claude run the command and report on it. The command's own output looks like this (a real run from the fleet demo):

    Turn run_0544…  2026-09-20 12:19:18Z  session e5b66bfb
      prompt    Give me a full detail report for every device in eu-west-1
      matched   run_be1c… (similarity 1.000) → scenario scn_4641…
      plan      steer, 4 steps  Bash:live Bash:live Bash:live Bash:live
      replay    steered_full 4/4 steps in 27.3 s
      reported  steered_full  derive $0.0004  session $0.4482  fallback $0.0000  → saved $0.0910 (16.9% of $0.5396)
      finished  8 steps, 33.6 s, cost $0.4482, not recorded
    
    Runner findings
      1. [warn] The plan armed in steer mode: 4 of 4 steps are reachable only inside the session (Bash).
         why:  In steer mode the runner pins each call's inputs … the whole live turn is still paid for.
         fix:  Put the data those tools reach behind an MCP server, wrap it (`bir install --server <name>`) …
      2. [warn] Saved 16.9%: $0.0910 of a $0.5396 baseline.
    
    From the service (execution sexec_…), owner you:
      recording run_be1c…  listed, embedded  4 tool calls (Bash)  cost $0.5397  hits 5
      scenario  scn_4641…  ready  4 steps (Bash)  baseline $0.5397 from 1 sample(s)
    
    Service findings
      1. [warn] 4 of 4 steps use built-in tools (Bash), so this scenario can only run in steer mode.
      2. [info] The baseline is one run ($0.5397), so every percentage is against that single measurement.

    Claude's summary of that would be: the task did use its scenario, but in steer mode, because every step is a Bash command; the model still paid for the whole turn, so it saved 17%. To save more, put the fleet data behind an MCP server and wrap it.

    If it goes wrong. No turns in the journal for this directory: the recorder was not running in this folder when the task ran — bir doctor says whether it is now; run the task again once it is — or the runner is older than this feature: paste the line from the console again (npm i -g @basein/runner@latest only if you installed by hand) and check that bir --help lists investigate. service not consulted: not signed in — run `bir login`: the runner is not signed in on this computer; bir setup in this folder signs it in (or bir login on its own). the service has nothing for …: the run belongs to another account, or the service is older than the runner.

  3. Ask about one run, or about the money

    The same command takes an id, so Claude can be pointed at a specific run, scenario or execution. Useful prompts:

    • "Run bir investigate list and tell me which turns were steered and what each saved."
    • "Run bir investigate scn_… and explain why the saving is low and how to fix it."
    • "Run bir investigate executions --json and show me the executions that failed, with the step that broke."
    • "Run bir investigate run_… — why was this recording never calculated?"

    Everything Claude runs for these prompts only reads: it changes nothing. When a finding says fix: recalculate or fix: wrap the server, that is a command you choose to run next — or ask Claude to. To let Claude change a step of the scenario itself, see step 5.

  4. Without Claude Code

    The command works on its own in any terminal in the folder, with the recorder running or not:

    bir investigate                        # the newest turn in this folder
    bir investigate run_… | scn_… | sexec_… # one run, scenario or execution
    bir investigate list --limit 10         # recent turns and their verdicts
    bir investigate executions              # the ledger, newest first
    bir investigate executions --user ada@example.com   # admins only

    Add --json to any of them for the raw data.

The finding saysIt meansFix
declined_not_readythe prompt matched a recording that has no ready scenario yetwait for the automatic calculation (three similar prompts), or bir scenario calc run_…
declined_similaritya hit, but below the runner's steering threshold (0.92)phrase the prompt closer to the recorded one
declined_coveragenone of the scenario's tools is reachable in this sessionbir install --server <name>, start a fresh session
steer_mode / steer_onlysome steps are built-in tools (Bash, Read…), so the model still runs the whole turnput that data behind an MCP server, wrap it, record the prompt again
saving_low / saving_negativethe replay cost most of, or more than, the baselinethe finding names the dominant cost — session, fallback or derivation — and its fix
baseline_singlethe % is against one measured runlet the agent run the task a few times unsteered (bir replay off, then bir up --restart; bir replay on afterwards)
run_below_thresholdfewer than four tool calls, so the run is not a recordingrecord a longer task, or mark it preferred
awaiting_hits2 of 3 similar prompts so farrun it once more, or calculate now
execution_failed / fell_backa step broke, and wherethe fix for that stage: recalculate, check the tool, check the target
step_nondeterministica step needs a judgement: its code could not work out what to send, so every replay hands over to the agent in front of itrecalculate once (bir scenario calc scn_… --force, with the id the finding prints; it adds --discard-edits when steps were edited by hand); if it is still marked, fix the step (step 5). A step you marked yourself (--freeze) is unmarked with bir scenario edit scn_… --step n --unfreeze
step_editeda step's code or mark was changed by hand, with bir scenario editnothing to fix. bir scenario edits scn_… shows the change; bir scenario undo scn_… --step n takes it back
step_edit_forceda step was saved with --force, although its check against the recording failedread the note with bir scenario edits scn_…; take it back with bir scenario undo scn_… --step n, or save a version that passes the check
  1. Let Claude fix a step

    Not released yet

    This is built in the service and in the runner. It arrives with the next version of the runner (@basein/runner) and the next update of the service. Until then it does not work. A runner of version 0.2.10 or older does not know scenario check. Version 0.2.11 lists an early form of it, but the service does not answer it yet: you get (not_found) or not yours.

    Sometimes a finding is about one step of the scenario. The most common one is step_nondeterministic: Step 4 (mcp__postgres__execute_sql) needs a judgement, so runs hand over in front of it. Every replay then stops before that step and the agent does the rest, so the saving from that step on is lost. Often the cause is a small mistake in the step's code, and Claude can fix it.

    A step's code is a few lines of JavaScript that work out what the step sends. In the hiring-year task of 2026-09-25, step 4 had this code:

    return {"sql":"SELECT * FROM employees.hiring_year_summary ORDER BY hire_year;"};

    That is a copy of the old command. It sends the same table every time, whatever the new request says. The fix builds the command from the request:

    return { sql: "SELECT * FROM " + parameters.summary_table + " ORDER BY hire_year;" };

    The service tries every change against the recording before it keeps it. It saves the change only if the new code, given the recording's values, makes the same call the recording made, and is not just a copy of it like the old code above. Only you can change your scenarios: not another account, and not an administrator.

    The new code runs at replay without asking you, exactly like the code the calculation wrote. The check looks only at what the code sends for the recording's values. Code that does something more in another case (for example, reads a file or calls a website only when a value is different) can still pass the check. So read the code before you say yes (What runs without asking you, the last point).

    Do this. In the same Claude Code session, in the same folder, paste:

    First run `bir investigate` in this folder; that only reads.
    If a step needs a judgement, or works out the wrong input, fix that step:
    1. Read it with `bir scenario show <scenario> --step <n>`.
    2. Write the corrected input logic to a file.
    3. Check it: `bir scenario check <scenario> --step <n> --input-logic <file>`.
       Show me what the check printed, and wait for my yes.
    4. Only if the check says "reproduces the recorded call", save it:
       `bir scenario edit <scenario> --step <n> --input-logic <file> --note "<why>"`.
    Always give --note. Never use --force. If the check refuses the change,
    tell me why, fix the code and check again.

    You should see Claude run bir investigate, then bir scenario show, then the check. bir scenario show prints the step's code, with the scenario's id and its chainRevision at the top. The check prints this:

    Step 4 (mcp__postgres__execute_sql) — checked against run_67afb010-…
      input    reproduces the recorded call
      later    not affected: only the input logic changed
      mark     needs a judgement (input_mismatch) → runs by itself
    Nothing saved. To save: bir scenario edit scn_4a17a126-… --step 4 --input-logic step4.js

    In plain words:

    • input reproduces the recorded call: given the recording's values, the new code makes the same call the recording made. This is the test that matters.
    • later not affected: only the code that works out what this step sends has changed. The steps after it use what this step gets back, and that did not change. (When a change can reach the later steps, the check tests them too, and the final answer.)
    • mark … → runs by itself: the step no longer needs a judgement. Replays will run it instead of handing over to the agent.
    • Nothing saved: a check never changes anything.

    When you say yes, Claude saves it:

    Step 4 (mcp__postgres__execute_sql) — checked against run_67afb010-…
      input    reproduces the recorded call
      mark     needs a judgement (input_mismatch) → runs by itself
    Saved as sedit_633dba0d-bbbe-4517-ac29-74cc85a3abda (revision 2). Undo: bir scenario undo scn_4a17a126-… --edit sedit_633dba0d-bbbe-4517-ac29-74cc85a3abda

    From then on, replays no longer hand over in front of that step. (A replay still hands over at an earlier step that needs a judgement. In the hiring-year task step 3 did too, and it was fixed the same way.) bir investigate now says the step was edited by hand (step_edited), and the console shows an edited by hand badge on the step.

    A change that does not pass is not saved. The check says where it differs. Here the new code put the schema name in twice (employees.employees):

    Step 4 (mcp__postgres__execute_sql) — checked against run_67afb010-…
      input    differs from the recorded call at character 32
               computed  … FROM employees.employees.hiring_year_summary ORDER BY h…
               recorded  … FROM employees.hiring_year_summary ORDER BY hire_year;"…
      later    not affected: only the input logic changed
      mark     needs a judgement (input_mismatch) → stays marked
    Nothing saved, and `bir scenario edit` would refuse it: the change does not reproduce the recording.
      Fix the code and check again. If the recording itself was wrong, edit can save it on purpose: add --force --note "why".

    To undo it.

    bir scenario edits scn_…             # every change: when, which step, and the note
    bir scenario undo scn_…              # take back the newest change
    bir scenario undo scn_… --step 4     # take back the newest change of step 4 only

    Undo walks back. Run bir scenario undo again, and it takes back the change before that one. Keep going, and the steps get the calculation's code back. In the hiring-year task, step 3 was fixed the same way, after step 4. Two undos took back step 3, then step 4:

    $ bir scenario undo scn_4a17a126-…
    Step 3 (mcp__postgres__execute_sql) — checked against run_67afb010-…
      input    back as it was before that edit
      mark     runs by itself → needs a judgement (input_mismatch)
    Step 3 is back as it was before sedit_02cb1210-1938-4902-9395-5a82b321276d (revision 4). Redo: bir scenario undo scn_4a17a126-… --edit sedit_1bcba735-a526-4a1c-a890-261addf2d82b
      One further back: bir scenario undo scn_4a17a126-…
    
    $ bir scenario undo scn_4a17a126-…
    Step 4 (mcp__postgres__execute_sql) — checked against run_67afb010-…
      input    back as it was before that edit
      mark     runs by itself → needs a judgement (input_mismatch)
    Step 4 is back as it was before sedit_633dba0d-bbbe-4517-ac29-74cc85a3abda (revision 5). Redo: bir scenario undo scn_4a17a126-… --edit sedit_8d3da392-1d96-4b2e-8c5d-34f8ab668b53
      One further back: bir scenario undo scn_4a17a126-…

    To bring a change back, run the Redo: command. It undoes the undo.

    An undo is checked like a change. If taking a change back would break a later step or the final answer, the undo is refused, and nothing changes. You can undo it anyway with --force --note "why". That is your decision, not Claude's.

    Let Claude use tools instead of the terminal (optional). Claude can always run bir in its terminal, where you see each command. The bir server in Claude Code can also give Claude the same actions as tools. The tools that only read (scenario_show, scenario_edits, investigate) are always there. The tools that change a scenario (scenario_check, scenario_edit, scenario_undo) are there only in a project where you switched them on:

    bir scenario editing on       # this project only
    bir scenario editing status   # is it on here?
    bir scenario editing off      # take the tools away again

    Then quit Claude Code and start claude again. A session that is already running does not see the change.

    The fixing is never recorded. The runner does not record its own bir tools: the bir server's tools (except run_scenario), and terminal commands (Bash or PowerShell) that only run bir. So a session that fixes a plan does not become a scenario of its own. A command that mixes bir with other work, such as npm test && bir investigate, is still recorded.

    What a recalculation does to a fix. A recalculation builds every step again from the recording, so it would throw the fix away. So it refuses:

    $ bir scenario calc run_67afb010-… --force
    Not recalculated: steps 3, 4 were edited by hand, and a recalculation rebuilds every step.
      To recalculate anyway and discard the edits: bir scenario calc run_67afb010-… --force --discard-edits
      (the edits stay in `bir scenario edits`, marked replaced)

    Add --discard-edits only when you want the calculation's code back. You can also give the scenario's own id: bir scenario calc scn_… --force --discard-edits. That works for every scenario, and it is the only way from the terminal for a sub-task's scenario. The service's own automatic repair also leaves a scenario with edits alone, and bir investigate says so (repair_skipped_edits).

    Saving on purpose. Sometimes the recording itself was wrong, and no correct code can match it. Then you can save the change anyway, with bir scenario edit … --force --note "why". If the check found any problem, the save is marked as forced. The step shows accepted on purpose, and bir investigate keeps saying so (step_edit_forced). Do this yourself, after you have read the code. Do not leave it to Claude.

    If it goes wrong. Each point but the last starts with words that bir prints.

    • usage: bir scenario <list|show|calc|replay> … after bir scenario check. Your runner is older than this feature. Once it is released, paste the line from the console again (installed by hand: npm i -g @basein/runner@latest).
    • the change does not reproduce the recording. The new code makes a different call. Claude should compare the computed and recorded lines, fix the code, and check again.
    • is a copy. The code returns the old values whatever the new request says. It would do the old job every time, so it is refused. The code must use the request's values (parameters) or what earlier steps returned.
    • too costly to check. Checking the code took more than its time (5 seconds of running code, at the default). Make the code do less, and check again.
    • Nothing would change (from a check) or Nothing to change (from an edit). The step already has this code. Nothing to do.
    • (not_a_tool_step). The step is a call to a sub-task. Only tool steps can be changed.
    • comes after a sub-task call. The check cannot rebuild what the step reads, so it can only be saved with --force. That is your decision, not Claude's.
    • (revision_changed). The scenario changed since Claude read it. Read the step again, and check again.
    • (not_ready) or (scenario_calculating). The scenario is not ready, or a calculation of it is running. Wait for it to finish, then check again.
    • (rate_limited). More than 30 checks, edits and undos in a minute. Wait a minute.
    • (not_found) or not yours. The id is wrong, or the scenario belongs to another account. Until this feature is released, the service itself answers this way.
    • (logic_sandbox_unavailable). The service could not run step code just then. Nothing was checked, saved or undone. Try again in a minute.
    • (invalid_input). Something in the request is not valid, for example code over 20 000 characters or a note over 500. The words after it say what.
    • (payload_too_large). The request is far too large. A step's code may be at most 20 000 characters, and a note 500.
    • Not undone: with after this undo. Taking the change back would break a later step or the final answer, so it was refused. The message names what would break. Fix or undo that first, or leave this change as it is.
    • (not_latest_edit). A newer change of the same step came after it. bir prints two commands: run them in that order.
    • (already_undone). That change was taken back already. To bring it back, run the command bir prints (it undoes the undo).
    • Nothing to undo. There is no change left to take back.
    • The tools do not appear after bir scenario editing on. Restart Claude Code.

Videos

Status

One of the four is recorded. Video 2, the install, was filmed on 2026-09-12 and is embedded at the top of Getting started — 7 min 54 s, silent, captioned. The other three are still shot lists and narration, written so somebody can sit down and record them without having to invent the structure first.

Common conventions, so the four feel like one set:

  • 1280×720, terminal at a size legible on a phone.
  • Light theme in the console — it is the default and it screenshots better.
  • No music. Voice-over or captions, not both.
  • Real data from the fleet sample. Never a mock-up of a screen that does not exist.
  • Cut every wait longer than two seconds. Calculation takes about ten; nobody needs to watch it.
1 · What is Basic2202? 90 seconds
TimeShotNarration
0:00Terminal: the same task run twice, cost visible both times"Your agent solved this yesterday. Today it paid full price to solve it again."
0:15The record → calculate → replay diagram"Basic2202 records the run, derives the plan, and runs the plan next time."
0:35Console: the recording, then its scenario — intent and parameters"A transcript says what the agent did. A scenario says what it was doing."
0:55Dashboard, verified tile, ⓘ open"And it measures what that saved. The number it leads with is the one it watched move."
1:15Recordings list, the savings column"It is free to use. What we sell is running the whole thing inside your own network, when the recordings cannot leave it."
2 · Install and record your first run 4 minutes

The guide, executed. One terminal, one browser window.

Recorded — the film in Getting started. It was filmed before the one-line path existed and follows this list loosely: the demo project and one run of the task by hand come first, then the install and the sign-in by hand and the recorder in a window of its own, and it runs on past the first recording to the scenario being calculated and the saving being verified. It is silent, with the step being done named in the corner, so it was cut without narration; the shot list below is the retake, with the line.

  1. Paste the line from the console's Set up the runner page — say what it installs, what bir setup rewrites, and that uninstall reverses it byte for byte.
  2. bir doctor. Point at bound (Tier 1) and the recorder in the background with its pid and log; say what Tier 2 would mean, and why this is the command to trust, not status.
  3. Run the fleet task. Watch run.start and run.finish land in the audit log.
  4. Console → Recordings → the row. Name the columns: Steps, LLM $/run, and the baseline's sample count.
  5. Close on the threshold: "three tool calls or fewer and it is not listed — there is no plan in a single call."
3 · Calculate a scenario, and check it before you trust it 5 minutes

The video that earns its place, because this is where people get burned.

  1. scenario calc, then scenario show. Read the intent aloud.
  2. Stop on the parameters. fleet: eu-west-1 — a sample value, not a constant.
  3. Stop on step 2's logic. Show it reading step 1's output. Then show, by hand, what a hard-coded dev_88f1 would look like: identical on this run, wrong on the next.
  4. Dry replay against us-east-2. Explain Result (recorded): and why a dry run still shows eu-west-1 devices.
  5. Real replay. dev_4411. "It computed that. It did not remember it."
  6. Close on the ledger row and the not-paying-off flag: "a scenario can cost you money, and this is where it says so."
4 · Reading the console 3 minutes
  1. The sidebar: the sections, and what each one is for.
  2. ⌘K, including the recordings-search row. Say plainly that it does not search everything.
  3. Recordings: verified versus projected, with the ⓘ open. The segment worth over-explaining.
  4. The Saved %/run bars: read one row's two $/run figures against its percentage, then show a red one.
  5. auto in the origin column, and the ★ Preferred and ⊘ Switched off badges under two titles: a row the service calculated by itself, one the user preferred over it, and one they switched off.
  6. The delete confirmation, naming the savings that will leave the headline.

They live in videos/ beside this page, deployed with it, and are embedded beside the section each one covers; the console embeds them from this address, VITE_DOCS_URL. The install film is there. Until the others exist, their written sections stand on their own — which is why they were written to.

Five

Security and privacy

What Basic2202 sees, where it goes, how long it stays, and what it does without asking you. This section describes what the software does today, in plain words.

The formal version is the Privacy Policy on the website; where the two differ, the policy is the one that counts. Terms of service will be published separately.

In one paragraph

The runner records what your agent did: the request you typed, every tool call with its arguments and results, the model's reasoning before each call, the final answer, and what it all cost. It strips the secrets it recognises, then sends the rest over HTTPS to your Basic2202 account, where you can read it, and so can an administrator of the service, read-only (see Who can read them below). Working out a plan from a recording sends that recording to Anthropic. Nothing is sold, nothing is used for advertising, and no analytics run anywhere. You can delete a recording at any time, and everything derived from it goes with it.

What is recorded

One recording is one run of your agent: from the request you typed to the answer it gave. For each run the runner keeps:

  • The request you typed, word for word.
  • Every tool call: the tool's name, the arguments the agent sent, and what came back, or the error if it failed. Calls to the project's MCP servers pass through the runner itself. The agent's built-in tools, such as reading a file or running a shell command, are seen through Claude Code's hooks, which is why the recorder (bir-hooks) has to be running — bir setup starts it, and the SessionStart hook starts it with every session.
  • The model's reasoning written just before each tool call. The runner reads it from the session's transcript file on your own machine and attaches it to the step.
  • The final answer, the model used, token counts, timings and the cost.
  • Run details: which recorder made it, whether the trace is complete or MCP-only, the host application, the names of the wrapped servers, whether any steps were dropped, the session id, and the path of the project folder, which can include your username. An MCP-only run also carries your computer's name.

A recording can therefore contain anything a tool read or returned: the contents of a file the agent opened, the rows a query returned, a page a browser tool fetched. Record only what you are comfortable storing.

What is not recorded. Your password: you never type it into the terminal, because bir login opens the browser, where you approve a code, or takes a one-time setup token made in the console. Anything the agent did not do: the runner does not scan your disk or watch other programs, and sees only what passed through a wrapped server or a hook, in a folder you ran bir setup in. Sessions in other folders: each project is recorded only while its own recorder is running there, and only a folder that was set up has one.

What is stripped before sending. Redaction runs on your machine, on the copy that is about to be sent, before it is even queued:

  • By name. A value stored under a name that looks secret is replaced with [redacted], whatever it is: token, secret, password, api_key, authorization, cookie, session_id, credential, private_key, access_key, client_secret and their variants.
  • Every value under an env block, because MCP server configurations put credentials there under any name at all.
  • By shape, wherever it appears, including inside free text: bearer tokens, JWTs, sk- keys of the OpenAI and Anthropic kind, GitHub, Slack and Google API keys, and AWS access key ids.
  • Size. A single string is cut at 8 KB and a step at 64 KB. A file a tool returned in full arrives as its first 8 KB.

This is pattern matching, not understanding. A secret stored under an unusual name, or shaped like ordinary text, goes through. Treat a recording the way you would treat a log file from your own machine, and do not wrap a tool that handles data you are not allowed to keep.

Where it goes

WhoWhat they receiveWhen
Basic2202's serverThe recording, as described above, over HTTPSEvery recorded run
AnthropicThe whole recording: reasoning, every tool input and output, and the final answerWhen a scenario is calculated, by you or automatically
AnthropicThe first 1,200 characters of the request, the names of the tools used, and a short excerpt of the first reasoning. Tool outputs and the final answer are deliberately left outWhen a run finishes, to write its title
AnthropicThe new request, the scenario's intent and its parameter samples, and — on a plan armed mid-task — the agent's reasoning, its pending call and its last few tool resultsOn a replay, so the service can work out what the request acts on. Nothing is stored. A runner with its own ANTHROPIC_API_KEY does this reading on your machine and your key instead, and never asks the service
PayPalThe invoice reference, the amount and the service's name. You pay on PayPal's own pages; Basic2202 never sees a card numberOnly if an invoice on this service is paid through PayPal. Using the service here is free, and In-house is paid on your AWS bill, so today nothing is sent
GoogleThat you signed in here; Basic2202 receives your Google id, email address and nameOnly if you sign in with Google
ResendYour email address and the one-time linkVerification and password-reset emails
Google FontsThe IP address and browser of whoever opens the pageThis handbook site only. The console loads nothing from anyone else
  • In transit. The runner talks to api.bi2202.com over HTTPS only, and refuses to follow a redirect to a different site while carrying your token.
  • At rest. Recordings are stored in a database on one server, rented from Hetzner in Germany. The application does not encrypt them itself; they are protected by the server and its disk.
  • Who can read them. You, signed in to your own account, and an administrator of the service: the Users screen opens any account's recordings, in full, read-only. An administrator cannot change, calculate, replay or delete a recording from there, and every time one is opened the service logs who looked at whose. The person who operates the server can read the database and the logs directly.
  • Recognising a repeated request is done with a small open-source model that runs on Basic2202's own server. Nothing leaves for that.
  • No analytics, no tracking pixels, no advertising scripts, in the console, the runner or this handbook.

How long it stays, and how to delete it

  • A recording stays until you delete it, from its own page in the console. Deleting it also deletes its steps, its fingerprint, its scenario and its replay history, and the savings it booked leave your totals with it.
  • Very short runs, a couple of tool calls, that were never calculated and never matched are pruned automatically.
  • A scenario does not expire. It stays with its recording, and steers a matching prompt, until the recording is deleted or its owner switches calculation and replay off.
  • Sign-in records keep the time, the method, the IP address and the browser or runner for every sign-in attempt, successful or not. They are kept for as long as the service runs, and your own are shown on your Sign-in history page. A bir login approval shows the runner's address on the approval page and is pruned a day later; a setup token made in the console is the same kind of record.
  • Sessions last thirty days from their last use. An access token lasts a day, a verification link a day, a password-reset link an hour, and a sign-in code ten minutes.
  • Closing your account, from Account settings, removes your profile, your password, your Google link and your sessions. It does not yet remove your recordings, scenarios, replay history or sign-in records. Delete your recordings first, and write to us for the rest.
  • On your own machine the runner keeps your session in ~/.baseinstrunner/credentials.json, readable by your user only, and a copy of every configuration file it changed, so that bir uninstall can put them back. bir logout revokes the session.
  • Server logs hold, for every request, the method, path, status, duration, IP address and, when signed in, your user id. Secrets are masked before they are written. Backups, when they are taken, can hold deleted data until they expire.

What runs without asking you

Four things happen without a prompt, and each is a switch you own.

  1. Wrapped tool calls are approved automatically while recording

    To attach a call id to a tool call, the runner answers Claude Code's permission hook with allow. So a call to a wrapped MCP server that you would otherwise have been asked about is not asked about. Set BIR_CORRELATION_DECISION=ask where the recorder is started from — Claude Code's environment, for one the hook starts — then bir up --restart, to keep the prompts at the cost of less exact matching.

  2. Replay is on once the runner is set up, and it is a switch

    bir setup installs the scenario server, and replay with it; bir replay off turns it off for that project (BIR_REPLAY=0 in the recorder's environment does the same). Nothing is replayed in a project that was never set up. When it is on, a steered step is approved for the agent, and a step the runner executes directly never reaches the permission system at all. Name the servers you are content to have called that way with bir replay allow <servers> (or BIR_REPLAY_ALLOW_SERVERS), and keep anything destructive, or anything that must not run unattended, off that list.

  3. Calculation can start by itself

    Once the same request has come back three times, the service works out the plan for the best candidate without anyone pressing the button, which sends that recording to Anthropic as described above. Every recording's page has a switch that turns calculation and replay off for that recording.

  4. Step code that you or Claude change runs like calculated code

    (Not released yet.) Each step of a scenario is a few lines of JavaScript that work out what the step sends. You can change a step with bir scenario edit, or let Claude do it for you (Let Claude fix a step). The new code then runs at replay on your machines, unattended, exactly like the code the calculation wrote. On your own machine the runner runs it with no sandbox, as it always has for calculated code, so read what Claude wrote before you agree. The limits are these:

    • The service saves a change only if it makes the same call the recording made, and is not just a copy of it. Saving anything else needs --force and a note saying why, and bir investigate keeps pointing it out.
    • The check looks only at what the code sends for the recording's values. Code that does something more in another case (for example, reads a file or calls a website only when a value is different) can pass the check, and then do that on your machine. That is why you read it.
    • Only the scenario's owner can change it. An administrator cannot.
    • Claude gets tools to change steps only in a project where you ran bir scenario editing on. bir scenario editing off takes them away. Claude can still run bir in its terminal, where you see each command.
    • bir scenario edits lists every change, when it was made and the note that says why. bir scenario undo takes back the newest change; run it again to go one further back.
    • The fixing itself is not recorded. The runner never records its own bir tools (except run_scenario, which runs a plan), or terminal commands that only run bir.
    • On the service, all step code runs in a sandbox, whoever wrote it. Each piece of code runs in a fresh, empty space inside a separate program (the evaluator). That program has none of the service's secrets, cannot read or write files, and cannot start other programs. The code gets only JavaScript's own built-ins: no network and no timers. It has a time limit and a memory limit (LOGIC_TIMEOUT_MS, LOGIC_MEMORY_MB). Code that goes past one is stopped, and the same code is refused at once for the next 10 minutes. The check of a change runs apart from the part of the service that answers other requests. It stops after about 5 seconds of running code (EDIT_CHECK_BUDGET_MS), and the change is then refused as too costly to check.

How the service is protected

  • Passwords are stored as argon2id hashes. Session, verification and reset tokens are random, stored only as hashes, and rotated when used.
  • An email address must be verified before it can sign in. Sign-in and account routes are rate-limited per address.
  • Everything is served over TLS, with strict transport security. The console loads scripts from its own origin only, and the API accepts browser requests from the console's origin only.
  • The API runs as an unprivileged user in a hardened system service that can write to one directory.
  • The runner package has no third-party dependencies at run time, so nothing else is loaded into your agent's process tree.

Your side of it

  • You choose what is recorded: which project you run bir setup in, which servers, and whether its recorder runs (bir down stops it; bir uninstall takes it out). Do not wrap a tool that returns secrets or personal data you are not allowed to store.
  • Delete what you no longer need. A recording is as sensitive as the tools it called.
  • Keep replay off destructive tools. A replayed scenario is a list of tool calls you recorded, run again with computed arguments. What those calls do is yours.
  • On a shared machine, run bir logout when you finish.

Reporting a security problem

Write to support@bi2202.com, the address on the Privacy Policy. Describe what you found and how to reproduce it, and please do not post it publicly before we have replied.

This section describes the runner at version 0.2.0 and the service as deployed on 2026-09-11. It is a statement of practice, not a contract; the Privacy Policy governs, and terms of service will be published separately. When the software changes in a way that matters here, this section changes with it.