Repository navigation
gc-ratchet: 10_store_receiver_across_alloc runs ZERO minor collections on main — the probe is inert and must not be re-pinned #9832
Description
Found while classifying the 43 cells in #9829 for a re-pin. This one must not
be pinned, and it is a coverage defect rather than a counter movement.
The probe no longer collects
From the measurement artifact of main's own scheduled run (33989581881,
d36a1af0c), against the pinned baseline:
| metric | baseline | today |
|---|---|---|
minor_cycles |
1 | 0 |
step_cycles |
1 | 0 |
copied_objects |
8,160 | 0 |
copied_bytes |
506,200 | 0 |
freed_bytes |
8,930,928 | 0 |
heap_used_bytes |
220,384 | 464,072 (+110.57 %) |
freed_bytes = 0 rules out "a minor ran and found nothing live" — no
collection ran at all.
Every other probe in the suite still collects (minor_cycles 1–22, unchanged
or ±1), so this is specific to probe 10, not a suite-wide pacing shift.
Why that is worse than a regression
The probe's own header says so:
a[i] = vevaluates the receiver first and the value last — spec order — so
the receiver sits in an SSA register while the RHS runs. When the RHS
allocates, an evacuating minor can relocate the array underneath it […]
THREE THINGS MAKE THIS PROBE BITE, and dropping any one of them makes it
silently measure nothing.
The third is "the RHS allocates", i.e. a minor must actually fire. With
minor_cycles = 0 there is no evacuation, so there is no window, so the probe
cannot catch the stale-root class it exists for (the #6970 / #9523 family:
a root slot rewritten by evacuation while a register still holds the old
address). It is the only probe covering that shape.
heap_used_bytes +110.57 % is a consequence of this, not a retention
regression: nothing was collected, so the post-gc() residue is whatever the
bump allocator left. Reading it as a memory regression would be the same
mistake as reading 12_large_live_set.heap_used_bytes that way (see #9829) —
these are stranded-detectors sampled after an explicit gc(), not memory
figures.
Why nobody saw it
gc-ratchet has been red on main since 2026-08-18 for unrelated reasons
(#9829), and its failures are unwatched (#9830). The suite even has a check
named test_a_probe_that_stopped_collecting_fails — the machinery to catch
this exists and did produce rows. Nobody read them.
So the probe has been inert for some part of nineteen days, and the mechanism
that would have said so was drowned in an unrelated red. That is the concrete
cost of #9830 and the best argument for it.
What this blocks
The #9829 re-pin cannot include this probe's four cells. Pinning
minor_cycles = 0 would permanently bless an inert probe — a green gate that
proves nothing, which is exactly the failure mode this suite's own header
warns about.
The decision I cannot make alone
Two orders, and they give different artefacts:
- Fix the probe first, then pin. Restoring a minor changes this probe's
numbers again, so a pin taken now would be stale for it either way. Cleanest,
but blocks the pin on a probe repair. - Pin the other 39 now and carry probe 10 as an explicit, documented
exclusion with this issue as its receipt, then remove the exclusion when the
probe is repaired. Gets a working gate back tomorrow, at the cost of one
probe's coverage being formally acknowledged as absent rather than silently
absent.
I lean to (2) — a gate that works for 13 probes with one honestly-declared gap
beats nineteen more days of nothing — but which probe-side change restores the
minor is a judgement for whoever owns the probe.
Repair direction, not a diagnosis
I have not bisected when the minor stopped. Given the suite's own note that the
probe needs an allocating RHS, the likely cause is that the probe's allocation
volume no longer reaches the nursery threshold — plausibly a consequence of
the object-size reduction in #8313 (56 → 40 bytes, see #9829), which would mean
the probe simply needs to allocate more to keep biting. That is a guess; the
counters above are the facts.
Cross-references: #9829 (the nineteen-day red and its bisection), #9830 (a red
scheduled main run should open an issue), #8313 (the object shrink).
Activity
proggeramlug commentedon Sep 6, 2026
Fixed in #9833 — and the sweep you would ask for next: five more probes are at the same cliff
The fix is margin, verified by sabotage; details in #9833. The part worth
promoting out of that PR is what the same measurement says about the rest of the
suite.
minor_cyclesper probe, pinned baseline vsmain@d36a1af0c(from thegate's own artifact, run 33989581881):
01_nursery_churn02_survivor_promotion03_cross_gen_writes04_dead_after_deep_stack05_closure_capture06_string_retention07_array_grow_evacuate08_map_set_sidetables09_try_catch_roots10_store_receiver_across_alloc11_collect_at_depth12_large_live_set13_large_eden_survivors14_grow_then_churnSix of fourteen probes were pinned at
minor_cycles == 1, and one of them hasalready fallen to 0. Probe 10 is not a special case; it is the first of a
cohort. Every one of
01,02,03,09,11goes inert on the nextallocation win of any size — and the campaign is currently landing allocation
wins deliberately.
That also explains why the tool's guard did not save us.
gc_ratchet.pyrefusesto pin
minor_cycles < 1, which catches a probe that is already dead. Ithappily pins
minor_cycles == 1, which is the state that produces one. Theguard is one off from where it needs to be, and a plausible follow-up is to
refuse to pin
minor_cycles < 2and re-size the five above — or, better, tohave the probes assert their own margin rather than depend on a pinning-time
check.
I am not taking that in this pass; #9833 fixes the one probe that is actually
broken and records the invariant in its header. Raising the guard would re-size
five probes and re-pin the suite, which belongs with the #9829 pin rather than
before it.
https://claude.ai/code/session_014UZWia6L37DpA93VLtNK9m
proggeramlug commentedon Sep 6, 2026
Fixed by #9833, landed on
mainvia merge train #9875. The train merged as its own branch, so the close-keyword never fired — closing manually, verified on main.