From e989fe44999a263a50c194129382682d32bcaf00 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 29 Aug 2026 15:04:52 -0400 Subject: [PATCH 1/2] feat: print the Day-1 partner kit after quickstart openadapt quickstart already runs MockMed through an independent oracle. After VERIFIED it now prints `openadapt-agent serve --allow-run`. --break-it still HALTs with the store unchanged. pip install openadapt pulls openadapt-agent. tests/test_partner_kit.py invokes Flow instead of mocking _invoke_flow. Opened by an agent session, not the founder. --- README.md | 14 ++++++-- docs/cli.md | 12 +++++-- openadapt/cli.py | 8 +++++ pyproject.toml | 10 +++++- tests/test_cli_smoke.py | 12 +++++-- tests/test_partner_kit.py | 73 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 tests/test_partner_kit.py diff --git a/README.md b/README.md index 2ea6373a0..3cdd793ff 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,18 @@ this repository doesn't reimplement them. ## Run it +A solutions engineer gets `VERIFIED` on synthetic MockMed, then hands an agent +the run tool: + ```bash python -m pip install --upgrade openadapt openadapt quickstart +openadapt-agent serve --allow-run ``` Python 3.10 through 3.12. No account, no API key, no extra. Chromium downloads -itself the first time a browser action runs. +itself the first time a browser action runs. `openadapt-agent` is in the base +install. `quickstart` records and compiles a task in MockMed, a synthetic practice-management fixture, certifies it against the shipped clinical-write @@ -68,7 +73,12 @@ openadapt quickstart --break-it Same certified bundle. This time the backend rejects the write after the app has already painted its success banner, so every on-screen check passes and the run halts anyway, because the independent read of the record store disagrees. -That halt is the whole product in one command. +The store is unchanged. That halt is the whole product in one command. + +`openadapt-agent serve --allow-run` then generates the public MockMed bundle +at serve time and keeps the app up. A client calls `run_local_quickstart`. +You'll get a receipt. You never see frames. Local unsigned success isn't a +Seal; treat unsigned production success as failure. Inspect what compiled, and what it failed to cover: diff --git a/docs/cli.md b/docs/cli.md index 84754ce17..050b25019 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -10,12 +10,18 @@ installed `openadapt-flow` engine. ## First run ```bash +python -m pip install --upgrade openadapt openadapt quickstart [--headed] [--break-it] [--out NEW_DIRECTORY] +openadapt-agent serve --allow-run ``` -`quickstart` runs the bundled synthetic workflow from recording through an -independently verified Standard-profile result. It refuses to overwrite an -existing output directory. +`quickstart` runs the bundled synthetic MockMed workflow from recording through +an independently verified Standard-profile result. It refuses to overwrite an +existing output directory. `--break-it` reruns the same certified bundle +against a lying backend; the independent oracle must HALT and leave the store +unchanged. `openadapt-agent serve --allow-run` then generates the public +MockMed bundle at serve time and hosts it over MCP. A client POSTs authorized +work and gets a receipt. Frames stay on this machine. ## Flow lifecycle diff --git a/openadapt/cli.py b/openadapt/cli.py index 99980f89d..dc018c393 100644 --- a/openadapt/cli.py +++ b/openadapt/cli.py @@ -63,6 +63,7 @@ def main(): Quick Start: python -m pip install --upgrade openadapt openadapt quickstart + openadapt-agent serve --allow-run \b Manual Demo Lifecycle: @@ -74,6 +75,7 @@ def main(): The manual demo is runnable but not certified for consequential work. Use `openadapt quickstart` for the effect-verified first run. + `openadapt-agent serve --allow-run` then exposes run_* tools over MCP. """ pass @@ -253,6 +255,7 @@ def quickstart( "the screen claimed success and the system of record disagreed." ) click.echo(f"Caught-fault evidence: {root / 'run-broken' / 'REPORT.md'}") + click.echo("The independent oracle kept the store unchanged.") click.echo(f"Inspect qualification gaps: openadapt flow lint {root / 'bundle'}") if not break_it: click.echo( @@ -263,6 +266,9 @@ def quickstart( "See a fail-safe halt: openadapt flow replay " f"{root / 'bundle'} --drift modal --run-dir {root}-halt" ) + click.echo("Give a local agent the run tool (synthetic MockMed, no frames):") + click.echo(" openadapt-agent serve --allow-run") + click.echo("A client POSTs authorized work and gets a receipt. Frames stay here.") click.echo( "Connect this computer when you want Cloud history and collaboration: " "https://app.openadapt.ai/dashboard/settings/ingest" @@ -974,6 +980,7 @@ def version(): packages = [ "openadapt", + "openadapt-agent", "openadapt-capture", "openadapt-ml", "openadapt-evals", @@ -1058,6 +1065,7 @@ def doctor(backend: str | None): click.echo("\nCore packages (installed with `pip install openadapt`):") core = [ "openadapt_flow", + "openadapt_agent", "playwright", ] for pkg in core: diff --git a/pyproject.toml b/pyproject.toml index 13831500b..76a1d8b61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,9 @@ classifiers = [ dependencies = [ "click>=8.0.0", "openadapt-flow[browser,hosted]>=1.29.0,<2.0.0", + # Day-1 partner kit: `pip install openadapt` must expose + # `openadapt-agent serve --allow-run` without a second package. + "openadapt-agent>=2.0.1,<3", ] [project.optional-dependencies] @@ -92,6 +95,11 @@ privacy = [ flow = [ "openadapt-flow>=1.29.0,<2.0.0", ] +# `openadapt-agent` also ships in the base install. Keep the extra so +# `pip install openadapt[agent]` still resolves. +agent = [ + "openadapt-agent>=2.0.1,<3", +] # Bundles core = [ "openadapt[capture,ml,evals,viewer]", @@ -100,7 +108,7 @@ all = [ # Cross-platform clients are always safe to resolve. Native bindings remain # conditional so `openadapt[all]` never pulls PyObjC onto Linux/Windows or # PyGObject onto macOS/Windows. - "openadapt[browser,core,grounding,retrieval,privacy,flow,windows,rdp]", + "openadapt[browser,core,grounding,retrieval,privacy,flow,windows,rdp,agent]", "openadapt[macos]; sys_platform == 'darwin'", "openadapt[linux]; sys_platform == 'linux'", ] diff --git a/tests/test_cli_smoke.py b/tests/test_cli_smoke.py index 8e0810e17..f8f68eb68 100644 --- a/tests/test_cli_smoke.py +++ b/tests/test_cli_smoke.py @@ -92,6 +92,8 @@ def test_quickstart_runs_one_local_lifecycle_without_overwriting(monkeypatch): assert calls[0][0] == "tutorial" assert calls[0][1] == "--out" assert calls[0][3:] == ["--name", "local-quickstart"] + assert "openadapt-agent serve --allow-run" in result.output + assert "Frames stay here" in result.output def test_quickstart_forwards_the_headed_tutorial_option(monkeypatch): @@ -136,6 +138,8 @@ def test_quickstart_forwards_the_break_it_option_and_names_the_evidence( assert calls[0][-1] == "--break-it" assert "run-broken" in result.output assert "HALTED" in result.output + assert "store unchanged" in result.output + assert "openadapt-agent serve --allow-run" in result.output def test_quickstart_restores_the_operator_scrub_setting(monkeypatch): @@ -239,8 +243,9 @@ def test_doctor_lists_quickstart_dependencies_as_core_not_extras(): core_section = out[core_idx:optional_idx] optional_section = out[optional_idx:] - # Flow and the browser tutorial driver are core. + # Flow, the agent bridge, and the browser tutorial driver are core. assert "openadapt_flow" in core_section + assert "openadapt_agent" in core_section assert "playwright" in core_section assert "playwright" not in optional_section # The excluded-by-default extras must appear only in the optional @@ -270,7 +275,9 @@ def test_launcher_flow_and_substrate_extras_metadata(): metadata["dependencies"].count("openadapt-flow[browser,hosted]>=1.29.0,<2.0.0") == 1 ) + assert metadata["dependencies"].count("openadapt-agent>=2.0.1,<3") == 1 assert extras["flow"] == ["openadapt-flow>=1.29.0,<2.0.0"] + assert extras["agent"] == ["openadapt-agent>=2.0.1,<3"] assert extras["browser"] == ["openadapt-flow[browser]>=1.29.0,<2.0.0"] assert extras["privacy"] == ["openadapt-flow[privacy]>=1.29.0,<2.0.0"] assert extras["capture"] == [ @@ -286,7 +293,7 @@ def test_launcher_flow_and_substrate_extras_metadata(): ] assert extras["rdp"] == ["openadapt-flow[rdp]>=1.29.0,<2.0.0"] assert extras["all"] == [ - "openadapt[browser,core,grounding,retrieval,privacy,flow,windows,rdp]", + "openadapt[browser,core,grounding,retrieval,privacy,flow,windows,rdp,agent]", "openadapt[macos]; sys_platform == 'darwin'", "openadapt[linux]; sys_platform == 'linux'", ] @@ -465,6 +472,7 @@ def test_top_level_help_leads_with_flow(): # Quick Start headline and Commands listing both lead with flow. assert "openadapt flow demo-record" in result.output assert "openadapt quickstart" in result.output + assert "openadapt-agent serve --allow-run" in result.output assert "effect-verified first run" in result.output assert "Standalone local human GUI capture" in result.output assert "Research: evaluate" in result.output diff --git a/tests/test_partner_kit.py b/tests/test_partner_kit.py new file mode 100644 index 000000000..929363322 --- /dev/null +++ b/tests/test_partner_kit.py @@ -0,0 +1,73 @@ +"""Day-1 partner kit: invoke Flow, keep the MockMed oracle honest. + +Launcher unit tests mock ``_invoke_flow``. That is OpenAdapt#1122-class +coverage: it proves argv forwarding, not that Flow ran. These tests call +the installed engine. The weekly ``quickstart-lifecycle`` job still owns +the full Playwright ``VERIFIED`` path. +""" + +from __future__ import annotations + +import io +import json +from contextlib import redirect_stderr, redirect_stdout +from urllib.error import HTTPError +from urllib.request import Request, urlopen + +import pytest + +from openadapt.cli import _invoke_flow + + +def test_invoke_flow_runs_the_installed_tutorial_help() -> None: + pytest.importorskip("openadapt_flow", reason="openadapt-flow not installed") + buffer = io.StringIO() + with redirect_stdout(buffer), redirect_stderr(buffer): + try: + code = _invoke_flow(["tutorial", "--help"]) + except SystemExit as exc: + code = int(exc.code or 0) + text = buffer.getvalue() + assert code == 0, text + assert "--break-it" in text + + +def test_mockmed_optimistic_fault_leaves_the_store_unchanged() -> None: + """--break-it injects this fault. The independent GET /api/db must not move.""" + pytest.importorskip("openadapt_flow", reason="openadapt-flow not installed") + from openadapt_flow.mockmed.fault_server import serve + + url, db, stop = serve() + try: + before = json.loads(urlopen(url + "api/db", timeout=2).read().decode("utf-8")) + assert before["records"] == [] + + request = Request( + url + "api/encounter?fault=optimistic", + data=json.dumps( + {"patient_id": "p1", "type": "Triage", "note": "synthetic"} + ).encode("utf-8"), + method="POST", + headers={"Content-Type": "application/json"}, + ) + with pytest.raises(HTTPError) as raised: + urlopen(request, timeout=2) + assert raised.value.code == 409 + + after = json.loads(urlopen(url + "api/db", timeout=2).read().decode("utf-8")) + assert after["records"] == [] + assert db.rejected_writes == 1 + control = Request( + url + "api/encounter?fault=ok", + data=json.dumps( + {"patient_id": "p1", "type": "Triage", "note": "synthetic"} + ).encode("utf-8"), + method="POST", + headers={"Content-Type": "application/json"}, + ) + landed = json.loads(urlopen(control, timeout=2).read().decode("utf-8")) + assert landed.get("ok") is True + stored = json.loads(urlopen(url + "api/db", timeout=2).read().decode("utf-8")) + assert len(stored["records"]) == 1 + finally: + stop() From e01e92117b82939c1f2a154849fa34c82f19c4d9 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 29 Aug 2026 15:42:37 -0400 Subject: [PATCH 2/2] chore: record openadapt-agent in platform-manifest launcher_requires pyproject.toml now pins openadapt-agent>=2.0.1,<3 as a base dependency. The committed platform-manifest.json must list that pin or the offline validate-platform-manifest job fails. Agent session, not the founder. --- platform-manifest.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform-manifest.json b/platform-manifest.json index 0dc3e1355..db8e0a9a8 100644 --- a/platform-manifest.json +++ b/platform-manifest.json @@ -1089,6 +1089,9 @@ "compatibility": { "python": ">=3.10,<3.13", "launcher_requires": { + "openadapt-agent": [ + ">=2.0.1,<3" + ], "openadapt-capture": [ ">=1.2.0,<2.0.0" ],