turnloop 0.1.2__tar.gz → 0.1.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 (150) hide show
  1. {turnloop-0.1.2 → turnloop-0.1.3}/PKG-INFO +45 -16
  2. {turnloop-0.1.2 → turnloop-0.1.3}/README.md +969 -940
  3. {turnloop-0.1.2 → turnloop-0.1.3}/pyproject.toml +89 -89
  4. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_providers.py +85 -0
  5. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/__init__.py +3 -3
  6. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/core/messages.py +7 -0
  7. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/providers/gemini.py +10 -2
  8. {turnloop-0.1.2 → turnloop-0.1.3}/.github/workflows/ci.yml +0 -0
  9. {turnloop-0.1.2 → turnloop-0.1.3}/.gitignore +0 -0
  10. {turnloop-0.1.2 → turnloop-0.1.3}/.turnloop/.gitignore +0 -0
  11. {turnloop-0.1.2 → turnloop-0.1.3}/tests/conftest.py +0 -0
  12. {turnloop-0.1.2 → turnloop-0.1.3}/tests/fixtures/anthropic_tool_call.sse +0 -0
  13. {turnloop-0.1.2 → turnloop-0.1.3}/tests/fixtures/glm_tool_call.sse +0 -0
  14. {turnloop-0.1.2 → turnloop-0.1.3}/tests/fixtures/mcp_echo_server.py +0 -0
  15. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_bash.py +0 -0
  16. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_compaction.py +0 -0
  17. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_config.py +0 -0
  18. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_experiments.py +0 -0
  19. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_hooks_mcp_commands.py +0 -0
  20. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_loop.py +0 -0
  21. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_permissions.py +0 -0
  22. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_tools_files.py +0 -0
  23. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_tui.py +0 -0
  24. {turnloop-0.1.2 → turnloop-0.1.3}/tests/test_websearch.py +0 -0
  25. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/__main__.py +0 -0
  26. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/agent/__init__.py +0 -0
  27. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/agent/factory.py +0 -0
  28. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/agent/headless.py +0 -0
  29. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/agent/loop.py +0 -0
  30. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/agent/subagent.py +0 -0
  31. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/agent/system_prompt.py +0 -0
  32. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/cli.py +0 -0
  33. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/commands/__init__.py +0 -0
  34. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/commands/dispatch.py +0 -0
  35. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/commands/loader.py +0 -0
  36. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/config.py +0 -0
  37. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/context/__init__.py +0 -0
  38. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/context/budget.py +0 -0
  39. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/context/compaction.py +0 -0
  40. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/context/memory.py +0 -0
  41. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/core/__init__.py +0 -0
  42. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/core/events.py +0 -0
  43. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/core/ids.py +0 -0
  44. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/core/tokens.py +0 -0
  45. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/diagnostics.py +0 -0
  46. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/errors.py +0 -0
  47. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/__init__.py +0 -0
  48. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/bakeoff.yaml +0 -0
  49. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/ceiling-check.yaml +0 -0
  50. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/context-glm.yaml +0 -0
  51. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/context.yaml +0 -0
  52. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/glm-selfhosted.yaml +0 -0
  53. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/groq-free.yaml +0 -0
  54. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/hard-calibration.yaml +0 -0
  55. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/hard-glm.yaml +0 -0
  56. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/loop.yaml +0 -0
  57. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/nvidia-smoke.yaml +0 -0
  58. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/reliability.yaml +0 -0
  59. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/rewrite-calibration.yaml +0 -0
  60. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/configs/smoke.yaml +0 -0
  61. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/graders.py +0 -0
  62. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/report.py +0 -0
  63. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/runner.py +0 -0
  64. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/default.yaml +0 -0
  65. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/bulky/module_1.py +0 -0
  66. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/bulky/module_2.py +0 -0
  67. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/bulky/module_3.py +0 -0
  68. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/bulky/module_4.py +0 -0
  69. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/bulky/module_5.py +0 -0
  70. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/bulky/module_6.py +0 -0
  71. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/bulky/module_7.py +0 -0
  72. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/bulky/module_8.py +0 -0
  73. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/circular_import/models.py +0 -0
  74. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/circular_import/services.py +0 -0
  75. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/circular_import/test_cancel.py +0 -0
  76. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/cli_flag/app.py +0 -0
  77. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/cross_file_contract/decode.py +0 -0
  78. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/cross_file_contract/encode.py +0 -0
  79. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/cross_file_contract/test_roundtrip.py +0 -0
  80. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/cross_file_contract/test_wire_format.py +0 -0
  81. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/empty/.keep +0 -0
  82. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/failing_test/calc.py +0 -0
  83. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/failing_test/test_calc.py +0 -0
  84. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/generate_bulky.py +0 -0
  85. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/grep_edit/ingest.py +0 -0
  86. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/grep_edit/load.py +0 -0
  87. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/grep_edit/transform.py +0 -0
  88. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/misleading_traceback/checkout.py +0 -0
  89. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/misleading_traceback/invoice.py +0 -0
  90. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/misleading_traceback/pricing.py +0 -0
  91. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/misleading_traceback/test_checkout.py +0 -0
  92. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/misleading_traceback/test_invoice.py +0 -0
  93. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/needle/inventory.py +0 -0
  94. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/needle/pricing.py +0 -0
  95. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/needle/shipping.py +0 -0
  96. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/regression_trap/test_validators.py +0 -0
  97. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/regression_trap/validators.py +0 -0
  98. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/rename/billing.py +0 -0
  99. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/rename/report.py +0 -0
  100. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/rename/test_billing.py +0 -0
  101. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/spec/parser.py +0 -0
  102. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/spec/test_parser.py +0 -0
  103. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/subtle_spec_edge/leaderboard.py +0 -0
  104. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/subtle_spec_edge/test_leaderboard.py +0 -0
  105. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/experiments/suite/fixtures/util/util.py +0 -0
  106. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/hooks/__init__.py +0 -0
  107. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/hooks/runner.py +0 -0
  108. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/mcp/__init__.py +0 -0
  109. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/mcp/adapter.py +0 -0
  110. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/mcp/client.py +0 -0
  111. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/permissions/__init__.py +0 -0
  112. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/permissions/engine.py +0 -0
  113. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/permissions/rules.py +0 -0
  114. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/providers/__init__.py +0 -0
  115. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/providers/anthropic.py +0 -0
  116. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/providers/base.py +0 -0
  117. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/providers/mock.py +0 -0
  118. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/providers/openai_compat.py +0 -0
  119. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/providers/pricing.py +0 -0
  120. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/providers/registry.py +0 -0
  121. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/providers/sse.py +0 -0
  122. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/sessions/__init__.py +0 -0
  123. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/sessions/models.py +0 -0
  124. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/sessions/store.py +0 -0
  125. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/__init__.py +0 -0
  126. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/ask.py +0 -0
  127. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/base.py +0 -0
  128. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/bash.py +0 -0
  129. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/builtin.py +0 -0
  130. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/edit.py +0 -0
  131. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/glob.py +0 -0
  132. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/grep.py +0 -0
  133. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/read.py +0 -0
  134. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/runner.py +0 -0
  135. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/shell.py +0 -0
  136. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/skill.py +0 -0
  137. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/task.py +0 -0
  138. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/textio.py +0 -0
  139. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/todo.py +0 -0
  140. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/webfetch.py +0 -0
  141. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/websearch.py +0 -0
  142. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tools/write.py +0 -0
  143. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tui/__init__.py +0 -0
  144. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tui/app.py +0 -0
  145. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tui/app.tcss +0 -0
  146. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tui/bridge.py +0 -0
  147. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tui/widgets/__init__.py +0 -0
  148. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tui/widgets/permission.py +0 -0
  149. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tui/widgets/status.py +0 -0
  150. {turnloop-0.1.2 → turnloop-0.1.3}/turnloop/tui/widgets/transcript.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: turnloop
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: A local-first agentic coding harness: streaming tool loop, permission gating, context compaction, subagents, and any LLM provider.
5
5
  Project-URL: Homepage, https://github.com/Pranesh-2005/turnloop
6
6
  Author: Pranesh
@@ -49,7 +49,7 @@ tl experiment run smoke # a measurement run, offline and free
49
49
 
50
50
  Four runtime dependencies: `textual`, `pydantic`, `httpx`, `pyyaml`. No vendor
51
51
  SDKs, no agent framework — the loop is the point, so the loop is written here.
52
- ~18,000 lines of Python, 249 tests, no network in the default test run.
52
+ ~18,000 lines of Python, 255 tests, no network in the default test run.
53
53
 
54
54
  ---
55
55
 
@@ -117,7 +117,7 @@ point.
117
117
  Verify:
118
118
 
119
119
  ```bash
120
- tl --version # turnloop 0.1.0
120
+ tl --version # turnloop 0.1.3
121
121
  tl doctor # every provider, key presence, shell, context budget
122
122
  ```
123
123
 
@@ -126,7 +126,7 @@ tl doctor # every provider, key presence, shell, context budge
126
126
  context arithmetic for the active provider:
127
127
 
128
128
  ```
129
- + context budget 32,000 window − 4,096 output − 835 system − 3,365 tools = 23,704 for history
129
+ + context budget 32,000 window - 4,096 output - 881 system - 3,567 tools = 23,456 for history
130
130
  ```
131
131
 
132
132
  ---
@@ -429,6 +429,12 @@ new users`. `gemini-flash-latest` follows Google's own pointer. Worth knowing wh
429
429
  reading a 429 from any provider: the status code says "slow down", the body says
430
430
  whether slowing down will ever help.
431
431
 
432
+ Its free tier is **5 requests per minute**, which an agent loop reaches in one
433
+ ordinary task — a fix-two-bugs-and-write-tests run spent six turns and died on the
434
+ seventh. The retry ladder is correct and still loses, because the window is longer
435
+ than the backoff. Free Gemini is a good way to try the harness and a bad way to do
436
+ sustained work.
437
+
432
438
  ---
433
439
 
434
440
  ## Adding a provider
@@ -825,10 +831,31 @@ suddenly have them exposed.
825
831
  never manifested only because `rg` was not installed on the machine and it had been
826
832
  falling back to a pure-Python walk.
827
833
 
828
- **The lesson, three times over in one project:** an 84-run sweep failing while
829
- printing a tidy report of zeros; a tool returning empty with `ok: true`; a sweep
830
- sitting at 10/18 doing nothing for 25 minutes. Each was caught only by looking past
831
- the summary line.
834
+ **The Gemini adapter passed a live test and was still broken.**
835
+
836
+ Gemini returns a `thoughtSignature` alongside each `functionCall` part. Replay the
837
+ assistant turn without it and the next request is rejected: `400 — Function call is
838
+ missing a thought_signature in functionCall parts`. The adapter decoded the call and
839
+ dropped the signature, so **turn two of every tool conversation failed**.
840
+
841
+ It was verified live before shipping — one text turn, one tool call, both correct —
842
+ and that verification is exactly what hid the bug. A single tool call never replays
843
+ anything. The failure needs a *second* request carrying the first one's output, which
844
+ is the first thing a real agent loop does and the last thing a smoke test does.
845
+
846
+ Found by setting the harness up as a user would and giving it an ordinary task, not by
847
+ testing the adapter. The signature now round-trips through `ToolUseBlock.signature`
848
+ and through session JSONL, so a `--resume`d conversation does not hit the same wall.
849
+
850
+ **0.1.2 shipped with this bug.** It went to PyPI on the strength of the single-turn
851
+ check, and anyone who pointed 0.1.2 at Gemini got one working tool call and then a
852
+ 400. Fixed in 0.1.3.
853
+
854
+ **The lesson, four times over in one project:** an 84-run sweep failing while printing
855
+ a tidy report of zeros; a tool returning empty with `ok: true`; a sweep sitting at
856
+ 10/18 doing nothing for 25 minutes; an adapter passing its live test and failing on
857
+ the turn the test never made. Each was caught only by looking past the summary line —
858
+ and the last one only by using the thing instead of testing it.
832
859
 
833
860
  ---
834
861
 
@@ -862,7 +889,7 @@ one-line edit in a CRLF checkout does not produce a whole-file diff.
862
889
  ## Testing
863
890
 
864
891
  ```bash
865
- pytest # 249 tests, no network
892
+ pytest # 255 tests, no network
866
893
  ruff check turnloop
867
894
  mypy turnloop
868
895
  ```
@@ -871,11 +898,11 @@ mypy turnloop
871
898
  |---|---|---|
872
899
  | `test_permissions.py` | 48 | rule grammar, compound-command splitting, mode enforcement |
873
900
  | `test_experiments.py` | 36 | runner, graders, report, suite invariants, config resolution |
874
- | `test_providers.py` | 31 | adapters against recorded `.sse` fixtures, image block encoding |
901
+ | `test_providers.py` | 35 | adapters vs recorded `.sse`, image blocks, thought signatures |
875
902
  | `test_tools_files.py` | 28 | Read/Write/Edit/Glob/Grep, encodings, newlines, image detection |
876
903
  | `test_loop.py` | 20 | streaming, truncation, iteration cap |
877
904
  | `test_hooks_mcp_commands.py` | 20 | lifecycle hooks, MCP client, slash commands |
878
- | `test_config.py` | 19 | layering, env overrides, capability presets |
905
+ | `test_config.py` | 21 | layering, env overrides, presets, narrow-console guard |
879
906
  | `test_compaction.py` | 16 | three tiers, tool_use/tool_result invariant |
880
907
  | `test_bash.py` | 14 | shell selection, process-tree kill |
881
908
  | `test_websearch.py` | 9 | HTML parsing, backend selection, truncation, failure modes |
@@ -948,11 +975,13 @@ Stated rather than buried:
948
975
  that matters. That trade is deliberate: the default has to work with no key at all.
949
976
  - **The Anthropic adapter is fixture-tested but has never sent a real request.** It is
950
977
  coded against the documented wire format and passes against recorded SSE, which is not
951
- the same as verified. It is now the only one: the Gemini adapter is verified live
952
- (text, tool calls and images on `gemini-flash-latest`), and OpenAI needs no separate
953
- verification because `openai` is `kind="openai_compat"` — the same adapter Groq,
954
- NVIDIA, Ollama, Blaxel and the self-hosted GLM endpoint all run through, making it
955
- the most exercised code path in the repo.
978
+ the same as verified. It is now the only one: Gemini is verified through a real
979
+ multi-turn agent loop on `gemini-flash-latest` — tool calls, edits, file writes and
980
+ images — and OpenAI needs no separate verification because `openai` is
981
+ `kind="openai_compat"`, the same adapter Groq, NVIDIA, Ollama, Blaxel and the
982
+ self-hosted GLM endpoint all run through, making it the most exercised code path in
983
+ the repo. "Verified" here means multi-turn deliberately: a single-turn check on
984
+ Gemini passed while the loop was broken. See [Bugs worth reading about](#bugs-worth-reading-about).
956
985
  - **`subtle_spec_edge` currently passes for nobody** (0/5 on gpt-4o-mini). The rewrite
957
986
  overshot — runs die on collection errors and leave `NotImplementedError`, which is
958
987
  failure-to-produce-working-code, not falling for the decoy. It needs another pass.