Skip to content

Background and Verification

Prakhar Yadav edited this page Sep 16, 2026 · 1 revision

Background and Verification

Why it exists

The A1708's fan is driven by Apple's SMC firmware curve via applesmc. The only maintained userspace controller for pre-T2 Macs, mbpfan (abandoned since 2019), was concluded unfit by a two-agent adversarial review (critic × architect, four rounds, all claims re-verified against source and live hardware):

  • C1 (critical): any crash/hang/start-limit-exhaustion leaves fans in manual at the last speed with the safety net disabled — silently, forever.
  • C2 (critical): a failed sysfs read corrupts stack memory (buf[-1] underwrite, buf[16] overflow) in a root daemon, every poll.
  • H1: averages sensors — one core at 92 °C hides behind an 80 °C average.
  • H2: direction-gated ratchet freezes speed on plateaus — permanent ~2.5× overspeed after cooldown.
  • H3: high_temp == max_temp passes validation → UB in the speed calc.

Root cause: nothing owns state, nothing observes outcomes. fancontrol is structurally incompatible (needs pwm* attrs applesmc lacks, verified live). No adoptable competitor — hence a small scoped successor where every failure fails toward the firmware. Second customer: the planned omafan Omarchy bar plugin (auto/off/low/med/high/full presets, slider), which fronts afanctl and never touches sysfs.

Evidence base (verified live 2026-09-14)

One fan (Exhaust), fan1_min/fan1_max 1200/7200, idle SMC ~1200 rpm, fan1_manual 0/1, fan1_output target, fan1_input tach, empty fan1_safe (ignored), 3 coretemp sensors (dynamic hwmon index), Tjmax 100 °C, attribute-less husk hwmon3 (mid-conversion), 33 raw SMC keys (out of scope), systemd PID1, Rust via extra/rust, no mbpfan/t2fanrd installed (clean slate, SMC in charge). Naming: fanctl/macfanctl/smctl taken; afanctl searched free 2026-09-14 (re-verify before publish).

What was verified, and how

  • Fixture/mock suite (policy traces, integration, schemas, constant pins, settle windows + co-located units): all four §8 gates green on every merge; --features hw skips cleanly without hardware.
  • Four adversarial review rounds (fresh context, read-only, mandated to falsify): T9 → 5 defects; T9b → 2 MAJOR + 7 MINOR; T9c → 1 MAJOR + 1 arithmetic + 5 MINOR; T9d → PASS / CLOSE. Every finding re-verified in source before ticketing. Reports: orchestration/REVIEW-T9*.md; flight recorder: orchestration/LEDGER.md; task cards: orchestration/instructions/.
  • The hardware gate found what no fixture could — (1) no startup reconcile, so SIGKILL left manual forever (F14); (2) verify compared command vs tachometer, disabling control during every ramp (F16); (3) the SMC adopts targets on a ~1 s tick, needing settle windows (F19). Mocks now model all three.
  • Gate results: doctor PASS; --roundtrip PASS (manual 2 s → AUTO); selftest-panic PASS (exit 101, fan1_manual 0); observe soak PASS (1 h 10 min, 0.05 % CPU, 2.4 MB peak, zero errors); SIGKILL in curve PASS (restart + reconcile → AUTO); curve genuinely controls the fan (manual=1 within 3 s); 600-sample --compare + polkit hold 3000 (fan 2980 rpm) PASS; makepkg -si + reboot (boots in observe, SMC curve, 0 restarts) PASS.

Non-goals (v1, PRD §12)

No T2 / Apple Silicon / macOS / multi-fan; no GUI (plugin is separate); no in-process config reload (restart is the reload); no pwm*; no below-min speeds / true fan-off; no auto-intervene; no config sprawl (exactly 5 keys); no persisted mode; no man page / crates.io / AUR publication yet.

License

GPL-3.0-only (LICENSE, GNU General Public License v3.0; SPDX GPL-3.0-only in cargo/package metadata). Free software, no warranty.

Clone this wiki locally