From ac59132bace3c79aaac70110c36848a5d4369e13 Mon Sep 17 00:00:00 2001 From: abrichr Date: Wed, 26 Aug 2026 17:53:14 -0400 Subject: [PATCH] fix: make successful quickstart the primary path --- docs/cli.md | 17 +++++++++- docs/getting-started/quickstart.md | 9 ------ openadapt/cli.py | 48 ++++++++++++++++------------ tests/test_cli_smoke.py | 50 +++++++++++++++++++++++++++--- 4 files changed, 91 insertions(+), 33 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 84754ce17..987252cc9 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -10,7 +10,7 @@ installed `openadapt-flow` engine. ## First run ```bash -openadapt quickstart [--headed] [--break-it] [--out NEW_DIRECTORY] +openadapt quickstart [--headed] [--out NEW_DIRECTORY] ``` `quickstart` runs the bundled synthetic workflow from recording through an @@ -100,6 +100,21 @@ openadapt flow push APPROVED_DERIVATIVE --kind recording Do not upload a raw recording. The push path accepts an approved sanitized derivative and validates its exact bytes. +## Advanced effect-verification simulation + +After the healthy quickstart succeeds, you can run an optional synthetic case +in which the application reports success while its independent source rejects +the write: + +```bash +openadapt quickstart --simulate-rejected-write \ + --out openadapt-quickstart-rejected-write +``` + +OpenAdapt runs the healthy tutorial first. It then uses the same certified +bundle for the simulated rejection. The independent effect check detects the +missing write and returns `HALTED`. This doesn't qualify your own workflow. + ## Capture and research commands The optional `capture` group exposes the supported low-level Capture component diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index 161ca489c..733806213 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -30,15 +30,6 @@ openadapt flow visualize openadapt-quickstart/bundle --out graph.html openadapt flow lint openadapt-quickstart/bundle ``` -Run the same certified bundle against a fault-injecting backend: - -```bash -openadapt quickstart --break-it --out openadapt-quickstart-broken -``` - -The application displays success, but the backend does not save the record. -The independent effect verifier detects the mismatch and returns `HALTED`. - ## Run the manual demo lifecycle The engine command is `openadapt-flow`. The launcher provides the equivalent diff --git a/openadapt/cli.py b/openadapt/cli.py index 99980f89d..ff02c217f 100644 --- a/openadapt/cli.py +++ b/openadapt/cli.py @@ -159,18 +159,27 @@ def _is_externally_managed_error(error: BaseException) -> bool: "--headed", is_flag=True, help="Show the browser while the tutorial runs." ) @click.option( - "--break-it", - "break_it", + "--simulate-rejected-write", + "simulate_rejected_write", is_flag=True, help=( - "After the verified run, rerun the same certified bundle against a " - "fault-injecting backend and watch the engine halt instead of " - "trusting the screen." + "After the verified run, simulate an application that reports success " + "while its independent source rejects the write." ), ) +@click.option( + "--break-it", + "deprecated_break_it", + is_flag=True, + hidden=True, +) @click.pass_context def quickstart( - command_ctx: click.Context, out: Optional[Path], headed: bool, break_it: bool + command_ctx: click.Context, + out: Optional[Path], + headed: bool, + simulate_rejected_write: bool, + deprecated_break_it: bool, ) -> None: """Run a verified local tutorial against the bundled synthetic app. @@ -185,6 +194,13 @@ def quickstart( _require_supported_python() + if deprecated_break_it: + click.echo( + "Warning: --break-it is deprecated; use --simulate-rejected-write.", + err=True, + ) + simulate_rejected_write = simulate_rejected_write or deprecated_break_it + if out is None: root = Path(_DEFAULT_QUICKSTART_DIR).resolve() suffix = 2 @@ -208,8 +224,8 @@ def quickstart( ] if headed: argv.append("--headed") - if break_it: - argv.append("--break-it") + if simulate_rejected_write: + argv.append("--simulate-rejected-write") # Engine-owned flags (--guided, --interactive-record, and future engine # additions) forward verbatim instead of being whitelisted here. argv.extend(command_ctx.args) @@ -247,21 +263,15 @@ def quickstart( "The synthetic write was confirmed through a read-only system-of-record API." ) click.echo("No model or Cloud call was enabled.") - if break_it: + if simulate_rejected_write: click.echo( - "The rerun against the fault-injecting backend HALTED as designed: " - "the screen claimed success and the system of record disagreed." + "The simulated rejected write HALTED as designed. The application " + "reported success, but the independent source rejected the write." ) - click.echo(f"Caught-fault evidence: {root / 'run-broken' / 'REPORT.md'}") + click.echo(f"Simulation evidence: {root / 'run-rejected-write' / 'REPORT.md'}") click.echo(f"Inspect qualification gaps: openadapt flow lint {root / 'bundle'}") - if not break_it: - click.echo( - "Watch it catch a lie: openadapt quickstart --break-it --out " - f"{root}-break-it" - ) click.echo( - "See a fail-safe halt: openadapt flow replay " - f"{root / 'bundle'} --drift modal --run-dir {root}-halt" + "Record your first workflow: https://docs.openadapt.ai/guides/record-your-app/" ) click.echo( "Connect this computer when you want Cloud history and collaboration: " diff --git a/tests/test_cli_smoke.py b/tests/test_cli_smoke.py index 8e0810e17..4f9c35d80 100644 --- a/tests/test_cli_smoke.py +++ b/tests/test_cli_smoke.py @@ -114,7 +114,7 @@ def test_quickstart_forwards_the_headed_tutorial_option(monkeypatch): assert calls[0][-1] == "--headed" -def test_quickstart_forwards_the_break_it_option_and_names_the_evidence( +def test_quickstart_forwards_the_rejected_write_simulation_and_names_the_evidence( monkeypatch, ): calls = [] @@ -127,17 +127,59 @@ def test_quickstart_forwards_the_break_it_option_and_names_the_evidence( with runner.isolated_filesystem(): result = runner.invoke( cli_main, - ["quickstart", "--break-it", "--out", "break-run"], + [ + "quickstart", + "--simulate-rejected-write", + "--out", + "rejected-write-run", + ], ) assert result.exit_code == 0, result.output assert len(calls) == 1 assert calls[0][0] == "tutorial" - assert calls[0][-1] == "--break-it" - assert "run-broken" in result.output + assert calls[0][-1] == "--simulate-rejected-write" + assert "run-rejected-write" in result.output assert "HALTED" in result.output +def test_quickstart_hides_and_warns_for_the_deprecated_break_it_alias(monkeypatch): + calls = [] + monkeypatch.setattr( + "openadapt.cli._invoke_flow", + lambda argv: calls.append(list(argv)) or 0, + ) + + runner = CliRunner() + help_result = runner.invoke(cli_main, ["quickstart", "--help"]) + assert help_result.exit_code == 0, help_result.output + assert "--simulate-rejected-write" in help_result.output + assert "--break-it" not in help_result.output + + with runner.isolated_filesystem(): + result = runner.invoke( + cli_main, + ["quickstart", "--break-it", "--out", "deprecated-alias-run"], + ) + + assert result.exit_code == 0, result.output + assert calls[0][-1] == "--simulate-rejected-write" + assert "Warning: --break-it is deprecated" in result.output + + +def test_quickstart_success_output_leads_to_a_real_workflow(monkeypatch): + monkeypatch.setattr("openadapt.cli._invoke_flow", lambda _argv: 0) + + runner = CliRunner() + with runner.isolated_filesystem(): + result = runner.invoke(cli_main, ["quickstart", "--out", "first-run"]) + + assert result.exit_code == 0, result.output + assert "--simulate-rejected-write" not in result.output + assert "--break-it" not in result.output + assert "https://docs.openadapt.ai/guides/record-your-app/" in result.output + + def test_quickstart_restores_the_operator_scrub_setting(monkeypatch): monkeypatch.setenv("OPENADAPT_FLOW_SCRUB", "auto") monkeypatch.setattr("openadapt.cli._invoke_flow", lambda _argv: 0)