Requirements and acceptance
Version 1.0.0. These values are teaching configuration, not sensor calibration.
| ID | Contract | Check |
|---|---|---|
| R01 | Startup JSON reports version, synthetic mode, default threshold and missing input; state begins FAULT | Startup schema plus initial snapshot |
| R02 | Inject time; acquire at most once per turn, no earlier than 1000 ms after prior acquisition; stale after age >3000 ms | Scheduler equality/gap/wrap and timestamp tests |
| R03 | JSON Lines with stable sample fields and JSON null for unavailable temperature | Independent JSON parser and expected event sequences |
| R04 | NORMAL enters ALERT at temperature >= threshold; default 28.0 °C | 28.0 equality and boundary jitter |
| R05 | ALERT clears at temperature <= threshold−1.0 °C; retain state inside band | 27.0 equality; 27.5 from each direction |
| R06 | Missing, nonfinite, or input age >3000 ms enters FAULT; stale value never current | Missing/NaN/infinity/stale tests and replay |
| R07 | Two consecutive fresh valid samples recover; invalid input or a stale gap resets count | Recovery and interrupted recovery; after fault classify using fresh entry threshold |
| R08 | threshold <decimal> terminated by LF; 64-byte maximum before LF, including optional CR; 0–50 °C inclusive; RAM only |
Full-line grammar, range, CRLF, NUL, overflow drain, truncation and repeated-command tests |
| R09 | New process or Monitor::reset() restores threshold 28.0 and FAULT/missing |
Reset tests and replay reset event |
| R10 | Compile exactly the same policy/parser/output .cpp sources for host and firmware |
Build source lists and file hashes |
| R11 | Tests detect seeded source defects; original source still passes | Isolated mutation reports and restored build evidence |
| R12 | Export contains every needed source; second clone compiles/tests without authoring files | Bundle verification, fresh clone host check; Windows second-checkout firmware receipt required separately |
At startup FAULT/missing also uses the two-sample recovery rule. A first valid sample is current but the state remains FAULT/recovering. A second valid sample chooses NORMAL or ALERT using the entry threshold, without resurrecting a pre-fault alert. Missing input means the adapter has explicitly failed to supply a value. NaN and infinity are invalid regardless of threshold.
Call poll(now) before inspecting time-dependent state or changing a threshold. Acquisition calls poll internally. Age is uint32_t(now - sampled_at); the one-wrap interpretation requires chronological calls and gaps below 2^31 ms (about 24.85 days). Replay rejects larger/backward gaps. A scheduling gap does not create invented catch-up samples. Freshness is inclusive at 3000 ms and expired at 3001 ms; tests use an injected clock rather than waiting.
A valid threshold command re-evaluates a currently non-faulted reading against the new entry threshold immediately. It cannot clear FAULT or advance recovery. Invalid commands leave every setting intact. The parser accepts optional leading +/-, at least one integer digit, and optional decimal point followed by at least one fractional digit; it rejects exponents, embedded whitespace, trailing characters and nonfinite words. The optional final CR enables CRLF terminals. Oversized lines are drained to LF before another command can be accepted. A partial line at end of host input returns truncated; firmware waits for LF without blocking its loop. No timeout, persistence, or host timestamp is claimed to measure physical hardware behavior.