Skip to content

[finding] Nothing gates the ref'd-timer/await class after five instances — and #10661's toBeLessThanOrEqual repair can still hide a real leak behind foreign expiries #10785

Description

@claude

Two observations from #10685, both about the same class and both left undone there deliberately. Filing together because they share one remedy shape.

1. Nothing stops the sixth site

The class — "a process-global getActiveResourcesInfo() timer count scored with toBe across an await" — has now been found five times over four cards:

wherehow it surfaced
kernel.test.ts (#4813)latent
health-monitor.test.ts (#6329)red in the merge queue — the window stretched past the runner's own non-unref'd 100 ms throttle(sendTasksUpdate, 100) timer, measured at 105 ms
timeout-guard.test.ts (#10604 / PR #10661)red in CIexpected 2 to be 4, two foreign timers expiring mid-test
kernel.test.ts x2, hot-reload.test.ts (#10685)latent, fixed in PR for #10685
service-automation/src/engine.test.ts (#10783)latent, still open

Each was repaired at the site. Nothing prevents the next one, and the failure it produces is a shard-only intermittent red whose message points at a timer count rather than at the change that caused it — which blocks every lane's merge queue at once.

A gate would close the class. The rule it needs to express is roughly: a test that derives a count from process.getActiveResourcesInfo() may compare two readings only across a window that contains no await.

Why #10685's PR did not add one

Three costs it could not pay inside an S card, all worth stating so the next attempt starts informed:

So: a gate is the right end state, and it is its own card rather than a rider.

2. toBeLessThanOrEqual on a process-global count can hide a real leak

PR #10661's repair for timeout-guard.test.ts was to assert non-increase:

before=refd();awaitexpect(raceWithTimeout(newPromise(()=>{}),10,()=>newError('hung')),).rejects.toThrow('hung');expect(refd()).toBeLessThanOrEqual(before);

The reasoning in that file is sound as far as it goes — a leak is a growth, so the direction that matters survives and foreign expiry stops being a signal. But the reading is still the whole process, and this leg deliberately spends ~10 ms of real time on the loop, so foreign timers really do expire inside the window. The arithmetic composes:

subject leaks +1
two foreign timers expire -2
reading before - 1 => toBeLessThanOrEqual passes

A real leak, green. The ablation cited in that PR (which reds on a real leak) was presumably run without ambient noise; under shard load the detection becomes probabilistic rather than certain. Nothing is red today and this is not a regression — it is a narrower detection guarantee than the comment above it claims, in the one pin of the family that spends real time on the loop and is therefore most exposed.

The remedy is the instrument #10685's PR added: record the handles the subject arms and ask how many of those are still pinning the loop, across a window that is synchronous by construction. That leg's guard is supposed to fire, so the conversion needs a little care — which is the other reason it was left out of #10685's scope rather than done as a drive-by rewrite of another card's deliberate, documented decision.

Not urgent; nothing is red.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions