Engel’s notebook

Conversation page, header and management menu

F07 · 25 mapped behaviors in the Self-Healing Codebase Project. Follow the prerequisites, actions and expected results; expand the original CLI recipe to replay it.

778 behavior IDs · 28 families · 844 named recipes & support steps
16 October 2026 · PR #18334 at 9a3c3a69
Canvas 1.27.1 · Agent Server 1.55.0 · automation 1.20.0

This is the map at the revision above, including its open bug notes. It does not incorporate later changes on main or claim a fresh verification run. Setup & reading guide · Download the scenario inventory

All scenarios & marker key · Open a script to read Given / Do / Expect. Expand the exact recipe for replayable commands.

F07 Conversation page, header and management menu

25 behaviors · 29 scripts & contracts · Maintained source

Entry points & prerequisites

What this family covers

The conversation page (/conversations/<id>) shows the chat pane: a header with the runtime status dot, the editable title, the "..." management menu and the Git actions, Overview and panel toggles, then the message history and the composer. From the header a user renames, stops or resumes the runtime, opens the skills, hooks and agent-tools dialogs, exports or downloads the conversation, jumps to usage and deletes it.

How a user gets here

  • Sidebar conversation row (conversation-card), or the home composer submit, which navigates to the new conversation.
  • Direct URL /conversations/<id>.
  • Direct URL /conversations/<id>/panel, or the header panel button, at phone width (up to 1024 px); at desktop width that URL redirects to /conversations/<id> with the drawer open.
  • Hovering a message in the history: Branch from here (local backends only).
  • Chips under the composer (git control bar) for conversations in a git workspace (home Open Workspace).
  • Header controls inside testid=chat-pane-header: the status dot (server-status-menu-trigger), the title (conversation-name-title), the "..." button (ellipsis-button, aria "More options"), conversation-git-actions-toggle, conversation-overview-toggle, right-panel-toggle.

Given

Preconditions:

  • Baseline state (launched, doctored, onboard --skip done) and an active LLM profile (control-openhands llm preset deepseek).
  • A finished conversation: control-openhands conversation start --prompt "Reply with the single word: pong" --wait --timeout 240 prints its <id>.
  • A running conversation for the active-only items: control-openhands conversation start --prompt "Run the shell command: sleep 90 ; then reply with the word done" --wait --until running --timeout 120 prints <running-id>. It finishes after about 90 s, so start it right before F07.stop-confirm (after a Stop it stays paused until Start Runtime).
  • A conversation in a git workspace for the git control bar: control-openhands fixture git-repo --name qa-f07-repo, then control-openhands browser goto / (conversation start left the browser on <id>), control-openhands browser click 'testid=open-workspace-button', control-openhands browser click 'role=dialog >> role=combobox[name="Select a workspace"]', control-openhands browser click 'role=dialog >> role=button[name="+ Add Workspace"]', control-openhands browser click 'testid=folder-browser-sidebar-workspace', control-openhands browser click 'testid=folder-browser-up' three times (to the run directory), control-openhands browser click 'testid=folder-browser-entry-workspace', control-openhands browser click 'testid=folder-browser-entry-qa-f07-repo', control-openhands browser click 'testid=folder-browser-use', control-openhands browser click 'testid=workspace-launch-button', then control-openhands conversation start --prompt "Reply with the single word: ok" --stay --wait --timeout 240 prints <git-id>.
  • An automation setup conversation for the setup-leaving bullet, made right after the finished conversation: control-openhands browser goto /automations, control-openhands browser click 'testid=automations-add-automation', control-openhands browser click 'testid=automations-add-automation-create' --expect-url '/automations/setup', control-openhands conversation start --stay --prompt 'Reply with only the word ok. Do not run any tools.' --wait --timeout 180 (prints <setup-id>), then control-openhands browser goto /.
  • Cloud-only rows (F07.cloud-only) are blocked without an OpenHands Cloud account.
  • Desktop viewport unless a bullet says otherwise.
Open by URLF07.open-by-urlKnown issueBlocked

Scenario F07--open-by-url · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Open /conversations/<id>.
  2. Wait
    Wait for “Chat pane header” to appear.

Expect

/conversations/<id> shows header, history and composer, and remembers the id as the backend's last conversation.

Linked known issues

Known issue #16727 (F07.open-by-url): Conversation corrupted after VM restart: "Missing event file". Live prerequisite: A disposable VM interrupted at the event-write boundary or the original missing-event persisted fixture.

Known issue #16356 (F07.open-by-url): Conversation LLM config is frozen at creation time — resuming after API key rotation fails permanently with LLMAuthenticationError. Live prerequisite: A working provider key that can be rotated/revoked, an existing conversation using it, and a replacement profile.

Known issue OpenHands/software-agent-sdk#5094 (F07.open-by-url): After ACP session/load fails, the backend can create a fresh model session while Canvas still shows the old context. Live prerequisite: An authenticated Codex ACP session whose original session/load fails while persisted history remains.

Known issue #16877 (F07.open-by-url): Untitled terminal-status conversations are reported to keep the 3-second title polling interval. On frozen target fa1f724ab5737a17453ad6081129eb91c5a423c6, a real Local autotitle:false/idle conversation retained title:null, while eight browser batch GETs had 30.008–30.031-second intervals over 210 seconds. The direct adapter supplies a default display title. This does not exercise the Cloud null-title IDLE/FINISHED branch, which needs an approved Cloud conversation with a failed/unsupported title job. Run backfill-18309-coordinator-checks, Agent Server/SDK 1.54.0, automation 1.19.3; retained evidence/F07.open-by-url/16877-local-polling.json and 16877-local-idle.png. Arrange the empty conversation with api POST /api/conversations --write --data @<idle-autotitle-disabled-payload>, open /conversations/<id>, clear browser network, leave the tab foreground, then read browser network --filter ids= --last 100 and conversation status <id>. No model call or response was used.

Exact CLI recipe & bug checks

Run control-openhands browser goto /conversations/<id>, control-openhands browser wait 'testid=chat-pane-header', control-openhands browser text 'testid=conversation-name-title' and control-openhands browser count 'testid=chat-interface >> testid=agent-message'. The title is the generated one and the count is 1. control-openhands browser eval "localStorage.getItem('openhands-last-conversation-by-backend')" reads {"default-local":"<id>"}.

Leaving an automation setup conversationF07.open-by-urlKnown issueBlocked

Scenario F07--leaving-an-automation-setup-conversation · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Open /.
  2. Do
    Choose “Conversation panel new thread picker”.
  3. Do
    Choose “Launch no workspace”.
  4. Do
    Choose the element identified in the CLI recipe.
  5. Wait
    Wait for “Chat pane header” to appear.
  6. Do
    Reload the page.
  7. Do
    Use browser storage as specified in the exact recipe.

Expect

/conversations/<id> shows header, history and composer, and remembers the id as the backend's last conversation.

Linked known issues

Known issue #16727 (F07.open-by-url): Conversation corrupted after VM restart: "Missing event file". Live prerequisite: A disposable VM interrupted at the event-write boundary or the original missing-event persisted fixture.

Known issue #16356 (F07.open-by-url): Conversation LLM config is frozen at creation time — resuming after API key rotation fails permanently with LLMAuthenticationError. Live prerequisite: A working provider key that can be rotated/revoked, an existing conversation using it, and a replacement profile.

Known issue OpenHands/software-agent-sdk#5094 (F07.open-by-url): After ACP session/load fails, the backend can create a fresh model session while Canvas still shows the old context. Live prerequisite: An authenticated Codex ACP session whose original session/load fails while persisted history remains.

Known issue #16877 (F07.open-by-url): Untitled terminal-status conversations are reported to keep the 3-second title polling interval. On frozen target fa1f724ab5737a17453ad6081129eb91c5a423c6, a real Local autotitle:false/idle conversation retained title:null, while eight browser batch GETs had 30.008–30.031-second intervals over 210 seconds. The direct adapter supplies a default display title. This does not exercise the Cloud null-title IDLE/FINISHED branch, which needs an approved Cloud conversation with a failed/unsupported title job. Run backfill-18309-coordinator-checks, Agent Server/SDK 1.54.0, automation 1.19.3; retained evidence/F07.open-by-url/16877-local-polling.json and 16877-local-idle.png. Arrange the empty conversation with api POST /api/conversations --write --data @<idle-autotitle-disabled-payload>, open /conversations/<id>, clear browser network, leave the tab foreground, then read browser network --filter ids= --last 100 and conversation status <id>. No model call or response was used.

Exact CLI recipe & bug checks

A conversation tagged automationsetup: draft (started from /automations/setup, see F04.automation-setup-tag) opens as a setup page (testid=automation-setup-topbar, no chat-pane-header). Make an empty conversation: control-openhands browser goto /, control-openhands browser click 'testid=conversation-panel-new-thread-picker', control-openhands browser click 'testid=launch-no-workspace' --expect-url '/conversations/' (note <plain-id>; control-openhands api GET /api/conversations/<plain-id> --pick tags is only clientsource). Open the setup conversation from the sidebar (control-openhands browser click 'a[href*="/conversations/<setup-id>"] >> nth=0' --expect-url '/conversations/<setup-id>'), then go back the same way ('a[href*="/conversations/<plain-id>"] >> nth=0'). Expected: <plain-id> opens as a normal conversation: control-openhands browser wait 'testid=chat-pane-header', browser count 'testid=chat-pane-header' is 1, browser count 'testid=automation-setup-topbar' is 0, and api GET /api/conversations/<plain-id> --pick tags is still only clientsource, also after browser reload and after repeating the round trip; the sidebar row of <plain-id> has no Automation setup: Draft chip (browser screenshot --feature F07.open-by-url --name plain-stays-plain). The setup conversation is in this tab's openhands-automation-setup-handoffs sessionStorage entry during the check (control-openhands browser storage --session --values). The same holds at phone width through the sidebar drawer (sidebar-mobile-menu-toggle, then the rows inside testid=sidebar-mobile-drawer).

Unknown idF07.missing-redirectMapped behavior

Scenario F07--unknown-id · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Open /conversations/00000000-0000-0000-0000-000000000000.
  2. Wait
    Wait for the element identified in the CLI recipe to appear.

Expect

an unknown id toasts "This conversation does not exist…" and redirects to /conversations.

Exact CLI recipe & bug checks

Run control-openhands browser goto /conversations/00000000-0000-0000-0000-000000000000, control-openhands browser wait-url '/conversations$' and control-openhands browser toasts. A toast reads This conversation does not exist, or you do not have permission to access it. If this is your conversation, try switching to the workspace where it was created. and the last-conversation slot is {}. It is the only toast: control-openhands browser toasts --history has no entry with does not understand.

Rename inlineF07.rename-inlineKnown failure

Scenario F07--rename-inline · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Open /conversations/<id>.
  2. Do
    Double-click “Conversation name title”.
  3. Do
    Enter “QA_F07 renamed” in “Conversation name”.
  4. Do
    Press Enter.
  5. Do
    Reload the page.

Expect

double-click the title (or menu Rename), Enter or blur saves with a toast; an empty value reverts.

Linked known issues

Known failure OpenHands/software-agent-sdk#5629 (F07.rename-inline), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0. A second server's native first load rewrote a newer saved title with its startup copy; the connected Canvas header showed that older title. Recipe: create an idle conversation on A and PATCH its title to QA_SHARED_BEFORE_B_5629_REPEAT; run pinned SDK Python python .agents/skills/verify-openhands/scripts/fixtures/shared-storage-server.py --run <run> --seconds 300 in the background. It starts only a bounded no-editor SDK B sharing this run's conversation directory/cipher/session key; its private server.json supplies the port and owned operator/PGID. After B listens, PATCH A to QA_SHARED_AFTER_B_5629_REPEAT and confirm disk. Run control-openhands service stop agent-server --run <run>, then python .../fixtures/shared-storage-server.py --run <run> --load-id <id>: native B /events/count returned 200 and disk reverted to BEFORE_B. Restart A only to serve Canvas, choose default-local and doctor; use the Add Backend recipe from F25 to connect B at its recorded port with the private key file, then open its card: the header reads BEFORE_B. SIGTERM only the recorded helper operator and verify its port/PGID gone; the helper also bounds runtime and terminates remaining group descendants. Evidence: backfill-18309-surface-rechecks/F07.rename-inline/metadata-repeat-before-b-load-5629.json, metadata-repeat-after-b-load-5629.json, shared-first-load-reverted-5629.png. The native first-load request is explicit; stale catalog display alone is insufficient proof of this overwrite.

Known failure OpenHands/software-agent-sdk#4800 (F07.rename-inline), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0. A refused rename changed the server's GET title while meta.json kept the previous title; graceful shutdown subsequently persisted the rejected title. Recipe: create an idle dummy-profile conversation, then double-click conversation-name-title, fill conversation-name-input with QA_META_BEFORE_4800 and press Enter; verify GET/disk. Record the original mode and temporarily chmod 0500 <run>/private/state/dev_conversations/<id-hex> to deny an atomic metadata replacement. Rename through that UI to QA_META_REFUSED_4800: the permission-denied toast/HTTP 500 appears, GET shows REFUSED, disk still shows BEFORE. Restore the exact original directory mode in finally, then restart, doctor, GET and reload (CLI with --run <run>): REFUSED was persisted during graceful shutdown. Only the owned temporary directory is changed; evidence contains title/timestamp fields, not stored secrets. Evidence: backfill-18309-surface-rechecks/F07.rename-inline/metadata-before-4800.json, metadata-denied-4800.json, refused-title-4800.png, metadata-after-graceful-restart-4800.json. Tags/autotitle and ownership-conflict branches remain untested.

Exact CLI recipe & bug checks

On /conversations/<id> (control-openhands browser goto /conversations/<id>; the unknown id redirected to /conversations) run control-openhands browser dblclick 'testid=conversation-name-title', control-openhands browser fill 'testid=conversation-name-input' 'QA_F07 renamed', control-openhands browser press Enter, control-openhands browser toasts (Conversation title updated successfully), then control-openhands browser reload and control-openhands browser text 'testid=conversation-name-title'. It reads QA_F07 renamed, and so does control-openhands browser text 'testid=conversation-card >> testid=conversation-card-title'.

Rename from the menu, empty valueF07.rename-inlineKnown failure

Scenario F07--rename-from-the-menu-empty-value · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Chat pane header” / “More options”.
  2. Do
    Choose “Conversation name context menu” / “Rename”.
  3. Do
    Enter “” in “Conversation name”.
  4. Do
    Press Enter.

Expect

double-click the title (or menu Rename), Enter or blur saves with a toast; an empty value reverts.

Linked known issues

Known failure OpenHands/software-agent-sdk#5629 (F07.rename-inline), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0. A second server's native first load rewrote a newer saved title with its startup copy; the connected Canvas header showed that older title. Recipe: create an idle conversation on A and PATCH its title to QA_SHARED_BEFORE_B_5629_REPEAT; run pinned SDK Python python .agents/skills/verify-openhands/scripts/fixtures/shared-storage-server.py --run <run> --seconds 300 in the background. It starts only a bounded no-editor SDK B sharing this run's conversation directory/cipher/session key; its private server.json supplies the port and owned operator/PGID. After B listens, PATCH A to QA_SHARED_AFTER_B_5629_REPEAT and confirm disk. Run control-openhands service stop agent-server --run <run>, then python .../fixtures/shared-storage-server.py --run <run> --load-id <id>: native B /events/count returned 200 and disk reverted to BEFORE_B. Restart A only to serve Canvas, choose default-local and doctor; use the Add Backend recipe from F25 to connect B at its recorded port with the private key file, then open its card: the header reads BEFORE_B. SIGTERM only the recorded helper operator and verify its port/PGID gone; the helper also bounds runtime and terminates remaining group descendants. Evidence: backfill-18309-surface-rechecks/F07.rename-inline/metadata-repeat-before-b-load-5629.json, metadata-repeat-after-b-load-5629.json, shared-first-load-reverted-5629.png. The native first-load request is explicit; stale catalog display alone is insufficient proof of this overwrite.

Known failure OpenHands/software-agent-sdk#4800 (F07.rename-inline), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0. A refused rename changed the server's GET title while meta.json kept the previous title; graceful shutdown subsequently persisted the rejected title. Recipe: create an idle dummy-profile conversation, then double-click conversation-name-title, fill conversation-name-input with QA_META_BEFORE_4800 and press Enter; verify GET/disk. Record the original mode and temporarily chmod 0500 <run>/private/state/dev_conversations/<id-hex> to deny an atomic metadata replacement. Rename through that UI to QA_META_REFUSED_4800: the permission-denied toast/HTTP 500 appears, GET shows REFUSED, disk still shows BEFORE. Restore the exact original directory mode in finally, then restart, doctor, GET and reload (CLI with --run <run>): REFUSED was persisted during graceful shutdown. Only the owned temporary directory is changed; evidence contains title/timestamp fields, not stored secrets. Evidence: backfill-18309-surface-rechecks/F07.rename-inline/metadata-before-4800.json, metadata-denied-4800.json, refused-title-4800.png, metadata-after-graceful-restart-4800.json. Tags/autotitle and ownership-conflict branches remain untested.

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=chat-pane-header >> testid=ellipsis-button', control-openhands browser click 'testid=conversation-name-context-menu >> testid=rename-button', control-openhands browser fill 'testid=conversation-name-input' '' and control-openhands browser press Enter. The title stays QA_F07 renamed and no toast appears.

History skeleton and sidebar entryF07.history-skeleton · F07.open-by-urlKnown issueBlocked

Scenario F07--history-skeleton-and-sidebar-entry · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Open /.
  2. Do
    Choose “Conversation card” / containing “QA_F07 renamed”.

Expect

opening a conversation shows a message skeleton until its history has loaded.

/conversations/<id> shows header, history and composer, and remembers the id as the backend's last conversation.

Linked known issues

Known issue #16876 (F07.history-skeleton): Planning-agent history flag stays true forever when sub-conversations fetch fails — infinite chat loading skeleton. Live prerequisite: A real planning sub-conversation whose batch lookup fails or yields null, with the main history available.

Known issue #16727 (F07.open-by-url): Conversation corrupted after VM restart: "Missing event file". Live prerequisite: A disposable VM interrupted at the event-write boundary or the original missing-event persisted fixture.

Known issue #16356 (F07.open-by-url): Conversation LLM config is frozen at creation time — resuming after API key rotation fails permanently with LLMAuthenticationError. Live prerequisite: A working provider key that can be rotated/revoked, an existing conversation using it, and a replacement profile.

Known issue OpenHands/software-agent-sdk#5094 (F07.open-by-url): After ACP session/load fails, the backend can create a fresh model session while Canvas still shows the old context. Live prerequisite: An authenticated Codex ACP session whose original session/load fails while persisted history remains.

Known issue #16877 (F07.open-by-url): Untitled terminal-status conversations are reported to keep the 3-second title polling interval. On frozen target fa1f724ab5737a17453ad6081129eb91c5a423c6, a real Local autotitle:false/idle conversation retained title:null, while eight browser batch GETs had 30.008–30.031-second intervals over 210 seconds. The direct adapter supplies a default display title. This does not exercise the Cloud null-title IDLE/FINISHED branch, which needs an approved Cloud conversation with a failed/unsupported title job. Run backfill-18309-coordinator-checks, Agent Server/SDK 1.54.0, automation 1.19.3; retained evidence/F07.open-by-url/16877-local-polling.json and 16877-local-idle.png. Arrange the empty conversation with api POST /api/conversations --write --data @<idle-autotitle-disabled-payload>, open /conversations/<id>, clear browser network, leave the tab foreground, then read browser network --filter ids= --last 100 and conversation status <id>. No model call or response was used.

Exact CLI recipe & bug checks

From / (control-openhands browser goto /) run control-openhands browser click 'testid=conversation-card >> has-text=QA_F07 renamed' --expect-url '/conversations/[0-9a-f-]+' --observe 'testid=chat-messages-skeleton' --observe-ms 3000 (run it after the rename above; before it, match the generated title instead). The observed list shows the skeleton present for a few hundred ms, then <absent>; the URL is /conversations/<id>?backend=default-local.

Status menu, stopF07.status-menuKnown issueBlocked

Scenario F07--status-menu-stop · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Hover over “Server status menu trigger”.
  2. Do
    Choose “Server status context menu” / “Stop server”.

Expect

hovering the status dot (or tapping it on touch) opens a menu with the state and Stop Runtime (active) or Start Runtime (paused).

Linked known issues

Known issue OpenHands/software-agent-sdk#4893 (F07.status-menu): A sandbox frozen mid-run can resume with a stale Running state that rejects normal resume requests. Live prerequisite: A disposable sandbox/VM frozen mid-run with a live model and persisted ownership state.

Exact CLI recipe & bug checks

On /conversations/<id> run control-openhands browser hover 'testid=server-status-menu-trigger' (for a mouse the hover opens the menu and a click on the open menu's dot closes it; a CLI click from elsewhere usually opens it only because the click lands before the hover renders, a race that sometimes leaves it closed, so do not click), control-openhands browser text 'testid=server-status-context-menu' (Running / Stop Runtime), then control-openhands browser click 'testid=server-status-context-menu >> testid=stop-server-button' --expect-url '/conversations$' and control-openhands browser toasts. The toasts read Stopping conversation... and Conversation stopped and the URL is /conversations.

MenuF07.menu-openMapped behavior

Scenario F07--menu · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Open /conversations/<git-id>.
  2. Do
    Choose “Chat pane header” / “More options”.
  3. Do
    Press Escape.
  4. Do
    Use browser mouse click as specified in the exact recipe.

Expect

the "..." menu opens under the title; Escape closes it and returns focus to "...", and so does an outside click (without the focus move).

Exact CLI recipe & bug checks

Stop Runtime left the browser on /conversations: run control-openhands browser goto /conversations/<git-id> (a finished conversation whose runtime was not stopped), then control-openhands browser click 'testid=chat-pane-header >> testid=ellipsis-button' and control-openhands browser testids 'testid=conversation-name-context-menu'. On a finished or running conversation the items are rename-button, show-skills-button, show-hooks-button, show-agent-tools-button, export-transcript-button (Export…), download-trajectory-button, display-cost-button (Display Usage and Cost), stop-button, delete-button. control-openhands browser press Escape closes it (control-openhands browser count 'testid=conversation-name-context-menu' is 0) and focus is back on the trigger (control-openhands browser eval "document.activeElement.dataset.testid" is ellipsis-button). Open it again: control-openhands browser mouse-click 900 500 closes it too (count 0).

Stop with confirmationF07.stop-confirmMapped behavior

Scenario F07--stop-with-confirmation · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Chat pane header” / “More options”.
  2. Do
    Choose “Conversation name context menu” / “Stop”.
  3. Do
    Choose the dialog / “Cancel”.
  4. Do
    Choose the dialog / “Confirm”.

Expect

Stop Conversation asks for confirmation; Confirm Close pauses the conversation and returns to /conversations.

Exact CLI recipe & bug checks

On /conversations/<running-id> run control-openhands browser click 'testid=chat-pane-header >> testid=ellipsis-button', control-openhands browser click 'testid=conversation-name-context-menu >> testid=stop-button' and control-openhands browser snapshot 'role=dialog' (Confirm Stop Conversation, buttons Cancel and Confirm Close, test ids cancel-button and confirm-button). control-openhands browser click 'role=dialog >> testid=cancel-button' closes it and control-openhands conversation status <running-id> stays running. Repeat the two clicks, then control-openhands browser click 'role=dialog >> testid=confirm-button' --expect-url '/conversations$'; the toasts read Conversation stopped and conversation status <running-id> is paused.

Branch from a messageF07.branch-from-messageKnown failure

Scenario F07--branch-from-a-message · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Open /conversations/<id>.
  2. Do
    Hover over “Agent message”.
  3. Do
    Choose “Agent message” / “Branch from here”.
  4. Do
    Open /conversations/<id>.
  5. Do
    Hover over “User message”.
  6. Do
    Choose “User message” / “Branch from here”.

Expect

Branch from here on a message forks the conversation as <title> (branch); on a user message the message is left out and its text goes back into the composer.

Linked known issues

After the user-message branch in F07.branch-from-message, Send stays disabled next to the prefilled text (browser enabled 'testid=submit-button' is false; Known failure #18202, see F05 Gotchas). Enter in the field sends it.

Exact CLI recipe & bug checks

On /conversations/<id> (control-openhands browser goto /conversations/<id>; Stop with confirmation ended on /conversations) run control-openhands browser hover 'testid=agent-message', then control-openhands browser click 'testid=agent-message >> role=button[name="Branch from here"]' --expect-url '/conversations/(?!<id>)[0-9a-f-]+', control-openhands browser text 'testid=conversation-name-title' (QA_F07 renamed (branch)) and control-openhands browser count 'testid=chat-interface >> testid=user-message' (1, the history is copied up to that reply). Back on /conversations/<id> (control-openhands browser goto /conversations/<id>), control-openhands browser hover 'testid=user-message' and control-openhands browser click 'testid=user-message >> role=button[name="Branch from here"]' --expect-url '/conversations/(?!<id>)[0-9a-f-]+' (it prints /conversations/<branch-id>): the new branch has 0 user messages and control-openhands browser text 'testid=chat-input' reads the original prompt Reply with the single word: pong, still there a few seconds later and not sent (control-openhands conversation events <branch-id> --kinds MessageEvent has count 0).

Delete with confirmationF07.delete-confirmMapped behavior

Scenario F07--delete-with-confirmation · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Chat pane header” / “More options”.
  2. Do
    Choose “Conversation name context menu” / “Delete”.
  3. Do
    Choose the dialog / “Cancel”.
  4. Do
    Choose the dialog / “Confirm”.
  5. Do
    Open /conversations/<id>.

Expect

Delete Conversation names the title in a confirmation; Cancel keeps it, Confirm Delete removes it and returns to /conversations.

Exact CLI recipe & bug checks

On a disposable conversation (for example the branch from F07.branch-from-message) run control-openhands browser click 'testid=chat-pane-header >> testid=ellipsis-button', control-openhands browser click 'testid=conversation-name-context-menu >> testid=delete-button' and control-openhands browser snapshot 'role=dialog': Confirm Delete Are you sure you want to delete the "<title>" conversation? This action cannot be undone. with Cancel (cancel-button) and Confirm Delete (confirm-button). control-openhands browser click 'role=dialog >> testid=cancel-button' keeps the URL. Repeat the two clicks, then control-openhands browser click 'role=dialog >> testid=confirm-button' --expect-url '/conversations$'; control-openhands api GET '/api/conversations?ids=<id>' returns [null] and browser goto /conversations/<id> redirects as in F07.missing-redirect.

Paused conversationF07.menu-items-by-state · F07.status-menuKnown issueBlocked

Scenario F07--paused-conversation · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Open /conversations/<running-id>.
  2. Do
    Use browser mouse click as specified in the exact recipe.
  3. Do
    Choose “Server status menu trigger”.
  4. Do
    Choose “Server status context menu” / “Start server”.
  5. Wait
    Wait for the conversation to finish.

Expect

Show Available Hooks and Stop Conversation appear only while the conversation is active.

hovering the status dot (or tapping it on touch) opens a menu with the state and Stop Runtime (active) or Start Runtime (paused).

Linked known issues

Known issue OpenHands/software-agent-sdk#4893 (F07.status-menu): A sandbox frozen mid-run can resume with a stale Running state that rejects normal resume requests. Live prerequisite: A disposable sandbox/VM frozen mid-run with a live model and persisted ownership state.

Exact CLI recipe & bug checks

Run control-openhands browser goto /conversations/<running-id>, open the "..." menu and control-openhands browser testids 'testid=conversation-name-context-menu': show-hooks-button and stop-button are absent. Close it with control-openhands browser mouse-click 900 500. Run control-openhands browser click 'testid=server-status-menu-trigger' and control-openhands browser text 'testid=server-status-context-menu' (Server Stopped / Start Runtime), then control-openhands browser click 'testid=server-status-context-menu >> testid=start-server-button' and control-openhands conversation wait <running-id> --until running,finished,idle --timeout 60. The status is running again and the menu reads Running / Stop Runtime.

HooksF07.hooks-modalMapped behavior

Scenario F07--hooks · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Conversation name context menu” / “Show hooks”.
  2. Do
    Choose “Hooks modal” / “Refresh hooks”.
  3. Do
    Choose “Close hooks modal”.

Expect

Show Available Hooks lists the workspace's .openhands/hooks.json hooks by event (read when it first opens for a conversation after a page load, and on Refresh; a reopen within 5 minutes of the last read shows the cached list), or "No hooks configured for this conversation."

Exact CLI recipe & bug checks

On /conversations/<running-id> (no workspace attached; Paused conversation left the browser there) open the "..." menu, control-openhands browser click 'testid=conversation-name-context-menu >> testid=show-hooks-button' and control-openhands browser text 'testid=hooks-modal'. It reads Available Hooks, the workspace explanation and No hooks configured for this conversation. (the backend workspace root has no hooks.json). Run control-openhands browser network --clear and control-openhands browser click 'testid=hooks-modal >> testid=refresh-hooks': the text stays and control-openhands browser network shows POST /api/hooks. control-openhands browser click 'testid=close-hooks-modal' closes it.

SkillsF07.skills-modalMapped behavior

Scenario F07--skills · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Conversation name context menu” / “Show skills”.
  2. Do
    Choose “Skills modal” / “github Knowledge”.
  3. Do
    Press Escape.

Expect

Show Available Skills lists the loaded skills with expandable rows and Refresh.

Exact CLI recipe & bug checks

Open the menu, control-openhands browser click 'testid=conversation-name-context-menu >> testid=show-skills-button', control-openhands browser snapshot 'role=dialog' (Public skills 12 and one button per skill), control-openhands browser click 'testid=skills-modal >> role=button[name="github Knowledge"]'; the row expands to Triggers and Content. control-openhands browser press Escape closes it (browser count 'testid=skills-modal' is 0).

Project skills and configured hooksF07.skills-modal-project · F07.hooks-modalMapped behavior

Scenario F07--project-skills-and-configured-hooks · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Start a conversation with the prompt “Reply with the single word: hi”.
  2. Do
    Choose “Conversation name context menu” / “Show skills”.
  3. Do
    Choose “Skills modal” / “qa-f07-skill AgentSkills”.
  4. Do
    Choose “Skills modal” / “Refresh skills”.
  5. Do
    Press Escape.
  6. Do
    Choose “Conversation name context menu” / “Show hooks”.
  7. Do
    Choose “Hooks modal” / “Pre Tool Use 1 hook”.
  8. Do
    Choose “Close hooks modal”.
  9. Do
    Choose “Conversation name context menu” / “Show hooks”.
  10. Do
    Choose “Hooks modal” / “Refresh hooks”.
  11. Do
    Choose “Hooks modal” / “Stop 1 hook”.
  12. Do
    Choose “Close hooks modal”.

Expect

a workspace with .agents/skills/<name>/SKILL.md adds a "Project skills" section above the public ones; each row expands to its triggers and body. The list is read with the page and on Refresh; opening the dialog within 10 minutes of the last read shows the cached list.

Show Available Hooks lists the workspace's .openhands/hooks.json hooks by event (read when it first opens for a conversation after a page load, and on Refresh; a reopen within 5 minutes of the last read shows the cached list), or "No hooks configured for this conversation."

Exact CLI recipe & bug checks

Arrange (outside the UI) a project skill and a hook in the git fixture: mkdir -p <run>/workspace/qa-f07-repo/.agents/skills/qa-f07-skill <run>/workspace/qa-f07-repo/.openhands, write <run>/workspace/qa-f07-repo/.agents/skills/qa-f07-skill/SKILL.md with front matter name: qa-f07-skill, description: QA fixture skill for F07., triggers: [qaf07trigger] and the body QA_F07 skill body., and write <run>/workspace/qa-f07-repo/.openhands/hooks.json as {"pre_tool_use":[{"matcher":"terminal","hooks":[{"type":"command","command":"true","timeout":10}]}]}. Then control-openhands conversation start --workspace qa-f07-repo --prompt "Reply with the single word: hi" --wait --timeout 240 prints <hooks-id>; control-openhands api GET /api/conversations/<hooks-id> shows that hook_config.pre_tool_use holds the terminal matcher. On /conversations/<hooks-id> open the menu, control-openhands browser click 'testid=conversation-name-context-menu >> testid=show-skills-button' and control-openhands browser snapshot 'role=dialog': Project skills 1 with button "qa-f07-skill AgentSkills" above Public skills 12. control-openhands browser click 'testid=skills-modal >> role=button[name="qa-f07-skill AgentSkills"]' expands it to Triggers qaf07trigger Content QA_F07 skill body.; control-openhands browser click 'testid=skills-modal >> testid=refresh-skills' re-reads the workspace (browser network shows POST /api/skills). Close with control-openhands browser press Escape. Hooks: open the menu, control-openhands browser click 'testid=conversation-name-context-menu >> testid=show-hooks-button' and control-openhands browser snapshot 'testid=hooks-modal': one collapsed event, button "Pre Tool Use 1 hook". control-openhands browser click 'testid=hooks-modal >> role=button[name="Pre Tool Use 1 hook"]' expands it to Matcher terminal Commands true Type: command Timeout: 10s (control-openhands browser screenshot --feature F07.hooks-modal --name workspace-hooks). Close it with control-openhands browser click 'testid=close-hooks-modal' and write <run>/workspace/qa-f07-repo/.openhands/hooks.json as {"pre_tool_use":[{"matcher":"terminal","hooks":[{"type":"command","command":"true","timeout":10}]}],"stop":[{"hooks":[{"type":"command","command":"echo qa-f07-stop"}]}]}. A reopen keeps the list it read: run control-openhands browser network --clear, open the menu, control-openhands browser click 'testid=conversation-name-context-menu >> testid=show-hooks-button' again, then control-openhands browser network --filter /api/hooks (no request) and control-openhands browser snapshot 'testid=hooks-modal' (still only button "Pre Tool Use 1 hook"). Refresh re-reads the file: control-openhands browser click 'testid=hooks-modal >> testid=refresh-hooks', then control-openhands browser network --filter /api/hooks shows POST /api/hooks and the snapshot adds button "Stop 1 hook"; control-openhands browser click 'testid=hooks-modal >> role=button[name="Stop 1 hook"]' expands it to Matcher * Commands echo qa-f07-stop Type: command Timeout: 60s (the SDK defaults). Close with control-openhands browser click 'testid=close-hooks-modal'.

Agent toolsF07.agent-tools-modalMapped behavior

Scenario F07--agent-tools · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Conversation name context menu” / “Show agent tools”.
  2. Do
    Choose the dialog / “Available Tools”.
  3. Do
    Choose “System message modal” / “Toggle” / match 1.
  4. Do
    Choose “Close system message modal”.

Expect

Show Agent Tools & Metadata shows the system message and the tool list.

Exact CLI recipe & bug checks

Open the menu, control-openhands browser click 'testid=conversation-name-context-menu >> testid=show-agent-tools-button'; the dialog testid=system-message-modal has tabs System Message (selected, the system prompt) and Available Tools. control-openhands browser click 'role=dialog >> role=tab[name="Available Tools"]', control-openhands browser count 'testid=system-message-modal >> testid=toggle-button' (backend-provided count; 10 on Agent Server 1.54.0: terminal, file_editor, task_tracker, canvas_ui_control, launch_child_conversation, request_secret (local backends, see F14.agent-request), finish, think, switch_llm, invoke_skill), control-openhands browser click 'testid=system-message-modal >> testid=toggle-button >> nth=0' expands terminal with its description. Close with control-openhands browser click 'testid=close-system-message-modal'.

Export transcriptF07.export-transcriptKnown issueBlocked

Scenario F07--export-transcript · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Conversation name context menu” / “Export transcript”.
  2. Do
    Choose “Confirm transcript export”.
  3. Do
    Choose the dialog / “Standalone web page (.html)”.
  4. Do
    Choose the dialog / “Include timestamps”.
  5. Do
    Choose “Cancel transcript export”.
  6. Do
    Press Escape.

Expect

Export… downloads a Markdown or HTML transcript with optional tool details and timestamps.

Linked known issues

Known issue #17494 (F07.export-transcript): Transcript export leaks raw i18n template for ACP tool calls without a title. Live prerequisite: An authenticated ACP session that emits a titleless tool call, then Markdown and HTML export.

Exact CLI recipe & bug checks

Open the menu, control-openhands browser click 'testid=conversation-name-context-menu >> testid=export-transcript-button'; control-openhands browser snapshot 'role=dialog' shows Export conversation, radio Markdown document (.md) checked, Standalone web page (.html), and checkboxes Include tool call details and Include timestamps checked. control-openhands browser click 'testid=confirm-transcript-export' then control-openhands browser downloads --last 1 --inspect: a <ms>-conversation-<id>.md (the harness prefixes a timestamp to the suggested conversation-<id>.md) lands in <run>/private/downloads starting # QA_F07 renamed, **Model:** deepseek/deepseek-flash, ## User with <sub> timestamps. Reopen, control-openhands browser click 'role=dialog >> role=radio[name="Standalone web page (.html)"]', control-openhands browser click 'role=dialog >> role=checkbox[name="Include timestamps"]', export: the .html has <title>QA_F07 renamed</title> and no timestamps. Reopen once more: control-openhands browser click 'testid=cancel-transcript-export' closes it without a download (the file count in <run>/private/downloads is unchanged); browser press Escape and testid=close-transcript-export-modal close it too.

Download dataF07.download-zipKnown failureKnown issueBlocked

Scenario F07--download-data · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Conversation name context menu” / “Download trajectory”.

Expect

Download conversation data downloads conversation_<id>.zip.

Linked known issues

Known issue OpenHands/software-agent-sdk#5640 (F07.download-zip): Download conversation data can return404 after a warm-pool server initializes a custom conversation-storage path. Although closed upstream, the pinned SDK 1.55.0 still reads the default configuration path here; this remains a supported-version distinction, not a freshly reproduced failure. Live prerequisite: A deferred-init warm-pool Agent Server initialized with a non-default conversations_path; this launcher starts an already-initialized server.

Known failure OpenHands/software-agent-sdk#5539 (F07.download-zip), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0: three real overlapping receivers of one idle conversation made 929 requests in 20 seconds, including 31 completed corrupt ZIPs with status 200, 106 server 500s, 269 ingress 502s and 133 RemoteProtocolErrors. A sequential 2,000-request control returned valid ZIPs. Recipe (use the pinned SDK Python with httpx): python .agents/skills/verify-openhands/scripts/fixtures/download-trajectory-overlap.py --base-url <own-base-url> --conversation-id <id> --api-key-file <run>/private/session-key --output-dir <run>/private/trajectory-overlap --seconds 20 --clients 3 --rounds 5000 --slow-ms 2. It enforces loopback, request/duration/network/uncompressed/member budgets and bounded CRC reads; aborts and unsupported archives remain unverified. While it runs, use browser goto /conversations/<id>, open chat-pane-header >> ellipsis-button, click download-trajectory-button, then browser downloads --last 3 --inspect (all CLI commands with --run <run>). That Canvas click downloaded an inspectable ZIP; its particular result is not claimed corrupt. Private raw samples stay private. Evidence: backfill-18309-surface-rechecks/F07.download-zip/trajectory-overlap-5539-summary.json and overlapping-download-5539.png. The summary correction excludes two ambiguous Content-Length differences that could be intentional deadline aborts; only completed corrupt ZIPs count as corrupt downloads.

Exact CLI recipe & bug checks

Open the menu, control-openhands browser click 'testid=conversation-name-context-menu >> testid=download-trajectory-button', then control-openhands browser downloads. A <ms>-conversation_<id>.zip lands in <run>/private/downloads; browser downloads --last 1 --inspect lists <id-without-dashes>/base_state.json and bash_events/, and every LLM api_key in base_state.json is masked as asterisks (**********).

UsageF07.display-costMapped behavior

Scenario F07--usage · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Conversation name context menu” / “Display cost”.
  2. Wait
    Wait for “Usage panel” to appear.

Expect

Display Usage and Cost opens the right panel on the Usage tab.

Exact CLI recipe & bug checks

Open the menu, control-openhands browser click 'testid=conversation-name-context-menu >> testid=display-cost-button', control-openhands browser wait 'testid=usage-panel' and control-openhands browser attr 'testid=right-panel-toggle' aria-pressed (true). The panel shows Context Window, Token Usage and Total Cost (contents are F27).

Right panelF07.right-panel-toggleMapped behavior

Scenario F07--right-panel · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Right panel toggle”.
  2. Wait
    Wait for “Tabs pane header” to disappear.
  3. Do
    Reload the page.

Expect

the header panel button shows and hides the right tabs panel (closed again after a reload).

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=right-panel-toggle' and control-openhands browser attr 'testid=right-panel-toggle' aria-label: it alternates Show panel / Hide panel, and control-openhands browser wait 'testid=tabs-pane-header' --state hidden --timeout 5000 (after Show panel) or --state visible (after Hide panel) follows; a plain browser visible right after the click still reads the old value while the panel animates. With the panel open, control-openhands browser reload and browser attr 'testid=right-panel-toggle' aria-pressed reads false (closed by design; the selected tab is remembered).

OverviewF07.overview-toggleMapped behavior

Scenario F07--overview · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Conversation overview toggle”.
  2. Do
    Choose “Right panel toggle”.
  3. Wait
    Wait for “Conversation overview column” to disappear.
  4. Do
    Hover over “Conversation overview toggle”.
  5. Wait
    Wait for “Conversation overview peek” to appear.
  6. Wait
    Wait for “Conversation overview column” to appear.

Expect

the header (i) button shows and hides the Overview column, which is exclusive with the right panel.

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=conversation-overview-toggle', control-openhands browser visible 'testid=conversation-overview-panel' (true) and browser attr 'testid=conversation-overview-toggle' aria-label (Hide overview). Then control-openhands browser click 'testid=right-panel-toggle': control-openhands browser wait 'testid=conversation-overview-column' --state hidden --timeout 5000 succeeds (an immediate browser visible can still read true). With the panel open, control-openhands browser hover 'testid=conversation-overview-toggle' and control-openhands browser wait 'testid=conversation-overview-peek' --timeout 5000 shows the peek. Clicking the overview toggle now closes the panel (aria-pressed false) and shows the overview (browser wait 'testid=conversation-overview-column' --state visible). Click the overview toggle once more to restore the closed state.

Git actionsF07.git-actions-menuKnown failure

Scenario F07--git-actions · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Conversation git actions toggle”.
  2. Do
    Choose “Conversation git actions menu” / “Conversation git actions commit”.
  3. Do
    Press Escape.
  4. Do
    Enter “” in “Message”.

Expect

the header Git actions menu offers Commits, Pull, Push, Create PR and Create New Branch; each pre-fills the composer without sending, and Send becomes enabled so the user can submit or edit the prompt.

Known bug observations

Known failure (#18202, reproduced 2026-10-09 on main@3500d9e5): after Pull, Send stays disabled until the user types.

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=conversation-git-actions-toggle' and control-openhands browser testids 'testid=conversation-git-actions-menu' (-commit, -pull, -push, -create-pr, -create-new-branch, labels Commits, Pull, Push, Create PR, Create New Branch). control-openhands browser click 'testid=conversation-git-actions-menu >> testid=conversation-git-actions-commit' then control-openhands browser text 'testid=chat-input': Please review the current changes and create a git commit with a concise, descriptive message.; conversation events <id> --kinds MessageEvent shows no new message. control-openhands browser enabled 'testid=submit-button' should be true, without typing. Each item replaces the composer text: -pull gives Please pull the latest code from the repository., -create-new-branch gives Please create a new branch with a descriptive name related to the work you plan to do., and the Push and Create PR prompts name GitHub by default. control-openhands browser press Escape closes the open menu. Clear the composer with control-openhands browser fill 'testid=chat-input' ''. Known failure (#18202, reproduced 2026-10-09 on main@3500d9e5): after Pull, Send stays disabled until the user types.

Panel routeF07.panel-routeMapped behavior

Scenario F07--panel-route · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Setup
    Use the phone viewport.
  2. Do
    Open /conversations/<id>.
  3. Do
    Choose “Right panel toggle”.
  4. Do
    Choose “Conversation mobile panel back”.
  5. Do
    Open /conversations/<id>/panel.
  6. Wait
    Wait for “Conversation mobile panel back” to appear.
  7. Setup
    Use the tablet viewport.
  8. Setup
    Use the desktop viewport.
  9. Wait
    Wait for the element identified in the CLI recipe to appear.
  10. Do
    Open /conversations/<id>/panel.
  11. Wait
    Wait for the element identified in the CLI recipe to appear.

Expect

at phone width (up to 1024 px) the header panel button opens /conversations/<id>/panel, the tabs panel full width with a Back button; wider, that URL redirects to /conversations/<id> with the drawer open beside the chat.

Exact CLI recipe & bug checks

Run control-openhands browser viewport phone, open /conversations/<id> (control-openhands browser goto /conversations/<id>), control-openhands browser click 'testid=right-panel-toggle' --expect-url '/panel$' and control-openhands browser testids --filter mobile (conversation-mobile-panel-top, conversation-mobile-panel-back), then control-openhands browser click 'testid=conversation-mobile-panel-back' --expect-url '/conversations/[0-9a-f-]+$'. Direct entry at phone width: control-openhands browser goto /conversations/<id>/panel and control-openhands browser wait 'testid=conversation-mobile-panel-back' succeed (control-openhands browser screenshot --feature F07.panel-route --name phone-direct). The panel page serves every width up to 1024 px: after control-openhands browser viewport tablet the URL still ends /panel and control-openhands browser count 'testid=conversation-mobile-panel-back' is 1. Wider, there is no panel page: run control-openhands browser viewport desktop, then control-openhands browser wait-url '/conversations/[0-9a-f-]+$' (the app replaces /panel with /conversations/<id>), control-openhands browser count 'testid=conversation-mobile-panel-back' (0) and control-openhands browser attr 'testid=right-panel-toggle' aria-pressed (true: the drawer is open beside the chat). A desktop-width visit lands the same way (#18045): control-openhands browser goto /conversations/<id>/panel, control-openhands browser wait-url '/conversations/[0-9a-f-]+$', the same count (0) and aria-pressed (true); control-openhands browser screenshot --feature F07.panel-route --name desktop-redirect shows the chat with the drawer open beside it.

Git control barF07.git-control-barKnown issueBlocked

Scenario F07--git-control-bar · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Open /conversations/<git-id>.
  2. Do
    Reload the page.

Expect

under the composer, a folder workspace shows an inert workspace chip and the branch; with a GitHub origin the repo and branch chips link to GitHub and Pull, Push and Pull Request appear.

Linked known issues

Known issue #15543 (F07.git-control-bar): Bad parsing for branch retrieval. Live prerequisite: The original Git probe stdout/interleaving that temporarily puts a filename in the branch badge; dirty state alone is not a deterministic trigger.

Exact CLI recipe & bug checks

On /conversations/<git-id> (control-openhands browser goto /conversations/<git-id>; Panel route ends on /conversations/<id>) run control-openhands browser snapshot 'testid=interactive-chat-box': button "qa-f07-repo" [disabled] and the branch text main, no Pull/Push. Add a GitHub origin to the fixture (arrange step, outside the UI): git -C <run>/workspace/qa-f07-repo remote add origin https://github.com/qa-example/qa-f07-repo.git. Then control-openhands browser reload and the snapshot again: link qa-example/qa-f07-repo (https://github.com/qa-example/qa-f07-repo), link main (…/tree/main) and buttons Pull, Push, Pull Request.

Pull sends at onceF07.git-control-bar-sendBlocked

Scenario F07--pull-sends-at-once · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Choose “Interactive chat box” / “Pull”.
  2. Wait
    Wait for “User message” / containing “Please pull the latest code” to appear.

Expect

the Pull, Push and Pull Request chips send their git prompt to the agent at once.

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=interactive-chat-box >> role=button[name="Pull"][exact]' and control-openhands browser wait 'testid=user-message >> has-text=Please pull the latest code' --timeout 15000; conversation status <git-id> is running. Stop it right away with Stop Conversation as in F07.stop-confirm (the fake remote cannot be pulled). Push and Pull Request are blocked: they need a real remote and GitHub credentials.

Cloud-onlyF07.cloud-onlyKnown issueBlocked

Scenario F07--cloud-only · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Blocked without a Cloud backend. On local, confirm the absence: the menu testids (see F07.menu-open) have no share-publicly-button, and the git control bar shows no Connect Repo chip. With Cloud: the menu's Share publicly switch (share-publicly-button, then copy-share-link-button, open-share-link-button), the repo chip Connect Repo opening the Open repository modal (close-open-repository-modal), /conversations/task-<uuid> while a conversation starts, and the Unavailable for Archives tooltips (see F25).

Expect

Share publicly, Connect Repo, task-<uuid> start URLs, paused-sandbox resume, archived read-only state and the shared-conversation redirect exist only on a Cloud backend.

Linked known issues

Known issue #18136 (F07.cloud-only): Connect Repo on a cloud conversation does not save the repository on the server. Live prerequisite: An authenticated Cloud sandbox/repository provider and a second fresh browser to compare server repository metadata.

Exact CLI recipe & bug checks

Blocked without a Cloud backend. On local, confirm the absence: the menu testids (see F07.menu-open) have no share-publicly-button, and the git control bar shows no Connect Repo chip. With Cloud: the menu's Share publicly switch (share-publicly-button, then copy-share-link-button, open-share-link-button), the repo chip Connect Repo opening the Open repository modal (close-open-repository-modal), /conversations/task-<uuid> while a conversation starts, and the Unavailable for Archives tooltips (see F25).

PhoneF07.phoneMapped behavior

Scenario F07--phone · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Setup
    Use the phone viewport.
  2. Do
    Choose “Chat pane header” / “More options”.
  3. Do
    Press Escape.
  4. Setup
    Use the desktop viewport.

Expect

at 390 px the header keeps its controls and the info dialogs fit the viewport.

Exact CLI recipe & bug checks

Run control-openhands browser viewport phone, control-openhands browser testids 'testid=chat-pane-header' (adds sidebar-mobile-menu-toggle; all header controls visible), open each info dialog from the menu and control-openhands browser bbox 'testid=skills-modal' (likewise hooks-modal, system-message-modal): insideViewport is true and pageHorizontalOverflow false. Then control-openhands browser click 'testid=chat-pane-header >> testid=ellipsis-button' and control-openhands browser bbox 'testid=conversation-name-context-menu': inside the viewport too (x about 145, width about 237), and control-openhands browser screenshot --feature F07.phone --name header-menu shows every label whole (Show Available Skills, Download conversation data, Delete Conversation). control-openhands browser press Escape closes it. Return with control-openhands browser viewport desktop.

Whitespace-only header renameF07.rename-inlineKnown failure

Scenario F07--whitespace-only-header-rename · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Press Enter.

Expect

double-click the title (or menu Rename), Enter or blur saves with a toast; an empty value reverts.

Known bug observations

Known failure (reproduced 2026-10-12, #17061): Canvas submits PATCH with an empty title. Agent Server 1.54.0 rejects it with 422 and restores the prior title; a persistently blank server title was not observed.

Linked known issues

Known failure OpenHands/software-agent-sdk#5629 (F07.rename-inline), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0. A second server's native first load rewrote a newer saved title with its startup copy; the connected Canvas header showed that older title. Recipe: create an idle conversation on A and PATCH its title to QA_SHARED_BEFORE_B_5629_REPEAT; run pinned SDK Python python .agents/skills/verify-openhands/scripts/fixtures/shared-storage-server.py --run <run> --seconds 300 in the background. It starts only a bounded no-editor SDK B sharing this run's conversation directory/cipher/session key; its private server.json supplies the port and owned operator/PGID. After B listens, PATCH A to QA_SHARED_AFTER_B_5629_REPEAT and confirm disk. Run control-openhands service stop agent-server --run <run>, then python .../fixtures/shared-storage-server.py --run <run> --load-id <id>: native B /events/count returned 200 and disk reverted to BEFORE_B. Restart A only to serve Canvas, choose default-local and doctor; use the Add Backend recipe from F25 to connect B at its recorded port with the private key file, then open its card: the header reads BEFORE_B. SIGTERM only the recorded helper operator and verify its port/PGID gone; the helper also bounds runtime and terminates remaining group descendants. Evidence: backfill-18309-surface-rechecks/F07.rename-inline/metadata-repeat-before-b-load-5629.json, metadata-repeat-after-b-load-5629.json, shared-first-load-reverted-5629.png. The native first-load request is explicit; stale catalog display alone is insufficient proof of this overwrite.

Known failure OpenHands/software-agent-sdk#4800 (F07.rename-inline), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0. A refused rename changed the server's GET title while meta.json kept the previous title; graceful shutdown subsequently persisted the rejected title. Recipe: create an idle dummy-profile conversation, then double-click conversation-name-title, fill conversation-name-input with QA_META_BEFORE_4800 and press Enter; verify GET/disk. Record the original mode and temporarily chmod 0500 <run>/private/state/dev_conversations/<id-hex> to deny an atomic metadata replacement. Rename through that UI to QA_META_REFUSED_4800: the permission-denied toast/HTTP 500 appears, GET shows REFUSED, disk still shows BEFORE. Restore the exact original directory mode in finally, then restart, doctor, GET and reload (CLI with --run <run>): REFUSED was persisted during graceful shutdown. Only the owned temporary directory is changed; evidence contains title/timestamp fields, not stored secrets. Evidence: backfill-18309-surface-rechecks/F07.rename-inline/metadata-before-4800.json, metadata-denied-4800.json, refused-title-4800.png, metadata-after-graceful-restart-4800.json. Tags/autotitle and ownership-conflict branches remain untested.

Exact CLI recipe & bug checks

After the named-title check, run control-openhands browser network --clear, double-click testid=conversation-name-title, fill testid=conversation-name-input with one space, and control-openhands browser press Enter. Read control-openhands browser network --filter '/api/conversations' --bodies, then reload and read the title. Expected: restore the previous title with no write. Known failure (reproduced 2026-10-12, #17061): Canvas submits PATCH with an empty title. Agent Server 1.54.0 rejects it with 422 and restores the prior title; a persistently blank server title was not observed.

Already-deleted conversationF07.delete-confirm · F07.rename-inlineKnown failure

Scenario F07--already-deleted-conversation · Permalink · Source

Given

Use F07’s prerequisites and the common setup. Follow the family’s order when a script uses an earlier fixture or says “as above”.

Do — reading outline

  1. Do
    Expected: a stale action reports that the conversation is missing, and a stale rename never reports success. Known failure (reproduced 2026-10-16, SDK 1.55.0, OpenHands/software-agent-sdk#5800): confirming Delete after another client removed this disposable conversation returns400 and displays Bad Request, instead of a missing-conversation404. The separate stale-rename control receives PATCH200 but re-fetches missing metadata and displays not-found, so its false-success variant is prevented by Canvas. Pause/interrupt were not tested.

Expect

Delete Conversation names the title in a confirmation; Cancel keeps it, Confirm Delete removes it and returns to /conversations.

double-click the title (or menu Rename), Enter or blur saves with a toast; an empty value reverts.

Known bug observations

Known failure (reproduced 2026-10-16, SDK 1.55.0, OpenHands/software-agent-sdk#5800): confirming Delete after another client removed this disposable conversation returns400 and displays Bad Request, instead of a missing-conversation404. The separate stale-rename control receives PATCH200 but re-fetches missing metadata and displays not-found, so its false-success variant is prevented by Canvas. Pause/interrupt were not tested.

Linked known issues

Known failure OpenHands/software-agent-sdk#5629 (F07.rename-inline), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0. A second server's native first load rewrote a newer saved title with its startup copy; the connected Canvas header showed that older title. Recipe: create an idle conversation on A and PATCH its title to QA_SHARED_BEFORE_B_5629_REPEAT; run pinned SDK Python python .agents/skills/verify-openhands/scripts/fixtures/shared-storage-server.py --run <run> --seconds 300 in the background. It starts only a bounded no-editor SDK B sharing this run's conversation directory/cipher/session key; its private server.json supplies the port and owned operator/PGID. After B listens, PATCH A to QA_SHARED_AFTER_B_5629_REPEAT and confirm disk. Run control-openhands service stop agent-server --run <run>, then python .../fixtures/shared-storage-server.py --run <run> --load-id <id>: native B /events/count returned 200 and disk reverted to BEFORE_B. Restart A only to serve Canvas, choose default-local and doctor; use the Add Backend recipe from F25 to connect B at its recorded port with the private key file, then open its card: the header reads BEFORE_B. SIGTERM only the recorded helper operator and verify its port/PGID gone; the helper also bounds runtime and terminates remaining group descendants. Evidence: backfill-18309-surface-rechecks/F07.rename-inline/metadata-repeat-before-b-load-5629.json, metadata-repeat-after-b-load-5629.json, shared-first-load-reverted-5629.png. The native first-load request is explicit; stale catalog display alone is insufficient proof of this overwrite.

Known failure OpenHands/software-agent-sdk#4800 (F07.rename-inline), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0. A refused rename changed the server's GET title while meta.json kept the previous title; graceful shutdown subsequently persisted the rejected title. Recipe: create an idle dummy-profile conversation, then double-click conversation-name-title, fill conversation-name-input with QA_META_BEFORE_4800 and press Enter; verify GET/disk. Record the original mode and temporarily chmod 0500 <run>/private/state/dev_conversations/<id-hex> to deny an atomic metadata replacement. Rename through that UI to QA_META_REFUSED_4800: the permission-denied toast/HTTP 500 appears, GET shows REFUSED, disk still shows BEFORE. Restore the exact original directory mode in finally, then restart, doctor, GET and reload (CLI with --run <run>): REFUSED was persisted during graceful shutdown. Only the owned temporary directory is changed; evidence contains title/timestamp fields, not stored secrets. Evidence: backfill-18309-surface-rechecks/F07.rename-inline/metadata-before-4800.json, metadata-denied-4800.json, refused-title-4800.png, metadata-after-graceful-restart-4800.json. Tags/autotitle and ownership-conflict branches remain untested.

Exact CLI recipe & bug checks

Expected: a stale action reports that the conversation is missing, and a stale rename never reports success. Known failure (reproduced 2026-10-16, SDK 1.55.0, OpenHands/software-agent-sdk#5800): confirming Delete after another client removed this disposable conversation returns400 and displays Bad Request, instead of a missing-conversation404. The separate stale-rename control receives PATCH200 but re-fetches missing metadata and displays not-found, so its false-success variant is prevented by Canvas. Pause/interrupt were not tested.

sh known-bug F07.delete-confirm already-deleted

  control-openhands browser viewport desktop
  QA_ID="$(python3 - <<'PY'
  import json, os, pathlib, subprocess, tempfile
  base = pathlib.Path(os.environ["OH_VERIFY_RUN"]) / "workspace"
  base.mkdir(exist_ok=True)
  fixture = pathlib.Path(tempfile.mkdtemp(prefix="qa-surface-", dir=base))
  llm = {"model":"openai/gpt-4o", "base_url":"http://127.0.0.1:1/v1", "api_key":"qa-not-real", "num_retries":0, "timeout":2}
  def create(workspace, parent=None, tags=None):
      payload = {"agent":{"kind":"Agent", "llm":llm, "tools":[]}, "workspace":{"working_dir":str(workspace)}, "autotitle":False}
      if parent: payload["parent_conversation_id"] = parent
      if tags: payload["tags"] = tags
      request = fixture / "request.json"
      request.write_text(json.dumps(payload))
      result = subprocess.run(["control-openhands", "api", "POST", "/api/conversations", "--write", "--data", "@"+str(request), "--pick", "id"], check=True, capture_output=True, text=True)
      return json.loads(result.stdout)["value"]
  def git(directory, *args):
      subprocess.run(["git", "-C", str(directory), *args], check=True, capture_output=True)
  workspace = fixture / "workspace"
  workspace.mkdir()
  print(create(workspace))
  PY
  )"
  control-openhands browser goto "/conversations/$QA_ID"
  control-openhands browser click 'testid=chat-pane-header >> testid=ellipsis-button'
  control-openhands browser click 'testid=conversation-name-context-menu >> testid=delete-button'
  control-openhands assert --field status --equals 200 -- api DELETE "/api/conversations/$QA_ID" --write
  control-openhands browser network --clear
  control-openhands browser click 'role=dialog >> testid=confirm-button' --observe 'role=status' --observe-ms 1000
  control-openhands browser screenshot --feature F07.delete-confirm --name stale-delete
  control-openhands assert --field recent.0.status --equals 404 --bug-shows 400 -- browser network --filter "/api/conversations/$QA_ID"  # bug
Family notes, prerequisites for known issues & limits Known failureKnown issueBlocked
  • The stop and delete confirmation buttons carry cancel-button and confirm-button; role names inside role=dialog work too.
  • stop-button, show-skills-button, show-hooks-button and show-agent-tools-button also exist in the composer, and ellipsis-button in sidebar rows: always scope to testid=chat-pane-header or testid=conversation-name-context-menu.
  • Stop Runtime (status menu) and Stop Conversation (menu) both navigate to /conversations. On a finished local conversation the agent server keeps finished; use a running conversation to see paused and Start Runtime.
  • Rename has no Escape-to-cancel: Escape leaves the input open and the next blur saves whatever was typed.
  • Escape closes the status-dot menu, as it closes the "..." and Git actions menus (browser count 'testid=server-status-context-menu' is 0). A menu opened by click or Enter returns focus to server-status-menu-trigger; one opened by hover closes without moving focus (focus left on a menu item falls to body). Touch taps cannot be driven with the CLI, so the tap path is unverified.
  • The status menu reads Running / Stop Runtime for a finished local conversation too; only a paused one shows Server Stopped / Start Runtime.
  • Panel and Overview columns animate: read their state with browser wait <sel> --state hidden|visible, not an immediate browser visible.
  • The Skills dialog shows the list read with the page or at the last Refresh: a project skill added later appears after Refresh (or browser reload), not on reopening the dialog within 10 minutes. Its own text says to click Refresh.
  • tabs-pane-header and conversation-overview-column stay in the DOM when hidden: assert with browser visible or aria-pressed, not count.
  • browser visible 'testid=usage-panel' right after Display Usage and Cost can read false; browser wait first.
  • Exports and the zip land in <run>/private/downloads; the transcript holds the prompts and the zip holds the full event store. Review before sharing.
  • --observe accepts the same selector syntax as other verbs ('testid=chat-messages-skeleton') as well as CSS.
  • A sidebar row click lands on /conversations/<id>?backend=default-local: end --expect-url regexes without $ there.
  • role=button[name="Pull"] also matches Pull Request; add [exact].
  • The Open Workspace folder browser has no path field: walk it with folder-browser-up and folder-browser-entry-<name>. fixture git-repo puts the repo in <run>/workspace, three levels above the workspace favorite.
  • The repo, branch and Pull/Push chips come from git remote get-url origin in the workspace, read when the page loads: reload after changing the remote.
  • The Usage tab logs GET /api/llm/balance 404 on a local backend (F27); count it in browser errors --app-only but it is not an F07 failure.
  • The Overview drawer has no Automations, Skills, MCP or Secrets sections. The Overview menu offers Workspace and Git rows only, and its drawer opens only Pull Requests (F27.overview-drawer).
  • Dialogs move focus inside when they open and keep Tab inside: the Skills and Hooks dialogs start on their close button, Stop and Delete on cancel-button, Export on close-transcript-export-modal, Agent Tools on close-system-message-modal. In the Hooks dialog, focusing Refresh with Tab opens its tooltip, which takes the first Escape; a second Escape closes the dialog.
  • The Hooks dialog shows the workspace's hooks.json as of its last read (its first open for the conversation after a page load, or Refresh), not the hooks the conversation started with: after the hooks steps above, browser goto /conversations/<git-id> (started before the file existed; its hook_config is null) and opening the dialog lists both events too. A reopen within 5 minutes of that read shows the same list and sends no request: click Refresh after editing hooks.json.
  • A prompt- or agent-type hook shows its text under Commands (the prompt, or an agent hook's system_prompt). With the Hooks dialog closed (the hooks steps above end that way; otherwise control-openhands browser click 'testid=close-hooks-modal'), write <run>/workspace/qa-f07-repo/.openhands/hooks.json as {"stop":[{"hooks":[{"type":"prompt","prompt":"QA_F07 prompt hook"}]}],"session_start":[{"hooks":[{"type":"agent","system_prompt":"QA_F07 agent hook"}]}]}, run control-openhands browser goto /conversations/<hooks-id>, open the menu and control-openhands browser click 'testid=conversation-name-context-menu >> testid=show-hooks-button', click testid=hooks-modal >> testid=refresh-hooks, then role=button[name="Session Start 1 hook"] and role=button[name="Stop 1 hook"] inside testid=hooks-modal (an event expanded before a Refresh stays expanded, and a second click collapses it, so start from a reopened dialog). In control-openhands browser snapshot 'testid=hooks-modal' they expand, in that order, to Matcher * Commands QA_F07 agent hook Type: agent Timeout: 60s and Matcher * Commands QA_F07 prompt hook Type: prompt Timeout: 60s, and browser text 'testid=hooks-modal >> pre' returns QA_F07 agent hook and QA_F07 prompt hook (control-openhands browser screenshot --feature F07.hooks-modal --name prompt-agent-hooks). An agent hook without system_prompt shows no box: with {"session_start":[{"hooks":[{"type":"agent"}]}]}, close, reopen and Refresh, Session Start 1 hook expands to Matcher * Commands Type: agent Timeout: 60s and browser count 'testid=hooks-modal >> pre' is 0.
  • An agent hook shows its system_prompt, the text it runs with, even when it also carries a prompt, and an agent hook with only a prompt shows no box. With {"session_start":[{"hooks":[{"type":"agent","prompt":"QA_F07 stray prompt","system_prompt":"QA_F07 agent system prompt"}]}],"stop":[{"hooks":[{"type":"agent","prompt":"QA_F07 prompt only"}]}]} in hooks.json, close, reopen and Refresh the dialog, then expand both events: Session Start 1 hook reads Matcher * Commands QA_F07 agent system prompt Type: agent Timeout: 60s, Stop 1 hook reads Matcher * Commands Type: agent Timeout: 60s, and control-openhands browser count 'text=QA_F07 stray prompt' and browser count 'text=QA_F07 prompt only' are 0 (browser screenshot --feature F07.hooks-modal --name agent-hook-system-prompt). Agent hooks in hooks.json really run on the next turn in that workspace (the session-start one shows a Hook: SessionStart failed row), so write the recipe's two-event hooks.json (pre_tool_use and stop) back and Refresh afterwards.
  • With the Agent Server down, Refresh in the Hooks dialog shows its own fetch error (#18177): after control-openhands service stop agent-server on an open dialog, refresh-hooks and browser wait 'testid=hooks-modal >> text=Failed to fetch hooks' --timeout 30000, the dialog reads Failed to fetch hooks. Please try again later. under its explanation (browser count 'testid=hooks-modal >> text=Failed to fetch skills' is 0). The 502s and the closed WebSocket that browser errors --app-only then lists are the outage itself. Run it last, or restart afterwards.
  • After the user-message branch in F07.branch-from-message, Send stays disabled next to the prefilled text (browser enabled 'testid=submit-button' is false; Known failure #18202, see F05 Gotchas). Enter in the field sends it.
  • Known issue #18136 (F07.cloud-only): Connect Repo on a cloud conversation does not save the repository on the server. Live prerequisite: An authenticated Cloud sandbox/repository provider and a second fresh browser to compare server repository metadata.
  • Known issue #17494 (F07.export-transcript): Transcript export leaks raw i18n template for ACP tool calls without a title. Live prerequisite: An authenticated ACP session that emits a titleless tool call, then Markdown and HTML export.
  • Known issue #16876 (F07.history-skeleton): Planning-agent history flag stays true forever when sub-conversations fetch fails — infinite chat loading skeleton. Live prerequisite: A real planning sub-conversation whose batch lookup fails or yields null, with the main history available.
  • Known issue #16727 (F07.open-by-url): Conversation corrupted after VM restart: "Missing event file". Live prerequisite: A disposable VM interrupted at the event-write boundary or the original missing-event persisted fixture.
  • Known issue #16356 (F07.open-by-url): Conversation LLM config is frozen at creation time — resuming after API key rotation fails permanently with LLMAuthenticationError. Live prerequisite: A working provider key that can be rotated/revoked, an existing conversation using it, and a replacement profile.
  • Known issue #15543 (F07.git-control-bar): Bad parsing for branch retrieval. Live prerequisite: The original Git probe stdout/interleaving that temporarily puts a filename in the branch badge; dirty state alone is not a deterministic trigger.
  • Known issue OpenHands/software-agent-sdk#5640 (F07.download-zip): Download conversation data can return404 after a warm-pool server initializes a custom conversation-storage path. Although closed upstream, the pinned SDK 1.55.0 still reads the default configuration path here; this remains a supported-version distinction, not a freshly reproduced failure. Live prerequisite: A deferred-init warm-pool Agent Server initialized with a non-default conversations_path; this launcher starts an already-initialized server.
  • Known failure OpenHands/software-agent-sdk#5629 (F07.rename-inline), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0. A second server's native first load rewrote a newer saved title with its startup copy; the connected Canvas header showed that older title. Recipe: create an idle conversation on A and PATCH its title to QA_SHARED_BEFORE_B_5629_REPEAT; run pinned SDK Python python .agents/skills/verify-openhands/scripts/fixtures/shared-storage-server.py --run <run> --seconds 300 in the background. It starts only a bounded no-editor SDK B sharing this run's conversation directory/cipher/session key; its private server.json supplies the port and owned operator/PGID. After B listens, PATCH A to QA_SHARED_AFTER_B_5629_REPEAT and confirm disk. Run control-openhands service stop agent-server --run <run>, then python .../fixtures/shared-storage-server.py --run <run> --load-id <id>: native B /events/count returned 200 and disk reverted to BEFORE_B. Restart A only to serve Canvas, choose default-local and doctor; use the Add Backend recipe from F25 to connect B at its recorded port with the private key file, then open its card: the header reads BEFORE_B. SIGTERM only the recorded helper operator and verify its port/PGID gone; the helper also bounds runtime and terminates remaining group descendants. Evidence: backfill-18309-surface-rechecks/F07.rename-inline/metadata-repeat-before-b-load-5629.json, metadata-repeat-after-b-load-5629.json, shared-first-load-reverted-5629.png. The native first-load request is explicit; stale catalog display alone is insufficient proof of this overwrite.
  • Known failure OpenHands/software-agent-sdk#5539 (F07.download-zip), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0: three real overlapping receivers of one idle conversation made 929 requests in 20 seconds, including 31 completed corrupt ZIPs with status 200, 106 server 500s, 269 ingress 502s and 133 RemoteProtocolErrors. A sequential 2,000-request control returned valid ZIPs. Recipe (use the pinned SDK Python with httpx): python .agents/skills/verify-openhands/scripts/fixtures/download-trajectory-overlap.py --base-url <own-base-url> --conversation-id <id> --api-key-file <run>/private/session-key --output-dir <run>/private/trajectory-overlap --seconds 20 --clients 3 --rounds 5000 --slow-ms 2. It enforces loopback, request/duration/network/uncompressed/member budgets and bounded CRC reads; aborts and unsupported archives remain unverified. While it runs, use browser goto /conversations/<id>, open chat-pane-header >> ellipsis-button, click download-trajectory-button, then browser downloads --last 3 --inspect (all CLI commands with --run <run>). That Canvas click downloaded an inspectable ZIP; its particular result is not claimed corrupt. Private raw samples stay private. Evidence: backfill-18309-surface-rechecks/F07.download-zip/trajectory-overlap-5539-summary.json and overlapping-download-5539.png. The summary correction excludes two ambiguous Content-Length differences that could be intentional deadline aborts; only completed corrupt ZIPs count as corrupt downloads.
  • Known issue OpenHands/software-agent-sdk#5094 (F07.open-by-url): After ACP session/load fails, the backend can create a fresh model session while Canvas still shows the old context. Live prerequisite: An authenticated Codex ACP session whose original session/load fails while persisted history remains.
  • Known issue OpenHands/software-agent-sdk#4893 (F07.status-menu): A sandbox frozen mid-run can resume with a stale Running state that rejects normal resume requests. Live prerequisite: A disposable sandbox/VM frozen mid-run with a live model and persisted ownership state.
  • Known failure OpenHands/software-agent-sdk#4800 (F07.rename-inline), reproduced 2026-10-12 at fa1f724ab573, SDK 1.54.0. A refused rename changed the server's GET title while meta.json kept the previous title; graceful shutdown subsequently persisted the rejected title. Recipe: create an idle dummy-profile conversation, then double-click conversation-name-title, fill conversation-name-input with QA_META_BEFORE_4800 and press Enter; verify GET/disk. Record the original mode and temporarily chmod 0500 <run>/private/state/dev_conversations/<id-hex> to deny an atomic metadata replacement. Rename through that UI to QA_META_REFUSED_4800: the permission-denied toast/HTTP 500 appears, GET shows REFUSED, disk still shows BEFORE. Restore the exact original directory mode in finally, then restart, doctor, GET and reload (CLI with --run <run>): REFUSED was persisted during graceful shutdown. Only the owned temporary directory is changed; evidence contains title/timestamp fields, not stored secrets. Evidence: backfill-18309-surface-rechecks/F07.rename-inline/metadata-before-4800.json, metadata-denied-4800.json, refused-title-4800.png, metadata-after-graceful-restart-4800.json. Tags/autotitle and ownership-conflict branches remain untested.
  • Known issue #16877 (F07.open-by-url): Untitled terminal-status conversations are reported to keep the 3-second title polling interval. On frozen target fa1f724ab5737a17453ad6081129eb91c5a423c6, a real Local autotitle:false/idle conversation retained title:null, while eight browser batch GETs had 30.008–30.031-second intervals over 210 seconds. The direct adapter supplies a default display title. This does not exercise the Cloud null-title IDLE/FINISHED branch, which needs an approved Cloud conversation with a failed/unsupported title job. Run backfill-18309-coordinator-checks, Agent Server/SDK 1.54.0, automation 1.19.3; retained evidence/F07.open-by-url/16877-local-polling.json and 16877-local-idle.png. Arrange the empty conversation with api POST /api/conversations --write --data @<idle-autotitle-disabled-payload>, open /conversations/<id>, clear browser network, leave the tab foreground, then read browser network --filter ids= --last 100 and conversation status <id>. No model call or response was used.

Before you start

Use an OpenHands checkout at the source revision, with Node 24+, installed npm dependencies, uv / uvx, and a supported Chromium browser. The verification skill is the complete setup contract.

export PATH="$PWD/.agents/skills/verify-openhands/scripts:$PATH"
export OH_VERIFY_RUN=$(control-openhands launch --new --print-run)
control-openhands doctor
control-openhands onboard --skip
# Configure a model only for a scenario that needs one:
control-openhands llm preset deepseek
# When the run is complete:
control-openhands stop

F01 walks onboarding instead of skipping it. No-model checks run before model configuration. Keep OH_VERIFY_RUN set so commands address your own run. Desktop is 1440 × 1000; phone is 390 × 844. Model-backed checks need the configured provider key and budget.

Recipes run in source order within each family and can share fixtures. The page preserves that order; “as above” is a dependency on an earlier case. Setup commands and API writes arrange state. They do not prove a UI path. Follow the family cleanup steps.

A known failure preserves the intended expectation beside the observed defect. Executable known-bug blocks use xfail for a reproduced symptom and xpass when it stops reproducing. Cloud accounts, enterprise capabilities and other unavailable prerequisites remain explicit. “Not run” is different from “blocked”.

The maintained map describes application behavior at its pinned revision. Once a fix is verified in the application version under test, remove the obsolete failure note and keep the normal expectation. Dated evidence belongs in the backfill ledger.

Read the full map index and coverage boundaries, the CLI guide, or the earlier illustrated edition. The verification approach adapts Lauren Tan’s (@poteto) pstack verification-skill generators; attribution and adaptation.