Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ Never report a step as done before `mise run check` has passed, and say which ga
- **No model calls in tests.** Unit and integration tests run a stand-in `copilot` (a script on `PATH`, such as `testdata/copilot`, which plays each role in the walking skeleton), never the real one.
- **tmux on its own socket.** Every tmux server that a test, a smoke run or a live check starts gets a config file of its own (`-f /dev/null`, or a file of the test's), so the user's `~/.tmux.conf` and its plugins never load, and runs without `TMUX` and `TMUX_PANE` (`env -u TMUX -u TMUX_PANE`), so a slip can't reach the user's server. Tests run tmux with `-L baton-test-<n>`, in a `TMUX_TMPDIR` and a `HOME` of their own, and kill only their servers. The walking skeleton (`TestSkeleton`) runs baton itself, so it runs tmux as a smoke run does, on the default socket, in a `TMUX_TMPDIR` (`/tmp/baton-test-skeleton-*`) and a `HOME` of its own. Smoke runs and live checks use a socket of their own, such as `-L baton-live`. baton itself uses tmux's default socket, or `TMUX`'s, so a smoke run of the binary gets a `TMUX_TMPDIR` and a `HOME` of its own, and starts that server first, with `-f /dev/null`; it never runs on the user's default server without the user's OK. The tmux tests' `TestMain` ends every server whose socket is in its `TMUX_TMPDIR`, even one that a test started on another socket, before it removes that folder. A test run that fails or stops early skips its cleanup, so after one, look for leftover servers (`pgrep -lf '^tmux .*(baton-test-|/Test)'`: a test's server shows its `-L baton-test-<n>`, or its config file in the test's temporary folder), end each on its own socket, or with `kill <pid>` once its socket is gone, and remove leftover `baton-tmux-<n>` folders in `/tmp` and `baton-test-skeleton-*` folders in `/tmp` and `$TMPDIR`. Never kill the user's tmux server, and never touch a session or window that baton didn't create.
- **git without the user's config.** Tests run git, theirs and baton's, with `GIT_CONFIG_GLOBAL=/dev/null`, `GIT_CONFIG_NOSYSTEM=1`, `LC_ALL=C` and a test author, and without the `GIT_*` variables that a hook of git's sets, such as `GIT_DIR`: each package's `TestMain` sets them. A test spells out git's arguments, so it runs no command that it doesn't show.
- **baton without the user's config.** Tests run baton with a `HOME` of their own and without `XDG_CONFIG_HOME` and `XDG_CACHE_HOME`, so it never reads the user's config file or writes in their cache: the cli tests' `TestMain` gives them an empty `HOME`, a `baton-test-home-*` folder in `$TMPDIR`, and a test that writes there, such as a config file, gives itself one of its own first. `TestMain` fails the run if a test left files in that folder. A run that stops early, such as one whose test panics, leaves the folder, so remove it after one.
- **Copilot CLI config stays untouched.** Develop with `--plugin-dir`, flags or a throwaway `COPILOT_HOME`, never by changing `~/.copilot/`.
- **Live checks are rare and small**: a throwaway repo in the spikes folder, the cheapest model that can do the job, and a `--max-ai-credits` cap (30 at least, the CLI's minimum). The PR records each live check's cost. On a Mac, a live check's `copilot` keeps the user's `HOME`, whose login keychain holds its login, and gets a throwaway `COPILOT_HOME`; its tmux server and the commands sent to it run in a clean environment (`env -i`), since a pane takes the `PATH` of the client that makes it (fact 30 in the plan).

Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,22 @@ You talk to one agent, the orchestrator. It reads the ticket, plans the work as
- macOS, git 2.36 or newer, and tmux 3.5 or newer.
- [GitHub Copilot CLI](https://docs.github.com/copilot/how-tos/copilot-cli).
- `gh` for GitHub pull requests; a Bitbucket Cloud API token for Bitbucket pull requests; a read-only Jira API token for Jira tickets.

## Config

baton needs no config file. To change its settings, write `config.json` in `~/Library/Application Support/baton/`. Its top holds the settings for every repo, and `repos` a section per repo, keyed by the host and path of the repo's `origin` remote, such as `github.com/codedogapp/baton` for `git@github.com:codedogapp/baton.git`. A repo's section lies over the top, one setting at a time.

- `roles`: a section per role, `orchestrator`, `lead`, `engineer`, `qa` or `reviewer`, with its `model` (by default `claude-opus-5.5`), its `reasoningEffort` (`none`, `minimal`, `low`, `medium`, `high`, `xhigh` or `max`; by default copilot's own), its `maxAiCredits` (30 or more; by default no cap) and, for the orchestrator alone, its `extraTools`: your own tools that it may use, by default nudge's `nudge-ask` and `nudge-report`.
- `roundCap`: the engineer's passes per brief before the orchestrator asks you (by default 3).
- `branchPrefix`: what goes before a feature's slug in its branch's name (by default `feature/`).

For example, QA on another model with a cap, for every repo, and two rounds with another branch prefix for one repo:

```json
{
"roles": {"qa": {"model": "claude-sonnet-5.5", "maxAiCredits": 60}},
"repos": {"github.com/codedogapp/baton": {"roundCap": 2, "branchPrefix": "feat/"}}
}
```

baton checks the whole file each time it reads it, and refuses a file with a mistake, naming the file and the mistake.
Loading
Loading