Clear the gauge reading when a gauge is re-pointed at another path - #577
Conversation
Code review — correctness + testing, findings appliedTwo reviewers, same defectClearing a widget's path left its reading on the dial. The no-path early return ran before Fixed: the signature is computed first and gates the That fix exposed a second one the correctness reviewer had already flagged: The highest-value missing test
All six original tests stubbed Added per gauge: a fake that replays synchronously, asserting the new path's reading is on screen. That is the only test separating "clears stale data" from "clears all data". Applied
Tests went 6 → 15 across the three gauges, plus 5 for the extracted helper. Suite: 2046 passing. Confirmed by review, no change neededThe reviewer checked several things I had reasoned about but not verified, and they hold: every reset target equals its initializer in all three components (so skipping the first run is safe); the radial's computed One decision the tests now record rather than leave implicit: |
fe5e848 to
359b9ad
Compare
Code review — 5 personas, findings appliedReviewers: correctness, testing, maintainability, project-standards, adversarial. Fresh contexts, isolated worktree. The core fix holdsCorrectness traced the diff, the streams directive, Host2's reconfigure ordering and Adversarial could not break it on the scenarios that would matter most: theme switch, unit-preference change, profile reload and dashboard duplication produce no signature change; a same-content config from a new object still compares equal because the signature is a string; data arriving between the clear and the new subscription is impossible synchronously. Three branches the specs did not holdTesting mutation-checked each one:
Radial had the equivalent cases; linear and compass got the main re-point scenario copied but not the rest. The dependency the fix rests on, written downTwo reviewers independently found that the reading comes back only because each effect run passes a new closure to Nothing recorded this, and no test can catch a regression: all three specs fake the streams directive with an Also applied
Not actionedThree findings are real and out of this PR's scope, filed as #585:
Also left: Gate on the head commit: 2057 tests pass, |
dataAvailable, value and textValue were written only inside the stream callback, and a rebuilt subscription against a silent path replays nothing -- the leading null is suppressed with a fresh closure -- so the previous path's needle stayed on the dial as a live reading of the new one. Gate a reset on the path signature so a theme change, which rebuilds the same subscription, leaves the reading alone. Fixes #534
…inel Review found the no-path early return ran before the clear, so emptying a widget's path tore the subscription down and left its reading on the dial -- the same lie, reached another way. The signature is now computed first and gates the observe, and `undefined` marks "never run" so a null signature is a real identity rather than a reset of that guard. Tests: re-point to a path that DOES report (the case separating "clears stale data" from "clears all data"), a cleared path, the re-point after one, zone-state reset, positive controls on the theme tests, and direct coverage for widgetPathSignature. Fixes #534
…-point Mutation testing found three branches the specs did not hold. Moving the clear below the no-path bail-out left linear and compass green; deleting their currentState reset left them green too. Both are now pinned, matching the radial. The compass matched its negative-to-port list against the raw configured path while the signature and the subscription both used the normalized one, so a padded path subscribed correctly and then clamped a negative apparent wind angle to 0 instead of converting it to a bearing. The sentinel's doc comment named the wrong value for the first-run state and left null's meaning unstated, in all three copies. The dependency on observe() receiving a fresh closure each run is now written down where it can be read.
619a524 to
80bc27f
Compare
Why
Re-point a Radial, Linear or Compass gauge at a path that reports nothing — edit
gaugePathfrom the options dialog on a live dashboard — and the gauge kept the previous path's needle position and numeric value, presented as a live reading of the new path.dataAvailablehad exactly one writer, inside thestreams.observe()callback. All three gauges setsuppressBootstrapNull: true, andWidgetStreamsDirective.buildAndSubscriberecreates the suppression closure with a freshseenNonNull = falseon every rebuild, so a rebuild against a silent path filters the replayed leading null and the callback never runs.widget-host2reconfigures in place, so the component instance and its signals survive the save.What
Each gauge now clears its stream-derived state —
dataAvailable,value,textValue, the tagged measure, and the zone state — when the configured path changes identity.The reset is gated on a path signature rather than run unconditionally: the data effect also re-runs on theme changes, and clearing there would blink the needle off and back on at every switch.
widgetPathSignatureis extracted from the directive's existingcomputePathSignature, so the widget and the subscription diff agree on what "the same reading" means (path, pathType, convertUnitTo, source, bootstrap-null policy) instead of drifting apart.One behaviour change beyond the report: the Linear gauge used to reset
effectiveUniton every run of the effect, so a theme switch briefly redrew its scale in the widget's stored unit instead of the server-resolved one. That reset moved under the same gate.Tests
Two per gauge — re-point to a silent path clears the reading, and an unrelated reconfigure (theme change) does not. All four fail on
main(the linear "keeps the reading" case fails there on theeffectiveUnitwipe described above).Full suite: 2035 passing, three consecutive clean runs. One earlier local run had a spec file abort during teardown (the documented
EnvironmentTeardownErrorclass, unrelated to these files); it did not reproduce.Fixes #534