# relay-baton v0.3.0 Release Notes Date: 2026-05-28 ## Summary v0.3.0 is a stability / safety pass on the v0.2 project layer. It fixes correctness issues found while reviewing v0.2 and lays groundwork for non-destructive registry handling. Highlights: - `ProjectResolver.resolve()` no longer writes to the registry as a side effect. - The registry recovers from invalid `projects.json` instead of crashing. - `RELAY_BATON_PROJECTS_FILE` overrides the registry path (testing / sandboxing). - `run` no longer leaves stale `lastError` after a clean Codex run that triggered a fallback. ## Patches ### Resolver is now side-effect free Before v0.3, every `--project` or active-project resolve also called `updateLastUsedAt`, which wrote `projects.json`. Read-only commands (`status`, `budget`, `doctor`) were doing disk writes on the user's registry. Change: - `ProjectResolver.resolve()` is pure: it only computes the repo root. - A new `ProjectResolver.touch(result)` updates `lastUsedAt` explicitly. - `resolveProjectContext(opts, touch = true)` in the CLI calls `touch()` only for commands that actually run an agent (`run`, `handoff`). - `status`, `budget`, `doctor`, `init`, `compact`, `squeeze`, `tui` no longer write the registry on resolution. ### Registry recovery If `projects.json` is unreadable or has the wrong shape, v0.2 threw `ProjectRegistryError` and the CLI exited. In v0.3: - The original file is backed up to `.corrupt-.bak`. - A fresh empty registry is written. - A `[relay-baton]` warning is printed. - The command continues instead of aborting. This applies to both JSON parse errors and shape validation failures. ### `RELAY_BATON_PROJECTS_FILE` env override `ProjectRegistry.defaultPath()` now honors `RELAY_BATON_PROJECTS_FILE` when set. This is the supported way to: - run smoke tests against an isolated registry, - sandbox `relay-baton` in CI, - point at a project registry shared between machines without touching `~/.relay-baton`. Empty values are ignored; default location is unchanged otherwise (`~/.relay-baton/projects.json`). ### `run` clears `lastError` on fallback In v0.2, when Codex emitted a fallback pattern but still exited non-zero, `meta.lastError` was set to `codex exited with `. The next agent run inherited that stale error. In v0.3, `lastError` is cleared to `null` whenever a fallback is detected, even if Codex exited non-zero. This matches the meaning of `status: "fallback_detected"`. ## Compatibility Notes - Public CLI surface is unchanged. All v0.2 commands and flags continue to work. - `ProjectResolver.resolve()` lost its side effect; consumers that relied on it for `lastUsedAt` updates must call `touch()` (only `run` and `handoff` did, both already updated). - `ProjectRegistry.read()` no longer throws on corrupt files; callers that caught `ProjectRegistryError` will simply see an empty registry and a stderr warning. - `RELAY_BATON_PROJECTS_FILE` is honored if set; unset behavior is identical to v0.2. ## Recommended Next Work - CLI-level smoke test that spawns the built binary against `RELAY_BATON_PROJECTS_FILE`. - TUI fuzzy project switcher. - Per-project session list and recent handoff history. - TUI command palette. - Additional agent adapter scaffolding (`opencode`, `gemini`, `aider`).