Skip to content

Add validation warning for common mistakes when defining reaction inputs #869

Description

@bdeadman

Summary

Add a validator in ord-schema that flags two common mistakes authors make when defining reaction inputs and returns warnings (not hard errors). Once added to ord-schema, the same validation should be surfaced in ord-app so users see it in the editor.

Mistake 1

  • Symptom: Reaction contains multiple inputs, but each input has only a single component.
  • Why it’s a problem: This is almost always a misunderstanding of how the schema is meant to represent inputs (multiple inputs usually implies separate additions/units). It is correct for simple reactions where every component is added sequentially as neat solids/liquids, but usually indicates an incomplete/incorrect record.
  • Suggested validator behavior: If reaction.inputs.length > 1 and every input.components.length == 1, emit a warning suggesting the user may have intended to list multiple components within a single input or to record separate addition steps.

Mistake 2

  • Symptom: The reaction contains a single input with multiple components of type REACTANT, REAGENT, or CATALYST.
  • Why it’s a problem: This pattern is widely used for reactions with indeterminate or very complex input addition systems; it’s acceptable but should be discouraged when a complete record of inputs is possible.
  • Suggested validator behavior: If reaction.inputs.length == 1 and that input.components contains multiple components where component.type is one of {REACTANT, REAGENT, CATALYST}, emit a warning recommending the user provide a more complete record (separate inputs for distinct additions) where feasible.

Implementation notes / acceptance criteria

  • Add a schema-level validator in ord-schema that returns warnings (not fatal errors) with clear messages and examples of preferred structures.
  • Include unit tests covering positive and negative examples for both warnings.
  • Expose the new validator message codes so ord-app can surface them in the editor UI.
  • Update ord-app to display these warnings inline in the editor with guidance and a link to documentation on how to represent input additions correctly.
  • Add short example snippets in the validator tests or docs demonstrating correct vs. flagged structures.

Suggested validator messages (examples)

  • Code: input-composition:multiple-inputs-single-component
    Text: "Reaction has multiple inputs where each input contains a single component. This often indicates a misunderstanding of input addition semantics — consider grouping components in the same input or recording explicit separate addition steps. (Warning)"

  • Code: input-composition:single-input-multiple-active-components
    Text: "Single input contains multiple REACTANT/REAGENT/CATALYST components. Consider recording separate inputs for distinct additions to improve completeness and clarity. (Warning)"

Quick examples (illustrative)

Flagged: multiple inputs each with one component

JSON
{
  "inputs": [
    { "components": [{ "name": "A", "type": "REACTANT" }] },
    { "components": [{ "name": "B", "type": "REACTANT" }] }
  ]
}

Flagged: single input with multiple REACTANT/REAGENT/CATALYST components

JSON
{
  "inputs": [
    {
      "components": [
        { "name": "A", "type": "REACTANT" },
        { "name": "B", "type": "REAGENT" },
        { "name": "Cat", "type": "CATALYST" }
      ]
    }
  ]
}

Preferred (example) — multiple components in a single well-described input or separate inputs that reflect distinct additions:

JSON
{
  "inputs": [
    {
      "components": [
        { "name": "A", "type": "REACTANT" },
        { "name": "B", "type": "REACTANT" }
      ],
      "addition_step": "add A and B together at start"
    },
    {
      "components": [
        { "name": "C", "type": "SOLVENT" }
      ],
      "addition_step": "add C later"
    }
  ]
}

Suggested validator implementation (example)

The example below follows the repository’s existing pattern of issuing warnings via warnings.warn(..., ValidationWarning). Adapt imports, message codes, enum names, and registration to match ord-schema.

File suggestion: ord_schema/validators/input_composition_validator.py

Python
import warnings
from ord_schema import reaction_pb2
from ord_schema.validations import ValidationWarning  # adapt import

ACTIVE_COMPONENT_TYPES = {"REACTANT", "REAGENT", "CATALYST"}

def _is_active_component(component) -> bool:
    try:
        return component.reaction_role in (
            reaction_pb2.ReactionRole.ReactionRoleType.REACTANT,
            reaction_pb2.ReactionRole.ReactionRoleType.REAGENT,
            reaction_pb2.ReactionRole.ReactionRoleType.CATALYST,
        )
    except Exception:
        t = getattr(component, "type", None)
        return isinstance(t, str) and t.upper() in ACTIVE_COMPONENT_TYPES

def _collect_components(inp):
    return list(inp.components) + list(getattr(inp, "crude_components", []))

def add_input_composition_warnings(message: reaction_pb2.Reaction) -> None:
    # Mistake 1: multiple inputs where each has only one component
    inputs_len = len(message.inputs)
    if inputs_len > 1:
        all_single = True
        for key in message.inputs:
            inp = message.inputs[key]
            comp_count = len(_collect_components(inp))
            if comp_count != 1:
                all_single = False
                break
        if all_single:
            warnings.warn(
                "Reaction has multiple inputs where each input contains a single component. "
                "This often indicates a misunderstanding of input addition semantics — consider "
                "grouping components in the same input or recording explicit separate addition steps.",
                ValidationWarning,
            )

    # Mistake 2: single input with multiple active components
    if inputs_len == 1:
        single_key = next(iter(message.inputs))
        inp = message.inputs[single_key]
        active_count = sum(1 for c in _collect_components(inp) if _is_active_component(c))
        if active_count > 1:
            warnings.warn(
                "Single input contains multiple REACTANT/REAGENT/CATALYST components. "
                "Consider recording separate inputs for distinct additions to improve completeness and clarity.",
                ValidationWarning,
            )

Suggested unit tests (example)
Adapt imports and helpers to the repository test harness (e.g., use _run_validation).

File suggestion: ord_schema/validations_test.py (or tests/test_input_composition_validator.py)

Python
def test_multiple_inputs_single_component_flagged():
    message = reaction_pb2.Reaction()
    inp1 = message.inputs["i1"]
    c1 = inp1.components.add()
    c1.identifiers.add(type="CUSTOM").value = "A"
    inp2 = message.inputs["i2"]
    c2 = inp2.components.add()
    c2.identifiers.add(type="CUSTOM").value = "B"
    output = _run_validation(message)
    assert any("multiple inputs" in w for w in output.warnings)

def test_single_input_multiple_active_components_flagged():
    message = reaction_pb2.Reaction()
    inp = message.inputs["single"]
    c1 = inp.components.add()
    c1.identifiers.add(type="CUSTOM").value = "A"
    # c1.reaction_role = reaction_pb2.ReactionRole.ReactionRoleType.REACTANT
    c2 = inp.components.add()
    c2.identifiers.add(type="CUSTOM").value = "B"
    # c2.reaction_role = reaction_pb2.ReactionRole.ReactionRoleType.REAGENT
    output = _run_validation(message)
    assert any("REACTANT/REAGENT/CATALYST" in w or "Single input contains multiple" in w for w in output.warnings)

def test_non_flagged_structures():
    message = reaction_pb2.Reaction()
    inp = message.inputs["i"]
    c1 = inp.components.add()
    c1.identifiers.add(type="CUSTOM").value = "A"
    c1.amount.mass.value = 1
    inp2 = message.inputs["i2"]
    c2 = inp2.components.add()
    c3 = inp2.components.add()
    output = _run_validation(message)
    assert not any("multiple inputs" in w or "Single input contains multiple" in w for w in output.warnings)

Notes for ord-app integration

  • Expose/register the two validator message codes so ord-app can map them to inline warnings.
  • In ord-app, display a non-blocking inline warning message with:
    • a concise explanation,
    • a link to docs with examples,
    • an ability to dismiss/acknowledge the warning when the user intentionally chooses this structure.
  • If ord-schema includes structured validation paths, ord-app can highlight the affected inputs/components in the editor UI.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions