← task board · runs · Qwen 3.6 27B · pi
Status: FAIL 3/3 trials across both 27b fast runs
(fast__qwen3.6-27b__20260706-124744 dk74haS @129s;
fast__qwen3.6-27b__20260706-132725 ytDudum @83s + tF7m98S @82s). No fast-cap
involvement (cap 180s, no fast-timeout.json). All three verdicts are
identical and deterministic: 5/6 subtests pass, only
test_tasks_cancel_above_max_concurrent fails —
stdout.count("Cleaned up.") == 0 where 2 expected (Task started. count is
correct at 2). Contrast the 35b MoE trial in fast__…113104: it deadlocked its
own smoke test and was cut at the cap with 1/6; the dense 27b writes plausible
code, self-verifies, and finishes clean in ~80s — a genuine near-miss.
test.py's task cleanup is itself async:
finally:
await asyncio.sleep(1)
print("Cleaned up.")
The failing subtest runs 3 tasks with max_concurrent=2, SIGINTs at 500ms, and
expects the 2 started tasks to still print Cleaned up. The docstring calls
out the gotcha: "asyncio.gather doesn't properly cancel existing tasks if there
are still tasks in the queue."
All three trials wrote the same shape: semaphore + create_task per task, then
try:
await asyncio.gather(*handles)
except (KeyboardInterrupt, asyncio.CancelledError):
for h in handles:
h.cancel() # ← the fatal line
await asyncio.gather(*handles, return_exceptions=True)
raise
Mechanism on the container's Python 3.13 (asyncio.Runner turns the first
SIGINT into a cancel of the main task, not a raw KeyboardInterrupt):
await gather cancels the GatheringFuture,
which already delivers cancel #1 to every child.semaphore.acquire() with nothing to clean up, so it
finishes cancelled instantly — and a non-return_exceptions gather
completes on the first failed child, waking the main task while tasks
1–2 are still inside finally: await asyncio.sleep(1).h.cancel() is cancel #2, delivered into that
cleanup sleep(1) → the finally block aborts before printing. 0 cleanups.This is exactly why the below/at-max subtests pass with the same code: with no
queued task, every child has the slow async cleanup, so the gather can't
complete until a child has finished cleaning up — the re-cancel loop then
finds t.done() and is harmless. The queued third task is what wakes the
handler early.
dk74haS's variant caught only KeyboardInterrupt, so its handler never fires
at all (the Runner delivers CancelledError) — same 0-cleanup outcome via a
different path: the main task dies immediately and asyncio.run teardown
(_cancel_all_tasks) delivers cancel #2 into the in-flight cleanups. Same
root-cause family: control escapes while cleanup is still in flight, and
whoever runs next re-cancels.
Reproduced host-side (agent's exact run.py + task test.py, SIGINT at
500ms): 2/3 → cleaned=2, 2/2 → cleaned=2, 3/2 → cleaned=0 — byte-for-byte
the benchmark failure. Deleting the re-cancel loop alone flips 3/2 to
cleaned=2 (the gather cancellation already propagated; you only need to await
the children once). The reference asyncio.TaskGroup solution passes for the
same reason: cancel once, await, never re-cancel.
This is the interesting part for the harness. The model did verify
(4 bash commands in tF7m98S, 7 in dk74haS): a signature check, a concurrency
smoke test, and — remarkably — the exact failing scenario: 3 tasks,
max_concurrent=2, cancelled mid-flight, asserting cleanup ran. It observed
['A start', 'B start', 'A cleanup', 'B cleanup'] → "All cleanup code ran!"
and declared done.
Its fixture's finally was synchronous (results.append(...)) — and a sync
finally executes atomically, so cancel #2 has no await point to land on. The
bug is only observable when the cleanup itself awaits, which is precisely what
the hidden test.py does (await asyncio.sleep(1) before the print). Right
scenario, wrong fixture: self-verification passed while the verifier failed.
Model capability near-miss, harness clean. No length-stops, no write-guard or recovery triggers, no timeout shaping — the trial ends with the model's own (honestly obtained, wrongly reassuring) green self-test. This lands in the known "hidden-criterion near-miss" false-success class, with a sharper edge: RULE 2's "verify with real commands" can't help when the model's test fixture lacks the one property the hidden test exercises. No mechanical fix proposed — detecting "your fixture is weaker than the hidden criterion" is task knowledge, not harness ground truth.
TaskGroup.