The operator's embryo selection can move on its own, with no input.
At 19:00 in the recording, the selection reverts from embryo 3 to embryo 1 with the cursor parked at (920, 530), away from the list.
onEmbryosUpdate is the one selection writer no click reaches (operate.js:1167-1180):
_embryos = (p && Array.isArray(p.embryos)) ? p.embryos : [];
if (_selected && !_embryos.some(e => e.id === _selected)) _selected = null;
if (!_selected && _embryos.length) _selected = _embryos[0].id; // :1174
It is bound to the server push (operate.js:1313 ← websocket.js:128 ← the agent's full-list snapshot, app/agent.py:1023-1051). So it treats "the selected id is not in this snapshot" as "select the first embryo". Any server-side mutation that removes or replaces the selected embryo — an agent re-mark reconcile, an import with clear_existing (agent.py:1398), or any payload arriving without an embryos array, which collapses _embryos to [] — silently moves the operator's cursor to embryo_1 with no notice.
Selection is documented as a cursor that gates nothing (operate.js:37-42), which is what makes the silent hop legal today and also what makes it safe to stop.
Fix: remember whether a selection existed before the reconcile and only auto-select on first population:
const had = _selected;
// … existing reconcile …
if (!_selected && !had && _embryos.length) _selected = _embryos[0].id;
First population still auto-selects, which is why the line exists; a vanished selection falls to null and renderSpimTarget shows No embryo selected instead of quietly pointing somewhere else. Same one-word change applies at operate.js:868.
Compounds the stale-frame bug (#127): a silent selection hop plus an unchanged image means the caption and the pixels can both be wrong at once, with nothing on screen having been touched.
Evidence: 2026-08-07 walkthrough, 19:00. Not previously captured.
The operator's embryo selection can move on its own, with no input.
At 19:00 in the recording, the selection reverts from embryo 3 to embryo 1 with the cursor parked at (920, 530), away from the list.
onEmbryosUpdateis the one selection writer no click reaches (operate.js:1167-1180):It is bound to the server push (
operate.js:1313←websocket.js:128← the agent's full-list snapshot,app/agent.py:1023-1051). So it treats "the selected id is not in this snapshot" as "select the first embryo". Any server-side mutation that removes or replaces the selected embryo — an agent re-mark reconcile, an import withclear_existing(agent.py:1398), or any payload arriving without anembryosarray, which collapses_embryosto[]— silently moves the operator's cursor to embryo_1 with no notice.Selection is documented as a cursor that gates nothing (
operate.js:37-42), which is what makes the silent hop legal today and also what makes it safe to stop.Fix: remember whether a selection existed before the reconcile and only auto-select on first population:
First population still auto-selects, which is why the line exists; a vanished selection falls to null and
renderSpimTargetshowsNo embryo selectedinstead of quietly pointing somewhere else. Same one-word change applies atoperate.js:868.Compounds the stale-frame bug (#127): a silent selection hop plus an unchanged image means the caption and the pixels can both be wrong at once, with nothing on screen having been touched.
Evidence: 2026-08-07 walkthrough, 19:00. Not previously captured.