Skip to content

Sequence work across two applications

One recording binds one surface. If the work starts in a browser intake form and finishes in a native posting app, record both.

for-each loops one bundle over a worklist. induce recovers a program from several traces of the same task on the same backend. The two parents that sequence work across surfaces are compose (compiled recordings) and process (independently admitted capabilities).

Record one program per surface

Record and compile each application the way you already do:

openadapt flow record --backend web --url https://intake.example --out rec-intake
openadapt flow compile rec-intake --out intake-bundle --name intake

openadapt flow record --backend windows --out rec-posting
openadapt flow compile rec-posting --out posting-bundle --name posting

Decide the handoff before you sequence anything. The parent will copy patient_id only if intake's effect contract already bound it. A window title isn't that fact. Compose won't retarget the intake recording onto the posting surface.

Sequence the recordings with compose

openadapt flow compose writes a parent directory of compiled children:

openadapt flow compose \
  --child intake=./intake-bundle \
  --child posting=./posting-bundle \
  --handoff intake.patient_id=posting.patient_id \
  --out composed
openadapt flow certify composed --policy clinical-write
openadapt flow run composed --config deploy.yaml

Default order is --child order. --after NAME=PRED declares a DAG; a cycle is refused at authoring. Child B starts only after child A ends VERIFIED, or a halt class you named with --allow-halt NAME=OUTCOME.

Handoffs copy parameter values that A's confirmed effect contract already bound. Missing evidence stops the run. The parent won't guess a URL.

The on-disk form is composed/composition.json (schema openadapt.composition/v1) plus composed/children/<name>/. Subflows and worklists stay inside each child. replay refuses this directory:

replay refuses a composition artifact; use `openadapt-flow run`

visualize on composed shows the child bundles, the handoff edges, and a terminal labeled End of declared steps. Open intake-bundle if you need intake's program graph.

openadapt flow visualize composed -o composed.html
openadapt flow visualize intake-bundle -o intake.html

Flags and the certify / run path are in the CLI reference.

Qualify and admit each child

Compose sequences recordings. A ProcessContract parent will refuse those copies. Qualify each child on its own surface, run its counted campaign, and keep the signed openadapt.qualification-admission/v1 envelope. The envelope's admission_id is a UUID. It is distinct from runtime_validation_id.

A recording you compiled five minutes ago and forgot to qualify is not a process child.

Sequence admitted capabilities with process

After both envelopes exist, author the process parent:

openadapt flow process \
  --child intake=./intake-bundle \
  --admission intake=./intake-admission.json \
  --child posting=./posting-bundle \
  --admission posting=./posting-admission.json \
  --handoff intake.patient_id=posting.patient_id \
  --out process-parent
openadapt flow visualize process-parent -o process.html

--handoff, --after, and --allow-halt have the same shape as compose. --child NAME=BUNDLE is the admitted bundle. --admission NAME=ENVELOPE is that child's signed openadapt.qualification-admission/v1 file. A compose child path under composition.json is not an envelope. The parent file is process-parent/process-contract.json, schema openadapt.process-contract/v0. It points at the envelopes. It doesn't copy recordings. It doesn't become a ProgramGraph.

Pointing process at a composition.json directory is refused. That check is what keeps unqualified recordings out of a process receipt.

Each child runs through OpenAdapt Execute with that child's envelope, qualification binding, and its own idempotency key. replay of the process parent is refused.

visualize on the process directory shows admitted children, handoff edges, and End of declared steps. That label means the declared sequence ended. It doesn't mean VERIFIED. Parent VERIFIED requires every child VERIFIED and zero model calls.

Open the child bundle when you need its steps. The parent view won't inline them.