Skip to content

fix(operate): make Start say which of four things it will do - #156

Merged
pskeshu merged 1 commit into
gently-project:developmentfrom
pskeshu:fix/start-says-what-it-does
Sep 5, 2026
Merged

fix(operate): make Start say which of four things it will do#156
pskeshu merged 1 commit into
gently-project:developmentfrom
pskeshu:fix/start-says-what-it-does

Conversation

@pskeshu

@pskeshu pskeshu commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Audit findings 4 and 5.

The button

startRun branches on _mode into four different verbs. The button said "Start" for all four, and the mode selector lives in a block above it — so an operator who chose a mode and then looked away had nothing on the button to read.

mode label what it does
single Acquire one volume takes one volume and finishes
adaptive Start timelapse
library Run tactic
agent Brief the agent

single is the one that matters — calling it "Start" invited an operator to believe they had started an experiment. The markup ships single's verb so there's no flash of the wrong one on load, and the label is restored after a run rather than reverting to "Start".

The fallback that was exactly backwards

const subs = _embryos.filter(e => e.role !== 'calibration').map(e => e.id);
return subs.length ? subs : _embryos.map(e => e.id);

Meant to be kind to a roster with no roles assigned. It could never do that: an embryo with no role, or 'test', or 'unassigned' already passes the filter — so subs is empty in exactly one case, when every embryo is marked calibration.

So it fired only when the operator had said "these are all references", and answered by imaging all of them as subjects. The precise opposite of the instruction, silently.

Gone. haveSubjects() refuses at the single point both roster-driven modes pass through, and distinguishes the two states — "no embryos" and "no subjects among your embryos" need different fixes.

Checks

Four source assertions: every mode has a verb (a mode added to setMode but not RUN_VERB silently falls back to "Start"), single's label contains no "start", subjectIds keeps no fallback, and the guard names both states.

Verified in the running app — labels track the mode with no flash, and all three roster states answer correctly: every-reference refused by name, empty roster refused differently, one real subject allowed through.

`startRun` branches on `_mode` into four different verbs. The button said
"Start" for all four, and the mode selector lives in a block above it, so an
operator who chose a mode and then looked away had nothing on the button to
read.

    single    Acquire one volume     acquires one volume and finishes
    adaptive  Start timelapse
    library   Run tactic
    agent     Brief the agent

`single` is the one that matters: it takes a single volume and stops. Calling
that "Start" invited an operator to believe they had started an experiment. The
markup now ships single's verb so there is no flash of the wrong one on load,
and the label is restored after a run rather than reverting to "Start".

## The fallback that was exactly backwards

    const subs = _embryos.filter(e => e.role !== 'calibration').map(e => e.id);
    return subs.length ? subs : _embryos.map(e => e.id);

The fallback was meant to be kind to a roster with no roles assigned. It could
never do that: an embryo with no role, or role 'test', or 'unassigned' already
passes the filter, so `subs` is empty in exactly ONE case — every embryo is
marked `calibration`. So it fired only when the operator had said "these are
all references", and answered by imaging all of them as subjects. The precise
opposite of the instruction, and silent.

Gone. `haveSubjects()` now refuses at the single point both roster-driven modes
pass through, and distinguishes the two states, because "no embryos" and "no
subjects among your embryos" need different fixes.

## Checks

Four source assertions: every mode has a verb (a mode added to `setMode` but
not to `RUN_VERB` silently falls back to "Start"), single's label does not
contain "start", `subjectIds` keeps no fallback, and the guard names both
states rather than emitting one message.

Verified in the running app: labels track the mode with no flash on load, and
all three roster states produce the right answer — every-reference refused by
name, empty roster refused differently, one real subject allowed through.

Audit findings 4 and 5 in docs/devices-tab-audit.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@pskeshu
pskeshu merged commit 80201f2 into gently-project:development Sep 5, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant