6. Core Atomic Operators and Call Contracts
Operator and call contracts specify:
- operation name
- input parameter names and parameter types
- return type policy
6.1 Layer Boundary (Surface Sugar vs Domain Built-ins)
- Surface sugar is defined in Chapter 4 and exists for source ergonomics.
- Source authoring truth is the combination of Chapter 4 source forms and the statement/operator contract set in
6.3.7-6.3.19. - The contracts in
6.3.7-6.3.19govern 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
Container: abstract logical container handle type (STRINGor resolvable identifier); runtime maps it to container state (volume_uL,mass_mg,components,component_quantities,metadata)TubeContainer: concreteContainerkind for tube-like vesselsWellContainer: concreteContainerkind for plate-well vesselsContent: logical content identity handle (independent of container position)ContentKind: canonical mid-level material family labelText: free-form text (STRING)Bool: boolean valueScalar: unitless numeric scalarQ[Volume]: quantity with volume unitQ[Mass]: quantity with mass unitQ[Temperature]: quantity with temperature unitQ[Time]: quantity with time unitQ[Count]: discrete-count quantity; the initial supported domain unit iscellsQ[Volume|Mass|Count]: quantity constrained to a material amount axisList[Container]: list of logical containersList[Text]: list of text valuesQuantity: quantity value with supported unit family (general form)Record: key-value map for structured metadata fieldsContainerContents: target view selecting the material contents carried by a containerContainerStructureFacet: target view selecting one physical container-structure facetContainerTargetView:ContainerContents | ContainerStructureFacetHoldTarget: environment-hold target;Container,ContainerTargetView, or a group composed of those targetsContainerGroup: flat, ordered group of resolvableContainervalues
ContainerGroup static indexing contract:
group_ref[i]requires a compile-time decidable non-negative integeri.- The result is the existing container at ordered position
i; indexing does not allocate, copy, or reconstruct a container. - A statically known index outside the group cardinality is rejected before execution.
- Plate selector-derived groups use selector expansion order and therefore preserve the selected well's original carrier identity.
6.2.2 Return Type Aliases
Void: no required return objectContainer: resulting logical containersep_container_group: fixed two-slot separation result handlefraction_group: ordered multi-slot fractionation result handledata_ref: single readout result handledata_group_ref: grouped readout result handle
6.2.3 Container Content Contract
Container content state (runtime record) is modeled as:
volume_uL: current volumemass_mg: current masscomponents: backward-compatible numeric composition summarycomponent_quantities: component identity to{dimension, unit, value}quantity recordmetadata: container metadata (for example carrier mapping, density)
Container target-accessor surface:
container.contentsdenotes the material contents carried by the container.container.structure.topdenotes the top structural facet of the container.container.structure.bottomdenotes the bottom structural facet of the container.container.structure.sidewalldenotes the sidewall structural facet of the container.container.structureby itself is a target-view namespace, not a complete target view.container.structure.topis 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.- A bare
containerused as aHoldTargetdefaults to the content-facing target view, equivalent tocontainer.contentsfor env-hold semantics. - 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/agitsample inputs unless a future operator explicitly accepts target-view types. 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.- These accessors are direct target views, not
partition(...)-style methods and not materialized child containers. - No deeper structure path is defined.
container.structure.top.innerand device-like aliases such ascontainer.lidare not container target-view accessors.
Recommended metadata keys:
carrier_kind:tube/plate/ othercarrier_id: physical carrier IDcarrier_position: position label (for example plate wellA1)capacity_uL: container upper volume bound (optional, for capacity enforcement)
6.2.4 Container/Carrier and Content Taxonomy
Container taxonomy rules:
Containeris an abstract handle type, not a user-mutable object/class.- Concrete container kinds are
tube,well,chamber, andsurface. plateis modeled as carrier context; content-bearing runtime state is on well containers.- Built-ins must accept abstract
Containerhandles and remain independent of frontend sugar spellings.
Content taxonomy rules:
- Content identity/classification is represented as
Contentrecords and container-linked content state. - Recommended content fields:
content_kind,content_type,content_code,content_name, andattrs. content_kindis a closed mid-level material family and answers "what family of material is this?".content_typeis a closed canonical material category withincontent_kindand answers "what material category is this?".content_codeandcontent_nameidentify/display the concrete instance; they are not taxonomy fields.attrsis the open metadata record for state, source, preparation, concentration, composition detail, andattrs.role.attrs.roleis the role field. It is not promoted to a top-levelroleargument in this baseline.- Source-level family constructors (
blood/reagent/buffer) are compatibility/ergonomic sugar and do not define canonicalcontent_kindnames by themselves.
6.2.5 Canonical Content Kind Set
content_kind set used by strict-mode validation:
bio_entitybio_fluidbio_cellularbio_subcellularbio_molecule_or_viruschemicalparticulateformulation
Mode note:
- Strict-mode conformance uses this exact whitelist.
- Authoring examples and conformance claims must stay within this whitelist.
- Legacy inputs such as
biosample,reagent,buffer,control,waste, orfractionare 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 common controlled/recommended role values for that kind/type; it does not make role a required top-level field.
| content_kind | canonical content_type | recommended attrs.role values |
|---|---|---|
bio_entity | organism, organ, tissue, other_bio_entity | material |
bio_fluid | whole_blood, plasma, serum, buffy_coat, urine, saliva, lymph, cerebrospinal_fluid, tears, semen, ascites, synovial_fluid, bronchoalveolar_lavage_fluid, other_body_fluid | material |
bio_cellular | cell_line, primary_cells, cell_population, microbial_cells, other_cellular_material | material |
bio_subcellular | organelle, membrane, vesicle, cytoskeletal_structure, other_subcellular_structure | sample |
bio_molecule_or_virus | dna | sample, template, primer, probe, marker, standard, vector |
bio_molecule_or_virus | rna | sample, template, guide, probe, marker, standard |
bio_molecule_or_virus | protein | sample, antibody, enzyme, cytokine, marker, standard, antigen |
bio_molecule_or_virus | virus | sample, vector |
bio_molecule_or_virus | other_biomolecule_or_virus | sample |
chemical | organic_compound | substrate, inhibitor, activator, fixative, selection, precipitation, crowding_agent, matrix_component |
chemical | inorganic_compound | salt, pH_adjuster, ion_source, cofactor |
chemical | solvent | solvent, carrier, extraction, fixative |
chemical | detergent | lysis, permeabilization, washing, solubilization |
chemical | dye | stain, detection, tracking |
chemical | other_chemical | material |
particulate | beads | capture, cleanup, purification, enrichment |
particulate | resin | affinity_purification, chromatography, capture |
particulate | particle | labeling, calibration |
particulate | other_particulate | material |
formulation | medium | culture, maintenance |
formulation | buffer | wash, elution, binding, lysis, storage, reaction_environment |
formulation | supplement | supplement, selection, growth_factor |
formulation | master_mix | reaction_mix |
formulation | gradient_medium | density_gradient_separation |
formulation | other_formulation | material |
Normalization rules:
content_typevalues SHOULD use lowercase snake_case for stable matching.- Display labels (human-readable) MAY be stored separately in metadata.
content_typeis closed by the canonical table. Unknown or lab-local material categories should use the appropriate canonicalother_*type plusattrs,code, andname.- Non-standard
attrs.rolevalues MAY be recorded as ordinary attr metadata; unknown attr keys remain allowed becauseattrsis open metadata. - Specific identities such as mitochondrion, exosome, plasmid, amplicon, peptide, polymer class, bead material, unusual body-fluid identity, or virus form belong in
attrswhen they are more specific than the canonicalkind/typetable. - Preparation or state labels such as pellet, suspension, lysate, stock, diluted, purified, or amplified belong in
attrs.stateor a more specific attr key, not incontent_type. - 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 incontent_kind. - 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, canonicalcontent_typeincluding another_*fallback when needed, andattrs.
Examples:
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.3 Global Call Rules
- All required args
MUSTbe provided. - Provided args
MUSTbe in the allowed arg set. - Duplicate arg names
MUSTbe rejected. - Quantity args
MUSTsatisfy declared dimension constraints. Containerargs resolve to runtime container identity and operate on container content state.- For volume-increasing writes, if target container has
metadata.capacity_uL, post-step volumeMUSTsatisfyvolume_uL <= capacity_uL. - Invalid non-positive
capacity_uLis a contract failure. - Execution stages operate on canonical IR and plan/runtime structures, not raw source text; some source forms lower into kernel built-ins, while others remain first-class statement/operator IR nodes.
Capacity enforcement:
- Capacity rules above are enforced through explicit capacity normalization and overflow diagnostics.
6.3.1 Operator Layering (Type Strategy)
Domain built-ins are classified by type-dependency level, following the Quantity + Container strategy.
| Layer | Type dependency focus | Typical return shape | Operations |
|---|---|---|---|
| L1 material/env/readout primitives | IR / plan executable units | material delta, env hold, indexed group, or data_ref/data_group_ref | Mutation (from mutation_stmt (<<)), sep, frac, img, ecp, phy, env_hold, stream, constraint |
| L2 Internal constructor/init ops | container/content initialization and metadata | container/content identity updates | AllocContainer, DefineContent, LoadContent, AnnotateContent |
Layering intent:
- L1 defines executable truth used by runtime.
- Constructor/init ops are internal canonical targets for frontend sugar and init boundaries, not preferred protocol-body authoring forms.
6.3.2 Semantic Domain Layering
Operations are classified by execution semantic domain.
Domain definitions:
material: operation primarily updates container material state (volume_uL,mass_mg,components,component_quantities, metadata).bridge: operation consumes container state and also produces structured analytical/process payload.data: operation consumes only data payloads/records and does not read/write container material state.
Built-in mapping:
| Operation | Semantic Domain | Primary state effect |
|---|---|---|
Mutation (mutation_stmt (<<) plan/runtime op) | material | move/merge material across containers |
sep | material | split material into a fixed two-slot indexed group |
frac | material | split material into an ordered indexed group |
stream | material | convert a macro sample into a unit stream |
img / ecp / phy | bridge | consume container state and emit data_ref / data_group_ref |
constraint | semantic policy | attach device-independent execution requirements to a scope or single step |
Domain boundary note:
bridgeoperators are container-addressed; they are not pure data operators.- Pure
dataoperators are reserved for future vocabulary extension and are not part of built-ins.
6.3.3 Surface Sugar Lowering Boundary
- Sugar forms from Chapter 4 are compile-time/frontend forms only.
- Validation/type/runtime stages operate on canonical IR and plan nodes after frontend compilation.
- Conformance checks for the source-level contracts in
6.3.7-6.3.19define the authoring surface. - Constructor sugar (
tube(...),well(...), and inline content specs such asbuffer(...),reagent(...),blood(...), orcontent(...)insideload=[...]) lowers toAllocContainer/DefineContent/LoadContent. mutation_stmt (<<)compiles to first-class mutation IR/plan nodes; it is not authored throughTransfer(...)/Mix(...)in the frontend.- Historical operation calls such as
Transfer(...),Mix(...), andDilute(...)are not part of the source contract and are rejected by the frontend.
6.3.4 Constructor Built-ins (Lowering Targets)
These built-ins are the canonical targets for container/content sugar lowering:
AllocContainer(kind: Text, [spec: Text], [carrier_kind: Text], [carrier_id: Text], [carrier_position: Text], [capacity: Q[Volume]], [open: Bool], [label: Text], [barcode: Text], [load: List[content_spec:Q]]) -> ContainerDefineContent(kind: ContentKind, [type: Text], [code: Text], [name: Text], [attrs: Record]) -> ContentLoadContent(container: Container, content: Content, amount: Q[Volume|Mass|Count]) -> ContainerAnnotateContent(content: Content, [type: Text], [code: Text], [attrs: Record]) -> Content
Rules:
AllocContainercreates a new logical container identity and initializes runtime container state.DefineContentcreates logical content identity/classification independent of container location.LoadContentmaterializes content into container state using explicit amount axis.AnnotateContentupdates content classification metadata without implicit quantity mutation.- All constructor built-ins above are domain-layer APIs; they are not user-defined functions or object methods.
openandloadbelong to the constructor/init lowering contract, even though they are authored through frontend sugar rather than direct user calls toAllocContainer(...).cellsis valid only for content normalized tobio_cellular; direct authored load counts MUST be non-negative integers.- Applying a count load does not itself add volume. After all constructor load items, container finalization may materialize an implicit carrier for a free count-only cellular population; that inferred volume participates in
capacity_uLchecks and records its assumption policy.
6.3.5 Mutation Statement Sugar Contract (<<)
Container mutation sugar contract:
Surface forms:
target << [s1, s2, ...]target << [s1:q1, s2:q2, ...]group_target << [series(src_ref, [q1, q2, ...])]target << [source.partition(program_expr)[i]: q]target << [container.contents[i]]target << [container.contents[i]: q]
Contract rules:
[]denotes the mutation source list. It is a statement-specific source-list form, not an ordinary list literal.- Full-transfer and quantified source items must not be mixed in one statement.
- Quantified mode requires unit-bearing quantities.
series(...)is the sole ordered-mapping surface form.series(...)is valid only when the target is acontainer_group_ref.series(src_ref, [v1, ...])requires cardinality equality between target-group size and value-list length.- Ordinary
[...]source-list semantics remain unchanged and do not imply ordered mapping. - 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. 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.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 barecontainer.contentsa material container alias.- If
container.contents[i]resolves to the same container astarget, the material transfer is a no-op but the current indexed contents state is disturbed and must not be reused for latercontainer.contents[j]reads without a new explicit organization-producing operation. - 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.
- 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. - 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.
- 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:
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.
Compilation/runtime mapping:
- Source form compiles to first-class
MutationStmt -> IRMutation -> PlanStep(op="Mutation"). - Full-transfer form uses merge/full-move material semantics at runtime.
- Quantified form uses ordered quantity transfer semantics at runtime. Volume/mass transfers reuse density bridge rules and proportionally carry component quantities. A count transfer from a homogeneous cell suspension resolves the requested count to carrier volume from the recorded concentration, then uses the ordinary volume-transfer path.
- Same-container ordinary source items are preserved through lowering and become no-op mutation entries at material execution.
- Same-container
container.contents[i]source items preserve material totals but invalidate the current indexed contents state. series(...)is compiler-lowered into ordered per-target quantified mutations; it does not introduce a new runtime primitive.
Note:
- Statement-first mutation is the preferred frontend source style.
6.3.6 Core Intrinsic Decomposition
- Scope is limited to surface sugar and domain built-in contracts.
- Deeper kernel intrinsic decomposition is intentionally deferred.
- Future intrinsic API standardization must not break the domain built-in contracts defined here.
6.3.7 Atomic Statement Contract Matrix
Alignment rule:
- Source-level shapes in this subsection MUST follow Chapter 4 when older draft wording conflicts.
- Operator call contracts are listed in one place.
| Atomic unit | Source shape | Required core args | Run spec / options | Return contract |
|---|---|---|---|---|
MutationStmt (<<) | target << [sources...]; | target, sources | no program; quantified and full-transfer source styles must not mix; quantified item must be unit-bearing (src:20uL) | mutates target/source container material state |
agit | agit(sample=..., mode=..., [duration=...], [rate=...], [cycles=...]); | `sample: Container | ContainerGroup, mode` | mode is vortex, invert, shake, or stir; invert uses cycles and forbids duration/rate; other modes forbid cycles and use duration/rate where required |
WithEnvStmt | with env(...) { [hold(target_expr);] ... } | env, body, optional hold target declarations | scalar 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-only | scoped environment context over statement block |
WithConstraintStmt | with constraint(...) { ... } or stmt with constraint(...); | constraint set, scoped stmt/block | device-independent execution requirements; detailed conflict rules live in semantic validator | applies execution requirement scope to enclosed statement(s) |
stream | let s = stream(sample = x, unit = single_cell, [panel = p]); | sample, unit | panel optional; result is unit-stream only, not material split group | unit_stream_ref |
sep | let g = sep(sample=..., program=centrifuge_program(...)); | sample, program | optional component_fates and transitions; program is mechanism group; keep_source only inside centrifuge_program(...) | sep_container_group fixed group[0] / group[1] |
frac | let fg = frac(sample=..., program=density_gradient_program(...)); | sample, program | program carries axis/order/bins | fraction_group ordered containers (fg[0..n-1]) |
img | let obs = img(sample=..., quantity=..., [schema_ref=...], [save_raw=...]); | sample, quantity | standard simple quantities use fixed result fields; complex results use schema_ref | single data_ref; grouped input returns data_group_ref |
ecp | let obs = ecp(sample=..., quantity=..., [schema_ref=...], [save_raw=...]); | sample, quantity | standard simple quantities use fixed result fields; complex results use schema_ref | single data_ref; grouped input returns data_group_ref |
phy | let obs = phy(sample=..., quantity=..., [schema_ref=...], [save_raw=...]); | sample, quantity | standard simple quantities use fixed result fields; complex/raw results use schema_ref | single data_ref; grouped input returns data_group_ref |
6.3.8 Program / Quantity / Schema Contracts
| Contract kind | Typical statement/operator | Role |
|---|---|---|
thermal_program | env(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_program | sep(..., program=...) | separation mechanism parameter groups |
density_gradient_program / chromatography_program | frac(..., program=...) | fractionation mechanism parameter groups |
quantity = ... | img/ecp/phy | standard measurement/readout target selector |
data_schema(...) / schema_ref | img/ecp/phy/data_ref | open 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 no runtime step is emitted until a compatible owner 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:
- atomic statements and statement-scoped forms from Chapter 4
- constructor/init lowering targets required to explain those forms
- readout/result contracts needed by
img/ecp/phyauthoring
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:
| Topic | Contract |
|---|---|
| Container/content mutation | Source-level mutation is unified as statement-first <<; no separate mix/dilute/transfer source keywords. |
| Mutation source style | One << source list must be style-consistent: all full-transfer (src) or all quantified (src:Q). |
| Quantified source item | Must carry physical units (for example 20uL); unitless ratio form (for example src:1) is forbidden. |
| Source-local partition selector | source.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 selector | container.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 control | Environment 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 layer | Execution requirements are represented by constraint(...), not by device/program fields; block form is formal semantics and trailing form is sugar. |
| Macro-to-micro transition | stream(sample=..., unit=...) is the source-level entry for converting macro samples into unit streams. |
| Binary separation | sep is fixed two-output split with ordered return group[0] / group[1], with mechanism program and mode-specific slot semantics. |
| Multi-fraction separation | frac is ordered multi-output split and is not collapsed into sep. |
| Measurement/readout | img/ecp/phy use quantity = ...; outputs are data_ref / data_group_ref. |
6.3.11 Source-Local Partition Selector Contract
Canonical source-level shapes:
target << [source.partition(program_expr)[0]: amount];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 program | Required fields | Optional fields | [0] | [1] |
|---|---|---|---|---|
centrifuge_program | drive: CentrifugalSetting | keep_source | supernatant | pellet |
magnetic_program | - | - | bound / retained | flowthrough / removable |
disrupt_program | - | method, duration | lysate | debris_or_residue |
field_program | exactly one of voltage: Q[ElectricPotential], current: Q[ElectricCurrent], or field: Q[ElectricPotential] | duration | target_band_fraction | non_target_fraction |
filtration_program | membrane, drive | - | filtrate | retentate |
centrifugal_filtration_program | membrane, drive: CentrifugalSetting | duration | filtrate | retentate |
phase_partition_program | solvent | - | target_phase | other_phase |
precipitation_program | reagent | - | precipitate | supernatant |
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:
- The index must be a compile-time decidable non-negative integer.
- Current partition selectors are binary; only
[0]and[1]are valid. density_gradient_program(...)andchromatography_program(...)are not accepted bypartition(...); they remainfracprograms.thermal_program(...)and readout-oriented descriptors are not partition programs.partition(...)does not accept free-form named fields such aspartition(mode = ..., drive = ...); program fields stay inside the concrete*_program(...)descriptor.- A full-transfer selector item moves the whole selected portion. A quantified selector item moves the requested amount from the selected portion.
- 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.
partition(...)does not replacesep(...). Usepartition(...)when the source container remains the operative container and the partition is only consumed by the current transfer. Usesep(...)when the protocol needs materialized, independently addressable output containers or groups.
Magnetic-bead liquid removal example:
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:
let sep_group = sep(sample=..., program=centrifuge_program(...));sep(sample=..., program=centrifuge_program(...));let sep_group = sep(sample=..., program=filtration_program(...), component_fates={RPE1:{filtrate:0%, retentate:100%}});let sep_group = sep(sample=..., program=magnetic_program(...), transitions=[transition(subject=sample.materials[1], output=MagneticProgramOutput.FLOWTHROUGH, to=MaterialRelation.FREE)]);let sep_group = sep(sample=..., program=magnetic_program(...), transitions=[transition(subject=sample.materials.get("TARGET_PROTEIN"), output=MagneticProgramOutput.FLOWTHROUGH, to=MaterialRelation.FREE)]);
Effect contract:
- Let-bound
sep(...)materializes a two-slot container group and preserves existingsep_group[0]/sep_group[1]return behavior. - Standalone
sep(...)acts on the source container and records an indexed contents organization for latercontainer.contents[0]/container.contents[1]mutation-source reads. - Standalone
sep(...)does not create a bindable group unless authored in aletbinding.
Per-content fate contract:
component_fatesis optional and maps content identity to a two-output ratio record.- Output keys may use the selected program's semantic output names or the string slot keys
"0"and"1". - 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%).
- An authored fate has precedence over association-derived retention and the reference partition strategy. It remains subject to quantity-axis and conservation validation.
- The referenced content must exist in the source container at runtime.
Per-material relationship-transition contract:
transitionsis optional and containstransition(...)values scoped to a concrete positive-quantity separation output.subjectis eithersample.materials[index]orsample.materials.get(entry_id), wherematerialsis the normalized, ordered list of live material entries immediately before the operation. The index is a non-negative static integer. Theentry_idkey is a statically resolvable non-empty string and is matched exactly within the same container.get(...)never matchescontent_ref, content codes, labels, or prefixes and never falls back to another same-content entry.outputis one namespaced member of the selected separation program's concrete output enum. The enum type must match the concrete program; for example,filtration_program(...)acceptsFiltrationProgramOutput.FILTRATEorFiltrationProgramOutput.RETENTATEand rejects members ofMagneticProgramOutput.tois a namespacedMaterialRelation.MEMBERexpression. The author-settable domain isfree,container_surface,pellet,precipitate,disrupted,bead_bound,membrane_bound,cell_bound, andfield_retained.UNRESOLVEDis an internal sentinel and MUST NOT be authored. String values, bare identifiers such asfree, and members outside this enum are invalid.- The directed transition graph has no source/target-pair whitelist. Runtime derives the current
MaterialRelationfrom the selected entry and accepts any target in the author-settable enum domain, subject to target-state structural invariants. freehas no association target and clears the source association. The container-associated target relations use the concrete output container as their typed association target.bead_bound,membrane_bound, andcell_boundrequireassociated_withto use either same-sample material selector form. The selected association target must have positive quantity in the same output and is represented as anAssociationTarget(kind=component_entry, id=...).associated_withis forbidden for target relations that are not component-bound.- An authored relationship transition overrides only Table 3 state resolution for the selected source entry and output. It does not replace Table 2
component_fates, create material, infer chemistry, or add a separation program. - 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.
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 member | Relationship target category | Explicit same-sample material selector | Runtime association target |
|---|---|---|---|
MaterialRelation.FREE | no association | not applicable | association is cleared |
MaterialRelation.CONTAINER_SURFACE | output-container association | not applicable | concrete output container |
MaterialRelation.PELLET | output-container association | not applicable | concrete output container |
MaterialRelation.PRECIPITATE | output-container association | not applicable | concrete output container |
MaterialRelation.DISRUPTED | output-container-scoped state | not applicable | concrete output container |
MaterialRelation.FIELD_RETAINED | output-container-scoped retention | not applicable | concrete output container |
MaterialRelation.BEAD_BOUND | component-entry association | required | selected bead material entry projected into the same output |
MaterialRelation.MEMBRANE_BOUND | component-entry association | required | selected membrane material entry projected into the same output |
MaterialRelation.CELL_BOUND | component-entry association | required | selected cell material entry projected into the same output |
MaterialRelation.UNRESOLVED | internal sentinel | invalid author target | none; 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 part_id:
| Program | Output enum | part 0 | part 1 |
|---|---|---|---|
sep_program compatibility surface | SepProgramOutput | FRACTION_A | FRACTION_B |
centrifuge_program | CentrifugeProgramOutput | SUPERNATANT | PELLET |
magnetic_program | MagneticProgramOutput | BOUND | FLOWTHROUGH |
disrupt_program | DisruptProgramOutput | LYSATE | DEBRIS_OR_RESIDUE |
field_program | FieldProgramOutput | TARGET_BAND_FRACTION | NON_TARGET_FRACTION |
filtration_program | FiltrationProgramOutput | FILTRATE | RETENTATE |
centrifugal_filtration_program | CentrifugalFiltrationProgramOutput | FILTRATE | RETENTATE |
phase_partition_program | PhasePartitionProgramOutput | TARGET_PHASE | OTHER_PHASE |
precipitation_program | PrecipitationProgramOutput | PRECIPITATE | SUPERNATANT |
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 are a compatibility projection of the enum, not a second output vocabulary.
Return contract:
sep_group[0]/sep_group[1]are the stable user-facing access forms for let-boundsep(...)output groups.- Return arity is fixed at 2 for all included modes.
container.contents[0]/container.contents[1]are valid only when a current standalone contents state exists for that container.
keep_source contract:
- Allowed only inside
centrifuge_program(...). - Values:
supernatant | pellet. - Omitted
keep_sourcemeans both outputs are newly materialized container identities.
Mode contract:
| program | Required fields | Optional fields | group[0] | group[1] |
|---|---|---|---|---|
centrifuge_program | drive: CentrifugalSetting | keep_source | supernatant | pellet |
magnetic_program | - | - | bound | flowthrough |
disrupt_program | - | method, duration | lysate | debris_or_residue |
field_program | exactly one of voltage: Q[ElectricPotential], current: Q[ElectricCurrent], or field: Q[ElectricPotential] | duration | target_band_fraction | non_target_fraction |
filtration_program | membrane, drive | - | filtrate | retentate |
centrifugal_filtration_program | membrane, drive: CentrifugalSetting | duration | filtrate | retentate |
phase_partition_program | solvent | - | target_phase | other_phase |
precipitation_program | reagent | - | precipitate | supernatant |
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" is represented as a sep(...) statement inside with env(thermal=4C, duration=5min).
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.
disrupt_program.method is a device-independent controlled value: mechanical | sonication | shear_homogenization | high_pressure_disruption | bead_impact. 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:
let frac_group = frac(sample=..., program=density_gradient_program(...));frac(sample=..., program=density_gradient_program(...));
Core argument contract:
- Required source-level args:
sample,program. programisdensity_gradient_program(...)orchromatography_program(...).binsis integer-like and must satisfy semantic lower bound for ordered fractionation.
Return contract:
- Return value is
fraction_groupwith stable ordered indexing. frac_group[i]resolves to container output at indexi; only compile-time non-negative integer indexing is part of the contract.- Order semantics are controlled by
order(for exampletop_to_bottom). - Standalone
frac(...)records an ordered indexed contents state for latercontainer.contents[i]mutation-source reads; it does not create a bindable group unless authored in aletbinding.
6.3.14 with env Contract
Canonical shape:
with env(thermal=..., [co2=...], [rh=...], [duration=...]) { ... }with env(thermal=thermal_program(...)) { ... }with env(field=...) { ... }
Explicit target declaration shape:
hold(container_ref);hold(container_ref.contents);hold(container_ref.structure.top);hold(container_ref.structure.bottom);hold(container_ref.structure.sidewall);hold(group([target_ref_1, target_ref_2, ...]));
Parameter contract:
- At least one supported environment dimension is required; dimensions are
thermalandfield. thermalaccepts union type:Q[Temperature] | thermal_program.fieldis used for non-thermal field domains such asmz_separation.co2/rhare optional thermal modifiers; they are not primary dimensions.
Behavior constraints:
thermal = Q[Temperature]: constant-temperature environment scope.durationis 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.thermal = Q[Temperature]may omitdurationfor 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.thermal = thermal_program: program-driven thermal scope;durationis forbidden because total time is derived by program.field = ...: field-domain environment scope; outerdurationis forbidden.co2/rhrequirethermal; they are forbidden whenthermalis program-typed.durationandthermal_program.cyclesdescribe the environment program itself and MUST NOT imply block-body repetition.- Block-body statements execute once in lexical order; per-cycle/stage hooks are not part of the contract.
- When a single enclosed action has no action-local duration field and the source supplies an execution time boundary, the enclosing scalar thermal
durationsupplies the time boundary for executing that action under the stated environment; for example centrifugation time may be expressed by wrappingsep(..., program=centrifuge_program(drive=...))inwith env(thermal=..., duration=...). - Action-local duration remains distinct from environment duration. Operations such as
agit(...)do not inheritenv.duration; if the action itself requires a duration, the action must declare it.
Target declaration constraints:
hold(...)is only valid directly inside awith env(...)block.- Canonical
hold(...)accepts exactly one positionalHoldTargetexpression and no named arguments. hold(...)records environment-hold/reporting targets for the nearest enclosingwith env(...); it does not define the environment scope and does not introduce temperature, time, program, device, or execution-requirement parameters.- Direct-child
hold(...)declarations are not standalone executable actions. If awith env(...)block contains onlyhold(...)declarations, the block lowers to a pure env hold over those targets. - If a
with env(...)block contains direct-childhold(...)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. - Direct-child
hold(...)declarations may appear anywhere in the immediatewith env(...)body and are collected as environment targets rather than executable steps. - If a
with env(...)block contains no explicithold(...)declarations, env targets are inferred from enclosed executable statements as in the body-inference model. - Bare container targets in
hold(container_ref)are content-facing by default. Authors should use a structure facet such ascontainer_ref.structure.topwhen the controlled object is the physical container structure rather than the contained material. - Legacy
hold(sample = container_ref)may be accepted by implementations as compatibility input, but it is not the canonical source form for new examples. hold(...)inside nestedif,repeat,with constraint, or other non-env sub-blocks is not a valid target declaration for the enclosing env block.
Target semantics:
- Chapter 6 defines the
HoldTargettype andwith envtarget-declaration contract; Chapter 4 defines the corresponding source spelling. with env(...)owns environmental conditions,thermal_program(...)owns thermal trajectory fields, andhold(...)owns only target declaration.- A container structure-facet target is not a sample/material state and must not be interpreted as sample thermal state.
- A bare container remains ergonomic for ordinary thermal holds by defaulting to its contents in
HoldTargetposition. container.contentsandcontainer.structure.<facet>are direct target views, not partitioning methods and not new container allocation.container.structure.topis closure-agnostic and must not be lowered to a device-specific lid/cap concept.- 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=..., [duration=...], [rate=...], [cycles=...]);. sampleaccepts oneContaineror a flat, orderedContainerGroup.- An explicit
group([...])and a let-bound group have the same agitation semantics. - A grouped sample is a batch target: the same agitation action applies independently to each member. Grouping does not merge members or transfer material between them.
modeis one ofvortex,invert,shake, orstir.invertusescyclesand forbidsdurationandrate; other modes forbidcyclesand usedurationandratewhere required by the action.agitdoes not return a material or data value.- Chapter 7 defines the per-container material-state effect.
6.3.16 Readout Family Contract (img/ecp/phy)
img:
- Shape:
let obs = img(sample=..., quantity=..., [schema_ref=...], [save_raw=...]); - Core args:
sample,quantity. - Quantity set:
uv_absorbance | fluorescence | colorimetric | customized. - Return: single-sample input yields
data_ref; grouped sample input yieldsdata_group_ref.
ecp:
- Shape:
let obs = ecp(sample=..., quantity=..., [schema_ref=...], [save_raw=...]); - Quantity set:
ph | conductivity | dissolved_oxygen | orp | customized. - Return: single-sample input yields
data_ref; grouped sample input yieldsdata_group_ref.
phy:
- Shape:
let obs = phy(sample=..., quantity=..., [schema_ref=...], [save_raw=...]); - Quantity set:
temperature | pressure | flow_rate | mass | volume | humidity | current | customized. - Return: single-sample input yields
data_ref; grouped sample input yieldsdata_group_ref.
Cross-family rule:
- Readout operators are data-producing operators and are not container-mutation operators.
data_refuses a shared envelope plus family- and quantity-specificresultpayload.resultfield sets are determined byfamily + quantity, not by free-form runtime text.raw_artifact(when present) is auxiliary evidence and does not replace structuredresult.- Optional
export_refsmay attach richer external/profile-specific result exports; they do not replaceresultas the source-level control surface. - Grouped sample input yields
data_group_ref, whose order matches the input container group.
6.3.17 Core Contract Boundary
- Sections
6.3.7to6.3.19define source-level contracts for the core atomic operator surface. - The core source path covers constructor/init,
<<,agit,with env,sep,frac, source-local partition selectors,img,ecp,phy, and indexed group references. - Higher-level workflow libraries are outside the core contract defined in this chapter.
- Compatibility behavior must not change the source-level contracts defined in this chapter.
6.3.18 Separation vs Fractionation Boundary
Principle distinction:
sepis binary semantic split (targetvsnon-target) and returns fixed two-slot outputs.fracis ordered distribution split across multiple bins and returns ordered multi-slot outputs.- If domain intent is to preserve distribution/layer profile, use
fracinstead of collapsing tosep.
Classification note:
sepandfracare parallel atomic operators under the same broad separation family.- They are not aliases of each other.
6.3.19 Readout Category Map
| Category | Typical scenarios | Preferred atomic path | Typical output form |
|---|---|---|---|
| Optical quantification | UV/optical fluor/colorimetric quant | img(sample=..., quantity=...) | scalar/data readout reference |
| Electric-field-separated readout | gel/capillary style readout | sep(sample=..., program=field_program(...)) + img(sample=..., quantity=customized, schema_ref=...) | band/peak data reference |
| Microscopy imaging | fixed/live-cell imaging | img(sample=..., quantity=customized, schema_ref=...) | image data reference |
| Physiological state readout | viability / pH / conductivity | img(...) (indicator route) or ecp(sample=..., quantity=...) | data reference |
| Separation-coupled readout | post-separation flow/seq/ms readout | decomposition path (stream/repeat/env/img/phy/data_ref) | data reference |
6.4 Authoring Boundary
Core source-level authoring boundary:
target << [sources...]for material transfer/merge/collectionagit(...)for agitation/mixing behaviorsource.partition(program)[i]inside mutation source items for source-local portion selectionsep(..., program=...)andfrac(..., program=...)for separation/fractionationimg(...),ecp(...),phy(...)for readout- constructor sugar and
with env/with constraintforms as defined by Chapters 4 and 5
6.5 Contract Failures and Diagnostics
Call-contract validation covers:
- unknown operation names, commonly reported as
SEM_UNKNOWN_STEP - missing required arguments, commonly reported as
SEM_MISSING_REQUIRED_ARG - unknown argument names, commonly reported as
SEM_UNKNOWN_ARG - duplicate argument names, commonly reported as
SEM_DUPLICATE_ARG - dimension/unit mismatches, reported with
TYPE_*diagnostics - 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
- Membership and arg-shape checks: semantic validation.
- Unit/dimension checks: typecheck stage.
- 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.
