Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
919b60a
on-prem: install, update and roll back the real engine offline in CI
abrichr Nov 4, 2026
f5eb3c3
on-prem: check the engine version from the release venv, not the chec…
abrichr Nov 4, 2026
efbb57c
on-prem audit: do not break a lock that a live writer just took
abrichr Nov 4, 2026
7f2de34
on-prem: build the runner image from a signed release with no network…
abrichr Nov 4, 2026
7770839
on-prem locks: take ownership with an exclusive file create, not mkdi…
abrichr Nov 5, 2026
977dfd7
Merge remote-tracking branch 'origin/agent/142-c2' into agent/142-c3
abrichr Nov 5, 2026
d6db42e
on-prem container test: keep pip's error in the missing-wheel log; re…
abrichr Nov 5, 2026
acf95a4
fix(flow): name shared rows by task values and keep changed columns i…
abrichr Nov 5, 2026
63b63a3
identity: read a dialog target's band on the dialog alone (OpenEMR du…
abrichr Nov 5, 2026
de6076b
fix: synchronize worklist stop and readback fixtures (#1930)
abrichr Nov 5, 2026
ff5257c
chore(release): openadapt 1.20.0 on openadapt-flow 2.6.0 (#1850)
abrichr Nov 5, 2026
9ec50e4
Add generic phase timing observations and coverage analysis (#1931)
abrichr Nov 5, 2026
7553066
fix: wait for the WPF census fixture to render (#1940)
abrichr Nov 5, 2026
2b5bb17
flow: API steps for multipart saves, the clicked button's value, and …
abrichr Nov 5, 2026
8b783a1
fix: frame lab SSH scripts and preflight rollback helpers (#1942)
abrichr Nov 5, 2026
160079a
No command falls back to the demo app on its own (#1764)
abrichr Nov 5, 2026
81f3b05
feat(policy): add record-write, a general write policy for non-clinic…
abrichr Nov 5, 2026
62c4849
on-prem image: copy only the named deploy files, and refuse a base im…
abrichr Nov 5, 2026
5f5894d
ensure_record: bind the read-back to the last version written and nam…
abrichr Nov 5, 2026
47ec7f4
fix: retain lab SSH operation budgets and unknown guest timeouts (#1949)
abrichr Nov 5, 2026
e3765a4
qualify: verifier.session for FHIR and GraphQL read-back, renewed eac…
abrichr Nov 5, 2026
72b743e
fix(flow): read bundle manifest and API proposal sidecars as UTF-8 (#…
abrichr Nov 5, 2026
bc5357a
Merge remote-tracking branch 'origin/main' into agent/142-c3
abrichr Nov 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ App 15368 is GitHub Actions, the app the current checks are pinned to.
| main.yml | `run-ci` | ubuntu-latest, Python 3.12 | ubuntu-latest, Python 3.12 | ubuntu-latest and macos-latest, Python 3.10 to 3.12 |
| main.yml | `unsupported-python-guard` | no | no | Python 3.13 and 3.14 |
| packages.yml | `types`, `agent` | Python 3.12 | Python 3.12 | Python 3.10 and 3.12 |
| packages.yml | `flow-lint`, `flow-critical` | yes | yes | yes |
| packages.yml | `flow-lint`, `flow-critical`, `flow-onprem-offline` | yes | yes | yes |
| ci.yml | `smoke`, `ci` | yes | yes | no |
| packages.yml | `flow-unit` | 3 shards packed by measured time, fast lane (without `nightly_only.txt`) | no | 2 shards |
| packages.yml | `flow-native-census` | no | no | Windows and macOS |
Expand Down
52 changes: 50 additions & 2 deletions .github/workflows/packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ jobs:
# every public data/config asset listed in public-artifacts.json.
run: python scripts/check_release_consistency.py
- name: Ruff lint
run: ruff check openadapt_flow benchmark/harness
run: ruff check openadapt_flow benchmark/harness benchmark/phase_latency
- name: No app-specific code in the engine
# App knowledge lives in profiles, contracts and fixtures (CONTRIBUTING:
# "App knowledge lives in data"); the denylist is scripts/generality.toml.
Expand All @@ -376,9 +376,11 @@ jobs:
ruff check --isolated --select E9 --target-version py310 .
uv run --no-project --python 3.10 python -m compileall -q -x '(^|/)(\.venv|node_modules|build|dist)/' .
- name: Ruff format check
run: ruff format --check openadapt_flow tests benchmark/harness
run: ruff format --check openadapt_flow tests benchmark/harness benchmark/phase_latency
- name: Mypy
run: mypy
- name: Phase timing contract types
run: mypy --follow-imports=silent --ignore-missing-imports benchmark/phase_latency/collector.py
- name: Mypy (strict, safety path only)
run: |
mypy \
Expand Down Expand Up @@ -630,6 +632,50 @@ jobs:
sys.exit(1 if bad else 0)
PY

# The on-prem install path for real: build two signed offline releases of
# openadapt-flow[privacy] from uv.lock on this connected runner, then, as
# root in a network namespace with no route out, install the first from its
# wheelhouse, refuse a tampered second, update to the signed second, roll
# back, and run the air-gap attestation (egress probe, audit chain). pip has
# no index and the machine has no network, so a wheel missing from the
# archive fails the install. deploy/on-prem/bin/test-offline-install.sh.
# First, bin/test-update.sh: the update state machine's refusal, migration
# and recovery paths with a fake venv build, which nothing else runs. Then
# bin/test-container.sh: the runner image built from the first release with
# no network, run on docker-compose.yml's internal network.
flow-onprem-offline:
name: flow on-prem offline install, update and rollback (egress blocked)
needs: scope
if: github.event.pull_request.draft != true && (needs.scope.outputs.flow == 'true')
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: packages/flow
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
enable-cache: false
- name: Signed update and rollback state machine (hermetic, fake build)
run: bash deploy/on-prem/bin/test-update.sh
- name: Build two signed offline releases from the lock (connected)
run: bash deploy/on-prem/bin/test-offline-install.sh stage "$RUNNER_TEMP/offline"
- name: Container image from the signed release (no-network build, internal network)
run: bash deploy/on-prem/bin/test-container.sh "$RUNNER_TEMP/offline"
- name: Install, update and roll back with egress blocked
# env -u SUDO_UID keeps the command root inside the namespace:
# install.sh owns the release tree as root.
run: >-
sudo env -u SUDO_UID -u SUDO_GID "PATH=$PATH"
deploy/on-prem/bin/run-egress-blocked.sh
bash deploy/on-prem/bin/test-offline-install.sh verify "$RUNNER_TEMP/offline"

# The agent-framework adapters (openadapt_flow.integrations.agents): each
# framework's own agent loop with a scripted model, writing through a real
# HTTP app, from the lock with every framework extra (CrewAI pinned beside
Expand Down Expand Up @@ -1425,6 +1471,7 @@ jobs:
- flow-lint
- flow-unit
- flow-critical
- flow-onprem-offline
- flow-agent-frameworks
- flow-native-census
- flow-linux-atspi
Expand Down Expand Up @@ -1478,6 +1525,7 @@ jobs:
"flow-lint": "flow",
"flow-unit": "flow",
"flow-critical": "flow",
"flow-onprem-offline": "flow",
"flow-agent-frameworks": "flow",
"flow-native-census": "flow",
"flow-linux-atspi": "flow",
Expand Down
4 changes: 4 additions & 0 deletions .gitleaksignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,7 @@ e8ec96725c4451aa64cb97230c18d04b55d98503:packages/flow/tests/e2e/test_mcp_teach_
# test_network_capture_redact_json.py; the next commit builds them at run time.
52502adf407793c211fb3c0ac5aba37556206d8c:packages/flow/tests/test_network_capture_redact_json.py:generic-api-key:17
52502adf407793c211fb3c0ac5aba37556206d8c:packages/flow/tests/test_network_capture_redact_json.py:generic-api-key:146
# The API-step generalization harness (PR #1851) first started its local NetBox
# container with a fixed synthetic superuser token (a run of hex digits, never a
# credential for any real system); the harness now generates it per run.
9922cee8dbbbaa4494b1c69ec6c71e4899305de6:packages/flow/benchmark/api_generalization/apps/nb.py:generic-api-key:60
4 changes: 3 additions & 1 deletion CAPABILITIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ monorepo and installable from source.
| Learns branching programs from several demonstrations, from repeated halts, and from many background runs. | `induce`, `teach-branch`, [SHADOW_MINING.md](packages/capture/docs/SHADOW_MINING.md), [LEARN_FROM_MANY_RUNS.md](packages/flow/docs/LEARN_FROM_MANY_RUNS.md) |
| Chains programs across surfaces, and one program can switch surfaces, tabs and popups mid-run. | `compose`, `process`, [#1197](https://github.com/OpenAdaptAI/OpenAdapt/pull/1197) |
| Uses page structure first on web, Windows, macOS and Linux, with pixels as the fallback for remote desktops. Benchmarks labelled "vision-only" ran a deliberately pixel-only arm. | [RESOLUTION_LADDER.md](packages/flow/docs/RESOLUTION_LADDER.md) |
| Replaces a single HTML form submission with the request the app itself sends, compiled from the network traffic captured while recording, then reads the record back. JSON or GraphQL requests, multi-step wizards and governed runs still use the screen steps. | `api`, [API_TIER.md](packages/flow/docs/API_TIER.md) |
| Replaces a single HTML form submission with the request the app itself sends, compiled from the network traffic captured while recording, then reads the record back. URL-encoded and file-less multipart saves, the clicked button's value, and saves the page's script sends to another endpoint are covered. JSON or GraphQL requests, uploads, multi-step wizards and governed runs still use the screen steps. | `api`, [API_TIER.md](packages/flow/docs/API_TIER.md) |
| Builds the record check itself. After a save, the app usually reads the record back to show it; `scaffold-verifier` finds that read in the captured traffic or a read-only database, builds the check from it and tries to break it before a person reviews it. No API or SQL needs to be written by hand. | [EFFECT_KIT.md](packages/flow/docs/EFFECT_KIT.md#a-record-check-built-from-the-apps-own-reads-scaffold-verifier) |
| Checks a save on desktop, RDP and Citrix apps with no API at all, by looking the record up in the app's own screens in a separate session. | [EFFECT_KIT.md](packages/flow/docs/EFFECT_KIT.md#separate-session-desktop-read-back-desktop_readback) |
| Keeps pixel-path runs working across display scaling from 100% to 175% and font smoothing on or off, and stops rather than saves when the record changed. | [benchmark/pixel_scale](packages/flow/benchmark/pixel_scale/README.md) |
Expand Down Expand Up @@ -230,6 +230,7 @@ improvising or retrying when something is off.
| One program that switches surfaces, tabs and popups mid-run | Released ([#1197](https://github.com/OpenAdaptAI/OpenAdapt/pull/1197)) | Real Chromium; Windows switch on a mock | [#1197](https://github.com/OpenAdaptAI/OpenAdapt/pull/1197) |
| Halt when a workflow qualified with page structure runs on a pixels-only surface | Released | Synthetic | `openadapt-flow qualify` |
| Stop before acting when another shown row has the same identity; when the rows carry an app-owned record id (a `data-*` id, the row link), act on the row with the recorded id; on a surface that cannot count the rows, stop before any step that leads into a write | Released ([#1504](https://github.com/OpenAdaptAI/OpenAdapt/pull/1504)) | Real browser (headless Chromium worklist with identical rows) | `runtime/identity_census.py`; `PlaywrightBackend.identity_candidates_at` |
| Read a dialog target's identity on the topmost modal dialog alone, not the page behind it, and stop when no dialog holds the target | On main ([#1615](https://github.com/OpenAdaptAI/OpenAdapt/pull/1615)) | Real app (local OpenEMR, synthetic data): at the duplicate dialog with the form scrolled to 8 positions behind it, 0 of 8 false stops, against 8 of 8 before; a dialog listing a possible duplicate still stops | `PlaywrightBackend.dialog_region_at`, `Anchor.dialog_region`; [dialog_identity_2026-10-28](packages/flow/benchmark/openemr_throughput/dialog_identity_2026-10-28/README.md) |
| Lift look-alike identity stops with a complete known-ID list | Released | Synthetic (rendered record list) | `openadapt-flow known-ids` |
| Take extra looks before stopping on a look-alike record ID: fresh frames, the known-ID list, and a select-and-copy of the ID where the surface allows it | Released ([#1333](https://github.com/OpenAdaptAI/OpenAdapt/pull/1333)) | Synthetic (dense record list in a browser) | `runtime.identity_copy_look` in deployment.yaml; [IDENTITY_LOOKS.md](packages/flow/benchmark/identity_looks/IDENTITY_LOOKS.md) |
| Check an icon-only click target's hover response (highlight, tooltip) before clicking on a pixel surface, and re-check the target and identity regions on a fresh frame right before every pixel click | Released ([#1334](https://github.com/OpenAdaptAI/OpenAdapt/pull/1334)) | Synthetic (in-process kiosk) | `Recorder(capture_hover=True)`; [hover_identity](packages/flow/benchmark/hover_identity/README.md) |
Expand All @@ -243,6 +244,7 @@ improvising or retrying when something is off.
| Capture a recording's network traffic and compile a clean save into an API step, with the same record check | Released ([#1269](https://github.com/OpenAdaptAI/OpenAdapt/pull/1269)) | Synthetic patients on a local OpenEMR test copy | network capture, `ApiBinding` |
| Fall back from the API step to page structure when the API is unavailable, before anything is sent | Released ([#1269](https://github.com/OpenAdaptAI/OpenAdapt/pull/1269)) | Synthetic | [#1269](https://github.com/OpenAdaptAI/OpenAdapt/pull/1269) |
| Run a form's own script before an API save instead of refusing the form: field handlers, `onsubmit`, submit listeners and custom validate.js rules (OpenEMR's `pastDate`) run on the run's values in a scratch copy of the session with every write blocked, and the save is sent only when the page lets the submit through and would send the same body | On main | Synthetic (headless Chromium against a local test page) | [API_TIER.md](packages/flow/docs/API_TIER.md#what-happens-at-run-time), `runtime/actuators/form_eval.py` |
| Propose API steps for more save shapes: multipart saves with no file (encoded as Chromium encodes them), the clicked submit button's `name=value`, empty file inputs, forms with no `action`, and saves the page's script sends to another endpoint (run through the page's own script before every save); success addresses keep a record's path with numbers and hashes generalized | This PR | Headless Chromium against local test pages, and recordings from Redmine, WordPress, NetBox, Dolibarr and Trac fixtures | [API_TIER.md](packages/flow/docs/API_TIER.md#what-the-compiler-checks) |
| Declare the other writes in an API step's span as an ordered sequence: the page's own background requests (another document's script, loaded before the form, to an endpoint the form doesn't name) are listed and not sent; a write a step caused is sent in its recorded place before the save, read back by that step's record check, and a failure stops the run with nothing after it sent and nothing retried | On main | Synthetic | [API_TIER.md](packages/flow/docs/API_TIER.md#other-writes-in-the-span) |
| Approve an API step's exact request under per-item approval: one digest binds the request bytes, the record and the record check, and a changed request is not sent | Released ([#1355](https://github.com/OpenAdaptAI/OpenAdapt/pull/1355)) | Synthetic | [API_TIER.md](packages/flow/docs/API_TIER.md#under-an-approval-mode) |
| Run an API step in a durable run: checkpoint the span after the save is sent, never resend or redo a save that may have landed, and reconcile it by reading the record back | Released ([#1358](https://github.com/OpenAdaptAI/OpenAdapt/pull/1358)) | Synthetic | [API_TIER.md](packages/flow/docs/API_TIER.md#in-a-durable-run) |
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

<!-- version list -->

## v1.20.0 (2026-11-04)

### Requirements

- Require `openadapt-flow>=2.6.0,<3.0.0` in the base install and every Flow
extra. `openadapt doctor` and `openadapt deploy` report older Flow releases
as unsupported. This supplies the persistent owed-recheck API used by
ledger listing.

### Features

- `openadapt verify ledger list` shows provisional confirmed writes whose
rechecks are still owed, with their due time and actor. Owed rechecks remain
visible after the server restarts and disappear when the recheck completes.
An empty ledger reports that no held writes, open writes or owed rechecks
remain.
- The platform manifest and compatibility report select published component
releases, including Desktop 0.25.1.

## v1.19.0 (2026-11-02)

### Breaking Changes
Expand Down
2 changes: 1 addition & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ openadapt flow demo-record --out rec
openadapt flow compile rec --out bundle --name my-task
openadapt flow lint bundle --strict
openadapt flow certify bundle --policy permissive
openadapt flow replay bundle --run-dir run
openadapt flow replay bundle --demo-app --run-dir run
```

The strict lint step refuses the unarmed demo write. The permissive policy is a
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ openadapt flow demo-record --out rec
openadapt flow compile rec --out bundle --name my-task
openadapt flow lint bundle --strict
openadapt flow certify bundle --policy permissive
openadapt flow replay bundle --run-dir run
openadapt flow replay bundle --demo-app --run-dir run
```

The strict lint step returns a nonzero exit code. The bundled manual demo has
Expand Down
Loading