Skip to content

bmc fan intent: adopt the srv3 stock aggressive curve (reverse the lazy quiet-duty policy) - #7471

Merged
briansrls merged 1 commit into
mainfrom
session/fan-intent-aggressive
Jul 30, 2026
Merged

briansrls merged 1 commit into
mainfrom
session/fan-intent-aggressive

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

What

Reconciles the merged fleet BMC fan intent to the operator's decision: the fleet standard is the srv3 2.07.00 vendor stock aggressive curve (40→30, 55→40, 65→50, 75→75, 85→100), not the lazy "hold 30% through 90°C then panic-ramp" curve that #7421/#7423 had encoded.

Why the lazy curve was the loud one (fleet incident 2026-07-30): srv4's config-asrr.json was a stripped onboarding overwrite. Holding a low duty across a wide band let the SoC climb hot, and the controller then panicked to maximum duty — minimizing duty produced the loudest outcome. The aggressive curve keeps the SoC steady-moderate (~4800 RPM at the 50% step, inaudible, ~65 °C under load) and never idles into a panic. The aggressive curve is what srv3 has shipped on since 2018.

Changes

  • bmc_intent — fleet curve → aggressive (zone failsafe 30, hysteresis 1/1, srv3 stock). BmcFanAcousticPolicy inverted from a low-duty ceiling to engage-cooling-early (≥50% duty by 65 °C): the policy now rejects the idle-until-panic shape by construction rather than blessing it. Firmware kept at track-3 (see follow-up).
  • bmc_fan_projection — content-identity digest → f559aff7…, measured by running the modeled TEMP_SOC jq realization on srv4's base config and reading the live digest back. Unlike the prior lazy row (whose hex was the raw onboarding file, idempotent only in structure), this hex is the realization's own output — the realization reproduces the cited digest byte-for-byte.
  • fleet_bmc_observation / fleet_bmc_state — srv4 observed curve + convergence/reboot receipts re-collected live 2026-07-30 on the aggressive curve: 170 s graceful-restart persistence re-proven, TEMP_SOC 60–63 °C, guarded component max 69 °C, FAN1 4600 / FAN2–4 1200 RPM.
  • witness — curve-duty test rewritten. asrock_altrad8ud_factory_temp_soc_fan_curve turns out byte-identical to the new fleet curve, so the "drifted"/"non-fleet" fixtures were inverting (the factory-curve observation is now converged, not drifted). Added lazy_onboarding_drift_curve (the real onboarding drift) and repointed those tests; reboot interval → 170000.

Live

srv4 was rebuilt to the stock curve through the modeled jq realization (staged atomic rename on the jffs2 overlay), replacing an earlier ad-hoc python patch. Digest f559aff7… reproduced byte-identically on the BMC's own jq; persistence re-proven across a Redfish GracefulRestart. srv4 is operationally on this curve right now.

Verification

  • Compile clean, 0 added diagnostics vs origin/main (202 == 202); all files importing the changed modules compile clean.
  • 54/55 bmc witnesses green by execution — bmc_firmware_thermal 41/42 + bmc_live_receipts 13/13.
  • The one failure (non_fleet_quiet_curve_projected_config_digest_is_unavailable) errors identically on origin/main — a pre-existing Value::Null-vs-Optional-Absent interpreter limitation on bmc_fan_projected_config_digest's Unavailable arm (the only test exercising it). Confirmed pre-existing by stash-compare. The edit to it (factory→lazy fixture) was required because the factory curve is now byte-equal to the fleet curve; it preserves the exact behavior.

Deliberately split out

Firmware keep-2.x (BmcFirmwareUnpinned) — the operator's decision that fan behaviour must not force srv3 off its known-good 2.07.00. BmcFirmwareUnpinned is the right expression (the same curve validates on both 2.07.00 and 3.22.00), but it ripples through ~6 firmware-management tests (drift / update-plan / track-conflict), a separate concern from the fan curve. Kept as a focused follow-up PR for reviewability. Context: incident record #7465.

🤖 Generated with Claude Code

…zy quiet-duty policy)

The onboarding overwrite left srv4 on a lazy "hold 30% through 90C then panic-ramp to
100% by 98C" curve, which #7421/#7423 had encoded as the fleet intent. On a hot chip
that curve let the SoC climb and drove the controller to maximum -- the loud complaint.
The operator's decision: the fleet standard is the srv3 2.07.00 vendor STOCK aggressive
curve (40->30, 55->40, 65->50, 75->75, 85->100), which holds the SoC steady-moderate
(~4800 RPM at the 50% step, inaudible, ~65C under load) and never idles into a panic.

bmc_intent:
- fleet curve -> aggressive (zone failsafe 30, hysteresis 1/1 -- srv3 stock).
- BmcFanAcousticPolicy INVERTED from a low-duty ceiling ("quiet through 90C") to
  engage-cooling-early (>=50% duty by 65C). The empirical safety property is that
  MINIMIZING duty produced the loudest outcome; the policy now rejects the
  idle-until-panic shape by construction instead of blessing it.
- Firmware kept at track-3; the operator's keep-2.x (BmcFirmwareUnpinned) is
  deliberately a separate follow-up -- it ripples through the firmware-management tests.

bmc_fan_projection:
- content-identity digest -> f559aff7, measured by running the modeled TEMP_SOC jq
  realization on srv4's base config and reading the live digest back. Unlike the prior
  lazy row (whose hex was the raw onboarding file, idempotent only in structure), this
  hex IS the realization's own output -- the realization reproduces the cited digest
  byte-for-byte.

fleet_bmc_observation / fleet_bmc_state:
- srv4 observed curve + convergence/reboot receipts re-collected LIVE 2026-07-30 on the
  aggressive curve: 170s graceful-restart persistence re-proven, TEMP_SOC 60-63C,
  guarded component max 69C, FAN1 4600 / FAN2-4 1200 RPM.

witness:
- curve-duty test rewritten for the aggressive shape.
- asrock_altrad8ud_factory_temp_soc_fan_curve is byte-identical to the new fleet curve,
  so the "drifted"/"non-fleet" fixtures were inverting (the factory-curve observation is
  now CONVERGED, not drifted). Added lazy_onboarding_drift_curve (the real onboarding
  drift) and repointed those tests; reboot interval -> 170000.

Live: srv4 rebuilt to the stock curve THROUGH the modeled jq realization (staged atomic
rename on the jffs2 overlay), digest f559aff7 reproduced byte-identically on the BMC's
own jq, persistence re-proven across a Redfish GracefulRestart.

Verification: compile clean, 0 added diagnostics vs origin/main (202 == 202). 54/55 bmc
witnesses green by execution (bmc_firmware_thermal 41/42, bmc_live_receipts 13/13). The
one failure (non_fleet_quiet_curve_projected_config_digest_is_unavailable) errors
IDENTICALLY on origin/main -- a pre-existing Value::Null-vs-Optional interpreter
limitation on bmc_fan_projected_config_digest's Unavailable arm, not a regression
(confirmed by stash-compare); the edit to it was required because the factory curve is
now byte-equal to the fleet curve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 30, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@briansrls
briansrls merged commit 07150f5 into main Jul 30, 2026
5 checks passed
@briansrls
briansrls deleted the session/fan-intent-aggressive branch July 30, 2026 19:30
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