joule-bench/RESULTS.md
David Charlot 740b6b21d2 Multi-file changes now amortise: all six repeats close at L0
Re-ran the two-pass L4 measurement on `real` after agent-joule cfe83c4.
Every repeat closed at L0 for $0, including both multi-file changes that
previously paid the cloud in full on every pass. The cascade now pays
exactly once per distinct task regardless of how many files the fix
touches, and $/resolved falls from $0.3176 to $0.2223.

The comparison is stated narrowly on purpose. With every task repeated
once and every repeat cached, the cascade makes half the cloud calls a
stateless agent does, so at equal per-call prices it costs exactly half —
by construction, not as a discovery. Against the earlier claude baseline
the gap is only 21%, because this run's per-call prices were higher and
the two figures come from different runs. The structural claim is the
robust one; the percentage is not.

Timing and local energy from this run are explicitly marked invalid. The
host sat at load average 50-87 from unrelated work, and one row reports
90,155 J of local energy for a 25 s run — about 3.5 kW. Tier outcomes and
dollar costs are not affected by CPU contention and are reported; nothing
else from this run should be read.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-17 07:54:27 -04:00

14 KiB
Raw Permalink Blame History

joule-bench results

Re-measured 2026-09-10 on Apple Silicon, after fixing three defects that made every earlier figure in this file wrong. The old numbers and what was wrong with them are kept at the bottom rather than deleted.

Resolution is graded by an executable test oracle: the task's tests must fail before the solver runs and pass after, with pristine test files restored first so a solver cannot pass by rewriting the test. Local energy is host-counter measured, idle-subtracted (idle calibrated per run at 36.9–38.4 W).

The mix suite (and what it is not)

Suite tasks/mix: four mechanical edits (rename, typo, constant, string) plus one novel logic bug. One pass, L4 disabled, so this is purely local.

This suite is not a workload model, and it used to claim to be. It was called a "realistic operation mix" on the grounds that mechanical edits dominate real work. Measured against actual history, they do not. Classifying 1,679 source-touching commits across three real repositories:

                            joulesperbit   open-standards   agent-joule
multi-file                       53.4%           66.0%          62.9%
single-file, large               37.8%           30.3%          27.8%
single-file, small (<=20 lines)   8.8%            3.7%           9.4%
single-file, <=10 lines           3.3%            2.3%             —
strict one-token rename           0.0%            0.0%           0.0%

Not one commit in 1,679 was a single-file consistent one-token rename. On the generous reading — any single-file change under ten lines — real work is 2.3-3.3% of that shape. mix is 80%. So the suite over-represents the cascade's cheapest tier by roughly 25-40x, and a resolution figure from it must not be read as a workload result.

What the measurement cannot see is repetition. Commit history records what changed, never how often someone asked for something similar, and L0 (cache) and L2 (retrieval) exist for exactly that. So this understates the local tiers' real-world reach by an unknown amount — but the correction is unmeasured, and pointing at it is not a defence of the 80%.

What the suite does establish is narrower and still worth having: when work is L1-shaped, it closes deterministically, sub-second, at energy below the measurement floor, and configuring a model does not change that.

solver       resolved  resol.%   note
reference       5/5     100.0%   applies each task's known-good patch — the ceiling
cascade         4/5      80.0%   four closed at L1; the logic bug did not close
noop            0/5       0.0%   touches nothing — the floor

The ceiling and floor are both where they should be, which is the first time that has been true. Per task, with no model configured:

task       tier   wall     resolved
const      L1     1457 ms  yes
greeting   L1      291 ms  yes
rename     L1      346 ms  yes
typo       L1      824 ms  yes
logic       —      415 ms  no

Configuring a local model no longer taxes work that does not need it

The same suite with a 1.6 GB LFM2-2.6B-Q4 wired in as L3:

task       tier   wall     energy      resolved
const      L1      518 ms   0 J~        yes
greeting   L1      557 ms   0 J~        yes
rename     L1      525 ms  17.9 J~      yes
typo       L1      421 ms   0 J~        yes
logic       —   193991 ms  1616 J       no   (L3 attempted, did not close)

The four L1 closes cost the same with a model configured as without one. That is the point of the cascade and it was not true a week ago: before the lazy-load fix these same four tasks cost 52–92 J and 4.7–10.5 s each, all of it spent loading a model none of them touched. Two causes — eager construction, and an L2-index embedding computed after every close, including deterministic ones, which forced the load anyway. Indexing now happens only when the model is already resident.

~ marks runs shorter than the counter's minimum sampling window. Their resolution is valid; their energy is not comparable and the harness says so rather than printing a fabricated number.

The one energy figure here that is properly measurable is the failure: escalating the novel logic bug to a 2.6B local model cost 1.6 kJ over 194 s and did not close it. That is a capability result, not a harness one.

Pay once, free forever — measured

The cascade's headline claim needs a close expensive enough to be worth caching. The local tiers cannot produce one on this suite, so this run enables L4 (the claude CLI) and takes two passes over tasks/mix.

pass task       tier   wall       $         cloud J    local J
1    const      L1        205 ms  $0.0000        0      21.0~
1    greeting   L1        822 ms  $0.0000        0       0.0~
1    logic      L4     24 669 ms  $0.3680   38 823     594.3
1    rename     L1        552 ms  $0.0000        0       0.0~
1    typo       L1        832 ms  $0.0000        0      13.0~
2    const      L0        613 ms  $0.0000        0       0.0~
2    greeting   L0        897 ms  $0.0000        0       7.9~
2    logic      L0        582 ms  $0.0000        0       0.0~
2    rename     L0        814 ms  $0.0000        0      12.2~
2    typo       L0        733 ms  $0.0000        0      15.4~

cascade   10/10   100.0%   $0.0368/resolved   3 882 cloud J/resolved

The novel logic bug cost $0.3680, 24.7 s and 38.8 kJ of (token-estimated) cloud energy the first time. The second time it closed at L0 for $0, no cloud, in 582 ms — 42x faster, with the entire cost eliminated. One cloud call amortises across ten resolutions to $0.0368 each.

This is the first time this result has been produced with a working meter. Note what it is: a demonstration that the cache eliminates the cost of a repeated request, on a suite whose repetition rate is 100% by construction. It is not a claim about how often real work repeats, which this benchmark does not measure.

The frontier, both solvers, same suite and passes

claude (Claude Code 2.1.168) run over the identical suite and two passes:

solver     resolved  resol.%   $/resolved   cloud J/res   local J/res   wall/task
cascade      10/10    100.0%      $0.0368         3 882          66.4   0.2-24.7 s
claude       10/10    100.0%      $0.2809        35 830         220.6    13-29 s

Identical resolution. 7.6x cheaper, 9.2x less cloud energy, 3.3x less local energy. The mechanism is visible per pass rather than inferred: claude pays full price on the second pass exactly as on the first ($0.2688-$0.2791 against $0.2525-$0.3534), because a stateless cloud agent has nothing to amortise. The cascade's entire second pass closed at L0 for $0.

Latency separates them further on the tiers that close locally: L0/L1 closes run 0.2-0.9 s against 13-29 s for a cloud round trip, though the cascade's own L4 escalation costs 24.7 s, which is the same round trip plus local overhead.

One prior belief corrected. It was recorded here that a cloud agent spends "~0 local joules" because its compute happens in the vendor's datacenter. With a working meter it spends 220.6 J per resolved task locally — the CLI process, orchestration and file I/O across a 13-29 s run are not free. The old figure was an artefact of the broken meter, not a property of cloud agents.

What this multiple is and is not

It is a measured result for this suite, which the section above shows is 80% L1-shaped against 2-3% in real commit history, and which repeats every task exactly once. Both properties favour the cascade, by construction. A workload with less repetition and more multi-file work would move the multiple toward 1, and nothing here bounds how far.

The real suite: what the local tiers do on representative work

mix over-represents L1-shaped work by 25-40x, so tasks/real was built to the measured distribution instead: six tasks, three whose fix spans two source files, two single-file rewrites of real logic, one small single-file fix. Ordinary bugs — a sliding window that never prunes, a parser folding operators left to right, a config loader silently accepting misspelled keys, interval merging that assumes sorted input, a retry loop that retries unretryable failures, a page count that drops the last partial page.

The suite is sound: reference 6/6, noop 0/6.

solver     resolved  resol.%
reference     6/6     100.0%
noop          0/6       0.0%
cascade       0/6       0.0%   (local tiers only, no L4)

The local tiers close none of it. Against 4/5 on mix. None of these bugs is a substitution, a cached repeat, or a paraphrase of something already solved, so L0, L1 and L2 have nothing to offer and every task falls through. With L4 disabled they simply fail, in under a second each.

This is the result the composition analysis predicted, and it bounds the cascade's local claim precisely: the cheap tiers address a real but narrow class of work, and that class is not what fills a commit log. The 7.6x advantage measured on mix comes from that suite's composition, not from a general property.

The 0/6 was checked against my own mistake before being believed. The first version of these tasks had no target field, so the cascade ran with an empty document path and its single-file tiers had nothing to act on — a handicap I had introduced, not a property of the cascade. With target set on the four tasks whose fix really is single-file (the other two span two files, which is the point of including them), the result is unchanged: still 0/6. These bugs need reasoning — prune the list, reorder precedence, reject unknown keys, sort before merging, tell permanent failures from transient, round the division up — and none is a substitution, a cached repeat, or a paraphrase.

Amortisation: single-file first, then multi-file

Before the fix. Two passes, L4 enabled. Every single-file close came back at L0 for free on the repeat; neither multi-file close did — both paid the cloud in full again. The cascade resolved 11/12 at $0.3176 per resolved task, no cheaper than raw claude.

The cause was not the open standard, whose Edit has always carried its own path. The L4 capture read only the target file, so a fix spanning two files was seen as no change and never recorded; target-less requests were keyed on an empty path; and replay ignored edit.path altogether. Fixed in agent-joule cfe83c4.

After the fix. The same two-pass run:

pass task              shape         tier    $
1    config-defaults   MULTI-FILE    L4   $0.5943
1    interval-merge    single-file   L4   $0.3927
1    pagination        single-file   L4   $0.4029
1    precedence        single-file   L4   $0.4417
1    rate-window       MULTI-FILE    L4   $0.3901
1    retry-backoff     single-file   L4   $0.4453
2    config-defaults   MULTI-FILE    L0   $0.0000
2    interval-merge    single-file   L0   $0.0000
2    pagination        single-file   L0   $0.0000
2    precedence        single-file   L0   $0.0000
2    rate-window       MULTI-FILE    L0   $0.0000
2    retry-backoff     single-file   L0   $0.0000

cascade  12/12  100.0%  $0.2223/resolved

All six repeats closed at L0, both multi-file changes included. The cascade now pays exactly once per distinct task, whatever the shape of the change.

Stated precisely, because the comparison is easy to overstate. With every task repeated once and every repeat cached, the cascade makes half the cloud calls a stateless agent does — so at equal per-call prices it costs exactly half, by construction. Against the claude baseline measured earlier ($0.2809) the gap is only 21%, because this run's per-call prices were higher ($0.39-$0.59 against $0.25-$0.35 then); per-call cost moves by more than 2x between runs, and those two figures come from different runs. The structural claim is the robust one.

Timing and local energy from this run are not valid. Load average was 50-87 throughout, from unrelated work on the host. The clearest symptom is rate-window pass one reporting 90,155 J of local energy for a 25 s run — about 3.5 kW. Tier outcomes and dollar costs do not depend on CPU contention and are unaffected; everything else in this run should be ignored.

What is not measured here

The agent loop. joule-coded agent exists and joule-bench has an agent solver, but the on-device runtime currently generates at 1.73 tokens/sec on this model — roughly twenty times slower than the hardware should manage. An agent-loop row today would measure the runtime, not the harness, so none is published. Enabling the loader's Metal decode driver (jouleclaw-runtime/fabric) was measured and is worse: 1.18 tok/s and 48.1 J/token against 1.73 tok/s and 33.8 J/token on the reference path.

Anything beyond nine tasks. mix is 5, smoke 3, cascade 1. These suites show the machinery works; they are far too small to support a comparative claim, and as measured above their composition is not representative either.

Any neutral third-party harness. We have never been scored on one.


Superseded: the figures published before 2026-09-10

They reported cascade 10/10 100% and local energy ~0 on this suite. Three defects, each fatal to a different column:

  1. Every energy reading measured the meter. One read() on the Apple model-based counter takes ~840 ms and reports the energy of its own sampling window; the meter bracketed each task between two of them. A 0 ms task read as 34 J. The counter is now sampled on a background thread.
  2. Idle draw was calibrated at about nine times reality. The calibration slept 300 ms between two of those 840 ms reads, then divided an energy delta covering ~2 s by 0.3 s — reporting 352.2 W idle for a Mac. It is 38 W. That inflated figure was subtracted from every task, which is the real reason the old local-energy column read ~0.
  3. Resolution was under-reported on every Python task. The baseline run left a __pycache__ and the graded run imported the stale bytecode, so a correct fix still failed. reference — the ceiling — scored 4/5.

Nothing about the harness's design was wrong; it could simply not measure. Both the energy and the resolution columns were unusable, in opposite directions.