fix(led): badge at the anchor, room_id wins, chain stays in its space, hidden loads nothing (#780)

r1 of the code review:
- M1: a strip keeps its marker's value badge, passive, at the half-length
  anchor on the card and on the static card (no icon core, pulse or slot).
- M2: one room resolver for the strip's Glow — an explicit valid room_id of
  the marker wins over the anchor room; a stale one falls back (stripRoom).
- M3: the chain remembers the space it is drawn in; a space switch finishes
  it there, never in the space shown next, and opens no picker over it.
- M4: visibility is decided before import(): a hidden marker or an
  HA-disabled device loads no LED chunk (ledVisible, also checks the stored
  marker so a just-hidden one does not slip through a stale device list).
- M6: the static card is a full light_pools × live_states browser matrix.

Unit tests for M2/M3, smokes for M1/M3/M4/M6, five registered mutants.

Issue: #780
User-Visible: yes
This commit is contained in:
Codex
2026-10-02 20:54:50 +03:00
parent ef3e7a5440
commit b40e8ff58d
20 changed files with 362 additions and 62 deletions
@@ -14,11 +14,11 @@ to prove that the Node witness actually kills it.
| --- | ---: | --- |
| Performance threshold | 4 | The witness measures real browser wall-time or frame work; a pure assertion cannot prove the budget. |
| Browser harness integrity | 4 | The mutation breaks page-error, round-trip or page-registration observation in the browser harness itself. |
| Paint, cascade and layer composition | 30 | The invariant depends on computed CSS, SVG paint, clipping, stacking or pixels produced by Chromium. |
| Paint, cascade and layer composition | 32 | The invariant depends on computed CSS, SVG paint, clipping, stacking or pixels produced by Chromium. |
| Pointer geometry and trusted interaction | 47 | The invariant depends on hit testing, pointer capture, touch/keyboard dispatch or live DOM geometry. |
| Responsive DOM layout | 38 | The invariant depends on measured element boxes, responsive breakpoints, native/HA dialog shells or focusable target size. |
| Custom-element and HA browser lifecycle | 100 | The invariant crosses Lit/custom-element lifecycle, browser storage/events, lazy loading or a complete HA-card state transition. |
| **Total** | **223 / 200** | Above the guideline `mutation-gate --check` warns rather than fails (#699); each guard above it is held by its own reason in this inventory and its `because`. |
| Custom-element and HA browser lifecycle | 101 | The invariant crosses Lit/custom-element lifecycle, browser storage/events, lazy loading or a complete HA-card state transition. |
| **Total** | **226 / 200** | Above the guideline `mutation-gate --check` warns rather than fails (#699); each guard above it is held by its own reason in this inventory and its `because`. |
## Measured effect
@@ -91,9 +91,11 @@ The invariant depends on computed CSS, SVG paint, clipping, stacking or pixels p
- `iso-sun-card-drops-occluders`
- `iso-sun-flat-wedges-remain`
- `iso-theme-dark-wall-rule-returns`
- `led-badge-dropped`
- `led-core-coloured-under-glow`
- `led-icon-not-suppressed`
- `led-source-stays-round-at-anchor`
- `led-static-live-ignored`
- `led-unbound-in-view`
- `stage3-w4-device-target-loses-44px-floor`
- `stage3-w5-runtime-nudge-writes-storage`
@@ -251,6 +253,7 @@ The invariant crosses Lit/custom-element lifecycle, browser storage/events, lazy
- `junction-limit-candidate-fail-open`
- `junction-limit-write-gate-removed`
- `led-auto-slot-reserved`
- `led-hidden-marker-loads-chunk`
- `locale-failure-toast-dropped`
- `marker-reject-keeps-optimistic-candidate`
- `marker-rollback-keeps-enqueue-time-revision`