EN / field notes OpenHands feature map

OpenHands / F22

Creating automations: templates, setup and import

A user adds an automation in three ways. The Templates sub-page (/automations/templates) lists the prebuilt catalog from @openhands/extensions (6 featured, 17 Beta) with search; picking a card either opens a manifest-driven setup dialog at /automations/new/<id> (prerequisites → form → review → Confirm and create), or first asks where a GitHub/Slack responder should run, or first asks the user to install missing MCP integrations, or seeds a conversation with the entry's launch prompt. Add Automation → Create Automation explains how to ask the agent and opens a chat pre-filled with Create an automation. Add Automation → Import automation reads an exported JSON file, previews it and creates it disabled.

29 mapped behaviors · 29 recipes and supporting checks · source snapshot 9 October 2026
From upstream main at 8793c111. Read the maintained source.

How to get to it

  • Sidebar Automate (sidebar-automations-link), then Templates in the Automate sub-page nav (automations-navbar-desktop >> automations-navigation-templates; at phone width automations-navbar-mobile). The command menu has no Templates entry (searching template gives No commands found).
  • Direct URLs /automations/templates and /automations/new/<catalog-id> (for example /automations/new/custom-automation, /automations/new/news-digest, /automations/new/qa-changes). A deep link opens the setup dialog over the page; it bypasses the responder and install-queue dialogs that a card click shows first.
  • Template cards elsewhere use the same launcher: the Recommended automations rail on home/New Chat (F03) and on the empty dashboard (F21.empty-rail), and the onboarding say-hello step (F01).
  • Dashboard header Add Automation (automations-add-automation) → Create Automation (automations-add-automation-create) or Import automation (automations-import-automation). The empty dashboard also shows the Create Automation instructions inline (automations-empty >> automations-create-automation).

Before you start

Start with the common launch and health checks, then follow this family’s preconditions in order. Recipes share the fixtures and state named below.

Preconditions:

  • Baseline state (launched, doctored, onboard --skip done) and control-openhands llm preset deepseek (the setup form's LLM profile field lists saved profiles; the agent check needs a model).
  • No automation named QA_F22 … or Daily news digest (control-openhands api GET '/api/automation/v1?limit=100'), no OPENHANDS_URL secret (control-openhands api GET /api/settings/secrets) and no MCP server installed (control-openhands api GET /api/settings, mcp_config empty). An existing OPENHANDS_URL skips the secret creation in the responder recipe; an installed Slack/Linear/Tavily server skips its install dialog.
  • Import fixtures (arrange only; each command prints the path, $OH_VERIFY_RUN/evidence/_fixtures/<name>): control-openhands fixture file --name qa-f22-import.automation.json --content '{"version":1,"kind":"automation","spec":{"name":"QA_F22 Imported","trigger":{"type":"cron","schedule":"0 0 1 1 *","timezone":"UTC"},"enabled":true,"prompt":"Reply with the single word: pong. Do not run any tools.","plugins":["github:OpenHands/extensions/plugins/qa-none"]}}', control-openhands fixture file --name qa-f22-view.automation.json --content '{"version":1,"kind":"automation","spec":{"name":"QA_F22 View","trigger":{"type":"cron","schedule":"0 0 1 1 *"},"enabled":true,"prompt":"Reply with the single word: pong. Do not run any tools."}}', control-openhands fixture file --name qa-f22-broken.json --content '{"version": 1, "kind": ' and control-openhands fixture file --name qa-f22-schema.json --content '{"version":2,"kind":"automation","spec":{"name":"","trigger":{"type":"event"},"enabled":"yes","prompt":"x"}}'.
  • Every automation created here uses the yearly cron 0 0 1 1 *, so none fires during the run.
  • Tarball fixture (arrange only): control-openhands fixture tarball --name qa-f22-tarball writes $OH_VERIFY_RUN/evidence/_fixtures/qa-f22-tarball.tar.gz (one main.py that prints pong).
  • Blocked here: completing F22.template-mcp-install-queue (and launching a card whose only missing integration is installable, such as research-brief-writer) needs a real Slack, Linear or Tavily credential: the install dialog tests the connection before saving.

Behavior inventory

29 stable behavior IDs and their expected behavior
  • F22.templates-page /automations/templates shows the h1 Templates, a description, a search box, Start from a proven workflow with a count badge (6) and a Beta section (17). Read recipe ↓
  • F22.template-card each card shows an icon or logo stack, name, category, two-line description, a plus badge and integration pills (N MCPs to connect before launch, Needs external setup for Jira/Bitbucket; Connected once installed, which needs a working integration and was not reached here); a +N overflow button opens a popover without launching the card. Read recipe ↓
  • F22.template-launch-setup a card whose entry ships a setup form (custom-automation, news-digest, github-pr-reviewer, qa-changes, …) navigates to /automations/new/<id>. Read recipe ↓
  • F22.template-launch-conversation a card without a setup form and with no installable integration missing creates a conversation and pre-fills its composer with the entry's launch command (for example /ticket-to-code-change:setup); nothing is sent until the user presses send. Read recipe ↓
  • F22.template-mcp-install-queue a card with missing required integrations opens each one's install dialog in turn; Cancel abandons the launch without creating anything. Read recipe ↓
  • F22.responder-deployment-choice on a local backend a GitHub/Slack-only responder card first asks Choose how your responder should run; Continue with local setup creates the OPENHANDS_URL secret and continues, Open OpenHands Cloud integrations opens Cloud in a new tab, X and Escape cancel. Read recipe ↓
  • F22.responder-local-launch with the responder's integration already installed (Slack for Slack standup digest), Continue with local setup skips the install dialog and opens a new conversation whose composer is pre-filled with the entry's setup command (/standup-digest:setup). Nothing is sent until the user presses send; Send delivers it as the user message. Read recipe ↓
  • F22.setup-prerequisites Before you start lists the entry's integrations with the reason each is needed and Manage integrations (/mcp); Continue is disabled while a required integration is missing; an entry with nothing to check skips the step. Read recipe ↓
  • F22.setup-action-kind the Action combobox (custom-automation: Prompt / Plugin / Upload tarball) swaps the action fields and keeps shared values. Read recipe ↓
  • F22.setup-form-validation Continue checks fields locally (This field is required., Must be at least N characters., Must be at most N.) and a field blur sends the draft to the service, whose errors appear under the field (Value error, Invalid cron expression: …). Read recipe ↓
  • F22.setup-agent-profile the Agent profile combobox appears only for actions that create through /v1 (Upload tarball and bundle entries), not for Prompt or Plugin; it lists Default and every saved agent profile and shows Default while no profile is chosen, picking a profile hides the LLM profile field, switching to Prompt or Plugin clears the choice, and the automation is created with that agent_profile_id. Read recipe ↓
  • F22.setup-plugin-create the Plugin action takes plugin sources and a prompt, and a picked Timezone carries through Review into the created automation (preset_metadata.plugins, trigger.timezone). Read recipe ↓
  • F22.setup-tarball-create the Upload tarball action requires a .tar/.tar.gz file, shows its file name in Review and creates an automation with the uploaded tarball and the entrypoint. Read recipe ↓
  • F22.setup-repository-list a repository list field has Add (disabled while the input is empty) and a Remove per row; an empty required list is refused. Read recipe ↓
  • F22.setup-review-confirm Review lists every value (Not set for blanks); Back keeps the values; Confirm and create creates the automation (enabled) and replaces the URL with its detail page. Read recipe ↓
  • F22.setup-bundle-create a bundle entry (Daily news digest) uploads its script tarball and creates an automation with the bundle's entrypoint, without any account. Read recipe ↓
  • F22.setup-unsupported an entry this deployment cannot run (qa-changes needs event triggers) shows Not available, the unmet requirements, Close and Set up in a conversation. Read recipe ↓
  • F22.setup-fallback-conversation Set up in a conversation creates a conversation seeded with the entry's setup message. Read recipe ↓
  • F22.setup-close X or Escape goes back in history; on a cold deep link it replaces the location with /automations. Read recipe ↓
  • F22.setup-unknown-id /automations/new/<id> for an id no catalog entry claims is a 404. Read recipe ↓
  • F22.create-helper-conversation Add Automation → Create Automation opens How to create an automation; its Create Automation closes the dialog, opens /conversations and pre-fills the composer with Create an automation; an agent asked to create an automation creates it. Read recipe ↓
  • F22.import-picker Add Automation → Import automation opens a dialog that explains the format (docs link), says the import is disabled, and offers a dropzone and Choose file. Read recipe ↓
  • F22.import-validation a file that is not JSON shows the toast The selected file is not valid JSON.; a JSON file that breaks the format shows one toast listing every issue; the dialog stays on the picker. Read recipe ↓
  • F22.import-preview a valid file switches the dialog to a preview (Name, Trigger, Prompt, Plugins) with the disabled notice, Cancel and Import automation. Read recipe ↓
  • F22.import-confirm Import automation reads Importing... while pending, closes the dialog, shows Imported "<name>" as disabled. View automation, and the automation is listed disabled; the toast link opens its detail page. Read recipe ↓
  • F22.import-choose-file Choose file, and a click anywhere on the dropzone, open the browser's file chooser; the chosen file is previewed. Read recipe ↓
  • F22.import-dropzone dragging a file over the dropzone highlights it (data-active="true"); dropping a .json file previews it like Choose file. Read recipe ↓
  • F22.phone the Templates grid, the setup dialog and the import dialog fit a 390 px viewport without horizontal overflow. Read recipe ↓

Readable recipes

Read each script from top to bottom. Code is copied from the map; prose gives the action, expected observation, and conditions. <id>, <run> and similar placeholders stand for values from your own run. Short forms such as browser count continue the same control-openhands invocation; they are kept as documented.

Expected observations describe the recipe’s contract. Captures below selected recipes show representative real states from this snapshot; they do not mark every mapped behavior as passed. Follow cleanup before moving to another family.

Templates page #

  1. Note
    From / run
  2. Check
    control-openhands browser click 'testid=sidebar-automations-link' --expect-url '/automations$'
  3. Do
    control-openhands browser click 'testid=automations-navbar-desktop >> testid=automations-navigation-templates' --expect-url '/automations/templates'
  4. Check
    control-openhands browser text 'role=main >> role=heading[level=1]'

    (Templates),

  5. Check
    control-openhands browser count '[data-testid^="recommended-automation-card-"]'

    (23) and

  6. Check
    control-openhands browser count 'testid=recommended-automations-beta-section >> [data-testid^="recommended-automation-card-"]'

    (17); browser text 'testid=recommended-automations-beta-heading' is Beta and 17.

  7. Check
    control-openhands browser snapshot 'role=main' --max-lines 60

    shows heading "Start from a proven workflow" with the count 6.

  8. Note
    Take
  9. Do
    control-openhands browser screenshot --feature F22.templates-page --name grid
  10. Do
    control-openhands browser goto /automations/templates
  11. Note
    renders the same page.
Templates page showing recommended workflows and automation template cards.
Templates provide ready workflows and a Custom automation entry. CLI capture · 1440 × 1000 · 9 October 2026 · Canvas 8793c111

The Templates page displays its workflow grid and Custom automation card.

Templates browse only; no template creates an automation.

How this screenshot was taken

agent server: 1.53.0 · automation: 1.19.0 (launcher default) · canvas: 1.26.0

control-openhands browser goto /automations/templates
control-openhands browser wait testid=recommended-automation-card-custom-automation --timeout 10000
control-openhands browser screenshot --feature F22.templates-page --name templates

Cards and pills #

  1. Check
    control-openhands browser testids 'testid=recommended-automation-card-research-brief-writer' --hidden
  2. Note
    : recommended-automation-icon-…, recommended-automation-plus-…, recommended-automation-pills-… and recommended-automation-pills-research-brief-writer-overflow (Show 1 more).
  3. Check
    control-openhands browser testids 'role=main' --hidden --filter integration

    lists automation-integration-external-jira and automation-integration-external-bitbucket (Needs external setup).

  4. Do
    control-openhands browser click 'testid=recommended-automation-pills-research-brief-writer-overflow'
  5. Note
    opens a popover (browser snapshot 'role=dialog': dialog: 2 MCPs to connect before launch) and the URL stays /automations/templates;
  6. Do
    control-openhands browser press Escape
  7. Note
    closes it (browser count 'role=dialog' 0).

Search #

  1. Do
    control-openhands browser fill 'role=main >> placeholder=Search automations...' tavily
  2. Check
    control-openhands browser testids 'role=main' --filter recommended-automation-card
  3. Note
    : only recommended-automation-card-research-brief-writer (matched through its Tavily integration).
  4. Do
    control-openhands browser fill 'role=main >> placeholder=Search automations...' qa-no-such-template
  5. Check
    control-openhands browser count 'testid=recommended-automations-section'
  6. Do
    control-openhands browser screenshot --feature F22.templates-search --name no-match
  7. Note
    Expected: a "no templates match" message.
  8. Note
    Actual: the count is 0 and browser text 'role=main' is only the title and description; the area under the search box is blank (recorded as fail).
  9. Note
    Clear with
  10. Do
    control-openhands browser fill 'role=main >> placeholder=Search automations...' ''

    (23 cards again).

Card opens setup, prerequisites #

  1. Check
    control-openhands browser click 'testid=recommended-automation-card-custom-automation' --expect-url '/automations/new/custom-automation'
  2. Check
    control-openhands browser snapshot 'testid=setup-dialog'
  3. Note
    : heading Before you start, GitHub Optional. Used when you choose a GitHub repository…, link Manage integrations (/mcp) and Continue;
  4. Check
    control-openhands browser enabled 'testid=setup-continue-button'

    is true (an optional integration only warns).

  5. Do
    control-openhands browser click 'testid=setup-continue-button'
  6. Note
    the heading becomes Custom automation and browser testids 'testid=setup-dialog' lists setup-action-kind, setup-field-schedule (0 9 * * *), setup-field-timezone (UTC), setup-field-name, setup-field-model, setup-field-timeout, setup-field-prompt, setup-field-repository and setup-field-ref.
  7. Expect
    There is no Trigger selector here because this deployment only supports cron, and no automation-agent-profile because the default Prompt action cannot take one (F22.setup-agent-profile).
Custom automation form showing Action, Schedule, Timezone, Automation name and LLM profile.
Custom setup collects the action and its settings after optional prerequisites. CLI capture · 1440 × 1000 · 9 October 2026 · Canvas 8793c111

Custom automation card opens prerequisite step; Continue reveals setup fields.

Setup form was not submitted. API-arranged dummy automations remain disabled; no external integrations installed.

How this screenshot was taken

agent server: 1.53.0 · automation: 1.19.0 (launcher default) · canvas: 1.26.0

control-openhands browser goto /automations/templates
control-openhands browser click testid=recommended-automation-card-custom-automation --expect-url /automations/new/custom-automation
control-openhands browser click testid=setup-continue-button
control-openhands browser screenshot --feature F22.template-launch-setup --name custom-setup

Validation #

  1. Note
    Click testid=setup-continue-button with the form untouched:
  2. Check
    control-openhands browser text 'testid=setup-field-name-error'
  3. Note
    and ... 'testid=setup-field-prompt-error' are This field is required. Fill testid=setup-field-timeout with 5000, testid=setup-field-name with QA_F22 Custom, testid=setup-field-prompt with Reply with the single word: pong. Do not run any tools., click Continue: browser text 'testid=setup-field-timeout-error' is Must be at most 1800. Fill the timeout with 60.
  4. Note
    Service preflight:
  5. Do
    control-openhands browser fill 'testid=setup-field-schedule' 'every day'
  6. Do
    control-openhands browser press Tab
  7. Wait
    control-openhands browser wait 'testid=setup-field-schedule-error' --timeout 5000
  8. Note
    then browser text 'testid=setup-field-schedule-error': Value error, Invalid cron expression: every day (from POST /api/automation/v1/validate, see browser network --last 5).
  9. Note
    Fill the schedule with 0 0 1 1 *, press Tab, and
  10. Wait
    control-openhands browser wait 'testid=setup-field-schedule-error' --state detached --timeout 5000
  11. Note
    succeeds.

Action kinds #

  1. Do
    control-openhands browser click 'testid=setup-action-kind'
  2. Check
    control-openhands browser snapshot 'role=listbox'

    (Prompt selected, Plugin, Upload tarball); the snapshot closes the listbox, so click testid=setup-action-kind again and then

  3. Do
    control-openhands browser click 'role=listbox >> role=option[name="Plugin"]'
  4. Note
    : browser testids 'testid=setup-dialog' now includes setup-field-plugins.
  5. Note
    Choose Upload tarball the same way: setup-field-tarball, setup-field-entrypoint and setup-field-setupScript replace prompt, repository and ref.
  6. Note
    Choose Prompt again;
  7. Check
    control-openhands browser value 'testid=setup-field-name'

    is still QA_F22 Custom and setup-field-prompt still holds the prompt.

Agent profile #

  1. Note
    In the same form (Prompt action, name QA_F22 Custom, prompt and schedule 0 0 1 1 * filled),
  2. Check
    control-openhands browser count 'testid=automation-agent-profile'

    is 0; after

  3. Do
    control-openhands browser choose 'testid=setup-action-kind' 'Plugin'
  4. Note
    it is still 0, and after
  5. Do
    control-openhands browser choose 'testid=setup-action-kind' 'Upload tarball'
  6. Note
    it is 1 (only actions that create through /v1 can carry a profile; the preset endpoints behind Prompt and Plugin reject agent_profile_id).
  7. Check
    control-openhands browser value 'testid=automation-agent-profile'
  8. Do
    control-openhands browser click 'testid=automation-agent-profile'
  9. Check
    control-openhands browser snapshot 'role=listbox'
  10. Note
    Expected: the value is Default and the listbox has option "Default" [selected] and option "default" (the seeded agent profile from api GET /api/agent-profiles), the same as on a fresh Upload tarball form (F22.setup-tarball-create).
  11. Note
    Known failure: the value is "" and option "Default" has no [selected]: switching to Prompt or Plugin stores an empty profile id, and the selector shows that as a blank field instead of Default (#18061).
  12. Do
    control-openhands browser press Escape
  13. Note
    closes the listbox and leaves the dialog open (browser count 'testid=setup-dialog' is 1).
  14. Do
    control-openhands browser choose 'testid=automation-agent-profile' 'default'
  15. Note
    : browser count 'testid=setup-field-model' is 0 (LLM profile hidden); browser choose 'testid=automation-agent-profile' 'Default' brings it back (1).
  16. Note
    Pick default again, then
  17. Do
    control-openhands browser choose 'testid=setup-action-kind' 'Prompt'
  18. Note
    : the selector is gone and setup-field-model is back (1).
  19. Note
    Choose Upload tarball once more and run
  20. Check
    control-openhands browser value 'testid=automation-agent-profile'
  21. Note
    Expected: Default (the choice was cleared, shown as on a fresh form).
  22. Note
    Known failure: "", the same blank field (choosing Default from the list blanks it too) (#18061).
  23. Note
    Choose Prompt again before continuing.
  24. Note
    Creating an automation with a profile is in the Upload tarball bullet below.

Review and create #

  1. Do
    control-openhands browser click 'testid=setup-field-model'
  2. Do
    control-openhands browser click 'role=listbox >> role=option[name="deepseek-flash"]'
  3. Do
    control-openhands browser click 'testid=setup-continue-button'
  4. Wait
    control-openhands browser wait 'testid=setup-review' --timeout 10000
  5. Check
    control-openhands browser snapshot 'testid=setup-dialog'
  6. Note
    : heading Review, terms Action Prompt, Schedule 0 0 1 1 *, Timezone UTC, Automation name, LLM profile deepseek-flash, Timeout 60, Prompt, Repository Not set, Branch or ref Not set, and buttons Back and Confirm and create.
  7. Note
    Take browser screenshot --feature F22.setup-review-confirm --name review.
  8. Do
    control-openhands browser click 'testid=setup-back-button'

    returns to the form with setup-field-name still QA_F22 Custom; click Continue, wait for setup-review again, then

  9. Check
    control-openhands browser click 'testid=setup-continue-button' --expect-url '/automations/[0-9a-f-]{8,}'
  10. Note
    browser url is /automations/<id> (the id of the new automation); after
  11. Do
    control-openhands browser reload
  12. Check
    control-openhands browser text 'role=heading[level=1]'

    is QA_F22 Custom and browser text 'testid=active-status-badge-active' is Active (the detail page has no main landmark: 'role=main >> …' times out there), and api GET '/api/automation/v1?limit=100' shows it enabled: true, trigger.schedule 0 0 1 1 *, timeout 60, model deepseek-flash.

  13. Do
    control-openhands browser back
  14. Note
    lands on /automations/templates (the setup URL was replaced).

Plugin action and timezone #

  1. Do
    control-openhands browser goto /automations/new/custom-automation
  2. Note
    click testid=setup-continue-button, then
  3. Do
    control-openhands browser choose 'testid=setup-action-kind' 'Plugin'
  4. Do
    control-openhands browser choose 'testid=setup-field-timezone' 'Europe/Berlin'
  5. Note
    Fill testid=setup-field-schedule with 0 0 1 1 *, testid=setup-field-name with QA_F22 Plugin, testid=setup-field-plugins with github:OpenHands/extensions/plugins/qa-none and testid=setup-field-prompt with Reply with the single word: pong. Do not run any tools.; click Continue and browser wait 'testid=setup-review' --timeout 10000. browser snapshot 'testid=setup-dialog': Action Plugin, Timezone Europe/Berlin, LLM profile Not set, Timeout Not set, Plugins github:OpenHands/extensions/plugins/qa-none.
  6. Check
    control-openhands browser click 'testid=setup-continue-button' --expect-url '/automations/[0-9a-f-]{8,}' --timeout 60000
  7. Note
    api GET '/api/automation/v1?limit=100' shows QA_F22 Plugin enabled with trigger.timezone Europe/Berlin and preset_metadata {preset_type: plugin, plugins: [{source: github:OpenHands/extensions/plugins/qa-none}]}.

Upload tarball action, with an agent profile #

  1. Do
    control-openhands browser goto /automations/new/custom-automation
  2. Note
    click testid=setup-continue-button,
  3. Do
    control-openhands browser choose 'testid=setup-action-kind' 'Upload tarball'

    (Entrypoint defaults to python3 main.py, and on this fresh form control-openhands browser value 'testid=automation-agent-profile' is Default), fill testid=setup-field-name with QA_F22 Tarball and testid=setup-field-schedule with 0 0 1 1 *, click Continue: browser text 'testid=setup-field-tarball-error' is This field is required. Run

  4. Do
    control-openhands browser upload 'testid=setup-field-tarball' "$OH_VERIFY_RUN/evidence/_fixtures/qa-f22-tarball.tar.gz"
  5. Do
    control-openhands browser choose 'testid=automation-agent-profile' 'default'

    (browser count 'testid=setup-field-model' is 0); take

  6. Do
    control-openhands browser screenshot 'testid=setup-dialog' --feature F22.setup-agent-profile --name upload-profile
  7. Note
    Click Continue, browser wait 'testid=setup-review' --timeout 10000: Review shows Action Upload tarball, LLM profile Not set, Tarball qa-f22-tarball.tar.gz, Entrypoint python3 main.py, Setup script Not set (Review does not list the agent profile).
  8. Check
    control-openhands browser click 'testid=setup-continue-button' --expect-url '/automations/[0-9a-f-]{8,}' --timeout 60000
  9. Note
    browser text 'role=heading[level=1]' is QA_F22 Tarball, and
  10. Check
    control-openhands api GET /api/automation/v1/<id>

    (the id in the URL) shows it enabled with entrypoint python3 main.py, a tarball_path oh-internal://uploads/…, model null and agent_profile_id equal to the default profile's id from

  11. Check
    control-openhands api GET /api/agent-profiles

Close #

  1. Note
    From /automations/templates (control-openhands browser goto /automations/templates; the previous bullet ends on the new automation's page),
  2. Check
    control-openhands browser click 'testid=recommended-automation-card-news-digest' --expect-url '/automations/new/news-digest'
  3. Check
    control-openhands browser click 'testid=setup-dialog-close' --expect-url '/automations/templates'
  4. Note
    the same with
  5. Do
    control-openhands browser press Escape
  6. Note
    instead of X also returns to /automations/templates.
  7. Note
    Cold deep link:
  8. Do
    control-openhands browser goto /automations/new/news-digest
  9. Check
    control-openhands browser click 'testid=setup-dialog-close' --expect-url '/automations$'

    (replaced, not back).

Bundle entry, min length #

  1. Note
    From /automations/templates (control-openhands browser goto /automations/templates; Close ends on /automations) click testid=recommended-automation-card-news-digest (--expect-url '/automations/new/news-digest'): there is no prerequisites step and the form shows automation-agent-profile (a bundle creates through /v1, so it offers a profile; leave it on Default), setup-field-schedule (0 8 * * *), setup-field-timezone, setup-field-feeds (three default feeds) and setup-field-topics.
  2. Note
    Fill testid=setup-field-feeds with short and click Continue: browser text 'testid=setup-field-feeds-error' is Must be at least 8 characters. Close (testid=setup-dialog-close), click the card again (the draft is gone), fill testid=setup-field-schedule with 0 0 1 1 *, click Continue, browser wait 'testid=setup-review' --timeout 10000, then
  3. Check
    control-openhands browser click 'testid=setup-continue-button' --expect-url '/automations/[0-9a-f-]{8,}' --timeout 60000
  4. Expect
    The detail h1 is Daily news digest; api GET '/api/automation/v1?limit=100' shows it enabled with entrypoint python3 main.py, a tarball_path oh-internal://uploads/… and timeout 900.

Repository list #

  1. Do
    control-openhands browser goto /automations/new/github-pr-reviewer

    (no prerequisites step locally).

  2. Check
    control-openhands browser enabled 'testid=setup-list-repositories-add'

    is false; fill testid=setup-field-repositories with qa-org/qa-repo (enabled turns true), click testid=setup-list-repositories-add: browser text 'testid=setup-list-repositories' starts qa-org/qa-repo / Remove and the input is empty again.

  3. Do
    control-openhands browser click 'testid=setup-list-repositories-remove-qa-org/qa-repo'
  4. Note
    removes it (count 0); Continue then shows setup-field-repositories-error This field is required. Close the dialog.

Blocking prerequisite #

  1. Do
    control-openhands browser goto /automations/new/incident-retrospective-drafter
  2. Check
    control-openhands browser snapshot 'testid=setup-dialog'

    (Slack, Linear and Notion with reasons) and

  3. Check
    control-openhands browser enabled 'testid=setup-continue-button'
  4. Note
    : false while Slack and Linear are not installed.

Not available and fallback #

  1. Do
    control-openhands browser goto /automations/new/qa-changes
  2. Check
    control-openhands browser snapshot 'testid=setup-dialog'
  3. Note
    : heading Not available, This deployment does not support everything this automation needs., setup-unmet-requirements webhookDelivery, event, buttons Close and Set up in a conversation.
  4. Check
    control-openhands browser click 'testid=setup-fallback-conversation' --expect-url '/conversations/[0-9a-f]' --timeout 60000
  5. Do
    control-openhands browser eval "document.querySelector('[data-testid=chat-input]').innerText"
  6. Note
    : /qa-changes, a blank line and This deployment cannot run the webhook-driven QA automation directly. Set it up in this conversation instead: ….
  7. Expect
    The message is pre-filled, not sent (no model cost until the user presses send).

Unknown id #

  1. Do
    control-openhands browser goto /automations/new/qa-no-such-template
  2. Check
    control-openhands browser snapshot
  3. Note
    : heading "404" and Not Found.

Responder choice #

  1. Note
    On /automations/templates (control-openhands browser goto /automations/templates; the previous bullet ends on a 404 page) run
  2. Do
    control-openhands browser click 'testid=recommended-automation-card-slack-channel-monitor'
  3. Check
    control-openhands browser snapshot 'testid=responder-deployment-modal'
  4. Note
    : Choose how your responder should run, Poll locally on your laptop / Continue with local setup, Use OpenHands Cloud / Open OpenHands Cloud integrations.
  5. Do
    control-openhands browser press Escape
  6. Note
    and, after reopening,
  7. Do
    control-openhands browser click 'testid=responder-deployment-modal-close'
  8. Note
    each make browser count 'testid=responder-deployment-modal' 0.
  9. Note
    Reopen and
  10. Do
    control-openhands browser click 'testid=responder-deployment-open-openhands-cloud'
  11. Note
    : the modal closes and
  12. Do
    control-openhands browser tabs

    lists tab 1 at https://app.all-hands.dev/settings/integrations (it can take a second to appear; rerun browser tabs).

  13. Note
    Close it with
  14. Do
    control-openhands browser close-tab 1
  15. Note
    Reopen and
  16. Do
    control-openhands browser click 'testid=responder-deployment-continue-local'
  17. Wait
    control-openhands browser wait 'testid=responder-deployment-modal' --state detached --timeout 10000
  18. Note
    : the Slack install dialog (testid=mcp-install-modal, dialog "Slack") opens next and api GET /api/settings/secrets now lists OPENHANDS_URL.
  19. Note
    Close it with
  20. Do
    control-openhands browser click 'testid=mcp-install-cancel'

Responder launch with Slack installed #

  1. Note
    Arrange Slack as installed with its catalog command, a dummy token and offline npm, so nothing is downloaded or contacted (arrange, not proof): write qa-slack.json (outside the workspace) containing {"agent_settings_diff":{"mcp_config":{"slack":{"command":"npx","args":["-y","@zencoderai/slack-mcp-server"],"env":{"SLACK_BOT_TOKEN":"xoxb-qa-dummy","SLACK_TEAM_ID":"T0000000000","npm_config_offline":"true"}}}}} and run
  2. Arrange
    control-openhands api PATCH /api/settings --data @qa-slack.json --write
  3. Note
    On /automations/templates run
  4. Do
    control-openhands browser click 'testid=recommended-automation-card-slack-standup-digest'
  5. Wait
    control-openhands browser wait 'testid=responder-deployment-modal'
  6. Do
    control-openhands browser click 'testid=responder-deployment-continue-local'
  7. Wait
    control-openhands browser wait-url '/conversations/[0-9a-f-]+' --timeout 60000

    (note <slack-id>).

  8. Check
    control-openhands browser count 'testid=mcp-install-modal'

    is 0.

  9. Expect
    After sleep 5,
  10. Do
    control-openhands browser eval "document.querySelector('[data-testid=chat-input]').innerText"

    is /standup-digest:setup and

  11. Check
    control-openhands browser count 'testid=user-message'

    is 0 (control-openhands browser screenshot --feature F22.responder-local-launch --name slack-prefilled).

  12. Do
    control-openhands browser click 'testid=submit-button'
  13. Wait
    control-openhands browser wait 'testid=user-message >> has-text=/standup-digest:setup' --timeout 15000
  14. Do
    control-openhands conversation pause <slack-id>
  15. Note
    at once.
  16. Expect
    The setup skill is open-ended.
  17. Note
    On 2026-10-08, deepseek-flash spent 25 tool calls exploring the host, including files outside the run, before asking a setup question.
  18. Note
    Restore with
  19. Arrange
    control-openhands api DELETE /api/settings/mcp/slack --write
  20. Check
    control-openhands api GET /api/settings --pick agent_settings.mcp_config

    is {}.

  21. Note
    Run this bullet after Responder choice, which needs Slack not installed.

Install queue #

  1. Do
    control-openhands browser click 'testid=recommended-automation-card-linear-triage-assistant'
  2. Note
    : dialog "Linear" (URL https://mcp.linear.app/mcp, optional API key).
  3. Do
    control-openhands browser click 'testid=mcp-install-cancel'
  4. Note
    browser count 'role=dialog' is 0, the URL stays /automations/templates,
  5. Do
    control-openhands conversation list

    has no new conversation and api GET /api/settings still has an empty mcp_config.

  6. Note
    Completing the queue is blocked: Install first tests the connection (Linear without a key: Connection failed: HTTPStatusError: Client error '401 Unauthorized'…; Tavily research-brief-writer with a dummy key: Connection timed out. Check the URL and try again.), so it never launches without a working credential.

Prompt launch #

  1. Note
    Use a card whose required integrations all need external setup, so no install dialog queues: on /automations/templates run
  2. Do
    control-openhands browser click 'testid=recommended-automation-pills-jira-issue-to-bitbucket-pr-overflow'

    (dialog: Bitbucket Needs external setup),

  3. Do
    control-openhands browser press Escape
  4. Check
    control-openhands browser click 'testid=recommended-automation-card-jira-issue-to-bitbucket-pr' --expect-url '/conversations/[0-9a-f]' --timeout 60000
  5. Do
    control-openhands browser eval "document.querySelector('[data-testid=chat-input]').innerText"

    is /ticket-to-code-change:setup, and

  6. Check
    control-openhands conversation events <id> --kinds MessageEvent --from-start

    (id from the URL) has total 0 even 20 s later: the launch command is pre-filled, not sent (the featured copy promises "a pre-filled conversation").

  7. Note
    Do not press send (no model cost).
  8. Note
    Take browser screenshot --feature F22.template-launch-conversation --name prefilled.
  9. Note
    Cards with an installable missing integration (research-brief-writer, Tavily) go through the install queue first and are blocked here (see Preconditions).

Create Automation helper #

  1. Note
    On /automations (control-openhands browser goto /automations; Prompt launch ends on a conversation) run
  2. Do
    control-openhands browser click 'testid=automations-add-automation'
  3. Do
    control-openhands browser click 'testid=automations-add-automation-create'
  4. Check
    control-openhands browser text 'testid=add-automation-modal >> testid=automations-create-instructions-example'

    (Create an automation), then

  5. Do
    control-openhands browser click 'testid=add-automation-modal >> testid=automations-create-automation' --expect-url '/conversations'
  6. Do
    control-openhands browser eval "document.querySelector('[data-testid=chat-input]').innerText"
  7. Note
    : the composer holds Create an automation, it has focus (control-openhands browser eval "document.activeElement.getAttribute('data-testid')" is chat-input), and the dialog is gone (control-openhands browser count 'testid=add-automation-modal' is 0).
  8. Note
    Take
  9. Do
    control-openhands browser screenshot --feature F22.create-helper-conversation --name prefilled
  10. Note
    Nothing is sent until the user presses send.
  11. Expect
    The agent half (conversation start reloads / and replaces the composer text):
  12. Wait
    control-openhands conversation start --prompt "Create an automation now, without asking questions: name QA_F22 Agent, cron schedule 0 0 1 1 * in UTC, prompt: Reply with the single word pong. Then reply with only its id." --wait --timeout 400
  13. Note
    finishes, and
  14. Do
    control-openhands browser goto /automations
  15. Check
    control-openhands browser count '[data-testid^="automation-card-"] >> has-text=QA_F22 Agent'

    is 1.

Import picker #

  1. Note
    On /automations run
  2. Do
    control-openhands browser click 'testid=automations-add-automation'
  3. Do
    control-openhands browser click 'testid=automations-import-automation'
  4. Check
    control-openhands browser attr 'testid=import-automation-modal' data-view

    (picker) and

  5. Check
    control-openhands browser snapshot 'testid=import-automation-modal'
  6. Note
    : heading Import automation, the explanation with link File format documentation (https://docs.openhands.dev/openhands/usage/agent-canvas/managing-automations#exported-file-format), The imported automation will be disabled until you review and enable it., Drop a .json file here, or, Choose file.

Bad files #

  1. Note
    In the open picker run
  2. Do
    control-openhands browser upload 'testid=automations-import-file' "$OH_VERIFY_RUN/evidence/_fixtures/qa-f22-broken.json"
  3. Check
    control-openhands browser toasts
  4. Note
    : The selected file is not valid JSON. Then upload qa-f22-schema.json the same way: one toast reads version: expected 1, spec.name: expected a non-empty string, spec.trigger.source: required for an event trigger, spec.trigger.on: required for an event trigger, spec.enabled: expected a boolean (one per line). data-view stays picker.

Preview and cancel #

  1. Note
    Upload qa-f22-import.automation.json: data-view is preview and browser snapshot 'testid=import-automation-modal' shows Review the automation details before importing., Name QA_F22 Imported, Trigger 0 0 1 1 * · UTC, the Prompt, Plugins github:OpenHands/extensions/plugins/qa-none, the disabled notice, Cancel and Import automation.
  2. Do
    control-openhands browser click 'testid=import-automation-cancel'
  3. Note
    closes it (count 0); reopening shows data-view picker again.

Import #

  1. Note
    Reopen the dialog, upload qa-f22-import.automation.json, then
  2. Do
    control-openhands browser click 'testid=import-automation-confirm' --observe 'testid=import-automation-confirm' --observe-ms 3000

    (observed Importing... for about 50 ms, then <absent>) and

  3. Check
    control-openhands browser toasts
  4. Note
    : Imported "QA_F22 Imported" as disabled. View automation.
  5. Expect
    The card [data-testid^="automation-card-"] >> has-text=QA_F22 Imported is under Inactive, and api GET /api/automation/v1/<id> has enabled: false, state INACTIVE and the plugin in preset_metadata.plugins.
  6. Expect
    The toast lasts about 5 s, so follow its link in the very next command: import qa-f22-view.automation.json the same way, then immediately
  7. Check
    control-openhands browser click 'role=status >> role=link[name="View automation"]' --expect-url '/automations/[0-9a-f-]{8,}'
  8. Note
    browser text 'role=heading[level=1]' is QA_F22 View.

Choose file #

  1. Note
    On /automations (control-openhands browser goto /automations; Import ends on QA_F22 View's page) open Add Automation → Import automation and run
  2. Do
    control-openhands browser upload-via 'testid=import-automation-choose-file' "$OH_VERIFY_RUN/evidence/_fixtures/qa-f22-import.automation.json"

    (answers the real file chooser): browser attr 'testid=import-automation-modal' data-view is preview.

  3. Note
    Close with testid=import-automation-modal-close, reopen, and
  4. Do
    control-openhands browser upload-via 'testid=import-automation-dropzone >> text=Drop a .json file here' "$OH_VERIFY_RUN/evidence/_fixtures/qa-f22-import.automation.json"
  5. Note
    : preview again.
  6. Do
    control-openhands browser press Escape
  7. Note
    closes the dialog (browser count 'testid=import-automation-modal' 0).

Drop a file #

  1. Note
    Open the import dialog again and run
  2. Do
    control-openhands browser drop-files 'testid=import-automation-dropzone' "$OH_VERIFY_RUN/evidence/_fixtures/qa-f22-import.automation.json" --stage over
  3. Note
    : browser attr 'testid=import-automation-dropzone' data-active is true (focus border).
  4. Note
    Close and reopen the dialog, then
  5. Do
    control-openhands browser drop-files 'testid=import-automation-dropzone' "$OH_VERIFY_RUN/evidence/_fixtures/qa-f22-import.automation.json"
  6. Note
    : data-view is preview with Name QA_F22 Imported (browser text 'testid=import-automation-modal').
  7. Note
    Close with testid=import-automation-modal-close.

Phone #

  1. Do
    control-openhands browser viewport phone
  2. Do
    control-openhands browser goto /automations/templates
  3. Check
    control-openhands browser bbox 'testid=recommended-automations-section'

    (pageHorizontalOverflow false) and browser screenshot --feature F22.phone --name templates.

  4. Note
    Click testid=recommended-automation-card-custom-automation, then testid=setup-continue-button; browser bbox 'testid=setup-dialog' is insideViewport true with no page overflow (--name setup).
  5. Note
    Close it,
  6. Do
    control-openhands browser goto /automations
  7. Note
    open Add Automation → Import automation, upload qa-f22-import.automation.json; browser bbox 'testid=import-automation-modal' is insideViewport true (--name import-preview).
  8. Note
    Close with testid=import-automation-modal-close, then
  9. Do
    control-openhands browser viewport desktop

Cleanup #

  1. Note
    Deleting an automation is F21's flow; here remove the fixtures with
  2. Arrange
    control-openhands api DELETE /api/automation/v1/<id> --write
  3. Note
    for each QA_F22 … (Custom, Plugin, Tarball, Imported, View, Agent) and Daily news digest id from
  4. Check
    control-openhands api GET '/api/automation/v1?limit=100'
  5. Note
    Delete OPENHANDS_URL in Settings → Secrets (control-openhands browser click 'testid=secret-item >> has-text=OPENHANDS_URL >> testid=delete-secret-button', then testid=confirmation-modal >> testid=confirm-button, as in F14.delete) unless the next recipe wants it.
  6. Expect
    The fallback and agent conversations stay in the conversation list (F04 deletes them).
  7. Note
    With zero automations,
  8. Do
    control-openhands browser goto /automations
  9. Do
    control-openhands browser click 'testid=automations-empty >> testid=automations-create-automation' --expect-url '/conversations'
  10. Note
    pre-fill the composer from the empty-state entry point too (browser eval "document.querySelector('[data-testid=chat-input]').innerText" is Create an automation).

Gotchas and known limits

  • A card click and a deep link are different paths: from the grid, GitHub/Slack-only responders (qa-changes, github-agents-md-maintainer, slack-channel-monitor, …) first show the responder choice and then the GitHub/Slack install dialog, so their setup form is reachable locally only by URL (/automations/new/<id>).
  • A card's N MCPs to connect before launch pill counts optional integrations too: Custom automation shows 1 MCPs to connect before launch for GitHub, yet opens its setup at once because GitHub is optional there (#17959).
  • The featured-section copy says picking a card opens "a pre-filled conversation"; most featured cards open a setup form instead.
  • The setup dialog's comboboxes (Action, Agent profile, Timezone, LLM profile) are autocomplete inputs: click the field and pick the option in the same breath, scoped as role=listbox >> role=option[name="…"]. A browser snapshot between the two closes the listbox (reproduced twice), and the option click then times out after 30 s; click the field again.
  • Service preflight runs 400 ms after a blur, not on every keystroke; browser press Tab then browser wait for the -error element.
  • Confirm and create creates the automation enabled; only imports arrive disabled. Use a yearly cron (0 0 1 1 *) so nothing fires during the run.
  • The Daily news digest entry names its automation Daily news digest (no QA_ prefix); remember it in cleanup.
  • The Agent profile combobox exists only while the chosen action creates through /v1 (Upload tarball, bundle entries): the automation service rejects agent_profile_id on /v1/preset/prompt and /v1/preset/plugin, so the Prompt and Plugin actions hide it and switching to them clears a chosen profile. Leave it on Default except in the Upload tarball bullet. A cleared choice (after a switch through Prompt or Plugin, or after picking Default) shows as a blank field rather than Default (F22.setup-agent-profile, #18061).
  • The automation detail page has no main landmark: read its title with browser text 'role=heading[level=1]', not 'role=main >> …'.
  • The import success toast is the only path to View automation and disappears after about 5 s.
  • Continue with local setup silently creates the OPENHANDS_URL secret (value: the app origin) the first time; F14's list then has an extra row.
  • The Install button in a template's install dialog verifies the MCP connection before saving, so dummy credentials cannot complete the queue; nothing is saved on failure.
  • Creating the first automation ticks Schedule a task in the sidebar Getting-started checklist (F02); a run that checks the checklist should do so before this file.
  • Known issue #17959: a template search with no matches leaves a blank area with no message (F22.templates-search).

Source paths: src/routes/automation-templates.tsx, src/routes/automation-setup-route.tsx, src/components/features/automations/ (recommended-automations-launcher.tsx, recommended-automations-section.tsx, responder-deployment-modal.tsx, add-automation-modal.tsx, create-instructions.tsx, import-automation-modal.tsx, agent-profile-selector.tsx), src/components/features/manifest/, src/manifests/, src/hooks/use-launch-skill-in-chat.ts, src/hooks/use-responder-url-secret.ts, src/utils/automation-export.ts, src/api/automation-service/automation-service.api.ts, node_modules/@openhands/extensions/automations/.