Files
brewpi/components/TODO.md
T
jensandClaude Sonnet 4.6 48d5235558 docs: add components/sud.py step lifecycle spec
Captures the ramp/hold/user-confirm phase order and a couple of
non-obvious clarifications from a design discussion (ramp is gated
purely by the schedule's 'ramp' key, not actual-vs-target temp;
WAIT_USER is a distinct phase, not an extended hold), plus one open
gap: pure-hold steps never re-set the controller's target temperature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhiQe64F74uHV8jzuoSa5K
2026-06-22 08:40:45 +02:00

2.7 KiB

components/sud.py step lifecycle spec

Confirmed spec for a Sud schedule step's internal phase order (components/sud.py), from a design discussion clarifying how ramp/ hold/user-confirm interact.

Phase order

ramp -> hold -> user-confirm. Each step independently opts into ramp and/or hold by including that key (_build_step(), sud.py:35-45); the other phase is skipped entirely if its key is absent. The 'ramp' key's presence is the only trigger for ramping (_advance(), sud.py:192-210) - there is no diff-based decision (T_actual vs. T_target) anywhere that decides whether to ramp, and no internal setpoint-trajectory limiter in the temperature controller (set_theta_soll() is an instant, unconditional assignment, components/pid/temp_controller_base.py:81-82). The ramp key both gates ramping and carries its settings (rate); it does not merely override settings on top of an always-on ramp.

Duration 0

Supported for hold: hold_remaining = duration * 60.0 can be 0; tick() (sud.py:186-190) finishes the step on the next tick once hold_remaining <= 0.

End-of-step branching

_finish_step() (sud.py:212-216): no user_wait_for_continue -> advances immediately to the next step; user_wait_for_continue: true -> enters WAIT_USER, resumed only by confirm().

WAIT_USER is not "hold, prolonged": HOLDING and WAIT_USER are distinct SudState values. The hold countdown runs to completion before WAIT_USER begins - tick() only decrements hold_remaining while state == HOLDING, so the timer is frozen (not extended) during the wait. It delays the next step, but it's a separate third phase, not a longer hold.

The gating field is user_wait_for_continue, not "presence of a user message": user_message (display text) and user_wait_for_continue (the actual block) are independent fields - a step can show a message without blocking, or block silently.

Open items

  • Pure-hold steps never (re-)set the controller's target temperature. For a step with only hold (no ramp), tasks/sud.py's on_step_changed never calls tc.set_theta_soll()
    • the guard at line 73 requires ramp is not None. The controller's target temperature for a pure-hold step is whatever was left from the previous step's ramp; there's no explicit fallback to the hold step's own temperature field. README.md (Mash schedules section) documents this as designed behavior ("SudTask drives ... theta_soll/heatrate_soll from ramp steps ... counts down hold steps' duration"), so it's intentional, not a bug - but it means a schedule can't express a temperature jump straight into a hold without a ramp block.