Skip to content

Harden safety paths: sensor fail-safe, monotonic time, structured logging - #24

Merged
CamSoper merged 2 commits into
mainfrom
claude/code-review-improvements-u19g8n
Jul 5, 2026
Merged

Harden safety paths: sensor fail-safe, monotonic time, structured logging#24
CamSoper merged 2 commits into
mainfrom
claude/code-review-improvements-u19g8n

Conversation

@CamSoper

@CamSoper CamSoper commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Implements a batch of code-review findings, concentrated on the fire-control paths where a bug has physical consequences. All 76 tests pass (Debug and Release build clean).

Safety fixes

  • Sear mode now enables the blower before feeding. Smoke() and Hold() both turn the blower on before running the auger, but the Sear() main path did not. A Sear entered from a cold blower (e.g. straight after Ready with a still-warm firepot, where the fire-started/establish gates can pass immediately) could feed pellets into residual embers with no combustion air. Added _blower.On() on the main sear path.
  • Sensor fail-safe in FireMinder. RtdArray drains to NaN after a sustained sensor failure and the Smoker surfaces that as -1. FireMinder was consuming -1 as a real temperature — below the check temp, so after the debounce it declared the fire unhealthy and ran the aggressive ~75%-duty recovery feed blind. It now counts consecutive invalid readings during a cook and fails safe to Error (fuel + igniter cut, blower clears the firepot).
  • Display dispose order. Dispose() freed the I2C bus before the LCD/driver that write through it on teardown; reordered to LCD → driver → I2C, matching Init().

Correctness

  • GetFireCheckTemp integer division. SetPoint - (SetPoint / 180 * 30) is integer math: SetPoint / 180 is 1 across 180–359 and 2 at 360+, so the fire-health threshold was a step function with a ~30 °F cliff at setpoint 360. Replaced with the intended proportional curve (5/6 of setpoint — a 30 °F margin at the 180 °F floor). Added a regression test pinning continuity across 360.

Robustness

  • Monotonic time. Duration measurements — shutdown cooldown (Smoker), igniter/fire/debounce timeouts (FireMinder), and PID dt (SmokerPid) — now use an injected TimeProvider and monotonic GetTimestamp/GetElapsedTime. The RTC-less Pi steps its wall clock when NTP syncs (plausibly during startup ignition); a forward step could previously fire a 10-minute timeout instantly, a backward step could hang it. Wall time is retained only for the status display fields.
  • Structured logging. Replaced Debug.WriteLine/Console.WriteLine with ILogger<T> across Smoker, FireMinder, SmokerPid, DisplayUpdater, and RtdArray, wired through DI in Program.cs. Debug.WriteLine is compiled out of the Release builds the deploy ships, so the entire mode/fire narrative was previously absent from journald.
  • Loop back-off. The mode loop and fire-minder loop now delay briefly after an exception so a persistently-throwing iteration can't spin the CPU at 100%.

Tests

  • Migrated FireMinder tests from the hand-rolled Func<DateTime> clock to FakeTimeProvider.
  • Added coverage for the sensor-fault fail-safe (sustained fault → Error; brief burst tolerated) and the fire-check-temp continuity at setpoint 360.
  • 71 → 76 tests, all green.

🤖 Generated with Claude Code

https://claude.ai/code/session_012nMVayWzPQkxynui9ZwDVB


Generated by Claude Code

…ging

Address several code-review findings, focused on the fire-control paths.

Safety fixes:
- Sear mode now turns the blower on before feeding the auger. Previously a
  Sear entered from a cold blower (e.g. straight after Ready with a warm
  firepot) could feed pellets with no combustion air.
- FireMinder fails safe to Error on a sustained invalid grill reading during
  a cook, instead of interpreting the -1 "unplugged" sentinel as a dying fire
  and running the aggressive recovery feed blind.
- Display disposes the LCD and driver before the I2C bus they write through,
  matching Init's teardown order (the old order could throw on shutdown).

Correctness:
- GetFireCheckTemp uses floating-point proportional math (5/6 of setpoint).
  The old integer `SetPoint / 180` collapsed to a step function with a ~30F
  cliff in the threshold at setpoint 360.

Robustness:
- Duration measurements in Smoker (shutdown cooldown), FireMinder (igniter/
  fire/debounce timeouts) and SmokerPid (dt) now use a monotonic TimeProvider
  clock, so an NTP step on the RTC-less Pi can't spuriously trip or hang a
  timeout. Wall time is kept only for status display.
- Replaced Debug.WriteLine/Console.WriteLine with ILogger across Smoker,
  FireMinder, SmokerPid, DisplayUpdater and RtdArray, wired through DI. Debug
  traces were compiled out of the Release builds the deploy ships, leaving
  the controller nearly silent in journald.
- Mode and Fire Minder loops back off briefly after an exception so a
  persistently-throwing iteration can't hot-spin the CPU.

Tests:
- FireMinder tests migrated to FakeTimeProvider; added coverage for the
  sensor-fault fail-safe and the fire-check-temp continuity across setpoint
  360.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012nMVayWzPQkxynui9ZwDVB
@pulumi

pulumi Bot commented Jul 4, 2026

Copy link
Copy Markdown

🚀 The Update (preview) for CamSoper-org/inferno-deploy/main (at f6fe2ca) was successful.

Resource Changes

    Name         Type                    Operation
+-  restart-api  command:remote:Command  replaced
+-  publish-api  command:local:Command   replaced

Longer PID period per Hold iteration; run/wait split still scales with the
PID duty cycle. Also lengthens the minimum-feed cycle Sear uses when the
grill is over the max temp.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012nMVayWzPQkxynui9ZwDVB
@CamSoper
CamSoper marked this pull request as ready for review July 5, 2026 03:38
@CamSoper
CamSoper merged commit cff185c into main Jul 5, 2026
3 checks passed
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.

2 participants