operaton-contracts¶
Pydantic task contracts for Robot Framework packages served as Operaton (Camunda 7) external tasks, for example by purjo.
One pair of models per external task topic is the source of truth for what the task accepts and returns:
OperatonTasks.py task contracts: one Input/Output model per topic
│ generates │ validates at runtime
element template (modeler) Robot task: Validate Task Input → keywords
- At runtime every Robot task starts with
${input}= Validate Task Input <Contract>, which reads the task's process variables, validates them strictly, and returns them normalized. - At build time
operaton-contracts generaterenders one bpmn.io element template per topic from the same models, andoperaton-contracts checkverifies that the templates, the purjo topics inpyproject.toml, and the Robot suites agree.
Install¶
uv add "operaton-contracts[robot]"
uv add --group dev "operaton-contracts[templates]" # uv-only: jsonschema for validate
The core needs only pydantic; the robot extra adds robotframework for
the OperatonContracts Robot library and the suite checks. Without Robot
Framework, use validate_input and validate_output directly. The
templates extra adds jsonschema for operaton-contracts validate. When using devenv,
provide development tools and jsonschema through devenv.nix instead of
duplicating them in uv's dev group.
To teach coding agents these conventions, install the bundled
agent skill: uv run operaton-contracts install-skill.
A complete topic¶
from typing import Any
from pydantic import Field
from OperatonContracts import TaskContract
class GreetInput(TaskContract):
name: str = Field(alias="name", title="Name", min_length=1)
dry_run: bool = Field(alias="dryRun", title="Dry run", default=False)
class GreetOutput(TaskContract):
greeting: str = Field(alias="greeting", title="Greeting")
*** Settings ***
Library OperatonContracts OperatonTasks
*** Variables ***
${BPMN:TASK} local
${name} ${EMPTY}
${dryRun} ${False}
*** Tasks ***
Greet
${input}= Validate Task Input GreetInput
VAR ${greeting}= Hello ${input}[name] scope=${BPMN:TASK}
[tool.purjo.topics."example.greet"]
name = "Greet"
on-fail = "ERROR"
process-variables = false
[tool.operaton-contracts]
specs = "OperatonTasks:TEMPLATES"
Append this declaration to OperatonTasks.py after the contract models:
from OperatonContracts.templates import TaskTemplate
TEMPLATES = (
TaskTemplate(
topic="example.greet",
template_id="org.example.greet",
name="Example: Greet",
description="Greets someone.",
filename="example-greet.json",
inputs=GreetInput,
outputs=GreetOutput,
),
)
To keep build-time specs separate, put this declaration in a root-level
OperatonTemplates.py and set specs = "OperatonTemplates:TEMPLATES" instead.
List that module in .wrapignore so pur wrap leaves it out.
operaton-contracts generate # writes .operaton/element-templates/example-greet.json
operaton-contracts check # offline consistency check, e.g. in CI
License¶
Apache License 2.0.