brindle 0.3.1__tar.gz → 0.3.3__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 (126) hide show
  1. {brindle-0.3.1 → brindle-0.3.3}/PKG-INFO +112 -2
  2. {brindle-0.3.1 → brindle-0.3.3}/README.md +111 -1
  3. {brindle-0.3.1 → brindle-0.3.3}/pyproject.toml +1 -1
  4. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/agents.py +190 -47
  5. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/antigravity.py +111 -20
  6. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/autopilot.py +17 -2
  7. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/ci_client.py +2 -3
  8. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/cli.py +42 -9
  9. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/codex_hook.py +22 -4
  10. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/config.py +11 -3
  11. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/db.py +18 -0
  12. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/doctor.py +24 -0
  13. brindle-0.3.3/src/brindle/launch_health.py +379 -0
  14. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/model_access.py +34 -11
  15. brindle-0.3.3/src/brindle/model_availability.py +504 -0
  16. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/native/runner.py +2 -0
  17. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/permissions.py +188 -4
  18. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/account.py +6 -1
  19. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/auth.py +1 -1
  20. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/providers.py +129 -15
  21. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/repos.py +5 -1
  22. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/tmux.py +22 -0
  23. {brindle-0.3.1 → brindle-0.3.3}/.gitignore +0 -0
  24. {brindle-0.3.1 → brindle-0.3.3}/LICENSE +0 -0
  25. {brindle-0.3.1 → brindle-0.3.3}/SCHEDULE-A +0 -0
  26. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/__init__.py +0 -0
  27. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/__main__.py +0 -0
  28. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/account.py +0 -0
  29. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/airgap.py +0 -0
  30. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/budget.py +0 -0
  31. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/__init__.py +0 -0
  32. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/backend-auditor.md +0 -0
  33. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/developer-codex.md +0 -0
  34. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/developer-heavy.md +0 -0
  35. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/developer-light.md +0 -0
  36. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/developer-local.md +0 -0
  37. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/developer.md +0 -0
  38. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/qa.md +0 -0
  39. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/reviewer-codex.md +0 -0
  40. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/reviewer-local.md +0 -0
  41. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/reviewer.md +0 -0
  42. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/subagent.md +0 -0
  43. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_agents/supervisor.md +0 -0
  44. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_rules/__init__.py +0 -0
  45. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_rules/security/backend.md +0 -0
  46. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_rules/style/minimal-diff.md +0 -0
  47. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/builtin_rules/tests/only.md +0 -0
  48. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/ci_adapters.py +0 -0
  49. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/ci_federation.py +0 -0
  50. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/ci_hosts.py +0 -0
  51. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/codemap.py +0 -0
  52. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/company_identity.py +0 -0
  53. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/company_login.py +0 -0
  54. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/conflicts.py +0 -0
  55. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/cost.py +0 -0
  56. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/cost_estimate.py +0 -0
  57. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/cull.py +0 -0
  58. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/demo.py +0 -0
  59. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/detect.py +0 -0
  60. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/escapes.py +0 -0
  61. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/events.py +0 -0
  62. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/gates.py +0 -0
  63. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/git.py +0 -0
  64. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/guardrails.py +0 -0
  65. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/history.py +0 -0
  66. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/identity_lock.py +0 -0
  67. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/inbox.py +0 -0
  68. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/keys_cmds.py +0 -0
  69. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/keystore.py +0 -0
  70. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/learned_rules.py +0 -0
  71. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/learning.py +0 -0
  72. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/legacy.py +0 -0
  73. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/mcp_server.py +0 -0
  74. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/native/__init__.py +0 -0
  75. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/native/client.py +0 -0
  76. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/native/loop.py +0 -0
  77. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/native/permissions.py +0 -0
  78. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/native/serve.py +0 -0
  79. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/native/tools.py +0 -0
  80. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pane_auth.py +0 -0
  81. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pipeline.py +0 -0
  82. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/plugins.py +0 -0
  83. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/policy.py +0 -0
  84. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pool.py +0 -0
  85. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pricing.py +0 -0
  86. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/__init__.py +0 -0
  87. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/_files.py +0 -0
  88. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/audit_chain.py +0 -0
  89. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/audit_export.py +0 -0
  90. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/brief_learning.py +0 -0
  91. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/cost_centers.py +0 -0
  92. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/credentials.py +0 -0
  93. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/features.py +0 -0
  94. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/keys.py +0 -0
  95. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/learning.py +0 -0
  96. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/license.py +0 -0
  97. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/loopback.py +0 -0
  98. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/managed_models.py +0 -0
  99. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/org_profiles.py +0 -0
  100. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/orgkey.py +0 -0
  101. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/rollout.py +0 -0
  102. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/settings_sync.py +0 -0
  103. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/status.py +0 -0
  104. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/team_events.py +0 -0
  105. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/pro/team_policy.py +0 -0
  106. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/procs.py +0 -0
  107. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/profiles.py +0 -0
  108. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/quota.py +0 -0
  109. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/repo_cmds.py +0 -0
  110. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/rewind.py +0 -0
  111. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/rule_checks.py +0 -0
  112. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/savings.py +0 -0
  113. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/scratch.py +0 -0
  114. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/secrets.py +0 -0
  115. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/self_serve.py +0 -0
  116. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/services.py +0 -0
  117. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/sessions.py +0 -0
  118. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/sidebar_follow.py +0 -0
  119. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/signin_pause.py +0 -0
  120. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/status_cache.py +0 -0
  121. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/tasks.py +0 -0
  122. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/usage.py +0 -0
  123. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/view.py +0 -0
  124. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/watch.py +0 -0
  125. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/watch_costs.py +0 -0
  126. {brindle-0.3.1 → brindle-0.3.3}/src/brindle/workspaces.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brindle
3
- Version: 0.3.1
3
+ Version: 0.3.3
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.1`). A session started before an upgrade keeps
92
+ session shows it too (`brindle 0.3.3`). 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
@@ -539,6 +539,7 @@ Autopilot, merge gates and cleanup:
539
539
  | `merge_into` | none | branch that worker branches are cut from and merge into, whatever branch the supervisor is on |
540
540
  | `auto_merge_default_branch` | `false` | let the pipeline merge into the repo's default branch (origin HEAD, else `main`/`master`) on its own; by default it sends a "needs you" message instead, and you run `merge_workspace` yourself (manual merges are never gated) |
541
541
  | `permission_policy` | `"off"` | `"on"`: brindle answers Claude Code workers' permission prompts from its rules (see "Permission policy" below); off, the prompts behave as they always did |
542
+ | `agy_approvals` | `"prompt"` | `"brindle"`: with the policy on, Antigravity workers run without approval prompts and brindle's hook allows or denies every call (see "Antigravity" under "Permission policy") |
542
543
  | `rules` | `[]` | standing rules for the supervisor, e.g. `["Fix review findings without asking", "Validate options before offering them"]`; added to every supervisor's brief. Rules in `~/.brindle/config.json`, the repo's config and `config.local.json` are all kept, so a team's rules and your own add up |
543
544
  | `plan_first` | `false` | workers propose a plan (`submit_plan`) and wait for `approve_plan` before editing |
544
545
  | `overlap` | `"block"` | a task whose `files` overlap a running task's is refused (`"warn"` starts it with a warning) |
@@ -602,6 +603,21 @@ keep theirs):
602
603
  No tier prefers another provider for its own sake: all-Claude or all-Codex may
603
604
  be what works best for you, and that's for the learner to find out.
604
605
 
606
+ Routing merges per tier in this order: built-in defaults, `~/.brindle/config.json`
607
+ (every repo), the repo's `.brindle/config.json` (shared with the team), then
608
+ `.brindle/config.local.json` (your override for that repo). Each tier you set
609
+ replaces that tier's list; tiers you leave out keep the list from the layer below.
610
+ For example, put this in `~/.brindle/config.json` to use Codex for light tasks
611
+ in every repo, or in a repo's `.brindle/config.local.json` to do so only there:
612
+
613
+ ```json
614
+ {"routing": {"light": ["developer-codex", "developer"]}}
615
+ ```
616
+
617
+ Run `brindle doctor` in the repo to see the effective list and source file for
618
+ each tier. Unknown profile names are skipped; doctor and the routing reply
619
+ report the missing name and the file that supplied it.
620
+
605
621
  `developer-light` runs on Claude Haiku at low effort; `developer` on your Claude
606
622
  Code default model; `developer-codex` on Codex; `developer-heavy` on Claude Fable
607
623
  at high effort.
@@ -612,6 +628,53 @@ wins. When every candidate is out, the repo's `default_agent` runs. The reply sa
612
628
  what was picked and why, e.g. `weight medium -> developer (Codex at 93%, skipped developer-codex)`.
613
629
  An `agent_profile` you pass, or a milestone's `profile`, always wins over weight.
614
630
 
631
+ Provisioning and routing also check explicit models against supported read-only
632
+ catalogs, with the managed model, stored keys and the worker's filtered pane
633
+ environment. A model absent from a complete authoritative catalog is rejected
634
+ before creating a worker worktree or process. Partial catalogs, aliases,
635
+ discovery errors and Codex's bundled catalogs remain unverified; being listed
636
+ does not prove generation access, billing or quota. Catalog discovery is bounded
637
+ to five seconds for standalone checks; foreground routing/provisioning limits
638
+ discovery to one second, leaving room in the three-second launch budget for
639
+ provisioning and bounded helper cleanup. A timeout remains unverified and
640
+ launch proceeds with the real-task health follow-up. Catalogs are cached
641
+ briefly by environment and local account/config
642
+ revision where available. Routing and provisioning reuse equivalent catalog
643
+ context across worktrees, excluding launch bookkeeping while retaining project
644
+ settings, credentials and account selectors. CLI catalogs remain advisory;
645
+ unobservable external/keychain login changes may remain cached for 30 seconds.
646
+ Custom CLI binaries retain separate cache entries per working directory.
647
+ Claude, Bedrock, Vertex and Foundry have no catalog
648
+ probe here; no Claude CLI or synthetic generation request is used for these checks.
649
+
650
+ The real first worker task is sent once. A detached helper inspects its current
651
+ pane five seconds after delivery, so readiness delays do not use up that window.
652
+ Fresh interactive Antigravity chats without a task still receive their profile,
653
+ tool instructions and any autopilot context once after readiness; worker
654
+ instructions accompany the actual task in the same delivery.
655
+ For argv tasks, the timer starts at prompt submission or observed CLI readiness,
656
+ after trust/startup handling; readiness timeout does not establish delivery or
657
+ release queued work. Paused workers, replaced launches and reassigned panes are
658
+ excluded from the observation.
659
+ Explicit fresh authentication, access, model and capacity errors, or an early
660
+ exit, produce a safe supervisor message with a next step. A live process with
661
+ no clear result is recorded as pending for diagnostics, not verified model
662
+ access; it does not send a supervisor notification. Refusals use
663
+ the existing one-hour cooldown, scoped to the effective model/environment and
664
+ local login revision. Changing the account, model or credentials invalidates
665
+ that refusal; CLI/keychain account changes without an observable local revision
666
+ may require waiting for the cooldown or clearing `model_refusals.json`.
667
+ This is a single observation, not an entitlement test: unfamiliar error layouts,
668
+ later failures and slow requests can remain pending. It does not scan
669
+ historical transcripts, and excludes task text, previous pane lines and older
670
+ marked replies, fenced examples and ordinary assistant/tool explanations. A
671
+ Codex follow-up retains the complete submitted argv prompt (including profile
672
+ instructions) and distinguishes assistant/tool responses from the idle composer.
673
+ Quoted prompts and error-looking markers inside assistant/tool output remain
674
+ ambiguous and pending; markers from another provider do not establish failure.
675
+ A separate detached check covers headless and argv startup exits before task
676
+ delivery. Existing readiness and watch/cull handling continues afterward.
677
+
615
678
  **Hosted learning (brindle Pro).** With a plan that includes it, brindle learns
616
679
  which of your `learning_candidates` profiles suits which kind of task, and picks
617
680
  one when `assign` gets no profile and no milestone names one (the reply says
@@ -710,6 +773,38 @@ Codex patches are checked file by file: allowed only if every file is. Codex
710
773
  doesn't say which tool run followed which prompt, so brindle doesn't learn from
711
774
  Codex approvals. To undo, delete that entry (or untrust it in Codex's `/hooks`).
712
775
 
776
+ Codex also uses the profile's `allowed_tools` shell entries: `Bash(just lint)`
777
+ allows that exact command, and `Bash(task test:*)` allows it with arguments.
778
+ The same simple-command restriction applies, and default, user, repo and
779
+ profile denies still win. Other `allowed_tools` entries aren't translated.
780
+ An unrecognized patch header prevents automatic approval without discarding
781
+ denies for files brindle can identify. Session/status bookkeeping failures
782
+ don't discard the hook's decision; failures to evaluate the policy leave
783
+ Codex's normal approval flow in place.
784
+
785
+ Scope and platform limits: this is an **approval-request hook**, not a gate
786
+ on every tool call. Codex commands already permitted by its sandbox or own
787
+ rules never reach it, so brindle cannot enforce denies on those calls. It
788
+ doesn't change Codex's sandbox or approval mode, and cannot make a `never`
789
+ approval mode interactive. Disabled hooks, managed-only hook policies, older
790
+ Codex versions without `PermissionRequest`, and nonlocal execution can prevent
791
+ the hook from running. Keep Codex's own restrictions for tools that must always
792
+ be blocked. See the [OpenAI hook protocol](https://learn.chatgpt.com/docs/hooks).
793
+
794
+ The integration targets local macOS/Linux Codex CLI processes (POSIX shell
795
+ quoting); native Windows hook commands aren't supported. The local app-server
796
+ protocol was checked on macOS with Codex CLI **0.162.1**: newline-delimited
797
+ `initialize`, `initialized`, and `hooks/list` returned the session-flags key
798
+ above, `eventName: "permissionRequest"`, `source: "sessionFlags"`,
799
+ `currentHash`, `enabled: true`, and `trustStatus: "trusted"` for the installed
800
+ brindle hook. This read-only probe made no model calls or trust edits; hook
801
+ dispatch/allow/deny behavior is covered by the permission tests. Linux runtime
802
+ and native Windows behavior were not exercised by that probe.
803
+ `codex features list` also reported `hooks stable true` without a user
804
+ `features.hooks` setting, both on 0.162.1 and the bundled 0.158.0-alpha.2.1.
805
+ A session override of `features.hooks=false` reported false. No feature-enable
806
+ flag is needed on these versions, and brindle leaves explicit disablement alone.
807
+
713
808
  **Antigravity.** agy's hook can deny but its "allow" is ignored (an agy bug),
714
809
  and it has no "no opinion" answer. So the hook denies what the policy denies,
715
810
  answers "allow" for a file inside the workspace (which agy reads and writes
@@ -726,6 +821,21 @@ does it by hand. To undo, turn `permission_policy` off and run `sync-agy`: brind
726
821
  removes exactly its own entries. brindle doesn't learn from agy approvals (its
727
822
  hook runs for every tool call, so a tool running doesn't mean you approved it).
728
823
 
824
+ With `"agy_approvals": "brindle"` (and the policy on), brindle's hook is the
825
+ only gate instead: agy workers start with `--dangerously-skip-permissions`,
826
+ under which agy still honors a hook's deny, and the hook runs on every tool,
827
+ subagents' calls included. It allows what a rule allows (a repo's checks
828
+ included), the shell commands in the profile's `allowed_tools` (what a Claude
829
+ Code worker on it runs without asking), files inside the workspace given as one
830
+ absolute path (writes under `.agents/`, `.git/` and
831
+ `.brindle/` only by a rule, since those hold the hook, git's hooks and the
832
+ repo's checks), brindle's own tools and agy's tools that act only on the
833
+ conversation, and denies everything else, telling the agent to
834
+ ask its supervisor; you allow it with `brindle permissions allow`. Any failure
835
+ in the hook is a deny, and a tool brindle doesn't know is denied. Nobody can
836
+ approve a prompt in the agent's pane in this mode. The default, `"prompt"`,
837
+ leaves agy asking as described above.
838
+
729
839
  ## brindle Pro and Team
730
840
 
731
841
  brindle is complete on its own. brindle Pro adds hosted learning (which profile
@@ -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.1`). A session started before an upgrade keeps
62
+ session shows it too (`brindle 0.3.3`). 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
@@ -509,6 +509,7 @@ Autopilot, merge gates and cleanup:
509
509
  | `merge_into` | none | branch that worker branches are cut from and merge into, whatever branch the supervisor is on |
510
510
  | `auto_merge_default_branch` | `false` | let the pipeline merge into the repo's default branch (origin HEAD, else `main`/`master`) on its own; by default it sends a "needs you" message instead, and you run `merge_workspace` yourself (manual merges are never gated) |
511
511
  | `permission_policy` | `"off"` | `"on"`: brindle answers Claude Code workers' permission prompts from its rules (see "Permission policy" below); off, the prompts behave as they always did |
512
+ | `agy_approvals` | `"prompt"` | `"brindle"`: with the policy on, Antigravity workers run without approval prompts and brindle's hook allows or denies every call (see "Antigravity" under "Permission policy") |
512
513
  | `rules` | `[]` | standing rules for the supervisor, e.g. `["Fix review findings without asking", "Validate options before offering them"]`; added to every supervisor's brief. Rules in `~/.brindle/config.json`, the repo's config and `config.local.json` are all kept, so a team's rules and your own add up |
513
514
  | `plan_first` | `false` | workers propose a plan (`submit_plan`) and wait for `approve_plan` before editing |
514
515
  | `overlap` | `"block"` | a task whose `files` overlap a running task's is refused (`"warn"` starts it with a warning) |
@@ -572,6 +573,21 @@ keep theirs):
572
573
  No tier prefers another provider for its own sake: all-Claude or all-Codex may
573
574
  be what works best for you, and that's for the learner to find out.
574
575
 
576
+ Routing merges per tier in this order: built-in defaults, `~/.brindle/config.json`
577
+ (every repo), the repo's `.brindle/config.json` (shared with the team), then
578
+ `.brindle/config.local.json` (your override for that repo). Each tier you set
579
+ replaces that tier's list; tiers you leave out keep the list from the layer below.
580
+ For example, put this in `~/.brindle/config.json` to use Codex for light tasks
581
+ in every repo, or in a repo's `.brindle/config.local.json` to do so only there:
582
+
583
+ ```json
584
+ {"routing": {"light": ["developer-codex", "developer"]}}
585
+ ```
586
+
587
+ Run `brindle doctor` in the repo to see the effective list and source file for
588
+ each tier. Unknown profile names are skipped; doctor and the routing reply
589
+ report the missing name and the file that supplied it.
590
+
575
591
  `developer-light` runs on Claude Haiku at low effort; `developer` on your Claude
576
592
  Code default model; `developer-codex` on Codex; `developer-heavy` on Claude Fable
577
593
  at high effort.
@@ -582,6 +598,53 @@ wins. When every candidate is out, the repo's `default_agent` runs. The reply sa
582
598
  what was picked and why, e.g. `weight medium -> developer (Codex at 93%, skipped developer-codex)`.
583
599
  An `agent_profile` you pass, or a milestone's `profile`, always wins over weight.
584
600
 
601
+ Provisioning and routing also check explicit models against supported read-only
602
+ catalogs, with the managed model, stored keys and the worker's filtered pane
603
+ environment. A model absent from a complete authoritative catalog is rejected
604
+ before creating a worker worktree or process. Partial catalogs, aliases,
605
+ discovery errors and Codex's bundled catalogs remain unverified; being listed
606
+ does not prove generation access, billing or quota. Catalog discovery is bounded
607
+ to five seconds for standalone checks; foreground routing/provisioning limits
608
+ discovery to one second, leaving room in the three-second launch budget for
609
+ provisioning and bounded helper cleanup. A timeout remains unverified and
610
+ launch proceeds with the real-task health follow-up. Catalogs are cached
611
+ briefly by environment and local account/config
612
+ revision where available. Routing and provisioning reuse equivalent catalog
613
+ context across worktrees, excluding launch bookkeeping while retaining project
614
+ settings, credentials and account selectors. CLI catalogs remain advisory;
615
+ unobservable external/keychain login changes may remain cached for 30 seconds.
616
+ Custom CLI binaries retain separate cache entries per working directory.
617
+ Claude, Bedrock, Vertex and Foundry have no catalog
618
+ probe here; no Claude CLI or synthetic generation request is used for these checks.
619
+
620
+ The real first worker task is sent once. A detached helper inspects its current
621
+ pane five seconds after delivery, so readiness delays do not use up that window.
622
+ Fresh interactive Antigravity chats without a task still receive their profile,
623
+ tool instructions and any autopilot context once after readiness; worker
624
+ instructions accompany the actual task in the same delivery.
625
+ For argv tasks, the timer starts at prompt submission or observed CLI readiness,
626
+ after trust/startup handling; readiness timeout does not establish delivery or
627
+ release queued work. Paused workers, replaced launches and reassigned panes are
628
+ excluded from the observation.
629
+ Explicit fresh authentication, access, model and capacity errors, or an early
630
+ exit, produce a safe supervisor message with a next step. A live process with
631
+ no clear result is recorded as pending for diagnostics, not verified model
632
+ access; it does not send a supervisor notification. Refusals use
633
+ the existing one-hour cooldown, scoped to the effective model/environment and
634
+ local login revision. Changing the account, model or credentials invalidates
635
+ that refusal; CLI/keychain account changes without an observable local revision
636
+ may require waiting for the cooldown or clearing `model_refusals.json`.
637
+ This is a single observation, not an entitlement test: unfamiliar error layouts,
638
+ later failures and slow requests can remain pending. It does not scan
639
+ historical transcripts, and excludes task text, previous pane lines and older
640
+ marked replies, fenced examples and ordinary assistant/tool explanations. A
641
+ Codex follow-up retains the complete submitted argv prompt (including profile
642
+ instructions) and distinguishes assistant/tool responses from the idle composer.
643
+ Quoted prompts and error-looking markers inside assistant/tool output remain
644
+ ambiguous and pending; markers from another provider do not establish failure.
645
+ A separate detached check covers headless and argv startup exits before task
646
+ delivery. Existing readiness and watch/cull handling continues afterward.
647
+
585
648
  **Hosted learning (brindle Pro).** With a plan that includes it, brindle learns
586
649
  which of your `learning_candidates` profiles suits which kind of task, and picks
587
650
  one when `assign` gets no profile and no milestone names one (the reply says
@@ -680,6 +743,38 @@ Codex patches are checked file by file: allowed only if every file is. Codex
680
743
  doesn't say which tool run followed which prompt, so brindle doesn't learn from
681
744
  Codex approvals. To undo, delete that entry (or untrust it in Codex's `/hooks`).
682
745
 
746
+ Codex also uses the profile's `allowed_tools` shell entries: `Bash(just lint)`
747
+ allows that exact command, and `Bash(task test:*)` allows it with arguments.
748
+ The same simple-command restriction applies, and default, user, repo and
749
+ profile denies still win. Other `allowed_tools` entries aren't translated.
750
+ An unrecognized patch header prevents automatic approval without discarding
751
+ denies for files brindle can identify. Session/status bookkeeping failures
752
+ don't discard the hook's decision; failures to evaluate the policy leave
753
+ Codex's normal approval flow in place.
754
+
755
+ Scope and platform limits: this is an **approval-request hook**, not a gate
756
+ on every tool call. Codex commands already permitted by its sandbox or own
757
+ rules never reach it, so brindle cannot enforce denies on those calls. It
758
+ doesn't change Codex's sandbox or approval mode, and cannot make a `never`
759
+ approval mode interactive. Disabled hooks, managed-only hook policies, older
760
+ Codex versions without `PermissionRequest`, and nonlocal execution can prevent
761
+ the hook from running. Keep Codex's own restrictions for tools that must always
762
+ be blocked. See the [OpenAI hook protocol](https://learn.chatgpt.com/docs/hooks).
763
+
764
+ The integration targets local macOS/Linux Codex CLI processes (POSIX shell
765
+ quoting); native Windows hook commands aren't supported. The local app-server
766
+ protocol was checked on macOS with Codex CLI **0.162.1**: newline-delimited
767
+ `initialize`, `initialized`, and `hooks/list` returned the session-flags key
768
+ above, `eventName: "permissionRequest"`, `source: "sessionFlags"`,
769
+ `currentHash`, `enabled: true`, and `trustStatus: "trusted"` for the installed
770
+ brindle hook. This read-only probe made no model calls or trust edits; hook
771
+ dispatch/allow/deny behavior is covered by the permission tests. Linux runtime
772
+ and native Windows behavior were not exercised by that probe.
773
+ `codex features list` also reported `hooks stable true` without a user
774
+ `features.hooks` setting, both on 0.162.1 and the bundled 0.158.0-alpha.2.1.
775
+ A session override of `features.hooks=false` reported false. No feature-enable
776
+ flag is needed on these versions, and brindle leaves explicit disablement alone.
777
+
683
778
  **Antigravity.** agy's hook can deny but its "allow" is ignored (an agy bug),
684
779
  and it has no "no opinion" answer. So the hook denies what the policy denies,
685
780
  answers "allow" for a file inside the workspace (which agy reads and writes
@@ -696,6 +791,21 @@ does it by hand. To undo, turn `permission_policy` off and run `sync-agy`: brind
696
791
  removes exactly its own entries. brindle doesn't learn from agy approvals (its
697
792
  hook runs for every tool call, so a tool running doesn't mean you approved it).
698
793
 
794
+ With `"agy_approvals": "brindle"` (and the policy on), brindle's hook is the
795
+ only gate instead: agy workers start with `--dangerously-skip-permissions`,
796
+ under which agy still honors a hook's deny, and the hook runs on every tool,
797
+ subagents' calls included. It allows what a rule allows (a repo's checks
798
+ included), the shell commands in the profile's `allowed_tools` (what a Claude
799
+ Code worker on it runs without asking), files inside the workspace given as one
800
+ absolute path (writes under `.agents/`, `.git/` and
801
+ `.brindle/` only by a rule, since those hold the hook, git's hooks and the
802
+ repo's checks), brindle's own tools and agy's tools that act only on the
803
+ conversation, and denies everything else, telling the agent to
804
+ ask its supervisor; you allow it with `brindle permissions allow`. Any failure
805
+ in the hook is a deny, and a tool brindle doesn't know is denied. Nobody can
806
+ approve a prompt in the agent's pane in this mode. The default, `"prompt"`,
807
+ leaves agy asking as described above.
808
+
699
809
  ## brindle Pro and Team
700
810
 
701
811
  brindle is complete on its own. brindle Pro adds hosted learning (which profile
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "brindle"
3
- version = "0.3.1"
3
+ version = "0.3.3"
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"