Thirteen test harnesses executed workflow `run:` bodies with their own bash
flags. Four of them used `bash -eo pipefail` "as in Actions", but the runner
executes a step without `shell:` as `bash -e {0}`: pipefail comes only from an
explicit `shell: bash` or from `set -o pipefail` in the body. The harness
supplied protection the step did not have, so a step that lost its pipefail
stayed green in tests (#729, #737, #751); the reverse also happened - the
#793 guard test was red only because of the harness flag.
test/helpers/workflow-step.mjs resolves the shell like the runner (step ->
jobs.<id>.defaults.run.shell -> workflow defaults.run.shell -> unset), maps it
to the runner's command lines (unset -> `bash -e {0}`, bash -> `bash
--noprofile --norc -e -o pipefail {0}`, sh, python, custom templates with
{0}), writes the body to a file and executes it by path. Unsupported YAML or
shells are refused loudly instead of guessed. All step-executing tests now go
through runStep(findStep(...)).
Witnesses: a step whose left pipeline side fails is green without a shell
(negative test) and red with `shell: bash`, job defaults or `set -o pipefail`;
the #751 executed test now also shows that the same real steps without their
pipefail line stay green, i.e. the test sees a removed pipefail; a guard fails
on any test that runs a step with its own bash flags. TESTING.md rule 7 names
the helper.
Issue: #766
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
A 3 s hold in kiosk mode opens the per-screen size dialog. The core card
draws it (`_renderKioskDialog`, `hp-dialog` from the core bundle, core
i18n keys) and the summary-panel runtime, which every card connects on
mount, saves it. Yet `_kioskDialog` was listed in `editorRuntimeRequested`,
so every hold on a wall tablet downloaded the whole editor chunk; with the
chunk unreachable the kiosk also showed "Could not load the editor" (#757
only capped the retries). Dropping it from the condition is the whole fix;
the loader and the gesture are untouched.
Proof: new smoke_kiosk_scale_no_editor runs a cold page, a real 3.4 s touch
hold (CDP synthesizeTapGesture: a long press, not a tap on the new backdrop),
sliders by keyboard, Reset/Close taps, a second hold to reopen, the chunk
refused by the network, and the ordinary editor entry as control. On dev it
is red in 3 network checks (chunk fetched; refused chunk = 2 requests and a
toast); with the fix all 20 checks pass. smoke_lazy_editor_chunk case (a)
pinned the old cycle and now asserts zero requests and zero notices; the
(checked by hand). New mutant kiosk-scale-dialog-loads-editor-runtime
(caught by hand), listed under browser lifecycle guards; the smoke is
linked in smoke-links because it names none of the touched members.
Issue: #763
User-Visible: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
A topology snapshot is labelled stale after one hour instead of five
minutes. Nothing refreshes the map on its own and a Zigbee2MQTT scan alone
can take more than ten minutes, so the old age marked almost every map. A
failed refresh still marks the retained snapshot stale at once.
Route lines and arrows now paint over battery indicators. Endpoint markers
rise above the route layer as a whole (#464), battery included, so the
overlay renders a copy of the routes one level higher (z 9, before the
captions in DOM order) clipped to the endpoint battery frames. The frames
are measured in the existing layout frame of the overlay; outside them the
picture is unchanged: endpoint cores stay above routes, captions above
routes and batteries (#792 AC7). The copy carries no data-hp, so route
counts and selectors stay as they were; parent-caption arrows update in both
layers.
Tests: smoke_device_battery_zigbee gains a local link whose route crosses a
whole battery frame — the hovered endpoint's and the neighbour endpoint's,
Flat and 2.5D, sampled along the drawn line. On dev three of the four probes
are red (2.5D neighbours are not raised today). New mutant
zigbee-route-copy-under-battery (manually red); the parent-LQI mutant anchor
follows the refactored route list. smoke_zigbee_topology_hover age fixtures
move to the one-hour boundary. The golden routes control hides both layers;
device-battery-zigbee-overlap-dark changes (arrowhead now over the battery)
and needs acceptance via ci:golden.
Issue: #808
User-Visible: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
Ship the reviewed battery-indicator shadow and per-device opt-out together
with the global All / Low only / None display modes. Rebuild the committed
bundle, refresh the pixel-identical documentation fingerprint, and tighten
the beta ratchets.
Issue: #806
Issue: #807
User-Visible: yes
Release: v1.80.0-beta.6
General settings offer All / Low only / None for battery indicators as a
three-button segmented control, the same visual as the light-source choice
in the device dialog. Low only keeps just the red low state (below 20 %, or
a binary battery sensor that is on); normal, warning and unknown are hidden.
Storage keeps #792 compatibility: absent = All, false = None, the exact
string "low" = Low only. The backend accepts bool or "low" and refuses any
other value; old frontends read "low" as All, old backends refuse to save it
(documented in CONFIG-COMPATIBILITY). One presentation option
(batteryLowOnly) filters the resolved indicator, so View, kiosk, 2.5D,
space-card and the device dialog preview share the same rule.
Track raised ship -> show: new i18n keys, a new config value and Python.
Tests: unit (mode resolver, write, dirty, low-only presentation), backend
validator and support package, smoke_device_battery (low-only keeps only
red), smoke_general_settings_form (radios, save/reopen/clean, All removes
the key). New mutant battery-low-only-shows-every-state; the backend mutant
anchor moves to the new validator.
Issue: #807
User-Visible: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
All eleven documentation frames are pixel-identical in the pinned Linux environment; update only their source fingerprint for the release candidate.
Issue: #792
Issue: #804
Issue: #776
User-Visible: no
Release: v1.80.0-beta.5
Count delivered page errors after awaited browser close. Exercise the public
harness lifecycle with deterministic negative subprocess cases and retain the
unchanged Chromium probes for the real transport. Move the premature-snapshot
mutation witness to the deterministic Node suite.
Issue: #776
User-Visible: no
Address the in-scope Medium from CODE-REVIEW-792-r1 with a deterministic
golden scenario that uses actual topology rendering and overlapping battery
ink. Keep the existing battery boards and their reviewed images unchanged.
Align the English guide's setting name with the shipped locale (Low).
Issue: #792
User-Visible: no
Append battery visibility after the existing tooltip, radar and volumetric
controls. The established 2.5D setting remains third. Extend the existing
browser oracle to allow exactly the new fourth option while preserving
its original first-three ordering requirement.
Full Validate 37461167422 exposed the ordering regression. It was reproduced
locally before the change; volumetric_setting, general_settings_form and
settings_dialog_cards all pass after it. Record the final gzip measurement.
Issue: #792
User-Visible: yes
Full 203-scene capture from published d4d48bb040
on pinned WSL/ext4 toolchain: 200 unchanged scenes and three new scenes;
105 environment witnesses passed (required floor: 10).
Reviewed light/dark boards and mobile framing against owner-approved MDI
glyphs and the designer's sizes, gaps and colors. Correct measured gzip
documentation by three bytes after the final source fingerprint settled.
Issue: #792
User-Visible: no
Release: v1.80.0-beta.4
Baseline-Reviewed-Local: sha256:7e238b1840401f791e00e53f5c77388006a0b04cc8d4e535775b3a5dc9f9db03
Resolve one stable battery source independently of the functional device face,
include battery-only ticks in immutable render snapshots, and add a default-on
shared preference. Keep LED strips excluded and Zigbee captions above the
passive, out-of-flow indicator. Use the owner's accepted four MDI icons in the
designer's frame geometry and colours.
Include resolver/backend/browser/pixel contracts and three new golden scenes.
Recalibrate the owner-approved absolute/raw size budgets; preserve the existing
initial View rolling ceiling and band. Lazy extraction remains separate #805.
Issue: #792
User-Visible: yes
Zigbee destination names and readable, non-overlapping hover diagnostics.
Local self-review authorized by the owner; WSL acceptance is green.
Issue: #802
User-Visible: yes
Release: v1.80.0-beta.4
Keep diagnostic captions and the matching pointer tooltip readable, including
late overlay paint, camera/resize changes and narrow-card fallback. Preserve
focus, touch and device actions. Add pure placement and real-mouse witnesses.
Issue: #802
User-Visible: yes