fix(api): Filter out air_gap()
calls as higher-order commands
#14985
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Overview
This fixes RQA-2621.
Test Plan
This PR adds unit tests and a "mid-level" integration test. @y3rsh mentioned he's working on adding an analysis snapshot test.
Details
Before ~v5.0, Python protocols reported their activity as a nested tree, roughly like this:
But the Protocol-Engine–based HTTP API introduced in v5.0 could only represent a flat list of the innermost commands. So our mapping logic in
LegacyCommandMapper
had to filter out the "higher-order" things like transfers and mixes, leaving only the "atomic" things like aspirates and dispenses.We apparently forgot to filter out
air_gap()
, which looks like this:The effect of forgetting to filter it out was that the engine would have two commands running at the same time: the outer air gap and the inner aspirate. This was a dormant bug; Protocol Engine is certainly not meant for that to work.
In #14726, we started being stricter about things like not having more than one command running at a time, so this started to raise an
AssertionError
(correctly, but confusingly).The fix is just to filter out air gaps, like we filter out transfers, mixes, etc.
Review requests
We should be on the lookout for any other higher-order commands that aren't being filtered out properly.
We should think about ways to make this inherently safer. Like, instead of mapping every command by default and filtering out the ones that we know to be higher-order, we might want to omit commands by default and only map the ones that we know to be at the deepest level of nesting. Or, we might want to defensively code
LegacyCommandMapper
to automatically close the last command before starting a new one.Risk assessment
Low risk to the fix, but there's definitely some risk that we're missing similar bugs.