brindle 0.3.2__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.2 → brindle-0.3.3}/PKG-INFO +96 -2
  2. {brindle-0.3.2 → brindle-0.3.3}/README.md +95 -1
  3. {brindle-0.3.2 → brindle-0.3.3}/pyproject.toml +1 -1
  4. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/agents.py +184 -44
  5. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/autopilot.py +13 -1
  6. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/ci_client.py +2 -3
  7. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/cli.py +34 -5
  8. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/codex_hook.py +22 -4
  9. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/config.py +5 -1
  10. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/db.py +18 -0
  11. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/doctor.py +24 -0
  12. brindle-0.3.3/src/brindle/launch_health.py +379 -0
  13. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/model_access.py +34 -11
  14. brindle-0.3.3/src/brindle/model_availability.py +504 -0
  15. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/native/runner.py +2 -0
  16. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/permissions.py +7 -5
  17. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/providers.py +98 -14
  18. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/tmux.py +22 -0
  19. {brindle-0.3.2 → brindle-0.3.3}/.gitignore +0 -0
  20. {brindle-0.3.2 → brindle-0.3.3}/LICENSE +0 -0
  21. {brindle-0.3.2 → brindle-0.3.3}/SCHEDULE-A +0 -0
  22. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/__init__.py +0 -0
  23. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/__main__.py +0 -0
  24. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/account.py +0 -0
  25. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/airgap.py +0 -0
  26. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/antigravity.py +0 -0
  27. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/budget.py +0 -0
  28. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/__init__.py +0 -0
  29. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/backend-auditor.md +0 -0
  30. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/developer-codex.md +0 -0
  31. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/developer-heavy.md +0 -0
  32. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/developer-light.md +0 -0
  33. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/developer-local.md +0 -0
  34. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/developer.md +0 -0
  35. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/qa.md +0 -0
  36. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/reviewer-codex.md +0 -0
  37. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/reviewer-local.md +0 -0
  38. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/reviewer.md +0 -0
  39. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/subagent.md +0 -0
  40. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_agents/supervisor.md +0 -0
  41. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_rules/__init__.py +0 -0
  42. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_rules/security/backend.md +0 -0
  43. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_rules/style/minimal-diff.md +0 -0
  44. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/builtin_rules/tests/only.md +0 -0
  45. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/ci_adapters.py +0 -0
  46. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/ci_federation.py +0 -0
  47. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/ci_hosts.py +0 -0
  48. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/codemap.py +0 -0
  49. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/company_identity.py +0 -0
  50. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/company_login.py +0 -0
  51. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/conflicts.py +0 -0
  52. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/cost.py +0 -0
  53. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/cost_estimate.py +0 -0
  54. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/cull.py +0 -0
  55. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/demo.py +0 -0
  56. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/detect.py +0 -0
  57. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/escapes.py +0 -0
  58. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/events.py +0 -0
  59. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/gates.py +0 -0
  60. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/git.py +0 -0
  61. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/guardrails.py +0 -0
  62. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/history.py +0 -0
  63. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/identity_lock.py +0 -0
  64. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/inbox.py +0 -0
  65. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/keys_cmds.py +0 -0
  66. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/keystore.py +0 -0
  67. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/learned_rules.py +0 -0
  68. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/learning.py +0 -0
  69. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/legacy.py +0 -0
  70. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/mcp_server.py +0 -0
  71. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/native/__init__.py +0 -0
  72. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/native/client.py +0 -0
  73. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/native/loop.py +0 -0
  74. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/native/permissions.py +0 -0
  75. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/native/serve.py +0 -0
  76. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/native/tools.py +0 -0
  77. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pane_auth.py +0 -0
  78. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pipeline.py +0 -0
  79. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/plugins.py +0 -0
  80. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/policy.py +0 -0
  81. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pool.py +0 -0
  82. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pricing.py +0 -0
  83. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/__init__.py +0 -0
  84. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/_files.py +0 -0
  85. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/account.py +0 -0
  86. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/audit_chain.py +0 -0
  87. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/audit_export.py +0 -0
  88. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/auth.py +0 -0
  89. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/brief_learning.py +0 -0
  90. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/cost_centers.py +0 -0
  91. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/credentials.py +0 -0
  92. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/features.py +0 -0
  93. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/keys.py +0 -0
  94. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/learning.py +0 -0
  95. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/license.py +0 -0
  96. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/loopback.py +0 -0
  97. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/managed_models.py +0 -0
  98. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/org_profiles.py +0 -0
  99. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/orgkey.py +0 -0
  100. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/rollout.py +0 -0
  101. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/settings_sync.py +0 -0
  102. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/status.py +0 -0
  103. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/team_events.py +0 -0
  104. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/pro/team_policy.py +0 -0
  105. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/procs.py +0 -0
  106. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/profiles.py +0 -0
  107. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/quota.py +0 -0
  108. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/repo_cmds.py +0 -0
  109. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/repos.py +0 -0
  110. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/rewind.py +0 -0
  111. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/rule_checks.py +0 -0
  112. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/savings.py +0 -0
  113. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/scratch.py +0 -0
  114. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/secrets.py +0 -0
  115. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/self_serve.py +0 -0
  116. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/services.py +0 -0
  117. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/sessions.py +0 -0
  118. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/sidebar_follow.py +0 -0
  119. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/signin_pause.py +0 -0
  120. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/status_cache.py +0 -0
  121. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/tasks.py +0 -0
  122. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/usage.py +0 -0
  123. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/view.py +0 -0
  124. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/watch.py +0 -0
  125. {brindle-0.3.2 → brindle-0.3.3}/src/brindle/watch_costs.py +0 -0
  126. {brindle-0.3.2 → 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.2
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.2`). 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
@@ -603,6 +603,21 @@ keep theirs):
603
603
  No tier prefers another provider for its own sake: all-Claude or all-Codex may
604
604
  be what works best for you, and that's for the learner to find out.
605
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
+
606
621
  `developer-light` runs on Claude Haiku at low effort; `developer` on your Claude
607
622
  Code default model; `developer-codex` on Codex; `developer-heavy` on Claude Fable
608
623
  at high effort.
@@ -613,6 +628,53 @@ wins. When every candidate is out, the repo's `default_agent` runs. The reply sa
613
628
  what was picked and why, e.g. `weight medium -> developer (Codex at 93%, skipped developer-codex)`.
614
629
  An `agent_profile` you pass, or a milestone's `profile`, always wins over weight.
615
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
+
616
678
  **Hosted learning (brindle Pro).** With a plan that includes it, brindle learns
617
679
  which of your `learning_candidates` profiles suits which kind of task, and picks
618
680
  one when `assign` gets no profile and no milestone names one (the reply says
@@ -711,6 +773,38 @@ Codex patches are checked file by file: allowed only if every file is. Codex
711
773
  doesn't say which tool run followed which prompt, so brindle doesn't learn from
712
774
  Codex approvals. To undo, delete that entry (or untrust it in Codex's `/hooks`).
713
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
+
714
808
  **Antigravity.** agy's hook can deny but its "allow" is ignored (an agy bug),
715
809
  and it has no "no opinion" answer. So the hook denies what the policy denies,
716
810
  answers "allow" for a file inside the workspace (which agy reads and writes
@@ -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.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
@@ -573,6 +573,21 @@ keep theirs):
573
573
  No tier prefers another provider for its own sake: all-Claude or all-Codex may
574
574
  be what works best for you, and that's for the learner to find out.
575
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
+
576
591
  `developer-light` runs on Claude Haiku at low effort; `developer` on your Claude
577
592
  Code default model; `developer-codex` on Codex; `developer-heavy` on Claude Fable
578
593
  at high effort.
@@ -583,6 +598,53 @@ wins. When every candidate is out, the repo's `default_agent` runs. The reply sa
583
598
  what was picked and why, e.g. `weight medium -> developer (Codex at 93%, skipped developer-codex)`.
584
599
  An `agent_profile` you pass, or a milestone's `profile`, always wins over weight.
585
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
+
586
648
  **Hosted learning (brindle Pro).** With a plan that includes it, brindle learns
587
649
  which of your `learning_candidates` profiles suits which kind of task, and picks
588
650
  one when `assign` gets no profile and no milestone names one (the reply says
@@ -681,6 +743,38 @@ Codex patches are checked file by file: allowed only if every file is. Codex
681
743
  doesn't say which tool run followed which prompt, so brindle doesn't learn from
682
744
  Codex approvals. To undo, delete that entry (or untrust it in Codex's `/hooks`).
683
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
+
684
778
  **Antigravity.** agy's hook can deny but its "allow" is ignored (an agy bug),
685
779
  and it has no "no opinion" answer. So the hook denies what the policy denies,
686
780
  answers "allow" for a file inside the workspace (which agy reads and writes
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "brindle"
3
- version = "0.3.2"
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"