# Ручной чек-лист по поверхностям > Приложение к [`docs/TESTING.md`](../TESTING.md): перенесено оттуда дословно (#634). > Индекс всех приложений — [`README.md`](README.md). ## Modes (v1.25.0) ★ - [ ] The card always loads in **View**; edit modes are never restored [manual] - [ ] View: pan/zoom/space-switch/tap/long-press/tooltips only — dragging an icon, label or opening does nothing; panning may start on top of an icon [manual] - [ ] View header: space tabs + count + zoom + editor tabs, the general-settings cog and the per-space gears (visible in EVERY mode since v1.30.1/v1.30.3 for users who may edit); no editor toolbars [auto: smoke_modes] - [ ] Space-tab reorder (#243): in an editor with at least three spaces, use a real mouse drag while browser pointer capture remains on the held tab; moving left resolves the tab under the cursor and paints its left divider, moving right paints the right divider, and each valid drop saves exactly once [auto: smoke_space_tab_reorder; golden: space-tab-drop-before-light, space-tab-drop-after-dark] - [ ] Move a held space from a valid target out over the plan: the divider clears immediately and release does not save. `pointercancel` and removing the card mid-drag also end the gesture without changing order [auto: smoke_space_tab_reorder] - [ ] A sub-threshold mouse gesture remains a tab click; the next click after an outside release still works. Touch, View and a card fixed to one `floor` never expose reordering [auto: smoke_space_tab_reorder] - [ ] Plan: markup toolbar, space gears, +space, ⚙ palette; device icons hidden, labels/openings draggable; orange stage frame [manual] - [ ] Devices: icon drag works, click opens the marker editor directly; +/👁/↺/⬡ buttons; accent stage frame [manual] - [ ] Mode tabs hidden for non-admin users; segmented control highlights the active mode - [ ] Openings in View (v1.28.1+): the door/window/gate itself is a pure drawing — no cursor change, no hover outline, no hit target, no click, regardless of bindings [manual] - [ ] The LOCK BADGE is the one exception: when a lock is bound it is shown and clickable in View (pointer cursor, click → door/lock info card); inert in Plan so it does not fight editing [manual] - [ ] Device icons in View show a pointer cursor (no grab); grab only in the Devices mode [manual] - [ ] In Plan an opening is interactive: grab cursor, hover outline, drag along walls, click (any tool) opens its properties — with a 3 px drag threshold since v1.43.1, so a tap is never swallowed [auto: smoke_inert_openings] - [ ] Opening drag rulers (2026-08-03): while an opening is dragged, a measure badge on EACH shoulder shows the along-the-wall distance from the wall end to the nearest opening edge, live; the wall is ONE room's edge — the edge the opening is snapped to — so a neighbouring room's collinear edge is never merged in; at that edge's center (±half a grid step) a perpendicular dashed tick appears and the center magnet-snaps; there is no modifier that disables the magnet; badges and tick vanish on release [auto: smoke_opening_measure + unit openingShoulders] - [ ] Physical rulers while PLACING a new opening (#238): one room shows two lines/four endpoint ticks from the preview jambs to the physical inner face endpoints. A shared wall shows four independently resolved lines/ eight ticks, two on each room face. A finished independent wall stops per direction at the nearest physical face of a connected wall/partition and falls back to its own endpoint where none exists. Lines, ticks and labels update in the same frame as the preview and are pointer/ARIA inert; saved opening drag retains the legacy two badges and no new lines [unit: opening-dimensions; auto: smoke_opening_inner_distances + smoke_opening_measure] - [ ] While PLACING a new opening: pressing **Opening** only opens the shared secondary tray; choosing Window / Door / Gate arms the 120 / 90 / 300 cm session preset. Moving over a physical wall shows the complete architectural symbol at 50% opacity, above the masonry, together with the physical dimension badges and the existing centre tick/magnet. The preview accepts pointer hits anywhere inside a thick wall body, is absent on virtual spans and existing openings, and never carries an interactive or persistent identity. A direct click without prior hover resolves the same candidate authoritatively and opens its dialog. Save and Cancel keep the selected preset for repeated placement; tool/mode/space exit and Esc clear it [unit: opening-placement; auto: smoke_opening_preview + smoke_opening_measure; golden: opening-placement-door-thick-wall-dark] ## Onboarding ★ - [ ] Empty config, HA has floors → floors-import wizard offers them sorted by level [manual] - [ ] Wizard: uncheck all → "Create" disabled; "Start from scratch" → classic dialog - [ ] Wizard: N floors → space dialog per floor with prefilled name and progress "i of N"; Skip skips one; Cancel aborts the whole queue [manual] - [ ] After the last wizard space (or first manual space) → markup mode auto-opens with a toast - [ ] Empty config, no floors → classic "New space" dialog auto-opens once per session - [ ] All floors skipped, nothing created → empty state with "Add space" button remains usable [auto: smoke_optional_space_model] ## Spaces ★ - [ ] Create with an image (SVG, PNG, JPG, WebP) → correct aspect, crisp at zoom (SVG) - [ ] Oversized plan (>8 MB) → readable error toast, dialog stays open - [ ] Create with "No image — I'll outline rooms by hand": orientation landscape/portrait/square respected [manual]; borders+names default ON [manual] - [ ] Draw-space (no background) renders a WHITE canvas (paper-like), markup works on it; room borders/names stay legible on white [manual] - [ ] Edit: rename; replace image; **switch image→draw detaches the plan** [manual] - [ ] Delete space with rooms/devices → tab disappears, layout of other spaces untouched - [ ] Delete the last space → empty state without console errors; active editor gestures and drafts are aborted, and creating the first space remains available [auto: smoke_optional_space_model] - [ ] Display settings: borders toggle, names toggle, color picker + opacity slider live-preview after save, fill selector [manual] - [ ] Fill "zigbee": rooms tint red→green by average LQI; rooms without zigbee stay unfilled [manual] - [ ] Fill "lights": yellow when any light on, grey when all off, unfilled when the room has no lights [manual]; toggling a light from the plan recolors the room - [ ] Fill "temperature": blue below the comfort range, green inside, yellow above [manual]; comfort bounds editable inline on the radio row (swapped bounds tolerated [manual], clearing a field cannot zero a bound [manual]); rooms without a temperature reading stay unfilled [manual] - [ ] Fill mode is a radio group (no dropdown); labels carry no color legend - [ ] Room hover adds a subtle accent wash and double contour without changing the underlying room fill or Glow brightness - [ ] Room tooltip shows average room temperature and humidity after the area line and before LQI; missing values are omitted [auto: smoke_ux_fixes] - [ ] General settings can hide only the room tooltip: default/Cancel/save/reopen semantics, skipped area work, persistent room highlight, unaffected device tooltip and restoration on the next mouse move [auto: smoke_room_tooltip_toggle] - [ ] Average room temperature counts ONLY thermometer/air-monitor devices — fridges, TRV heads, smart-plug chip temperatures (`*_device_temperature`) and diagnostic-category temps are excluded [manual] - [ ] Space dialog is 500 px wide; the comfort-bounds inputs are compact (56 px) - [ ] The scale input is compact (72 px), not full-width; it shows cm in metric HA and inches in imperial HA [manual; auto: smoke_space_scale_defaults] - [ ] A new manual space and every floor-import draft start at 1 cm in metric HA or exactly 1 inch/2.54 canonical cm in imperial HA. Opening and saving an existing 5 cm, fractional or missing legacy value without editing the field is lossless; changing language does not rewrite the canonical draft [auto: smoke_space_scale_defaults] - [ ] Physically equivalent rich fixtures at 1 cm and 5 cm have equal View, Plan-with-grid-masked and static-card pixels/critical bounds. The grid has five times the intervals only; openings retain their edge hit target, and physical/screen-fixed layers are not double-scaled [auto: smoke_grid_scale_invariance; unit: grid-scale.test.mjs, opening-symbol.test.mjs, canvas.test.mjs] - [ ] General settings (⚙ in the header): fill colors grouped by mode (lights on/off/none, temp cold/comfy/hot, LQI weak/strong), each with its own opacity slider [manual]; Reset restores defaults; saving defaults stores nothing [manual] - [ ] Custom fill colors apply to the full card AND the static space-card - [ ] **Room colour follows the room's own mode (#581):** a room colour counts only with the room's own «Свой цвет»; «Как у пространства» clears the colour draft at once, the saved room keeps neither `fill_mode` nor `custom_fill`, and the plan paints the space colour. A colour stored without the mode (an orphan from an older editor) paints the space colour, opens as «Как у пространства» without a colour row and is dropped by a plain save; `name_scale`/`label_scale` survive [unit: `logic.test.mjs` `#581 AC1`; auto: `smoke_room_settings` step 7, `smoke_space_settings` `customRoomOrphanInheritsSpace`; mutation: `room-orphan-colour-wins-again`] - [ ] LQI gradient interpolates between the configured weak/strong colors [manual] - [ ] Per-space "Show zigbee signal (LQI)" toggle hides/shows the badges next to devices and the signal line in room tooltips for that space only [manual] - [ ] Device icon badge is centred exactly on its point (no 1 px down-right drift) [manual] - [ ] Device glyph is centred within its badge (no vertical drift — real ha-icon is block+line-height) [manual] - [ ] Room hover highlight still works when custom borders/fills are on - [ ] Settings persist across reload and other browsers (server-side) ## Room markup editor ★ - [ ] The toolbar has one **Walls** tool and no separate Room outline or Partition drawing button; Split remains available [auto: smoke_unified_wall_tool + unified-wall-tool-source.test] - [ ] Grid appears; dots snap; the wall chain draws pair-by-pair; shared walls reused - [ ] Ruler: while drawing, the length of the current segment follows the cursor (metres, or feet+inches on an imperial HA); scale = canonical per-space `cell_cm` (new-space default 1 cm or 1 inch; missing legacy fallback 5 cm) - [ ] Every completed segment is saved immediately as one ordinary partition. Changing tool, editor or floor clears only the session-local chain state; accepted walls stay unchanged and are not resumed after reload/remount [auto: smoke_unified_wall_tool + smoke_free_walls + smoke_plan_snap_overlay] - [ ] A legacy warm-viewport token `partition` still opens the unified Walls tool, but `partition` is not a runtime tool-state, dispatch branch or golden-matrix option [auto: wall-face-graph.test + unified-wall-tool-source.test + golden-matrix.test] - [ ] Re-selecting Walls, Reset, pan, pinch, a second pointer, `pointercancel` and a suppressed synthetic click never finish a chain or save an extra segment [auto: smoke_unified_wall_tool] - [ ] The active segment keeps a visible thin axis and endpoint above a thick preview. Distinct nodes inside the ambiguity radius fail closed with a zoom prompt. Shift constrains the actual endpoint to the nearest exact 45° ray, including exact ray/wall intersections; the angle colour follows the stored vector, not pointer intent [unit: plan-snap-overlay.test.mjs; auto: smoke_plan_snap_overlay + smoke_plan_drawing_repairs]. - [ ] With no active chain, a click strictly inside the smallest unoccupied exact wall face offers a room; boundary/snap hits and Shift bypass it. Keep/Cancel is a true no-op. If one endpoint→endpoint or endpoint→solid-line repair closes the face within 2 physical cm, the red diagnostic is offered and moves geometry only together with Create. A larger gap, hosted-opening mover or multiple possible repairs fails closed [unit: wall-face-graph.test.mjs + wall-face-repair.test.mjs; auto: smoke_plan_drawing_repairs]. - [ ] Deleting a room uses an accessible Keep walls / Delete walls / Cancel dialog. Keep materialises only exclusive positive solid intervals as partitions and rehosts their openings; Delete cascades only openings on those exclusive walls. Shared walls/openings, explicit partitions and partition-hosted openings survive. Either accepted choice is one Undo/Redo/save transaction [unit: room-deletion.test.mjs; auto: smoke_plan_drawing_repairs]. - [ ] There is no "Erase" tool in the markup toolbar (removed in v1.19.0) - [ ] Rooms never overlap (v1.20.0): a click strictly inside an existing room is refused with a toast; a click ON a shared wall (including mid-span of a longer neighbour wall) still works - [ ] Closing an outline drawn AROUND an existing room is refused; the outline stays open - [ ] Merge (v1.21.0): two rooms sharing a wall merge into one; the dialog picks the surviving name/area; rooms touching only at a corner or apart are refused with a toast - [ ] Split (v1.21.0): click a room, then two points on its walls — the bigger part keeps the name/area/devices, the smaller opens the new-room dialog; Cancel leaves the room whole - [ ] Split: a cut with an end off the wall, or along a wall, is refused with a toast - [ ] Split: the click snaps to the nearest wall, so it works on non-grid-aligned rooms (imported/legacy polygons), not only on rooms drawn on the current grid [manual] - [ ] Split: a click far from any wall (middle of the room) is a miss with a toast — the wall-snap pull is capped, accidental clicks do not pick a wall [manual] - [ ] Esc / Ctrl+Z removes the last dot (and its line); Reset clears the active path - [ ] A latest segment that creates bounded endpoint, T or X faces opens the room queue in area/key order. Existing exact/partial rooms and physical gaps, including opening cuts, are excluded; nested rooms remain eligible [unit: wall-face-graph; auto: smoke_unified_wall_tool + smoke_room_autoclose] - [ ] Create and Keep as walls answers are buffered. The last answer applies all rooms and unconsumed wall atoms as one Undo/Redo transaction; Cancel/Esc restores the terminal draft without partial rooms [auto: smoke_unified_wall_tool] - [ ] A clean divider across one existing room offers only the smaller child; the larger child keeps the original room id, name, area binding, settings and device placement [auto: smoke_unified_wall_tool] - [ ] Room dialog: area list shows only unassigned areas; picking an area prefills the name - [ ] Room dialog uses the medium width and its body has no horizontal overflow; long options stay inside it at desktop and narrow widths [auto: smoke_editor_tabs; manual: narrow viewport] - [ ] "No area" room (decorative) requires a name; saves with `area: null` - [ ] Cancel in a Walls face queue restores the persisted terminal draft - [ ] Saving a room with an area: area devices appear with icons; positions are fixed into the layout [manual] - [ ] Delete-room consequences are chosen explicitly as described above; there is no Erase tool - [ ] Device icons hidden during markup; visible again on exit ## Devices on the plan ★ - [ ] Auto devices appear only in rooms bound to their area [manual] - [ ] **Entity/parent ownership (#226):** placing `entity:X` removes X from its automatic parent. A visible unclaimed sibling keeps one residual parent; an empty or HA-hidden-only residual removes it. State, primary/action, `allEntities`, light/Glow and LQI cannot see X twice [auto: unit `devices.test.mjs`; browser `smoke_entity_parent_dedup.mjs`]. - [ ] Two explicit markers `entity:X` + `device:D` coexist and the device stays complete. A user-hidden live entity marker still owns X, while an entity tombstone returns X to the parent; disabled entity ownership follows the known full-registry relation [auto: unit `devices.test.mjs`]. - [ ] The #94 curtain boundary is deliberate: untouched hidden `cover.*` stays cover-first, but after placing the only visible auxiliary switch the hidden-only automatic remainder disappears. An explicit `device:D` restores the complete curtain beside that entity marker [auto: unit `devices.test.mjs`]. - [ ] Renderer and seeder cannot drift back to exact-binding-only ownership [mutation: `entity-marker-kept-in-parent-device`, `entity-marker-parent-seeded`]. - [ ] Filtering hides bridges/groups/scenes/excluded integrations; 👁 "show all" reveals [manual] - [ ] Duplicate "name|area" numbered ("Lamp", "Lamp 2") [manual] - [ ] Light groups fold their single lamps; `group_lights=false` unfolds [manual] - [ ] Drag anywhere (no edit mode), snaps to grid, persists after reload, per space - [ ] ↺ reset restores auto layout after confirm - [ ] Temperature badge on thermometers; LQI value under zigbee icons with red→green color - [ ] Unified live states (dev, owner 2026-08-05; lock palette #219): actual work is yellow; open door/window and open valve are orange; unlocked lock is red and locked lock is green; covers stay neutral and morph their icon; unavailable is faded. The plate and the activity effect come from the same semantic resolver - [ ] State icons (v1.26.0): auto icons morph with state — door/window/garage open↔closed, lock locked↔unlocked, bulb on; custom icons and unavailable states never morph [manual] - [ ] display "Value instead of an icon": the marker shows the measurement (°/%/unit) as its body, small badges hidden; non-numeric fallback keeps the icon [manual] - [ ] RGB lights (v1.27.0, contract changed in v1.52.0): a lamp's colour lives in its glow spot and the activity-effect fallback ONLY — the icon/badge/border get no RGB tint; explicit activity color still wins; off lights unchanged [auto: smoke_light_badges + smoke_rgb_alarm] - [ ] Alarm pulse (v1.27.0, unified dev): leak/smoke/gas/CO/siren in `on` and an alarm control panel in `triggered` get a red plate and red pulse over every dynamic display mode and even with ordinary live-state dressing off; `static_icon` is the deliberate neutral exception after an editor warning; clears on 'off'; unavailable never alarms [manual]; reduced-motion is static - [ ] Render cost (v1.43.1, audit L1): geometry (space model, open pairs) is computed once per config change, not per HA state push — smoke asserts zero recomputations across 10 state pushes and recomputation after an edit; the plan still renders dashes/islands correctly [auto: smoke_render_perf] - [ ] Opening tap vs drag (v1.43.1, audit L4): a tap on a door in the Plan editor opens its properties (3 px threshold like the other pipelines) and writes nothing; a real drag that ends where it started also writes nothing [auto: smoke_render_perf] - [ ] Concave containment (v1.43.1, audit G2): an island room inside a U- or L-shaped parent is accepted and punches the evenodd hole; a traced duplicate outline is still NOT containment [auto: smoke_inert_openings] - [ ] Backend hardening (v1.43.1, audit B2-B5): the admin check fails closed when the entry is unavailable; layout/set and config/set without expected_rev may bootstrap revision zero, but over a non-empty store each returns `conflict` without changing its document/rev/backups or firing an event (including a no-op body); explicit stale revisions are rejected; a production config-writer inventory requires expected_rev; NaN/Infinity coordinates and oversized collections are rejected [auto: tests_backend/test_ha_websocket.py + coordinate-write-barrier-guard.test] - [ ] Save race (v1.43.0, audit L2): make a markup edit, then press Save in any dialog within 500 ms (or let another client save) — the markup edit must survive and reach the server; a failed reload now shows a toast [auto: unit: tests_backend] - [ ] Niche split (v1.43.0, audit G1): a cut that starts AND ends on the same wall carves a niche; the two parts' areas must sum to the original (the invariant is enforced in code and asserted for every split test) [auto: smoke_save_race] - [ ] Authenticated content (v1.43.0, audit B1): plan images and marker files are only reachable through /api/houseplan/content/… with a session; the old /houseplan_files/plans|files paths return 404 after a restart; old stored URLs keep working (rewritten on read) [auto+manual] - [ ] Every editor option is storable (v1.45.3, issue #3): set a sensor to "value instead of an icon" and save — no validation error, the value shows on the plan after a reload. Same for each tap action and each fill mode [auto: backend test_every_display_mode_the_editor_offers_is_accepted and neighbours, test_a_marker_showing_its_value_can_be_saved] - [ ] Room settings button (dev): detached from the (movable) name label — always at the room's geometric centre, one button-height below it; sized at 70% of a device icon and zooming WITH the plan; the small metric rows under the room name now show in the plan editor too [auto: smoke_room_cards gearDetached/plainInPlan] - [ ] Working plate remains universal: in a source-glow space a lit lamp or other actually working device stays yellow in View and in every editor; the glow pool is an additional spatial indicator, not a replacement [auto: smoke_light_badges] - [ ] Size/angle parity (v1.52.1, HP-1513-01): a marker with size 3 / angle 37 scales x3 and rotates on BOTH cards [auto: smoke_size_angle_parity] - [ ] Tap runs an automation (dev, owner's spec 2026-07-29): the tap-action list has "Run automation/script/scene" with a searchable picker; saving without a target is refused; the confirm checkbox guards toggle AND run (our dialog, Esc/cancel = no call); automation.trigger / script.turn_on / scene.turn_on per domain; a deleted target toasts and calls nothing [auto: smoke_tap_run + unit resolveToggleIntent/runServiceFor + backend test_run_target_is_bounded_to_runnable_domains] - [ ] Universal Toggle state (#94): the option is visible for device, entity and virtual markers; the separate Open/close option is absent. The hint names the exact target(s), current state, next effect and skipped refs. Exact entity never retargets to a sibling; explicit controls never fall back to the controller; partial groups call exactly the shown available subset; a no-target tap is a quiet no-op. Locks, alarm panels and garage/door/gate covers are explained secure no-ops. Cover/valve use closed→open, open→close, moving→stop when supported, otherwise their domain toggle. Confirmation re-resolves current state but cancels if the target set changed. Opening and saving an untouched legacy `cover` or an absent light default preserves the original token/absence; an intentional selector edit writes `toggle`. Feature-gated climate/water-heater/siren/ camera/media-player/legacy-vacuum entities require their exact HA bits; an empty service catalog is unsupported. If #73 retains an older visual device while live controls change, click calls only the current controls [auto: test/device-toggle.test.mjs + smoke_cover_tap + smoke_cover_not_primary + smoke_controls + backend action-schema parity] The synthetic HA fixture publishes its service catalog explicitly, so browser checks exercise the same fail-closed resolver as production - [ ] A cover is NEVER painted (dev, owner 2026-08-04): «у штор не должно быть жёлтой подложки никогда, индикация открыто/закрыто за счёт морфинга иконки». Walk one curtain through closed / open / ajar / opening / closing: the plate is the plain neutral badge every time — never the yellow «включено» one, never the orange «открыто» frame it used to wear while open — the icon is the only open/closed signal, and the breathing `.activity-transition` ring appears in the two travelling states and nowhere else when «Icon + activity» is selected. The morph is exhaustive: every device class gives two DIFFERENT glyphs (awning included), a cover with no device_class morphs within its own auto-icon family (mdi:roller-shade, mdi:garage-variant), a hand-picked icon morphs only inside the pair it was picked from, and an unknown/unavailable state morphs nothing. NOT touched: an open door / window binary sensor and an open valve still wear the orange «открыто» frame (a valve has no icon pair, so the frame is all it has); lock keeps its separate red-unlocked/green-locked palette [auto: smoke_cover_no_plate + unit stateIcon «every class, both ways»] - [ ] Cover target and indication parity: on a device where a hidden functional `cover.*` competes with an auxiliary option switch, the shared resolver selects the cover, calls only it and supplies the same entity to icon morph/activity. A mixed lamp+cover remains a lamp unless the universal toggle result actually selects the cover. Cover presentation remains neutral in every state and cannot be painted yellow by its controls; secure cover classes call nothing and never fall back to info [auto: smoke_cover_not_primary + smoke_cover_plate_precedence + test/device-toggle.test.mjs] - [ ] Light-source badges (current contract): in glow fill a lit lamp's badge stays yellow just like a lit socket; the pool keeps the source's RGB while the marker keeps semantic yellow. Other fills behave identically; morphing and the activity-colour fallback survive [auto: smoke_light_badges + smoke_rgb_alarm] - [ ] Icon size multiplier scales the glyph (dev): set a marker's size to 3 — the icon inside grows with the badge instead of staying default [auto: smoke_icon_scale] - [ ] Auto-grid parity (v1.51.2, HP-1511-01): with an empty layout, a visible device among hidden ones sits at the same spot on both cards [auto: smoke_hidden_flag autoGridParity] - [ ] Activity ghost (v1.51.2, unified dev): a hidden icon+activity marker shows its base icon and no effect [auto: smoke_hidden_flag rippleGhost*] - [ ] Hidden LQI parity (v1.51.1, HP-1510-01): a room whose only Zigbee devices are hidden paints the same lqi fill on the full and the static card [auto: smoke_hidden_flag lqiParity] - [ ] Ghost shows no numbers (v1.51.1, HP-1510-02): a hidden value-display device renders as a plain ghost — no value/temp/hum/LQI, no icon morph [auto: smoke_hidden_flag ghostHidesValue] - [ ] Hide-from-plan flag (dev, docs/FILTERING.md): every existing device dialog has a bottom-left "Hide" / "Show" action, incl. virtual; the change is applied by Save; hidden devices vanish from every mode and the count, still count toward room LQI, cast no glow/light fill; the device editor's "Show hidden" (local, per tab) shows them as BLUE dashed ghosts — distinct from a grey unavailable icon — with NO live state paint (no yellow, no alarm, no activity); showing and saving keeps a hidden:false marker (re-seed protection); an old config materialises on first load by an editing client and legacy clients keep the old behaviour until then [auto: smoke_hidden_flag + unit seedHiddenBindings/seeded/legacy] - [ ] Yellow means working (dev): a TRV whose hvac_action is heating glows yellow; one that is merely enabled (idle) or has a service switch on (anti-scaling, child lock) stays dark; a lit light turns its state on by the same condition that lights the glow pool and shows the yellow badge even where that pool is drawn [auto: smoke_yellow_principle + smoke_light_badges] - [ ] Activity baseline (beta.10 audit): rebuilding the device registry seeds the current snapshot immediately, so the very first later motion/event transition is detected. Rebinding a marker's effective source clears the old source's finite flash in the same update [auto: smoke_motion_sense] - [ ] Touch gesture ownership (dev): in the plan editor on a phone, pinch zooms and a moving finger pans; releasing after a gesture does not draw a point and a clean tap still does. In View, both marker-first and stage-first pinches change zoom; the latter holds its second contact on the marker for more than 600 ms. Neither order opens the local card, HA more-info, confirmation/service or a compatibility click/contextmenu action. Reverse release, a third contact, pointer cancel and lost capture stay blocked, while the next deliberate single-touch tap and long press work once without waiting; real mouse and keyboard context menus remain available [unit: touch-gesture-click-guard; auto: smoke_editor_gestures; mutations: touch-pinch-click-block-cleared-on-terminal, touch-pinch-marker-hold-rearmed, touch-pinch-contextmenu-guard-removed] - [ ] Legacy geometry parity (v1.50.4, HP-1503-01): a store with a zero viewport and a negative rect renders identically sane in BOTH cards — full canvas fallback, normalised rectangle [auto: smoke_legacy_geometry] - [ ] Sizes are positive (v1.50.3, HP-1502-01): view_box or room w/h of zero or below is refused; a store that already holds one opens on the full canvas, not a blank screen [auto: test_sizes_are_not_coordinates + unit safeViewBox fallback] - [ ] Room card layout (v1.50.3): the settings button is the bottom row of the card and the room name sits in the same spot in view and plan modes [manual; verified by vb-coordinate measurement] - [ ] Geometry bounds (v1.50.2, HP-1501-01): a config with a 1e100 room vertex is refused by the server; one already stored still renders with a sane frame [auto: test_geometry_magnitudes_are_bounded + unit contentBounds legacy case] - [ ] No-op repair (v1.50.2, HP-1501-02): geometry/repair with a typo'd space id errors, moves no revision and keeps the previous backup undoable [auto: test_a_noop_repair_does_not_eat_the_backup] - [ ] Card below other dashboard content (v1.50.1, HP-1500-02): place the card after a tall card in a normal dashboard — the plan still gets most of the viewport instead of a zero-height stage [auto: smoke_zoom_out] - [ ] Frame never degenerate (v1.50.1, HP-1500-03): a space with one lone marker opens with canvas around it, not an empty scene; an absurd stored coordinate neither hides the plan nor is accepted by the server [auto: unit contentBounds + backend test_layout_coordinates_are_bounded] - [ ] Stranded migration repair (v1.50.1, HP-1500-01): geometry/repair with dry_run previews, applies with a backup, undo restores; wrong space is recoverable [auto: test_geometry_repair_is_explicit_previewable_and_undoable] - [ ] Editors see the whole canvas (v1.50.0, HP-1490-03): a hand-drawn space with one small room opens content-fit in View; switching to the plan editor shows the full square with room to draw a second room far away; back to View restores the content fit [auto: smoke_audit_1490] - [ ] Save waits for a picked plan's proportions (v1.50.0, HP-1490-04): pick a saved plan and hit Save before the thumbnail loads — the stored aspect is the real one, never the previous file's [auto: smoke_audit_1490] - [ ] Zoom goes below the fit (v1.50.0): minus past 100% floats the plan centred, floor at 0.4x; entering an editor keeps the stage inside the viewport [auto: smoke_zoom_out] - [ ] Migration crash recovery (v1.50.0, HP-1490-01): kill HA between the two store writes of the square migration — the next start finishes the layout half from the saved intent [auto: test_square_migration_finishes_after_a_crash_between_the_writes] - [ ] Parallel upload quota (v1.50.0, HP-1490-02): two simultaneous uploads with one slot left — exactly one succeeds [auto: test_parallel_uploads_cannot_slip_past_the_quota_together] - [ ] Zoom opens on the content (v1.49.0): a space with no background and one small room opens with that room filling the screen, with a small margin. With a background it still fits the whole image [auto: unit: contentBounds] - [ ] Deleting a picked plan is refused (v1.49.0, HP-1470-02): pick a saved plan, reopen the list — its delete button is disabled. Ask the server to store a plan url whose file is gone: `missing_plan`, and the revision does not move [auto: smoke_saved_plans + backend test_config_set_refuses_a_plan_that_no_longer_exists] - [ ] Portable import rechecks local content under its paired-write lock: a plan or marker PDF deleted after preview fails with `missing_plan` or `missing_content`, and neither store advances [auto: backend test_apply_rechecks_plan_file_under_the_write_lock + test_apply_rechecks_attachment_under_the_write_lock] - [ ] Uploads are bounded (v1.49.0, HP-1470-01): past the store quota an upload is refused with a clear error and the disk does not grow; the plan list returns the newest 60 with a total [auto: unit: test_check_quota_counts_the_whole_store_not_one_request, backend test_uploads_are_bounded_by_a_store_quota] - [ ] An attachment already written to staging is not reserved twice: with the real 512 MiB disk reserve intact its same-filesystem promotion succeeds, while one byte below the reserve still fails. Uploads checked before any bytes are written continue to reserve their full incoming size [auto: backend test_issue_554_low_disk_reserve_distinguishes_staged_and_unwritten_bytes + test_issue_554_upload_uses_actual_free_space_after_staging; mutation: quota-reserves-staged-bytes-twice-on-disk] - [ ] Square canvas migration (v1.48.0): after the upgrade every existing plan looks exactly as before, just with margins where the canvas was extended. Measure a wall in the plan editor — the length in cm is unchanged. Marker positions, doors, decor and the saved zoom are all where they were [auto: unit: test_a_wide_plan_gains_margins_above_and_below and neighbours, test_migration_preserves_real_lengths_and_shapes] - [ ] A plan image is centred (v1.48.0): a wide image sits in the middle with empty bands above and below, a tall one with bands at the sides, and it is never stretched [auto: unit: fitInSquare + smoke_space_settings] - [ ] Re-attaching a detached plan (v1.47.0): detach a plan, save, RELOAD THE PAGE, open space settings → "Already uploaded" → the image is listed with its size and no "in use" note → attach it → it renders. The one a space uses shows that space and cannot be deleted; a free one can, with a confirm, and disappears from the list [auto: smoke_saved_plans + backend test_stored_plans_can_be_listed_and_deleted_on_request] - [ ] Detaching a plan keeps the file (v1.46.6): switch a space to "draw" and SAVE — the image is still in `config/houseplan/plans/` right afterwards, and after a restart, and can be re-attached. Deleting the space keeps it too. Replacing a plan still removes the one it replaced, immediately. Check straight after the save: the earlier bug deleted the file at that moment, while every scheduled-pass test passed [auto: unit: test_plan_collection_matrix, test_attachment_collection_matrix, backend test_detaching_a_plan_keeps_the_file] - [ ] Rebinding a device does not eat its manuals (v1.46.5): attach two files to a device, rebind it to another HA device — both are readable afterwards. If a copy failed, the file it failed on is still there rather than deleted with the folder [auto: backend test_files_cleanup_keeps_referenced_files] - [ ] Nothing accumulates on an idle instance (v1.46.2/v1.46.3, HP-1461-01, HP-1462-01): attach a file, cancel the dialog, and do not save anything else — the file is gone after a restart AND after the daily pass, while every file the configuration still references is untouched. Seed the strays AFTER the last save, or `config/set` collects them and the check proves nothing [auto: backend test_startup_sweep_collects_what_no_commit_will, test_daily_sweep_callback_collects_too, test_sweep_and_a_config_write_do_not_race] - [ ] A drag wins over a concurrent remote move (v1.46.2, HP-1461-02): drag an icon and, while the save is still in flight, have another window move a different icon — your icon stays where you put it and the other one updates [auto: smoke_layout_sync] - [ ] Concurrent uploads of one name (v1.46.1, HP-1460-01): attach the same file from two browser tabs at once — two attachments, two sets of bytes, neither lost. A file whose name is at the length limit still downloads [auto: unit: test_reserve_filename_is_safe_under_concurrency and neighbours] - [ ] No temporary files survive (v1.46.1, HP-1460-02): abort a large upload mid-transfer, send two files in one request, make promotion fail — in each case the files folder holds no `.upload-*`. An old one is swept at startup [auto: backend test_upload_leaves_no_temporary_behind + unit: sweep_upload_temps] - [ ] Two full cards agree on positions (v1.46.1, HP-1460-03): open the plan in two windows, drag an icon in one — it moves in the other without a reload; a drag in progress in the second window is not thrown away [auto: smoke_layout_sync] - [ ] Uploaded SVG is inert as a document (v1.46.0, HP-1454-01): open a plan's signed url directly in a tab — a `