Skip to content

Safety floor: per-class thresholds + persistence, pressure-primary (M4 holds were overridden to max) - #68

Open
adurham wants to merge 1 commit into
ProducerGuy:mainfrom
adurham:fix/m4-safety-floor
Open

adurham wants to merge 1 commit into
ProducerGuy:mainfrom
adurham:fix/m4-safety-floor

Conversation

@adurham

@adurham adurham commented Oct 11, 2026

Copy link
Copy Markdown

Problem

On M4-class machines the thermal safety floor makes manual fan control unusable (reproduced on a MacBook Pro 14" M4 Max, Mac16,6, macOS 27): hotspot-class keys legitimately read 96-113°C under sustained load while macOS itself stays nominal (fans near minimum, thermal pressure Nominal, no thermal or performance warnings ever recorded in pmset). The floor compares the peak over ALL TC/Tp/TG/Tg keys against a single 95°C threshold whenever a below-max hold is active, so every manual hold is overridden to max within seconds and re-armed forever — commanded 2600 → target reads 7826 until auto.

Fix

SafetyGovernor replaces the single-threshold peak check, shared by the daemon backstop and the app's client monitor:

  • Primary trip: macOS thermal pressure — Serious/Critical immediate, Fair sustained 30s. Apple's model already accounts for hotspot readings.
  • Per-class backstops with seconds-based persistence (callers pass measured elapsed time, so a cadence change can't rescale the windows): hotspot class (TC/Tp/Tg/TG minus the M4 Max: TG0B/TG0H/TG0V are battery sensors, not GPU #63 misnamed battery keys) 118°C/10s judged by the second-highest reading — a single glitchy key can neither trip nor block a clear; structure (TS0P) 95°C/30s; battery (TB0T) 50°C/60s. These are deliberately conservative backstops for older generations where hardware throttling arrives first.
  • Readings outside 1-130°C are sanitized before classification; a fully-blind hold (no hotspot reading) trips after 5s; pressure is checked before sensor state so a blind hold cannot wait on the sweep.
  • Trip behavior unchanged from main: the hold is pinned to max and latched until pressure is NOMINAL and every class is 10°C below its threshold for 30s. After 3 trips within 10 minutes the restore cycle stops and the fans are left with Apple (stayAuto); a deliberate re-apply starts fresh.
  • #63: TG0B/TG0H/TG0V excluded from the hotspot class (battery sensors, not GPU).

Evidence

  • Live before/after on the affected machine: a quiet 2400 RPM hold held while hotspots ran 95.7 → 109.7°C with zero overrides (main's floor trips at the first ≥95 reading and maxes within ~2s); clean auto restore afterwards; daemon log clean the whole time.
  • 12 new unit tests (real-world M4 case, sustained trips, second-highest trip AND clear, sanitized readings, pressure ladder, blind hold, re-trip backoff); full suite: 164 tests green.
  • Validated on one machine so far (M4 Max, macOS 27). Reports from M1-M3 laptops and desktops are very welcome — the per-class thresholds are plain constants, easy to tune.

Deliberately not in this PR

Related: the fan-control side of #35 and #62, plus #63. Possible rebase conflicts with #37/#25/#47 in ThermalMonitor.swift/Daemon.swift — happy to rebase as needed.

…4 holds)

The floor compared the peak over ALL TC/Tp/TG/Tg keys to a single 95C
threshold while a below-max hold was active. On M4-class machines hotspot
keys legitimately read 96-113C under sustained load while macOS itself stays
nominal (fans near minimum, thermal pressure Nominal, no thermal/performance
warnings ever recorded), so every manual hold was overridden to max within
seconds and re-armed forever - manual fan control was unusable under load
(reproduced twice on a MacBook Pro 14in M4 Max, Mac16,6, macOS 27).

The rebuilt governor (SafetyGovernor):
- Primary trip: macOS thermal pressure (serious/critical immediate, fair
  sustained 30s) - Apple's model already accounts for hotspot readings.
- Per-class backstops with seconds-based persistence (measured elapsed time,
  so caller cadence changes cannot rescale them): hotspot (TC/Tp/Tg/TG minus
  the ProducerGuy#63 misnamed battery keys) 118C/10s judged by the second-highest
  reading (a single glitchy key can neither trip nor block clearing);
  structure (TS0P) 95C/30s; battery (TB0T) 50C/60s. Backstops are deliberately
  conservative for older generations where hardware throttling arrives first.
- Readings outside 1-130C are sanitized before classification; a fully-blind
  hold (no hotspot reading) trips after 5s; serious/critical pressure is
  checked before sensor state so a blind hold cannot wait on the sweep.
- On a trip the hold is pinned to max (unchanged behavior) and latched until
  pressure is NOMINAL and every class sits 10C below its threshold for 30s.
- After 3 trips within 10 minutes the governor stops the restore cycle and
  leaves the fans with Apple (stayAuto); a deliberate re-apply starts fresh.
- ProducerGuy#63: TG0B/TG0H/TG0V excluded from the hotspot class (battery, not GPU).

Wired into the daemon backstop and the app's client monitor. 12 new unit
tests; full suite 164 tests green. Live-validated on the affected machine:
a quiet 2400 RPM hold held while hotspots ran 95.7-109.7C with zero overrides
(old build tripped at the first >=95 reading).

Deliberately not in this PR (to keep it reviewable): fan-stall detection and
release-to-auto on trips; display-side key labeling (per-generation tables,
see ProducerGuy#62) is complementary and untouched.
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