You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[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
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.
Detection fidelity. Deciding "is there an await between these two readings" is an AST question, and a text scan that gets it approximately right is the failure mode AGENTS.md already documents for source-scanning gates: it sees only the spellings it knows, and an unrecognised one produces no flag, silently. The cheaper and more robust formulation is to ban the raw probe outside one approved helper, which is a grep-level rule with a tiny allowlist.
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.
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 withtoBeacross anawait" — has now been found five times over four cards:kernel.test.ts(#4813)health-monitor.test.ts(#6329)throttle(sendTasksUpdate, 100)timer, measured at 105 mstimeout-guard.test.ts(#10604 / PR #10661)expected 2 to be 4, two foreign timers expiring mid-testkernel.test.tsx2,hot-reload.test.ts(#10685)service-automation/src/engine.test.ts(#10783)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 noawait.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:
check:*gates are registered in rootpackage.json, which is fenced territory of the @changesets/cli v3 migration lane (Migrate the release toolchain to @changesets/cli v3 — one atomic PR carrying the bump, the pre-mode restructure, and the gates that model v2's semantics #9465) while it runs. There is a precedent for the workaround —lint.ymlalready invokes several gates asnode scripts/...directly, for exactly this reason, anddispatch-gates.mjsderives gate families from either spelling — so this is a cost, not a blocker.awaitbetween these two readings" is an AST question, and a text scan that gets it approximately right is the failure mode AGENTS.md already documents for source-scanning gates: it sees only the spellings it knows, and an unrecognised one produces no flag, silently. The cheaper and more robust formulation is to ban the raw probe outside one approved helper, which is a grep-level rule with a tiny allowlist.await— green only because their windows happen not to cross a timer phase #10685's PR put one inpackages/core/src/refd-timer-probe.testkit.ts;service-automation([finding] A fifth ref'd-timer pin scores a process-global count across anawait— service-automation'sengine.test.ts, outside #10685's site list #10783) cannot import it without either publishing a test helper on@objectstack/core's public surface — which theapi-surfaceratchet would rightly notice — or a cross-package test input, whichcheck:cross-package-test-inputsrequires to be spelled so it can see it.So: a gate is the right end state, and it is its own card rather than a rider.
2.
toBeLessThanOrEqualon a process-global count can hide a real leakPR #10661's repair for
timeout-guard.test.tswas to assert non-increase: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:
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