rabbit-brain 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.egg-info/
4
+ build/
5
+ dist/
6
+ .pytest_cache/
7
+ rb-runs/
8
+ .venv/
@@ -0,0 +1,139 @@
1
+ # Rabbit Brain: AGENTS.md
2
+
3
+ Rabbit Brain (`rb`) reviews a candidate checkpoint of an iterative perception model against the current one: it ranks the cases that regressed on error or never settled during refinement, explains each one, and keeps checks so the next checkpoint gets the same review. It runs locally; nothing leaves this machine.
4
+
5
+ Read this whole file before running anything. **Compute per-case errors in your own evaluator; that is your metric. Do not compute regressions, rankings, stability or verdicts yourself.** `rb` defines them, applies the same definitions on every run, and records how, which is what makes a result comparable across checkpoints and people. If you find yourself writing a comparison script, stop and run `rb import`.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pip install rabbit-brain # or: uvx --from rabbit-brain rb
11
+ rb version # rabbit-brain 0.1.x
12
+ rb docs # prints this file
13
+ ```
14
+
15
+ Python 3.10 or later. The only dependency is pydantic. Nothing here needs a GPU: version 0.1 reads results your evaluator already produced.
16
+
17
+ ## When to use it, and when not
18
+
19
+ Use it when there are two checkpoints (or two models for the same task), one lower-is-better error per case for each, and a release decision to make. If the model refines its answer iteratively (RAFT-family optical flow, RAFT-Stereo-style depth), also export one number per refinement iteration per case and `rb` will flag the answers the candidate never settled on, which needs no labels.
20
+
21
+ Do not use it for training, hyperparameter search, certifying a model, or metrics where higher is better (convert those to an error first).
22
+
23
+ ## What is in 0.1, and what is not
24
+
25
+ In 0.1 you bring the numbers: `rb import` reads a results file (JSON, or a CSV of per-case metrics) and everything downstream works on it. `rb run`, the runner that executes both checkpoints and records trajectories itself, arrives in 0.2 together with `rb init`, `rb doctor`, `rb verify-hook`, `rb rerun` and `rb open`. Calling any of them in 0.1 returns `E_NOT_AVAILABLE` and explains this; do not try to work around it.
26
+
27
+ ## The workflow
28
+
29
+ ```sh
30
+ rb example # a run from built-in example data, to see what the output looks like
31
+ rb import results.json # your results (version-1 JSON; format below) → a new run under rb-runs/
32
+ rb import metrics.csv --project <p> --baseline-name <a> --candidate-name <b> --dataset <d> --metric <m> --unit <u>
33
+ rb findings <run> --top 5 # the ranked queue, the summary, the verdict
34
+ rb case <run> <case_id> # one case: numbers, trajectory statistics, the reasoning
35
+ rb check save <run> <case_id> # keep this case for the next checkpoint (limit = current error + max regression)
36
+ rb check run <run> --checks checks.json # next time: exit 1 if a saved check fails or a case is flagged
37
+ rb report <run> --print # the receipt a human reads (also written to the run directory)
38
+ ```
39
+
40
+ Other commands: `rb runs` (list runs), `rb check list|rm`, `rb schema <name>`, `rb docs --errors`, `rb version`. `<run>` is a run id (`rb runs`), a run directory, a `bundle.json`, or a version-1 results file read in place.
41
+
42
+ Limits, on any command that reads a run: `--max-regression 0.3 --max-late-share 0.25 --max-reversals 2` (these are the defaults; they are generic heuristics, and a scorer fitted to the model is a separate, paid step). `rb findings` also takes `--filter flagged|all|regressions|unstable|improved-unstable|settled-regressions|improved|stable` (default `flagged`), `--sort priority|error-change|late-share|name` and `--top N`.
43
+
44
+ ## The results file (version 1)
45
+
46
+ ```json
47
+ {
48
+ "version": 1,
49
+ "project": "My perception project",
50
+ "baseline": "model-v1", "candidate": "model-v2",
51
+ "dataset": "Regression set", "metric": "mean_endpoint_error", "unit": "px",
52
+ "cases": [
53
+ { "id": "seq-001", "name": "First sequence", "baseline_error": 2.1, "candidate_error": 2.4,
54
+ "candidate_trajectory": [2.3, 1.4, 0.9, 0.6, 0.4, 0.3, 0.2, 0.15, 0.1, 0.08, 0.06, 0.05] }
55
+ ]
56
+ }
57
+ ```
58
+
59
+ Rules: 1–500 cases; case ids match `^[a-zA-Z0-9_.-]{1,80}$` and stay the same across checkpoints (saved checks follow them); `baseline_error` and `candidate_error` are the same lower-is-better metric per case for the current and the candidate model, computed against the same ground truth and valid mask; `baseline_trajectory` / `candidate_trajectory` are optional, 2–64 numbers each, the mean |update| per refinement iteration (record them with the hook below); `baseline_frames` / `candidate_frames` are optional paired per-frame error series of equal length; `tags` and `notes` are optional. `rb schema example` prints this file; `rb schema comparison-v1` prints the JSON Schema. A CSV works too: columns `case_id, baseline_error, candidate_error` and optionally `name, tags, baseline_trajectory, candidate_trajectory, baseline_frames, candidate_frames` (series as semicolon-separated numbers), with the project, model names, dataset, metric and unit given as flags.
60
+
61
+ ## Recording a trajectory (the one line you add to the model)
62
+
63
+ ```python
64
+ from rabbit_brain import TrajectoryRecorder
65
+ rec = TrajectoryRecorder() # optional: TrajectoryRecorder(mask=valid_pixels)
66
+ for k in range(iters):
67
+ delta_flow = update_block(...) # the existing update
68
+ flow = flow + delta_flow
69
+ rec.step(delta_flow) # one number per iteration: mean |update| over the valid region
70
+ case["candidate_trajectory"] = rec.values # one recorder per case per model
71
+ ```
72
+
73
+ Works with torch tensors (shape `(B, 2, H, W)` is reduced to a per-pixel L1 magnitude), numpy arrays and nested lists. Record the current model the same way so both trajectories are compared under the same definition. If the update loop cannot be reached, import without trajectories: stability is then reported as "not assessed", never as "settled".
74
+
75
+ ## Output
76
+
77
+ Every command accepts `--json` and prints exactly one JSON object on stdout; logs and warnings go to stderr.
78
+
79
+ ```json
80
+ {"ok": true, "rb_version": "0.1.0", "command": "import", "run_id": "20260925-1412-model-v2",
81
+ "data": {"run_dir": "rb-runs/20260925-1412-model-v2",
82
+ "summary": {"cases": 12, "regressions": 3, "unstable": 4, "improved_unstable": 2, "settled_regressions": 1, "flagged": 5},
83
+ "verdict": {"status": "investigate", "ready": false, "line": "Not ready: 3 error regressions, 2 unstable cases that pass on error. Start with aisle-042.", "start": "aisle-042"}},
84
+ "errors": [],
85
+ "next": ["rb findings 20260925-1412-model-v2 --top 5", "rb case 20260925-1412-model-v2 aisle-042"]}
86
+ ```
87
+
88
+ `next` lists the commands that usually follow. A run directory `rb-runs/<run_id>/` holds `bundle.json` (every case's numbers, trajectories, stability statistics and flags), `record.json` (the receipt: rb version, the exact command, input file and its sha256, environment, limits), `findings.json` (the ranked queue, the summary, the verdict under those limits) and `report.md` (the human receipt). `bundle.json`, `record.json`, `findings.json` and `report.md` are small and committable; `checks.json` belongs in the repo next to the model code. Schemas: `rb schema bundle|record|findings|checks|envelope`.
89
+
90
+ Definitions `rb` applies, and restates in every report: a case is a **regression** when the candidate's error exceeds the current model's by more than `max_regression`; **late share** is the fraction of all refinement that happened in the last third of the iterations; a **reversal** is an iteration where the update grew by more than 5% over the previous one; a case is **unstable** when late share exceeds `max_late_share` or reversals exceed `max_reversals`. Cases are ranked regression+unstable, then regression, then **improved but unstable** (they pass on error and still need a look), then the rest, by error change. A **saved check** is an absolute limit for a case id in a project: candidate error ≤ `max_error`, optionally late share ≤ `max_late_share` and reversals ≤ `max_reversals`; a saved case missing from a later run fails the check.
91
+
92
+ ## Exit codes
93
+
94
+ `0` ok · `1` a saved check failed or is missing, or a case is flagged (`rb check run`; choose with `--fail-on any|checks|flags`, default `any`) · `2` invalid input or config · `3` environment failure. Read `errors[].fix`.
95
+
96
+ ## Errors and what to do
97
+
98
+ | code | meaning | do this |
99
+ |---|---|---|
100
+ | `E_IMPORT_INVALID` | the results file is not a valid comparison | fix every problem listed in `errors[0].problems`; start from `rb schema example` |
101
+ | `E_IMPORT_NOT_JSON` | the file is not JSON | check for a missing comma or bracket |
102
+ | `E_IMPORT_TOO_LARGE` | over 2 MB / 500 cases | split the case set or drop per-frame series |
103
+ | `E_IMPORT_CONFIG` | a CSV import is missing names | pass `--project --baseline-name --candidate-name --dataset --metric --unit` |
104
+ | `E_CSV_INVALID` | the CSV could not be converted | required columns `case_id, baseline_error, candidate_error`; series are semicolon-separated |
105
+ | `E_FILE_NOT_FOUND` | a path does not exist | paths are relative to the current directory |
106
+ | `E_RUN_NOT_FOUND` | no such run | `rb runs`; or pass a `bundle.json` / results file path |
107
+ | `E_CASE_NOT_FOUND` | no such case id in this run | `rb findings <run> --filter all` lists every id |
108
+ | `E_CHECKS_INVALID` | the checks file is not valid | `rb schema checks` |
109
+ | `E_CHECKS_PROJECT_MISMATCH` | the named checks file is for another project | use the same project name, or another `--checks` file (an unnamed `checks.json` for another project is ignored with a warning) |
110
+ | `E_LIMITS_INVALID` | a limit is out of range | `--max-regression ≥ 0`, `0 ≤ --max-late-share ≤ 1`, `0 ≤ --max-reversals ≤ 64` |
111
+ | `E_NOT_AVAILABLE` | the command is not in this version | see "What is in 0.1"; import results instead |
112
+ | `E_WRITE_FAILED` | a file could not be written | check permissions, or `--runs-dir` |
113
+ | `E_INTERNAL` | unexpected failure | re-run with `--json` and report it |
114
+
115
+ ## Worked example
116
+
117
+ Prompt from the human: *"Review candidate checkpoint v2 against v1 on the warehouse set with Rabbit Brain and tell me what to look at."*
118
+
119
+ ```sh
120
+ python evaluate.py --checkpoint v1 --out v1.json # your evaluator, unchanged: per-case errors (+ trajectories via the hook)
121
+ python evaluate.py --checkpoint v2 --out v2.json
122
+ python merge.py v1.json v2.json > results.json # one results file in the version-1 format above (ids identical in both)
123
+ rb import results.json --json
124
+ rb findings <run_id> --top 5 --json
125
+ rb case <run_id> <top case id> --json
126
+ rb report <run_id>
127
+ ```
128
+
129
+ Then report to the human, in this order: the verdict line; the top cases with their `why` text; the run id; the path to `report.md`; the command that reproduces the queue (`rb findings <run_id>` with the limits used). Quote numbers only from `findings.json`. If there were no trajectories, say that stability was not assessed.
130
+
131
+ When a later checkpoint arrives, import it under the same project with the same case ids and run `rb check run <new run> --checks checks.json` first: it answers "are the cases we cared about still fine?" and exits 1 in CI if not. Then `rb findings` for the full review.
132
+
133
+ ## Boundaries
134
+
135
+ `rb` does not run inference, does not modify model code (the one recorder line is the exception, and it only reads the update), and does not certify a model. Stability limits are generic heuristics and a starting point; a scorer fitted to the model is a separate, paid step. Nothing leaves the machine.
136
+
137
+ ## For humans: verify what your agent did
138
+
139
+ Open `rb-runs/<run_id>/report.md`. The header lists the run id, the `rb` version, the exact command, the input file with its sha256, the environment and whether trajectories were present; the body restates the definitions with the limits in force and lists every case. Re-run the command from the header, or `rb findings <run_id>` with the same limits, and compare. If what the agent told you differs from the report, the report is right.
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-09-19)
4
+ First release: the checker and importer from the Rabbit Brain v5 integration kit as a package.
5
+ - `rb import` (version-1 JSON or metrics CSV) → a run directory with `bundle.json` (version 2), `record.json`, `findings.json`, `report.md`.
6
+ - `rb findings`, `rb case`, `rb report`, `rb check save|run|list|rm`, `rb example`, `rb runs`, `rb docs`, `rb schema`, `rb version`.
7
+ - `--json` envelope on every command; exit codes 0/1/2/3; error codes with fixes.
8
+ - `TrajectoryRecorder` (the one-line hook) as a Python API.
9
+ - Same arithmetic as the web workspace and the v5 `check.py`: late share, reversals, regression threshold, ranking, verdict, report.
10
+ Not yet: `rb run` (the runner), adapters, evidence rendering, `rb open`. Those are 0.2.
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.5
2
+ Name: rabbit-brain
3
+ Version: 0.1.0
4
+ Summary: Release review for iterative perception models: rank the cases that regressed or never settled, keep checks for the next checkpoint.
5
+ Project-URL: Homepage, https://rabbit-brain-evaluation.jhetchan.chatgpt.site
6
+ Project-URL: Documentation, https://github.com/rabbit-brain/rb/blob/main/AGENTS.md
7
+ Project-URL: Source, https://github.com/rabbit-brain/rb
8
+ Project-URL: Paper, https://jhetchan.com
9
+ Author: Jhet Chan
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ Keywords: RAFT,agents,ci,depth,evaluation,model-release,optical-flow,perception,regression-testing,stereo
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Quality Assurance
24
+ Classifier: Topic :: Software Development :: Testing
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: pydantic<3,>=2.5
27
+ Provides-Extra: dev
28
+ Requires-Dist: build>=1.2; extra == 'dev'
29
+ Requires-Dist: pytest>=8; extra == 'dev'
30
+ Requires-Dist: twine>=5; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # Rabbit Brain
34
+
35
+ Release review for iterative perception models. Give it the per-case errors of your current and candidate checkpoints (and, if the model refines its answer iteratively, one number per refinement iteration) and it ranks the cases to look at: the ones that regressed on error, and the ones that pass on error but never settled. Then it keeps checks for the next checkpoint.
36
+
37
+ ```sh
38
+ pip install rabbit-brain
39
+ rb example # see it on built-in example data
40
+ rb import results.json # your evaluator's per-case results (format: rb schema example)
41
+ rb findings <run> --top 5 # the ranked queue and a verdict
42
+ rb check save <run> <case> # keep a case for the next checkpoint
43
+ rb check run <run> # exit 1 in CI when something regressed
44
+ ```
45
+
46
+ Everything runs locally and nothing leaves your machine. Every run leaves a receipt (`report.md`, `record.json`) with the exact command, the input's hash and the definitions in force, so a colleague, or you after your coding agent ran it, can verify the numbers.
47
+
48
+ **What it does.** Compares two model versions case by case; flags regressions above a limit you choose; reads each model's own refinement trajectory (label-free) to flag answers that were still moving late or reversed direction; ranks what needs a decision first; explains each case in plain language; saves checks that follow case ids across checkpoints.
49
+
50
+ **What it needs.** One lower-is-better error per case for both models (any metric with a unit: endpoint error in px, depth error in cm, …), computed by your evaluator against the same ground truth and valid mask. Optionally the trajectory, recorded with the one-line hook (`from rabbit_brain import TrajectoryRecorder`).
51
+
52
+ **What it doesn't do.** Run inference (that arrives with the runner in 0.2), train anything, or certify a model. The stability limits are generic heuristics; a scorer fitted to your model is a separate, paid evaluation.
53
+
54
+ **For coding agents.** `rb docs` prints [AGENTS.md](AGENTS.md): the workflow, the file format, the JSON output (`--json` on every command), exit codes and every error code with its fix. Tell your agent: *"Review candidate checkpoint B against A on this case set with Rabbit Brain."*
55
+
56
+ The trajectory diagnostic comes from *Beyond Endpoint Sufficiency* (Jhet Chan, NeurIPS 2026 submission). The web workspace that reads the same files is at https://rabbit-brain-evaluation.jhetchan.chatgpt.site.
57
+
58
+ Apache-2.0.
@@ -0,0 +1,26 @@
1
+ # Rabbit Brain
2
+
3
+ Release review for iterative perception models. Give it the per-case errors of your current and candidate checkpoints (and, if the model refines its answer iteratively, one number per refinement iteration) and it ranks the cases to look at: the ones that regressed on error, and the ones that pass on error but never settled. Then it keeps checks for the next checkpoint.
4
+
5
+ ```sh
6
+ pip install rabbit-brain
7
+ rb example # see it on built-in example data
8
+ rb import results.json # your evaluator's per-case results (format: rb schema example)
9
+ rb findings <run> --top 5 # the ranked queue and a verdict
10
+ rb check save <run> <case> # keep a case for the next checkpoint
11
+ rb check run <run> # exit 1 in CI when something regressed
12
+ ```
13
+
14
+ Everything runs locally and nothing leaves your machine. Every run leaves a receipt (`report.md`, `record.json`) with the exact command, the input's hash and the definitions in force, so a colleague, or you after your coding agent ran it, can verify the numbers.
15
+
16
+ **What it does.** Compares two model versions case by case; flags regressions above a limit you choose; reads each model's own refinement trajectory (label-free) to flag answers that were still moving late or reversed direction; ranks what needs a decision first; explains each case in plain language; saves checks that follow case ids across checkpoints.
17
+
18
+ **What it needs.** One lower-is-better error per case for both models (any metric with a unit: endpoint error in px, depth error in cm, …), computed by your evaluator against the same ground truth and valid mask. Optionally the trajectory, recorded with the one-line hook (`from rabbit_brain import TrajectoryRecorder`).
19
+
20
+ **What it doesn't do.** Run inference (that arrives with the runner in 0.2), train anything, or certify a model. The stability limits are generic heuristics; a scorer fitted to your model is a separate, paid evaluation.
21
+
22
+ **For coding agents.** `rb docs` prints [AGENTS.md](AGENTS.md): the workflow, the file format, the JSON output (`--json` on every command), exit codes and every error code with its fix. Tell your agent: *"Review candidate checkpoint B against A on this case set with Rabbit Brain."*
23
+
24
+ The trajectory diagnostic comes from *Beyond Endpoint Sufficiency* (Jhet Chan, NeurIPS 2026 submission). The web workspace that reads the same files is at https://rabbit-brain-evaluation.jhetchan.chatgpt.site.
25
+
26
+ Apache-2.0.
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "rabbit-brain"
7
+ version = "0.1.0"
8
+ description = "Release review for iterative perception models: rank the cases that regressed or never settled, keep checks for the next checkpoint."
9
+ readme = "README.md"
10
+ license = "Apache-2.0"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ authors = [{ name = "Jhet Chan" }]
14
+ keywords = ["optical-flow", "stereo", "depth", "evaluation", "regression-testing", "model-release", "RAFT", "perception", "ci", "agents"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Environment :: Console",
18
+ "Intended Audience :: Developers",
19
+ "Intended Audience :: Science/Research",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
26
+ "Topic :: Software Development :: Quality Assurance",
27
+ "Topic :: Software Development :: Testing",
28
+ ]
29
+ dependencies = ["pydantic>=2.5,<3"]
30
+
31
+ [project.optional-dependencies]
32
+ dev = ["pytest>=8", "build>=1.2", "twine>=5"]
33
+
34
+ [project.urls]
35
+ Homepage = "https://rabbit-brain-evaluation.jhetchan.chatgpt.site"
36
+ Documentation = "https://github.com/rabbit-brain/rb/blob/main/AGENTS.md"
37
+ Source = "https://github.com/rabbit-brain/rb"
38
+ Paper = "https://jhetchan.com"
39
+
40
+ [project.scripts]
41
+ rb = "rabbit_brain.cli:main"
42
+ rabbit-brain = "rabbit_brain.cli:main"
43
+
44
+ [tool.hatch.build.targets.wheel]
45
+ packages = ["src/rabbit_brain"]
46
+
47
+ [tool.hatch.build.targets.wheel.force-include]
48
+ "AGENTS.md" = "rabbit_brain/docs/AGENTS.md"
49
+
50
+ [tool.hatch.build.targets.sdist]
51
+ include = ["src/rabbit_brain", "tests", "AGENTS.md", "README.md", "LICENSE", "CHANGELOG.md", "pyproject.toml"]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
55
+ addopts = "-q"
@@ -0,0 +1,12 @@
1
+ """Rabbit Brain: release review for iterative perception models.
2
+
3
+ Rank the cases that regressed or never settled, keep checks for the next checkpoint.
4
+ CLI: `rb`. Docs for agents and humans: `rb docs` (AGENTS.md).
5
+ """
6
+ from __future__ import annotations
7
+
8
+ __version__ = "0.1.0"
9
+
10
+ from .recorder import TrajectoryRecorder # noqa: E402
11
+
12
+ __all__ = ["__version__", "TrajectoryRecorder"]
@@ -0,0 +1,107 @@
1
+ """Saved checks: absolute limits per case id for one project, kept in the repo (`checks.json`) and run against the next candidate."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from datetime import date
6
+ from pathlib import Path
7
+ from typing import Optional, Union
8
+
9
+ from pydantic import ValidationError
10
+
11
+ from .errors import RBError
12
+ from .fmt import pct, to_fixed
13
+ from .models import Bundle, CheckResult, ChecksV1, ChecksV2, CheckV1, CheckV2, ComparisonV1
14
+ from .stability import EPS, trajectory_stats
15
+
16
+ DEFAULT_CHECKS_PATH = Path("checks.json")
17
+
18
+
19
+ def load_checks(path: Path, project: Optional[str] = None, strict: bool = True) -> ChecksV2:
20
+ """Read a version-1 (workspace / kit) or version-2 checks file. Returns version 2, filtered to `project` when given.
21
+
22
+ A file for another project raises E_CHECKS_PROJECT_MISMATCH when `strict`, otherwise returns no checks (the caller may warn)."""
23
+ if not path.exists():
24
+ return ChecksV2(version=2, project=project or "", checks=[])
25
+ try:
26
+ raw = json.loads(path.read_text(encoding="utf-8-sig"))
27
+ except json.JSONDecodeError:
28
+ raise RBError("E_CHECKS_INVALID", message=f"{path} is not valid JSON.")
29
+ if not isinstance(raw, dict):
30
+ raise RBError("E_CHECKS_INVALID")
31
+ try:
32
+ if raw.get("version") == 2:
33
+ v2 = ChecksV2.model_validate(raw)
34
+ if project and v2.checks and v2.project != project:
35
+ if strict:
36
+ raise RBError("E_CHECKS_PROJECT_MISMATCH", message=f"{path} holds checks for '{v2.project}', not '{project}'.")
37
+ return ChecksV2(version=2, project=project, checks=[])
38
+ return v2
39
+ if raw.get("version") == 1:
40
+ v1 = ChecksV1.model_validate(raw)
41
+ checks = [c for c in v1.checks if project is None or c.project == project]
42
+ if project and v1.checks and not checks:
43
+ if strict:
44
+ raise RBError("E_CHECKS_PROJECT_MISMATCH", message=f"No saved checks in {path} match project '{project}'.")
45
+ return ChecksV2(version=2, project=project, checks=[])
46
+ proj = project or (checks[0].project if checks else "")
47
+ return ChecksV2(version=2, project=proj, checks=[CheckV2(case_id=c.id, name=c.name, max_error=c.max_error, max_late_share=c.max_late_share) for c in checks])
48
+ except ValidationError as exc:
49
+ raise RBError("E_CHECKS_INVALID", message=f"{path}: {exc.errors()[0].get('msg', 'invalid')}")
50
+ raise RBError("E_CHECKS_INVALID", message=f"{path}: expected version 1 or 2.")
51
+
52
+
53
+ def save_checks(path: Path, checks: ChecksV2) -> None:
54
+ try:
55
+ path.write_text(json.dumps(checks.model_dump(exclude_none=True), indent=2) + "\n", encoding="utf-8")
56
+ except OSError as exc:
57
+ raise RBError("E_WRITE_FAILED", message=f"Could not write {path}: {exc}")
58
+
59
+
60
+ def evaluate_check(check: CheckV2, comparison: Union[ComparisonV1, Bundle], unit: str) -> CheckResult:
61
+ """Same semantics as the workspace's checkResult() and the kit's check.py."""
62
+ found = next((c for c in comparison.cases if c.id == check.case_id), None)
63
+ base = dict(case_id=check.case_id, max_error=check.max_error, max_late_share=check.max_late_share, max_reversals=check.max_reversals)
64
+ if found is None:
65
+ return CheckResult(status="missing", reason="Case not present in this comparison.", **base)
66
+ base["candidate_error"] = found.candidate_error
67
+ if found.candidate_error > check.max_error + EPS:
68
+ return CheckResult(status="failing", reason=f"Candidate error {to_fixed(found.candidate_error)} {unit} exceeds the {to_fixed(check.max_error)} {unit} limit.", **base)
69
+ if check.max_late_share is not None or check.max_reversals is not None:
70
+ if not found.candidate_trajectory:
71
+ return CheckResult(status="failing", reason="This check requires a settled trajectory, but none was exported for the candidate.", **base)
72
+ stats = trajectory_stats(found.candidate_trajectory)
73
+ if check.max_late_share is not None and stats.late_share > check.max_late_share + EPS:
74
+ return CheckResult(status="failing", reason=f"Late revision {pct(stats.late_share)} exceeds the {pct(check.max_late_share)} limit.", **base)
75
+ if check.max_reversals is not None and stats.reversals > check.max_reversals:
76
+ return CheckResult(status="failing", reason=f"Reversals {stats.reversals} exceed the {check.max_reversals} limit.", **base)
77
+ return CheckResult(status="passing", reason="Within limits.", **base)
78
+
79
+
80
+ def evaluate_all(checks: ChecksV2, comparison: Union[ComparisonV1, Bundle], unit: str, case_id: Optional[str] = None) -> list[CheckResult]:
81
+ selected = [c for c in checks.checks if case_id is None or c.case_id == case_id]
82
+ return [evaluate_check(c, comparison, unit) for c in selected]
83
+
84
+
85
+ def upsert_check(checks: ChecksV2, new: CheckV2) -> ChecksV2:
86
+ others = [c for c in checks.checks if c.case_id != new.case_id]
87
+ return ChecksV2(version=2, project=checks.project, checks=[*others, new])
88
+
89
+
90
+ def default_check(bundle: Bundle, case_id: str, *, max_error: Optional[float], max_late_share: Optional[float], max_reversals: Optional[int], require_settled: bool, note: str) -> CheckV2:
91
+ case = next((c for c in bundle.cases if c.id == case_id), None)
92
+ if case is None:
93
+ raise RBError("E_CASE_NOT_FOUND", message=f"Case '{case_id}' is not in run {bundle.run_id}.")
94
+ if max_error is None:
95
+ max_error = float(to_fixed(case.baseline_error + bundle.limits.max_regression, 2))
96
+ if max_late_share is None and require_settled and case.candidate_trajectory:
97
+ max_late_share = bundle.limits.max_late_share
98
+ return CheckV2(case_id=case.id, name=case.name, max_error=max_error, max_late_share=max_late_share, max_reversals=max_reversals, from_run=bundle.run_id, created=date.today().isoformat(), note=note or "")
99
+
100
+
101
+ def describe_check(check: CheckV2, unit: str) -> str:
102
+ parts = [f"candidate error ≤ {to_fixed(check.max_error)} {unit}"]
103
+ if check.max_late_share is not None:
104
+ parts.append(f"late revision ≤ {pct(check.max_late_share)}")
105
+ if check.max_reversals is not None:
106
+ parts.append(f"reversals ≤ {check.max_reversals}")
107
+ return ", ".join(parts)