Make the live-frame probe survive a transient source-read fault - #653
Conversation
Three one-off failures in one week, all in frame-sensitive tests -- WarningLocationTestCase (#594), FrameEqualityTestCase (#623), TracebackTestCase (#626) -- all "call stack is not deep enough" shapes, none reproducible: every one passed in isolation and on every re-run. The mechanism was already half-documented at the site. ___isGeneratedPythonMethod___ tells compiled Python apart from Grail's own runtime by probing the method SOURCE for the ``___curPos___'' marker, and an earlier fix stopped a FAILED probe from being cached -- its comment records that injecting a single false reproduces the exact failure shape. What remained was the failure's effect on the CURRENT walk: a transient fault answered false once, the frame dropped, the chain came up short, and the next walk re-probed fine. That is precisely a once-per-week flake, and the source string is the one read in the walk that goes back to the repository -- the read that can fault under four concurrent shard workers. Three changes, each doing one thing: * a FAST PATH reads the method's own debugInfo first (argsAndTemps -- in memory, off the stack triple, unfaultable): a module-level def declares the marker as a METHOD temp and is conclusive without touching source. Not a replacement: a def whose body compiles into an inner BLOCK declares the marker block-side, invisible to method-level debugInfo (_py_warnings: 11 of 46 methods), so a miss falls through; * the source probe RETRIES once -- the page read that just faulted is the likeliest read to succeed a moment later; * a double fault leaves a breadcrumb (#GrailPyProbeFailures) so a run that still flakes says why, and stays uncached as before. #GrailPyProbeFailCount is a test seam injecting that many consecutive simulated faults, because a real page fault cannot be scheduled. The new SUnit case proves the retry absorbs one fault with no breadcrumb, that two faults answer false for that walk only and leave the breadcrumb, that the failed probe is not cached (the next probe answers true), and that the temps fast path never reaches the source probe at all. Probe answers verified unchanged over every env-1 method of _py_warnings (46/46 against the source-based truth), and the three flake-family test classes looped five times clean. Full curated suite: 0 regressions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Verifying the stacked #656-to-be (the function-type-name PR) produced the A/B this PR's "honest limits" section said was missing: its install recompiles Two follow-ons, both in the stacked PR: the same recipe applied to the locals reader ( |
Three flakes, one mechanism
Three one-off failures in one week, all in frame-sensitive tests —
WarningLocationTestCase(flagged in #594),FrameEqualityTestCase(#623),TracebackTestCase(#626) — allValueError: call stack is not deep enoughshapes, none reproducible: every one passed in isolation and on every re-run.The mechanism turned out to be half-documented at the site already.
___isGeneratedPythonMethod___tells compiled Python apart from Grail's own runtime by probing the method source for the___curPos___marker, and an earlier fix stopped a failed probe from being cached — its comment even records that injecting a single false reproduces the exact failure shape. What remained was the failure's effect on the current walk: a transient fault answered false once, the frame silently dropped, the chain came up short, and the next walk re-probed fine. That is precisely a once-per-week flake — and the source string is the one read in the whole walk that goes back to the repository, the read that can fault under four concurrent shard workers. Everything else reads the in-memory method object.Three changes, each doing one thing
argsAndTemps— in memory, off the stack triple) lists a module-level def's___curPos___as a method temp: conclusive without touching source. Not a replacement — a def whose body compiles into an inner block declares the marker block-side, invisible to method-level debugInfo (_py_warnings: 11 of 46 methods), so a miss falls through. I checkedargsAndTemps' kernel implementation to confirm it decodes in-method data rather than parsing source.#GrailPyProbeFailures) so a run that still flakes says why — and stays uncached, as before: a real false is a property of the method, a failed probe is a property of the moment.Testing something you cannot schedule
A real page fault cannot be provoked on demand, so
#GrailPyProbeFailCountis a test seam injecting that many consecutive simulated faults, consumed inside the probe's own failure path.LiveFrameProbeResilienceTestCase(4/4) proves:Verification
_py_warnings(the block-temp-heavy module)WarningLocation+FrameEquality+Tracebacklooped ×5, all cleanHonest limits: the fault itself was never observed directly — the case rests on the site's own prior measurement ("injecting a single false" reproduces the shape), on the source read being the only repository-bound read in the walk, and on the failure signature matching all three sightings. If a one-off still appears, the breadcrumb now turns it from a footnote into a diagnosis.
🤖 Generated with Claude Code