Skip to content

feat(compose): sequence separately recorded Flow bundles - #430

Merged
abrichr merged 2 commits into
mainfrom
feat/compose-recorded-bundles
Aug 29, 2026
Merged

feat(compose): sequence separately recorded Flow bundles#430
abrichr merged 2 commits into
mainfrom
feat/compose-recorded-bundles

Conversation

@abrichr

@abrichr abrichr commented Aug 29, 2026

Copy link
Copy Markdown
Member

Summary

openadapt-flow compose sequences already-compiled child bundles under a handoff contract. Each child stays bound to the surface it was recorded on. certify and run execute the parent directory; replay refuses it.

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

The launcher form is openadapt flow compose.

Child B starts only after child A ends VERIFIED, or a halt class named with --allow-halt. Handoffs copy parameter values that A's confirmed effect contract already bound. Window titles and URLs are not evidence. Missing evidence HALTs.

Default order is --child order. --after NAME=PRED declares a DAG; cycles refuse at authoring. The parent artifact is composition.json plus copied children, not a larger ProgramGraph.

Child execution goes through execute(capability, admission, inputs, ...) in openadapt_flow/runtime/composition.py. Today that binds to governed run (admission gate plus Replayer). When a public execute() primitive lands, rebind there. Raw replay is not the public path.

Rebased onto main after #432. openadapt_flow/compiler/__init__.py and docs/design/WORKFLOW_PROGRAM_IR.md carry both the REST-admission exports and the compose sequencer.

What this does not do

  • No process contract, and no new workflow-program IR (StateKind, subflow, worklist) on the parent.
  • No backend switch inside one recording. Compose will not retarget a child onto a surface it was not recorded on.
  • No Production admission or live Citrix claim. Evidence is unit tests plus a two-child fixture whose second child is a local FakeBackend.

What a reviewer must check

  • Fail-closed handoffs: a missing or empty effect-bound fact HALTs before the next child starts. tests/test_compose_runtime.py covers missing evidence, unverified predecessor, and allowed halt class.
  • Compose is a sequencer of admitted child bundles, not a second orchestrator and not an IR enlargement. Confirm openadapt_flow/composition.py stays a parent spec plus copied children.
  • replay refuses a composition directory; only certify / run execute it.
  • compiler/__init__.py exports both author_composition and the REST admission symbols from feat(compiler): admit REST bindings only after held-out Effect confirmation #432.

Test plan

  • tests/test_compose_authoring.py — copy, effect-bound source, unknown target, one child, cycle, backwards handoff, explicit --after
  • tests/test_compose_runtime.py — missing handoff HALT, unverified predecessor HALT, allowed halt class, scripted VERIFIED handoff, MockMed + FakeBackend fixture
  • tests/test_cli_compose.py — parser, CLI write, replay refusal
  • ruff + mypy on the new modules
  • CI test job passed on 32722d8 (38m9s), binding the three compose test files for compose-recorded-bundles

@abrichr
abrichr force-pushed the feat/compose-recorded-bundles branch 2 times, most recently from f2191bf to c1f3243 Compare August 29, 2026 04:46
Add `openadapt-flow compose` as a parent sequencer of already-compiled child
bundles. Each child keeps its recorded surface. Handoffs copy effect-bound
parameter facts after a VERIFIED (or explicitly allowed) predecessor outcome;
missing evidence HALTs. certify/run execute the parent; replay refuses it.
The child-run extension point is execute(capability, admission, inputs, ...)
bound today to the governed run path.

Signed-off-by: Richard Abrich <richard.abrich@mldsai.com>
Ruff wanted the compose --after help on one line. The claims report used a
HEAD timestamp, so CI's committed-registry --now failed the gate and the
wheel job saw a stale public-artifact inventory.

Signed-off-by: Richard Abrich <richard.abrich@mldsai.com>
@abrichr
abrichr force-pushed the feat/compose-recorded-bundles branch from c1f3243 to 32722d8 Compare August 29, 2026 05:30
@abrichr
abrichr enabled auto-merge (squash) August 29, 2026 05:30
@abrichr
abrichr merged commit 751a888 into main Aug 29, 2026
16 checks passed
@abrichr
abrichr deleted the feat/compose-recorded-bundles branch August 29, 2026 06:08
@abrichr

abrichr commented Aug 29, 2026

Copy link
Copy Markdown
Member Author

Merged by an agent session, not the founder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant