← board

Measured 2026-09-13 (frankS), 3000 iterations, -dPXX_ALLOC_CENSUS

The whole finding is one contrast, and the CONTROL is the important half:

mo = re.match("b","banana"); if mo:      live=8        BOUND    clean
if re.match("b","banana"):               live=3000     DISCARDED 1/iter
r = re.findall("a","banana"); len(r)     live=13       BOUND    clean
if "b,a".split(","):                     live=11129    DISCARDED ~3.7/iter
if ["a"]:                                live=3        LITERAL  clean

str.split is the row that makes it general: it is not re, it is not a shim with an unusual return, and it leaks the same way. if ["a"]: is the row that locates it: a discarded LITERAL is released correctly, so the discard machinery exists and a CALL result is what misses it.

Why nothing caught it

The values are all correct -- every one of these programs prints the right answer. A leak cannot fail a value check, which is the class CLAUDE.md records under "MATCH THE ASSERTION CLASS TO THE DEFECT CLASS". Found only because a census was being run for an unrelated reason ([[bug-n-a-module-level-regex-call-recompiles-and-leaks-its-pattern]]), and the residue did not go to zero after that fix.

Scope not yet established

Measured for if <call>: only. NOT measured, and someone should before ranking this higher or lower:

Do not assume the if result generalises; the literal-versus-call asymmetry above is exactly the kind of boundary that moves when the spelling changes.

Positive control for whoever takes it

if ["a"]: must STAY clean (live=3) -- it is the arm that already works, and a fix that releases a call result by making the discard path more aggressive could double-release the literal. And the assertion has to be a census bound over a LOOP: a handful of iterations cannot separate a per-iteration leak from a fixed residue, which is the whole reason this sat unnoticed.

Closed 2026-10-03 (frankuser): flat at HEAD, no fix of its own

Re-measured at 13fcd29da1 (x86-64, -dPXX_ALLOC_CENSUS, live= from the census header, not the size histogram, which counts allocations, not live blocks). Every shape this ticket names is now flat, 100 against 5000 passes:

Nothing landed for this ticket by name. The ownership work of 2026-09-15 to 2026-10-03 (owned call results, the discard binder, the position-0 spill) closed it on the way. What keeps it closed is test_nilpy_a_discarded_or_variant_routed_result_is_released, which has one arm per ticket of the four closed together: HEAP_DEBUG diff against CPython, census bound 300 at 5000 passes (live 90) with a keep control that trips, and i386.