Skip to content

The record

This page says what the record holds, where each value in it came from, and who wrote what.

The record is a folder, .rb/, at the root of your repository. You commit it with your code, so it travels with the branch and git versions it. Every file in it is plain YAML you can read.

KindWhat it is
QuestionsWhat the work is trying to find out, in your words. Questions form a tree: a sub-question names its parent.
ExperimentsWhat is tested: a hypothesis or a purpose, the variants (one baseline, and candidates, controls or ablations), and the settings the variants differ in on purpose.
SettingsEach value an experiment depends on, with the file it came from and whether rb has checked it there.
MetricsEach number’s unit, which way is better, and its other names.
CriteriaWhat a result must meet: a rule over one comparison, with who declared it and when. See Criteria.
RunsWhat ran: its numbers, its per-run settings such as the seed, and a receipt.
AnalysesA computation over saved outputs, such as a bootstrap or a reanalysis, and the comparisons it produced with their intervals.
ResourcesDatasets, checkpoints and code the runs read or made: what each is, where its bytes are, and how to get it again.
NotesWords about an experiment or the project: an assumption, a caveat, context. A note changes no finding.
DecisionsThe calls made on the work, each with who made it, why, and what it was made on. See Calls.

There is one file per record, and one folder per experiment. Two branches that add runs write different files, so their work merges without a conflict:

.rb/project.yaml the project: its title, where it was cloned from
.rb/notes/<id>.yaml one file per note on the project
.rb/questions/<id>.yaml what the work is trying to find out
.rb/experiments/<id>/experiment.yaml an experiment: its questions, purpose, hypothesis, shared settings, freeze
.rb/experiments/<id>/variants/<name>.yaml one file per variant: its role and its own settings
.rb/experiments/<id>/notes/<id>.yaml one file per note on the experiment
.rb/experiments/<id>/criteria/<id>.yaml one file per criterion
.rb/experiments/<id>/runs/<id>.yaml one file per run, with its receipt
.rb/experiments/<id>/analyses/<id>.yaml one file per analysis
.rb/metrics/<id>.yaml the metric catalogue
.rb/resources/<id>.yaml datasets, checkpoints, code
.rb/decisions/<id>.yaml calls, each with who made it, why, and what it was made on
.rb/log.jsonl every write: who made it, in which chat session, how, and the hash of what was written
.rb/cache/ local copies, kept out of git

A setting is only as good as its source. rb spec set records a value with the file that states it: a config key (configs/train.yaml#optim.lr), a line, a quoted sentence, one row of a SQLite database, or one row of a CSV, TSV or JSON Lines table (results/sweep.csv#[precision=int8].calib). rb reads that source on the spot. Each setting then has one of four statuses:

  • verified: the source states this value. rb keeps the text it read, the file’s hash, and the commit.
  • provisional: the value was given but not checked, for example a web address.
  • unknown: nobody has found it yet.
  • inherited: it came with a record cloned from someone else’s.

rb is strict on purpose. The source must name the setting, not only hold the value. A comment never verifies. resnet50 does not state 50, and adamw does not state adam. A source that states another value is a conflict, and one that no longer states it is stale: both are problems on every finding of that experiment. Every read checks the sources again, so a config edited since is caught.

A verified setting is what the config file states, not what a run used. To check what a run used, record the run with its own resolved config (rb run add ... --config out/seed1/config.yaml). A run whose config disagrees with the spec is shown and not counted.

Agents and people both write to the record, and rb never refuses a write because of who makes it. What keeps the record honest is attribution: every write records who made it (you, or the agent rb detects), the chat session when the agent’s runtime gives one, the agent’s model when it said it, and the reason as given.

That attribution is in the data: rb log --json, --json on every command, and .rb/log.jsonl. It is never on a screen. rb status, rb open, rb ui and the page never say who made a write. They do say when something an agent wrote waits for you, for example “design not confirmed”.

A finding, an experiment’s state and a question’s state are never in a file. rb computes them from the runs every time the record is read, with the same definitions every time. If a page you were sent ever disagrees with rb status, rb status is right.

Never edit .rb/ by hand. rb compares each record’s content with its last write, and catches a change made outside rb: a run in that state is not counted, and every finding that depends on it lists the problem. rb doctor lists such files. rb doctor --restore puts back what rb last wrote, and rb doctor --adopt --why "..." keeps the change, recorded as whoever adopts it. A comment you add, or a reformat, is not an edit.

Every rb status says, in one line, what rb looks at:

Terminal window
$ rb status
…
rb looks in out/, outputs/, results/, eval-out/ and metrics/ at the top, in folders named for results (results/, eval/, metrics/, reports/, artifacts/ and the like), down to 4 levels from the top, at the files in the repository's top folder, and at .json, .yaml and .toml files git shows as new or changed. It last looked at 2026-11-06 20:25 UTC. It reads what code, configs, docs and database rows say only where the record points to them, and it sees only the commands run through rb journal run.
…

It also looks in every folder named for results (eval/, reports/, artifacts/ and the like) down to four levels from the top, which the line then names, and in any folder a recorded run named. rb reconcile lists what it found there that nobody recorded, and the rb run add line that records it. Under “Not read” it names a folder it passed over that holds results files of its own.

It names each results file in the repository’s top folder too: a table (results.tsv, results.csv, results.jsonl and the like) with the --where line that reads a row, and a report in prose (RESULTS.md, report.md), from which rb reads no run. Once a table’s rows are recorded, it says how many (“8 of 8 rows recorded as runs”).

A row of a CSV, TSV or JSON Lines table is a run: rb run add <exp> --from results.tsv --where <column>=<value> records one, and --every-row records every row --where picks, each as its own run. --every-row reads a SQLite table the same way: --from results.db --table evals. Record one table of many runs and Record a folder per run show both layouts.

Every file opens with its format. rb writes a criterion’s file in format 2 (rb: 2) only while it holds one of four kinds of rule that format 1 cannot hold: a count, a best of N, a relative change or a cited difference of means (see Criteria). It writes every other file in format 1 (rb: 1), as rb 0.7 writes it, and reads both formats. A record that uses none of the four is format 1 throughout.

rb 0.7.0 and 0.7.1 read format 1 only. They read a record with no format-2 file, and write to it as before. Once a file is format 2, they name it (“is format 2; this rb reads format 1: it was written by a newer rb”), say to upgrade (pip install --upgrade rabbit-brain), and write nothing to the record.

Both formats are one JSON Schema, rb-v2.schema.json. rb schema all prints it, and The record’s format lists every kind.

Rabbit Brainrabbitbrain.ai · GitHub · PyPI · Privacy · Apache-2.0