EN / field notes OpenHands feature map

OpenHands / F04

Conversation list and folders

The expanded sidebar always shows the user's conversations under a Conversations header. Each row opens its conversation and carries a pin and a ⋮ menu (Rename, Edit tags, Stop, Download, Archive or Unarchive, Delete). The header has a folder-plus picker that starts a conversation in a chosen workspace and a View menu with layout presets, tag filters, More options (organize, sort, scope, archived, age cutoff, automation filter, row metadata) and Delete all. Grouped by workspace, folders collapse, preview five rows and start new conversations in place. Preferences, pins, archives and the workspace each conversation was started in live in this browser's localStorage.

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

How to get to it

  • Always visible in the expanded sidebar on every page (/, the direct URL /conversations, /conversations/<id>, settings pages): the Conversations section under New Chat / Customize / Automate. At phone width it is inside the drawer opened by sidebar-mobile-menu-toggle.
  • Header: folder-plus (conversation-panel-new-thread-picker, aria "Create thread folder — choose workspace or repository"), View (conversation-layouts-toggle), and in grouped mode the Conversations label itself (conversations-header-toggle).
  • Row hover: pin (conversation-pin-toggle-<id>) and ⋮ (ellipsis-button, aria "More options"), which opens context-menu. A row with more tags than fit shows a +N chip (conversation-card-tag-overflow). The Pinned section has its own More/Less (conversation-panel-pinned-view-more).
  • View → More options (advanced-options-row) opens advanced-conversation-options-modal (Organize, Sort by, Threads scope-all/scope-relevant, Additional, Automations, Metadata).
  • Grouped mode: folder heading (thread-folder-drag-<suffix>), folder + (aria "Start new conversation in <folder>"), folder More/Less (thread-folder-view-more-<suffix>).
  • No command-menu entry or keyboard shortcut exists for the list.

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), desktop viewport, deepseek-flash active for the one prompted conversation.
  • F04.empty-state and the disabled Delete all are first checked on a run with no conversations (a fresh launch --new), and again after F04.delete-all.
  • Workspaces: control-openhands fixture git-repo --name qa-f04-repo and control-openhands fixture folder --name qa-f04-folder, then add both through the picker (F03 owns the folder browser): control-openhands browser click 'testid=conversation-panel-new-thread-picker', control-openhands browser click 'testid=add-workspaces-button', control-openhands browser click 'testid=folder-browser-up' twice (from <run>/private/home to the run directory; check with browser text 'testid=folder-browser-current-path'), control-openhands browser dblclick 'testid=folder-browser-entry-workspace', control-openhands browser click 'testid=folder-browser-add-all-subdirs'. The picker popover stays open afterwards; control-openhands browser click 'testid=conversation-panel-new-thread-picker' closes it.
  • A finished conversation, started right after the View menu bullet (the first two bullets need a run with no conversations): control-openhands conversation start --prompt "Reply with only the word pong. Do not run any tools." --wait --timeout 240. The model writes the title (✅ Reply only with pong, ✅ Reply with pong only, ...): select its row with has-text=pong.
  • F04.load-more and F04.folder-preview-more need more than 20 conversations, at least 6 in one folder. Empty conversations from the folder + cost no model call (see F04.folder-new-conversation).
  • F04.cloud-picker and F04.start-task-cards are blocked: they need an OpenHands Cloud backend with a connected git provider. The kept rows of F04.automation-filter (Only automation runs with real runs) and the hiding effect of F04.hide-older need data a fresh run cannot have (an automation-run conversation; a conversation older than one hour).

Behavior inventory

40 stable behavior IDs and their expected behavior

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.

Empty list #

  1. Note
    On a run with no conversations, run
  2. Check
    control-openhands browser text 'testid=conversation-panel-empty-state'
  3. Expect
    The text is No conversations found.

View menu #

  1. Do
    control-openhands browser click 'testid=conversation-layouts-toggle'
  2. Check
    control-openhands browser snapshot 'testid=conversation-layouts-menu'
  3. Note
    : radios By workspace, Recent ([checked]), Active, Compact, items Filter by tags, More options and Delete all (control-openhands browser attr 'testid=layout-preset-recent-activity' aria-checked is true).
  4. Note
    With no conversations
  5. Check
    control-openhands browser enabled 'testid=delete-all-conversations'

    is false; after

  6. Do
    control-openhands browser click 'testid=tag-filters-section'
  7. Check
    control-openhands browser text 'testid=tag-filters-empty'

    is No tags yet.

  8. Do
    control-openhands browser press Escape
  9. Note
    closes the menu (browser count 'testid=conversation-layouts-menu' is 0).
Conversation View menu with By workspace, Recent, Active, Compact, tag filtering and More options.
The View menu selects a conversation layout and offers filtering and additional controls. CLI capture · 1440 × 1000 · 9 October 2026 · Canvas 8793c111

Conversation View menu with By workspace, Recent, Active, Compact, tag filtering and More options.

Representative documentation capture, not a complete run of this recipe or family. Captured on an isolated local backend at desktop viewport using the current main checkout.

How this screenshot was taken

canvas: 1.26.0 · agent server: 1.53.0 · sdk: 1.53.0 · automation: 1.19.0

OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser goto /
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser wait testid=home-screen
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser click testid=open-workspace-button
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser wait role=dialog
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser press Escape
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser press Control+k
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser wait testid=command-menu
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser press Escape
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser click testid=conversation-layouts-toggle
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser wait testid=conversation-layouts-menu
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser screenshot testid=conversation-layouts-menu --feature F04.view-menu --name conversation-view-menu

Start in a workspace #

  1. Do
    control-openhands browser goto /
  2. Do
    control-openhands browser click 'testid=conversation-panel-new-thread-picker'
  3. Check
    control-openhands browser snapshot 'testid=conversation-panel-new-thread-popover'
  4. Note
    : buttons No workspace, qa-f04-folder, qa-f04-repo, + Add Workspace, Manage Workspaces.
  5. Do
    control-openhands browser click 'testid=conversation-panel-new-thread-popover >> testid=launch-workspace >> has-text=qa-f04-repo' --expect-url '/conversations/'

    returns /conversations/<id>, and

  6. Do
    control-openhands conversation list

    shows that id with workspace ending in /workspace/qa-f04-repo.

  7. Note
    From / again, testid=launch-no-workspace opens a new conversation whose row sits in the No workspace folder.
  8. Do
    control-openhands browser click 'testid=manage-workspaces-button'
  9. Note
    opens testid=manage-workspaces-modal (F03); Escape closes it.

Row opens its conversation #

  1. Note
    From / (control-openhands browser goto /; the previous bullet's launch left a conversation page, where --expect-url '/conversations/' would pass at once) run
  2. Do
    control-openhands browser click 'testid=conversation-card >> has-text=pong' --expect-url '/conversations/'
  3. Check
    control-openhands browser attr 'testid=conversation-card >> has-text=pong' data-active

    (true; other rows false).

  4. Do
    control-openhands browser eval "document.querySelector('[data-testid=conversation-card][data-active=true]').closest('a').getAttribute('href')"

    is /conversations/<id>?backend=default-local.

  5. Expect
    The finished conversation shows testid=conversation-status-check, idle ones conversation-status-active (green), paused ones conversation-status-paused (grey).
  6. Expect
    The reopened chat has its transcript back:
  7. Wait
    control-openhands browser wait 'testid=agent-message'
  8. Check
    control-openhands browser text 'testid=agent-message >> nth=-1'

    is pong and

  9. Check
    control-openhands browser text 'testid=user-message >> nth=-1'

    is the prompt, with

  10. Check
    control-openhands browser count 'testid=error-message-banner'
  11. Note
    0 (browser screenshot --feature F04.card --name reopened).
The sidebar lists a finished conversation named Markdown table and Python code block with a completion check.
A completed conversation stays in the sidebar and can be reopened from its row. CLI capture · 1440 × 1000 · 9 October 2026 · Canvas 8793c111

The sidebar lists a finished conversation named Markdown table and Python code block with a completion check.

Representative documentation capture, not a complete run of this recipe or family. Captured on an isolated local backend at desktop viewport using the current main checkout.

How this screenshot was taken

canvas: 1.26.0 · agent server: 1.53.0 · sdk: 1.53.0 · automation: 1.19.0

OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser goto /
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser wait testid=home-screen
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser screenshot testid=conversation-panel --feature F04.card --name conversation-sidebar

Live status #

  1. Do
    control-openhands conversation start --prompt "Reply with only the word ping. Do not run any tools."

    (no --wait; one tiny model call), then

  2. Wait
    control-openhands browser wait 'testid=conversation-panel >> testid=conversation-status-working' --timeout 20000
  3. Note
    : the active row (Conversation <id5>) shows the working indicator.
  4. Note
    After
  5. Wait
    control-openhands conversation wait <id> --timeout 180
  6. Note
    reports finished,
  7. Wait
    control-openhands browser wait 'testid=conversation-panel >> [data-testid=conversation-card][data-active=true] >> testid=conversation-status-check' --timeout 60000
  8. Note
    succeeds and
  9. Check
    control-openhands browser text 'testid=conversation-panel >> [data-testid=conversation-card][data-active=true] >> testid=conversation-card-title'

    is the generated title (it varies: ✅ Reply with only ping, ✅ Reply only ping, no tools).

  10. Expect
    The row lags the conversation by up to about 40 s (list refresh).

Group by workspace #

  1. Do
    control-openhands browser click 'testid=conversation-layouts-toggle'
  2. Do
    control-openhands browser click 'testid=layout-preset-by-workspace'
  3. Check
    control-openhands browser snapshot 'testid=conversation-panel'
  4. Note
    Regions No workspace, qa-f04-repo and qa-f04-folder each list their own conversations; a workspace without conversations still gets a folder.
  5. Note
    Second view in another browser:
  6. Do
    control-openhands browser reset
  7. Do
    control-openhands onboard --skip
  8. Note
    choose By workspace again; the workspace conversations must still be in their folders (currently they all fall into No workspace, a known failure).

Collapse one folder #

  1. Do
    control-openhands browser click '[data-testid^="thread-folder-drag-ws-"][data-testid$="qa-f04-repo"]'
  2. Check
    control-openhands browser attr '[data-testid^="thread-folder-drag-ws-"][data-testid$="qa-f04-repo"]' aria-expanded

    (false) and

  3. Check
    control-openhands browser count '[data-testid^="thread-folder-ws-"][data-testid$="qa-f04-repo"] >> testid=conversation-card'

    (0).

  4. Note
    Click again: true and the cards return.

Collapse all #

  1. Do
    control-openhands browser click 'testid=conversations-header-toggle'
  2. Check
    control-openhands browser attr 'testid=conversations-header-toggle' aria-expanded

    (false) and

  3. Check
    control-openhands browser count 'testid=conversation-panel-list-scroll >> testid=conversation-card'

    (0).

  4. Note
    Click again: true and every card is back.

New conversation in a folder #

  1. Do
    control-openhands browser goto /
  2. Check
    control-openhands browser click 'role=button[name="Start new conversation in qa-f04-folder"]' --expect-url '/conversations/'

    (returns the new /conversations/<id>), then

  3. Check
    control-openhands browser count '[data-testid^="thread-folder-ws-"][data-testid$="qa-f04-folder"] >> testid=conversation-card'
  4. Note
    it grows by one.
  5. Note
    Repeating browser goto / plus the click for qa-f04-repo 18 times arranges the data for the next two bullets.

Folder preview #

  1. Note
    With more than five conversations in qa-f04-repo,
  2. Check
    control-openhands browser count '[data-testid^="thread-folder-ws-"][data-testid$="qa-f04-repo"] >> testid=conversation-card'

    is 5 and

  3. Check
    control-openhands browser text '[data-testid^="thread-folder-view-more-ws-"][data-testid$="qa-f04-repo"]'

    is More.

  4. Note
    Click that button: every loaded row shows and the label is Less; click again: 5.

Reorder folders #

  1. Note
    In By workspace mode read the order with
  2. Do
    control-openhands browser eval "[...document.querySelectorAll('[data-testid^=thread-folder-drag-]')].map(e=>e.textContent.trim())"
  3. Do
    control-openhands browser drag '[data-testid^="thread-folder-drag-ws-"][data-testid$="qa-f04-folder"]' '[data-testid^="thread-folder-drag-ws-"][data-testid$="qa-f04-repo"]' --position before
  4. Expect
    The same eval now starts with qa-f04-folder, qa-f04-repo, also after browser reload, and
  5. Do
    control-openhands browser eval "JSON.parse(localStorage.getItem('conversation-panel-preferences')).state.groupFolderOrder"

    lists the ws:<path> keys in that order.

Load more #

  1. Note
    In By date mode (More options → testid=organize-chronological, or the Recent preset) with 22 or more conversations,
  2. Check
    control-openhands browser count 'testid=conversation-panel-list-scroll >> testid=conversation-card'

    is 20.

  3. Do
    control-openhands browser click 'testid=load-more-conversations'
  4. Wait
    control-openhands browser wait 'testid=load-more-conversations' --state detached --timeout 15000
  5. Note
    the count is the total (23 with 23 conversations).
  6. Note
    In By workspace mode (organize-grouped, then browser reload) the same click adds the later rows to their own folders: with the oldest rows in No workspace and none of them loaded, testid=thread-folder-__none_workspace appears only after the click and browser count 'testid=conversation-card' grows by the number of unloaded No workspace rows (6 → 9 with three such rows, 5 → 7 with two).
  7. Expect
    A folder that already shows five rows keeps the extra ones behind its More.

Organize #

  1. Do
    control-openhands browser click 'testid=conversation-layouts-toggle'
  2. Do
    control-openhands browser click 'testid=advanced-options-row'
  3. Check
    control-openhands browser snapshot 'testid=advanced-conversation-options-modal'

    (sections Organize, Sort by, Threads, Additional, Automations, Metadata),

  4. Do
    control-openhands browser click 'testid=organize-chronological'

    (browser attr 'testid=organize-chronological' aria-checked is true),

  5. Do
    control-openhands browser click 'testid=advanced-options-close'
  6. Do
    control-openhands browser reload
  7. Check
    control-openhands browser count '[data-testid^=thread-folder-ws-]'
  8. Expect
    The count is 0 and the rows form one list; organize-grouped brings the folders back.

Header divider #

  1. Note
    With more rows than fit (By date mode with 20 loaded rows, as Organize leaves it; By workspace mode previews too few rows to scroll),
  2. Do
    control-openhands browser eval "document.querySelector('[data-testid=older-conversations-summary]').parentElement.classList.contains('border-border')"

    is false; after

  3. Do
    control-openhands browser scroll 'testid=conversation-panel-list-scroll' --by 300
  4. Note
    it is true, and false again after --by -300.

Rename #

  1. Note
    Pick the oldest untitled conversation (b8d99... from the picker step; the sort check below needs an old one).
  2. Expect
    It is beyond the first 20 rows after a reload, so first run
  3. Do
    control-openhands browser click 'testid=load-more-conversations'
  4. Wait
    control-openhands browser wait 'testid=load-more-conversations' --state detached --timeout 15000
  5. Do
    control-openhands browser scroll 'testid=conversation-card >> has-text=Conversation <id5>'

    (the first five characters of its id),

  6. Do
    control-openhands browser hover 'testid=conversation-card >> has-text=Conversation <id5>'
  7. Do
    control-openhands browser click 'testid=conversation-card >> has-text=Conversation <id5> >> testid=ellipsis-button'
  8. Do
    control-openhands browser click 'testid=context-menu >> testid=edit-button'
  9. Do
    control-openhands browser fill 'input[data-testid=conversation-card-title]' 'QA_F04 renamed'
  10. Do
    control-openhands browser press Enter
  11. Check
    control-openhands browser toasts

    (Conversation title updated successfully),

  12. Do
    control-openhands browser reload
  13. Check
    control-openhands browser count 'testid=conversation-card-title >> has-text=QA_F04 renamed'

    (1).

Sort #

  1. Note
    Under Sort by Updated (default) the just-renamed old conversation is first.
  2. Note
    Sorting applies to the loaded rows only: after a reload the first page is the 20 most recently updated.
  3. Note
    In More options run
  4. Do
    control-openhands browser click 'testid=sort-created'
  5. Do
    control-openhands browser click 'testid=advanced-options-close'
  6. Do
    control-openhands browser eval "[...document.querySelectorAll('[data-testid=conversation-panel-list-scroll] [data-testid=conversation-card-title]')].map(e=>e.textContent)"
  7. Note
    now ends with QA_F04 renamed (with every page loaded, the older pong row comes after it), and the View menu row reads More options · Custom.
  8. Note
    Restore with testid=layout-preset-recent-activity.

Stop #

  1. Note
    On an idle row open ⋮ and run
  2. Do
    control-openhands browser click 'testid=context-menu >> testid=stop-button'
  3. Check
    control-openhands browser snapshot 'role=dialog'

    reads Confirm Stop Conversation, Are you sure you want to stop this conversation?.

  4. Do
    control-openhands browser click 'role=dialog >> role=button[name="Cancel"]'
  5. Note
    keeps it idle (control-openhands conversation status <id>).
  6. Note
    Repeat and click 'role=dialog >> role=button[name="Confirm Close"]';
  7. Wait
    control-openhands conversation wait <id> --until paused --timeout 30
  8. Note
    reaches paused,
  9. Wait
    control-openhands browser wait 'testid=conversation-card >> has-text=Conversation <id5> >> testid=conversation-status-paused'
  10. Note
    succeeds, and the reopened ⋮ menu has no Stop item.
  11. Do
    control-openhands browser press Escape
  12. Note
    closes it (browser count 'testid=context-menu' is 0) and puts focus back on the row's ⋮ (control-openhands browser eval "document.activeElement.dataset.testid" is ellipsis-button).

Presets #

  1. Do
    control-openhands browser click 'testid=conversation-layouts-toggle'
  2. Do
    control-openhands browser click 'testid=layout-preset-recent-activity'
  3. Do
    control-openhands browser reload
  4. Note
    reopen the menu, then
  5. Check
    control-openhands browser attr 'testid=layout-preset-recent-activity' aria-checked

    (true) and

  6. Check
    control-openhands browser text 'testid=advanced-options-row'

    (More options).

  7. Note
    With the paused conversation from Stop (above), testid=layout-preset-focused (Active) and testid=layout-preset-minimal (Compact) make
  8. Check
    control-openhands browser count 'testid=conversation-card >> has-text=Conversation <id5>'
  9. Note
    0; Recent shows it again.

Threads scope #

  1. Note
    With the paused conversation from Stop and the Recent preset, open More options and run
  2. Do
    control-openhands browser click 'testid=scope-relevant'

    (browser attr 'testid=scope-relevant' aria-checked is true) and

  3. Do
    control-openhands browser click 'testid=advanced-options-close'
  4. Check
    control-openhands browser count 'testid=conversation-card >> has-text=Conversation <id5>'

    is 0, also after browser reload, and the View menu now checks no preset and reads More options · Custom (Recent shows old conversations; the Active preset also hides them). testid=scope-all brings the row back (1) and Recent is checked again.

Hide older #

  1. Note
    In More options,
  2. Check
    control-openhands browser attr 'testid=toggle-older-conversations' aria-checked

    is false (Recent shows conversations of every age) and

  3. Check
    control-openhands browser text 'testid=older-conversation-cutoff'

    is Over 1 week.

  4. Do
    control-openhands browser click 'testid=older-conversation-cutoff'

    (radios Over 1 hour, Over 1 day, Over 1 week, Over 30 days),

  5. Do
    control-openhands browser click 'role=menuitemradio[name="Over 1 hour"]'
  6. Do
    control-openhands browser click 'testid=toggle-older-conversations >> text=Hide old conversations'

    (aria-checked turns true; a click on the row's center lands on the cutoff dropdown instead),

  7. Do
    control-openhands browser click 'testid=advanced-options-close'
  8. Do
    control-openhands browser reload
  9. Do
    control-openhands browser storage --values
  10. Note
    : conversation-panel-preferences holds "showOlderConversations":false and "olderConversationCutoff":"1h", and the View menu reads More options · Custom.
  11. Note
    Restore with testid=layout-preset-recent-activity (it turns hiding off but keeps the cutoff) and the cutoff Over 1 week.
  12. Note
    Hiding itself needs a conversation last updated before the cutoff (not reachable on a fresh run).

Automation filter #

  1. Note
    In More options run
  2. Do
    control-openhands browser click 'testid=automation-filter-only'
  3. Do
    control-openhands browser click 'testid=advanced-options-close'
  4. Note
    with no automation runs
  5. Check
    control-openhands browser text 'testid=conversation-panel-empty-state'

    is No conversations match the automation filter (Load more stays). testid=automation-filter-hide keeps every manual conversation; testid=automation-filter-all restores the default.

  6. Note
    Unpin everything first: a pinned row suppresses the empty state.

Row metadata #

  1. Note
    In More options click testid=toggle-repo-branch-metadata, testid=toggle-llm-profiles and testid=toggle-tags-metadata, then testid=advanced-options-close.
  2. Check
    control-openhands browser testids 'testid=conversation-card >> has-text=Conversation <id5>'

    lists conversation-card-workspace-folder (qa-f04-repo) and conversation-card-agent-chip (deepseek-flash) and no conversation-card-tag-chip.

  3. Note
    Restore with testid=layout-preset-recent-activity (presets reset all metadata toggles).

Hover preview #

  1. Do
    control-openhands browser mouse-click 900 900
  2. Do
    control-openhands browser tooltip 'testid=conversation-card >> has-text=Conversation <id5>'
  3. Expect
    The text is the title, Directory with the workspace path, Model deepseek/deepseek-flash and Created with a date.
  4. Expect
    After testid=toggle-hover-metadata is off (aria-checked false),
  5. Do
    control-openhands browser tooltip ... --timeout 5000
  6. Note
    finds no tooltip.

Pin #

  1. Do
    control-openhands browser hover 'testid=conversation-card >> has-text=QA_F04 renamed'
  2. Do
    control-openhands browser click 'testid=conversation-card >> has-text=QA_F04 renamed >> [data-testid^=conversation-pin-toggle-]'
  3. Check
    control-openhands browser text 'testid=conversation-panel-pinned-section'

    (Pinned and the row),

  4. Check
    control-openhands browser count 'testid=conversation-card >> has-text=QA_F04 renamed'

    (1: not duplicated) and, after browser reload,

  5. Check
    control-openhands browser attr 'testid=conversation-panel-pinned-section >> [data-testid^=conversation-pin-toggle-]' aria-pressed

    (true).

  6. Note
    Unpin with browser hover and browser click on testid=conversation-panel-pinned-section >> testid=conversation-card and its [data-testid^=conversation-pin-toggle-]; after a reload browser count 'testid=conversation-panel-pinned-section' is 0.

Pinned preview #

  1. Note
    Pin six rows (browser scroll, browser hover, then click each row's [data-testid^=conversation-pin-toggle-]).
  2. Check
    control-openhands browser count 'testid=conversation-panel-pinned-section >> testid=conversation-card'

    is 5 and

  3. Check
    control-openhands browser text 'testid=conversation-panel-pinned-view-more'

    is More;

  4. Do
    control-openhands browser click 'testid=conversation-panel-pinned-view-more'
  5. Note
    makes the count 6 and the label Less; click again: 5.
  6. Note
    Unpin all six with browser hover and a pin click on 'testid=conversation-panel-pinned-section >> testid=conversation-card >> nth=0', repeated.

Edit tags #

  1. Note
    Open ⋮ on a row (hover, testid=ellipsis-button) and run
  2. Do
    control-openhands browser click 'testid=context-menu >> testid=edit-tags-button'
  3. Do
    control-openhands browser fill 'testid=new-tag-key-input' 'Bad Key'
  4. Do
    control-openhands browser press Enter --selector 'testid=new-tag-key-input'
  5. Note
    →
  6. Check
    control-openhands browser text 'testid=edit-tags-error'

    is Tag names may only contain lowercase letters and numbers; key title with testid=add-tag-button → This tag name is reserved; key qaenv, browser fill 'testid=new-tag-value-input' one, Enter → browser count 'testid=edit-tag-row-qaenv' is 1; key qaenv again → This tag name is already in use; key qalong with a 257-character value → Tag values must be 256 characters or fewer; key qabare with an empty value left in the inputs, then

  7. Do
    control-openhands browser click 'testid=edit-conversation-tags-modal >> testid=confirm-button'
  8. Check
    control-openhands browser toasts

    shows Tags updated; after browser reload the row shows chips Qabare and Qaenv: one (conversation-card-tag-chip), and

  9. Check
    control-openhands api GET '/api/conversations/search?limit=3'

    has tags qaenv: one, qabare: "".

Tag overflow #

  1. Note
    On the tagged row add three more tags in Edit tags (keys qaregion, qastage, qaowner, values value-for-<key>, Enter after each) and save.
  2. Expect
    After browser reload the row shows Qabare and Qaenv: one plus
  3. Check
    control-openhands browser text 'testid=conversation-card >> has-text=Conversation <id5> >> testid=conversation-card-tag-overflow'
  4. Note
    +3 (aria-label Show 3 more tags).
  5. Note
    Clicking it keeps the URL, sets aria-expanded true, and
  6. Check
    control-openhands browser text 'testid=conversation-card-tag-overflow-popover'

    lists Qaowner value-for-qaowner, Qaregion …, Qastage … (3 conversation-card-tag-overflow-row);

  7. Do
    control-openhands browser press Escape
  8. Note
    closes it (count 0).

Remove tags #

  1. Note
    Reopen Edit tags on that row and run
  2. Do
    control-openhands browser click 'testid=remove-tag-qaregion'

    (likewise remove-tag-qastage, remove-tag-qaowner);

  3. Check
    control-openhands browser count 'testid=edit-tags-rows >> [data-testid^=edit-tag-row-]'

    is 2.

  4. Note
    Save with testid=edit-conversation-tags-modal >> testid=confirm-button (Tags updated); after browser reload the +3 chip is gone and
  5. Check
    control-openhands api GET '/api/conversations/search?limit=1' --pick items.0.tags

    is clientsource, qabare, qaenv only.

Tag filter #

  1. Expect
    After Edit tags (above) gave one row qaenv=one, run
  2. Do
    control-openhands browser click 'testid=conversation-layouts-toggle'
  3. Do
    control-openhands browser click 'testid=tag-filters-section'
  4. Do
    control-openhands browser click 'testid=tag-facet-row-qaenv=one'

    (aria-checked true; the menu stays open),

  5. Do
    control-openhands browser press Escape
  6. Check
    control-openhands browser count 'testid=conversation-panel-list-scroll >> testid=conversation-card'
  7. Note
    : only the tagged row plus pinned rows.
  8. Expect
    The count is the same after browser reload.

Filter chips #

  1. Note
    With the facet active,
  2. Check
    control-openhands browser text 'testid=conversation-active-tag-filters'

    is qaenv=one and Clear all.

  3. Do
    control-openhands browser click 'testid=active-tag-filter-qaenv=one'
  4. Note
    removes it (browser count 'testid=conversation-active-tag-filters' is 0).
  5. Note
    Select tag-facet-row-qaenv=one and tag-facet-row-qabare= again, then
  6. Do
    control-openhands browser click 'testid=clear-tag-filters'
  7. Note
    : the strip disappears and the full list returns.

Download #

  1. Note
    Open ⋮ and run
  2. Do
    control-openhands browser click 'testid=context-menu >> testid=download-trajectory-button'
  3. Check
    control-openhands browser downloads
  4. Expect
    A file …-conversation_<id>.zip lands in <run>/private/downloads;
  5. Check
    control-openhands browser downloads --last 1 --inspect

    lists <id-hex>/base_state.json and meta.json for any row, plus event folders such as bash_events/ for a conversation that ran (the pong row; it is the oldest, so Load more first).

Download failure #

  1. Note
    With ⋮ already open on a row, run
  2. Check
    control-openhands browser errors --clear
  3. Do
    control-openhands service stop agent-server
  4. Note
    click testid=context-menu >> testid=download-trajectory-button, then
  5. Check
    control-openhands browser toasts --history
  6. Check
    control-openhands browser count 'testid=context-menu'
  7. Expect
    There is exactly one toast, Couldn't download the conversation data: <cause> (with the server down the cause is the transport text HTTP request failed (502 Bad Gateway): "Bad Gateway: connect ECONNREFUSED 127.0.0.1:<port>"), and the menu is closed (0).
  8. Check
    control-openhands browser errors --app-only

    has pageErrors 0; its 502 request and console error are the outage itself.

  9. Do
    control-openhands restart
  10. Check
    control-openhands doctor

Archive #

  1. Note
    Open a conversation from its row, open ⋮ and run
  2. Do
    control-openhands browser click 'testid=context-menu >> testid=archive-button'
  3. Check
    control-openhands browser snapshot 'role=dialog'

    reads Archive conversation and Archive "<title>"? The conversation stays available with its full history and can be restored from Show archived.

  4. Do
    control-openhands browser click 'role=dialog >> testid=cancel-button'
  5. Note
    keeps the row.
  6. Note
    Repeat and
  7. Do
    control-openhands browser click 'role=dialog >> testid=confirm-button' --expect-url '/conversations(\?|$)'
  8. Note
    the row count is 0, also after browser reload.
  9. Note
    Archiving a pinned row removes the Pinned section.

Show archived and Unarchive #

  1. Note
    In More options click testid=toggle-show-archived (aria-checked true) and testid=advanced-options-close;
  2. Check
    control-openhands browser count 'testid=conversation-card >> has-text=Conversation <id5> >> testid=conversation-card-archived-chip'

    is 1 and the ⋮ menu offers Unarchive Conversation.

  3. Do
    control-openhands browser click 'testid=context-menu >> testid=unarchive-button'
  4. Note
    opens no dialog (browser count 'role=dialog' 0) and the chip count drops to 0.
  5. Note
    Turn Show archived off again.

Delete #

  1. Note
    Open a conversation from its row, open ⋮ and run
  2. Do
    control-openhands browser click 'testid=context-menu >> testid=delete-button'
  3. Note
    the dialog reads Confirm Delete and Are you sure you want to delete the "<title>" conversation? This action cannot be undone.
  4. Do
    control-openhands browser click 'role=dialog >> role=button[name="Cancel"]'
  5. Note
    keeps the row.
  6. Note
    Repeat and
  7. Do
    control-openhands browser click 'role=dialog >> role=button[name="Confirm Delete"]' --expect-url '/conversations(\?|$)'
  8. Note
    after browser reload the row count is 0 and
  9. Check
    control-openhands api GET /api/conversations/<id>

    is 404.

Phone #

  1. Do
    control-openhands browser viewport phone
  2. Do
    control-openhands browser goto /
  3. Do
    control-openhands browser click 'testid=sidebar-mobile-menu-toggle'
  4. Check
    control-openhands browser bbox 'testid=sidebar-mobile-drawer >> testid=conversation-panel'

    (insideViewport true, pageHorizontalOverflow false) and

  5. Do
    control-openhands browser screenshot --feature F04.phone --name drawer
  6. Note
    Hover a row in the drawer and click its testid=ellipsis-button;
  7. Check
    control-openhands browser bbox 'testid=context-menu'

    is inside the viewport.

  8. Do
    control-openhands browser mouse-click 380 20
  9. Note
    closes the menu (browser count 'testid=context-menu' 0) and the drawer (control-openhands browser wait 'testid=sidebar-mobile-drawer' --state hidden --timeout 5000; an immediate visible still reads true during the slide-out).
  10. Note
    Reopen the drawer and
  11. Do
    control-openhands browser click 'testid=sidebar-mobile-drawer >> testid=conversation-card >> has-text=QA_F04 renamed' --expect-url '/conversations/'
  12. Wait
    control-openhands browser wait 'testid=sidebar-mobile-drawer' --state hidden --timeout 5000
  13. Note
    succeeds.
  14. Note
    Return with
  15. Do
    control-openhands browser viewport desktop

Delete all #

  1. Do
    control-openhands browser click 'testid=conversation-layouts-toggle'
  2. Do
    control-openhands browser click 'testid=delete-all-conversations'
  3. Check
    control-openhands browser snapshot 'role=dialog'
  4. Note
    : Delete all conversations, Are you sure you want to delete N conversations?.
  5. Note
    Cancel keeps every row.
  6. Note
    Confirm with
  7. Do
    control-openhands browser click 'role=dialog >> role=button[name="Confirm Delete"]'
  8. Wait
    control-openhands browser wait 'role=dialog' --state detached --timeout 30000
  9. Check
    control-openhands api GET '/api/conversations/search?limit=100'

    has no items, Delete all is disabled again, and in By date mode the empty state returns.

  10. Note
    Expected for every conversation; today N and the deletion cover only the loaded pages (22 conversations, 20 loaded: 2 remain).

Collapsed rail #

  1. Do
    control-openhands browser click 'testid=sidebar-collapse-toggle'
  2. Check
    control-openhands browser count 'testid=conversation-panel'

    (0).

  3. Note
    Expand with
  4. Do
    control-openhands browser click 'aside[data-collapsed]'

    (see F02); the count is 1.

Cloud picker and start tasks #

  1. Note
    Blocked without an OpenHands Cloud backend and git provider.
  2. Note
    On Cloud, the same conversation-panel-new-thread-picker opens cloud-provider-tabs, cloud-repo-search-input (Search repositories) and launch-repository rows; start tasks render testid=start-task-card linking to /conversations/task-<id>.

Cleanup #

  1. Note
    View → Delete all twice (the second pass removes the rows the first left behind), until api GET '/api/conversations/search?limit=100' is empty; re-apply the Recent preset.
  2. Note
    Fixture folders and the workspace registry vanish with the run.

Gotchas and known limits

  • A plain --expect-url '/conversations/' returns at once when the browser is already on a conversation page. Start creation clicks from / (browser goto /), and never navigate away before the click returns: leaving early skips the client-side metadata write, so the new conversation lands in No workspace although the server has its working directory.
  • Workspace grouping, pins, archives and preferences live in this browser's localStorage (openhands-agent-server-conversation-metadata, pinned-conversations, archived-conversations, conversation-panel-preferences). browser reset loses them: archived rows reappear and every conversation moves to No workspace.
  • Folder test ids embed the sanitized absolute workspace path (thread-folder-ws--tmp-...-qa-f04-repo). Select with [data-testid^="thread-folder-ws-"][data-testid$="<folder>"] or by the + button's accessible name.
  • Row links carry ?backend=default-local; anchor URL regexes without $.
  • Hover and ⋮ need the row on screen: browser scroll '<row>' first, and browser hover before clicking ellipsis-button or the pin.
  • Escape closes the ⋮ menu and returns focus to its ⋮ button (#18044); an outside browser mouse-click closes it too. If Tab has moved focus to the next row's link, that row's preview tooltip opens and takes the first Escape, so press Escape twice. Inside the phone drawer the first Escape closes the menu and the next one the drawer.
  • The Stop, Delete and Delete all dialogs have no test ids on their buttons (data-testid is dropped by BrandButton) (#17915); use role=dialog >> role=button[name="..."]. The Archive and Edit tags dialogs do have cancel-button and confirm-button.
  • With the drawer open at phone width there are two conversation-panel elements; scope with testid=sidebar-mobile-drawer >> ....
  • Recent is exactly the fresh default layout (By date, Updated, all threads, old conversations shown), so a new user sees Recent checked. Active and Compact also turn on Hide old conversations: Recent plus a manual Threads → Active only reads More options · Custom, not Active. Presets never change the hide-older cutoff.
  • In By workspace mode Load more often adds no visible row: later rows join existing folders behind their More button.
  • By workspace never shows No conversations found while any workspace is registered: empty workspace folders stay.
  • Pinned rows bypass tag and automation filters and suppress the filtered empty states; unpin before asserting them.
  • The page size is 20; filters, Active scope and archives apply only to loaded pages.
  • The ⋮ menu offers Stop for idle and finished conversations too (they count as active); it disappears only once paused.
  • service stop agent-server needs control-openhands restart and a browser reload before anything else works.
  • The list refreshes on an interval: a row keeps conversation-status-working and Conversation <id5> for up to about 40 s after the conversation finished. Use browser wait ... --timeout 60000, not an immediate count.
  • Model-written titles vary from run to run; match finished conversations on a word from the prompt (has-text=pong), never on the whole title.
  • Known issue #17919: Delete all deletes only the loaded pages, and its dialog states that smaller count (F04.delete-all).

Source paths: src/components/features/sidebar/sidebar-conversation-list.tsx, src/components/features/conversation-panel/ (panel, layouts menu, advanced options modal, new-thread picker, folder list/row, pinned section, active filter chips, confirm and tags modals, conversation-card/), src/stores/conversation-panel-preferences-store.ts, src/stores/pinned-conversations-store.ts, src/stores/archived-conversations-store.ts, src/hooks/query/use-paginated-conversations.ts, src/hooks/use-download-conversation.ts, src/api/conversation-metadata-store.ts.