-
Notifications
You must be signed in to change notification settings - Fork 0
Background and Verification
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_temppasses 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.
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).
-
Fixture/mock suite (policy traces, integration, schemas, constant pins,
settle windows + co-located units): all four §8 gates green on every merge;
--features hwskips 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
SIGKILLleft 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:
doctorPASS;--roundtripPASS (manual 2 s → AUTO);selftest-panicPASS (exit 101,fan1_manual0); observe soak PASS (1 h 10 min, 0.05 % CPU, 2.4 MB peak, zero errors);SIGKILLin curve PASS (restart + reconcile → AUTO); curve genuinely controls the fan (manual=1within 3 s); 600-sample--compare+ polkithold 3000(fan 2980 rpm) PASS;makepkg -si+ reboot (boots in observe, SMC curve, 0 restarts) PASS.
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.
GPL-3.0-only (LICENSE, GNU General Public License v3.0; SPDX
GPL-3.0-only in cargo/package metadata). Free software, no warranty.
- Home
- Installation
- CLI Reference
- Configuration
- Control Policy
- Supervisor and Run Modes
- Safety Model
- Hardware Interface
- Diagnostics (doctor)
- Plugin Surface (omafan)
- JSON Schemas
- systemd and Packaging
- Development and Testing
- Architecture and Internals
- Troubleshooting and FAQ
- Background and Verification
- afanctl-vs-mbpfan