Skip to content

feat: interactive record --url + secret-typed parameters (never persisted)#64

Merged
abrichr merged 2 commits into
mainfrom
feat/record-and-secrets
Jul 13, 2026
Merged

feat: interactive record --url + secret-typed parameters (never persisted)#64
abrichr merged 2 commits into
mainfrom
feat/record-and-secrets

Conversation

@abrichr

@abrichr abrichr commented Jul 13, 2026

Copy link
Copy Markdown
Member

Addresses two 'fix first' items from the external review.

1. Interactive record --url

openadapt-flow record --url <app> --out <dir> opens a headed browser on the user's own app and captures real clicks/typing/keys/scrolls via in-page capture-phase DOM listeners, writing the exact recording format compile already consumes. Closes the record→compile→replay self-serve loop for any app. Structured DOM identity captured in-page at click time (arms the identity ladder on interactively-recorded bundles). Reuses the existing Recorder via a new record_observed seam — format not forked.

2. Secret-typed parameters (persisted-vs-injected contract)

input[type=password] auto-detected; any field markable via --secret <name>. A secret's literal is never read into Python, never written to meta/events/bundle, and its field region is redacted (black) from persisted frames. Injected at replay from OPENADAPT_FLOW_SECRET_<PARAM>; missing secret fails fast naming the env var. Schema: ir.Step.secret + Workflow.secret_params.

Tests

962 passed, 9 skipped (pre-existing). test_secret_params.py (no-leak: no literal persisted, region redacted, env injection). test_interactive_recorder.py (headless record→compile→replay of MockMed triage: literal appears in no persisted artifact; replay fails without env var, succeeds with it).

Touches shared files (ir.py/replayer.py/compiler/main/README) — reconcile with the structural-rung, effect-integration, policy, and hygiene PRs on merge.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

…sisted)

Closes the #1 adoption gap: the README promised "record a GUI workflow once"
but the only recorder (`demo-record`) ran the hard-coded MockMed script. There
was no way to record your OWN app.

record --url:
- New `openadapt-flow record --url <app>` opens a headed browser on the user's
  own app and watches real clicks/typing/keys/scrolls via in-page capture-phase
  DOM listeners (installed with add_init_script so they survive navigations),
  writing the EXACT recording format `compile` already consumes. Stop with
  Ctrl-C or by closing the window. record -> compile -> replay now closes the
  self-serve loop for any app, not just the bundled demo.
- Architecture: the expose_binding callback only appends raw events to a Python
  list (calling any page method inside a sync-API binding callback deadlocks the
  driver); the main loop drains it and does all screenshotting. Each step's
  before-frame is the previous step's settled frame (no post-navigation race);
  type/scroll runs capture their after-frame+structural state at the moment they
  happen so a following navigating click can't corrupt them. Structured DOM
  identity is captured in-page at click time, arming the identity ladder on
  interactively-recorded bundles. Reuses the existing Recorder via a new
  `record_observed` seam — the recording format is not forked.

Secret-typed parameters:
- input[type=password] is auto-detected as secret; any field can be marked with
  `--secret <name>`. A secret's literal value is NEVER read into Python, never
  written to meta.json / events.jsonl / the compiled bundle, and its field
  region is redacted (solid black) from the persisted before/after frames.
- At replay the value is injected from OPENADAPT_FLOW_SECRET_<PARAM>; a missing
  secret fails fast with an actionable message naming the env var.
- Schema: ir.Step.secret + Workflow.secret_params; compiler carries the secret
  through with text=None; replayer resolves it from the environment.

Tests: tests/test_secret_params.py (fast unit: recorder redaction/non-persist,
compiler carry-through, replayer env injection + missing-secret error) and
tests/test_interactive_recorder.py (headless scripted record -> compile ->
replay proving the loop, no secret leak in any artifact, frame redaction, and
env injection). Full suite: 962 passed, 9 skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ
@abrichr abrichr marked this pull request as ready for review July 13, 2026 16:24
@abrichr abrichr merged commit 7f145f1 into main Jul 13, 2026
2 checks passed
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