tare-cli 0.2.0__tar.gz → 0.3.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.
Files changed (39) hide show
  1. {tare_cli-0.2.0 → tare_cli-0.3.0}/CONCEPT.md +53 -11
  2. {tare_cli-0.2.0 → tare_cli-0.3.0}/PKG-INFO +14 -13
  3. {tare_cli-0.2.0 → tare_cli-0.3.0}/README.md +12 -12
  4. {tare_cli-0.2.0 → tare_cli-0.3.0}/pyproject.toml +2 -1
  5. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/agents.py +146 -44
  6. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/capsule.py +30 -27
  7. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/cli.py +5 -4
  8. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/judge.py +68 -14
  9. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/probe.py +66 -25
  10. tare_cli-0.3.0/src/tare/room.py +267 -0
  11. tare_cli-0.3.0/tests/conftest.py +16 -0
  12. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_pi.py +20 -1
  13. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_probe.py +50 -0
  14. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_room.py +19 -0
  15. tare_cli-0.3.0/tests/test_seatbelt.py +272 -0
  16. tare_cli-0.2.0/src/tare/room.py +0 -67
  17. {tare_cli-0.2.0 → tare_cli-0.3.0}/.gitignore +0 -0
  18. {tare_cli-0.2.0 → tare_cli-0.3.0}/LICENSE +0 -0
  19. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/__init__.py +0 -0
  20. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/calibrate.py +0 -0
  21. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/cliff.py +0 -0
  22. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/dashboard.html +0 -0
  23. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/dashboard.py +0 -0
  24. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/fake.py +0 -0
  25. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/journal.py +0 -0
  26. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/overview.html +0 -0
  27. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/recipe.py +0 -0
  28. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/swap.py +0 -0
  29. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/trail.py +0 -0
  30. {tare_cli-0.2.0 → tare_cli-0.3.0}/src/tare/usage.py +0 -0
  31. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_agents.py +0 -0
  32. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_agy.py +0 -0
  33. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_calibrate.py +0 -0
  34. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_cliff.py +0 -0
  35. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_dashboard.py +0 -0
  36. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_fake.py +0 -0
  37. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_judge.py +0 -0
  38. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_swap.py +0 -0
  39. {tare_cli-0.2.0 → tare_cli-0.3.0}/tests/test_usage.py +0 -0
@@ -4,7 +4,7 @@ tare starts a coding agent in an isolated room without your personal setup, and
4
4
  clean before the run. It prints `tare: 0.00`, names every leak and where it came from, or says
5
5
  that it could not see your setup (`tare: not proven (blind)`). It refuses to start the agent
6
6
  until the reading is `tare: 0.00`, unless you pass `--allow-dirty`. It supports Claude Code, Codex, Pi
7
- and the Antigravity CLI (`agy`) on Linux and WSL.
7
+ and the Antigravity CLI (`agy`) on Linux and WSL, and Claude Code, Codex and Pi on macOS.
8
8
 
9
9
  This document describes the design and the measurements behind it. The [README](README.md)
10
10
  explains the words this document uses: room, probe, reading, leak, declared, dirty twin, check, tail and
@@ -64,7 +64,9 @@ your instructions, memories, plugins, hooks and skills. That is not enough.
64
64
  credential files, and called it reconnaissance. That is correct behaviour, but useless for a
65
65
  probe. The probe must not depend on the agent's cooperation or honesty.
66
66
  - **Reach:** a plain script runs inside the room and looks for your files and inherited
67
- environment variables.
67
+ environment variables. On macOS it also looks for `~/Library` (Keychains, Preferences,
68
+ Application Support), your directory under `/var/folders`, Claude Code's temporary files in
69
+ `/private/tmp`, and whether the pasteboard can be read.
68
70
  - **Context:** tare points the real agent CLI at a fake model server on your machine. The
69
71
  server keeps the request, which is the context the agent CLI put together, and answers "ok".
70
72
  The idea is the blank run from analytical chemistry, which runs the whole procedure without a
@@ -83,13 +85,38 @@ your instructions, memories, plugins, hooks and skills. That is not enough.
83
85
 
84
86
  ### Room
85
87
 
86
- A room is a bubblewrap sandbox for one agent run: an empty `/home` with the room's own home
88
+ A backend builds the room, and every path the agent sees comes from the room it built: its
89
+ home, the project and a run's store. On Linux and WSL the backend is bubblewrap, which mounts
90
+ them at fixed paths. A backend that cannot mount, such as Seatbelt on macOS, uses the host paths
91
+ instead, and the adapters follow (the trust entry, the session directories, the config
92
+ variables).
93
+
94
+ A bubblewrap room is a sandbox for one agent run: an empty `/home` with the room's own home
87
95
  directory at `/home/tare`, a fresh copy of the login, the agent's executable, the project at
88
96
  `/work`, and the environment allowlist. `tare claude` and the other agent commands mount your
89
97
  project directly, so the agent changes your real files. Calibrate, Swap and Cliff give every run
90
98
  its own copy of the project. The room has network access and a working login. It keeps your setup out of the measurement. It does not protect your machine from a hostile
91
99
  agent.
92
100
 
101
+ A Seatbelt room (macOS) cannot mount anything. It is one directory under `/private/tmp` that
102
+ holds the room's home, an APFS clone of the project and clones of the agent's executable, placed
103
+ where bubblewrap would mount them (`opt/agent/...`). A clone shares the original's disk blocks,
104
+ so it costs next to nothing. The profile (`sandbox-exec`) denies your home directory, your
105
+ directory under `/var/folders` (other programs' temporary files and caches), the rest of
106
+ `/private/tmp` (where Claude Code keeps its temporary files), `/private/var/tmp`,
107
+ `/Users/Shared` and the pasteboard. Then it allows the room. When the room closes, the run's
108
+ changes to the clone are copied back to the project. A file the run did not touch stays as it
109
+ is. On macOS, Claude Code keeps its login in the Keychain. tare reads it there the way Claude
110
+ Code does and gives the room a copy as a file, which Claude Code uses when the room has no
111
+ Keychain item. No further Seatbelt profile can start inside a Seatbelt room, so the agents' own
112
+ sandboxes do not work there. Codex runs without its sandbox anyway, and tare turns Claude Code's
113
+ off in a macOS room, because a project that turns it on would make every Bash call fail there.
114
+ On macOS, `tar` stores extended attributes as `._` files, so the snapshot hook runs it with
115
+ `COPYFILE_DISABLE=1`. **Measured** on macOS 27.0 (Claude Code 2.1.289, Codex CLI 0.159.0,
116
+ Pi 0.99.1): each agent answered in such a room with its real login. Afterwards the room was
117
+ gone and the Keychain item unchanged, and nothing under the home directory was readable from
118
+ inside (epic #85).
119
+
93
120
  Claude Code may replace its login token when it renews it, which could log out your real
94
121
  session. This has not been tested, so tare refuses to start when the copy would need renewal
95
122
  within the next hour. **Measured:** the Antigravity CLI's Google login keeps its refresh token
@@ -121,7 +148,7 @@ lines of your own files that the probe looks for in a request.
121
148
  | Agent | How the room is set up | Way to the fake model server | Kinds of context the dirty twin showed on the measuring machine |
122
149
  |---|---|---|---|
123
150
  | Claude Code | `CLAUDE_CONFIG_DIR` and `HOME` in the room, a copy of `.credentials.json`, `--strict-mcp-config --setting-sources project,local` | `ANTHROPIC_BASE_URL` | instructions, home path, MCP servers, skills, plugins, email, reach |
124
- | Codex | `CODEX_HOME` and `HOME` in the room, a copy of `auth.json` only, `--disable remote_plugin` | `-c openai_base_url=<server>/v1` keeps the ChatGPT login | instructions, memories, home path, skills, reach |
151
+ | Codex | `CODEX_HOME` and `HOME` in the room, a copy of `auth.json` only, `--disable remote_plugin` | `-c openai_base_url=<server>/v1` keeps the ChatGPT login | instructions, memories, home path, skills, plugins, custom agents, reach |
125
152
  | Pi | `PI_CODING_AGENT_DIR` and `HOME` in the room, copies of `auth.json` and `models.json`, a minimal `settings.json`, no flags | a provider of its own (`tare`, api `anthropic-messages`), because Pi has no base-URL variable | instruction files above the project, extensions, home path, reach |
126
153
  | Antigravity CLI | `HOME` in the room, a copy of `antigravity-oauth-token` and the chosen model, no flags | `CLOUD_CODE_URL` | home path, reach. Global rules (`~/.gemini/GEMINI.md`) and global skills (`~/.gemini/config/skills/`) showed after copies were placed in the setup for the test, because the measuring machine had none. |
127
154
 
@@ -132,9 +159,16 @@ What each agent needed:
132
159
  of its built-in provider that would stop this. So each Codex probe waits about 8 seconds. **Measured:** the captured
133
160
  request holds more than `codex debug prompt-input` shows, because Codex's own view leaves out
134
161
  the memories. Memories load only when your `config.toml` enables `[features] memories`. Hooks
135
- did not fire in one-shot runs, and the first request offered no tools from configured MCP
136
- servers, so the probe cannot check those two kinds of context for Codex. The room has no
137
- `config.toml` or `hooks.json` of yours, so neither can come in. A fresh `CODEX_HOME` means
162
+ did not fire in one-shot runs, so the probe cannot check hooks for Codex. On the measuring
163
+ machine the first request offered no tools from configured MCP servers. On a Mac (Codex
164
+ 0.159.0) it offered one server's tools grouped as `mcp__<server>`, and the probe reads such a
165
+ group as that server's MCP tools (#109). The room has no `config.toml` or `hooks.json` of
166
+ yours, so neither can come in. Codex reports nothing like Claude Code's init event, so the
167
+ probe finds your plugins in the request's skills listing, where each plugin skill is a line
168
+ `- <plugin>:<skill>: …`. Your custom agents (`~/.codex/agents/`, a `.toml` file each) appear
169
+ as roles of the `spawn_agent` tool, `<name>: {` and the description. The probe matches the
170
+ name and the description's first line, so a built-in agent of the same name is not taken for
171
+ yours. A fresh `CODEX_HOME` means
138
172
  Codex's default model. Pass `-m` when you compare runs.
139
173
  - **Pi:** a fresh agent directory is clean on its own and keeps the project's `AGENTS.md`, so the
140
174
  room uses no flags. `--no-context-files` would drop the project's `AGENTS.md`. tare finds Pi's
@@ -190,7 +224,9 @@ read `tare: 0.00`, including a skill that started `claude -p` inside the room (s
190
224
 
191
225
  The judge scores results that no test can decide, such as a web page. tare opens the page in
192
226
  headless Google Chrome inside a room with the workspace read-only and no home directory, and
193
- takes a screenshot. A judge agent (Claude Code with Sonnet by default) then scores the page from
227
+ takes a screenshot. On macOS it uses Chrome, Chromium or Microsoft Edge from `/Applications`, in
228
+ a Seatbelt room with a browser profile of its own and a mock keychain. Edge keeps running after
229
+ its screenshot, so tare ends it once the file is written. A judge agent (Claude Code with Sonnet by default) then scores the page from
194
230
  the screenshot, the source and a rubric, in a fresh room without any agent or model names.
195
231
  **Measured:** one test page scored 55, 60, 58, 58, 66 and 62 in six judgements. `tare judge-noise`
196
232
  therefore refuses a threshold that lies inside a page's range of scores. On the same page the
@@ -249,7 +285,8 @@ names what has changed since.
249
285
 
250
286
  ## Measurements
251
287
 
252
- All measurements ran on WSL2 on 2026-10-04 and 2026-10-05.
288
+ All measurements ran on WSL2 on 2026-10-04 and 2026-10-05. The rows that name macOS ran on
289
+ macOS 27.0 (arm64) on 2026-10-05 and 2026-10-06.
253
290
 
254
291
  | What | Agent and version | Result | Source |
255
292
  |---|---|---|---|
@@ -262,6 +299,10 @@ All measurements ran on WSL2 on 2026-10-04 and 2026-10-05.
262
299
  | Real task 1: a data migration | Claude Code with Sonnet and Haiku, Codex | Deleting the source file after a wrong-encoding import was planned as the point of no return. Swap showed that the workspace could still be repaired. Haiku passed about 1 in 14 runs, Codex with gpt-6.1-sol 4 of 4 | [results](experiments/migration/RESULTS.md) |
263
300
  | Real task 2: a web page judged by Sonnet | Claude Code with Haiku, Codex with gpt-6.1-sol | Swap confirmed all four predictions of the plan written before the runs. Cliff called a failed run unlucky, and a later rescoring of the same page confirmed it | [results](experiments/html/RESULTS.md) |
264
301
  | Field test: two ideation skills compared | Claude Code 2.1.289 with Opus 5.5 | all 18 rooms read `tare: 0.00`, including a skill that started `claude -p` inside the room | not published |
302
+ | macOS: what a Seatbelt room has to handle | Claude Code 2.1.289, Codex CLI 0.159.0, Pi 0.99.1 | Claude Code's login is in the Keychain. With only the home directory denied, `/var/folders`, `/private/tmp` and the pasteboard stayed readable. Agents' own sandboxes fail inside a room (`sandbox_apply: Operation not permitted`) | comments on epic #85 |
303
+ | macOS: a Seatbelt room with the real login | same | each agent answered one prompt. The room and its login copy were removed afterwards, and the Keychain item was unchanged (digest before and after). `tare probe` read `tare: 0.00` for all three | comments on epic #85 |
304
+ | macOS: dirty twin, Seatbelt room, and parts of the real setup copied into the room | same | every room read `tare: 0.00`. Each copied part that reached the request was named with its source, and nothing under the home directory, under `/var/folders`, in `/private/tmp` or on the pasteboard was reachable from inside. Two kinds of Codex context are invisible to the probe on any system (#109) | [results](experiments/dirty-twin-macos/RESULTS.md) |
305
+ | macOS: scripted Cliff and Swap, the judge | Claude Code 2.1.290 against scripted models; Microsoft Edge for the judge's screenshot | Cliff found step 4. Swap passed the null check and found the blame passing from the model to the workspace between cut 0 and cut 0.5. `tare judge` rendered a page and scored it. With a project that turns Claude Code's sandbox on, every Bash call failed in a Seatbelt room until tare turned it off | comments on epic #85 |
265
306
 
266
307
  ## Open questions and ideas
267
308
 
@@ -274,8 +315,9 @@ All measurements ran on WSL2 on 2026-10-04 and 2026-10-05.
274
315
  - **Codex handoffs:** Codex can run several commands in one tool call, which makes its handoffs
275
316
  long.
276
317
  - **Cost:** every tail is a full agent run, so a Cliff search or a Swap costs as much as its tails.
277
- - **macOS:** a room built on Seatbelt (`sandbox-exec`), which Claude Code and Codex already use
278
- for their own sandboxes. In progress (epic #85), not in a release yet.
318
+ - **macOS:** the room is built on Seatbelt, released in 0.3.0 (epic #85). Not done: the
319
+ Antigravity CLI, whose dirty twin needs Linux's overlay, so tare stops with a message there.
320
+ Not tested: whether a program started through launchd or XPC runs outside the room's profile.
279
321
  - **Other probes, not built:** weighing the first request's input tokens before and after adding
280
322
  junk to the real setup, and file tripwires that report which secret files a run opened.
281
323
  - **Docker:** could contain files where bubblewrap is missing, but does nothing against context
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tare-cli
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Prove your coding agent starts without your personal setup before you measure it.
5
5
  Project-URL: Homepage, https://github.com/a-ratz/tare
6
6
  Project-URL: Repository, https://github.com/a-ratz/tare
@@ -13,6 +13,7 @@ Keywords: agent-evaluation,ai-agents,claude-code,codex,coding-agents,evals,llm-e
13
13
  Classifier: Development Status :: 3 - Alpha
14
14
  Classifier: Environment :: Console
15
15
  Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: MacOS
16
17
  Classifier: Operating System :: POSIX :: Linux
17
18
  Classifier: Programming Language :: Python :: 3
18
19
  Classifier: Programming Language :: Python :: 3.11
@@ -37,7 +38,7 @@ Description-Content-Type: text/markdown
37
38
  [![License: MIT][license-shield]][license-url]
38
39
  [![PyPI][version-shield]][version-url]
39
40
  [![Python 3.11+][python-shield]][python-url]
40
- [![Linux | WSL][platform-shield]][platform-url]
41
+ [![Linux | WSL | macOS][platform-shield]][platform-url]
41
42
  [![Claude Code | Codex | Pi | Antigravity][agent-shield]][agent-url]
42
43
 
43
44
  </div>
@@ -71,7 +72,7 @@ environment, and none of them checks it.
71
72
  tare starts the agent in a clean environment and proves that it is clean before every run. It
72
73
  works with four coding agents on Linux and WSL: Claude Code, Codex, Pi
73
74
  ([`@earendil-works/pi-coding-agent`](https://www.npmjs.com/package/@earendil-works/pi-coding-agent))
74
- and Google's Antigravity CLI (`agy`).
75
+ and Google's Antigravity CLI (`agy`). On macOS it works with Claude Code, Codex and Pi.
75
76
 
76
77
  ## The words tare uses
77
78
 
@@ -80,7 +81,7 @@ something. tare does the same for an agent before you measure it.
80
81
 
81
82
  | Word | Meaning |
82
83
  |---|---|
83
- | room | An isolated environment for one agent run. It has an empty home directory, a fresh copy of your login (the file in which the agent keeps your sign-in) and only four basic environment variables from your shell (an allowlist), plus its own `HOME` and `PATH`. Your project appears at `/work`. tare builds the room with [bubblewrap](https://github.com/containers/bubblewrap). |
84
+ | room | An isolated environment for one agent run. It has an empty home directory, a fresh copy of your login (the file in which the agent keeps your sign-in) and only four basic environment variables from your shell (an allowlist), plus its own `HOME` and `PATH`. Your project appears at `/work`. tare builds the room with [bubblewrap](https://github.com/containers/bubblewrap). On macOS it builds the room with Seatbelt, the sandbox built into macOS: the agent works on a copy of your project inside the room, and its changes are copied back to your project when the room closes. |
84
85
  | probe | The test that tare runs before an agent starts. It makes no paid model calls. The agent talks to a fake model server on your machine, which records what the agent sends. |
85
86
  | reading | The result of a probe, one of three. A clean room reads `tare: 0.00`, the zeroed scale. A room with leaks reads, for example, `tare: 4 leaks`. A probe that could not see your setup reads `tare: not proven (blind)`. There are no values in between. |
86
87
  | leak | Something of your personal setup that reaches the room, for example your global instructions. The reading names each leak and the file it came from. |
@@ -123,7 +124,7 @@ something. tare does the same for an agent before you measure it.
123
124
  | Use tare when | Look elsewhere when |
124
125
  |---|---|
125
126
  | you compare agent runs with and without a skill, plugin or prompt | you need protection from a hostile agent, because the room has network access and a working login |
126
- | you benchmark agents on your own machine | you are on macOS (a room is in progress, not released yet) |
127
+ | you benchmark agents on your own machine | you use the Antigravity CLI on macOS (not supported there yet) |
127
128
  | you want a fresh-machine run without a fresh machine | you log in with an API key only (untested) |
128
129
  | an agent failed a task and you want to know which step lost it | you want a single run explained without running the agent again, because Cliff runs it many times |
129
130
 
@@ -131,10 +132,10 @@ something. tare does the same for an agent before you measure it.
131
132
 
132
133
  | Requirement | Notes |
133
134
  |---|---|
134
- | Linux or WSL2 | tare builds the room with user namespaces |
135
- | [bubblewrap](https://github.com/containers/bubblewrap) | `sudo apt install bubblewrap` |
136
- | at least one of the four agents | on `PATH` and logged in. Codex must resolve to a path under `/usr` for now, because tare mounts Codex's npm package and Node.js from there. If `which codex` points elsewhere, for example into nvm, install it with the system's npm (`sudo npm install -g @openai/codex`) and put `/usr/bin` first on `PATH`. The other agents can live anywhere on `PATH`. |
137
- | Google Chrome, only for `tare judge` | `google-chrome` on `PATH`, installed under `/usr` or `/opt`. `tare judge` scores web pages and takes their screenshots with it. |
135
+ | Linux or WSL2, or macOS | on Linux tare builds the room with user namespaces, on macOS with Seatbelt (`sandbox-exec`, part of macOS). On macOS the project must be on an APFS volume for the room's copy to be instant; elsewhere tare copies it in full. |
136
+ | [bubblewrap](https://github.com/containers/bubblewrap), Linux and WSL only | `sudo apt install bubblewrap` |
137
+ | at least one of the agents | on `PATH` and logged in. On Linux, Codex must resolve to a path under `/usr` for now, because tare mounts Codex's npm package and Node.js from there. If `which codex` points elsewhere, for example into nvm, install it with the system's npm (`sudo npm install -g @openai/codex`) and put `/usr/bin` first on `PATH`. The other agents, and every agent on macOS, can live anywhere on `PATH`. On macOS, Claude Code keeps its login in the Keychain: tare reads it there the way Claude Code does and gives the room a copy. |
138
+ | a Chromium browser, only for `tare judge` | Linux: `google-chrome` on `PATH`, installed under `/usr` or `/opt`. macOS: Google Chrome, Chromium or Microsoft Edge in `/Applications`. `tare judge` scores web pages and takes their screenshots with it. |
138
139
  | [uv](https://docs.astral.sh/uv/) or [pipx](https://pipx.pypa.io/) | Python 3.11 or newer |
139
140
 
140
141
  Install tare from PyPI. The package is `tare-cli`, the command is `tare`:
@@ -472,12 +473,12 @@ real setup, so tare knows that it can see your setup at all. Third, a plain scri
472
473
  room looks for your files and for secret environment variables.
473
474
 
474
475
  → [Concept: the design and the measurements behind it](https://github.com/a-ratz/tare/blob/main/CONCEPT.md)
475
- → [The dirty-twin experiments that decided the probe design: Claude Code](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin/RESULTS.md), [Codex](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-codex/RESULTS.md), [Pi](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-pi/RESULTS.md), [Antigravity CLI](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-agy/RESULTS.md)
476
+ → [The dirty-twin experiments that decided the probe design: Claude Code](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin/RESULTS.md), [Codex](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-codex/RESULTS.md), [Pi](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-pi/RESULTS.md), [Antigravity CLI](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-agy/RESULTS.md), [the three agents on macOS](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-macos/RESULTS.md)
476
477
 
477
478
  ## Roadmap
478
479
 
479
- A room for macOS, built on Seatbelt, is in progress ([epic #85](https://github.com/a-ratz/tare/issues/85)).
480
- Until it is released, tare runs on Linux and WSL.
480
+ The Antigravity CLI on macOS: its dirty twin uses an overlay over `~/.gemini`, which macOS does not
481
+ have, so tare stops with a message there for now.
481
482
 
482
483
  ## Contributing
483
484
 
@@ -499,7 +500,7 @@ Crafted with [Readme Craft](https://github.com/motiful/readme-craft)
499
500
  [version-url]: https://github.com/a-ratz/tare/blob/main/pyproject.toml
500
501
  [python-shield]: https://img.shields.io/badge/python-3.11%2B-3776AB.svg
501
502
  [python-url]: https://www.python.org/
502
- [platform-shield]: https://img.shields.io/badge/platform-Linux%20%7C%20WSL-555555.svg
503
+ [platform-shield]: https://img.shields.io/badge/platform-Linux%20%7C%20WSL%20%7C%20macOS-555555.svg
503
504
  [platform-url]: #install
504
505
  [agent-shield]: https://img.shields.io/badge/agents-Claude%20Code%20%7C%20Codex%20%7C%20Pi%20%7C%20Antigravity-D97757.svg
505
506
  [agent-url]: #install
@@ -13,7 +13,7 @@
13
13
  [![License: MIT][license-shield]][license-url]
14
14
  [![PyPI][version-shield]][version-url]
15
15
  [![Python 3.11+][python-shield]][python-url]
16
- [![Linux | WSL][platform-shield]][platform-url]
16
+ [![Linux | WSL | macOS][platform-shield]][platform-url]
17
17
  [![Claude Code | Codex | Pi | Antigravity][agent-shield]][agent-url]
18
18
 
19
19
  </div>
@@ -47,7 +47,7 @@ environment, and none of them checks it.
47
47
  tare starts the agent in a clean environment and proves that it is clean before every run. It
48
48
  works with four coding agents on Linux and WSL: Claude Code, Codex, Pi
49
49
  ([`@earendil-works/pi-coding-agent`](https://www.npmjs.com/package/@earendil-works/pi-coding-agent))
50
- and Google's Antigravity CLI (`agy`).
50
+ and Google's Antigravity CLI (`agy`). On macOS it works with Claude Code, Codex and Pi.
51
51
 
52
52
  ## The words tare uses
53
53
 
@@ -56,7 +56,7 @@ something. tare does the same for an agent before you measure it.
56
56
 
57
57
  | Word | Meaning |
58
58
  |---|---|
59
- | room | An isolated environment for one agent run. It has an empty home directory, a fresh copy of your login (the file in which the agent keeps your sign-in) and only four basic environment variables from your shell (an allowlist), plus its own `HOME` and `PATH`. Your project appears at `/work`. tare builds the room with [bubblewrap](https://github.com/containers/bubblewrap). |
59
+ | room | An isolated environment for one agent run. It has an empty home directory, a fresh copy of your login (the file in which the agent keeps your sign-in) and only four basic environment variables from your shell (an allowlist), plus its own `HOME` and `PATH`. Your project appears at `/work`. tare builds the room with [bubblewrap](https://github.com/containers/bubblewrap). On macOS it builds the room with Seatbelt, the sandbox built into macOS: the agent works on a copy of your project inside the room, and its changes are copied back to your project when the room closes. |
60
60
  | probe | The test that tare runs before an agent starts. It makes no paid model calls. The agent talks to a fake model server on your machine, which records what the agent sends. |
61
61
  | reading | The result of a probe, one of three. A clean room reads `tare: 0.00`, the zeroed scale. A room with leaks reads, for example, `tare: 4 leaks`. A probe that could not see your setup reads `tare: not proven (blind)`. There are no values in between. |
62
62
  | leak | Something of your personal setup that reaches the room, for example your global instructions. The reading names each leak and the file it came from. |
@@ -99,7 +99,7 @@ something. tare does the same for an agent before you measure it.
99
99
  | Use tare when | Look elsewhere when |
100
100
  |---|---|
101
101
  | you compare agent runs with and without a skill, plugin or prompt | you need protection from a hostile agent, because the room has network access and a working login |
102
- | you benchmark agents on your own machine | you are on macOS (a room is in progress, not released yet) |
102
+ | you benchmark agents on your own machine | you use the Antigravity CLI on macOS (not supported there yet) |
103
103
  | you want a fresh-machine run without a fresh machine | you log in with an API key only (untested) |
104
104
  | an agent failed a task and you want to know which step lost it | you want a single run explained without running the agent again, because Cliff runs it many times |
105
105
 
@@ -107,10 +107,10 @@ something. tare does the same for an agent before you measure it.
107
107
 
108
108
  | Requirement | Notes |
109
109
  |---|---|
110
- | Linux or WSL2 | tare builds the room with user namespaces |
111
- | [bubblewrap](https://github.com/containers/bubblewrap) | `sudo apt install bubblewrap` |
112
- | at least one of the four agents | on `PATH` and logged in. Codex must resolve to a path under `/usr` for now, because tare mounts Codex's npm package and Node.js from there. If `which codex` points elsewhere, for example into nvm, install it with the system's npm (`sudo npm install -g @openai/codex`) and put `/usr/bin` first on `PATH`. The other agents can live anywhere on `PATH`. |
113
- | Google Chrome, only for `tare judge` | `google-chrome` on `PATH`, installed under `/usr` or `/opt`. `tare judge` scores web pages and takes their screenshots with it. |
110
+ | Linux or WSL2, or macOS | on Linux tare builds the room with user namespaces, on macOS with Seatbelt (`sandbox-exec`, part of macOS). On macOS the project must be on an APFS volume for the room's copy to be instant; elsewhere tare copies it in full. |
111
+ | [bubblewrap](https://github.com/containers/bubblewrap), Linux and WSL only | `sudo apt install bubblewrap` |
112
+ | at least one of the agents | on `PATH` and logged in. On Linux, Codex must resolve to a path under `/usr` for now, because tare mounts Codex's npm package and Node.js from there. If `which codex` points elsewhere, for example into nvm, install it with the system's npm (`sudo npm install -g @openai/codex`) and put `/usr/bin` first on `PATH`. The other agents, and every agent on macOS, can live anywhere on `PATH`. On macOS, Claude Code keeps its login in the Keychain: tare reads it there the way Claude Code does and gives the room a copy. |
113
+ | a Chromium browser, only for `tare judge` | Linux: `google-chrome` on `PATH`, installed under `/usr` or `/opt`. macOS: Google Chrome, Chromium or Microsoft Edge in `/Applications`. `tare judge` scores web pages and takes their screenshots with it. |
114
114
  | [uv](https://docs.astral.sh/uv/) or [pipx](https://pipx.pypa.io/) | Python 3.11 or newer |
115
115
 
116
116
  Install tare from PyPI. The package is `tare-cli`, the command is `tare`:
@@ -448,12 +448,12 @@ real setup, so tare knows that it can see your setup at all. Third, a plain scri
448
448
  room looks for your files and for secret environment variables.
449
449
 
450
450
  → [Concept: the design and the measurements behind it](https://github.com/a-ratz/tare/blob/main/CONCEPT.md)
451
- → [The dirty-twin experiments that decided the probe design: Claude Code](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin/RESULTS.md), [Codex](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-codex/RESULTS.md), [Pi](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-pi/RESULTS.md), [Antigravity CLI](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-agy/RESULTS.md)
451
+ → [The dirty-twin experiments that decided the probe design: Claude Code](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin/RESULTS.md), [Codex](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-codex/RESULTS.md), [Pi](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-pi/RESULTS.md), [Antigravity CLI](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-agy/RESULTS.md), [the three agents on macOS](https://github.com/a-ratz/tare/blob/main/experiments/dirty-twin-macos/RESULTS.md)
452
452
 
453
453
  ## Roadmap
454
454
 
455
- A room for macOS, built on Seatbelt, is in progress ([epic #85](https://github.com/a-ratz/tare/issues/85)).
456
- Until it is released, tare runs on Linux and WSL.
455
+ The Antigravity CLI on macOS: its dirty twin uses an overlay over `~/.gemini`, which macOS does not
456
+ have, so tare stops with a message there for now.
457
457
 
458
458
  ## Contributing
459
459
 
@@ -475,7 +475,7 @@ Crafted with [Readme Craft](https://github.com/motiful/readme-craft)
475
475
  [version-url]: https://github.com/a-ratz/tare/blob/main/pyproject.toml
476
476
  [python-shield]: https://img.shields.io/badge/python-3.11%2B-3776AB.svg
477
477
  [python-url]: https://www.python.org/
478
- [platform-shield]: https://img.shields.io/badge/platform-Linux%20%7C%20WSL-555555.svg
478
+ [platform-shield]: https://img.shields.io/badge/platform-Linux%20%7C%20WSL%20%7C%20macOS-555555.svg
479
479
  [platform-url]: #install
480
480
  [agent-shield]: https://img.shields.io/badge/agents-Claude%20Code%20%7C%20Codex%20%7C%20Pi%20%7C%20Antigravity-D97757.svg
481
481
  [agent-url]: #install
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tare-cli"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  description = "Prove your coding agent starts without your personal setup before you measure it."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -13,6 +13,7 @@ classifiers = [
13
13
  "Development Status :: 3 - Alpha",
14
14
  "Environment :: Console",
15
15
  "Intended Audience :: Developers",
16
+ "Operating System :: MacOS",
16
17
  "Operating System :: POSIX :: Linux",
17
18
  "Programming Language :: Python :: 3",
18
19
  "Programming Language :: Python :: 3.11",