Skip to content

6. Core Atomic Operators and Call Contracts ​

Operator and call contracts specify:

  1. operation name
  2. input parameter names and parameter types
  3. return type policy

6.1 Source Forms and Operator Contracts ​

  1. Surface sugar is defined in Chapter 4 and exists for source ergonomics.
  2. Source authoring truth is the combination of Chapter 4 source forms and the statement/operator contract set in 6.3.7-6.3.20.
  3. The contracts in 6.3.7-6.3.20 govern source-level operator behavior; historical frontend spellings are accepted only when listed as compatibility inputs.

6.2 Contract Type Notation ​

6.2.1 Argument Type Aliases ​

  1. Container: abstract logical container handle identifying a container and its tracked material state
  2. TubeContainer: concrete Container kind for tube-like vessels
  3. WellContainer: concrete Container kind for plate-well vessels
  4. Content: logical content identity handle (independent of container position)
  5. ContentKind: enum of canonical material families (§6.2.5 and §6.2.7)
  6. Text: free-form text (STRING)
  7. Bool: boolean value
  8. Scalar: unitless numeric scalar
  9. Q[Volume]: quantity with volume unit
  10. Q[Mass]: quantity with mass unit
  11. Q[Temperature]: quantity with temperature unit
  12. Q[Time]: quantity with time unit
  13. Q[Count]: discrete-count quantity; the initial supported domain unit is cells
  14. Q[Volume|Mass|Count]: quantity constrained to a material amount axis
  15. List[Container]: list of logical containers
  16. List[Text]: list of text values
  17. Quantity: quantity value with supported unit family (general form)
  18. Record: key-value map for structured metadata fields
  19. ContainerContents: target view selecting the material contents carried by a container
  20. ContainerStructureFacet: target view selecting one physical container-structure facet
  21. ContainerTargetView: ContainerContents | ContainerStructureFacet
  22. HoldTarget: environment-hold target; Container, ContainerTargetView, or a group composed of those targets
  23. ContainerGroup: flat, ordered group of resolvable Container values
  24. ContentType: enum of canonical material types (§6.2.6 and §6.2.7)
  25. ContainerKind: enum of concrete container kinds (§6.2.4 and §6.2.7)
  26. LegacyText: a bare token or text value admitted by the classification compatibility rules (§6.2.7–8)
  27. PlateCarrier: a plate identity, distinct from its individual content-bearing wells and their ordered groups

ContainerGroup static indexing contract:

  1. group_ref[i] requires a compile-time decidable non-negative integer i.
  2. The result is the existing container at ordered position i; indexing does not allocate, copy, or reconstruct a container.
  3. A statically known index outside the group cardinality is rejected before execution.
  4. Plate selector-derived groups use selector expansion order and therefore preserve the selected well's original carrier identity.

6.2.2 Return Type Aliases ​

  1. Void: no required return object
  2. Container: resulting logical container
  3. sep_container_group: fixed two-slot separation result handle
  4. fraction_group: ordered multi-slot fractionation result handle
  5. data_ref: single readout result handle
  6. data_group_ref: grouped readout result handle

6.2.3 Container Content Contract ​

Container content state records the current aggregate volume and mass, material identities and their quantities, and relevant metadata such as density and carrier position. Material quantities retain their dimension and unit. This contract specifies the information preserved, independently of its storage representation. The aggregate contents volume is readable without changing material state. A read reflects the modeled amount after preceding material operations; it does not report container capacity or constitute a physical measurement.

Container target-accessor surface:

  1. container.contents denotes the material contents carried by the container.
  2. container.structure.top denotes the top structural facet of the container.
  3. container.structure.bottom denotes the bottom structural facet of the container.
  4. container.structure.sidewall denotes the sidewall structural facet of the container.
  5. container.structure by itself is a target-view namespace, not a complete target view.
  6. container.structure.top is closure-agnostic. For closed vessels it may correspond to the cap/seal-facing top structure; for open vessels it denotes the upper rim/top structural facet, not headspace air and not liquid surface.
  7. A bare container used as a HoldTarget defaults to the content-facing target view, equivalent to container.contents for env-hold semantics.
  8. Container target views are not material container aliases; they do not carry volume, mass, or components and must not be used as mutation targets or as sep/frac/img/ecp/phy/stream/agit sample inputs unless a future operator explicitly accepts target-view types.
  9. container.contents[i] is the defined exception for mutation source position: it selects an indexed, container-backed source portion from the container's current contents state.
  10. These accessors are direct target views, not partition(...)-style methods and not materialized child containers.
  11. No deeper structure path is defined. container.structure.top.inner and device-like aliases such as container.lid are not container target-view accessors.

Carrier information identifies the carrier kind, carrier identity, and position (for example plate well A1). A declared container capacity is an upper volume bound and applies to every operation that increases the contained volume.

6.2.4 Container/Carrier and Content Taxonomy ​

Container taxonomy rules:

  1. Container is an abstract handle type, not a user-mutable object/class.
  2. Concrete container kinds are tube, well, chamber, and surface.
  3. plate denotes a carrier identity; material state belongs to its individual well containers. Carrier identity follows the declaration rules in §4.5.1.
  4. Built-ins must accept abstract Container handles and remain independent of frontend sugar spellings.

Content taxonomy rules:

  1. Content identity/classification is represented as Content records and container-linked content state.
  2. Recommended content fields: content_kind, content_type, content_code, content_name, and attrs.
  3. content_kind is a closed mid-level material family and answers "what family of material is this?".
  4. content_type is a closed canonical material category within content_kind and answers "what material category is this?".
  5. content_code and content_name identify/display the concrete instance; they are not taxonomy fields.
  6. attrs is the open metadata record for state, source, preparation, concentration, composition detail, and attrs.role.
  7. attrs.role is the role field. It is not promoted to a top-level role argument in this baseline. Standard metadata may use ContentRole, ContentState and BeadProperty members in the corresponding role, state and bead_property fields. Member names use uppercase canonical spellings, for example ContentRole.CULTURE, ContentState.SUSPENSION, and BeadProperty.MAGNETIC. Role members reflect the existing recommendation table; wash and washing remain distinct and pH_adjuster retains its spelling. Initial state members include suspension, solution, stock, extract and reaction_assembled; the standard bead property is magnetic. Standard strings retain their existing persisted representation. Unknown text, other metadata forms and unrelated attribute keys remain subject to the existing open-metadata contract, not a new closed whitelist. Explicit members must belong to the field's domain. Installed extensions retain stable domain identity, version and member data when stored; they are metadata declarations, not new physical-state or scientific behavior. Recommendation rows do not become mandatory combinations, and a stored extension identity must not cause automatic code loading.
  8. Source-level family constructors (blood/reagent/buffer) are compatibility/ergonomic sugar and do not define canonical content_kind names by themselves.

6.2.5 Canonical Content Kind Set ​

content_kind set used by strict-mode validation:

  1. bio_entity
  2. bio_fluid
  3. bio_cellular
  4. bio_subcellular
  5. bio_molecule_or_virus
  6. chemical
  7. particulate
  8. formulation

Mode note:

  1. Strict-mode conformance uses this exact whitelist.
  2. Authoring examples and conformance claims must stay within this whitelist.
  3. Legacy inputs such as biosample, reagent, buffer, control, waste, or fraction are compatibility inputs only when legacy normalization mode is explicitly enabled.

6.2.6 Canonical content_type and attrs.role Table ​

This table defines the canonical material taxonomy. attrs.role contains recommended role values for that kind/type. It is optional, remains within the open attrs record, and is not a top-level constructor parameter. The recommendation table does not restrict a material to the listed roles.

content_kindcanonical content_typerecommended attrs.role values
bio_entityorganism, organ, tissue, other_bio_entitymaterial
bio_fluidwhole_blood, plasma, serum, buffy_coat, urine, saliva, lymph, cerebrospinal_fluid, tears, semen, ascites, synovial_fluid, bronchoalveolar_lavage_fluid, other_body_fluidmaterial
bio_cellularcell_line, primary_cells, cell_population, microbial_cells, other_cellular_materialmaterial
bio_subcellularorganelle, membrane, vesicle, cytoskeletal_structure, other_subcellular_structuresample
bio_molecule_or_virusdnasample, template, primer, probe, marker, standard, vector
bio_molecule_or_virusrnasample, template, guide, probe, marker, standard
bio_molecule_or_virusproteinsample, antibody, enzyme, cytokine, marker, standard, antigen
bio_molecule_or_virusvirussample, vector
bio_molecule_or_virusother_biomolecule_or_virussample
chemicalorganic_compoundsubstrate, inhibitor, activator, fixative, selection, precipitation, crowding_agent, matrix_component
chemicalinorganic_compoundsalt, pH_adjuster, ion_source, cofactor
chemicalsolventsolvent, carrier, extraction, fixative
chemicaldetergentlysis, permeabilization, washing, solubilization
chemicaldyestain, detection, tracking
chemicalother_chemicalmaterial
particulatebeadscapture, cleanup, purification, enrichment
particulateresinaffinity_purification, chromatography, capture
particulateparticlelabeling, calibration
particulateother_particulatematerial
formulationmediumculture, maintenance
formulationbufferwash, elution, binding, lysis, storage, reaction_environment
formulationsupplementsupplement, selection, growth_factor
formulationmaster_mixreaction_mix
formulationgradient_mediumdensity_gradient_separation
formulationother_formulationmaterial

Normalization rules:

  1. content_type values SHOULD use lowercase snake_case for stable matching.
  2. Display labels (human-readable) MAY be stored separately in metadata.
  3. content_type is closed by the canonical table. Unknown or lab-local material categories should use the appropriate canonical other_* type plus attrs, code, and name.
  4. Non-standard attrs.role values MAY be recorded as ordinary attr metadata; unknown attr keys remain allowed because attrs is open metadata.
  5. Specific identities such as mitochondrion, exosome, plasmid, amplicon, peptide, polymer class, bead material, unusual body-fluid identity, or virus form belong in attrs when they are more specific than the canonical kind/type table.
  6. Preparation or state labels such as pellet, suspension, lysate, stock, diluted, purified, or amplified belong in attrs.state or a more specific attr key, not in content_type.
  7. Workflow labels such as control, waste, fraction, wash, elution, binding, lysis, detection, or capture belong in attrs.role, attrs.disposition, or operation-return metadata, not in content_kind.
  8. Legacy aliases or previously accepted unknown/custom type strings are compatibility inputs; legacy normalization mode should emit a warning and normalize to canonical content_kind, canonical content_type including an other_* fallback when needed, and attrs.

Examples:

culsma
content(kind = bio_fluid, type = whole_blood, code = "WB-001")
content(kind = bio_fluid, type = synovial_fluid, code = "SF-001")
content(kind = formulation, type = buffer, code = "PBS-001", attrs = { role: wash })
content(kind = bio_molecule_or_virus, type = protein, code = "AB-001", attrs = { role: antibody })
content(kind = bio_molecule_or_virus, type = dna, code = "PRIMER-001", attrs = { role: primer })
content(kind = bio_fluid, type = other_body_fluid, code = "AF-001", attrs = { fluid_identity: amniotic_fluid })

6.2.7 Explicit Content Enum Contract ​

ContentKind, ContentType, and ContainerKind are distinct enum families. Their members are named by uppercasing the canonical tokens in §6.2.4–6. For example, ContentKind.FORMULATION, ContentType.MEDIUM, and ContainerKind.TUBE denote the corresponding classification values. Member names are case-sensitive. A member of one family MUST NOT be accepted in a position requiring another family, even if their textual representations match.

A content classification consists of a kind and a type from that kind's canonical set. Invalid members and invalid kind/type pairs MUST be rejected. If either argument is an explicit enum, compatibility normalization MUST NOT repair an invalid pair.

Bindings follow the language's lexical scope rules. Local bindings shadow enum namespaces; aliases and parameter defaults preserve enum family and value. Assignments MUST preserve the binding's enum family. An alias captures the value at its declaration, so subsequent assignment to the original binding does not change the alias. Where a branch or loop can change a value, validation MUST consider the value that reaches the use rather than assume an earlier value still applies.

Bare canonical tokens and quoted canonical strings are accepted in both strict and compatibility taxonomy modes. Noncanonical legacy inputs are admitted only in compatibility mode and are subject to §6.2.8. These compatibility forms do not add enum members.

A classification MUST be valid before it is recorded as material state. Static errors are rejected when decidable; values determined only during execution are checked before the affected material write. Serialization and restoration MUST preserve enum family and value wherever subsequent classification checks depend on them. Material-record classification fields use canonical text values; unknown historical values MUST NOT be treated as valid canonical classifications. Diagnostic placement is specified in Chapter 8.

Example:

culsma
protocol EnumMedium {
  let medium = container(kind = ContainerKind.TUBE, capacity = 5mL, load = [
    content(kind = ContentKind.FORMULATION, type = ContentType.MEDIUM,
            code = "DMEM_F12_COMPLETE", attrs = { role: culture }):4mL
  ]);
}
EnumMedium();

6.2.8 Selected Legacy Normalization Conformance Cases ​

The following cases specify representative legacy conversions. They are not additional canonical members or an exhaustive alias list. The fallback for each canonical kind is its unique other_* type in §6.2.6. Conversion preserves the original kind/type and inferred attributes; explicitly supplied attributes take precedence when material is defined.

legacy kindlegacy typecanonical kindcanonical typeinferred attrs
biosampledna_stockbio_molecule_or_virusdna{"state":"stock"}
biosamplecell_pelletbio_cellularother_cellular_material{"state":"pellet"}
bufferwash_bufferformulationbuffer{"role":"wash"}
reagentenzymebio_molecule_or_virusprotein{"role":"enzyme"}
bloodlab_localbio_fluidwhole_blood{}
formulationlab_localformulationother_formulation{"original_type":"lab_local"}
chemicalmediumchemicalother_chemical{"original_type":"medium"}
wastelab_localformulationother_formulation{"disposition":"waste","original_type":"lab_local"}

Historical material plans may retain the generic container kind. It is not an additional source-level ContainerKind member. Compatibility of source forms and compatibility of historical records are independent contracts.

6.3 Global Call Rules ​

These rules apply to every call, including constructor expressions nested in load items and expressions reached through aliases.

  1. All required args MUST be provided.
  2. Provided args MUST be in the allowed arg set. Open records such as attrs do not make the call's top-level parameter set open.
  3. Duplicate arg names MUST be rejected; an implementation MUST NOT silently choose one occurrence.
  4. Quantity args MUST satisfy declared dimension constraints.
  5. Container args resolve to runtime container identity and operate on container content state.
  6. For volume-increasing writes, the resulting volume MUST not exceed the target container's declared capacity.
  7. A declared capacity MUST be positive.
  8. Equivalent source forms MUST preserve the same argument, quantity, identity, and effect contracts.

Capacity enforcement:

  1. Capacity rules above are enforced through explicit capacity normalization and overflow diagnostics.

6.3.1 Initialization and Operations ​

Initialization establishes container and content identities, initial quantities, and metadata. Operations consume these declared values to move or organize material, apply environmental conditions and execution requirements, or obtain readouts. Both are governed by the argument types and quantity rules in this chapter; their internal execution representation is implementation-defined.

6.3.2 Semantic Domain Layering ​

Operations are classified by execution semantic domain.

Domain definitions:

  1. material: operation primarily updates container material state (aggregate quantities, material identities and quantities, and metadata).
  2. bridge: operation consumes container state and also produces structured analytical/process payload.
  3. data: operation consumes only data payloads/records and does not read/write container material state.

Built-in mapping:

OperationSemantic DomainPrimary state effect
<<materialmove/merge material across containers
sepmaterialsplit material into a fixed two-slot indexed group
fracmaterialsplit material into an ordered indexed group
streammaterialconvert a macro sample into a unit stream
img / ecp / phybridgeconsume container state and emit data_ref / data_group_ref
constraintsemantic policyattach device-independent execution requirements to a scope or single step

Domain boundary note:

  1. bridge operators are container-addressed; they are not pure data operators.
  2. Pure data operators are reserved for future vocabulary extension and are not part of built-ins.

6.3.3 Equivalent Source Forms ​

  1. Chapter 4 defines source syntax; this chapter defines its accepted arguments and effects.
  2. Container constructors and inline content declarations establish the identities and initial quantities described in §6.3.4.
  3. target << [sources...] is the source form for transfer and merge behavior.
  4. Historical calls such as Transfer(...), Mix(...), and Dilute(...) are not accepted source forms.

6.3.4 Container and Content Initialization ​

  1. A container constructor creates a new logical container identity. Its declared kind, capacity, closure state, and metadata describe that container.
  2. A content(...) declaration establishes material identity and classification independently of container position. Classification follows §6.2.7–8.
  3. Each item in load assigns a declared material quantity to the container. The quantity must use a supported volume, mass, or count axis.
  4. Descriptive metadata does not by itself change material quantity.
  5. open declares the initial closure state; load declares the initial contents. The source forms and accepted constructor arguments are defined in Chapter 4.
  6. cells is valid only for content classified as bio_cellular; directly authored count loads MUST be non-negative integers.
  7. A count load does not itself add volume. A free count-only cellular population may acquire an inferred carrier volume under the declared carrier-assumption policy. Any such volume participates in capacity checks, and its assumed origin must remain explicit.

6.3.5 Mutation Statement Sugar Contract (<<) ​

Container mutation sugar contract:

Surface forms:

  1. target << [s1, s2, ...]
  2. target << [s1:q1, s2:q2, ...]
  3. group_target << [series(src_ref, [q1, q2, ...])]
  4. target << [source.partition(program_expr)[i]: q]
  5. target << [container.contents[i]]
  6. target << [container.contents[i]: q]

Contract rules:

  1. [] denotes the mutation source list. It is a statement-specific source-list form, not an ordinary list literal.
  2. Full-transfer (src) and quantified (src:Q) items may appear in the same source list. They are applied in source-list order as one mutation step; material-state changes are committed only if every item succeeds.
  3. Quantified mode requires unit-bearing quantities, including expression results governed by Section 11.2.1.
  4. series(...) is the sole ordered-mapping surface form.
  5. series(...) is valid only when the target is a container_group_ref.
  6. series(src_ref, [v1, ...]) requires cardinality equality between target-group size and value-list length.
  7. Ordinary [...] source-list semantics remain unchanged and do not imply ordered mapping.
  8. If an ordinary source item resolves to the same container as target, that source item is valid and evaluates as a no-op rather than a validation/runtime error.
  9. source.partition(program_expr)[i] is a source-local selector form; its accepted program set and selector semantics are defined in Section 6.3.11.
  10. container.contents[i] is a container-backed contents-portion selector valid only as a mutation source item. It does not create a child container and does not make bare container.contents a material container alias.
  11. If container.contents[i] resolves to the same container as target, the material transfer is a no-op but the current indexed contents state is disturbed and must not be reused for later container.contents[j] reads without a new explicit organization-producing operation.
  12. The current public contract is conservative: ordinary mutation of a container with current indexed contents state invalidates that indexed state unless the mutation effect can be applied to the current contents state.
  13. A mutation effect is preservable only when it is covered by an explicit selected-part operation such as container.contents[i], or by a preservation contract recorded on the current indexed contents state.
  14. Preservation contracts are declared by the operation or program that produced the indexed contents state. A preserving mutation must satisfy the contract through the current execution gate and must map the mutation effect to a declared indexed part, such as a default incoming slot.
  15. If the contract is satisfied and the effect is mappable, runtime updates the affected indexed part and keeps the contents state valid. Otherwise the indexed state is marked stale.

Currently defined preservation contracts:

  1. field_retention: produced by magnetic separation. It requires a matching active field context and declares a retained slot plus a default incoming slot for compatible additions.

Material effects:

  1. Source items contribute in source-list order. A full-transfer item contributes its entire current material contents to the target.
  2. A quantified item contributes its declared quantity from the source's state at that point in the list. Volume/mass transfers follow the density rules and proportionally carry component quantities. A count transfer from a homogeneous cell suspension determines the corresponding carrier volume from the recorded concentration.
  3. An ordinary source that is the target itself has no material effect.
  4. Same-container container.contents[i] sources preserve material totals but invalidate the current indexed contents state.
  5. series(...) maps the declared quantities to the target group in order.

6.3.5.1 Author-Declared Component Replacement ​

Component replacement is an author-declared change in tracked composition. It identifies the existing material entries being replaced and declares the products that take their place within the same container. Transfer moves existing material; a relationship transition preserves an entry's identity and quantity; replacement introduces new product identities.

The declaration must identify distinct live source entries against the same pre-change material state. Each source has a tracked mass, volume, or count quantity and one or more declared products. Product quantities may be known mass, volume, or whole-number count, or explicitly unknown mass.

Replacement preserves the container identity and the identity and order of unselected entries. Products occupy their source entry's position and retain provenance to that source. An empty product set does not constitute deletion. Material in active indexed contents must be collected before replacement; coupled multi-source transformations are outside this contract.

The product identities and quantities are authored assumptions. A preceding disruption does not determine them, and unspecified constituents and yields are not inferred. Chapter 7 defines provenance, atomic state changes, and subsequent movement; Section 11.2.2 defines unknown quantity. These semantic requirements do not prescribe a particular implementation API.

6.3.6 Implementation Independence ​

Implementations may use different internal operations and data structures. Conformance is determined by the source forms, accepted values, observable results, and failure behavior specified here.

6.3.7 Atomic Statement Contract Matrix ​

Alignment rule:

  1. Source-level shapes in this subsection MUST follow Chapter 4 when older draft wording conflicts.
  2. Operator call contracts are listed in one place.
  3. For closed enum parameters, qualified members retain their enum-family identity through bindings and protocol arguments. A member from another family is a type error even if its text is identical. Unknown members and members outside the operation's allowed subset are rejected before the affected operation executes. Existing permitted tokens and strings are compatibility spellings, not an extension mechanism. Normal name binding takes precedence over interpreting an unbound name as a legacy token.
Atomic unitSource shapeRequired core argsRun spec / optionsReturn contract
<<target << [sources...];target, sourcesno program; full-transfer and quantified items may mix in source order; quantified item must be unit-bearing (src:20uL)mutates target/source container material state
agitagit(sample=..., mode=..., [drive=...], [motion=...], [duration=...], [rate=...], [cycles=...]);`sample: ContainerContainerGroup, or PlateCarrierforTAP; mode`closed AgitationMode; SHAKE accepts optional ShakeMotion; CENTRIFUGE requires drive and duration; parameter combinations follow §6.3.15
with envwith env(...) { [hold(target_expr);] ... }env, body, optional hold target declarationsscalar thermal may use duration inside env(...) when a finite environment window is required; thermal_program forbids outer duration duplication; env targets are declared by positional hold(target_expr) or inferred from body; legacy hold(sample = ...) is compatibility-onlyscoped environment context over statement block
with constraintwith constraint(...) { ... } or stmt with constraint(...);constraint set, scoped stmt/blockdevice-independent execution requirements; see Constraint Contractapplies execution requirement scope to enclosed statement(s)
streamlet s = stream(sample = x, unit = single_cell, [panel = p]);sample, unitpanel optional; result is unit-stream only, not material split groupunit_stream_ref
seplet g = sep(sample=..., program=centrifuge_program(...));sample, programoptional component_fates and transitions; program is mechanism group; keep_source only inside centrifuge_program(...)sep_container_group fixed group[0] / group[1]
fraclet fg = frac(sample=..., program=density_gradient_program(...));sample, programprogram carries axis/order/binsfraction_group ordered containers (fg[0..n-1])
imglet obs = img(sample=..., quantity=..., [schema_ref=...], [save_raw=...]);sample, quantitystandard simple quantities use fixed result fields; complex results use schema_refsingle data_ref; grouped input returns data_group_ref
ecplet obs = ecp(sample=..., quantity=..., [schema_ref=...], [save_raw=...]);sample, quantitystandard simple quantities use fixed result fields; complex results use schema_refsingle data_ref; grouped input returns data_group_ref
phylet obs = phy(sample=..., quantity=..., [schema_ref=...], [save_raw=...]);sample, quantitystandard simple quantities use fixed result fields; complex/raw results use schema_refsingle data_ref; grouped input returns data_group_ref

6.3.8 Program / Quantity / Schema Contracts ​

Contract kindTypical statement/operatorRole
thermal_programenv(thermal=thermal_program(...)), pcr(...)retained as single-segment thermal trajectory object
centrifuge_program / centrifugal_filtration_program / magnetic_program / field_program / filtration_program / phase_partition_program / precipitation_program / disrupt_programsep(..., program=...)separation mechanism parameter groups; non-fractionating agit(mode=CENTRIFUGE, ...) uses action-local drive instead
density_gradient_program / chromatography_programfrac(..., program=...)fractionation mechanism parameter groups
quantity = ...img/ecp/phystandard measurement/readout target selector
data_schema(...) / schema_refimg/ecp/phy/data_refopen result-structure declaration and injection surface

Program constructors are descriptor/callable values, not executable actions by themselves. They may be bound with let for readability and reuse, but take effect only when a compatible operation or scope consumes the descriptor, for example sep(..., program=p), frac(..., program=p), or with env(thermal=tp). The consuming owner is responsible for resolving the descriptor and enforcing the program family match.

6.3.9 Core Contract Boundary ​

Core source contract:

  1. atomic statements and statement-scoped forms from Chapter 4
  2. container/content initialization contracts required to explain those forms
  3. readout/result contracts needed by img/ecp/phy authoring

Workflow libraries and higher-level standard-library compositions are outside this chapter's scope.

6.3.10 Source-Level Contract Summary ​

Cross-cutting source-level contracts used by the operator-specific sections:

TopicContract
Container/content mutationSource-level mutation is unified as statement-first <<; no separate mix/dilute/transfer source keywords.
Mutation source styleOne << source list may mix full-transfer (src) and quantified (src:Q) items; each item uses its own transfer mode in source order.
Quantified source itemMust carry physical units (for example 20uL); unitless ratio form (for example src:1) is forbidden.
Source-local partition selectorsource.partition(program)[i] is a mutation-source-only selector for drawing from one ordered portion of a source container without first materializing a sep group.
Container contents selectorcontainer.contents[i] is a mutation-source-only selector for drawing from the current indexed contents state produced by standalone sep(...) or frac(...); it is not a child container or bindable value.
Environment controlEnvironment is block-scoped via with env(...) { ... }; scalar thermal uses optional duration inside env(...) when the block needs a finite environment window; thermal_program.cycles does not imply block repetition; canonical explicit target declaration is hold(target_expr).
Constraint layerExecution requirements are represented by constraint(...), not by device/program fields; block form is formal semantics and trailing form is sugar. See Constraint Contract.
Macro-to-micro transitionstream(sample=..., unit=...) is the source-level entry for converting macro samples into unit streams.
Binary separationsep is fixed two-output split with ordered return group[0] / group[1], with mechanism program and mode-specific slot semantics.
Multi-fraction separationfrac is ordered multi-output split and is not collapsed into sep.
Measurement/readoutimg/ecp/phy use quantity = ...; outputs are data_ref / data_group_ref.

6.3.10.1 Transfer Delivery Requirements ​

See Constraint Contract: transfer delivery for DROPWISE and SPREAD.

6.3.10.2 Sealed Environment-Hold Requirement ​

See Constraint Contract: sealed environment holds.

6.3.11 Source-Local Partition Selector Contract ​

Canonical source-level shapes:

  1. target << [source.partition(program_expr)[0]: amount];
  2. target << [source.partition(program_expr)[1]];

The selector source.partition(program_expr)[i] is a source-local portion reference. It selects one ordered portion of source under the supplied partition program and is valid only as a mutation source item. It is not a container, not a materialized group, and not a value that authors may bind with let for later standalone use.

program_expr must resolve to a concrete let-bound or inline partition-capable program descriptor. The accepted program descriptors are the same binary separation program descriptors used by sep; no new program type is introduced for this selector.

partition(program_expr) accepted programRequired fieldsOptional fields[0][1]
centrifuge_programdrive: CentrifugalSettingkeep_sourcesupernatantpellet
magnetic_program--bound / retainedflowthrough / removable
disrupt_program-method, durationlysatedebris_or_residue
field_programexactly one of voltage: Q[ElectricPotential], current: Q[ElectricCurrent], or field: Q[ElectricPotential]durationtarget_band_fractionnon_target_fraction
filtration_programmembrane, drive-filtrateretentate
centrifugal_filtration_programmembrane, drive: CentrifugalSettingdurationfiltrateretentate
phase_partition_programsolvent-target_phaseother_phase
precipitation_programreagent-precipitatesupernatant

centrifugal_filtration_program(...) models membrane/column filtration driven by centrifugal force. It does not inherit the sedimentation result semantics of centrifuge_program(...): its stable outputs remain filtrate and retentate. Its drive field has type CentrifugalSetting.

Rules:

  1. The index must be a compile-time decidable non-negative integer.
  2. Current partition selectors are binary; only [0] and [1] are valid.
  3. density_gradient_program(...) and chromatography_program(...) are not accepted by partition(...); they remain frac programs.
  4. thermal_program(...) and readout-oriented descriptors are not partition programs.
  5. partition(...) does not accept free-form named fields such as partition(mode = ..., drive = ...); program fields stay inside the concrete *_program(...) descriptor.
  6. A full-transfer selector item moves the whole selected portion. A quantified selector item moves the requested amount from the selected portion.
  7. Unselected portions, and any residual material left in the selected portion after quantified transfer, remain associated with the original source container according to the program's partition semantics.
  8. partition(...) does not replace sep(...). Use partition(...) when the source container remains the operative container and the partition is only consumed by the current transfer. Use sep(...) when the protocol needs materialized, independently addressable output containers or groups.

Magnetic-bead liquid removal example:

culs
let mag_sep = magnetic_program(duration = 5min);

with env(field = magnetic_rack) {
  waste << [bead_tube.partition(mag_sep)[1]: 180uL];
}

Here [0] is the bound/retained bead-associated portion and [1] is the flowthrough/removable liquid portion. The transfer removes liquid from the source-local portion without first creating a sep_group.

6.3.12 sep Detailed Contract ​

Canonical source-level shapes:

  1. let sep_group = sep(sample=..., program=centrifuge_program(...));
  2. sep(sample=..., program=centrifuge_program(...));
  3. let sep_group = sep(sample=..., program=filtration_program(...), component_fates={RPE1:{filtrate:0%, retentate:100%}});
  4. let sep_group = sep(sample=..., program=magnetic_program(...), transitions=[transition(subject=sample.materials[1], output=MagneticProgramOutput.FLOWTHROUGH, to=MaterialRelation.FREE)]);
  5. let sep_group = sep(sample=..., program=magnetic_program(...), transitions=[transition(subject=sample.materials.get("TARGET_PROTEIN"), output=MagneticProgramOutput.FLOWTHROUGH, to=MaterialRelation.FREE)]);

Effect contract:

  1. Let-bound sep(...) materializes a two-slot container group and preserves existing sep_group[0] / sep_group[1] return behavior.
  2. Standalone sep(...) acts on the source container and records an indexed contents organization for later container.contents[0] / container.contents[1] mutation-source reads.
  3. Standalone sep(...) does not create a bindable group unless authored in a let binding.

Per-content fate contract:

  1. component_fates is optional and maps content identity to a two-output ratio record.
  2. Output keys may use the selected program's semantic output names or the string slot keys "0" and "1".
  3. Both outputs are required. Each value is a unitless ratio from 0 to 1 or a percentage from 0% to 100%, and the pair must sum to 1 (100%).
  4. An authored fate has precedence over association-derived retention and the reference partition strategy. It remains subject to quantity-axis and conservation validation.
  5. The referenced content must exist in the source container at runtime.

Per-material relationship-transition contract:

A live quantity may be a positive known amount or a retained positive share of an unknown mass. Selection does not grant transition eligibility: unknown-mass replacement does not support relationship changes on its products (Sections 7.2.2 and 11.2.2).

  1. transitions is optional and contains transition(...) values scoped to a concrete positive-quantity separation output.
  2. subject is either sample.materials[index] or sample.materials.get(entry_id), where materials is the normalized, ordered list of live material entries immediately before the operation. The index is a non-negative static integer. The entry_id key is a statically resolvable non-empty string and is matched exactly within the same container. get(...) never matches content_ref, content codes, labels, or prefixes and never falls back to another same-content entry.
  3. output is one namespaced member of the selected separation program's concrete output enum. The enum type must match the concrete program; for example, filtration_program(...) accepts FiltrationProgramOutput.FILTRATE or FiltrationProgramOutput.RETENTATE and rejects members of MagneticProgramOutput.
  4. to is a namespaced MaterialRelation.MEMBER expression. The author-settable domain is free, container_surface, pellet, precipitate, disrupted, bead_bound, membrane_bound, cell_bound, and field_retained. UNRESOLVED is an internal sentinel and MUST NOT be authored. String values, bare identifiers such as free, and members outside this enum are invalid.
  5. The directed transition graph has no source/target-pair whitelist. Runtime derives the current MaterialRelation from the selected entry and accepts any target in the author-settable enum domain, subject to target-state structural invariants.
  6. free has no association target and clears the source association. The container-associated target relations use the concrete output container as their typed association target.
  7. bead_bound, membrane_bound, and cell_bound require associated_with to use either same-sample material selector form. The selected association target must have positive quantity in the same output and is the material entry to which the selected subject becomes bound.
  8. associated_with is forbidden for target relations that are not component-bound.
  9. An authored relationship transition specifies the relation of the selected source entry in the selected output, taking precedence over the default relation assigned by the separation model. component_fates independently determines quantity allocation. A transition preserves material identity and quantity; it does not infer chemistry or introduce another separation program.
  10. Runtime preserves identity and quantity, validates the complete candidate, and commits atomically. Any selector, enum, association, output, or conservation failure preserves the previously committed source state.

Surface-release authoring pattern:

filtration_program(membrane = "adherent_cell_surface", drive = "cell_lifter") uses the existing surface-boundary partition vocabulary: FILTRATE is the released liquid fraction and RETENTATE is the surface-retained fraction. It does not declare an additional filter material. An author routes cells and carrier with component_fates and declares the selected cellular entry MaterialRelation.FREE in FILTRATE. A bound let lifted = sep(...) produces the output group; subsequent transfer collects lifted[0]. Detachment therefore precedes collection and any chemical-lysis material replacement.

Complete recovery is an author-declared fate, not a predicted efficiency. This pattern preserves count and carrier volume and supplies no rupture, yield, duration, force, or stroke-count inference. Human execution presents cell-lifter handling; a robot projection identifies surface release separately from a filter-device action. Actual device execution requires an adapter for that handling operation. Existing selector, output, quantity, and association validation rules apply without additional syntax or diagnostic codes.

MaterialRelation has the following closed association contract. In this table, “required” means that the author must supply associated_with = sample.materials[index] or associated_with = sample.materials.get(entry_id); “not applicable” means that the argument MUST NOT appear because the relationship either has no target or its target is the concrete output container.

to memberRelationship target categoryExplicit same-sample material selectorRuntime association target
MaterialRelation.FREEno associationnot applicableassociation is cleared
MaterialRelation.CONTAINER_SURFACEoutput-container associationnot applicableconcrete output container
MaterialRelation.PELLEToutput-container associationnot applicableconcrete output container
MaterialRelation.PRECIPITATEoutput-container associationnot applicableconcrete output container
MaterialRelation.DISRUPTEDoutput-container-scoped statenot applicableconcrete output container
MaterialRelation.FIELD_RETAINEDoutput-container-scoped retentionnot applicableconcrete output container
MaterialRelation.BEAD_BOUNDcomponent-entry associationrequiredselected bead material entry projected into the same output
MaterialRelation.MEMBRANE_BOUNDcomponent-entry associationrequiredselected membrane material entry projected into the same output
MaterialRelation.CELL_BOUNDcomponent-entry associationrequiredselected cell material entry projected into the same output
MaterialRelation.UNRESOLVEDinternal sentinelinvalid author targetnone; the transition is rejected

Therefore, only BEAD_BOUND, MEMBRANE_BOUND, and CELL_BOUND explicitly bind one material entry to another material entry. For those three members, the selected association target must belong to the operation's source materials list and must have positive quantity in the same output. All other author-settable relations reject the associated_with argument.

Program-owned output enums are closed and ordered by output index:

ProgramOutput enumpart 0part 1
sep_program compatibility surfaceSepProgramOutputFRACTION_AFRACTION_B
centrifuge_programCentrifugeProgramOutputSUPERNATANTPELLET
magnetic_programMagneticProgramOutputBOUNDFLOWTHROUGH
disrupt_programDisruptProgramOutputLYSATEDEBRIS_OR_RESIDUE
field_programFieldProgramOutputTARGET_BAND_FRACTIONNON_TARGET_FRACTION
filtration_programFiltrationProgramOutputFILTRATERETENTATE
centrifugal_filtration_programCentrifugalFiltrationProgramOutputFILTRATERETENTATE
phase_partition_programPhasePartitionProgramOutputTARGET_PHASEOTHER_PHASE
precipitation_programPrecipitationProgramOutputPRECIPITATESUPERNATANT

The same enum members define the ordered sep result and the transition selector. sep_group[0] / sep_group[1] remain the stable structural access surface. component_fates record keys remain the program's semantic-role keys because record-key grammar does not use member expressions; those keys denote the same output roles as the enum members.

Return contract:

  1. sep_group[0] / sep_group[1] are the stable user-facing access forms for let-bound sep(...) output groups.
  2. Return arity is fixed at 2 for all included modes.
  3. container.contents[0] / container.contents[1] are valid only when a current standalone contents state exists for that container.

keep_source contract:

  1. Allowed only inside centrifuge_program(...).
  2. Values: CentrifugeProgramOutput.SUPERNATANT or CentrifugeProgramOutput.PELLET. Compatible supernatant / pellet tokens and strings denote those same members; an output member from another program family is not interchangeable.
  3. Omitted keep_source means both outputs are newly materialized container identities.

Mode contract:

programRequired fieldsOptional fieldsgroup[0]group[1]
centrifuge_programdrive: CentrifugalSettingkeep_sourcesupernatantpellet
magnetic_program--boundflowthrough
disrupt_program-method, durationlysatedebris_or_residue
field_programexactly one of voltage: Q[ElectricPotential], current: Q[ElectricCurrent], or field: Q[ElectricPotential]durationtarget_band_fractionnon_target_fraction
filtration_programmembrane, drive-filtrateretentate
centrifugal_filtration_programmembrane, drive: CentrifugalSettingdurationfiltrateretentate
phase_partition_programsolvent-target_phaseother_phase
precipitation_programreagent-precipitatesupernatant

Separation program fields describe the separation mechanism and its direct mechanism parameters. They do not duplicate block-scoped physical environment control. Run temperature remains an enclosing with env(thermal=...) condition. centrifugal_filtration_program(...) owns an optional action-local duration because it describes the membrane run time. In contrast, centrifuge_program(...) intentionally does not accept duration; an authoring phrase such as "centrifuge 5 min at 4C" for an actual separation is represented as a sep(...) statement inside with env(thermal=4C, duration=5min). For non-fractionating centrifugation, agit(mode=CENTRIFUGE, drive=..., duration=...) owns both the centrifugal drive and finite action time. It does not use a separation program.

field_program(...) describes electric-field-driven separation. It requires exactly one control setting: voltage: Q[ElectricPotential], current: Q[ElectricCurrent], or field: Q[ElectricPotential], where field is an alias for voltage. Voltage and current are not implicitly converted.

filtration_program.drive accepts the standard members FiltrationDrive.PRESSURE, FiltrationDrive.ASPIRATION, and FiltrationDrive.CELL_LIFTER, corresponding to pressure, aspiration, and cell_lifter. These names identify authored handling; they do not calculate separation efficiency or supply an equipment implementation. Hosts may install a Python enum subclass of FiltrationDriveBase with a stable identity and version, then reference its qualified members in source. Types from other parameter families are not interchangeable. Standard bare tokens and strings normalize to the standard members; other historical text remains compatible text, not an implicitly installed extension. Registration rejects duplicate standard spellings and identities. This contract does not change the quantity-valued drive of centrifuge_program or centrifugal_filtration_program.

disrupt_program.method has the closed type DisruptionMethod, with members MECHANICAL, SONICATION, SHEAR_HOMOGENIZATION, HIGH_PRESSURE_DISRUPTION, and BEAD_IMPACT. The corresponding lowercase tokens and strings remain compatibility inputs. It records the authored disruption method or physical mechanism; it does not select a device. Concrete equipment binding remains a planner or driver concern. Omit method when the source does not state one; implementations must not infer it from the resulting material state.

6.3.13 frac Detailed Contract ​

Canonical source-level shapes:

  1. let frac_group = frac(sample=..., program=density_gradient_program(...));
  2. frac(sample=..., program=density_gradient_program(...));

Core argument contract:

  1. Required source-level args: sample, program.
  2. program is density_gradient_program(...) or chromatography_program(...).
  3. bins is integer-like and must satisfy semantic lower bound for ordered fractionation.

Return contract:

  1. Return value is fraction_group with stable ordered indexing.
  2. frac_group[i] resolves to container output at index i; only compile-time non-negative integer indexing is part of the contract.
  3. For density_gradient_program, axis accepts DensityGradientAxis.DENSITY; order accepts DensityGradientOrder.TOP_TO_BOTTOM or DensityGradientOrder.BOTTOM_TO_TOP. The lowercase tokens and strings are compatible spellings of those members. These closed types do not close the separate extensible axis/order vocabulary of chromatography_program.
  4. For chromatography_program, the standard pair is ChromatographyAxis.RETENTION_TIME and ChromatographyOrder.EARLY_TO_LATE. The existing retention_time / early_to_late tokens and strings resolve to those members. Other previously accepted text remains compatibility metadata, not a declaration of a registered extension.
  5. Extension packages provide domain-specific axis and order types. An order declares its exact axis; registered typed values must form a matching pair. An unknown compatibility string cannot satisfy the counterpart of an explicit extension. Historical text-only pairs retain their prior acceptance. Protocols reference installed type members; this does not introduce class declarations into the source language.
  6. Installed extensions have stable domain identity, version and member names. Stored values use that identity rather than implementation module paths. Restoring an unknown type/version fails explicitly and must not load code from a serialized class path. Type names obey ordinary source binding and shadowing; installation must not replace standard types.
  7. Declaring an extension does not provide a new separation algorithm or device capability. Execution requires explicit support for the extension identities; an unsupported driver must reject the operation before executing it.
  8. Standalone frac(...) records an ordered indexed contents state for later container.contents[i] mutation-source reads; it does not create a bindable group unless authored in a let binding.

6.3.14 with env Contract ​

Canonical shape:

  1. with env(thermal=..., [co2=...], [rh=...], [duration=...]) { ... }
  2. with env(thermal=thermal_program(...)) { ... }
  3. with env(field=...) { ... }

Explicit target declaration shape:

  1. hold(container_ref);
  2. hold(container_ref.contents);
  3. hold(container_ref.structure.top);
  4. hold(container_ref.structure.bottom);
  5. hold(container_ref.structure.sidewall);
  6. hold(group([target_ref_1, target_ref_2, ...]));

Parameter contract:

  1. At least one supported environment dimension is required; dimensions are thermal and field.
  2. thermal accepts union type: Q[Temperature] | thermal_program.
  3. field is used for non-thermal field domains such as mz_separation.
  4. co2/rh are optional thermal modifiers; they are not primary dimensions.

Behavior constraints:

  1. thermal = Q[Temperature]: constant-temperature environment scope. duration is required when the block is a pure environment hold/incubation, when the block body needs an enclosing time boundary, or when the author intentionally supplies a finite environment window.
  2. thermal = Q[Temperature] may omit duration for active-operation blocks whose environment lifecycle is exactly the lexical execution of the enclosed statements. This form represents instructions such as assembling or mixing a reaction under a stated temperature condition without inventing a finite hold time.
  3. thermal = thermal_program: program-driven thermal scope; duration is forbidden because total time is derived by program.
  4. field = ...: field-domain environment scope; outer duration is forbidden.
  5. co2/rh require thermal; they are forbidden when thermal is program-typed.
  6. duration and thermal_program.cycles describe the environment program itself and MUST NOT imply block-body repetition.
  7. Block-body statements execute once in lexical order; per-cycle/stage hooks are not part of the contract.
  8. When a single enclosed action has no action-local duration field and the source supplies an execution time boundary, the enclosing scalar thermal duration supplies the time boundary for executing that action under the stated environment; for example centrifugation time may be expressed by wrapping sep(..., program=centrifuge_program(drive=...)) in with env(thermal=..., duration=...).
  9. Action-local duration remains distinct from environment duration. Operations such as agit(...) do not inherit env.duration; if the action itself requires a duration, the action must declare it.

Target declaration constraints:

  1. hold(...) is only valid directly inside a with env(...) block.
  2. Canonical hold(...) accepts exactly one positional HoldTarget expression and no named arguments.
  3. hold(...) records environment-hold/reporting targets for the nearest enclosing with env(...); it does not define the environment scope and does not introduce temperature, time, program, device, or execution-requirement parameters.
  4. Direct-child hold(...) declarations are not standalone executable actions. If a with env(...) block contains only hold(...) declarations, the block applies a pure environment hold to those targets.
  5. If a with env(...) block contains direct-child hold(...) declarations plus executable body statements, those declarations record environment-hold targets for that layer and the remaining statements execute under that same layer. They do not narrow the lexical environment scope, impose ordering, or exclude other body statements from the environment context.
  6. Direct-child hold(...) declarations may appear anywhere in the immediate with env(...) body and are collected as environment targets rather than executable steps.
  7. If a with env(...) block contains no explicit hold(...) declarations, env targets are inferred from enclosed executable statements as in the body-inference model.
  8. Bare container targets in hold(container_ref) are content-facing by default. Authors should use a structure facet such as container_ref.structure.top when the controlled object is the physical container structure rather than the contained material.
  9. Legacy hold(sample = container_ref) may be accepted by implementations as compatibility input, but it is not the canonical source form for new examples.
  10. hold(...) inside nested if, repeat, with constraint, or other non-env sub-blocks is not a valid target declaration for the enclosing env block.

Target semantics:

  1. Chapter 6 defines the HoldTarget type and with env target-declaration contract; Chapter 4 defines the corresponding source spelling.
  2. with env(...) owns environmental conditions, thermal_program(...) owns thermal trajectory fields, and hold(...) owns only target declaration.
  3. A container structure-facet target is not a sample/material state and must not be interpreted as sample thermal state.
  4. A bare container remains ergonomic for ordinary thermal holds by defaulting to its contents in HoldTarget position.
  5. container.contents and container.structure.<facet> are direct target views, not partitioning methods and not new container allocation.
  6. container.structure.top is closure-agnostic and must not be restricted to a device-specific lid/cap concept.
  7. A PCR anti-condensation or top-structure heating requirement is represented as a structure-facet hold such as hold(pcr_tube.structure.top) under a thermal env, not as sample temperature.

6.3.15 Agitation Contract (agit) ​

Shape: agit(sample=..., mode=..., [drive=...], [motion=...], [duration=...], [rate=...], [cycles=...]);.

sample accepts one Container or a flat, ordered ContainerGroup. Explicit and let-bound groups have the same semantics: the action applies independently to each member without merging members or transferring material between them. For TAP, the sample may instead be a whole plate carrier. This denotes one carrier action affecting the contents organization of its allocated wells, without transferring material between wells. A selected well group continues to denote independent member actions. Whole-plate TAP requires a declared carrier; an inline descriptor does not identify that instance. Its affected wells must have an explicit association with the carrier. Incomplete membership is rejected before the action changes any contents organization; membership must not be inferred from equal labels or well names.

Existing plate-descriptor calls for other modes, including whole-plate SHAKE, remain accepted with their established meaning. They do not become independent well-group actions.

mode has the closed type AgitationMode: VORTEX, INVERT, FLICK, TAP, SHAKE, STIR, CENTRIFUGE, and the compatibility member ROTATION. Qualified members are canonical; the corresponding lowercase tokens and strings remain accepted.

SHAKE describes agitation by moving the container. Its optional motion has the closed type ShakeMotion: LINEAR, ORBITAL, ROCK, or ROTATION. Lowercase tokens and strings denote the corresponding motion members. The two enum families are not interchangeable. Omitting motion leaves the trajectory unspecified; no orbital or other default motion is inferred. motion is not accepted with any other mode, including the compatibility mode=ROTATION.

Mode / motionMeaningParameter contract
VORTEXVortex mixingOptional duration and rate in rpm; no cycles or motion.
INVERTDiscrete inversionOptional cycles; no duration, rate or motion.
FLICKGentle tube flicking, distinct from inversion, vortexing and plate tappingOptional cycles; no duration, rate or motion. An omitted count is not inferred.
TAPDiscrete tapping of a container or plate carrier to mix its contentsNo duration, rate, cycles, motion or drive. Count, timing and intensity remain unspecified. Gentle handling is a separate requirement.
STIRStirringOptional duration and rate in rpm; no cycles or motion.
SHAKE without motionUnspecified shaking trajectoryExisting optional duration and rate in rpm; no cycles.
SHAKE, LINEARBack-and-forth translation along a lineRequired duration, optional frequency rate; no cycles.
SHAKE, ORBITALCircular orbital translation, without implying end-over-end inversionRequired duration, optional rate in rpm; no cycles.
SHAKE, ROCKRepeated limited-angle tilting and returnExactly one of duration or cycles, plus optional frequency rate. One cycle is a complete out-and-back movement.
SHAKE, ROTATIONContinuous rotation, such as end-over-end tube rotationRequired duration, optional rate in rpm; no cycles.
Compatibility ROTATIONSame motion and parameter contract as SHAKE with ShakeMotion.ROTATIONExisting source and stored plans remain valid.
CENTRIFUGECentrifugation of the same container without declared fractionsRequired drive in g or rpm and finite positive duration; no motion, rate, cycles, or program.

For an explicit motion, duration must be a finite positive time quantity. Rocking cycles must be a positive, unitless integer. Every supplied motion rate must be finite and positive: LINEAR and ROCK use Hz or cycle/min, while ORBITAL and ROTATION use rpm. A frequency is not implicitly converted to a rotation rate. Rocking may combine a count with a rate, but not a count with a duration; no additional duration is inferred or inserted.

culs
agit(sample = sample, mode = AgitationMode.SHAKE,
     motion = ShakeMotion.ROCK, cycles = 3);
agit(sample = sample, mode = AgitationMode.SHAKE,
     motion = ShakeMotion.ORBITAL, duration = 1min, rate = 200rpm);
agit(sample = sample, mode = AgitationMode.SHAKE,
     motion = ShakeMotion.ROTATION, duration = 2h, rate = 20rpm);
agit(sample = sample, mode = AgitationMode.CENTRIFUGE,
     drive = 3000g, duration = 5s);

Omitted rate and geometric details remain unspecified. This contract does not introduce times, orbit_diameter, stroke_length, tilt_angle, a SWIRL member, or a SURFACE_RINSE mode. Gentle execution can be expressed separately with a compatible constraint; rocking does not itself guarantee gentleness or preservation of adherent cells.

These rules apply after aliases, protocol arguments and runtime expressions are resolved. Known invalid combinations are rejected before execution; deferred values must be checked before the action executes. An enclosing environment duration does not supply a required action-local duration.

agit returns no material or data value. All modes preserve container identity and quantities and invalidate prior indexed-contents organization as specified in Chapter 7. CENTRIFUGE does not create separation slots or assert a pellet, supernatant, or bead-settling outcome. Its finite drive/time settings describe the action, not an observed result. Other modes do not imply binding yield, reaction progress, or proof of complete homogenization. The execution backend must receive the selected mode and parameters without silently substituting another motion or separation.

6.3.16 Readout Family Contract (img/ecp/phy) ​

quantity has the closed type ReadoutQuantity. Each lowercase quantity below maps to the uppercase member of that type, for example fluorescence to ReadoutQuantity.FLUORESCENCE and ph to ReadoutQuantity.PH. Compatible lowercase tokens and strings denote the same members. Each operation accepts only its own subset; sharing the enum type does not permit PH in img. ReadoutQuantity.CUSTOMIZED still requires schema_ref.

img:

  1. Shape: let obs = img(sample=..., quantity=..., [schema_ref=...], [save_raw=...]);
  2. Core args: sample, quantity.
  3. Quantity set: uv_absorbance | fluorescence | colorimetric | customized.
  4. Return: single-sample input yields data_ref; grouped sample input yields data_group_ref.

ecp:

  1. Shape: let obs = ecp(sample=..., quantity=..., [schema_ref=...], [save_raw=...]);
  2. Quantity set: ph | conductivity | dissolved_oxygen | orp | customized.
  3. Return: single-sample input yields data_ref; grouped sample input yields data_group_ref.

phy:

  1. Shape: let obs = phy(sample=..., quantity=..., [schema_ref=...], [save_raw=...]);
  2. Quantity set: temperature | pressure | flow_rate | mass | volume | humidity | current | customized.
  3. Return: single-sample input yields data_ref; grouped sample input yields data_group_ref.

Cross-family rule:

  1. Readout operators are data-producing operators and are not container-mutation operators.
  2. data_ref uses a shared envelope plus family- and quantity-specific result payload.
  3. result field sets are determined by family + quantity, not by free-form runtime text.
  4. raw_artifact (when present) is auxiliary evidence and does not replace structured result.
  5. Optional export_refs may attach richer external/profile-specific result exports; they do not replace result as the source-level control surface.
  6. Grouped sample input yields data_group_ref, whose order matches the input container group.

Local data reference constructors:

data_ref(kind=...) and data_group_ref(kind=...) share the DataKind parameter family: OBSERVATION, SEQUENCE_READ, and SORT_RECORD denote observation, sequence_read, and sort_record. A host may explicitly install a Python enum subclass of DataKindBase; source refers to its qualified members. Standard old spellings normalize to these members, while other historical text remains text. A bound value of another enum family is rejected rather than reinterpreted by its spelling. Declaring a data kind does not define result fields, grant measurement behavior, or change the schema_ref rules.

Serialized local references retain the textual data_kind. An installed extension also retains data_kind_type, carrying its stable identity, version, and member; standard kinds and historical text do not add that field. Extension-bearing plans require the matching installed identity for replay; decoding never loads Python code from stored paths. Both extension families follow the existing scoped naming and parameter-validation rules, including aliases, protocol arguments and bindings. An installed type must belong to exactly one parameter family: multiple inheritance across unrelated enum bases is rejected at registration, including combinations of chromatography axis and order bases. Intermediate bases within one family and ordinary non-enum mixins remain valid. Open enum members cannot be restored from closed class-name/member payloads; identity and version checks always use the currently installed registry, even when a decoder outlives an installation scope.

6.3.17 Core Contract Boundary ​

  1. Sections 6.3.7 to 6.3.19 define source-level contracts for the core atomic operator surface.
  2. The core source path covers constructor/init, <<, agit, with env, sep, frac, source-local partition selectors, img, ecp, phy, and indexed group references.
  3. Higher-level workflow libraries are outside the core contract defined in this chapter.
  4. Compatibility behavior must not change the source-level contracts defined in this chapter.

6.3.18 Separation vs Fractionation Boundary ​

Principle distinction:

  1. sep is binary semantic split (target vs non-target) and returns fixed two-slot outputs.
  2. frac is ordered distribution split across multiple bins and returns ordered multi-slot outputs.
  3. If domain intent is to preserve distribution/layer profile, use frac instead of collapsing to sep.

Classification note:

  1. sep and frac are parallel atomic operators under the same broad separation family.
  2. They are not aliases of each other.

6.3.19 Readout Category Map ​

CategoryTypical scenariosPreferred atomic pathTypical output form
Optical quantificationUV/optical fluor/colorimetric quantimg(sample=..., quantity=...)scalar/data readout reference
Electric-field-separated readoutgel/capillary style readoutsep(sample=..., program=field_program(...)) + img(sample=..., quantity=customized, schema_ref=...)band/peak data reference
Microscopy imagingfixed/live-cell imagingimg(sample=..., quantity=customized, schema_ref=...)image data reference
Physiological state readoutviability / pH / conductivityimg(...) (indicator route) or ecp(sample=..., quantity=...)data reference
Separation-coupled readoutpost-separation flow/seq/ms readoutdecomposition path (stream/repeat/env/img/phy/data_ref)data reference

6.3.20 Constraint Contract (with constraint) ​

This section owns constraint meaning, applicability, execution requirements and compatibility checks. Chapter 4 defines the source forms; Chapter 8 defines the diagnostic catalog.

Forms:

culs
with constraint(ConstraintRequirement.GENTLE) {
  target << [source:10uL];
}

target << [source:10uL] with constraint(ConstraintRequirement.GENTLE);

The trailing form is equivalent to a block containing that one statement. The standard requirement catalog below specifies which actions each requirement allows; the compatibility checks specify additional semantic conditions.

6.3.20.1 Meaning and Scope ​

Execution requirements constrain how an existing operation is performed. They do not introduce a new material operation or override its quantity and conservation rules. Requirements must be compatible with the enclosed action family; incompatibility is SEM_CONSTRAINT_ACTION_FAMILY_MISMATCH.

Standard requirement identities use ConstraintRequirement members; bare names remain compatibility spellings. agit accepts GENTLE, ASEPTIC, CROSS_CONTAM_CONTROL, COLD_CHAIN, DARK_PROTECTED, and CONTROLLED_ATMOSPHERE. These requirements restrict the explicit mechanical action without changing its mode, quantities, or indexed-contents effects. Other agitation combinations remain outside the accepted contract.

Nested scopes accumulate requirements, including through protocol calls. A requirement cannot be silently cancelled or skipped for an incompatible action; leaving a scope restores its enclosing requirements. Combination restrictions, including the exclusivity of CUSTOMIZED, apply to the combined active set.

Standard requirements support both block and trailing forms. Requirement names are static declarations. Unknown names, empty bodies, duplicate options and unsupported options are rejected. Standard requirements accept no extra options; CUSTOMIZED requires schema_ref and accepts no other option.

Applicability and decidable conflicts are checked before execution. Deferred conditions must be checked once their required values become available. Execution support is a separate requirement: a backend cannot override a language conflict.

6.3.20.2 Transfer Delivery ​

dropwise is a standard requirement in the delivery-mode category, valid on << transfers in either block or trailing form:

culs
target << [source:150uL] with constraint(ConstraintRequirement.DROPWISE);

The liquid is delivered drop by drop. 150uL remains the total transfer quantity; a full-transfer source likewise retains full-transfer semantics. No drop count, per-drop volume, rate, duration, mixing, or material conversion is inferred. The requirement applies to each source delivery in the statement and can coexist with compatible requirements such as gentle. As with other standard requirements, additional options are rejected with SEM_CONSTRAINT_UNKNOWN_OPTION.

The requirement must be preserved through execution. Human instructions must make dropwise delivery explicit, and device execution must honor it. A driver's declared lack of support produces RT_DRIVER_REQUIREMENT_UNSUPPORTED before material movement. An instruction or command projection alone does not establish physical execution.

spread is a standard delivery-mode requirement for distributing transferred material evenly across a receiving surface. It accepts the same block and trailing forms as dropwise:

culs
plate_surface << [suspension:100uL]
  with constraint(ConstraintRequirement.SPREAD, ConstraintRequirement.ASEPTIC);

The target must resolve to a surface, including through protocol parameters; a different target kind is SEM_SPREAD_TARGET_NOT_SURFACE. The requirement preserves the transferred amount, source and destination identities, and material accounting. It does not specify a spreading tool or add a second transfer. aseptic is independent and may be combined with it; spread and dropwise are not intrinsically exclusive. The requirement must reach the execution provider, and declared lack of support fails before material movement.

6.3.20.3 Sealed Environment Holds ​

sealed is a standard requirement in the environmental-protection category. It is valid on a pure environment hold and states that the hold target remains sealed for the duration of that hold:

culs
with constraint(ConstraintRequirement.SEALED) {
  with env(thermal = 25C, duration = 2h) {
    hold(elisa_plate);
  }
}

For a plate-level closure requirement, the hold target is the plate binding, not a selected well group. sealed does not create a material operation, container-state transition, implicit seal/unseal step, or persistent closure state after the constraint scope ends. Applying it to an unrelated action family is SEM_CONSTRAINT_ACTION_FAMILY_MISMATCH.

The requirement applies throughout the hold. Human instructions must explicitly state that the target remains sealed, and device execution must honor that condition. A driver's declared lack of support produces RT_DRIVER_REQUIREMENT_UNSUPPORTED before the hold.

6.3.20.4 Standard Requirement Scenarios ​

The following entries define the standard vocabulary and its current applicability. Each name is a member of ConstraintRequirement. An allowed action must still satisfy applicable semantic checks and execution-provider requirements. Qualitative requirements do not introduce numerical limits or prove physical outcomes. For stream, applicability attaches to the declaration; it does not itself establish that sampling or measurement has occurred.

DROPWISE ​

Use for gradual liquid delivery into a receiving vessel; the specified transfer quantity remains the total amount.

Applies to: <<.

SPREAD ​

Use when transferred suspension or liquid must be distributed across a receiving surface.

Applies to: <<.

PRESERVE_BOUNDARY ​

Use when transferring above a pellet or handling separated material while protecting a material interface. The requirement does not identify a particular interface or imply preserved cell adhesion.

Applies to: <<, sep, frac.

PRESERVE_LAYERING ​

Use when overlaying or handling layered material without disrupting its layers. It cannot be applied to agitation, whose contract invalidates existing layer indices.

Applies to: <<, sep, frac.

PRESERVE_FRACTION_ORDER ​

Use when collecting separation outputs or a fraction series whose order must remain identifiable. It does not reorder the declared outputs.

Applies to: sep, frac.

ASEPTIC ​

Use when handling material that requires aseptic technique, such as transferring culture medium or agitating a sterile preparation.

Applies to: <<, agit, sep, img, ecp, phy, stream.

LOW_CARRYOVER ​

Use when a shared transfer or sampling path must limit material carried from one sample to another.

Applies to: <<, sep, img, ecp, phy, stream.

CROSS_CONTAM_CONTROL ​

Use when handling multiple samples or outputs that must remain protected from cross-contamination.

Applies to: <<, agit, sep, img, ecp, phy, stream.

GENTLE ​

Use for disturbance-sensitive material during transfer, separation or agitation. The requirement does not prescribe a speed, force or duration.

Applies to: <<, agit, sep, frac, stream, environment hold.

AVOID_RESUSPENSION ​

Use when removing liquid above settled material while leaving that material undisturbed. It does not establish that sediment exists or guarantee adhesion.

Applies to: <<, sep, frac.

AVOID_SHEAR ​

Use when transferring or processing shear-sensitive material. The requirement does not prescribe a numerical shear limit.

Applies to: <<, sep, frac, stream.

PRESERVE_VIABILITY ​

Use when transferring or preparing a stream from living material for which viability-preserving handling is required. The requirement is not a prediction or measurement of survival.

Applies to: <<, stream.

COLD_CHAIN ​

Use for temperature-sensitive material during transfer, agitation, separation or an environment hold. Explicit thermal settings must satisfy the cold-chain compatibility checks below.

Applies to: <<, agit, sep, frac, img, ecp, phy, stream, environment hold.

DARK_PROTECTED ​

Use for light-sensitive preparations during handling or agitation. No protected spectral range or exemption for readout illumination is inferred; execution support must address the intended protection.

Applies to: <<, agit, img, ecp, phy, stream, environment hold.

SEALED ​

Use when an environment-hold target must remain sealed throughout the hold; see Section 6.3.20.3.

Applies to: environment hold.

CONTROLLED_ATMOSPHERE ​

Use when handling or holding material that requires a controlled atmosphere. No gas composition or concentration is inferred.

Applies to: <<, agit, img, ecp, phy, stream, environment hold.

HIGH_PRECISION ​

Use when a transfer quantity, separation control or measured quantity requires high-precision execution. No numerical tolerance is inferred.

Applies to: <<, sep, frac, ecp, phy.

LOW_LOSS ​

Use when handling limited or valuable material to minimize losses from transfer, separation or sampling. Intended waste is not thereby designated for retention.

Applies to: <<, sep, frac, ecp, phy.

QUANTITATIVE_RECOVERY ​

Use when transfer or separation is intended to recover a designated material or output quantitatively. The requirement does not alter allocation ratios or assert an achieved recovery percentage.

Applies to: <<, sep, frac.

STABILIZED_READING ​

Use when a measurement should be taken after its reading has stabilized. No settling time or stability threshold is inferred.

Applies to: img, ecp, phy.

LOW_NOISE ​

Use when a readout requires noise-reducing execution conditions. No numerical noise threshold is inferred.

Applies to: img, ecp, phy.

NONINVASIVE ​

Use when the selected readout must avoid invasive alteration of the sample. The selected method must support that requirement.

Applies to: img, ecp, phy.

CUSTOMIZED ​

Use when an execution requirement needs an explicitly declared schema rather than a standard requirement. Supply schema_ref and do not combine CUSTOMIZED with another requirement, including an inherited one.

Applies to: <<, sep, frac, img, ecp, phy, stream.

6.3.20.5 Compatibility Checks ​

Constraint checks supplement the owning action's parameter contract. They inspect only the inputs relevant to the requirement; they do not replace checks of an action's required arguments, parameter types or units.

CheckRelevant inputRequired behavior
Action applicabilityRequirement and enclosed actionReject an action outside the requirement's permitted families with SEM_CONSTRAINT_ACTION_FAMILY_MISMATCH.
Scope and combinationRequirements in the current and enclosing scopesAccumulate requirements, restore the outer set on exit, and reject incompatible combinations. CUSTOMIZED cannot be mixed with other requirements (SEM_CONSTRAINT_CUSTOMIZED_EXCLUSIVE).
Surface spreadingReceiving target kindA resolved non-surface target is rejected with SEM_SPREAD_TARGET_NOT_SURFACE, including when resolved through a protocol parameter.
Cold-chain temperatureEach explicit scoped thermal settingCheck a finite temperature in C or K against 8 C / 281.15 K. A known high temperature is SEM_CONSTRAINT_ENV_CONFLICT; a bound high temperature is RT_CONSTRAINT_ENV_CONFLICT. An explicit value still unresolved or invalid at execution is rejected with RT_CONSTRAINT_INVALID.
Customized contractschema_ref and supplied optionsRequire schema_ref; reject unsupported options.
Execution supportAction and attached requirementsDeclared lack of support is RT_DRIVER_REQUIREMENT_UNSUPPORTED before the affected action. Support cannot override a semantic conflict.

For example, this temperature is compatible with the cold-chain requirement:

culs
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);
  }
}

Changing 4C to 37C creates a semantic conflict even though COLD_CHAIN is allowed on agit. 277.15K and 310.15K have the corresponding outcomes. A temperature determined only during execution must be checked before the action. Omitting an explicit temperature does not infer one or remove the execution requirement. None of these checks proves the sample's physical temperature.

For other qualitative requirements, the action whitelist and execution support remain distinct from condition-specific semantic checks. No numerical gentle, precision, noise, viability or recovery criterion is defined here. In particular, permitted readout applicability does not by itself settle light-exposure or invasiveness compatibility.

6.4 Authoring Boundary ​

Core source-level authoring boundary:

  1. target << [sources...] for material transfer/merge/collection
  2. agit(...) for agitation/mixing behavior
  3. source.partition(program)[i] inside mutation source items for source-local portion selection
  4. sep(..., program=...) and frac(..., program=...) for separation/fractionation
  5. img(...), ecp(...), phy(...) for readout
  6. constructor sugar and with env / with constraint forms as defined by Chapters 4 and 5

6.5 Contract Failures and Diagnostics ​

Call-contract validation covers:

  1. unknown operation names, commonly reported as SEM_UNKNOWN_STEP
  2. missing required arguments, commonly reported as SEM_MISSING_REQUIRED_ARG
  3. unknown argument names, commonly reported as SEM_UNKNOWN_ARG
  4. duplicate argument names, commonly reported as SEM_DUPLICATE_ARG
  5. dimension/unit mismatches, reported with TYPE_* diagnostics
  6. state-dependent material feasibility, such as capacity bounds, reported with MAT_* diagnostics

The complete diagnostic list and earliest-stage placement are specified in Chapter 8.

6.6 Check Placement ​

  1. Membership and arg-shape checks: semantic validation.
  2. Unit/dimension checks: typecheck stage.
  3. State-dependent feasibility: runtime/material stage.

6.7 Conformance Boundary ​

Validators, typecheckers, planners, runtime material handlers, and examples must conform to these operation vocabulary and call contracts; they do not define separate operation behavior.

Culsma Language Reference · CC BY 4.0