4take 0.2.0__tar.gz → 0.2.2__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 (130) hide show
  1. {4take-0.2.0 → 4take-0.2.2}/AGENTS.md +3 -1
  2. {4take-0.2.0 → 4take-0.2.2}/CHANGELOG.md +47 -0
  3. {4take-0.2.0 → 4take-0.2.2}/CLAUDE.md +2 -1
  4. {4take-0.2.0 → 4take-0.2.2}/PKG-INFO +76 -56
  5. {4take-0.2.0 → 4take-0.2.2}/README.md +74 -54
  6. {4take-0.2.0 → 4take-0.2.2}/SECURITY.md +1 -1
  7. {4take-0.2.0 → 4take-0.2.2}/SKILL.md +1 -1
  8. {4take-0.2.0 → 4take-0.2.2}/evals/README.md +0 -1
  9. {4take-0.2.0 → 4take-0.2.2}/llms.txt +2 -2
  10. {4take-0.2.0 → 4take-0.2.2}/pyproject.toml +1 -1
  11. 4take-0.2.2/src/fourtake/__init__.py +1 -0
  12. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/cli.py +1 -1
  13. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/findings.py +18 -2
  14. {4take-0.2.0 → 4take-0.2.2}/tests/test_findings.py +57 -0
  15. 4take-0.2.0/evals/backfill_duplicates.py +0 -74
  16. 4take-0.2.0/src/fourtake/__init__.py +0 -1
  17. {4take-0.2.0 → 4take-0.2.2}/.gitignore +0 -0
  18. {4take-0.2.0 → 4take-0.2.2}/.pre-commit-config.yaml +0 -0
  19. {4take-0.2.0 → 4take-0.2.2}/CLA.md +0 -0
  20. {4take-0.2.0 → 4take-0.2.2}/CONTRIBUTING.md +0 -0
  21. {4take-0.2.0 → 4take-0.2.2}/LICENSE +0 -0
  22. {4take-0.2.0 → 4take-0.2.2}/POST_MORTEMS.md +0 -0
  23. {4take-0.2.0 → 4take-0.2.2}/ROADMAP.md +0 -0
  24. {4take-0.2.0 → 4take-0.2.2}/docs/adr/0001-use-exponential-backoff-with-10-retries.md +0 -0
  25. {4take-0.2.0 → 4take-0.2.2}/docs/adr/0002-cap-retries-at-four-and-honour-retry-after.md +0 -0
  26. {4take-0.2.0 → 4take-0.2.2}/docs/adr/0003-retry-a-rate-limited-provider-eight-times.md +0 -0
  27. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/async_gather_swallow.py +0 -0
  28. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/auth_jwt_decode.js +0 -0
  29. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/auth_timing_compare.py +0 -0
  30. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/clean_lru.py +0 -0
  31. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/clean_slugify.py +0 -0
  32. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/cmdinjection_backup.py +0 -0
  33. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/cmdinjection_git.js +0 -0
  34. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/divzero_average_latency.py +0 -0
  35. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/leak_db_connection.py +0 -0
  36. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/leak_file_handle.py +0 -0
  37. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/leak_subprocess.py +0 -0
  38. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/none_env_parse.py +0 -0
  39. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/none_json_response.js +0 -0
  40. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/none_lookup_chain.py +0 -0
  41. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/offbyone_pagination.py +0 -0
  42. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/offbyone_retry_window.js +0 -0
  43. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/offbyone_ring_buffer.py +0 -0
  44. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/overflow_backoff.py +0 -0
  45. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/pathtraversal_download.py +0 -0
  46. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/pathtraversal_zip.py +0 -0
  47. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/race_async_cache.py +0 -0
  48. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/race_counter_increment.py +0 -0
  49. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/race_lazy_singleton.py +0 -0
  50. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/sqli_order_by.py +0 -0
  51. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/sqli_user_search.py +0 -0
  52. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/toctou_config_write.py +0 -0
  53. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/toctou_upload_quota.py +0 -0
  54. {4take-0.2.0 → 4take-0.2.2}/evals/corpus/unbounded_memory_tail.py +0 -0
  55. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/cache_key_version/context/profiles/events.py +0 -0
  56. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/cache_key_version/review.diff +0 -0
  57. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/config_key_mismatch/context/app/settings.py +0 -0
  58. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/config_key_mismatch/review.diff +0 -0
  59. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/ctx_clean_locked_by_caller/context/cache/lru.py +0 -0
  60. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/ctx_clean_locked_by_caller/review.diff +0 -0
  61. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/ctx_clean_validated_upstream/context/billing/coupons.py +0 -0
  62. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/ctx_clean_validated_upstream/review.diff +0 -0
  63. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/duplicate_normalizer/context/accounts/identity.py +0 -0
  64. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/duplicate_normalizer/context/accounts/login.py +0 -0
  65. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/duplicate_normalizer/review.diff +0 -0
  66. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/event_renamed/context/src/notifications/subscribers.js +0 -0
  67. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/event_renamed/review.diff +0 -0
  68. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/lock_not_taken/context/limits/counter.py +0 -0
  69. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/lock_not_taken/review.diff +0 -0
  70. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/missing_await/context/web/sessions.py +0 -0
  71. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/missing_await/review.diff +0 -0
  72. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/nested_commit/context/orders/lines.py +0 -0
  73. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/nested_commit/review.diff +0 -0
  74. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/none_return_breaks_caller/context/app/api.py +0 -0
  75. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/none_return_breaks_caller/context/app/mailer.py +0 -0
  76. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/none_return_breaks_caller/review.diff +0 -0
  77. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/shell_helper_injection/context/deploy/git_tools.py +0 -0
  78. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/shell_helper_injection/review.diff +0 -0
  79. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/swapped_positional_args/context/src/billing.js +0 -0
  80. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/swapped_positional_args/review.diff +0 -0
  81. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/tuple_as_bool/context/shop/payments.py +0 -0
  82. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/tuple_as_bool/review.diff +0 -0
  83. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/units_seconds_to_ms/context/jobs/watchdog.py +0 -0
  84. {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/units_seconds_to_ms/review.diff +0 -0
  85. {4take-0.2.0 → 4take-0.2.2}/evals/labels.json +0 -0
  86. {4take-0.2.0 → 4take-0.2.2}/evals/labels_ctx.json +0 -0
  87. {4take-0.2.0 → 4take-0.2.2}/evals/record.py +0 -0
  88. {4take-0.2.0 → 4take-0.2.2}/evals/score.py +0 -0
  89. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/autocontext.py +0 -0
  90. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/cache.py +0 -0
  91. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/client.py +0 -0
  92. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/config.py +0 -0
  93. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/context.py +0 -0
  94. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/discovery.py +0 -0
  95. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/engine.py +0 -0
  96. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/filelock.py +0 -0
  97. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/input_kind.py +0 -0
  98. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/mcp_entry.py +0 -0
  99. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/mcp_server.py +0 -0
  100. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/output.py +0 -0
  101. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/preflight.py +0 -0
  102. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/prompts.py +0 -0
  103. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/rate_limiter.py +0 -0
  104. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/safety.py +0 -0
  105. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/telemetry.py +0 -0
  106. {4take-0.2.0 → 4take-0.2.2}/src/fourtake/tokenizer.py +0 -0
  107. {4take-0.2.0 → 4take-0.2.2}/tests/conftest.py +0 -0
  108. {4take-0.2.0 → 4take-0.2.2}/tests/test_autocontext.py +0 -0
  109. {4take-0.2.0 → 4take-0.2.2}/tests/test_cache.py +0 -0
  110. {4take-0.2.0 → 4take-0.2.2}/tests/test_cli.py +0 -0
  111. {4take-0.2.0 → 4take-0.2.2}/tests/test_client.py +0 -0
  112. {4take-0.2.0 → 4take-0.2.2}/tests/test_config.py +0 -0
  113. {4take-0.2.0 → 4take-0.2.2}/tests/test_context.py +0 -0
  114. {4take-0.2.0 → 4take-0.2.2}/tests/test_council_scheduling.py +0 -0
  115. {4take-0.2.0 → 4take-0.2.2}/tests/test_discovery.py +0 -0
  116. {4take-0.2.0 → 4take-0.2.2}/tests/test_docs.py +0 -0
  117. {4take-0.2.0 → 4take-0.2.2}/tests/test_engine.py +0 -0
  118. {4take-0.2.0 → 4take-0.2.2}/tests/test_evals.py +0 -0
  119. {4take-0.2.0 → 4take-0.2.2}/tests/test_input_kind.py +0 -0
  120. {4take-0.2.0 → 4take-0.2.2}/tests/test_integration.py +0 -0
  121. {4take-0.2.0 → 4take-0.2.2}/tests/test_mcp_server.py +0 -0
  122. {4take-0.2.0 → 4take-0.2.2}/tests/test_output.py +0 -0
  123. {4take-0.2.0 → 4take-0.2.2}/tests/test_output_contract.py +0 -0
  124. {4take-0.2.0 → 4take-0.2.2}/tests/test_preflight.py +0 -0
  125. {4take-0.2.0 → 4take-0.2.2}/tests/test_prompts.py +0 -0
  126. {4take-0.2.0 → 4take-0.2.2}/tests/test_rate_limiter.py +0 -0
  127. {4take-0.2.0 → 4take-0.2.2}/tests/test_safety.py +0 -0
  128. {4take-0.2.0 → 4take-0.2.2}/tests/test_telemetry.py +0 -0
  129. {4take-0.2.0 → 4take-0.2.2}/tests/test_tokenizer.py +0 -0
  130. {4take-0.2.0 → 4take-0.2.2}/tests/test_zero_cost_enforcement.py +0 -0
@@ -118,11 +118,13 @@ usable review; read `agreement` against `summary.distinct_models`.
118
118
 
119
119
  ```json
120
120
  {
121
- "version": "0.2.0",
121
+ "version": "0.2.2",
122
122
  "timestamp": "2026-08-16T14:20:00Z",
123
+ "config": {"models_requested": 12, "min_responses": 4, "timeout_seconds": 600, "strict": false},
123
124
  "summary": {
124
125
  "task_hash": "25d297f773c3",
125
126
  "responses_received": 4,
127
+ "distinct_models": 4,
126
128
  "responses_failed": 0,
127
129
  "total_latency_ms": 3200,
128
130
  "context": {"files": ["src/mailer.py", "src/users.py (lines 40-90)"], "tokens": 812, "dropped": []}
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.2] - 2026-10-26
11
+
12
+ One fix to what the review shows; how findings are grouped and counted is unchanged.
13
+
14
+ ### Changed
15
+ - **The README leads with what the council found, not with what an agent did with it.** On the
16
+ weak-primary test's 50 PRs the council's findings caught 37 bugs and Gemini 3.8 Flash alone 24
17
+ (15 against 2 discordant, exact McNemar p ≈ 0.002, not pre-registered; `docs/benchmark` gives the
18
+ strict figures). The model given those findings caught 26, which is now the reason for
19
+ `report_for_user` rather than the section's headline. The MCP section no longer opens with the
20
+ 20-bug Opus test, which stays in the ROADMAP.
21
+ - The README's JSON example shows `served_model`, `served_provider`, `finish_reason`, `truncated`,
22
+ `duplicate_of`, `config` and `summary.distinct_models`, which the CLI has emitted all along and
23
+ the agent guides tell readers to rely on.
24
+ - `uv.lock` pins the patched PyJWT, certifi, cryptography, mcp and urllib3. It is used by CI and
25
+ `uv sync`; installs from PyPI resolve their own versions and already had the fixed ones.
26
+
27
+ ### Fixed
28
+ - **A consensus entry no longer takes a bare symbol as its title.** Cohere's north-mini-code
29
+ sometimes writes the symbol where the title belongs (`[CRITICAL] INJECTION find_user (def
30
+ find_user)`, 11 of its 75 recorded findings). Among equally severe findings the first in order set
31
+ the title, so in a live review on 26 October all five entries read "find_user", in the CLI and in
32
+ `report_for_user` alike. An entry now takes a title that names the defect when one of its findings
33
+ has one, and otherwise the start of the issue, cut at a word. Grouping and agreement are
34
+ unchanged: `evals/score.py` prints the same report before and after.
35
+
36
+ ## [0.2.1] - 2026-10-26
37
+
38
+ Documentation and packaging only; the review behaves exactly as in 0.2.0.
39
+
40
+ ### Changed
41
+ - **The benchmark comparison says when each side was recorded, and what the models may have
42
+ seen.** The commercial reviewers' comments on Greptile's 50 PRs date from late July 2025;
43
+ 4take's runs from October 2026. The PRs and their bug descriptions have been public since 2025,
44
+ so a model trained later may have seen them. Both favour 4take, and the README, `docs/benchmark`
45
+ and the 0.2.0 release notes now say so.
46
+ - **Claims the measurements do not support are gone.** The agent diagram no longer says the
47
+ council "dismisses hallucinations" and yields "final clean code", `CLAUDE.md` no longer says it
48
+ eliminates single-model hallucinations, and "universal", "seamlessly" and "beautiful" are out of
49
+ the README, `llms.txt`, the CLI help and the PyPI summary.
50
+ - **Two README badges carried other projects' logos** (FastAPI's for FastMCP, OpenAI's for
51
+ OpenRouter). They are replaced by the PyPI version.
52
+
53
+ ### Removed
54
+ - `evals/backfill_duplicates.py`, a one-off migration of recordings made before 0.2.0. It stays in
55
+ the git history.
56
+
10
57
  ## [0.2.0] - 2026-10-26
11
58
 
12
59
  ### Added
@@ -2,7 +2,8 @@
2
2
 
3
3
  ## What is this?
4
4
  4take is a CLI and MCP server that queries 4 independent LLMs in parallel to review code.
5
- You pipe code into it, and it returns multi-model opinions to eliminate single-model hallucinations.
5
+ You pipe code into it, and it returns four models' reviews with their findings grouped by how
6
+ many raised each one.
6
7
 
7
8
  It reviews four axes, not just defects: **correctness**, **security**, **structure**
8
9
  (responsibility in the wrong layer, coupling that spreads one behavioural change across
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: 4take
3
- Version: 0.2.0
4
- Summary: Universal LLM Council CLI — get 4 independent AI opinions on your code in one command
3
+ Version: 0.2.2
4
+ Summary: Code review by a council of four free LLMs on OpenRouter, with findings ranked by how many reviewers raised them
5
5
  Project-URL: Homepage, https://github.com/aleksbuss/4take
6
6
  Project-URL: Repository, https://github.com/aleksbuss/4take
7
7
  Project-URL: Issues, https://github.com/aleksbuss/4take/issues
@@ -40,16 +40,15 @@ Description-Content-Type: text/markdown
40
40
 
41
41
  [![Python](https://img.shields.io/badge/Python-3.10+-3776AB?style=flat&logo=python&logoColor=white)](https://www.python.org/)
42
42
  [![License: BUSL-1.1](https://img.shields.io/badge/License-BUSL--1.1-yellow.svg)](https://github.com/aleksbuss/4take/blob/main/LICENSE)
43
- [![FastMCP](https://img.shields.io/badge/FastMCP-Ready-6366f1?style=flat&logo=fastapi&logoColor=white)](https://modelcontextprotocol.io/)
44
- [![Powered by OpenRouter](https://img.shields.io/badge/API-OpenRouter-000000?style=flat&logo=openai&logoColor=white)](https://openrouter.ai/)
43
+ [![PyPI](https://img.shields.io/pypi/v/4take)](https://pypi.org/project/4take/)
45
44
  [![CI Status](https://github.com/aleksbuss/4take/actions/workflows/ci.yml/badge.svg)](https://github.com/aleksbuss/4take/actions)
46
45
  [![Linted with Ruff](https://img.shields.io/badge/linting-ruff-D7FF64.svg)](https://github.com/astral-sh/ruff)
47
46
 
48
47
  **Multi-Model AI Council for Code Review.**
49
- *Get 4 independent AI opinions on your code via OpenRouter in one command.*
48
+ *Four free models from four vendors review your code in one command, and their findings are ranked by how many raised each one.*
50
49
 
51
50
  On Greptile's own 50-PR bug benchmark, a council of free models found 37 of 50 bugs:
52
- level with Greptile (41), ahead of Copilot and CodeRabbit. [How it was measured](#-against-the-paid-reviewers-greptiles-benchmark)
51
+ level with Greptile (41), ahead of Copilot and CodeRabbit, whose reviews date from July 2025. [How it was measured](#-against-the-paid-reviewers-greptiles-benchmark)
53
52
 
54
53
  [⚡ 30-Second Quickstart](#-30-second-quickstart) •
55
54
  [🤖 AI Agent & MCP Integration](#-ai-coding-agents--mcp-integration) •
@@ -114,33 +113,33 @@ git diff --cached | 4take
114
113
 
115
114
  ## 🤖 AI Coding Agents & MCP Integration
116
115
 
117
- `4take` is built for **AI Coding Agents** (Claude Code, Cursor, Antigravity IDE, Windsurf, Cline, Roo Code, Codex). It provides agents with a native "Council of LLMs" to audit their code in parallel before presenting it to the user.
116
+ `4take` works with AI coding agents (Claude Code, Cursor, Antigravity IDE, Windsurf, Cline, Roo Code, Codex) as an MCP server or a CLI: the agent sends its code to the council and gets the findings back ranked by agreement, to show the user.
118
117
 
119
- Whether the council improves what a given coding agent ships has not been measured. The eval below scores the council against single reviewers on a fixed corpus, and the one test with a frontier model as the primary (20 bugs) found no gain. The [ROADMAP](https://github.com/aleksbuss/4take/blob/main/ROADMAP.md) lists what is planned.
118
+ An agent tends to keep the findings most reviewers agree on and drop the rest, which loses much of what the council catches ([measured](#-read-the-council-yourself)). So the MCP payload opens with every finding and its agreement, for the agent to show you.
120
119
 
121
120
  ```text
122
- ┌─────────────────────────┐
123
- │ AI Coding Agent │
124
- │ (Chairman / Synthesizer)│
125
- └────────────┬────────────┘
126
- │
127
- │ 1. Submits code draft
128
- ▼
129
- ┌────────────────────┐
130
- │ 4take Council │
131
- └──────────┬─────────┘
132
- │
133
- ┌────────────────────────────┼────────────────────────────┐
134
- ▼ ▼ ▼
135
- Model 1 (Nemotron) Model 2 (Laguna) Model 3 (Cohere)
136
- (Flags Race Condition) (Flags Race Condition) (Suggests Type Hint)
137
- └────────────────────────────┬────────────────────────────┘
138
- │ 2. Synthesizes consensus
139
- │ 3. Dismisses hallucinations
140
- ▼
141
- ┌─────────────────────────┐
142
- │ Final Clean Code & PR │
143
- └─────────────────────────┘
121
+ ┌──────────────────────┐
122
+ │ AI coding agent │
123
+ └──────────┬───────────┘
124
+ │ 1. sends its code or diff
125
+ ▼
126
+ ┌──────────────────────┐
127
+ │ 4take council │
128
+ └──────────┬───────────┘
129
+ ┌────────────┬───────┴────┬────────────┐
130
+ ▼ ▼ ▼ ▼
131
+ reviewer 1 reviewer 2 reviewer 3 reviewer 4
132
+ (vendor A) (vendor B) (vendor C) (vendor D)
133
+ └────────────┴───────┬────┴────────────┘
134
+ │ 2. findings grouped and ranked
135
+ ▼ by how many raised each one
136
+ ┌──────────────────────┐
137
+ │ report_for_user │
138
+ │ e.g. "3/4 HIGH ..." │
139
+ └──────────┬───────────┘
140
+ │ 3. the agent shows every line and
141
+ ▼ checks each against the code
142
+ user
144
143
  ```
145
144
 
146
145
  ### Option A: FastMCP Server (`4take-mcp`)
@@ -224,11 +223,13 @@ cat modified_file.py | 4take --format json --accept-risk --quiet
224
223
 
225
224
  ```json
226
225
  {
227
- "version": "0.2.0",
226
+ "version": "0.2.2",
228
227
  "timestamp": "2026-08-16T14:20:00Z",
228
+ "config": {"models_requested": 12, "min_responses": 4, "timeout_seconds": 600, "strict": false},
229
229
  "summary": {
230
230
  "task_hash": "25d297f773c3",
231
231
  "responses_received": 4,
232
+ "distinct_models": 4,
232
233
  "responses_failed": 0,
233
234
  "total_latency_ms": 3200,
234
235
  "context": {"files": ["src/mailer.py", "src/users.py (lines 40-90)"], "tokens": 812, "dropped": []}
@@ -251,12 +252,22 @@ cat modified_file.py | 4take --format json --accept-risk --quiet
251
252
  "responses": [
252
253
  {
253
254
  "model": "openrouter/nvidia/nemotron-3-super-120b-a12b:free",
255
+ "served_model": "nvidia/nemotron-3-super-120b-a12b:free",
256
+ "served_provider": "NVIDIA",
254
257
  "content": "### Code Review\n1. Found SQL Injection on line 8...",
258
+ "finish_reason": "stop",
259
+ "truncated": false,
260
+ "duplicate_of": null,
255
261
  "latency_ms": 1150
256
262
  },
257
263
  {
258
264
  "model": "openrouter/poolside/laguna-s-2.1:free",
265
+ "served_model": "poolside/laguna-s-2.1:free",
266
+ "served_provider": "Poolside",
259
267
  "content": "### Security & Performance\n1. Use parameterized query for user_id...",
268
+ "finish_reason": "stop",
269
+ "truncated": false,
270
+ "duplicate_of": null,
260
271
  "latency_ms": 1420
261
272
  }
262
273
  ],
@@ -299,7 +310,7 @@ council took about 8½ minutes, and 6 ran to the 600 s limit. On the 28 short sn
299
310
  corpus the median was about 50 s, and 27 of 28 councils seated four.
300
311
 
301
312
  ### Ready-to-Use Agent Configuration Files:
302
- - **Universal Agent Instructions**: [`AGENTS.md`](https://github.com/aleksbuss/4take/blob/main/AGENTS.md)
313
+ - **Instructions for any agent**: [`AGENTS.md`](https://github.com/aleksbuss/4take/blob/main/AGENTS.md)
303
314
  - **Claude Code**: [`CLAUDE.md`](https://github.com/aleksbuss/4take/blob/main/CLAUDE.md)
304
315
  - **Antigravity Skill**: [`SKILL.md`](https://github.com/aleksbuss/4take/blob/main/SKILL.md)
305
316
  - **Machine Summary**: [`llms.txt`](https://github.com/aleksbuss/4take/blob/main/llms.txt)
@@ -308,15 +319,15 @@ corpus the median was about 50 s, and 27 of 28 councils seated four.
308
319
 
309
320
  ## ✨ Features
310
321
 
311
- - 🚀 **1-Command Multi-Model Council**: Pipe any code, diff, or file directly into `4take`.
322
+ - 🚀 **One Command**: Pipe a file or a diff into `4take`; four free models review it in parallel.
312
323
  - 🧠 **Auto-Discovery Ranked on Evidence**: Fetches and filters the free models on OpenRouter, then ranks them by the success rate and median latency they have actually delivered on your machine. The name-based heuristic (parameter count, context window) survives only as the cold-start prior for models you have not run yet.
313
324
  - 🔎 **Four Review Axes**: Correctness, security, structure and simplicity — not just defects. Every structural finding has to name its cost, so the breadth does not become a flood of taste.
314
325
  - 🤝 **Consensus Grouping**: Findings several models raised independently are grouped and ranked by how many raised them, so the cross-referencing is done for you rather than left in four parallel reviews.
315
- - 🔄 **Fault-Tolerant Dynamic Rotation**: If a model hits a rate limit (HTTP 429) or fails, the engine seamlessly rotates in replacement candidates from a pre-ranked 12-model pool with exponential backoff.
326
+ - 🔄 **Fault-Tolerant Dynamic Rotation**: A rate-limited model (HTTP 429) is retried with exponential backoff; one that keeps failing gives its seat to the next model in a pre-ranked pool of up to 12.
316
327
  - 🆓 **Free Models Only**: Requests go only to OpenRouter's free (`:free`) models, and a paid model is rejected before anything is sent. Free has limits: roughly 4 to 8 reviews a day on an account that has never been topped up, roughly 80 to 160 once it has ([details](#-openrouter-free-tier--rate-limits)).
317
328
  - 🛡️ **Secret Redaction**: Automatically scrubs API keys, passwords, and authorization tokens before sending payloads over the wire. The code itself still goes to third-party free endpoints ([privacy](#%EF%B8%8F-privacy--security)).
318
329
  - 🚦 **Cross-Process Rate Limiter**: Shared file-locked token bucket ensures concurrent terminal sessions or background agents never trigger rate limit bans.
319
- - 🎨 **Rich Terminal UI & JSON Output**: Beautiful ANSI Markdown rendering in terminal, plus `--format json` for automated scripts & CI pipelines.
330
+ - 🎨 **Terminal and JSON Output**: Markdown rendered in the terminal, or `--format json` for scripts and CI.
320
331
 
321
332
  ---
322
333
 
@@ -324,9 +335,9 @@ corpus the median was about 50 s, and 27 of 28 councils seated four.
324
335
 
325
336
  Greptile published a benchmark of 50 pull requests from five open-source projects (Cal.com,
326
337
  Discourse, Grafana, Keycloak, Sentry), each with one labelled bug, together with the comments five
327
- commercial AI reviewers left on them. 4take reviewed the same 50 diffs using only OpenRouter's free
328
- models (commit 2f07956, 8 October 2026). Its reviews were graded blind, against a rubric and an
329
- analysis written down before the run.
338
+ commercial AI reviewers left on them in late July 2025. Fifteen months later 4take reviewed the
339
+ same 50 diffs using only OpenRouter's free models (commit 2f07956, 8 October 2026). Its reviews were
340
+ graded blind, against a rubric and an analysis written down before the run.
330
341
 
331
342
  | reviewer | benchmark standard, 50 PRs | strict, 42 verified labels |
332
343
  | :--- | ---: | ---: |
@@ -355,6 +366,10 @@ analysis written down before the run.
355
366
  4take. A strong signal, not proof. The pool has moved on since: two of the four most-seated
356
367
  models of that run (apodex-1.1-mini, ling-3.0-flash-sante) have left the free tier, so the same
357
368
  councils cannot be seated again.
369
+ - **Fifteen months apart, on public data.** The commercial reviews were written in July 2025 and
370
+ 4take's in October 2026, and models improved in between: those tools would probably score higher
371
+ today. The PRs and the benchmark's bug descriptions have been public since 2025, so a model trained
372
+ later may have seen them, and nothing here rules that out. Both effects favour 4take.
358
373
  - **Where it is weakest.** Large Java diffs: Keycloak 5 of 10. On two of them, and on one Sentry
359
374
  diff, only one free vendor reasoned through to an answer. A council took a median of six
360
375
  minutes on these diffs and up to the 600 s timeout.
@@ -365,30 +380,30 @@ diffs are other projects' code, so the recordings and the graded packets are not
365
380
 
366
381
  ---
367
382
 
368
- ## 🧪 When the Council Helps, and When It Doesn't
383
+ ## 🧪 Read the Council Yourself
369
384
 
370
- **Agreement is the signal.** On the eval corpus, over three recordings with four reviewers seated,
371
- a finding all four raised held a real, labelled defect 96% of the time; one raised by two or three
372
- held one 60% of the time; one raised by a single reviewer 41%. A single reviewer's finding is a
373
- pointer to check, not a verdict, and four in ten of them are real.
374
-
375
- **Read the council's output; don't only hand it to another model.** A
385
+ **Four free models found more than a commercial model alone.** A
376
386
  [pre-registered test](https://github.com/aleksbuss/4take/blob/main/docs/benchmark/README.md#does-the-council-help-a-weaker-model)
377
- on the same 50 PRs gave Gemini 3.8 Flash each diff alone, and again with the council's output as
378
- notes (three runs each, graded blind):
387
+ on the same 50 PRs, in October 2026, gave Gemini 3.8 Flash (thinking high) each diff alone, and
388
+ again with the council's findings as notes; three runs each, graded blind:
379
389
 
380
- | | benchmark standard, 50 PRs |
390
+ | | bugs found, of 50 |
381
391
  | :--- | ---: |
392
+ | **the council's findings, read directly** | **37** |
382
393
  | Gemini 3.8 Flash alone | 24 |
383
- | Gemini 3.8 Flash with the council's notes | 26 (no difference shown, p = 0.69) |
384
- | the council's output, read directly | 37 |
394
+ | Gemini 3.8 Flash given the council's findings | 26 |
395
+
396
+ The council caught 15 bugs the model missed, and the model 2 that the council missed (exact McNemar
397
+ p ≈ 0.002). This comparison was not pre-registered, and it credits the council's best reviewer per
398
+ PR against the model's majority of three runs. Both sides were recorded in the same month on the
399
+ same diffs, so the time gap of the commercial comparison does not arise here, and any exposure of
400
+ the PRs in training data applies to both.
385
401
 
386
- In an exploratory breakdown, the model took up 3 of the 4 bugs only the council caught that three or
387
- four reviewers shared, and none of the 11 that one or two raised: it kept the consensus and dropped
388
- the long tail with the noise. (The council's row credits its best reviewer per PR against the
389
- model's majority of three runs, which favours the council.) An earlier test with Claude Opus as the
390
- primary model showed no gain either (15 of 20 real bugs from 4take's own history alone, 14 with the
391
- council). 4take does not make a model a better reviewer by being pasted into its context.
402
+ **An agent that reads the findings keeps only part of them.** Given the council's findings as
403
+ notes, the model caught 26, not 37; against the model alone that is no difference shown (p = 0.69).
404
+ In an exploratory breakdown it took up 3 of the 4 bugs only the council caught that three or four
405
+ reviewers shared, and none of the 11 that one or two raised: it kept the consensus and dropped the
406
+ long tail.
392
407
 
393
408
  **So 4take puts every finding in front of you.** Over MCP the user sees only what the agent
394
409
  relays, so the payload opens with `report_for_user`: one line per finding with its agreement
@@ -396,6 +411,11 @@ relays, so the payload opens with `report_for_user`: one line per finding with i
396
411
  act on agreement 3 or more, and to check agreement 1 or 2 against the code instead of dropping it.
397
412
  The CLI prints the same ranking to your terminal.
398
413
 
414
+ **Agreement tells you where to look first.** On the eval corpus, over three recordings with four
415
+ reviewers seated, a finding all four raised held a real, labelled defect 96% of the time; one
416
+ raised by two or three held one 60% of the time; one raised by a single reviewer 41%. A single
417
+ reviewer's finding is a pointer to check, not a verdict, and four in ten of them are real.
418
+
399
419
  ---
400
420
 
401
421
  ## 🔎 What the Council Reviews
@@ -4,16 +4,15 @@
4
4
 
5
5
  [![Python](https://img.shields.io/badge/Python-3.10+-3776AB?style=flat&logo=python&logoColor=white)](https://www.python.org/)
6
6
  [![License: BUSL-1.1](https://img.shields.io/badge/License-BUSL--1.1-yellow.svg)](https://github.com/aleksbuss/4take/blob/main/LICENSE)
7
- [![FastMCP](https://img.shields.io/badge/FastMCP-Ready-6366f1?style=flat&logo=fastapi&logoColor=white)](https://modelcontextprotocol.io/)
8
- [![Powered by OpenRouter](https://img.shields.io/badge/API-OpenRouter-000000?style=flat&logo=openai&logoColor=white)](https://openrouter.ai/)
7
+ [![PyPI](https://img.shields.io/pypi/v/4take)](https://pypi.org/project/4take/)
9
8
  [![CI Status](https://github.com/aleksbuss/4take/actions/workflows/ci.yml/badge.svg)](https://github.com/aleksbuss/4take/actions)
10
9
  [![Linted with Ruff](https://img.shields.io/badge/linting-ruff-D7FF64.svg)](https://github.com/astral-sh/ruff)
11
10
 
12
11
  **Multi-Model AI Council for Code Review.**
13
- *Get 4 independent AI opinions on your code via OpenRouter in one command.*
12
+ *Four free models from four vendors review your code in one command, and their findings are ranked by how many raised each one.*
14
13
 
15
14
  On Greptile's own 50-PR bug benchmark, a council of free models found 37 of 50 bugs:
16
- level with Greptile (41), ahead of Copilot and CodeRabbit. [How it was measured](#-against-the-paid-reviewers-greptiles-benchmark)
15
+ level with Greptile (41), ahead of Copilot and CodeRabbit, whose reviews date from July 2025. [How it was measured](#-against-the-paid-reviewers-greptiles-benchmark)
17
16
 
18
17
  [⚡ 30-Second Quickstart](#-30-second-quickstart) •
19
18
  [🤖 AI Agent & MCP Integration](#-ai-coding-agents--mcp-integration) •
@@ -78,33 +77,33 @@ git diff --cached | 4take
78
77
 
79
78
  ## 🤖 AI Coding Agents & MCP Integration
80
79
 
81
- `4take` is built for **AI Coding Agents** (Claude Code, Cursor, Antigravity IDE, Windsurf, Cline, Roo Code, Codex). It provides agents with a native "Council of LLMs" to audit their code in parallel before presenting it to the user.
80
+ `4take` works with AI coding agents (Claude Code, Cursor, Antigravity IDE, Windsurf, Cline, Roo Code, Codex) as an MCP server or a CLI: the agent sends its code to the council and gets the findings back ranked by agreement, to show the user.
82
81
 
83
- Whether the council improves what a given coding agent ships has not been measured. The eval below scores the council against single reviewers on a fixed corpus, and the one test with a frontier model as the primary (20 bugs) found no gain. The [ROADMAP](https://github.com/aleksbuss/4take/blob/main/ROADMAP.md) lists what is planned.
82
+ An agent tends to keep the findings most reviewers agree on and drop the rest, which loses much of what the council catches ([measured](#-read-the-council-yourself)). So the MCP payload opens with every finding and its agreement, for the agent to show you.
84
83
 
85
84
  ```text
86
- ┌─────────────────────────┐
87
- │ AI Coding Agent │
88
- │ (Chairman / Synthesizer)│
89
- └────────────┬────────────┘
90
- │
91
- │ 1. Submits code draft
92
- ▼
93
- ┌────────────────────┐
94
- │ 4take Council │
95
- └──────────┬─────────┘
96
- │
97
- ┌────────────────────────────┼────────────────────────────┐
98
- ▼ ▼ ▼
99
- Model 1 (Nemotron) Model 2 (Laguna) Model 3 (Cohere)
100
- (Flags Race Condition) (Flags Race Condition) (Suggests Type Hint)
101
- └────────────────────────────┬────────────────────────────┘
102
- │ 2. Synthesizes consensus
103
- │ 3. Dismisses hallucinations
104
- ▼
105
- ┌─────────────────────────┐
106
- │ Final Clean Code & PR │
107
- └─────────────────────────┘
85
+ ┌──────────────────────┐
86
+ │ AI coding agent │
87
+ └──────────┬───────────┘
88
+ │ 1. sends its code or diff
89
+ ▼
90
+ ┌──────────────────────┐
91
+ │ 4take council │
92
+ └──────────┬───────────┘
93
+ ┌────────────┬───────┴────┬────────────┐
94
+ ▼ ▼ ▼ ▼
95
+ reviewer 1 reviewer 2 reviewer 3 reviewer 4
96
+ (vendor A) (vendor B) (vendor C) (vendor D)
97
+ └────────────┴───────┬────┴────────────┘
98
+ │ 2. findings grouped and ranked
99
+ ▼ by how many raised each one
100
+ ┌──────────────────────┐
101
+ │ report_for_user │
102
+ │ e.g. "3/4 HIGH ..." │
103
+ └──────────┬───────────┘
104
+ │ 3. the agent shows every line and
105
+ ▼ checks each against the code
106
+ user
108
107
  ```
109
108
 
110
109
  ### Option A: FastMCP Server (`4take-mcp`)
@@ -188,11 +187,13 @@ cat modified_file.py | 4take --format json --accept-risk --quiet
188
187
 
189
188
  ```json
190
189
  {
191
- "version": "0.2.0",
190
+ "version": "0.2.2",
192
191
  "timestamp": "2026-08-16T14:20:00Z",
192
+ "config": {"models_requested": 12, "min_responses": 4, "timeout_seconds": 600, "strict": false},
193
193
  "summary": {
194
194
  "task_hash": "25d297f773c3",
195
195
  "responses_received": 4,
196
+ "distinct_models": 4,
196
197
  "responses_failed": 0,
197
198
  "total_latency_ms": 3200,
198
199
  "context": {"files": ["src/mailer.py", "src/users.py (lines 40-90)"], "tokens": 812, "dropped": []}
@@ -215,12 +216,22 @@ cat modified_file.py | 4take --format json --accept-risk --quiet
215
216
  "responses": [
216
217
  {
217
218
  "model": "openrouter/nvidia/nemotron-3-super-120b-a12b:free",
219
+ "served_model": "nvidia/nemotron-3-super-120b-a12b:free",
220
+ "served_provider": "NVIDIA",
218
221
  "content": "### Code Review\n1. Found SQL Injection on line 8...",
222
+ "finish_reason": "stop",
223
+ "truncated": false,
224
+ "duplicate_of": null,
219
225
  "latency_ms": 1150
220
226
  },
221
227
  {
222
228
  "model": "openrouter/poolside/laguna-s-2.1:free",
229
+ "served_model": "poolside/laguna-s-2.1:free",
230
+ "served_provider": "Poolside",
223
231
  "content": "### Security & Performance\n1. Use parameterized query for user_id...",
232
+ "finish_reason": "stop",
233
+ "truncated": false,
234
+ "duplicate_of": null,
224
235
  "latency_ms": 1420
225
236
  }
226
237
  ],
@@ -263,7 +274,7 @@ council took about 8½ minutes, and 6 ran to the 600 s limit. On the 28 short sn
263
274
  corpus the median was about 50 s, and 27 of 28 councils seated four.
264
275
 
265
276
  ### Ready-to-Use Agent Configuration Files:
266
- - **Universal Agent Instructions**: [`AGENTS.md`](https://github.com/aleksbuss/4take/blob/main/AGENTS.md)
277
+ - **Instructions for any agent**: [`AGENTS.md`](https://github.com/aleksbuss/4take/blob/main/AGENTS.md)
267
278
  - **Claude Code**: [`CLAUDE.md`](https://github.com/aleksbuss/4take/blob/main/CLAUDE.md)
268
279
  - **Antigravity Skill**: [`SKILL.md`](https://github.com/aleksbuss/4take/blob/main/SKILL.md)
269
280
  - **Machine Summary**: [`llms.txt`](https://github.com/aleksbuss/4take/blob/main/llms.txt)
@@ -272,15 +283,15 @@ corpus the median was about 50 s, and 27 of 28 councils seated four.
272
283
 
273
284
  ## ✨ Features
274
285
 
275
- - 🚀 **1-Command Multi-Model Council**: Pipe any code, diff, or file directly into `4take`.
286
+ - 🚀 **One Command**: Pipe a file or a diff into `4take`; four free models review it in parallel.
276
287
  - 🧠 **Auto-Discovery Ranked on Evidence**: Fetches and filters the free models on OpenRouter, then ranks them by the success rate and median latency they have actually delivered on your machine. The name-based heuristic (parameter count, context window) survives only as the cold-start prior for models you have not run yet.
277
288
  - 🔎 **Four Review Axes**: Correctness, security, structure and simplicity — not just defects. Every structural finding has to name its cost, so the breadth does not become a flood of taste.
278
289
  - 🤝 **Consensus Grouping**: Findings several models raised independently are grouped and ranked by how many raised them, so the cross-referencing is done for you rather than left in four parallel reviews.
279
- - 🔄 **Fault-Tolerant Dynamic Rotation**: If a model hits a rate limit (HTTP 429) or fails, the engine seamlessly rotates in replacement candidates from a pre-ranked 12-model pool with exponential backoff.
290
+ - 🔄 **Fault-Tolerant Dynamic Rotation**: A rate-limited model (HTTP 429) is retried with exponential backoff; one that keeps failing gives its seat to the next model in a pre-ranked pool of up to 12.
280
291
  - 🆓 **Free Models Only**: Requests go only to OpenRouter's free (`:free`) models, and a paid model is rejected before anything is sent. Free has limits: roughly 4 to 8 reviews a day on an account that has never been topped up, roughly 80 to 160 once it has ([details](#-openrouter-free-tier--rate-limits)).
281
292
  - 🛡️ **Secret Redaction**: Automatically scrubs API keys, passwords, and authorization tokens before sending payloads over the wire. The code itself still goes to third-party free endpoints ([privacy](#%EF%B8%8F-privacy--security)).
282
293
  - 🚦 **Cross-Process Rate Limiter**: Shared file-locked token bucket ensures concurrent terminal sessions or background agents never trigger rate limit bans.
283
- - 🎨 **Rich Terminal UI & JSON Output**: Beautiful ANSI Markdown rendering in terminal, plus `--format json` for automated scripts & CI pipelines.
294
+ - 🎨 **Terminal and JSON Output**: Markdown rendered in the terminal, or `--format json` for scripts and CI.
284
295
 
285
296
  ---
286
297
 
@@ -288,9 +299,9 @@ corpus the median was about 50 s, and 27 of 28 councils seated four.
288
299
 
289
300
  Greptile published a benchmark of 50 pull requests from five open-source projects (Cal.com,
290
301
  Discourse, Grafana, Keycloak, Sentry), each with one labelled bug, together with the comments five
291
- commercial AI reviewers left on them. 4take reviewed the same 50 diffs using only OpenRouter's free
292
- models (commit 2f07956, 8 October 2026). Its reviews were graded blind, against a rubric and an
293
- analysis written down before the run.
302
+ commercial AI reviewers left on them in late July 2025. Fifteen months later 4take reviewed the
303
+ same 50 diffs using only OpenRouter's free models (commit 2f07956, 8 October 2026). Its reviews were
304
+ graded blind, against a rubric and an analysis written down before the run.
294
305
 
295
306
  | reviewer | benchmark standard, 50 PRs | strict, 42 verified labels |
296
307
  | :--- | ---: | ---: |
@@ -319,6 +330,10 @@ analysis written down before the run.
319
330
  4take. A strong signal, not proof. The pool has moved on since: two of the four most-seated
320
331
  models of that run (apodex-1.1-mini, ling-3.0-flash-sante) have left the free tier, so the same
321
332
  councils cannot be seated again.
333
+ - **Fifteen months apart, on public data.** The commercial reviews were written in July 2025 and
334
+ 4take's in October 2026, and models improved in between: those tools would probably score higher
335
+ today. The PRs and the benchmark's bug descriptions have been public since 2025, so a model trained
336
+ later may have seen them, and nothing here rules that out. Both effects favour 4take.
322
337
  - **Where it is weakest.** Large Java diffs: Keycloak 5 of 10. On two of them, and on one Sentry
323
338
  diff, only one free vendor reasoned through to an answer. A council took a median of six
324
339
  minutes on these diffs and up to the 600 s timeout.
@@ -329,30 +344,30 @@ diffs are other projects' code, so the recordings and the graded packets are not
329
344
 
330
345
  ---
331
346
 
332
- ## 🧪 When the Council Helps, and When It Doesn't
347
+ ## 🧪 Read the Council Yourself
333
348
 
334
- **Agreement is the signal.** On the eval corpus, over three recordings with four reviewers seated,
335
- a finding all four raised held a real, labelled defect 96% of the time; one raised by two or three
336
- held one 60% of the time; one raised by a single reviewer 41%. A single reviewer's finding is a
337
- pointer to check, not a verdict, and four in ten of them are real.
338
-
339
- **Read the council's output; don't only hand it to another model.** A
349
+ **Four free models found more than a commercial model alone.** A
340
350
  [pre-registered test](https://github.com/aleksbuss/4take/blob/main/docs/benchmark/README.md#does-the-council-help-a-weaker-model)
341
- on the same 50 PRs gave Gemini 3.8 Flash each diff alone, and again with the council's output as
342
- notes (three runs each, graded blind):
351
+ on the same 50 PRs, in October 2026, gave Gemini 3.8 Flash (thinking high) each diff alone, and
352
+ again with the council's findings as notes; three runs each, graded blind:
343
353
 
344
- | | benchmark standard, 50 PRs |
354
+ | | bugs found, of 50 |
345
355
  | :--- | ---: |
356
+ | **the council's findings, read directly** | **37** |
346
357
  | Gemini 3.8 Flash alone | 24 |
347
- | Gemini 3.8 Flash with the council's notes | 26 (no difference shown, p = 0.69) |
348
- | the council's output, read directly | 37 |
358
+ | Gemini 3.8 Flash given the council's findings | 26 |
359
+
360
+ The council caught 15 bugs the model missed, and the model 2 that the council missed (exact McNemar
361
+ p ≈ 0.002). This comparison was not pre-registered, and it credits the council's best reviewer per
362
+ PR against the model's majority of three runs. Both sides were recorded in the same month on the
363
+ same diffs, so the time gap of the commercial comparison does not arise here, and any exposure of
364
+ the PRs in training data applies to both.
349
365
 
350
- In an exploratory breakdown, the model took up 3 of the 4 bugs only the council caught that three or
351
- four reviewers shared, and none of the 11 that one or two raised: it kept the consensus and dropped
352
- the long tail with the noise. (The council's row credits its best reviewer per PR against the
353
- model's majority of three runs, which favours the council.) An earlier test with Claude Opus as the
354
- primary model showed no gain either (15 of 20 real bugs from 4take's own history alone, 14 with the
355
- council). 4take does not make a model a better reviewer by being pasted into its context.
366
+ **An agent that reads the findings keeps only part of them.** Given the council's findings as
367
+ notes, the model caught 26, not 37; against the model alone that is no difference shown (p = 0.69).
368
+ In an exploratory breakdown it took up 3 of the 4 bugs only the council caught that three or four
369
+ reviewers shared, and none of the 11 that one or two raised: it kept the consensus and dropped the
370
+ long tail.
356
371
 
357
372
  **So 4take puts every finding in front of you.** Over MCP the user sees only what the agent
358
373
  relays, so the payload opens with `report_for_user`: one line per finding with its agreement
@@ -360,6 +375,11 @@ relays, so the payload opens with `report_for_user`: one line per finding with i
360
375
  act on agreement 3 or more, and to check agreement 1 or 2 against the code instead of dropping it.
361
376
  The CLI prints the same ranking to your terminal.
362
377
 
378
+ **Agreement tells you where to look first.** On the eval corpus, over three recordings with four
379
+ reviewers seated, a finding all four raised held a real, labelled defect 96% of the time; one
380
+ raised by two or three held one 60% of the time; one raised by a single reviewer 41%. A single
381
+ reviewer's finding is a pointer to check, not a verdict, and four in ten of them are real.
382
+
363
383
  ---
364
384
 
365
385
  ## 🔎 What the Council Reviews
@@ -8,7 +8,7 @@ Only the latest release receives security fixes. Today that is 0.2.x.
8
8
 
9
9
  Please do not open a public issue for a vulnerability.
10
10
 
11
- - Use GitHub's **Report a vulnerability** button (Security tab, once the repository is public), or
11
+ - Use GitHub's **Report a vulnerability** button on the Security tab, or
12
12
  - write to **takelicensing@gmail.com** with the subject `4take security`.
13
13
 
14
14
  Include the output of `4take --version`, the steps to reproduce, and what an attacker gains. A
@@ -7,7 +7,7 @@ description: Get multiple independent AI opinions on your code using the 4take C
7
7
  ## When to use
8
8
  - After writing a new feature or complex algorithm
9
9
  - Before committing critical security, database, or async logic
10
- - When you want multiple independent AI reviews to catch hallucinations and blindspots
10
+ - When you want several independent AI reviews to catch what one model misses
11
11
  - When code works and you want to know whether its structure will survive the next change
12
12
 
13
13
  Reviews four axes: correctness, security, structure and simplicity. Takes code, a diff or
@@ -19,4 +19,3 @@ carries.
19
19
  | `recordings_ctx/`, `runs_ctx/` | the cross-file corpus recorded without context, with it, and with `--auto-context` |
20
20
  | `record.py` | re-records against live models; the only step that uses the network |
21
21
  | `score.py` | scores recordings offline |
22
- | `backfill_duplicates.py` | one-off migration that restored `duplicate_of` in older recordings |
@@ -1,6 +1,6 @@
1
- # 4take — Universal LLM Council CLI (OpenRouter)
1
+ # 4take — LLM council for code review (OpenRouter free models)
2
2
 
3
- > Universal CLI and FastMCP server to query 4 independent LLMs in parallel via OpenRouter for code review, with findings grouped by how many reviewers raised them. Source-available under the Business Source License 1.1: free for individuals, solo developers and volunteer projects; organizations need a commercial license.
3
+ > CLI and MCP server that sends code to 4 free LLMs from different vendors via OpenRouter for review, with findings grouped by how many reviewers raised them. Source-available under the Business Source License 1.1: free for individuals, solo developers and volunteer projects; organizations need a commercial license.
4
4
 
5
5
  ## Quickstart
6
6
 
@@ -10,7 +10,7 @@ name = "4take"
10
10
  # Single source of truth: `src/fourtake/__init__.py`. Two hand-maintained version strings
11
11
  # drift, and the one that ships on PyPI is not the one `4take --version` prints.
12
12
  dynamic = ["version"]
13
- description = "Universal LLM Council CLI — get 4 independent AI opinions on your code in one command"
13
+ description = "Code review by a council of four free LLMs on OpenRouter, with findings ranked by how many reviewers raised them"
14
14
  readme = "README.md"
15
15
  license = {text = "BUSL-1.1"}
16
16
  requires-python = ">=3.10"
@@ -0,0 +1 @@
1
+ __version__ = "0.2.2"