Final phase of the autonomous program-execution engine. Multi-record
clausal programs (firmware ≥3.0.0) now run end-to-end:
* WHEN-headed chains dispatch through emit_event() — same code path as
raw EVENT programs, but with optional AND/OR condition guards.
* AT-headed chains schedule like TIMED (absolute or sun-relative).
* EVERY-headed chains fire on a recurring interval (every_interval
seconds — the unit derivation matches the existing programs.py
decode).
New types:
* ClausalChain dataclass — (head, conditions, actions). Built once at
engine construction; engine.chains exposes the list.
* build_chains(programs) walks a slot-ordered Program tuple,
grouping adjacent multi-record records into chains. Stops at the
next clausal head, a non-clausal record, or an empty slot. Drops
chains with no THEN action (they have no effect).
* evaluate_conditions(cs, is_satisfied=fn) — AND-of-OR-groups
evaluator. Empty conditions tuple is True; OR records start a new
group; within a group all ANDs must satisfy; overall True iff any
group satisfies. The detailed semantic decode of each AND/OR record
(zone-state checks, structured TEMP>70-style ops, …) is deferred
to a follow-up — for now ``is_satisfied`` is the integration hook
callers supply.
ProgramEngine.set_condition_evaluator(fn) lets tests / HA plug in a
state-aware evaluator. The default is a stub that passes ANDs and
fails ORs — a usable smoke-test default, deliberately not a real one.
14 new tests covering chain construction (single chain, with conditions,
with multiple THENs, adjacent chains, missing-THEN drop, non-clausal
boundary), the condition evaluator (empty/all-AND/AND-fail/OR-group
separation), and end-to-end execution (WHEN chain dispatch, condition
blocking, custom evaluator, AT chain schedule, EVERY chain interval).
With this the engine implements every program type the panel firmware
exposes — TIMED / EVENT / YEARLY compact-form plus WHEN / AT / EVERY +
AND / OR / THEN clausal. MockPanel + ProgramEngine + .pca decode +
MockState.from_pca composes into a complete "run any panel's programs
autonomously" sandbox.
Full suite: 563 passed, 1 skipped (up from 549, 64 engine tests total).