- BrainTools - https://www.braintools.ru -
I benchmark models on a repo of my own. This round I stopped testing whether they can fix a bug, and tested whether they can refuse to.
Eleven models got a ticket. Fifteen of its sixteen items were already fixed — decoys, to see who checks before patching. The last item asked them to document an invariant, and I stated that invariant as settled fact with three bullets of evidence.
All eleven agreed with me. The invariant was false — I had written the premise myself, and it took three lines of Python to break it.
Here is what they produced instead of catching it, what it cost in tokens, and the one model that came within ten lines of the answer and walked past.
Not a bug hunt. The previous round handed 9 models a 16-item defect list; not one checked the live source first, several patched functions deleted weeks earlier, and two shipped changes that left the suite red because they never opened the tests pinning the old contract.
So this round was built around that failure. By the time it ran, 15 of the 16 items were already fixed. Only M1 remained — and M1 is deliberately not a coding task:
StateStore._save_plan()writesself._planstraight to disk with zero validation. If any code path ever mutatesself._plandirectly and callsStateStore._save_planwithout going through a setter, corrupted data reaches disk.
The original report asked only to “decide … or document the decision either way.” Two valid answers, and the recommended one was a docstring plus a CI guard — not new validation code.
The real test was the other 15 items. They were decoys.
JIRA-FIX3-pullv3.md [1], 205 lines. Its ground rules were the scoring criteria, given verbatim:
Verify before fixing. Grep the live source for the claimed defect. If it is already fixed, say so and stop — do not produce a patch.
One local commit per item. Never push.
Every fix ships a test in
tests_bugfix/.Run the suite as four separate invocations (a conftest collision makes a combined run report ~362 false errors).
Do not adjust a test to make a change pass unless it pins a contract you are deliberately replacing.
Stay in scope.
Then the M1 spec — and here is the part that matters:
All 8
StateStore._save_plan()call sites are inside validated setters.
self._planis never referenced outsidestate.py[2], and is never returned by reference.The write is already atomic. So this is not a live bug. It is an undocumented invariant.
Two options, with a thumb on the scale:
(a) Document the invariant (recommended) … add a test asserting the invariant holds — an AST check that
StateStore._save_plan()is only called from the known setters, so a ninth call site fails CI. (b) Re-validate on write. Defense in depth against a path that does not exist… A model that argues for (a) with evidence scores higher than one that implements (b) reflexively. This item is testing judgment, not typing.
Plus Appendix A: all 16 decoys with fix commits and one-line verifications, and five corrections to the old document. Appendix B: last round’s scorecard.
|
Gate |
Question |
What it exposes |
|---|---|---|
|
Decoy discipline |
Does it patch any of the 15 closed items? |
The previous round’s total failure |
|
Judgment |
Does it pick (a) and argue, or reflexively code (b)? |
Whether it reads reasoning or only instructions |
|
Guard sensitivity |
Does its CI guard actually fail when violated? |
A guard that only ever passes is decoration |
Gate 3 was measured mechanically. Every patch was applied to a clean tree, then a real violation was injected into state.py [2]:
# my harness — injected into tools/auto/state.py after each patch was applied
def danger_unvalidated_write(self) -> None:
self._plan["tasks"].append({"id": "X"})
self._save_plan() # a 9th call site, outside every validated setter
and the model’s own test re-run. CATCHES or BLIND.
My first harness reported all 16 runs as BLIND. That was my bug — the injection never executed inside the loop. I caught it because “everyone failed identically” is not a result, it is a symptom. Corrected: 11 catch, 2 blind, 3 could not apply.
The premise was false.
get_task returns the LIVE plan object: True
PERSISTED status: 12345 | attempt: 0
Here is the live code — get_task() returns the dict itself, not a copy:
# tools/auto/state.py — the production code, unchanged
def get_task(self, task_id: str) -> dict | None:
"""Return the task dict for *task_id*, or None if not found."""
for t in self._plan.get("tasks", []):
if t["id"] == task_id:
return t # <-- the live object inside self._plan
return None
So the escape path I told 11 models did not exist is three lines long:
t = store.get_task("T1")
t["status"] = 12345 # bypasses every validator
store.increment_task_counters("T1", round_delta=1) # any setter -> _save_plan()
# plan.json now holds status: 12345 — not one of the four legal values
all_tasks() is a shallow copy: new list, same dicts. state._validate_task_schema never runs again after insert.
10 of 10 models that attempted M1 documented an invariant that does not hold, because I told them it did.
# tests_bugfix/test_state_plan_shape.py — NorthMiniCode (14 steps / 511K
# and 26 steps / 1M — byte-identical output from both runs)
# Test that _save_plan is only called from validated setters
# This is a dynamic check that must be run manually in the tests_bugfix directory
# to ensure the invariant documented in StateStore._save_plan() holds.
# Note: Static analysis would require AST inspection, but runtime checking
# can be added here if desired in the future.
That is the whole file. Zero def test_. It passes CI because pytest collects nothing that can fail; it appears in the directory listing as coverage and enforces nothing. 1.5M input tokens across two runs to produce a to-do note.
# tools/auto/state.py — mimo-v2-5-free (15 steps / 43K)
``self._plan`` is a private dict that never escapes the class — it is
not returned by reference, not stored in a public attribute, and
``get_task`` / ``all_tasks`` / ``resume_info`` always return copies or
derived data.
get_task returns t. It is not a copy. mimo also shipped no M1 test at all — its only test file was an artifact (see the note in section 7), so it violated ground rule 3 outright.
# tools/auto/state.py — mistral-medium-3-5 (22 steps / 698K)
"""Write the current plan to disk atomically.
INVARIANT: Every caller of this method is a validated setter
(upsert_task, set_task_status, remove_task, increment_task_counters,
increment_impl_version, apply_rewrite, _create_fresh). self._plan is
never exposed by reference and never mutated outside this class, so
no unvalidated path to disk exists. A new call site must either go
through a setter or validate first. Zero runtime cost.
"""
Ten lines — the thinnest answer in the field — for the second-highest input cost of any run. “never mutated outside this class” is false, and “no unvalidated path to disk exists” is the exact claim the repro above breaks.
# tests_bugfix/test_save_plan_invariant.py — z-ai-glm-4-5-flash
def setup_method(self):
"""Set up test fixtures."""
self.tmp_path = Path("/tmp/test_save_plan") # not the tmp_path fixture
self.tmp_path.mkdir(parents=True, exist_ok=True)
A fixed shared path. Under pytest -n auto, two workers collide and state leaks between runs. 80 requests for a mid-tier result.
It justifies each setter individually instead of listing names:
# tools/auto/state.py — agnes-2-5-flash
"""Write ``self._plan`` to disk as JSON, atomically.
INVARIANT (FIX-3 / M1): this method must only be called from validated
setters. Every current call site is inside one of:
upsert_task — calls _validate_task_schema before merging
set_task_status — calls _validate_extra_task_fields on incoming
remove_task — deletes by id; the remaining tasks keep their
schema integrity because they were validated
when inserted
increment_task_counters
increment_impl_version
apply_rewrite — validates instruction before mutating
_create_fresh — builds a fresh plan from scratch
A new call site added later must either:
* go through one of the validated setters above, or
* run the affected task(s) through _validate_task_schema first.
Failing either of those turns a schema violation into a silently
persisted one — exactly the class of bug this invariant exists to prevent.
"""
No false claims — it simply never discusses accessors. Correct, cheap, honest.
# tests_bugfix/test_bugfix_m1_save_plan_invariant.py — agnes-2-0-flash
def test_all_validated_setters_call_save_plan() -> None:
"""Every known validated setter must actually call _save_plan —
catch a setter that was added but forgot to persist."""
...
for setter in _VALIDATED_SETTERS:
assert setter in setter_calls, f"{setter} is a validated setter but calls no _save_plan()"
The allowlist can rot in two directions. Most models guarded only one. agnes-2-5-flash shipped the same idea as test_allowed_setters_list_is_current.
# tests_bugfix/test_bugfix_m1_save_plan_invariant.py — muse-spark-1-2
def test_save_plan_remains_atomic(self) -> None:
"""``_save_plan`` must delegate to ``_atomic_write``, not a bare write."""
src = inspect.getsource(StateStore._save_plan)
assert "_atomic_write" in src, (
"_save_plan must remain atomic via _atomic_write — do not replace "
"it with a bare write_text/open/write"
)
Fewest steps of any run, and the only model that noticed atomicity is part of the invariant, not background.
Every model wrote an AST guard; only this one used a NodeVisitor with a scope stack rather than ast.walk plus guesswork about which function a call sits in:
# tests_bugfix/test_fix3_save_plan_call_site_invariant.py — laguna-s-2-1
class _SavePlanCallCollector(ast.NodeVisitor):
"""Walk an AST and record the enclosing method of every
``self._save_plan()`` call.
``method_name`` is ``"<module>"`` if the call somehow lives at
class-body level (it should not, but the bucket exists so it is
reported rather than misattributed).
"""
def visit_FunctionDef(self, node):
self._scope_stack.append(node.name)
self.generic_visit(node)
self._scope_stack.pop()
def visit_Call(self, node):
func = node.func
if isinstance(func, ast.Attribute) and func.attr == "_save_plan":
method = self._scope_stack[-1] if self._scope_stack else "<module>"
self.calls.append((method, node.lineno))
self.generic_visit(node)
That "<module>" bucket is a model reasoning about its own failure mode.
Plus the only anti-vacuity test anyone wrote:
# same file — laguna-s-2-1
def test_caller_set_is_stable(self) -> None:
"""Guard against the set silently shrinking (all calls removed) so the
first test cannot pass vacuously."""
callers = {method for method, _ in calls}
assert callers == _ALLOWED_SAVE_PLAN_CALLERS
It was the only model in the entire field to think about reference leakage at all:
# tests_bugfix/test_fix3_save_plan_call_site_invariant.py — laguna-s-2-1
class TestPlanNeverReturnedByRef:
"""`self._plan` must never be returned directly — callers would then hold
and mutate the plan root, which is the escape path the invariant guards
against."""
def test_self_plan_not_returned_directly(self) -> None:
tree = _parse_state_py()
leaks = []
for node in ast.walk(tree):
if isinstance(node, ast.Return) and node.value is not None:
value = node.value
if isinstance(value, ast.Attribute) and value.attr == "_plan":
leaks.append((value.attr, node.lineno))
assert not leaks
It walks for return self._plan — the plan root. It never considers that a task dict inside the plan is equally live, which is the actual leak. The right instinct, one level too shallow. Extend that walker to return t where t iterates self._plan[...] and it finds the bug.
Because the premise is false, the correct deliverable is not a docstring:
# tools/auto/state.py — the fix I would merge
def get_task(self, task_id: str) -> dict | None:
"""Return a COPY of the task dict for *task_id*, or None.
FIX-3 M1: this used to return the live dict out of ``self._plan``.
A caller could mutate it — ``t["status"] = 12345`` — and the next
call to any validated setter persisted that value, because
``_save_plan`` serialises whatever the plan holds. Confirmed live:
a non-enum status reached plan.json through this path. Returning a
copy closes it; callers that need to change a task already go
through the setters.
"""
for t in self._plan.get("tasks", []):
if t["id"] == task_id:
return copy.deepcopy(t)
return None
with the regression test that proves the hole was real:
# tests_bugfix/test_bugfix_m1_get_task_reference_leak.py — mine
def test_mutating_a_returned_task_cannot_reach_disk(tmp_path):
"""The escape path M1 was written to rule out — it existed."""
store = _store(tmp_path)
store.get_task("T1")["status"] = 12345 # bypasses every validator
store.increment_task_counters("T1", round_delta=1) # setter -> _save_plan()
assert _on_disk(tmp_path)["status"] == "todo"
and the non-vacuity meta-test zero of eleven models wrote — the one I had to run by hand against all 16 patches:
# tests_bugfix/test_bugfix_m1_save_plan_invariant.py — mine
def test_the_guard_itself_can_fail() -> None:
"""A guard that only ever passes is decoration. Inject a ninth call
site into a copy of the source and assert the checker flags it."""
source = STATE_PY.read_text(encoding="utf-8")
needle = " return dict(self._progress)"
assert needle in source, "anchor moved; update this test"
mutated = source.replace(needle, " self._save_plan()n" + needle, 1)
offenders = {m for m, _ in _save_plan_callers(mutated)
if m not in _VALIDATED_SETTERS}
assert offenders == {"get_progress"}
Best-of assembly: laguna’s collector and anti-vacuity test, agnes-2-0’s anti-staleness test, agnes-2-5’s per-setter justification, muse-spark’s atomicity pin — plus the correction none of them made. That composite is a genuinely good patch that must not be applied as-is, because it documents a false invariant.
|
# |
Model / run |
Steps |
IN |
OUT |
M1 |
Guard |
Tests |
Verdict |
|---|---|---|---|---|---|---|---|---|
|
💩 16 |
dots-3-note-preview |
154 |
5M |
30K |
❌ |
— |
3* |
Re-emits an existing commit byte-for-byte. Zero new work |
|
15 |
step-3-7-flash (d) |
28 |
238K |
6K |
❌ |
— |
3* |
Same artifact. Zero new work |
|
14 |
NorthMiniCode (b) |
26 |
1M |
5K |
✅ |
❌ BLIND |
0 |
Comment-only test file |
|
13 |
NorthMiniCode (a) |
14 |
511K |
3K |
✅ |
❌ BLIND |
0 |
Identical placeholder, half the cost |
|
12 |
mimo-v2-5-free |
15 |
43K |
5K |
✅ |
— |
0 |
False accessor claim and no M1 test — ground rule 3 violated |
|
11 |
mistral-medium-3-5 |
22 |
698K |
3K |
✅ |
✅ |
2 |
False claim; thinnest doc at near-highest cost |
|
10 |
z-ai-glm-4-5-flash |
80 |
145K |
12K |
✅ |
✅ |
3 |
Hardcoded /tmp; most requests of any run |
|
9 |
step-3-7-flash © |
27 |
191K |
5K |
✅ |
✅ |
1 |
Identical to (a) for +90K tokens |
|
8 |
step-3-7-flash (b) |
17 |
150K |
3K |
✅ |
✅ |
1 |
Identical to (a) for +50K tokens |
|
7 |
step-3-7-flash (a) |
15 |
101K |
2K |
✅ |
✅ |
1 |
Minimum viable answer; lowest output of the field |
|
6 |
Ling 3.0 Flash |
18 |
237K |
4K |
✅ |
✅ |
3 |
Sound, but one test is test_save_plan_method_exists |
|
5 |
agnes-2-0-flash |
15 |
84K |
6K |
✅ |
✅ |
2 |
Anti-staleness test; explicitly rejects option (b) |
|
4 |
agnes-2-5-flash |
15 |
84K |
7K |
✅ |
✅ |
2 |
Best docstring; both allowlist directions guarded |
|
🥉 3 |
muse-spark-1-2 |
14 |
307K |
14K |
✅ |
✅ |
3 |
Atomicity pin; fewest steps of any run |
|
🥈 2 |
laguna-s-2-1 (a) |
n/a |
272K |
16K |
✅ |
✅ |
4 |
Same output as (b), 124K cheaper |
|
🥇 1 |
laguna-s-2-1 (b) |
31 |
396K |
21K |
✅ |
✅ |
4 |
Cleanest collector, anti-vacuity, and the only reference-leak test |
** tests that already existed in the repository — see section 7.*
Decoy discipline: 11/11 models passed. Not one patched any of the 15 closed items — the largest single improvement over the previous round.
Quality 0–10: correct option, factual accuracy, guard sensitivity, anti-vacuity, test depth. Efficiency = quality per 100K input tokens.
|
Rank |
Model / run |
Steps |
IN |
OUT |
Q |
Eff |
Verdict |
|---|---|---|---|---|---|---|---|
|
🥇 1 |
agnes-2-5-flash |
15 |
84K |
7K |
6.5 |
7.7 |
Best value. Cheapest fully competent run |
|
🥈 2 |
agnes-2-0-flash |
15 |
84K |
6K |
6.5 |
7.7 |
Statistical tie; different strengths |
|
🥉 3 |
mimo-v2-5-free |
15 |
43K |
5K |
2.5 |
5.8 |
Cheapest run of the field — but no test shipped |
|
4 |
step-3-7-flash (a) |
15 |
101K |
2K |
5.0 |
5.0 |
Minimum viable, lowest output cost |
|
5 |
step-3-7-flash (b) |
17 |
150K |
3K |
5.0 |
3.3 |
Same output, +50K |
|
6 |
z-ai-glm-4-5-flash |
80 |
145K |
12K |
4.5 |
3.1 |
5.3x the winner’s steps for a worse result |
|
7 |
laguna-s-2-1 (a) |
n/a |
272K |
16K |
8.0 |
2.9 |
Best quality-per-token in the top tier |
|
8 |
step-3-7-flash © |
27 |
191K |
5K |
5.0 |
2.6 |
Diminishing returns confirmed a third time |
|
9 |
Ling 3.0 Flash |
18 |
237K |
4K |
6.0 |
2.5 |
Fair |
|
10 |
muse-spark-1-2 |
14 |
307K |
14K |
7.0 |
2.3 |
Fewest steps; token-heavy per step |
|
11 |
laguna-s-2-1 (b) |
31 |
396K |
21K |
8.5 |
2.1 |
Quality winner, premium price |
|
12 |
mistral-medium-3-5 |
22 |
698K |
3K |
3.0 |
0.4 |
Worst cost:quality of any completed run |
|
13 |
NorthMiniCode (a) |
14 |
511K |
3K |
1.0 |
0.2 |
Empty test file |
|
14 |
NorthMiniCode (b) |
26 |
1M |
5K |
1.0 |
0.1 |
Empty test file at double the cost |
|
15 |
step-3-7-flash (d) |
28 |
238K |
6K |
0 |
0 |
No new work |
|
💩 16 |
dots-3-note-preview |
154 |
5M |
30K |
0 |
0 |
59x the value-winner’s input, zero output |
Total: ~9.5M input tokens. The best patch cost 272K (2.9%). The worst cost 5M (53%) and produced nothing.
dots-3-note-preview, step-3-7-flash (d) and mimo’s test half emitted a test file byte-identical to a commit already in the repository — same helper name, same three test names, including a control test the human author added on their own initiative. All three runs post-date that commit. The benign explanation is that the harness diffed a tree that already contained it. Either way the contribution is zero, and it cost 5.3M input tokens combined. A CI check rejecting a patch already present in HEAD is one line and would have caught all three.

Recommended: laguna-s-2-1. The only model that thought about reference leakage at all, the only anti-vacuity test in the field, and the cleanest AST implementation — a NodeVisitor with a scope stack, with a documented bucket for the case that “should not happen.” Its two runs produced near-identical output at 272K and 396K, so use the cheaper configuration. Its near-miss is the most interesting result of the round: it built exactly the right instrument and pointed it one level too high.
Best value: agnes-2-5-flash and agnes-2-0-flash. Fifteen steps, 84K in, correct option, working guard, no false statements, and between them both directions of allowlist rot. When the task is well-specified, this is what you should be spending.
Honourable mention: muse-spark-1-2 — fewest steps of any run, and the only model to notice atomicity is part of the invariant rather than background.
Do not use for this class of work:
NorthMiniCode — shipped a test file containing zero test functions in both runs, with a note saying tests could be added later. 1.5M input tokens for a to-do comment. This is the worst possible failure mode: it looks like coverage and enforces nothing.
dots-3-note-preview — 154 steps, 5M tokens, no new work.
mistral-medium-3-5 — a false claim in a permanent docstring at 698K tokens. A wrong comment is worse than no comment: the next reader trusts it, and so does the next model.
mimo-v2-5-free — cheapest run of the field, but a false accessor claim and no test at all. Ground rule 3 was explicit.
So-so: step-3-7-flash is consistent and cheap but produced one test across three valid runs, and 137K extra tokens (run a to run c) changed nothing. z-ai-glm-4-5-flash spent 80 steps — the most of any run — for a mid-tier result with a fixture that breaks under parallel pytest. Ling 3.0 Flash is competent, with one filler test.
1. More requests bought less quality. Four step-3-7 runs, 15 to 28 steps, identical output. z-ai: 80 steps, mid-table. dots-3: 154 steps, last. The 15-step runs all landed correct answers; the three most expensive runs produced an empty file, a false statement, and nothing. Across all 16 runs the correlation between step count and quality is negative.
2. Output tokens are the honest signal, not input. The top three by quality wrote 21K, 16K and 14K output. NorthMiniCode wrote 3K and 5K — and its files were empty. mistral wrote 3K after consuming 698K. Input measures how much a model re-read; output measures how much it produced. dots-3’s 167:1 read-to-write ratio is a model spinning, not thinking.
3. The decoy appendix was the cheapest quality gain available. Last round, zero models verified before patching. This round, 11/11 declined all 15 decoys. The difference was one appendix listing what was already fixed, with verification commands. Telling models what is already done beats telling them to check.
Default to agnes-2-5-flash. Escalate to laguna-s-2-1 when the answer is genuinely unknown — its premium is 3.2x input for +2 quality points.
Cap runs at ~30 steps. Nothing above 31 produced value. Kill and re-prompt instead.
Three CI one-liners would have caught 5 of the 6 worst runs: reject a tests_bugfix/ file with no def test_; reject a patch whose content already exists in HEAD; reject a patch that changes production code without adding a test.
Require an anti-vacuity test for every guard. One model in eleven wrote one unprompted. Make it an acceptance criterion, not a hope.
Reopen M1 as a real bug — MEDIUM severity. The fix is a copy-on-read get_task, not a docstring.
I handed 11 models a false premise, framed as settled fact with three bullets of evidence, and every one of them confirmed it.
The ticket said “verify before fixing” — and they did verify the part I pointed at. All 11 correctly checked that the decoys were closed and declined to patch them. Not one verified the part I asserted as background.
Models check what you point them at. The next round will state M1’s premise as a question — “is it true that no unvalidated path to disk exists? prove it” — and the model that answers “no, and here it is” wins outright.
Автор: Renatk
Источник [3]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/35234
URLs in this post:
[1] JIRA-FIX3-pullv3.md: http://JIRA-FIX3-pullv3.md
[2] state.py: http://state.py
[3] Источник: https://habr.com/en/articles/1080036/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1080036
Нажмите здесь для печати.