(1) Corner and edge handles carry the same hit radius (1.8 % of the
view), so on furniture narrower than 4·hr — a 40 cm cabinet — the two
circles overlap and whichever is painted last takes the tap. Edges were
painted last. Corners are now, because a side handle scales one axis
while a corner scales both, and the object is small exactly when
proportional resize matters most. The visible beads are unchanged.
The audit called this 'proportional resize becomes unavailable'; the
measurement says otherwise and the spec records the correction: the
corner centre lies outside the edge circle, so the corner was reachable
— its area was halved, not lost. A polish, not a bug, and worth fixing
because it is one line of ordering.
(2) Alignment guides in the devices mode excluded the dragged marker by
_drag, which has been null there since #74 moved device dragging into
_deviceDrag. So the marker being moved was among its own candidates.
Nothing looked wrong because a point always matches itself within
tolerance — the guide was drawn from the marker to itself, visually
identical to an honest one, and the smoke asserted only guides() >= 1.
The smoke now compares the candidate lists with and without the drag and
demands exactly one removed entry.
(3) The 38 settings-help strings stay in the initial chunk, and that is
now a recorded decision rather than an oversight: measured 2 654 B gzip,
0.9 % of the ceiling, against splitting a synchronous dictionary in two,
a second request on first hint, and a second source for the key type
derived from en.json (#391). docs/ARCHITECTURE.md says so, with the
number that would justify revisiting it.
Both mutants run by hand: reverting the paint order reddens the 40 cm
probe while the 160 cm one stays green; restoring _drag reddens the
guides smoke.
User-Visible: yes
Issue: #400
The guard introduced by #394 matched the literal
sys.modules['custom_components... and therefore never looked at
pure_imports.py, which writes through a variable — the third instance of
the #389 class walked straight past the check created for it.
The guard now inspects the write itself and decides by the key: a whole
literal or the literal head of an f-string is safe unless it starts with
custom_components (that is how tests register homeassistant.*, hp_pure.*
and houseplan.trails); anything else — a variable, a concatenation,
setdefault/update — counts as a violation whenever the file is able to
name the package at all, i.e. contains a custom_components. literal. A
file that never names the package cannot poison it through a variable,
so restoring a snapshot stays legal.
load_pure now removes what it registered. Removing its own name is not
enough: relative imports pull neighbours in, so junction_limits leaves
wall_segment_model and coordinate_canonicalization behind. It removes
the whole custom_components difference accumulated during exec_module,
in a finally, and a repeated call still works.
pure_imports.py is a named exemption of the static guard precisely
because that guard cannot see the cleanup — so the cleanup is proven by
an executable test instead, and the mutant pure-imports-stops-cleaning
reddens it. Both mutants were run by hand.
User-Visible: no
Issue: #398
B3: _persistDevicePlacement sent canonicalizePosition(...) to the server
and left the raw value in _layout, then recorded the fingerprint over
that raw snapshot. Canonicalization is not identity — it snaps to the
lattice — so 39 of 115 pixel-derived coordinates differ, and the next
_reloadLayoutOnly or _adoptStructuralResponses saw its own write as a
remote edit: history cleared, _layout replaced. The old _persistLayout
wrote the canonical value back; the per-device path introduced by #74
lost that line.
M1: the smoke that was supposed to prove AC10 assigned
serverLayout = structuredClone(c._layout) right before the reload —
erasing by hand the very divergence it existed to catch, so it could not
fail. The fake WS already stores what went over the wire; the
assignment is gone and the check now reddens on the unfixed code
(verified: three checks red without the fix, including this one).
Also proven, because the fix touches their neighbourhood: the echo of a
DELETE keeps the history (the branch removes a key rather than replacing
a value), and an in-flight write still wins the merge against a server
answer holding the old position.
One existing assertion was loosened deliberately: undo now restores a
position that may differ from the raw one by the lattice snap (<1e-9 of
the plan). That is the point of the fix — local and server agree — so the
equality is stated to that precision, with the snap size pinned
separately so a real drift would still fail.
User-Visible: yes
Issue: #397
Three findings of the v1.70.0-beta.1 audit, all on the transition path
added by #82, all of the same shape — the new path did not inherit a
property the old one had.
B1: persisting the zoom moved into _settleCameraTransition only, and a
cancellation never settles. Touching the plan mid-flight — the literal
scenario of the issue — froze the shown frame and threw it away; before
which kind it is: the user one (_stagePointerDown) persists the frame
that stays on screen, the eleven structural ones keep writing nothing.
The distinction is now also written down in spec #82 §13, which had one
line for both.
B2: the anchor was read from the presented (lagging) frame while the
zoom accumulated from the target, so a six-notch trackpad series walked
the point under the cursor 17 px away — against §10's own promise. Both
now come from the same state. Spec §10 said to use the presented frame
and to keep the anchor within 0.5 px; those two are incompatible, and
the paragraph is corrected rather than left as a trap.
M2: the feather freeze keyed on the two gesture flags, which an
animated transition does not set, so every tween frame rebuilt the blur
region. It keys on 'the camera is still' now.
Guards: unit tests pin the anchor at 1e-9 across 8/16/33 ms series and
prove zoom accumulation is untouched; the smoke checks the shown zoom is
the stored one, that a structural cancellation stores nothing, and that
the anchor holds; three mutants (cancel-loses-zoom, anchor-from-
presented, feather-thaws) were run by hand and each reddens.
User-Visible: yes
Issue: #396