From 10b29db3b5c71e822cc731dfbe232bb86b3958ca Mon Sep 17 00:00:00 2001 From: abrichr Date: Wed, 26 Aug 2026 18:19:21 -0400 Subject: [PATCH 1/5] docs: make the first workflow read-only --- docs/get-started/first-workflow.md | 188 +++++++++++++++++------------ docs/get-started/index.md | 6 +- 2 files changed, 116 insertions(+), 78 deletions(-) diff --git a/docs/get-started/first-workflow.md b/docs/get-started/first-workflow.md index 5ce94e9d..9db0805b 100644 --- a/docs/get-started/first-workflow.md +++ b/docs/get-started/first-workflow.md @@ -1,24 +1,34 @@ +--- +description: >- + Record, inspect, and supervise one read-only workflow before you qualify any + write. +--- + # Your first workflow -This walks through compiling a workflow on **your own** web app: record what you -do, compile it, replay it, and read the report. It takes about five minutes and -makes zero model calls. Web is the quickest -[substrate](../reference/glossary.md#substrate) to start on; the same -record, compile, replay loop drives native Windows, macOS, or Linux applications -and RDP or Citrix sessions by choosing a -[backend](../reference/cli.md#backend). +Choose one small real task that doesn't change business data. A read-only lookup +against test data works well. Open a known test record, then stop when a field +shows the expected value. Don't start with a task that saves, submits, creates, +or deletes data. -Here is the loop you are about to run — record once, replay deterministically, -and watch the run heal or halt under drift: +For a visual first workflow, we recommend +[OpenAdapt Desktop](../desktop/install.md). It uses the same compiler and +runtime as the commands on this page. Its install guide lists the current +release-admission, signing, checksum, and permission details. Follow the CLI +path below if you prefer a terminal. + +This walkthrough uses a Playwright-driven browser. The same record, compile, +lint, and supervised replay loop works with native Windows, macOS, or Linux +applications and RDP or Citrix sessions when you choose a different +[backend](../reference/cli.md#backend). ![Record, compile, and replay a workflow with OpenAdapt](../assets/showcase/demo.gif) -## Prerequisites and install +## Install OpenAdapt -- **macOS, Linux, or Windows.** This walkthrough selects the Playwright-driven - browser capability, so it has no OS-specific steps. Its matching Chromium - provisions automatically on the first web action; native, RDP, and Citrix - paths do not install it. +This browser walkthrough runs on each desktop platform in the tabs below. It +has no OS-specific steps. Its matching Chromium provisions automatically on the +first web action. Native, RDP, and Citrix paths don't install it. **Recommended: install with pip in a virtual environment.** The base package includes the browser driver used by this walkthrough. The engine requires @@ -71,35 +81,38 @@ For an isolated command-line installation, run `curl -fsSL https://openadapt.ai/install.sh | sh`. It installs the same base package with [uv](https://docs.astral.sh/uv/). -!!! tip "No app to record against yet?" - You do not need your own target to try the loop. The engine bundles - **MockMed**, a synthetic demo clinic app (fake data only): - `openadapt flow demo-record --out rec` serves it locally and records the - canonical triage demo, and `openadapt flow replay bundle` with no `--url` - serves it again as the replay target. It is a local development fixture, - not a production workflow or product outcome — but it is a real, running - web app, so every step below works against it unchanged. +### Optional installation check -## 1. Record +Run `openadapt quickstart` if you want to check the installation before you +touch your own application. It records, compiles, certifies, and verifies a +bundled synthetic workflow. A healthy run ends in `VERIFIED`. That result +applies only to the bundled task, application, and local system of record. It +doesn't qualify your workflow. -`record --backend web --url` opens a headed browser pointed at your app and -watches what you do: real clicks, typing, key presses, and scrolls. It writes -the same recording format that `compile` consumes. +## 1. Record a read-only task + +Before recording, confirm that the application contains test data and that the +task needs no write. If the task unexpectedly requires a write, stop and choose +a different first workflow. + +`record --backend web --url` opens a headed browser and watches your clicks, +typing, key presses, and scrolling. It writes the recording format that +`compile` consumes. ```bash openadapt flow record --backend web --url https://your.app --out rec ``` -(Omitting `--backend` defaults to `web` with a printed notice; production +(Omitting `--backend` defaults to `web` with a printed notice. Production profiles require it explicitly.) -Perform the task once. When you are done, press ++ctrl+c++ or close the browser -window to finish. The recording is written to `rec/`. +Perform the task once with a known test record. When the expected result is +visible, press ++ctrl+c++ or close the browser window. OpenAdapt writes the +recording to `rec/`. -!!! tip "Record a clean demonstration" - Do the task the way you want it replayed: one clear path, no dead ends. The - compiler treats your demonstration as evidence of intent, so a tidy run - compiles into a tidy workflow. +!!! tip "Keep the demonstration clean" + Take one direct path and leave out exploratory clicks. The compiler treats + the recording as evidence of intent. ## 2. Compile @@ -107,61 +120,86 @@ window to finish. The recording is written to `rec/`. openadapt flow compile rec --out bundle --name my-task ``` -Compilation turns the recording into a **workflow bundle**: an ordered list of -steps, each carrying the evidence needed to re-find its target (a template crop, -an OCR label, geometry landmarks — the -[capability ladder](../reference/glossary.md#capability-ladder)) and -postconditions derived from what the demo actually changed on screen. -Write-shaped clicks (save, submit, create, delete) are auto-classified as -irreversible so they refuse to act on a low-confidence match. +Compilation turns the recording into a **workflow bundle**. Each step carries +evidence that OpenAdapt can use to find the target again, such as a DOM +identity, template crop, OCR label, or geometry landmark. See the +[capability ladder](../reference/glossary.md#capability-ladder). The compiler +also derives screen postconditions from the recorded change. -## 3. Lint +OpenAdapt classifies write-shaped clicks such as save, submit, create, and +delete as irreversible. Treat that classification as a stop signal. Review the +bundle before any replay. + +## 3. Lint and review ```bash -openadapt flow lint bundle +openadapt flow lint bundle --strict ``` -`lint` reports coverage gaps before you trust the bundle: clicks that act with -no [identity check](../reference/glossary.md#identity-gate), steps that assert -nothing, writes that may be under-classified. Each finding carries a severity. -It is advice, not a gate. See -[Write and enforce a policy](../guides/policy-and-certification.md) for the -`certify` gate that refuses an unsafe bundle outright. +`lint` reports missing evidence, weak risk classification, unexpected writes, +and steps that don't assert a result. It doesn't authorize replay. + +!!! danger "Stop before replay when a safety contract is missing" + Do not replay if lint or your review finds any of these conditions: -!!! success "A nonzero exit here is expected, not broken" - `lint` exits `1` when any finding reaches `error` severity — an unarmed or - vacuous **irreversible** step. That is the safety boundary working: it is - telling you a write-shaped click would act without a wrong-record guard. - Review the findings, then continue to step 4; replay still runs, and the - [`certify` gate](../guides/policy-and-certification.md) is where a failing - bundle is actually refused. All exit codes are listed in - [Run outcomes and halt reasons](../reference/run-outcomes.md#cli-exit-codes). + - an action writes data or its risk is unknown; + - an action is consequential or irreversible; + - an identity, effect, or policy contract is missing; or + - the recording contains an unexpected application, page, or data source. -## 4. Replay + Move the bundle to [workflow qualification](../guides/qualify-a-workflow.md). + Set the action risks, add the required contracts, run the qualification + cases, and certify the exact version. Then run lint again. + +Continue only after strict lint and your manual review are clean. The recorded +task must remain read-only and non-consequential. + +## 4. Replay under supervision + +Keep the browser in view for the entire run. Use the same test environment and +stay ready to stop if OpenAdapt opens the wrong record, leaves the expected +path, changes test data, or reaches a screen that wasn't in the demonstration. ```bash -openadapt flow replay bundle --url https://your.app +openadapt flow replay bundle \ + --url https://your.app \ + --headed \ + --run-dir runs/first-workflow ``` -Recorded parameter values are the defaults; override any of them with -`--param key=value`. The run is deterministic and local. On the healthy path it -makes zero model calls and finishes in seconds. `replay` exits `0` on success -and `1` on a [halt](../reference/glossary.md#halt) — a halt is the fail-closed -refusal to guess, not a crash. +Confirm that the expected value appears on screen. Don't use this first replay +for a real write. A completed replay is evidence for this supervised run; it +doesn't certify the workflow or make it safe for unattended use. + +## 5. Inspect the report + +Open `runs/first-workflow/REPORT.md`. The machine-readable report is beside it +at `runs/first-workflow/report.json`. + +Check that the report shows: + +- only the expected test application and data; +- no write or consequential action; +- the visible result you checked during replay; and +- the target evidence, postconditions, halt, or heal for every step. + +If the report disagrees with what you saw, stop. Keep the report and recording, +then fix or re-record the workflow before another replay. See +[Read and audit run reports](../guides/run-reports.md) for the evidence fields +and [Run outcomes and halt reasons](../reference/run-outcomes.md) for every +terminal outcome. -## 5. Read the report +## Before you automate a write -Each replay writes a timestamped run directory under `runs/` containing an -illustrated `REPORT.md` and a machine-readable `report.json`. The report tells -you, per step, which rung of the resolution ladder resolved the target, whether -the identity check was armed and what it verified, which postconditions passed, -and any heals that were applied. See [What you get](what-you-get.md) and -[Read and audit run reports](../guides/run-reports.md). Every outcome and halt -reason the report can show is defined in -[Run outcomes and halt reasons](../reference/run-outcomes.md). +[Qualify the workflow](../guides/qualify-a-workflow.md) before a real write or +any unattended run. Bind the exact application and environment, review every +action risk, add identity and independent effect contracts, exercise the +required success and fault cases, and certify the exact workflow version under +its policy. Use the governed `run` path only after those checks pass. ## What is next -- Parameterize a value and inject a secret: [Parameters and secrets](../guides/parameters-and-secrets.md) -- Understand why a step healed or halted: [Governed self-healing](../concepts/self-healing.md) -- Gate a bundle behind a safety policy: [Policy and certify](../concepts/policy-and-certify.md) +- Build the full qualification project: [Qualify a workflow](../guides/qualify-a-workflow.md) +- Enforce a workload policy: [Write and enforce a policy](../guides/policy-and-certification.md) +- Audit the evidence from a run: [Read and audit run reports](../guides/run-reports.md) +- Add governed inputs: [Parameters and secrets](../guides/parameters-and-secrets.md) diff --git a/docs/get-started/index.md b/docs/get-started/index.md index a25fc522..dea194a5 100644 --- a/docs/get-started/index.md +++ b/docs/get-started/index.md @@ -80,7 +80,7 @@ After the first run, choose the path that matches your goal: | Goal | Next guide | |---|---| -| Record one real browser workflow | [Your first workflow](first-workflow.md) | +| Record one real, read-only browser workflow | [Your first workflow](first-workflow.md) | | Use the Desktop application | [Install Desktop](../desktop/install.md) | | Use native desktop, RDP, or Citrix | [Install a different execution surface](#install-a-different-execution-surface) | | Prepare a qualified production run | [Move from demo to deployment](#move-from-demo-to-deployment) | @@ -212,8 +212,8 @@ The local runtime also supports these paths: - [__Your first workflow__](first-workflow.md) - Record, compile, and replay on your own app step by step, and read the - run report. + Record a read-only task with test data, review it, supervise its first + replay, and inspect the report. - [__What you get__](what-you-get.md) From 3f4ef75f641f4d348d2603e406a17ae303b8df0a Mon Sep 17 00:00:00 2001 From: abrichr Date: Wed, 26 Aug 2026 18:20:55 -0400 Subject: [PATCH 2/5] docs: keep first workflow CLI-first --- docs/get-started/first-workflow.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/get-started/first-workflow.md b/docs/get-started/first-workflow.md index 9db0805b..9550346e 100644 --- a/docs/get-started/first-workflow.md +++ b/docs/get-started/first-workflow.md @@ -11,12 +11,6 @@ against test data works well. Open a known test record, then stop when a field shows the expected value. Don't start with a task that saves, submits, creates, or deletes data. -For a visual first workflow, we recommend -[OpenAdapt Desktop](../desktop/install.md). It uses the same compiler and -runtime as the commands on this page. Its install guide lists the current -release-admission, signing, checksum, and permission details. Follow the CLI -path below if you prefer a terminal. - This walkthrough uses a Playwright-driven browser. The same record, compile, lint, and supervised replay loop works with native Windows, macOS, or Linux applications and RDP or Citrix sessions when you choose a different From 8f083fb2134ea006663bd0085ad4b14ef3973a2b Mon Sep 17 00:00:00 2001 From: abrichr Date: Wed, 26 Aug 2026 18:25:37 -0400 Subject: [PATCH 3/5] docs: name each missing safety contract --- docs/get-started/first-workflow.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/get-started/first-workflow.md b/docs/get-started/first-workflow.md index 9550346e..ce3d9dd5 100644 --- a/docs/get-started/first-workflow.md +++ b/docs/get-started/first-workflow.md @@ -138,7 +138,8 @@ and steps that don't assert a result. It doesn't authorize replay. - an action writes data or its risk is unknown; - an action is consequential or irreversible; - - an identity, effect, or policy contract is missing; or + - an identity or effect contract is missing; + - a policy contract is missing; or - the recording contains an unexpected application, page, or data source. Move the bundle to [workflow qualification](../guides/qualify-a-workflow.md). From 62b6ac2ffefbc73b2240439942a275a1442fa9ea Mon Sep 17 00:00:00 2001 From: abrichr Date: Wed, 26 Aug 2026 18:33:00 -0400 Subject: [PATCH 4/5] docs: bind first replay stops to real risk --- docs/get-started/first-workflow.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/get-started/first-workflow.md b/docs/get-started/first-workflow.md index ce3d9dd5..530a2973 100644 --- a/docs/get-started/first-workflow.md +++ b/docs/get-started/first-workflow.md @@ -130,16 +130,17 @@ bundle before any replay. openadapt flow lint bundle --strict ``` -`lint` reports missing evidence, weak risk classification, unexpected writes, -and steps that don't assert a result. It doesn't authorize replay. +`lint` reports missing evidence, missing assertions, write-shaped or +irreversible actions, and risks that may be under-classified. It doesn't decide +whether a write belongs in the recorded task. Review every action yourself. -!!! danger "Stop before replay when a safety contract is missing" +!!! danger "Stop before replay when a required safety contract is missing" Do not replay if lint or your review finds any of these conditions: - an action writes data or its risk is unknown; - an action is consequential or irreversible; - - an identity or effect contract is missing; - - a policy contract is missing; or + - an action requires an identity, effect, or policy contract and that + contract is missing; or - the recording contains an unexpected application, page, or data source. Move the bundle to [workflow qualification](../guides/qualify-a-workflow.md). From 924545f3542b3867fe047c4190bfcb8d65b79af4 Mon Sep 17 00:00:00 2001 From: abrichr Date: Wed, 26 Aug 2026 18:41:49 -0400 Subject: [PATCH 5/5] test: lock first-workflow safety scope --- docs/get-started/first-workflow.md | 2 + scripts/validate_docs.py | 43 ++++++++++++++++++ tests/test_validate_docs.py | 71 ++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) diff --git a/docs/get-started/first-workflow.md b/docs/get-started/first-workflow.md index 530a2973..24163ee4 100644 --- a/docs/get-started/first-workflow.md +++ b/docs/get-started/first-workflow.md @@ -2,6 +2,8 @@ description: >- Record, inspect, and supervise one read-only workflow before you qualify any write. +first_workflow_scope: read_only +first_write_admission: qualification_required --- # Your first workflow diff --git a/scripts/validate_docs.py b/scripts/validate_docs.py index bdeb020f..45630c5d 100644 --- a/scripts/validate_docs.py +++ b/scripts/validate_docs.py @@ -62,6 +62,15 @@ REQUIRED_NAV_PAGES = set(REQUIRED_PUBLIC_PAGES) - {"packages/openadapt.md"} +FIRST_WORKFLOW_CONTRACT = { + "first_workflow_scope": "read_only", + "first_write_admission": "qualification_required", +} +FIRST_WORKFLOW_FORBIDDEN_FLAGS = ( + "--break-it", + "--simulate-rejected-write", +) + STALE_PRELAUNCH_MARKERS = { "Available for qualification", "Beta launch candidate", @@ -107,6 +116,20 @@ def check_empty_pages(docs_dir=None): return issues +def _frontmatter(content): + """Return a page's YAML frontmatter, or an empty mapping when invalid.""" + if not content.startswith("---\n"): + return {} + end = content.find("\n---\n", 4) + if end == -1: + return {} + try: + metadata = yaml.safe_load(content[4:end]) or {} + except yaml.YAMLError: + return {} + return metadata if isinstance(metadata, dict) else {} + + def check_product_docs_contract(docs_dir=None, mkdocs_file=None): """Keep sync/deploy from erasing product truth or restoring package-first IA.""" docs_dir = pathlib.Path(docs_dir or DOCS_DIR) @@ -123,6 +146,26 @@ def check_product_docs_contract(docs_dir=None, mkdocs_file=None): if marker not in content: issues.append(f"Missing product marker in {relative_path}: {marker}") + first_workflow = docs_dir / "get-started" / "first-workflow.md" + if not first_workflow.is_file(): + issues.append("Missing required product page: get-started/first-workflow.md") + else: + first_workflow_text = first_workflow.read_text() + first_workflow_metadata = _frontmatter(first_workflow_text) + for field, expected in FIRST_WORKFLOW_CONTRACT.items(): + actual = first_workflow_metadata.get(field) + if actual != expected: + issues.append( + "First-workflow safety contract requires " + f"{field}: {expected}; found: {actual}" + ) + for flag in FIRST_WORKFLOW_FORBIDDEN_FLAGS: + if flag in first_workflow_text: + issues.append( + "Failure-demo flag is forbidden in first-workflow " + f"onboarding: {flag}" + ) + if not mkdocs_file.exists(): issues.append(f"Missing MkDocs config: {mkdocs_file}") return issues diff --git a/tests/test_validate_docs.py b/tests/test_validate_docs.py index 001762bf..2adc77f6 100644 --- a/tests/test_validate_docs.py +++ b/tests/test_validate_docs.py @@ -3,6 +3,8 @@ import pathlib import sys +import pytest + sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent / "scripts")) from validate_docs import check_empty_pages, check_product_docs_contract @@ -78,6 +80,10 @@ def _write_contract_docs(root): "`openadapt flow`." ), "get-started/first-workflow.md": ( + "---\n" + "first_workflow_scope: read_only\n" + "first_write_admission: qualification_required\n" + "---\n\n" "# Your first workflow\n\nInstall with `pip install openadapt` and " "record a bounded workflow." ), @@ -142,6 +148,71 @@ def test_product_docs_contract_passes_for_product_first_nav(tmp_path): assert check_product_docs_contract(docs_dir, mkdocs_file) == [] +@pytest.mark.parametrize( + ("field", "bad_value"), + ( + ("first_workflow_scope", "read_write"), + ("first_write_admission", "replay_allowed"), + ), +) +def test_product_docs_contract_rejects_wrong_first_workflow_contract( + tmp_path, field, bad_value +): + docs_dir = tmp_path / "docs" + pages = _write_contract_docs(docs_dir) + first_workflow = docs_dir / "get-started/first-workflow.md" + content = first_workflow.read_text() + expected = { + "first_workflow_scope": "read_only", + "first_write_admission": "qualification_required", + }[field] + first_workflow.write_text( + content.replace(f"{field}: {expected}", f"{field}: {bad_value}") + ) + mkdocs_file = tmp_path / "mkdocs.yml" + mkdocs_file.write_text( + "nav:\n - Reference:\n" + + "".join(f" - {path}\n" for path in pages) + + " - Package and repository lifecycle: ecosystem/index.md\n" + ) + + issues = check_product_docs_contract(docs_dir, mkdocs_file) + + assert any( + "First-workflow safety contract requires" in issue and field in issue + for issue in issues + ) + + +@pytest.mark.parametrize( + "flag", + ("--break-it", "--simulate-rejected-write"), +) +def test_product_docs_contract_rejects_failure_demo_in_first_workflow( + tmp_path, flag +): + docs_dir = tmp_path / "docs" + pages = _write_contract_docs(docs_dir) + first_workflow = docs_dir / "get-started/first-workflow.md" + first_workflow.write_text( + first_workflow.read_text() + f"\n`openadapt quickstart {flag}`\n" + ) + mkdocs_file = tmp_path / "mkdocs.yml" + mkdocs_file.write_text( + "nav:\n - Reference:\n" + + "".join(f" - {path}\n" for path in pages) + + " - Package and repository lifecycle: ecosystem/index.md\n" + ) + + issues = check_product_docs_contract(docs_dir, mkdocs_file) + + assert any( + "Failure-demo flag is forbidden in first-workflow onboarding" in issue + and flag in issue + for issue in issues + ) + + def test_product_docs_contract_rejects_missing_page_and_package_first_nav(tmp_path): docs_dir = tmp_path / "docs" pages = _write_contract_docs(docs_dir)