brindle 0.3.2__tar.gz → 0.3.4__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 (129) hide show
  1. {brindle-0.3.2 → brindle-0.3.4}/.gitignore +2 -0
  2. {brindle-0.3.2 → brindle-0.3.4}/PKG-INFO +122 -9
  3. {brindle-0.3.2 → brindle-0.3.4}/README.md +121 -8
  4. {brindle-0.3.2 → brindle-0.3.4}/pyproject.toml +1 -1
  5. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/agents.py +421 -64
  6. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/antigravity.py +8 -1
  7. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/autopilot.py +337 -41
  8. brindle-0.3.4/src/brindle/builtin_agents/reviewer-codex.md +30 -0
  9. brindle-0.3.4/src/brindle/builtin_agents/reviewer-gemini.md +30 -0
  10. brindle-0.3.4/src/brindle/builtin_agents/reviewer-local.md +29 -0
  11. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/reviewer.md +8 -3
  12. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/supervisor.md +6 -4
  13. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/ci_client.py +2 -3
  14. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/cli.py +146 -20
  15. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/codex_hook.py +22 -4
  16. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/config.py +7 -2
  17. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/db.py +76 -3
  18. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/doctor.py +24 -0
  19. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/git.py +68 -4
  20. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/keystore.py +14 -3
  21. brindle-0.3.4/src/brindle/launch_health.py +379 -0
  22. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/mcp_server.py +37 -9
  23. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/model_access.py +34 -11
  24. brindle-0.3.4/src/brindle/model_availability.py +507 -0
  25. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/native/runner.py +2 -0
  26. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pane_auth.py +5 -4
  27. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/permissions.py +7 -5
  28. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pipeline.py +35 -4
  29. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/providers.py +140 -16
  30. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/sessions.py +2 -2
  31. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/tasks.py +25 -1
  32. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/tmux.py +26 -1
  33. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/view.py +2 -0
  34. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/watch.py +4 -0
  35. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/workspaces.py +70 -3
  36. brindle-0.3.2/src/brindle/builtin_agents/reviewer-codex.md +0 -23
  37. brindle-0.3.2/src/brindle/builtin_agents/reviewer-local.md +0 -25
  38. {brindle-0.3.2 → brindle-0.3.4}/LICENSE +0 -0
  39. {brindle-0.3.2 → brindle-0.3.4}/SCHEDULE-A +0 -0
  40. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/__init__.py +0 -0
  41. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/__main__.py +0 -0
  42. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/account.py +0 -0
  43. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/airgap.py +0 -0
  44. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/budget.py +0 -0
  45. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/__init__.py +0 -0
  46. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/backend-auditor.md +0 -0
  47. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/developer-codex.md +0 -0
  48. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/developer-heavy.md +0 -0
  49. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/developer-light.md +0 -0
  50. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/developer-local.md +0 -0
  51. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/developer.md +0 -0
  52. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/qa.md +0 -0
  53. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_agents/subagent.md +0 -0
  54. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_rules/__init__.py +0 -0
  55. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_rules/security/backend.md +0 -0
  56. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_rules/style/minimal-diff.md +0 -0
  57. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/builtin_rules/tests/only.md +0 -0
  58. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/ci_adapters.py +0 -0
  59. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/ci_federation.py +0 -0
  60. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/ci_hosts.py +0 -0
  61. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/codemap.py +0 -0
  62. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/company_identity.py +0 -0
  63. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/company_login.py +0 -0
  64. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/conflicts.py +0 -0
  65. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/cost.py +0 -0
  66. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/cost_estimate.py +0 -0
  67. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/cull.py +0 -0
  68. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/demo.py +0 -0
  69. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/detect.py +0 -0
  70. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/escapes.py +0 -0
  71. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/events.py +0 -0
  72. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/gates.py +0 -0
  73. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/guardrails.py +0 -0
  74. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/history.py +0 -0
  75. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/identity_lock.py +0 -0
  76. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/inbox.py +0 -0
  77. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/keys_cmds.py +0 -0
  78. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/learned_rules.py +0 -0
  79. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/learning.py +0 -0
  80. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/legacy.py +0 -0
  81. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/native/__init__.py +0 -0
  82. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/native/client.py +0 -0
  83. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/native/loop.py +0 -0
  84. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/native/permissions.py +0 -0
  85. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/native/serve.py +0 -0
  86. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/native/tools.py +0 -0
  87. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/plugins.py +0 -0
  88. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/policy.py +0 -0
  89. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pool.py +0 -0
  90. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pricing.py +0 -0
  91. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/__init__.py +0 -0
  92. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/_files.py +0 -0
  93. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/account.py +0 -0
  94. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/audit_chain.py +0 -0
  95. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/audit_export.py +0 -0
  96. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/auth.py +0 -0
  97. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/brief_learning.py +0 -0
  98. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/cost_centers.py +0 -0
  99. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/credentials.py +0 -0
  100. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/features.py +0 -0
  101. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/keys.py +0 -0
  102. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/learning.py +0 -0
  103. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/license.py +0 -0
  104. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/loopback.py +0 -0
  105. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/managed_models.py +0 -0
  106. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/org_profiles.py +0 -0
  107. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/orgkey.py +0 -0
  108. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/rollout.py +0 -0
  109. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/settings_sync.py +0 -0
  110. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/status.py +0 -0
  111. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/team_events.py +0 -0
  112. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/pro/team_policy.py +0 -0
  113. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/procs.py +0 -0
  114. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/profiles.py +0 -0
  115. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/quota.py +0 -0
  116. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/repo_cmds.py +0 -0
  117. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/repos.py +0 -0
  118. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/rewind.py +0 -0
  119. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/rule_checks.py +0 -0
  120. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/savings.py +0 -0
  121. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/scratch.py +0 -0
  122. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/secrets.py +0 -0
  123. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/self_serve.py +0 -0
  124. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/services.py +0 -0
  125. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/sidebar_follow.py +0 -0
  126. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/signin_pause.py +0 -0
  127. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/status_cache.py +0 -0
  128. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/usage.py +0 -0
  129. {brindle-0.3.2 → brindle-0.3.4}/src/brindle/watch_costs.py +0 -0
@@ -10,3 +10,5 @@ graphify-out/
10
10
  docs/
11
11
  .brindle/config.local.json
12
12
  .brindle/bench.json
13
+ .DS_Store
14
+ commit_msg.txt
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brindle
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Run coding agents in parallel, each on its own git branch, with a supervisor that delegates, reviews, and merges
5
5
  Project-URL: Homepage, https://pawdelta.com/brindle/
6
6
  Project-URL: Documentation, https://pawdelta.com/brindle/docs/recommended-use
@@ -89,7 +89,7 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
89
89
  brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
90
90
 
91
91
  `brindle --version` prints the installed version; the tmux status bar of every brindle
92
- session shows it too (`brindle 0.3.2`). A session started before an upgrade keeps
92
+ session shows it too (`brindle 0.3.4`). A session started before an upgrade keeps
93
93
  running the old code, and shows the old number, until you restart it.
94
94
 
95
95
  ## Quick start
@@ -118,11 +118,11 @@ a writable home) and, in a repo, what's set up for it, and says what to do about
118
118
  anything missing. Bare `brindle` runs the essential ones (tmux, the chat's CLI)
119
119
  before it launches anything.
120
120
 
121
- That's it. `brindle` opens a supervisor chat (Claude Code) in your repo, with a
122
- narrow sidebar on the left showing every agent: who's working, who's idle, and
123
- who's waiting for your approval. Tell the supervisor what you want. It splits the
124
- work between workers, each on its own branch, then reviews and merges their
125
- branches. It starts in under a second.
121
+ That's it. Bare `brindle` opens a fresh interactive developer chat (Claude Code)
122
+ in your repo (with autopilot off), with a narrow sidebar on the left showing every
123
+ agent: who's working, who's idle, and who's waiting for your approval. To launch an
124
+ autopilot supervisor that splits work between parallel workers, reviews, and merges
125
+ their branches toward verified milestones, run `brindle supervisor`. It starts in under a second.
126
126
 
127
127
  **The sidebar follows you.** There's one sidebar pane per session root, not
128
128
  one per window: switch to any other brindle window or session (⏎ in the
@@ -242,7 +242,7 @@ for mechanical edits and a Codex reviewer for a second model's opinion.
242
242
 
243
243
  ## Autopilot
244
244
 
245
- `brindle` starts the supervisor with autopilot on. Tell it what we're building,
245
+ `brindle supervisor` starts the supervisor with autopilot on (or `brindle start -a supervisor`). Tell it what we're building,
246
246
  or put the goal in `.brindle/goals.md`, and it works like a project manager:
247
247
 
248
248
  1. **Goal and milestones.** The goal is split into milestones, and each one has
@@ -337,7 +337,8 @@ your own status line prints, so what you see doesn't change.
337
337
 
338
338
  | | |
339
339
  |---|---|
340
- | `brindle` | a fresh supervisor chat here, dashboard alongside |
340
+ | `brindle` | a fresh developer chat here (autopilot off), dashboard alongside |
341
+ | `brindle supervisor` | start an explicit autopilot supervisor chat to coordinate parallel workers and milestones |
341
342
  | `brindle init` | detect setup and test commands, write `.brindle/config.json`, check tools |
342
343
  | `brindle demo [--local]` | watch brindle finish a tiny practice repo: parallel workers, reviews, gated merges |
343
344
  | `brindle new BRANCH [-b BASE] [-a PROFILE] [-p PROMPT]` | worktree + branch + agent |
@@ -346,6 +347,7 @@ your own status line prints, so what you see doesn't change.
346
347
  | `brindle start [-a PROFILE] [-p PROMPT] [--no-watch] [--no-autopilot] [-b BRANCH] [-w PATH]` | the same, with options; `-b`/`-w` run it in that branch's worktree (created if needed, or the one you already made), which gets the repo's `.brindle` config |
347
348
  | `brindle handover --to BRANCH\|PATH [-n NOTE]` | hand the session to a new supervisor there: goal and milestones, workers, queued tasks and your note move across; the old one is paused |
348
349
  | `brindle autopilot [on\|off\|check]` | the goal's progress; turn autopilot on or off; run the checks now |
350
+ | `brindle audit-decision FINDING [-r RATIONALE]` | record acceptance of an audit finding with rationale so subsequent audits respect it |
349
351
  | `brindle delegation [conservative\|balanced\|fast]` | how readily the supervisor delegates: fewest tokens, the default, or quickest |
350
352
  | `brindle transfer [REPO] [--from SESSION] [-b BRANCH]` | move a scratch session's work into a real repo |
351
353
  | `brindle ls [--all]` | workspaces and agents |
@@ -407,6 +409,7 @@ knowing them helps when you tell the supervisor how to work.
407
409
  | `approve_plan` | supervisor | approve a worker's plan, or send it back with feedback (`approved=false`) |
408
410
  | `complete_subagent` | supervisor | record the result of a `subagent`-profile task |
409
411
  | `set_goal` / `get_progress` / `check_milestone` | supervisor | autopilot's goal, its progress, and running the checks |
412
+ | `record_audit_decision` | supervisor | record acceptance of an audit finding with rationale so subsequent audits respect it |
410
413
  | `need_user` | supervisor | stop autopilot and ask you a question |
411
414
  | `transfer_to_repo` | supervisor | move a scratch session's work into a repository |
412
415
  | `handover` | supervisor | hand the session to a new supervisor on another branch or worktree, with a note |
@@ -603,6 +606,21 @@ keep theirs):
603
606
  No tier prefers another provider for its own sake: all-Claude or all-Codex may
604
607
  be what works best for you, and that's for the learner to find out.
605
608
 
609
+ Routing merges per tier in this order: built-in defaults, `~/.brindle/config.json`
610
+ (every repo), the repo's `.brindle/config.json` (shared with the team), then
611
+ `.brindle/config.local.json` (your override for that repo). Each tier you set
612
+ replaces that tier's list; tiers you leave out keep the list from the layer below.
613
+ For example, put this in `~/.brindle/config.json` to use Codex for light tasks
614
+ in every repo, or in a repo's `.brindle/config.local.json` to do so only there:
615
+
616
+ ```json
617
+ {"routing": {"light": ["developer-codex", "developer"]}}
618
+ ```
619
+
620
+ Run `brindle doctor` in the repo to see the effective list and source file for
621
+ each tier. Unknown profile names are skipped; doctor and the routing reply
622
+ report the missing name and the file that supplied it.
623
+
606
624
  `developer-light` runs on Claude Haiku at low effort; `developer` on your Claude
607
625
  Code default model; `developer-codex` on Codex; `developer-heavy` on Claude Fable
608
626
  at high effort.
@@ -613,6 +631,53 @@ wins. When every candidate is out, the repo's `default_agent` runs. The reply sa
613
631
  what was picked and why, e.g. `weight medium -> developer (Codex at 93%, skipped developer-codex)`.
614
632
  An `agent_profile` you pass, or a milestone's `profile`, always wins over weight.
615
633
 
634
+ Provisioning and routing also check explicit models against supported read-only
635
+ catalogs, with the managed model, stored keys and the worker's filtered pane
636
+ environment. A model absent from a complete authoritative catalog is rejected
637
+ before creating a worker worktree or process. Partial catalogs, aliases,
638
+ discovery errors and Codex's bundled catalogs remain unverified; being listed
639
+ does not prove generation access, billing or quota. Catalog discovery is bounded
640
+ to five seconds for standalone checks; foreground routing/provisioning limits
641
+ discovery to one second, leaving room in the three-second launch budget for
642
+ provisioning and bounded helper cleanup. A timeout remains unverified and
643
+ launch proceeds with the real-task health follow-up. Catalogs are cached
644
+ briefly by environment and local account/config
645
+ revision where available. Routing and provisioning reuse equivalent catalog
646
+ context across worktrees, excluding launch bookkeeping while retaining project
647
+ settings, credentials and account selectors. CLI catalogs remain advisory;
648
+ unobservable external/keychain login changes may remain cached for 30 seconds.
649
+ Custom CLI binaries retain separate cache entries per working directory.
650
+ Claude, Bedrock, Vertex and Foundry have no catalog
651
+ probe here; no Claude CLI or synthetic generation request is used for these checks.
652
+
653
+ The real first worker task is sent once. A detached helper inspects its current
654
+ pane five seconds after delivery, so readiness delays do not use up that window.
655
+ Fresh interactive Antigravity chats without a task still receive their profile,
656
+ tool instructions and any autopilot context once after readiness; worker
657
+ instructions accompany the actual task in the same delivery.
658
+ For argv tasks, the timer starts at prompt submission or observed CLI readiness,
659
+ after trust/startup handling; readiness timeout does not establish delivery or
660
+ release queued work. Paused workers, replaced launches and reassigned panes are
661
+ excluded from the observation.
662
+ Explicit fresh authentication, access, model and capacity errors, or an early
663
+ exit, produce a safe supervisor message with a next step. A live process with
664
+ no clear result is recorded as pending for diagnostics, not verified model
665
+ access; it does not send a supervisor notification. Refusals use
666
+ the existing one-hour cooldown, scoped to the effective model/environment and
667
+ local login revision. Changing the account, model or credentials invalidates
668
+ that refusal; CLI/keychain account changes without an observable local revision
669
+ may require waiting for the cooldown or clearing `model_refusals.json`.
670
+ This is a single observation, not an entitlement test: unfamiliar error layouts,
671
+ later failures and slow requests can remain pending. It does not scan
672
+ historical transcripts, and excludes task text, previous pane lines and older
673
+ marked replies, fenced examples and ordinary assistant/tool explanations. A
674
+ Codex follow-up retains the complete submitted argv prompt (including profile
675
+ instructions) and distinguishes assistant/tool responses from the idle composer.
676
+ Quoted prompts and error-looking markers inside assistant/tool output remain
677
+ ambiguous and pending; markers from another provider do not establish failure.
678
+ A separate detached check covers headless and argv startup exits before task
679
+ delivery. Existing readiness and watch/cull handling continues afterward.
680
+
616
681
  **Hosted learning (brindle Pro).** With a plan that includes it, brindle learns
617
682
  which of your `learning_candidates` profiles suits which kind of task, and picks
618
683
  one when `assign` gets no profile and no milestone names one (the reply says
@@ -711,6 +776,38 @@ Codex patches are checked file by file: allowed only if every file is. Codex
711
776
  doesn't say which tool run followed which prompt, so brindle doesn't learn from
712
777
  Codex approvals. To undo, delete that entry (or untrust it in Codex's `/hooks`).
713
778
 
779
+ Codex also uses the profile's `allowed_tools` shell entries: `Bash(just lint)`
780
+ allows that exact command, and `Bash(task test:*)` allows it with arguments.
781
+ The same simple-command restriction applies, and default, user, repo and
782
+ profile denies still win. Other `allowed_tools` entries aren't translated.
783
+ An unrecognized patch header prevents automatic approval without discarding
784
+ denies for files brindle can identify. Session/status bookkeeping failures
785
+ don't discard the hook's decision; failures to evaluate the policy leave
786
+ Codex's normal approval flow in place.
787
+
788
+ Scope and platform limits: this is an **approval-request hook**, not a gate
789
+ on every tool call. Codex commands already permitted by its sandbox or own
790
+ rules never reach it, so brindle cannot enforce denies on those calls. It
791
+ doesn't change Codex's sandbox or approval mode, and cannot make a `never`
792
+ approval mode interactive. Disabled hooks, managed-only hook policies, older
793
+ Codex versions without `PermissionRequest`, and nonlocal execution can prevent
794
+ the hook from running. Keep Codex's own restrictions for tools that must always
795
+ be blocked. See the [OpenAI hook protocol](https://learn.chatgpt.com/docs/hooks).
796
+
797
+ The integration targets local macOS/Linux Codex CLI processes (POSIX shell
798
+ quoting); native Windows hook commands aren't supported. The local app-server
799
+ protocol was checked on macOS with Codex CLI **0.162.1**: newline-delimited
800
+ `initialize`, `initialized`, and `hooks/list` returned the session-flags key
801
+ above, `eventName: "permissionRequest"`, `source: "sessionFlags"`,
802
+ `currentHash`, `enabled: true`, and `trustStatus: "trusted"` for the installed
803
+ brindle hook. This read-only probe made no model calls or trust edits; hook
804
+ dispatch/allow/deny behavior is covered by the permission tests. Linux runtime
805
+ and native Windows behavior were not exercised by that probe.
806
+ `codex features list` also reported `hooks stable true` without a user
807
+ `features.hooks` setting, both on 0.162.1 and the bundled 0.158.0-alpha.2.1.
808
+ A session override of `features.hooks=false` reported false. No feature-enable
809
+ flag is needed on these versions, and brindle leaves explicit disablement alone.
810
+
714
811
  **Antigravity.** agy's hook can deny but its "allow" is ignored (an agy bug),
715
812
  and it has no "no opinion" answer. So the hook denies what the policy denies,
716
813
  answers "allow" for a file inside the workspace (which agy reads and writes
@@ -1343,6 +1440,22 @@ A profile chooses between the two with `auth`:
1343
1440
 
1344
1441
  Your org's `deny_personal_keys` (Enterprise) still applies on top of `auth`.
1345
1442
 
1443
+ #### Running unattended on API keys
1444
+
1445
+ When running long, unattended autopilot sessions with API keys:
1446
+
1447
+ - **Quota, token and cost measurements per provider**:
1448
+ - Autopilot's automatic usage pause (`usage_limit: 90`) relies on the percentage numbers emitted by Claude Code's status line under subscription plans. Under API-key billing (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`), the CLIs report no subscription percentage, so subscription usage pause thresholds do not trigger; manage spend via provider account budget limits or brindle budget policies.
1449
+ - Token and cost tracking: Token counts and cost estimates are captured from Claude and Codex transcripts. Antigravity (`agy`) currently has **no token/cost measurements**, so budget policies and cost tracking cannot monitor Antigravity spend; enforce usage caps directly in Google AI Studio or your Google Cloud project billing settings.
1450
+ - **Runtime auth failure handling and provider differences**:
1451
+ - **Claude and Antigravity**: Runtime authentication failures (such as 401/403, revoked API keys, or credit/billing errors) are intercepted via StopFailure and exit hooks. Brindle marks the worker as failed, alerts the supervisor, invalidates cached sign-in checks, and pauses autopilot with a `Credentials need attention` notice rather than cycling idle nudges.
1452
+ - **Codex (unsupported runtime auth detection)**: Codex CLI turn completion (`agent-turn-complete`) does not classify auth errors at runtime. If an OpenAI API key expires or is rejected, Codex workers will not automatically transition to `failed` or trigger an autopilot credential pause. When running Codex workers unattended, monitor their progress via `brindle watch` or check tmux panes; if an auth failure occurs, manually restart or rotate keys.
1453
+ - **Supervisor credential refresh and key rotation**:
1454
+ - **Recovering failed/blocked workers**: When an agent fails on an expired key or autopilot pauses with a credential error, export the fresh key (or update `brindle keys set`), then run `brindle continue`. This unpauses the session and relaunches paused/failed agents with the new credentials. The replacement credentials are automatically synced to the session credential store, dynamically refreshed by the supervisor's launcher on all subsequent worker delegations (`assign`/`handoff`).
1455
+ - **Proactive rotation in live sessions**: Exporting a new key while an active session is healthy does not restart running workers or the supervisor (running `brindle continue` on an active healthy session simply reattaches to tmux without restarting workers). To proactively rotate credentials for the entire session and all running workers: run `brindle stop` -> update credentials in your environment or via `brindle keys set` -> run `brindle continue`.
1456
+ - **Precedence**: Newly stored keys (`brindle keys set`) or session credentials updated during recovery take effect dynamically for all new panes, overriding stale launcher environments. For profile-level overrides, an explicit profile key (`env.NAME`) takes precedence over default provider credentials.
1457
+ - **Antigravity permissions prerequisite**: Antigravity (`agy`) does not support approving shell commands solely via external hooks. With `GEMINI_API_KEY`, unattended agy workers will still stall on approval prompts unless permissions are pre-populated in `settings.json` (see Permissions) or run with `agy_approvals: "brindle"` under `permission_policy: "on"` (`--dangerously-skip-permissions`).
1458
+
1346
1459
  ### Cheap workers
1347
1460
 
1348
1461
  By default a Claude Code worker loads everything your own `claude` does: your
@@ -59,7 +59,7 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
59
59
  brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
60
60
 
61
61
  `brindle --version` prints the installed version; the tmux status bar of every brindle
62
- session shows it too (`brindle 0.3.2`). A session started before an upgrade keeps
62
+ session shows it too (`brindle 0.3.4`). A session started before an upgrade keeps
63
63
  running the old code, and shows the old number, until you restart it.
64
64
 
65
65
  ## Quick start
@@ -88,11 +88,11 @@ a writable home) and, in a repo, what's set up for it, and says what to do about
88
88
  anything missing. Bare `brindle` runs the essential ones (tmux, the chat's CLI)
89
89
  before it launches anything.
90
90
 
91
- That's it. `brindle` opens a supervisor chat (Claude Code) in your repo, with a
92
- narrow sidebar on the left showing every agent: who's working, who's idle, and
93
- who's waiting for your approval. Tell the supervisor what you want. It splits the
94
- work between workers, each on its own branch, then reviews and merges their
95
- branches. It starts in under a second.
91
+ That's it. Bare `brindle` opens a fresh interactive developer chat (Claude Code)
92
+ in your repo (with autopilot off), with a narrow sidebar on the left showing every
93
+ agent: who's working, who's idle, and who's waiting for your approval. To launch an
94
+ autopilot supervisor that splits work between parallel workers, reviews, and merges
95
+ their branches toward verified milestones, run `brindle supervisor`. It starts in under a second.
96
96
 
97
97
  **The sidebar follows you.** There's one sidebar pane per session root, not
98
98
  one per window: switch to any other brindle window or session (⏎ in the
@@ -212,7 +212,7 @@ for mechanical edits and a Codex reviewer for a second model's opinion.
212
212
 
213
213
  ## Autopilot
214
214
 
215
- `brindle` starts the supervisor with autopilot on. Tell it what we're building,
215
+ `brindle supervisor` starts the supervisor with autopilot on (or `brindle start -a supervisor`). Tell it what we're building,
216
216
  or put the goal in `.brindle/goals.md`, and it works like a project manager:
217
217
 
218
218
  1. **Goal and milestones.** The goal is split into milestones, and each one has
@@ -307,7 +307,8 @@ your own status line prints, so what you see doesn't change.
307
307
 
308
308
  | | |
309
309
  |---|---|
310
- | `brindle` | a fresh supervisor chat here, dashboard alongside |
310
+ | `brindle` | a fresh developer chat here (autopilot off), dashboard alongside |
311
+ | `brindle supervisor` | start an explicit autopilot supervisor chat to coordinate parallel workers and milestones |
311
312
  | `brindle init` | detect setup and test commands, write `.brindle/config.json`, check tools |
312
313
  | `brindle demo [--local]` | watch brindle finish a tiny practice repo: parallel workers, reviews, gated merges |
313
314
  | `brindle new BRANCH [-b BASE] [-a PROFILE] [-p PROMPT]` | worktree + branch + agent |
@@ -316,6 +317,7 @@ your own status line prints, so what you see doesn't change.
316
317
  | `brindle start [-a PROFILE] [-p PROMPT] [--no-watch] [--no-autopilot] [-b BRANCH] [-w PATH]` | the same, with options; `-b`/`-w` run it in that branch's worktree (created if needed, or the one you already made), which gets the repo's `.brindle` config |
317
318
  | `brindle handover --to BRANCH\|PATH [-n NOTE]` | hand the session to a new supervisor there: goal and milestones, workers, queued tasks and your note move across; the old one is paused |
318
319
  | `brindle autopilot [on\|off\|check]` | the goal's progress; turn autopilot on or off; run the checks now |
320
+ | `brindle audit-decision FINDING [-r RATIONALE]` | record acceptance of an audit finding with rationale so subsequent audits respect it |
319
321
  | `brindle delegation [conservative\|balanced\|fast]` | how readily the supervisor delegates: fewest tokens, the default, or quickest |
320
322
  | `brindle transfer [REPO] [--from SESSION] [-b BRANCH]` | move a scratch session's work into a real repo |
321
323
  | `brindle ls [--all]` | workspaces and agents |
@@ -377,6 +379,7 @@ knowing them helps when you tell the supervisor how to work.
377
379
  | `approve_plan` | supervisor | approve a worker's plan, or send it back with feedback (`approved=false`) |
378
380
  | `complete_subagent` | supervisor | record the result of a `subagent`-profile task |
379
381
  | `set_goal` / `get_progress` / `check_milestone` | supervisor | autopilot's goal, its progress, and running the checks |
382
+ | `record_audit_decision` | supervisor | record acceptance of an audit finding with rationale so subsequent audits respect it |
380
383
  | `need_user` | supervisor | stop autopilot and ask you a question |
381
384
  | `transfer_to_repo` | supervisor | move a scratch session's work into a repository |
382
385
  | `handover` | supervisor | hand the session to a new supervisor on another branch or worktree, with a note |
@@ -573,6 +576,21 @@ keep theirs):
573
576
  No tier prefers another provider for its own sake: all-Claude or all-Codex may
574
577
  be what works best for you, and that's for the learner to find out.
575
578
 
579
+ Routing merges per tier in this order: built-in defaults, `~/.brindle/config.json`
580
+ (every repo), the repo's `.brindle/config.json` (shared with the team), then
581
+ `.brindle/config.local.json` (your override for that repo). Each tier you set
582
+ replaces that tier's list; tiers you leave out keep the list from the layer below.
583
+ For example, put this in `~/.brindle/config.json` to use Codex for light tasks
584
+ in every repo, or in a repo's `.brindle/config.local.json` to do so only there:
585
+
586
+ ```json
587
+ {"routing": {"light": ["developer-codex", "developer"]}}
588
+ ```
589
+
590
+ Run `brindle doctor` in the repo to see the effective list and source file for
591
+ each tier. Unknown profile names are skipped; doctor and the routing reply
592
+ report the missing name and the file that supplied it.
593
+
576
594
  `developer-light` runs on Claude Haiku at low effort; `developer` on your Claude
577
595
  Code default model; `developer-codex` on Codex; `developer-heavy` on Claude Fable
578
596
  at high effort.
@@ -583,6 +601,53 @@ wins. When every candidate is out, the repo's `default_agent` runs. The reply sa
583
601
  what was picked and why, e.g. `weight medium -> developer (Codex at 93%, skipped developer-codex)`.
584
602
  An `agent_profile` you pass, or a milestone's `profile`, always wins over weight.
585
603
 
604
+ Provisioning and routing also check explicit models against supported read-only
605
+ catalogs, with the managed model, stored keys and the worker's filtered pane
606
+ environment. A model absent from a complete authoritative catalog is rejected
607
+ before creating a worker worktree or process. Partial catalogs, aliases,
608
+ discovery errors and Codex's bundled catalogs remain unverified; being listed
609
+ does not prove generation access, billing or quota. Catalog discovery is bounded
610
+ to five seconds for standalone checks; foreground routing/provisioning limits
611
+ discovery to one second, leaving room in the three-second launch budget for
612
+ provisioning and bounded helper cleanup. A timeout remains unverified and
613
+ launch proceeds with the real-task health follow-up. Catalogs are cached
614
+ briefly by environment and local account/config
615
+ revision where available. Routing and provisioning reuse equivalent catalog
616
+ context across worktrees, excluding launch bookkeeping while retaining project
617
+ settings, credentials and account selectors. CLI catalogs remain advisory;
618
+ unobservable external/keychain login changes may remain cached for 30 seconds.
619
+ Custom CLI binaries retain separate cache entries per working directory.
620
+ Claude, Bedrock, Vertex and Foundry have no catalog
621
+ probe here; no Claude CLI or synthetic generation request is used for these checks.
622
+
623
+ The real first worker task is sent once. A detached helper inspects its current
624
+ pane five seconds after delivery, so readiness delays do not use up that window.
625
+ Fresh interactive Antigravity chats without a task still receive their profile,
626
+ tool instructions and any autopilot context once after readiness; worker
627
+ instructions accompany the actual task in the same delivery.
628
+ For argv tasks, the timer starts at prompt submission or observed CLI readiness,
629
+ after trust/startup handling; readiness timeout does not establish delivery or
630
+ release queued work. Paused workers, replaced launches and reassigned panes are
631
+ excluded from the observation.
632
+ Explicit fresh authentication, access, model and capacity errors, or an early
633
+ exit, produce a safe supervisor message with a next step. A live process with
634
+ no clear result is recorded as pending for diagnostics, not verified model
635
+ access; it does not send a supervisor notification. Refusals use
636
+ the existing one-hour cooldown, scoped to the effective model/environment and
637
+ local login revision. Changing the account, model or credentials invalidates
638
+ that refusal; CLI/keychain account changes without an observable local revision
639
+ may require waiting for the cooldown or clearing `model_refusals.json`.
640
+ This is a single observation, not an entitlement test: unfamiliar error layouts,
641
+ later failures and slow requests can remain pending. It does not scan
642
+ historical transcripts, and excludes task text, previous pane lines and older
643
+ marked replies, fenced examples and ordinary assistant/tool explanations. A
644
+ Codex follow-up retains the complete submitted argv prompt (including profile
645
+ instructions) and distinguishes assistant/tool responses from the idle composer.
646
+ Quoted prompts and error-looking markers inside assistant/tool output remain
647
+ ambiguous and pending; markers from another provider do not establish failure.
648
+ A separate detached check covers headless and argv startup exits before task
649
+ delivery. Existing readiness and watch/cull handling continues afterward.
650
+
586
651
  **Hosted learning (brindle Pro).** With a plan that includes it, brindle learns
587
652
  which of your `learning_candidates` profiles suits which kind of task, and picks
588
653
  one when `assign` gets no profile and no milestone names one (the reply says
@@ -681,6 +746,38 @@ Codex patches are checked file by file: allowed only if every file is. Codex
681
746
  doesn't say which tool run followed which prompt, so brindle doesn't learn from
682
747
  Codex approvals. To undo, delete that entry (or untrust it in Codex's `/hooks`).
683
748
 
749
+ Codex also uses the profile's `allowed_tools` shell entries: `Bash(just lint)`
750
+ allows that exact command, and `Bash(task test:*)` allows it with arguments.
751
+ The same simple-command restriction applies, and default, user, repo and
752
+ profile denies still win. Other `allowed_tools` entries aren't translated.
753
+ An unrecognized patch header prevents automatic approval without discarding
754
+ denies for files brindle can identify. Session/status bookkeeping failures
755
+ don't discard the hook's decision; failures to evaluate the policy leave
756
+ Codex's normal approval flow in place.
757
+
758
+ Scope and platform limits: this is an **approval-request hook**, not a gate
759
+ on every tool call. Codex commands already permitted by its sandbox or own
760
+ rules never reach it, so brindle cannot enforce denies on those calls. It
761
+ doesn't change Codex's sandbox or approval mode, and cannot make a `never`
762
+ approval mode interactive. Disabled hooks, managed-only hook policies, older
763
+ Codex versions without `PermissionRequest`, and nonlocal execution can prevent
764
+ the hook from running. Keep Codex's own restrictions for tools that must always
765
+ be blocked. See the [OpenAI hook protocol](https://learn.chatgpt.com/docs/hooks).
766
+
767
+ The integration targets local macOS/Linux Codex CLI processes (POSIX shell
768
+ quoting); native Windows hook commands aren't supported. The local app-server
769
+ protocol was checked on macOS with Codex CLI **0.162.1**: newline-delimited
770
+ `initialize`, `initialized`, and `hooks/list` returned the session-flags key
771
+ above, `eventName: "permissionRequest"`, `source: "sessionFlags"`,
772
+ `currentHash`, `enabled: true`, and `trustStatus: "trusted"` for the installed
773
+ brindle hook. This read-only probe made no model calls or trust edits; hook
774
+ dispatch/allow/deny behavior is covered by the permission tests. Linux runtime
775
+ and native Windows behavior were not exercised by that probe.
776
+ `codex features list` also reported `hooks stable true` without a user
777
+ `features.hooks` setting, both on 0.162.1 and the bundled 0.158.0-alpha.2.1.
778
+ A session override of `features.hooks=false` reported false. No feature-enable
779
+ flag is needed on these versions, and brindle leaves explicit disablement alone.
780
+
684
781
  **Antigravity.** agy's hook can deny but its "allow" is ignored (an agy bug),
685
782
  and it has no "no opinion" answer. So the hook denies what the policy denies,
686
783
  answers "allow" for a file inside the workspace (which agy reads and writes
@@ -1313,6 +1410,22 @@ A profile chooses between the two with `auth`:
1313
1410
 
1314
1411
  Your org's `deny_personal_keys` (Enterprise) still applies on top of `auth`.
1315
1412
 
1413
+ #### Running unattended on API keys
1414
+
1415
+ When running long, unattended autopilot sessions with API keys:
1416
+
1417
+ - **Quota, token and cost measurements per provider**:
1418
+ - Autopilot's automatic usage pause (`usage_limit: 90`) relies on the percentage numbers emitted by Claude Code's status line under subscription plans. Under API-key billing (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`), the CLIs report no subscription percentage, so subscription usage pause thresholds do not trigger; manage spend via provider account budget limits or brindle budget policies.
1419
+ - Token and cost tracking: Token counts and cost estimates are captured from Claude and Codex transcripts. Antigravity (`agy`) currently has **no token/cost measurements**, so budget policies and cost tracking cannot monitor Antigravity spend; enforce usage caps directly in Google AI Studio or your Google Cloud project billing settings.
1420
+ - **Runtime auth failure handling and provider differences**:
1421
+ - **Claude and Antigravity**: Runtime authentication failures (such as 401/403, revoked API keys, or credit/billing errors) are intercepted via StopFailure and exit hooks. Brindle marks the worker as failed, alerts the supervisor, invalidates cached sign-in checks, and pauses autopilot with a `Credentials need attention` notice rather than cycling idle nudges.
1422
+ - **Codex (unsupported runtime auth detection)**: Codex CLI turn completion (`agent-turn-complete`) does not classify auth errors at runtime. If an OpenAI API key expires or is rejected, Codex workers will not automatically transition to `failed` or trigger an autopilot credential pause. When running Codex workers unattended, monitor their progress via `brindle watch` or check tmux panes; if an auth failure occurs, manually restart or rotate keys.
1423
+ - **Supervisor credential refresh and key rotation**:
1424
+ - **Recovering failed/blocked workers**: When an agent fails on an expired key or autopilot pauses with a credential error, export the fresh key (or update `brindle keys set`), then run `brindle continue`. This unpauses the session and relaunches paused/failed agents with the new credentials. The replacement credentials are automatically synced to the session credential store, dynamically refreshed by the supervisor's launcher on all subsequent worker delegations (`assign`/`handoff`).
1425
+ - **Proactive rotation in live sessions**: Exporting a new key while an active session is healthy does not restart running workers or the supervisor (running `brindle continue` on an active healthy session simply reattaches to tmux without restarting workers). To proactively rotate credentials for the entire session and all running workers: run `brindle stop` -> update credentials in your environment or via `brindle keys set` -> run `brindle continue`.
1426
+ - **Precedence**: Newly stored keys (`brindle keys set`) or session credentials updated during recovery take effect dynamically for all new panes, overriding stale launcher environments. For profile-level overrides, an explicit profile key (`env.NAME`) takes precedence over default provider credentials.
1427
+ - **Antigravity permissions prerequisite**: Antigravity (`agy`) does not support approving shell commands solely via external hooks. With `GEMINI_API_KEY`, unattended agy workers will still stall on approval prompts unless permissions are pre-populated in `settings.json` (see Permissions) or run with `agy_approvals: "brindle"` under `permission_policy: "on"` (`--dangerously-skip-permissions`).
1428
+
1316
1429
  ### Cheap workers
1317
1430
 
1318
1431
  By default a Claude Code worker loads everything your own `claude` does: your
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "brindle"
3
- version = "0.3.2"
3
+ version = "0.3.4"
4
4
  description = "Run coding agents in parallel, each on its own git branch, with a supervisor that delegates, reviews, and merges"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"