Lid/temp-holder planner review
Goal
Decide the intended holder-allocation model, then fix branch
saahas/lids-temp-planner without preserving accidental complexity.
Context
Branch commit e664744d55 adds two forms of capacity reuse:
- a temperature-controlled holder may satisfy a plain
well_plate_holderspec; - a plate-capable holder marked
reserved_for_well_plate_lidmay hold a plate when the protocol contains no lids.
The underlying invariant should be stated directly: at every point in the plan,
plates and concurrently parked lids need a compatible matching to distinct holder
slots. The current implementation approximates this with type substitution plus a
protocol-global uses_lids boolean.
Review findings
Blocking
-
Cross-type matches cannot be materialized.
candidate_modulescan match aWellPlateHolderSpectoTempWellPlateHolderProtoscript, butprotospec/compiler.py:_merge_spec_fieldsthen copiesentity_type="well_plate_holder"into the temperature-holder model. Pydantic rejects the literal discriminator. Reproduced through bothmerge_spec_onto_moduleandmerge_spec_fields_onto_physical.Likely fix: the physical module owns
entity_type; never overwrite it during a spec merge. Cover this throughcompile_single_deckand full multi-deck planning, not onlymatch_specs_to_modules. -
An active temperature block is not behaviorally substitutable for an ambient holder.
_substitute_is_placeableadmitsis_on_at_start=True. Fleet inventory includesml1-003holders configured and preflight-checked at 4 °C. Unless cooling an otherwise ambient plate is intentional, substitution needs proof that the physical block is ambient/off; shared cooling loops may require an explicit capability rather than inferring this fromis_on_at_start.
Should fix
-
Lidless reclaim skips later-recruited storage decks.
plan_instructionsclears the flag only on initially assignedruntime_hardware. Spill selection evaluatesrecruitable_decksfrom untoucheddeck.hardware, and_recruit_storage_deckcopies that untouched state. A valid overflow deck is therefore rejected when its only free compatible slot is a plate-capable lid holder. -
The relaxation erases an explicit
reserved_for_well_plate_lid=Truerequirement.constraint_data_for_matchingdrops the field regardless of its value. Reproduced a deliberately lid-reserved spec preferentially matching an ordinary holder. If this mechanism remains, relaxation must be directional: ordinary (False) specs may claim physicalTrueholders under the policy, but explicitTrueremains a constraint. -
reserved_for_well_plate_lidconflates physical capability, preference, and runtime allocation. It selects lid destinations, hides dedicated holders from generic placement/UI paths, and permits pose-less lid rests to omit plate home waypoints._reclaim_lidless_holdersthen mutates the same field as if it were temporary allocation state.supported_well_plate_typesbeing nonempty is not a canonical proof that plate waypoints exist. -
Matching policy depends on caller memory.
uses_lids=Trueis a defaulted argument repeated across compiler, dry run, resolver, MILP, and candidate-deck paths. Production diagnostic calls omit the protocol context, so feasibility and explanations can disagree. If the boolean remains, make the context required and first-class.
Documentation
The task-planning knowledge page/log and the MILP module header should be corrected after the model is chosen. The current prose overstates round-trip safety and still describes capacity as exact-type/ignorant of compatibility.
Intent questions
- May a plain plate intentionally sit on a block actively held at 4 °C or 37 °C, or only on a provably inactive/ambient-safe temperature holder?
- Can a lid physically park on any empty holder, or are some positions uniquely calibrated/collision-safe for lids?
- For a lidded protocol, should the planner reserve every lid-marked holder for the whole run, or only the peak number of simultaneously parked lids?
Simplification fork
- If any empty holder is lid-safe, remove
reserved_for_well_plate_lidfrom allocation. Prefer bare lid-only rests, then dynamically match remaining lids and plates to compatible slots. - If only certain positions are lid-safe, retain an immutable capability or dedicated subtype, but keep protocol-time reservation in planner state rather than rewriting hardware.
Validation performed
bin/lint-diff.sh main: passed.medra_robotics/tests/protoscript/test_protospec.py: 45 passed.- Direct reproductions confirmed the cross-type merge failure and the explicit-true constraint reversal.
- Full planner suite and hardware validation were not run.
No fixes were applied during review.