Summary
Setting merge_batch_size: 1 makes _hierarchical_merge spin forever. The
tournament loop never reduces the patch list, so training hangs indefinitely
after the reflect stage produces two or more patches.
Affected code
skillopt/gradient/aggregate.py, _hierarchical_merge (lines 92–136):
while len(current) > 1: # line 92
level += 1
for i in range(0, len(current), batch_size): # line 95
batch = current[i : i + batch_size]
...
if len(batch) == 1: # line 110
next_level[idx] = batch[0] # line 111
With batch_size = 1, line 95 splits N patches into N single-item batches.
Line 110 then treats every batch as "already merged" and copies the patch
through unchanged, so current never shrinks and len(current) > 1 stays
true forever. level increments without bound — in our run it passed
7,200,000 before we killed the process.
Reproduction
Use any config with merge_batch_size: 1. configs/searchqa/claude_code_smoke.yaml
(line 34) ships with this value; every other benchmark config defaults to 8.
Run training until the reflect stage yields at least two patches.
The run hangs at the aggregate step, emitting
[aggregate success] level=N 2 patches → 2 batches (parallel, batch_size=1)
with N climbing without limit.
Note that a step where reflect produces zero patches will short-circuit with
action: skip_no_patches and appear to succeed — the hang only surfaces once
patches actually reach the merge stage, which makes this easy to miss.
Why it went unnoticed
trainer.py:793 reads merge_batch_size and validates batch_size and
accumulation at lines 806–809, but never validates merge_batch_size.
The affected lines are unchanged since the initial release commit 244e346.
Suggested fix
Two small changes:
Clamp the stride in aggregate.py:95 so a round always makes progress:
range(0, len(current), max(2, batch_size))
Reject the degenerate value early — raise ValueError in trainer.py
(near the existing validation at 806–809) when merge_batch_size < 2.
Additionally, configs/searchqa/claude_code_smoke.yaml should be updated,
since as shipped it will hang on any step that produces patches.
Environment
SkillOpt 0.2.0 (editable install from source)
Python 3.12.10, Windows 11
Backend: claude_chat (optimizer) / claude_code_exec (target)
Summary
Setting
merge_batch_size: 1makes_hierarchical_mergespin forever. Thetournament loop never reduces the patch list, so training hangs indefinitely
after the reflect stage produces two or more patches.
Affected code
skillopt/gradient/aggregate.py,_hierarchical_merge(lines 92–136):