Automation
AI
Test Automation
Stop Teaching Browser Agents the Same Workflow Twice

Stop Teaching Browser Agents the Same Workflow Twice

5 min read
🎯

The short version

  • Browser agents should explore unknown paths. A known path should become a reusable engineering asset.
  • Flow2Skill compiles a successful Playwright demonstration instead of asking a model to summarize it.
  • Each bundle contains a protected workflow contract, a portable SKILL.md, and a standalone pytest proof.
  • Unsupported behavior, missing assertions, and unsafe dynamic values fail compilation instead of being silently omitted.
  • Flow2Skill is deliberately not an autonomous browser agent or a secret store.

The expensive part of a browser workflow is not finding the path once. It is paying an agent to rediscover the same path every time.

Exploration makes sense when the page, goal, or route is unknown. Once a human has completed the flow successfully, continued improvisation becomes a reliability problem. Selectors drift between runs. The agent may choose a different route, skip an assertion, or treat a visible confirmation as proof without checking the exact state that matters.

The design decision
Use agents to discover. Use compiled workflows to repeat. The boundary should move as soon as a browser path is understood.

A successful recording is not yet reusable infrastructure

Playwright codegen is excellent at capturing what happened. It can preserve navigation, selectors, fills, clicks, and assertions as Python. But a raw recording still carries the assumptions of the original session. It may contain typed values, application specific text, unsupported control flow, or no mechanical success condition at all.

A reusable agent procedure needs more than instructions. It needs an explicit contract: what steps are allowed, which inputs must arrive at runtime, which actions need review, and what assertion proves the workflow actually finished.

Compile the demonstration, do not summarize it

Flow2Skill treats the recording as source code for a narrow compiler. It parses synchronous Playwright Python through the abstract syntax tree. The recorded file is never imported or executed during compilation. Only an allowlisted set of selectors, actions, and assertions can become part of the generated workflow.

Code
human demonstration
        |
        v
strict AST compiler
        |
        v
protected workflow contract
        |
        +--> portable SKILL.md
        +--> standalone Playwright/pytest proof

That choice is intentionally less flexible than an LLM rewrite. Flexibility is useful during discovery. A compiler needs the opposite property: it must reject anything it cannot preserve faithfully.

One capture becomes five inspectable artifacts

ArtifactPurpose
flow.jsonFingerprint validated machine contract for the workflow.
flow.yamlReadable workflow contract for review and tooling.
SKILL.mdPortable operating procedure for Hermes, Codex, Claude Code, Cursor, and other agents.
test_.pyStandalone Playwright and pytest proof with an executable assertion.
README.mdRuntime variables, review requirements, and replay instructions for the bundle.

The generated test has no runtime dependency on Flow2Skill. That matters because the proof should remain usable even if the compiler is not installed in the environment that replays the flow.

The safety work is mostly in what gets rejected

A workflow compiler becomes dangerous when it silently flattens code it does not understand. Flow2Skill fails closed on dynamic expressions, assignments, loops, branches, context managers, nested functions, multiple tests, nested locator scopes, unknown selector modifiers, unsupported calls, malformed manifests, and missing executable assertions.

  • Typed and asserted values are protected by default.
  • Protected values echoed later in selector or result text are replaced with the same runtime variable.
  • Common credential bearing URL parameters are converted into protected runtime inputs.
  • Runtime variables are restricted to the compiler owned F2S namespace.
  • A SHA-256 fingerprint detects contract modification, while the documentation is explicit that this is not a trusted publisher signature.

The result is not a promise that every browser workflow can be compiled. It is a smaller promise: supported workflows are preserved without importing the recording, leaking captured values into the bundle, or pretending unsupported behavior was handled.

Clicks are not all equal

A browser test can create real side effects. Flow2Skill marks clicks, key presses, fills, selections, and check operations for review. Actions associated with publishing, sending, buying, submitting, deleting, and similar high impact outcomes are approval gated in the generated skill.

Generated tests are disabled until FLOW2SKILL_LIVE=1 is set. Workflows with review or approval actions also require FLOW2SKILL_ALLOW_SIDE_EFFECTS=1. A missing permission fails the live test instead of reporting a false green skip.

Permission stays outside the compiler
Flow2Skill can label risk and require runtime gates. It cannot authorize a real purchase, publication, submission, or deletion. That decision still belongs to the operator.

A real browser proof in a few commands

Version 0.1.0 ships a packaged local fixture so the complete compile and replay loop can be tested without an account, an external request, a persistent write, or a production credential.

Code
python -m pip install flow2skill
python -m playwright install chromium
flow2skill demo --out ./flow2skill-demo

F2S_LABEL_API_TOKEN_1="runtime-demo-token" \
FLOW2SKILL_LIVE=1 \
FLOW2SKILL_ALLOW_SIDE_EFFECTS=1 \
pytest -q ./flow2skill-demo/test_agent_release_gate.py

The demo fills a synthetic API token field, validates the local form, and asserts the exact ready state. The side effect flag is still required because the browser performs a fill and a click. The fixture runs on loopback, makes zero external calls, and stores nothing.

The release is small enough to audit

Flow2Skill 0.1.0 supports Python 3.10 through 3.13 and pins Playwright 1.61.0. The public package includes a wheel and source archive on PyPI, an MIT licensed GitHub release, a 50 second narrated demo, and a local suite that currently passes 46 tests.

The scope is intentionally narrow. Flow2Skill does not include cloud sync, a browser extension, shared secret storage, autonomous planning, or model driven selector healing. Those could turn a focused compiler into another agent platform before the core workflow has earned trust.

Where the pattern is useful

  • A browser workflow was discovered manually and now needs repeatable agent execution.
  • A support or operations flow must be reviewable before it is handed to an agent.
  • A team wants the same procedure expressed as both human-readable instructions and executable regression evidence.
  • Captured values must stay outside the generated bundle.
  • The cost of silent miscompilation is higher than the cost of rejecting an unsupported recording.

If the route changes every run, an exploratory browser agent is still the right tool. If the route is stable and already proven, rediscovery is unnecessary entropy.

The automation architect's verdict
Do not turn every known browser task into another planning problem. Capture the successful path, compile only what can be preserved safely, and require the exported test to prove that the workflow still works.

Flow2Skill is available on GitHub, installable from PyPI, and documented with a narrated proof on the portfolio project page.

Dhiraj Das

About the Author

Dhiraj Das | Automation Consultant | 10+ years building automation systems that expose failures, reduce flakiness, and make complex workflows repeatable. He now applies that discipline independently to AI-agent validation, run replay, LLM testing, and postmortems.

He shares small open source utilities from real automation work, including: waitless (flaky tests), sb-stealth-wrapper (bot detection), selenium-teleport (state persistence), selenium-chatbot-test (AI chatbot testing), lumos-shadowdom (Shadow DOM), and visual-guard (visual regression).

Share this article: