Skip to content

Only fail the settle loop on observed motion, not starvation#148

Merged
kyleve merged 1 commit into
mainfrom
fix-settle-starvation
Jul 27, 2026
Merged

Only fail the settle loop on observed motion, not starvation#148
kyleve merged 1 commit into
mainfrom
fix-settle-starvation

Conversation

@kyleve

@kyleve kyleve commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fixes the intermittent CI snapshot failure (Snapshot content never settled from SnapshotRenderingSupport.swift:38) that hit the About suite on cold, loaded runners — including the red run on Drop the stale WhereCore dependency from StuffTestHost #144, which was this flake, not that branch's changes.
  • Root cause: under CPU starvation a single settle pass (16ms sleep + layout + quarter-res render) can cost over a second, so the three passes stability needs don't fit the 2.5s budget — and the loop failed static content as "still changing" even though it never once observed a change. Reproduced locally by duty-cycling SIGSTOP/SIGCONT on StuffTestHost (1.4s stopped / 0.12s running); post-timeout frames were byte-identical, and the captures still matched their references.
  • Fix: the budget now bounds observed motion. .timedOut requires a change seen past anchor establishment; a change-free loop keeps running until it can prove stability, giving up only at a hard cap of 4× the budget with a new .starved outcome naming the pass count (an environment failure, not view motion). Genuinely moving content still fails at the budget exactly as before.
  • Settle failures now name the full snapshot identifier (renderSnapshotImage(of:named:...)), so a timeout in a 16-image matrix says which configuration — the gap that made the CI failures undiagnosable from the result bundle.
  • Deadlines measure with ContinuousClock, closing that P2 in the module TODOs.md; docs (README.md, AGENTS.md, TODOs.md) updated in the same change.

Test plan

  • New SnapshotRenderingSupportTests pin all three ending conditions: starved-but-static settles (pre-fix code always failed this), observed motion times out at the budget, unsampleable content ends .starved at the hard cap.
  • SnapshotKitTestingTests bundle green on the checkout simulator.
  • Replayed the exact starvation repro against the fix: the duty cycle that previously produced four settle timeouts now runs the About suite green (103s — every capture extended past its budget, proved stability, and matched its reference).
  • Full StuffSnapshotTests scheme green unstarved (no behavior change for healthy runs).
  • ./swiftformat --lint clean.

Made with Cursor

Under CPU starvation (a cold, loaded CI runner) a single settle pass
(16ms sleep + layout + quarter-res render) can cost over a second, so
the three passes stability needs don't fit the 2.5s budget and
settleContent failed static content as "Snapshot content never
settled" — ~50% of cold CI runs on the About screen, whose capture
still matched its reference. Reproduced locally by duty-cycling
SIGSTOP/SIGCONT on StuffTestHost (1.4s stopped / 0.12s running).

The budget now bounds *observed motion*: .timedOut requires a change
seen past anchor establishment, a change-free loop keeps running until
it can prove stability, and a hard cap at 4x the budget gives up as the
new .starved outcome naming the pass count. Deadlines measure with
ContinuousClock (closing that TODO), and settle failures now name the
full snapshot identifier via renderSnapshotImage(of:named:...), so a
timeout in a 16-image matrix says which configuration.

Verified: the same duty-cycle starvation that produced four settle
timeouts now runs the About suite green (103s, every capture extended
past its budget and still matched the reference), and the full
StuffSnapshotTests scheme passes unstarved. New
SnapshotRenderingSupportTests pin all three ending conditions.

Co-authored-by: Cursor <[email protected]>
@kyleve
kyleve merged commit ade25c7 into main Jul 27, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant