fix(ios): stop the XCTest-channel penalty from re-arming itself

Merged
TH
3d ago
+386-29

Summary

  • Restore normal-plan query-sweep timeout penalties (base threshold >3 s unchanged).
  • Bounded recovery attempts cannot renew the penalty; an accepted tree probe within 1 s clears it early.
  • The penalty is one SnapshotXCTestChannelPenalty value (arm / clear / isProbeDue / claimProbe). Every 30 s a due probe runs in-band: effectiveSnapshotCapturePlan puts the 1 s tree ahead of private AX, so abandoned probe work is charged to that capture and its busy stamp. No XCTest work starts after a response. ADR 0004 updated. 8 files, no knobs.

Refs #3844

Validation

Head 7640bdc6e6 (Swift identical to 092d21aa4a); base 5f6dfb3e6f. Owned iPad Pro M5 / iOS 27.0, Release heavy feed. Both checkouts carry the same uncommitted harness line that keeps iPad off the host AX bridge (#3895), so the runner penalty is exercised.

  • 60 hostile snapshots / 5 min, two runs each: PENALIZED/CLEARED/DEFERRED 3/0/57 → 3/0/57 (7 probes per run); pooled median/p95 1.563/4.923 s → 1.558/4.900 s.
  • Heavy→Settings, 30 light captures: 1/0/24 → 1/1/7; first undeferred capture 122 s → cleared at 32 s; median/p95 0.551/0.631 → 0.431/0.616 s.
  • Snapshot-then-press, 10×: RUNNER_BUSY command=tap 3/3 on 2fd5cd8 → 0/10. The press itself fails on base too (deep-tree point.x != INFINITY, see #3893).
  • 83 focused runner XCTests pass, 0 skips; 4 planted mutations detected.
  • check:xctest-selection and check:affected --run --head 7640bdc6e6 passed (exactCommit=true). CI owns unit-ci and device lanes.

🤖 Generated with Claude Code