Execution Requirements
constraint(...) specifies how an existing operation must be performed. It does not add an action, change a transfer amount, or select an agitation mode. Use environment blocks to specify physical conditions, and constraints to express requirements those conditions and actions must satisfy. For example, env(thermal = 4C) supplies a temperature, while ConstraintRequirement.COLD_CHAIN requires cold-chain handling.
Block Form
with constraint(ConstraintRequirement.GENTLE, ConstraintRequirement.PRESERVE_BOUNDARY) {
target << [source:5uL];
}The block applies its requirements to the enclosed operations, including those inside protocol calls. Nested constraint blocks add to the outer requirements; leaving a block restores the outer set. There is no separate release operation. Every enclosed action must accept the combined requirements, so keep an operation-specific requirement close to the operation it applies to.
Single-Step Form
target << [source:5uL] with constraint(ConstraintRequirement.GENTLE);This is equivalent to wrapping that single statement in a constraint block. Trailing with is only for constraints. It is not used for env(...).
Requirement Names
Standard requirements use ConstraintRequirement members, such as ConstraintRequirement.GENTLE. The lowercase names in the table are equivalent compatibility spellings. Each name declares a static requirement rather than a variable-valued expression. Unknown names are rejected; standard requirements accept no extra options.
| Category | Requirements |
|---|---|
| Delivery mode | dropwise, spread |
| Structure preservation | preserve_boundary, preserve_layering, preserve_fraction_order |
| Contamination control | aseptic, low_carryover, cross_contam_control |
| Material integrity | gentle, avoid_resuspension, avoid_shear, preserve_viability |
| Environmental protection | cold_chain, dark_protected, sealed, controlled_atmosphere |
| Quantitative quality | high_precision, low_loss, quantitative_recovery |
| Measurement quality | stabilized_reading, low_noise, noninvasive |
These categories group related purposes; they do not mean every action accepts every requirement in a category. The Constraint Contract defines each requirement's scenarios, permitted actions and compatibility rules.
Compatibility
An operation must satisfy both the requirement's applicability rules and any checks on its actual inputs. For example, COLD_CHAIN is allowed on agitation, but an explicit temperature of 37C conflicts with it. SPREAD is allowed on transfer, but its receiving target must be a surface.
Known conflicts fail validation. Conditions that depend on runtime values are checked before the affected action executes. A declared lack of execution-provider support also prevents the action; provider support cannot override a semantic conflict.
Qualitative requirements such as GENTLE and LOW_NOISE do not introduce numerical speed or noise limits. A successful validation or simulated run does not establish physical compliance.
Dropwise Transfer
Use DROPWISE on a transfer when the liquid must be added drop by drop:
target << [source:150uL] with constraint(ConstraintRequirement.DROPWISE);The quantity is the total amount transferred. This requirement does not supply a drop count, drop volume, rate, or duration, and it does not add a mixing step. It also applies to full transfers (target << [source]) and may be combined with compatible requirements such as GENTLE. A constraint block applies it to each enclosed transfer; unrelated actions such as agitation and separation cannot carry it.
Human instructions state that the liquid must be added drop by drop. Robot command projections retain the requirement for the device adapter to implement. A driver's declared lack of support prevents material movement. Generating an instruction or command is not evidence of physical delivery.
Surface Spreading
Use SPREAD to distribute transferred material across a receiving surface:
plate_surface << [suspension:100uL]
with constraint(ConstraintRequirement.SPREAD, ConstraintRequirement.ASEPTIC);Here plate_surface must resolve to a surface, including when supplied as a protocol argument. A known non-surface target is rejected. The requirement preserves the transferred amount and does not select a spreading tool or add another transfer. ASEPTIC is a separate, compatible handling requirement.
Agitation Requirements
Mechanical agitation accepts GENTLE, ASEPTIC, CROSS_CONTAM_CONTROL, COLD_CHAIN, DARK_PROTECTED, and CONTROLLED_ATMOSPHERE:
with constraint(ConstraintRequirement.GENTLE, ConstraintRequirement.DARK_PROTECTED) {
agit(sample = sample, mode = AgitationMode.SHAKE, motion = ShakeMotion.ROTATION, duration = 2h, rate = 20rpm);
}These requirements constrain the selected mechanical motion. DROPWISE, SPREAD, SEALED, and PRESERVE_LAYERING are not accepted on agitation. Agitation invalidates existing layer indices; a constraint cannot preserve them through that operation.
Cold-Chain Conditions
COLD_CHAIN can accompany several action families, including transfer, agitation, separation and environment holds. Use an environment block to state an explicit temperature:
let sample = tube(label = "Sample");
with constraint(ConstraintRequirement.COLD_CHAIN) {
with env(thermal = 4C) {
agit(sample = sample, mode = AgitationMode.SHAKE, motion = ShakeMotion.ROTATION, duration = 1min);
}
}Each explicit scoped thermal setting must resolve to a finite temperature in C or K at or below 8 C (281.15 K). Changing 4C to 37C makes this example invalid; 277.15K and 310.15K have the corresponding outcomes. Nested environment settings are checked individually, so an inner cold setting does not excuse an incompatible outer setting.
A known high temperature fails validation. A temperature determined at runtime is checked before the action; an explicit value that remains unresolved or invalid is rejected. Omitting an explicit temperature does not infer one: the execution provider must still honor the cold-chain requirement.
Sealed Holds
Use SEALED when a target must remain sealed throughout a pure environment hold:
with constraint(ConstraintRequirement.SEALED) {
with env(thermal = 25C, duration = 2h) {
hold(elisa_plate);
}
}The constraint surrounds the environment block; hold(...) remains a direct marker inside that block. For a plate-level closure, use the plate binding as the target rather than a group of selected wells. SEALED does not add sealing or unsealing actions, and it does not establish a persistent closure state after the hold. It is not applicable to transfer or agitation.
Customized Constraints
Use CUSTOMIZED with a schema when a requirement does not fit the named set.
let req_schema = data_schema(label = "CustomRequirement", fields = [target_state]);
with constraint(ConstraintRequirement.CUSTOMIZED, schema_ref = req_schema) {
target << [source:5uL];
}schema_ref is required and no other option is accepted. CUSTOMIZED is exclusive: do not combine it with another requirement, including one inherited from an enclosing scope.
Python Extensions
Python extensions inherit ConstraintRequirementBase and declare a ConstraintRequirementSpec for applicability, scope, conflicts and any context-check functions. RequirementSpec remains an alias for existing extensions. These APIs belong to culsma.domains.constraints.
Extensions must be explicitly registered and supported by the executing driver; registration alone does not implement physical handling. Context checks receive ConstraintContext and return ConstraintViolation or None. The current context carries an explicit thermal value; registering a check does not provide additional action parameters automatically.
