Engel’s notebook

Workspace drawer: files and changes

F08 · 34 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.

F08 Workspace drawer: files and changes

34 behaviors · 37 scripts & contracts · Maintained source

Entry points & prerequisites

What this family covers

Next to the chat, a conversation has a right-hand drawer with tabs. This family covers the drawer itself (toggle, tab bar, overflow menu with pinning, the phone panel page) and its two workspace tabs. Files is a read-only browser of the conversation's working directory: a path row with copy, a strip of open files, a collapsible tree, and a Rich/Plain viewer for code, Markdown, HTML, images and binaries. Commits lists an "Uncommitted" row and the git history, newest first. Each expands into per-file Monaco diffs with old/diff/new modes. The Terminal, Browser, Planner, Task list and Usage tab bodies belong to F27. The Files tab has no upload, download or edit.

How a user gets here

  • Chat header: the panel toggle (right-panel-toggle, block-drawer icon, right of the overview i button). At desktop width it shows or hides the drawer; at 1024 px or narrower it navigates to the panel page.
  • Drawer tab bar (tabs-pane-header): conversation-tab-files, conversation-tab-commits. The ellipsis (tabs-pane-header >> testid=ellipsis-button) opens the menu with conversation-tabs-menu-open-<tab> and conversation-tabs-menu-pin-<tab>.
  • Direct URL /conversations/<id>/panel (the phone panel page at 1024 px or narrower; wider, it redirects to /conversations/<id> with the drawer open).
  • Overview (conversation-overview-toggle) → Changes row (conversation-overview-diffs) → Commits tab with Uncommitted expanded.
  • Files toolbar: File discovery (file-discovery-settings, left of files-tab-refresh) opens the File discovery dialog.
  • Workspace paths in agent messages written as inline code (markdown-file-path-link), plus path chips in file-editor tool cards (file-path-chip) → Files tab with that file selected.
  • There is no command-menu entry or keyboard shortcut for the drawer or these tabs.

Given

Preconditions:

  • Baseline state (launched, doctored, onboard --skip done) and control-openhands llm preset deepseek (three short model turns, deepseek-flash).
  • A git fixture: control-openhands fixture git-repo (creates <run>/workspace/qa-repo with README.md, src/calc.py, src/test_calc.py and one commit by QA Fixture).
  • The conversation runs in that repo. From / run control-openhands browser click 'testid=open-workspace-button', control-openhands browser click 'testid=workspace-dropdown', control-openhands browser click 'role=button[name="+ Add Workspace"]'. The folder browser opens at <run>/private/home, so then run control-openhands browser click 'testid=folder-browser-up' twice, control-openhands browser dblclick 'testid=folder-browser-entry-workspace', control-openhands browser dblclick 'testid=folder-browser-entry-qa-repo', control-openhands browser click 'testid=folder-browser-use' and control-openhands browser click 'testid=workspace-launch-button'. The composer now shows a qa-repo chip. Start with --stay, which keeps that chip; a plain conversation start navigates home and loses it.
  • Seed changes with a prompt file, because the command contains quotes. Write qa-f08-1.txt (anywhere outside the workspace) containing: In the current workspace directory run exactly this one bash command and then reply only "done": printf '# QA notes\n\nHello **world**\n' > qa-notes.md && printf '<h1>QA page</h1>\n' > qa-page.html && printf 'def sub(a, b):\n return a - b\n' >> src/calc.py && rm src/test_calc.py && printf 'PK\003\004' > qa-deck.pptx && head -c 64 /dev/urandom > qa-blob.bin && git status --short. Then run control-openhands conversation start --stay --prompt "$(cat qa-f08-1.txt)" --wait --timeout 240. Read <id> from its id (or control-openhands browser url); the browser stays on /conversations/<id>.
  • Viewport desktop (1440×1000) unless a bullet says phone.
Open the drawerF08.panel-toggleMapped behavior

Scenario F08--open-the-drawer · Permalink · Source

Given

Use F08’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
    Use browser tooltip as specified in the exact recipe.
  2. Do
    Choose “Right panel toggle”.

Expect

the chat-header toggle (Show panel/Hide panel, aria-pressed) opens and closes the drawer; the first open shows Files.

Exact CLI recipe & bug checks

Run control-openhands browser attr 'testid=right-panel-toggle' aria-pressed (false) and control-openhands browser tooltip 'testid=right-panel-toggle' (Show panel). Then run control-openhands browser click 'testid=right-panel-toggle', followed by control-openhands browser attr 'testid=right-panel-toggle' aria-label and control-openhands browser visible 'testid=files-tab'. The label is Hide panel, aria-pressed is true, and Files is visible with the workspace path and tree (browser screenshot --feature F08.panel-toggle --name drawer-open-files).

Switch and close by tabF08.tab-barMapped behavior

Scenario F08--switch-and-close-by-tab · Permalink · Source

Given

Use F08’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 tab commits”.
  2. Wait
    Wait for “Commit list” to appear.

Expect

the tab bar (Files, Commits, Planner, Terminal, Browser, Usage; Task list only when the agent has one) switches the drawer body, and only the active tab shows its label. Clicking the active tab closes the drawer.

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=conversation-tab-commits' and control-openhands browser wait 'testid=commit-list' --state visible --timeout 10000 (the first open shows Loading changes... for a moment, so an immediate browser visible can read false). Click testid=conversation-tab-commits again. right-panel-toggle aria-pressed becomes false and control-openhands browser visible 'testid=tabs-pane-header' is false. Reopen with right-panel-toggle.

ReloadF08.drawer-state-reloadMapped behavior

Scenario F08--reload · Permalink · Source

Given

Use F08’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
    Reload the page.

Expect

after a reload the drawer starts closed, and reopening it restores the last selected tab for that conversation.

Exact CLI recipe & bug checks

With Commits selected, run control-openhands browser reload, then control-openhands browser attr 'testid=right-panel-toggle' aria-pressed (false; the drawer is closed). Click testid=right-panel-toggle; control-openhands browser visible 'testid=commit-list' is true.

Overflow menuF08.tabs-menuMapped behavior

Scenario F08--overflow-menu · Permalink · Source

Given

Use F08’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 “Tabs pane header” / “More options”.
  2. Do
    Choose “Conversation tabs menu open usage”.
  3. Do
    Use browser mouse click as specified in the exact recipe.

Expect

the ellipsis menu at the end of the tab bar lists every tab. Choosing a row opens that tab and closes the menu, and so does clicking outside.

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=tabs-pane-header >> testid=ellipsis-button' and control-openhands browser testids --filter conversation-tabs-menu; there is one open row and one pin row per tab. Run control-openhands browser click 'testid=conversation-tabs-menu-open-usage'; then control-openhands browser count 'testid=conversation-tabs-menu-open-usage' is 0 (menu closed) and the bar's active label is Usage. Reopen the menu and run control-openhands browser mouse-click 560 600 (over the chat); the menu count is 0.

Pin/unpinF08.tabs-pinMapped behavior

Scenario F08--pin-unpin · Permalink · Source

Given

Use F08’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 tabs menu pin usage”.
  2. Do
    Use browser mouse click as specified in the exact recipe.
  3. Do
    Reload the page.
  4. Do
    Choose “Conversation tabs menu open files”.

Expect

the pill on each menu row pins or unpins a tab. Unpinning the active tab selects another pinned one, and the choice persists across reloads.

Exact CLI recipe & bug checks

With Usage active, open the menu and run control-openhands browser click 'testid=conversation-tabs-menu-pin-usage', then control-openhands browser attr 'testid=conversation-tabs-menu-pin-usage' aria-label (Pin tab to bar; aria-pressed is false). Close the menu with browser mouse-click 560 600, then run control-openhands browser count 'testid=tabs-pane-header >> testid=conversation-tab-usage'. The count is 0 and Planner is now active. Run control-openhands browser reload and reopen the drawer; the count is still 0. To restore, open the menu, click testid=conversation-tabs-menu-pin-usage (label Unpin tab from bar), then control-openhands browser click 'testid=conversation-tabs-menu-open-files'. The usage tab count is 1 again.

ResizeF08.drawer-resizeMapped behavior

Scenario F08--resize · Permalink · Source

Given

Use F08’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
    Use browser drag as specified in the exact recipe.
  2. Do
    Reload the page.
  3. Do
    Use browser drag as specified in the exact recipe.

Expect

dragging the chat/drawer divider resizes both panes (chat 30–80 %, default 50 %, stored in desktop-layout-panel-width).

Exact CLI recipe & bug checks

The divider has no test id; its grip is the first .cursor-ew-resize on the page (the second is the Files tree divider). With the drawer open, control-openhands browser bbox 'testid=chat-interface' and control-openhands browser bbox 'testid=tabs-pane-header' are about 570 px wide each and control-openhands browser eval "localStorage.getItem('desktop-layout-panel-width')" is "50". Run control-openhands browser drag '.cursor-ew-resize >> nth=0' --by 200,0: the chat is 770 px, the drawer about 369 px and the key about 67.5. --by 900,0 clamps at "80", --by -1200,0 at "30" (chat 342 px), and after browser reload plus the toggle the chat is still 342 px. Restore with control-openhands browser drag '.cursor-ew-resize >> nth=0' --by 228,0 (chat 570 px, key "50").

ArchivedF08.archived-disabledBlocked

Scenario F08--archived · Permalink · Source

Given

Use F08’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
    Use browser tooltip as specified in the exact recipe.

Expect

on an archived conversation the toggle is disabled with the tooltip Unavailable for Archives.

Exact CLI recipe & bug checks

Blocked locally: archived status comes from a Cloud sandbox (sandbox_status). On such a conversation, browser enabled 'testid=right-panel-toggle' should be false and browser tooltip 'testid=right-panel-toggle' should read Unavailable for Archives.

Workspace pathF08.files-workspace-pathMapped behavior

Scenario F08--workspace-path · Permalink · Source

Given

Use F08’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
    Run control-openhands browser attr 'testid=files-tab-workspace-path-value' title (ends in /workspace/qa-repo). Click testid=files-tab-workspace-path >> testid=copy-to-clipboard. Immediately afterwards, control-openhands browser attr 'testid=files-tab-workspace-path >> testid=copy-to-clipboard' aria-label is Copied to clipboard and browser enabled on the same button is false. control-openhands browser clipboard returns the full path. About 3 s later the label is Copy to clipboard again.

Expect

Workspace: <working_dir> with the full path in its tooltip; the copy button turns into Copied to clipboard for about 2 s.

Exact CLI recipe & bug checks

Run control-openhands browser attr 'testid=files-tab-workspace-path-value' title (ends in /workspace/qa-repo). Click testid=files-tab-workspace-path >> testid=copy-to-clipboard. Immediately afterwards, control-openhands browser attr 'testid=files-tab-workspace-path >> testid=copy-to-clipboard' aria-label is Copied to clipboard and browser enabled on the same button is false. control-openhands browser clipboard returns the full path. About 3 s later the label is Copy to clipboard again.

TreeF08.files-treeKnown issueBlocked

Scenario F08--tree · Permalink · Source

Given

Use F08’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 “Files tab refresh”.
  2. Do
    Choose “File tree dir src”.
  3. Do
    Choose “File tree file src/calc.py”.

Expect

a tree of folders, collapsed at first, and files. Clicking a file selects it and adds it to the open-file strip.

Linked known issues

Known issue #18108 (F08.files-tree): Files tab shows "No files in workspace" on cloud when the agent writes outside /workspace/project. Live prerequisite: An authenticated Enterprise Cloud sandbox, no repository selected, with files outside its default working directory.

Known issue #17773 (F08.files-tree): Files tab always shows "No files in workspace" on Windows local backend. Live prerequisite: A Windows local Agent Server using cmd.exe; this run is macOS.

Known issue #16331 (F08.files-tree): file preview panel stops working during streaming. Live prerequisite: A real streaming/tool-running conversation while selecting a second workspace file.

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=files-tab-refresh' first (see Gotchas: files the agent made with bash are missing until a refresh). Then control-openhands browser attr 'testid=file-tree-dir-src' aria-expanded (false), control-openhands browser click 'testid=file-tree-dir-src' (now true) and control-openhands browser click 'testid=file-tree-file-src/calc.py'. control-openhands browser text 'testid=file-content-viewer-highlighted' shows the 4 lines def add…return a - b, and testid=file-quick-row-item-src/calc.py exists.

Tree toggleF08.files-tree-toggleMapped behavior

Scenario F08--tree-toggle · Permalink · Source

Given

Use F08’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 “File quick row tree toggle”.

Expect

the list-tree button hides or shows the tree, and the choice persists per conversation. The tree is 224 px wide by default.

Exact CLI recipe & bug checks

Run control-openhands browser bbox 'testid=files-tab-tree' (width 224). Then control-openhands browser click 'testid=file-quick-row-tree-toggle', control-openhands browser attr 'testid=file-quick-row-tree-toggle' aria-label (Show file tree) and control-openhands browser count 'testid=files-tab-tree' (0). Reload and reopen the drawer: the count is still 0. Click the toggle again to restore (count 1).

Tree resizeF08.files-tree-resizeMapped behavior

Scenario F08--tree-resize · Permalink · Source

Given

Use F08’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
    Use browser drag as specified in the exact recipe.
  2. Do
    Reload the page.

Expect

dragging the divider between the tree and the preview resizes the tree (160–480 px, default 224) and the width persists across reloads (files-tab-tree-width in localStorage).

Exact CLI recipe & bug checks

Run control-openhands browser drag 'testid=files-tab-tree-resize-handle >> .cursor-ew-resize' --by 100,0; browser bbox 'testid=files-tab-tree' is 324 wide and control-openhands browser eval "localStorage.getItem('files-tab-tree-width')" is "324". --by 500,0 clamps at 480, --by -600,0 at 160, and the width survives browser reload plus the toggle. Restore with --by 64,0 (224).

File discoveryF08.files-discovery-settingsMapped behavior

Scenario F08--file-discovery · Permalink · Source

Given

Use F08’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 “File discovery settings”.
  2. Do
    Enter “-1” in the element identified in the CLI recipe.
  3. Do
    Enter “$(printf '.git\nnode_modules\nsrc')” in the element identified in the CLI recipe.
  4. Do
    Choose “Files tab refresh”.
  5. Do
    Reload the page.
  6. Do
    Press Escape.
  7. Setup
    Use the phone viewport.
  8. Do
    Open /conversations/<id>/panel.
  9. Do
    Choose “Conversation tab files”.

Expect

the Files toolbar's File discovery button opens a dialog (excluded directory patterns, maximum files, symbolic links) whose Save applies to every conversation on that workspace and backend; an excluded directory leaves the tree, Restore defaults brings the default patterns back, an invalid limit disables Save.

Exact CLI recipe & bug checks

With the drawer open on Files (open it with right-panel-toggle and click testid=conversation-tab-files only if Files is not already the active tab: clicking the active tab closes the drawer), run control-openhands browser click 'testid=file-discovery-settings' and control-openhands browser snapshot 'role=dialog[name="File discovery"]': Applies to this workspace on this backend, including other conversations using it., textbox Excluded directories (.git, node_modules, .venv, … target), spinbutton Maximum files (0 for unlimited) 2000, checkbox Include symbolic links to files, buttons Restore defaults, Cancel, Save. With D='role=dialog[name="File discovery"]': control-openhands browser fill "$D >> role=spinbutton" '-1' makes control-openhands browser enabled "$D >> role=button[name=\"Save\"]" false; 2000 makes it true. Run control-openhands browser fill "$D >> role=textbox" "$(printf '.git\nnode_modules\nsrc')" and click Save: the dialog closes, control-openhands api GET /api/settings holds workspace_file_discovery keyed by the workspace path with excludedPatterns .git, node_modules, src, and after control-openhands browser click 'testid=files-tab-refresh' control-openhands browser count 'testid=file-tree-dir-src' is 0, also after browser reload and reopening the drawer (browser screenshot --feature F08.files-discovery-settings --name dialog before saving). Restore: reopen the dialog, click Restore defaults (the textbox returns to the default list; the dialog stays open), then Save; after Refresh the file-tree-dir-src count is 1. control-openhands browser press Escape closes the dialog without saving. At phone width the dialog fits the viewport: on the 390 px /panel page (control-openhands browser viewport phone, browser goto /conversations/<id>/panel, browser click 'testid=conversation-tab-files', then file-discovery-settings) control-openhands browser bbox 'testid=file-discovery-settings-modal' is about 351 px wide at x about 20 with insideViewport true (520 px at desktop), and browser screenshot --feature F08.files-discovery-settings --name phone-fits shows the title, the textbox and Restore defaults, Cancel, Save whole. Measure the file-discovery-settings-modal test id: role=dialog is the full-screen backdrop. Focus starts in the dialog (the Excluded directories textbox) and Tab cycles its fields and buttons without leaving it.

Rich/PlainF08.files-rich-plainKnown issueBlocked

Scenario F08--rich-plain · Permalink · Source

Given

Use F08’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 “Files tab content mode toggle option plain”.
  2. Do
    Reload the page.

Expect

Rich highlights code, renders Markdown and shows HTML in an iframe sandboxed without scripts. Plain shows highlighted source. The mode persists across reloads.

Linked known issues

Known issue #15501 (F08.files-rich-plain): Generated-page JavaScript was blocked by an iframe sandbox. Files Rich intentionally disables scripts; the report does not identify whether Files or Browser was used. Live prerequisite: The original preview panel and file URL or minimal page identifying the intended surface; Files Rich deliberately blocks scripts, while Browser is separate.

Known issue OpenHands/software-agent-sdk#5784 (F08.files-rich-plain, F08.files-fallbacks, F08.files-open-new-window): a path-prefixed Agent Server can set a workspace cookie whose Path omits the prefix, leaving HTML/image/file previews unauthorized. Live prerequisite: an authenticated path-prefixed owned Local server with OH_WEB_URL and a real cookie-authenticated preview; the ordinary root-path launch cannot exercise that cookie Path.

Exact CLI recipe & bug checks

Click testid=file-tree-file-qa-notes.md; testid=file-content-viewer-markdown exists. Click testid=file-tree-file-qa-page.html, then run control-openhands browser attr 'testid=file-content-viewer-iframe' sandbox (allow-same-origin) and control-openhands browser eval "document.querySelector('[data-testid=file-content-viewer-iframe]').contentDocument.body.innerText" (QA page). Run control-openhands browser click 'testid=files-tab-content-mode-toggle-option-plain'; control-openhands browser text 'testid=files-tab-content >> testid=file-content-viewer-highlighted' reads 1<h1>QA page</h1>. After browser reload and reopening, control-openhands browser attr 'testid=files-tab-content-mode-toggle-option-plain' aria-checked is true. Switch back with testid=files-tab-content-mode-toggle-option-rich.

Open in new windowF08.files-open-new-windowKnown issueBlocked

Scenario F08--open-in-new-window · Permalink · Source

Given

Use F08’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 “Files tab open in new window”.
  2. Do
    Use browser tabs as specified in the exact recipe.
  3. Do
    Use browser tab as specified in the exact recipe.
  4. Do
    Use browser close tab as specified in the exact recipe.

Expect

the external-link icon opens the file from the workspace fileserver in a new browser tab.

Linked known issues

Known issue OpenHands/software-agent-sdk#5784 (F08.files-rich-plain, F08.files-fallbacks, F08.files-open-new-window): a path-prefixed Agent Server can set a workspace cookie whose Path omits the prefix, leaving HTML/image/file previews unauthorized. Live prerequisite: an authenticated path-prefixed owned Local server with OH_WEB_URL and a real cookie-authenticated preview; the ordinary root-path launch cannot exercise that cookie Path.

Exact CLI recipe & bug checks

With qa-page.html selected, run control-openhands browser click 'testid=files-tab-open-in-new-window' and control-openhands browser tabs. Page 1's URL is /api/conversations/<id>/workspace/qa-page.html?v=<n>. Run control-openhands browser tab 1, control-openhands browser text 'body' (QA page) and control-openhands browser close-tab 1.

Open-file stripF08.files-open-tabsMapped behavior

Scenario F08--open-file-strip · Permalink · Source

Given

Use F08’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 “File quick row item qa notes.md”.
  2. Do
    Choose “File quick row close qa notes.md”.
  3. Do
    Reload the page.

Expect

the open-file strip, each tab with a close X. Closing the selected file selects a neighbour. Open files and the selection survive a reload and do not leak into other conversations.

Exact CLI recipe & bug checks

With several files opened, run control-openhands browser attr 'testid=file-quick-row-item-qa-page.html' title (the path). Select qa-notes.md with control-openhands browser click 'testid=file-quick-row-item-qa-notes.md', then close it with control-openhands browser click 'testid=file-quick-row-close-qa-notes.md'. control-openhands browser eval "[...document.querySelectorAll('[data-testid^=file-quick-row-item-]')].filter(e=>e.getAttribute('aria-selected')==='true').map(e=>e.dataset.testid)" names the neighbour. Run browser reload, reopen the drawer and control-openhands browser text 'testid=file-quick-row': the same files remain. Opening a different conversation (see Commits empty state) shows no quick-row items: browser text 'testid=file-quick-row' is empty. browser testids omits strip tabs scrolled out of view; count them with browser eval "[...document.querySelectorAll('[data-testid^=file-quick-row-item-]')].length".

No selectionF08.files-no-selectionMapped behavior

Scenario F08--no-selection · Permalink · Source

Given

Use F08’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
    Close every open file with its file-quick-row-close-<path> button. control-openhands browser text 'testid=files-tab-content' reads Select a file to view its contents, and control-openhands browser count 'testid=files-tab-content-mode-toggle' is 0.

Expect

with no file selected the pane reads Select a file to view its contents and the Rich/Plain toggle is absent.

Exact CLI recipe & bug checks

Close every open file with its file-quick-row-close-<path> button. control-openhands browser text 'testid=files-tab-content' reads Select a file to view its contents, and control-openhands browser count 'testid=files-tab-content-mode-toggle' is 0.

Second agent turn: commit, binaries, auto-refreshF08.files-auto-refresh · F08.files-refreshKnown failureKnown issueBlocked

Scenario F08--second-agent-turn-commit-binaries-auto-refresh · Permalink · Source

Given

Use F08’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 “$(cat qa-f08-2.txt)”.

Expect

files the agent writes appear without a manual refresh.

Refresh files re-reads the file list and the selected file's content.

Known bug observations

Known failure (reproduced 2026-10-06): neither changes until Refresh, because the auto-refresh hook skips the file queries for shell commands on purpose (see Gotchas). Repro candidate, not yet filed; a report would ask to revisit that choice. Click testid=files-tab-refresh once: the list gains the three files and the open preview ends with edited by agent.

Linked known issues

Known issue #16949 (F08.files-refresh): Keep Files and Changes views source-driven after refresh. Live prerequisite: An authenticated Cloud repository for the refresh-query mismatch, or real file-editor observations causing an in-place M-to-D Git refetch.

Exact CLI recipe & bug checks

Keep Files open with qa-notes.md selected in Rich mode. Write qa-f08-2.txt with: In the current workspace directory run exactly this one bash command and then reply only "done": printf 'BIN\000\001' > qa-nul.bin && printf 'PK\003\004\000\000' > qa-real.docx && python3 -c "import base64;open('qa-dot.png','wb').write(base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='))" && printf '\n- edited by agent\n' >> qa-notes.md && git add qa-notes.md && git -c user.name=QA-Agent -c user.email=qa@example.invalid commit -qm 'QA agent commit' && git log --oneline. Run control-openhands conversation start --stay --prompt "$(cat qa-f08-2.txt)" --wait --timeout 240, then control-openhands browser testids 'testid=files-tab-tree' and control-openhands browser text 'testid=file-content-viewer-markdown'. Expected: qa-nul.bin, qa-real.docx and qa-dot.png are listed, and the preview ends with edited by agent. Known failure (reproduced 2026-10-06): neither changes until Refresh, because the auto-refresh hook skips the file queries for shell commands on purpose (see Gotchas). Repro candidate, not yet filed; a report would ask to revisit that choice. Click testid=files-tab-refresh once: the list gains the three files and the open preview ends with edited by agent.

FallbacksF08.files-fallbacksKnown issueBlocked

Scenario F08--fallbacks · Permalink · Source

Given

Use F08’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
    After a refresh, click testid=file-tree-file-qa-nul.bin (testid=file-content-viewer-binary-fallback, text Binary file – preview not available). Then click testid=file-tree-file-qa-real.docx (testid=file-content-viewer-unsupported-document, Preview isn't available for Word files.). Then click testid=file-tree-file-qa-dot.png and run control-openhands browser eval "document.querySelector('[data-testid=file-content-viewer-image] img').naturalWidth" (1). In Plain mode the PNG shows the binary fallback. qa-deck.pptx and qa-blob.bin from the first prompt have no NUL byte and render as text (see Gotchas).

Expect

images render as <img> in Rich mode. Files with NUL bytes show Binary file – preview not available, and Office files show Preview isn't available for Word files. (or PowerPoint/Excel).

Linked known issues

Known issue OpenHands/software-agent-sdk#5784 (F08.files-rich-plain, F08.files-fallbacks, F08.files-open-new-window): a path-prefixed Agent Server can set a workspace cookie whose Path omits the prefix, leaving HTML/image/file previews unauthorized. Live prerequisite: an authenticated path-prefixed owned Local server with OH_WEB_URL and a real cookie-authenticated preview; the ordinary root-path launch cannot exercise that cookie Path.

Exact CLI recipe & bug checks

After a refresh, click testid=file-tree-file-qa-nul.bin (testid=file-content-viewer-binary-fallback, text Binary file – preview not available). Then click testid=file-tree-file-qa-real.docx (testid=file-content-viewer-unsupported-document, Preview isn't available for Word files.). Then click testid=file-tree-file-qa-dot.png and run control-openhands browser eval "document.querySelector('[data-testid=file-content-viewer-image] img').naturalWidth" (1). In Plain mode the PNG shows the binary fallback. qa-deck.pptx and qa-blob.bin from the first prompt have no NUL byte and render as text (see Gotchas).

File-editor edits auto-refresh; deleted file errorsF08.files-auto-refresh · F08.files-load-errorKnown issueBlocked

Scenario F08--file-editor-edits-auto-refresh-deleted-file-errors · Permalink · Source

Given

Use F08’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 “Do two steps, then reply only "done". Step 1: run the bash command: rm qa-blob.bin. Step 2: use your file editor tool (not bash) to create the file qa-editor.txt in the current workspace directory with the single line: created by file editor”.

Expect

files the agent writes appear without a manual refresh.

a selected file that can no longer be read shows the error (Failed to read <path>: 404).

Linked known issues

Known issue #16689 (F08.files-load-error): When workspace/ paths throw a 401, call the workspace-session endpoint. Live prerequisite: The original fresh-VM URL, auth/cookie state and exact request sequence; the report says the trigger is unclear.

Known issue #15567 (F08.files-load-error): Workspace file preview returns 401 Unauthorized on non-HTTPS non-loopback hosts. Live prerequisite: A plain-HTTP non-loopback deployment and browser accessing that origin, with workspace-cookie observation.

Known issue #15519 (F08.files-load-error): "files" tab in Files side panel throws 401 Unauthorized. Live prerequisite: The reported Linux Docker deployment/auth topology; this isolated run uses the macOS source launcher.

Known issue OpenHands/software-agent-sdk#4562 (F08.files-load-error): Plain-HTTP non-loopback workspace previews can return401 after the browser rejects the workspace-session cookie. Live prerequisite: A plain-HTTP non-loopback same-origin browser deployment with real workspace-cookie observation.

Exact CLI recipe & bug checks

Keep Files open with testid=file-tree-file-qa-blob.bin selected. Run control-openhands conversation start --stay --prompt 'Do two steps, then reply only "done". Step 1: run the bash command: rm qa-blob.bin. Step 2: use your file editor tool (not bash) to create the file qa-editor.txt in the current workspace directory with the single line: created by file editor' --wait --timeout 240. Without clicking Refresh, control-openhands browser count 'testid=file-tree-file-qa-editor.txt' is 1 and browser count 'testid=file-tree-file-qa-blob.bin' is 0 (the editor observation refetches the whole list). qa-blob.bin stays in the open-file strip, and the same observation re-reads the open file: control-openhands browser text 'testid=file-content-viewer-error' reads Failed to read qa-blob.bin: 404 and control-openhands browser network --filter qa-blob shows the 404 (browser screenshot --feature F08.files-load-error --name deleted-open-file).

Open from chatF08.files-open-from-chatMapped behavior

Scenario F08--open-from-chat · Permalink · Source

Given

Use F08’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
    Follow the quoted command in the exact recipe.
  2. Do
    Choose “Agent message” / nth=-1 / “Markdown file path”.
  3. Do
    Choose “File quick row item qa notes.md”.
  4. Setup
    Use the phone viewport.
  5. Setup
    Use the desktop viewport.
  6. Wait
    Wait for the element identified in the CLI recipe to appear.

Expect

clicking a workspace path in an agent (or user) message opens the drawer on Files with that file selected.

Exact CLI recipe & bug checks

Run control-openhands conversation start --stay --prompt 'Without running any tool, reply with exactly this text and nothing else: See src/calc.py for the code.' --wait --timeout 180. Close the drawer, then run control-openhands browser click 'testid=agent-message >> nth=-1 >> testid=markdown-file-path-link'. right-panel-toggle aria-pressed turns true, testid=files-tab is visible and file-quick-row-item-src/calc.py is the selected tab. The same path in the prompt bubble works too: close the drawer and click 'testid=user-message >> testid=markdown-file-path-link'. Phone: select another file (control-openhands browser click 'testid=file-quick-row-item-qa-notes.md'), run control-openhands browser viewport phone, then the same agent-message click with --expect-url '/panel$': the URL becomes /conversations/<id>/panel, browser visible 'testid=files-tab' is true and control-openhands browser attr 'testid=file-quick-row-item-src/calc.py' aria-selected is true (browser screenshot --feature F08.files-open-from-chat --name phone-after-click). Return with control-openhands browser viewport desktop: the app replaces /panel with /conversations/<id> (control-openhands browser wait-url '/conversations/[^/]+$') and shows the drawer beside the chat.

Tool-card path chipF08.files-open-from-tool-chipMapped behavior

Scenario F08--tool-card-path-chip · Permalink · Source

Given

Use F08’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 “2 actions completed”.
  2. Do
    Choose “Collapse actions” / “Expand” / nth=-1.
  3. Do
    Choose “File path chip”.

Expect

the path chip on a file-editor tool card (absolute path) opens the drawer on Files with that file selected.

Exact CLI recipe & bug checks

After the file-editor turn above, close the drawer and expand its tool group and card: control-openhands browser click 'text=2 actions completed', then control-openhands browser click 'role=region[name="Collapse actions"] >> role=button[name="Expand"] >> nth=-1' (the Create qa-editor.txt card). control-openhands browser text 'testid=file-path-chip' is the absolute <run>/workspace/qa-repo/qa-editor.txt. Run control-openhands browser click 'testid=file-path-chip': right-panel-toggle aria-pressed turns true, the selected strip tab is file-quick-row-item-qa-editor.txt (relative) and browser text 'testid=files-tab-content' ends with created by file editor.

Commits listF08.uncommitted-changes · F08.commit-rowsKnown issueBlocked

Scenario F08--commits-list · Permalink · Source

Given

Use F08’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 “Uncommitted changes row toggle”.
  2. Do
    Choose “Commit row” / containing “Initial fixture commit” / “Commit row toggle”.

Expect

the top row reads -, Uncommitted and a file count; expanding it lists the changed paths.

each commit row shows the short SHA, subject, author (only when there are several authors) and relative time. Rows are single-open accordions, and new agent commits appear without a reload.

Linked known issues

Known issue #16716 (F08.uncommitted-changes): Diff view is regularly out of sync. Live prerequisite: The original simultaneous-agent/shared-repository state or a deterministic stale-diff trigger; the report only suggests that timing.

Known issue #16454 (F08.uncommitted-changes): Diff list is showing duplicate entries. Live prerequisite: The original Git changes response and update sequence producing duplicate rows; the report provides no trigger.

Known issue #15692 (F08.uncommitted-changes): Diff still shows after agent has committed and pushed. Live prerequisite: A real agent commit/push terminal observation while the diff remains mounted, plus a disposable Git remote.

Exact CLI recipe & bug checks

Click testid=conversation-tab-commits and run control-openhands browser text 'testid=commit-list'. After the first prompt it reads -, Uncommitted, 6 files, then 4bf8272-style SHA, Initial fixture commit and … ago; after the second prompt QA agent commit appears above it, with author names QA-Agent and QA Fixture shown because there are now two authors. Run control-openhands browser click 'testid=uncommitted-changes-row-toggle' and control-openhands browser text 'testid=uncommitted-changes-row-content' (the changed paths; aria-expanded is true). Run control-openhands browser click 'testid=commit-row >> has-text=Initial fixture commit >> testid=commit-row-toggle'; Uncommitted's aria-expanded turns false, and control-openhands browser text 'testid=commit-row-content' lists README.md, src/calc.py, src/test_calc.py. After the third prompt the Uncommitted count is 8 files.

Diff modesF08.diff-view-modesMapped behavior

Scenario F08--diff-modes · Permalink · Source

Given

Use F08’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 “Uncommitted changes row content” / “File diff viewer outer” / containing “src/calc.py” / “Collapse”.

Expect

expanding a changed file shows its diff; the view-mode buttons (Old version, Diff, New version) switch between old, diff and new without page errors.

Exact CLI recipe & bug checks

In the expanded Uncommitted row, run control-openhands browser click 'testid=uncommitted-changes-row-content >> testid=file-diff-viewer-outer >> has-text=src/calc.py >> testid=collapse'. control-openhands browser text 'testid=uncommitted-changes-row-content >> testid=file-diff-viewer-outer >> has-text=src/calc.py >> testid=editor-container' shows the added def sub lines, and view-mode-diff has aria-pressed true. The row's buttons are named: control-openhands browser snapshot 'testid=uncommitted-changes-row-content >> testid=file-diff-viewer-outer >> has-text=src/calc.py' lists button "Old version", button "Diff" [pressed], button "New version" and button "Collapse" (the same collapse button reads Expand while the row is collapsed), so … >> role=button[name="Old version"] works as well as the test id. Click … >> testid=view-mode-old (2 lines), then … >> testid=view-mode-new (4 lines), then view-mode-diff. Run control-openhands browser errors --app-only before and after: each collapse and each diff→old switch adds TextModel got disposed before DiffEditorWidget model got reset (fail, #17567).

Markdown previewF08.diff-markdown-previewMapped behavior

Scenario F08--markdown-preview · Permalink · Source

Given

Use F08’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 “Commit row” / containing “QA agent commit” / “Commit row toggle”.

Expect

a Markdown file in new (or old) mode renders as a prose preview.

Exact CLI recipe & bug checks

In this order qa-notes.md is already committed, so check it in the QA agent commit row: control-openhands browser click 'testid=commit-row >> has-text=QA agent commit >> testid=commit-row-toggle', then with F='testid=commit-row >> has-text=QA agent commit >> testid=file-diff-viewer-outer >> has-text=qa-notes.md' click "$F >> testid=collapse" and "$F >> testid=view-mode-new". control-openhands browser count "$F >> testid=markdown-preview" is 1, rendering <h1>QA notes</h1>, world in bold and the edited by agent list item. (Before the second prompt the same check works under testid=uncommitted-changes-row-content.) Re-expand Uncommitted with uncommitted-changes-row-toggle for the next bullet.

Deleted fileF08.diff-deleted-fileMapped behavior

Scenario F08--deleted-file · Permalink · Source

Given

Use F08’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
    Expand src/test_calc.py in Uncommitted. control-openhands browser text 'testid=uncommitted-changes-row-content >> testid=file-diff-viewer-outer >> has-text=src/test_calc.py >> testid=file-deleted-message' reads This file was deleted. Its previous contents are no longer available for preview.

Expect

a deleted working-tree file reads This file was deleted. Its previous contents are no longer available for preview.

Exact CLI recipe & bug checks

Expand src/test_calc.py in Uncommitted. control-openhands browser text 'testid=uncommitted-changes-row-content >> testid=file-diff-viewer-outer >> has-text=src/test_calc.py >> testid=file-deleted-message' reads This file was deleted. Its previous contents are no longer available for preview.

From the overviewF08.changes-from-overviewMapped behavior

Scenario F08--from-the-overview · Permalink · Source

Given

Use F08’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 “Conversation overview diffs”.
  3. Do
    Reload the page.

Expect

the overview's Changes row opens the drawer on Commits with Uncommitted expanded and closes the overview.

Exact CLI recipe & bug checks

With the drawer closed, run control-openhands browser click 'testid=conversation-overview-toggle' and control-openhands browser click 'testid=conversation-overview-diffs' (its siblings conversation-overview-diffs-additions/-deletions show +N/-N; deleted files are not counted). right-panel-toggle aria-pressed is true, testid=commit-list is visible, uncommitted-changes-row-toggle has aria-expanded true and testid=conversation-overview-panel is hidden. After browser reload and the toggle, Commits is still selected.

Empty statesF08.commits-states · F08.files-empty-workspaceMapped behavior

Scenario F08--empty-states · Permalink · Source

Given

Use F08’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
    Create the disposable folder fixture specified in the recipe.
  2. Do
    Open /.
  3. Do
    Start a conversation with the prompt “Run exactly this bash command, then reply only "done": rm notes.md && git status --short”.
  4. Do
    Reload the page.
  5. Do
    Choose “Conversation tab commits”.
  6. Setup
    Change the run-owned service state: stop agent-server.

Expect

with no commits and no changes the Commits tab shows No commits yet. While the runtime is inactive it shows Waiting for runtime to start..., and while loading Loading changes....

a workspace with no files shows No files in workspace.

Exact CLI recipe & bug checks

Run control-openhands fixture folder, which makes <run>/workspace/qa-folder, a plain folder (no .git) with notes.md; by the time the conversation has run it holds an empty .git (seen live; not created by the prompt), so it ends up a repo with no commits. Run control-openhands browser goto / (the browser is on the qa-repo conversation) and pick it with the workspace picker as in Preconditions (folder-browser-entry-qa-folder). Then run control-openhands conversation start --stay --prompt 'Run exactly this bash command, then reply only "done": rm notes.md && git status --short' --wait --timeout 180. Open the drawer and click testid=conversation-tab-commits; control-openhands browser count 'text="No commits yet"' is 1 (browser screenshot --feature F08.commits-states --name no-commits). On Files, click testid=files-tab-refresh; control-openhands browser text 'testid=files-tab-tree' reads No files in workspace. Loading changes... (testid=commits-tab-status) shows briefly on the first Commits open after a load: on /conversations/<id> with Files selected, browser reload, open the drawer, then control-openhands browser click 'testid=conversation-tab-commits' --observe 'testid=commits-tab-status' --observe-ms 4000 records Loading changes... for a few ms. Waiting for runtime to start... (agent state init/loading/error) was not reached: service stop agent-server and restart both left No commits yet on screen.

Commit capF08.commits-capMapped behavior

Scenario F08--commit-cap · Permalink · Source

Given

Use F08’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
    Reload the page.

Expect

the history loads the latest 50 commits; a longer history ends with Showing the latest 50 commits.

Exact CLI recipe & bug checks

Arrange more than 50 commits in the fixture (read-only for the UI): for i in $(seq 1 50); do git -C "$OH_VERIFY_RUN/workspace/qa-repo" -c user.name=QA-Bulk -c user.email=qa@example.invalid commit -q --allow-empty -m "QA bulk $i"; done. On the qa-repo conversation (control-openhands browser goto /conversations/<id>; Empty states left the qa-folder conversation) run browser reload, open the drawer on Commits and wait for testid=commit-list. control-openhands browser text 'testid=commit-list-cap-notice' reads Showing the latest 50 commits and browser count 'testid=commit-row' is 50. Do this last: it changes the commit list the other bullets read.

Phone panel pageF08.phone-panel-pageMapped behavior

Scenario F08--phone-panel-page · Permalink · Source

Given

Use F08’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 “Right panel toggle”.
  3. Do
    Choose “Conversation mobile panel back”.
  4. Setup
    Use the desktop viewport.

Expect

at 1024 px or narrower the toggle navigates to /conversations/<id>/panel, a full-screen page with a Back chevron, the same tab bar and the tab body.

Exact CLI recipe & bug checks

Run control-openhands browser viewport phone on /conversations/<id>, then control-openhands browser click 'testid=right-panel-toggle' --expect-url '/panel$'. control-openhands browser bbox 'testid=files-tab' gives insideViewport true and pageHorizontalOverflow false, with Commits diffs usable at 390 px (browser screenshot --feature F08.phone-panel-page --name panel). control-openhands browser attr 'testid=conversation-mobile-panel-back' aria-label is Back. Run control-openhands browser click 'testid=conversation-mobile-panel-back' --expect-url '/conversations/[^/]+$'; the chat is visible. Return with control-openhands browser viewport desktop.

Direct panel URLF08.panel-direct-urlMapped behavior

Scenario F08--direct-panel-url · Permalink · Source

Given

Use F08’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>/panel.
  2. Setup
    Use the phone viewport.
  3. Do
    Choose “Conversation mobile panel back”.
  4. Setup
    Use the desktop viewport.

Expect

opening /conversations/<id>/panel directly renders the panel page at 1024 px or narrower, and Back returns to the chat; wider, the URL is replaced with /conversations/<id> and the drawer opens beside the chat.

Exact CLI recipe & bug checks

At desktop run control-openhands browser goto /conversations/<id>/panel. The app replaces the URL with /conversations/<id> (the goto result already shows it; control-openhands browser url confirms), control-openhands browser count 'testid=conversation-mobile-panel-back' is 0, browser visible 'testid=chat-interface' is true and right-panel-toggle aria-pressed is true (the drawer opens beside the chat; browser screenshot --feature F08.panel-direct-url --name desktop-redirect). Then run control-openhands browser viewport phone and the same goto: the URL keeps /panel, the conversation-mobile-panel-back count is 1 and chat-interface is not visible. control-openhands browser click 'testid=conversation-mobile-panel-back' --expect-url '/conversations/[^/]+$' returns to the chat. Return with control-openhands browser viewport desktop.

ErrorsF08 · setup / cleanupSupport step

Scenario F08--errors · Permalink · Source

Given

Use F08’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
    After each group run control-openhands browser errors --app-only. The Monaco disposal error above belongs to this family. 404s on /api/llm/balance and .agents_tmp/ downloads come from the Usage and Planner tabs (F27).

Expect

Complete this support step before continuing to the next scenario.

Exact CLI recipe & bug checks

After each group run control-openhands browser errors --app-only. The Monaco disposal error above belongs to this family. 404s on /api/llm/balance and .agents_tmp/ downloads come from the Usage and Planner tabs (F27).

Rich fenced codeF08.files-rich-plainKnown failureKnown issueBlocked

Scenario F08--rich-fenced-code · Permalink · Source

Given

Use F08’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
    Arrange a Markdown file in the selected workspace containing a heading and a fenced Python print("qa") block. Open it from Files in Rich mode and take browser screenshot --feature F08.files-rich-plain --name fenced-code. Known failure (reproduced 2026-10-12, #16802): the code renders in nested dark/gray containers with repeated padding. This is a layout-only observation; the measured pre and prose ancestor had zero border width, so it is not a claim of visible CSS borders.

Expect

Rich highlights code, renders Markdown and shows HTML in an iframe sandboxed without scripts. Plain shows highlighted source. The mode persists across reloads.

Known bug observations

Known failure (reproduced 2026-10-12, #16802): the code renders in nested dark/gray containers with repeated padding. This is a layout-only observation; the measured pre and prose ancestor had zero border width, so it is not a claim of visible CSS borders.

Linked known issues

Known issue #15501 (F08.files-rich-plain): Generated-page JavaScript was blocked by an iframe sandbox. Files Rich intentionally disables scripts; the report does not identify whether Files or Browser was used. Live prerequisite: The original preview panel and file URL or minimal page identifying the intended surface; Files Rich deliberately blocks scripts, while Browser is separate.

Known issue OpenHands/software-agent-sdk#5784 (F08.files-rich-plain, F08.files-fallbacks, F08.files-open-new-window): a path-prefixed Agent Server can set a workspace cookie whose Path omits the prefix, leaving HTML/image/file previews unauthorized. Live prerequisite: an authenticated path-prefixed owned Local server with OH_WEB_URL and a real cookie-authenticated preview; the ordinary root-path launch cannot exercise that cookie Path.

Exact CLI recipe & bug checks

Arrange a Markdown file in the selected workspace containing a heading and a fenced Python print("qa") block. Open it from Files in Rich mode and take browser screenshot --feature F08.files-rich-plain --name fenced-code. Known failure (reproduced 2026-10-12, #16802): the code renders in nested dark/gray containers with repeated padding. This is a layout-only observation; the measured pre and prose ancestor had zero border width, so it is not a claim of visible CSS borders.

Git filename with a spaceF08.uncommitted-changesKnown failureKnown issueBlocked

Scenario F08--git-filename-with-a-space · Permalink · Source

Given

Use F08’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
    In a disposable workspace Git fixture, create and commit my notes.md, then modify it (plain shell Git/file commands are fixture setup, not UI proof). Open its empty Local conversation's Commits tab and read browser network --filter '/git/'. Expected: the modified path and other changes stay listed. Known failure (reproduced 2026-10-12, OpenHands/software-agent-sdk#5642): the scoped changes call returns 400 and hides the Uncommitted changes. Only the space-name case was tested; the same issue's non-ASCII and rename cases still require their own checks.

Expect

the top row reads -, Uncommitted and a file count; expanding it lists the changed paths.

Known bug observations

Known failure (reproduced 2026-10-12, OpenHands/software-agent-sdk#5642): the scoped changes call returns 400 and hides the Uncommitted changes. Only the space-name case was tested; the same issue's non-ASCII and rename cases still require their own checks.

Linked known issues

Known issue #16716 (F08.uncommitted-changes): Diff view is regularly out of sync. Live prerequisite: The original simultaneous-agent/shared-repository state or a deterministic stale-diff trigger; the report only suggests that timing.

Known issue #16454 (F08.uncommitted-changes): Diff list is showing duplicate entries. Live prerequisite: The original Git changes response and update sequence producing duplicate rows; the report provides no trigger.

Known issue #15692 (F08.uncommitted-changes): Diff still shows after agent has committed and pushed. Live prerequisite: A real agent commit/push terminal observation while the diff remains mounted, plus a disposable Git remote.

Exact CLI recipe & bug checks

In a disposable workspace Git fixture, create and commit my notes.md, then modify it (plain shell Git/file commands are fixture setup, not UI proof). Open its empty Local conversation's Commits tab and read browser network --filter '/git/'. Expected: the modified path and other changes stay listed. Known failure (reproduced 2026-10-12, OpenHands/software-agent-sdk#5642): the scoped changes call returns 400 and hides the Uncommitted changes. Only the space-name case was tested; the same issue's non-ASCII and rename cases still require their own checks.

File listing above one MiBF08.files-treeKnown failureKnown issueBlocked

Scenario F08--file-listing-above-one-mib · Permalink · Source

Given

Use F08’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
    In a disposable fixture, arrange 1800 tiny files beneath three 220-character directory segments so the sorted relative-path listing exceeds one MiB while remaining below the 2000-file cap. Keep a normal tracked README at the root. Open Files, click testid=files-tab-refresh, then count role=button[name="README.md"][exact] and inspect the tree. Known failure (reproduced 2026-10-12, OpenHands/software-agent-sdk#5638): the README disappears and a spurious partial-path folder appears because the command API returns the tail output chunk only. The greater-than-100-chunk/null-exit case was not driven.

Expect

a tree of folders, collapsed at first, and files. Clicking a file selects it and adds it to the open-file strip.

Known bug observations

Known failure (reproduced 2026-10-12, OpenHands/software-agent-sdk#5638): the README disappears and a spurious partial-path folder appears because the command API returns the tail output chunk only. The greater-than-100-chunk/null-exit case was not driven.

Linked known issues

Known issue #18108 (F08.files-tree): Files tab shows "No files in workspace" on cloud when the agent writes outside /workspace/project. Live prerequisite: An authenticated Enterprise Cloud sandbox, no repository selected, with files outside its default working directory.

Known issue #17773 (F08.files-tree): Files tab always shows "No files in workspace" on Windows local backend. Live prerequisite: A Windows local Agent Server using cmd.exe; this run is macOS.

Known issue #16331 (F08.files-tree): file preview panel stops working during streaming. Live prerequisite: A real streaming/tool-running conversation while selecting a second workspace file.

Exact CLI recipe & bug checks

In a disposable fixture, arrange 1800 tiny files beneath three 220-character directory segments so the sorted relative-path listing exceeds one MiB while remaining below the 2000-file cap. Keep a normal tracked README at the root. Open Files, click testid=files-tab-refresh, then count role=button[name="README.md"][exact] and inspect the tree. Known failure (reproduced 2026-10-12, OpenHands/software-agent-sdk#5638): the README disappears and a spurious partial-path folder appears because the command API returns the tail output chunk only. The greater-than-100-chunk/null-exit case was not driven.

Subdirectory Git pathsF08.uncommitted-changes · F08.diff-view-modesKnown failureKnown issueBlocked

Scenario F08--subdirectory-git-paths · Permalink · Source

Given

Use F08’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 workspace at repo/src lists changes beneath that folder relative to it, and its diffs open. Known failure (reproduced 2026-10-16, SDK 1.55.0, OpenHands/software-agent-sdk#5843): tracked paths retain the repository prefix while untracked paths are relative to src; opening displayed src/app.py requests repo/src/src/app.py and returns400. The ordinary repository-root control lists correct paths. The recipe creates only disposable, idle offline conversations.

Expect

the top row reads -, Uncommitted and a file count; expanding it lists the changed paths.

expanding a changed file shows its diff; the view-mode buttons (Old version, Diff, New version) switch between old, diff and new without page errors.

Known bug observations

Known failure (reproduced 2026-10-16, SDK 1.55.0, OpenHands/software-agent-sdk#5843): tracked paths retain the repository prefix while untracked paths are relative to src; opening displayed src/app.py requests repo/src/src/app.py and returns400. The ordinary repository-root control lists correct paths. The recipe creates only disposable, idle offline conversations.

Linked known issues

Known issue #16716 (F08.uncommitted-changes): Diff view is regularly out of sync. Live prerequisite: The original simultaneous-agent/shared-repository state or a deterministic stale-diff trigger; the report only suggests that timing.

Known issue #16454 (F08.uncommitted-changes): Diff list is showing duplicate entries. Live prerequisite: The original Git changes response and update sequence producing duplicate rows; the report provides no trigger.

Known issue #15692 (F08.uncommitted-changes): Diff still shows after agent has committed and pushed. Live prerequisite: A real agent commit/push terminal observation while the diff remains mounted, plus a disposable Git remote.

Exact CLI recipe & bug checks

Expected: a workspace at repo/src lists changes beneath that folder relative to it, and its diffs open. Known failure (reproduced 2026-10-16, SDK 1.55.0, OpenHands/software-agent-sdk#5843): tracked paths retain the repository prefix while untracked paths are relative to src; opening displayed src/app.py requests repo/src/src/app.py and returns400. The ordinary repository-root control lists correct paths. The recipe creates only disposable, idle offline conversations.

sh known-bug F08.diff-view-modes subdirectory-paths

  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)
  repo = fixture / "repo"
  (repo / "src").mkdir(parents=True)
  git(repo, "init", "-q", "-b", "main")
  (repo / "README.md").write_text("root before\n")
  (repo / "src/app.py").write_text("x = 1\n")
  git(repo, "add", ".")
  git(repo, "-c", "user.name=QA", "-c", "user.email=qa@example.invalid", "commit", "-qm", "Owned fixture")
  (repo / "README.md").write_text("root after\n")
  (repo / "src/app.py").write_text("x = 2\n")
  (repo / "src/new.py").write_text("new\n")
  print(create(repo / "src"))
  PY
  )"
  control-openhands browser goto "/conversations/$QA_ID"
  control-openhands browser click 'testid=right-panel-toggle'
  control-openhands browser click 'testid=conversation-tab-commits'
  control-openhands browser wait-text Uncommitted --timeout 10000
  control-openhands browser click 'testid=uncommitted-changes-row-toggle'
  control-openhands browser network --clear
  control-openhands browser click 'testid=file-diff-viewer-outer >> has-text=src/app.py >> testid=collapse' --observe 'testid=uncommitted-changes-row-content' --observe-ms 1000
  control-openhands browser screenshot --feature F08.diff-view-modes --name subdir-mixed-paths
  control-openhands assert --field recent.0.status --equals 200 --bug-shows 400 -- browser network --filter '/git/diff'  # bug
Plain parent containing a repositoryF08.uncommitted-changesKnown failureKnown issueBlocked

Scenario F08--plain-parent-containing-a-repository · Permalink · Source

Given

Use F08’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 plain workspace holding one dirty repository exposes that repository's changes. Known failure (reproduced 2026-10-16, pinned SDK 1.55.0, OpenHands/software-agent-sdk#5641): the changes request returns200 with no changes and Commits reads No commits yet although the child repository is dirty. The upstream fix is closed but not included in the current pin. The recipe removes only its own verified empty parent .git; never remove a user's repository.

Expect

the top row reads -, Uncommitted and a file count; expanding it lists the changed paths.

Known bug observations

Known failure (reproduced 2026-10-16, pinned SDK 1.55.0, OpenHands/software-agent-sdk#5641): the changes request returns200 with no changes and Commits reads No commits yet although the child repository is dirty. The upstream fix is closed but not included in the current pin. The recipe removes only its own verified empty parent .git; never remove a user's repository.

Linked known issues

Known issue #16716 (F08.uncommitted-changes): Diff view is regularly out of sync. Live prerequisite: The original simultaneous-agent/shared-repository state or a deterministic stale-diff trigger; the report only suggests that timing.

Known issue #16454 (F08.uncommitted-changes): Diff list is showing duplicate entries. Live prerequisite: The original Git changes response and update sequence producing duplicate rows; the report provides no trigger.

Known issue #15692 (F08.uncommitted-changes): Diff still shows after agent has committed and pushed. Live prerequisite: A real agent commit/push terminal observation while the diff remains mounted, plus a disposable Git remote.

Exact CLI recipe & bug checks

Expected: a plain workspace holding one dirty repository exposes that repository's changes. Known failure (reproduced 2026-10-16, pinned SDK 1.55.0, OpenHands/software-agent-sdk#5641): the changes request returns200 with no changes and Commits reads No commits yet although the child repository is dirty. The upstream fix is closed but not included in the current pin. The recipe removes only its own verified empty parent .git; never remove a user's repository.

sh known-bug F08.uncommitted-changes nested-repository

  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)
  import shutil
  parent = fixture / "parent"
  child = parent / "proj"
  child.mkdir(parents=True)
  git(child, "init", "-q", "-b", "main")
  (child / "README.md").write_text("before\n")
  git(child, "add", ".")
  git(child, "-c", "user.name=QA", "-c", "user.email=qa@example.invalid", "commit", "-qm", "Owned nested fixture")
  (child / "README.md").write_text("after\n")
  (child / "notes.txt").write_text("untracked\n")
  conversation_id = create(parent)
  empty_git = parent / ".git"
  if empty_git.exists():
      assert empty_git.is_dir() and not empty_git.is_symlink()
      top = pathlib.Path(subprocess.check_output(["git", "-C", str(parent), "rev-parse", "--show-toplevel"], text=True).strip())
      assert top.resolve() == parent.resolve()
      assert not subprocess.check_output(["git", "-C", str(parent), "ls-files"], text=True).strip()
      shutil.rmtree(empty_git)
  print(conversation_id)
  PY
  )"
  control-openhands browser goto "/conversations/$QA_ID"
  control-openhands browser click 'testid=right-panel-toggle'
  control-openhands browser click 'testid=conversation-tab-commits'
  control-openhands browser wait '#conversation-tab-panel main'
  control-openhands browser wait 'testid=commits-tab-status' --state detached --timeout 10000
  control-openhands assert --field recent.0.status --equals 200 -- browser network --filter '/git/changes'
  control-openhands browser screenshot --feature F08.uncommitted-changes --name nested-repository
  control-openhands assert --field count --equals 1 --bug-shows 0 -- browser count 'testid=uncommitted-changes-row'  # bug
Family notes, prerequisites for known issues & limits Known issueBlocked
  • When the drawer is closed, its tabs stay mounted at width 0. browser count 'testid=files-tab' is 1 even then, so assert with browser visible or right-panel-toggle aria-pressed.
  • The tab row is a tablist "Conversation panels" of role=tab controls, each named after its tab even when inactive (#17612), so role=tab[name="Commits"] finds the Commits tab whether or not it is active; role=button[name="Commits"] finds nothing. aria-selected marks the selected tab. The conversation-tab-* test ids still work.
  • The tab row ends with the ellipsis menu (Customize tabs), then the VSCode button (drawer-vscode-link, named Open in VS Code) when the runtime reports a running editor. #17660 removed that button and #18048 restored it, so on a checkout between the two the drawer has no editor control at all.
  • The overflow menu is not a role=menu. Escape closes it and returns focus to the tab bar's ellipsis-button, both in the desktop drawer and on the phone /panel page; choosing a row or an outside click (browser mouse-click) closes it too.
  • The Files list and the open preview do not follow bash edits: use-auto-refresh-files-on-edit.ts refreshes the file queries only for file-editor observations, and its comment says shell observations skip them on purpose (so that every shell command does not reload canvas iframes). This map still expects files the agent writes to appear (F08.files-auto-refresh), so the bash case stays a fail row. Click files-tab-refresh once before asserting on files the agent made with the shell. Since #18032 that one click also re-reads the open file (the content fetch revalidates instead of using the browser HTTP cache, and the rich preview gets a new ?v=), and a page reload shows the current body without a click.
  • The chat also fetches the file list (to linkify paths). A conversation page left open while the agent works can therefore show a stale tree the moment the drawer first opens.
  • Binary detection is a NUL-byte sniff of the first 8 KB, and the Office message also needs that sniff to trip. A 4-byte PK\003\004 "pptx" or random bytes without a NUL render as text in the plain viewer. Fixtures must contain a NUL.
  • control-openhands fixture folder writes a plain folder with notes.md, but by the time the conversation has run the folder holds an empty .git, so a conversation that leaves notes.md in place shows Uncommitted 1 file rather than the empty state. Delete it first (as in the recipe).
  • Diff views are Monaco. testid=file-diff-viewer and file-single-viewer are not on the DOM; read text through testid=editor-container. The narrow drawer falls back to an inline (not side-by-side) diff. The view-mode buttons are named Old version, Diff and New version, and collapse is named Expand or Collapse after its state (#18033).
  • browser eval "navigator.clipboard.readText()" hangs for about 2 minutes (no clipboard permission). Use control-openhands browser clipboard to read what Copy wrote.
  • browser click ... --observe accepts the usual selector syntax (testid=...).
  • Two .cursor-ew-resize grips sit in the conversation page (chat/drawer, then the Files tree); scope the tree one with testid=files-tab-tree-resize-handle.
  • At phone width, chat path links (and the agent's canvas_ui_control tab and file requests, through src/services/canvas-ui.ts) navigate to /conversations/<id>/panel (#18045); at desktop width they open the side drawer and the URL does not change. A /panel URL above 1024 px redirects to /conversations/<id>, so a bullet that leaves the phone viewport on /panel ends on the chat route.
  • If the agent's terminal resets mid-command ("terminal session was reset", for example after a restart), the agent reruns the command. Check the workspace with git status through a second prompt rather than trusting the first observation. Runs no longer share a tmux server (OpenHands/OpenHands#17946).
  • Known issue: #17567 (Monaco disposed-model page error on diff unmount; it also fires on collapse and on a diff→old switch).
  • Known issue #18108 (F08.files-tree): Files tab shows "No files in workspace" on cloud when the agent writes outside /workspace/project. Live prerequisite: An authenticated Enterprise Cloud sandbox, no repository selected, with files outside its default working directory.
  • Known issue #17773 (F08.files-tree): Files tab always shows "No files in workspace" on Windows local backend. Live prerequisite: A Windows local Agent Server using cmd.exe; this run is macOS.
  • Known issue #16949 (F08.files-refresh): Keep Files and Changes views source-driven after refresh. Live prerequisite: An authenticated Cloud repository for the refresh-query mismatch, or real file-editor observations causing an in-place M-to-D Git refetch.
  • Known issue #16716 (F08.uncommitted-changes): Diff view is regularly out of sync. Live prerequisite: The original simultaneous-agent/shared-repository state or a deterministic stale-diff trigger; the report only suggests that timing.
  • Known issue #16689 (F08.files-load-error): When workspace/ paths throw a 401, call the workspace-session endpoint. Live prerequisite: The original fresh-VM URL, auth/cookie state and exact request sequence; the report says the trigger is unclear.
  • Known issue #16454 (F08.uncommitted-changes): Diff list is showing duplicate entries. Live prerequisite: The original Git changes response and update sequence producing duplicate rows; the report provides no trigger.
  • Known issue #16331 (F08.files-tree): file preview panel stops working during streaming. Live prerequisite: A real streaming/tool-running conversation while selecting a second workspace file.
  • Known issue #15692 (F08.uncommitted-changes): Diff still shows after agent has committed and pushed. Live prerequisite: A real agent commit/push terminal observation while the diff remains mounted, plus a disposable Git remote.
  • Known issue #15567 (F08.files-load-error): Workspace file preview returns 401 Unauthorized on non-HTTPS non-loopback hosts. Live prerequisite: A plain-HTTP non-loopback deployment and browser accessing that origin, with workspace-cookie observation.
  • Known issue #15519 (F08.files-load-error): "files" tab in Files side panel throws 401 Unauthorized. Live prerequisite: The reported Linux Docker deployment/auth topology; this isolated run uses the macOS source launcher.
  • Known issue #15501 (F08.files-rich-plain): Generated-page JavaScript was blocked by an iframe sandbox. Files Rich intentionally disables scripts; the report does not identify whether Files or Browser was used. Live prerequisite: The original preview panel and file URL or minimal page identifying the intended surface; Files Rich deliberately blocks scripts, while Browser is separate.
  • Known issue OpenHands/software-agent-sdk#4562 (F08.files-load-error): Plain-HTTP non-loopback workspace previews can return401 after the browser rejects the workspace-session cookie. Live prerequisite: A plain-HTTP non-loopback same-origin browser deployment with real workspace-cookie observation.
  • Known issue #16871 (F08.vscode-link): the shared transformVSCodeUrl still accepts arbitrary schemes, and the current Cloud use-unified-vscode-url.ts branch passes sandbox exposed_urls editor values through it without the Local origin gate. Live prerequisite: an authorized Cloud account/sandbox whose editor exposed_urls value can be controlled for safe URL-scheme rejection checks. Local inspection on 2026-10-12 at fa1f724ab573 found no Download via VS Code action in the real card menu: its only production caller does not supply the required showOptions prop. The current drawer opens with noopener,noreferrer. Neither the old opener exploit nor the remaining Cloud scheme scenario was reproduced. Evidence: backfill-18309-surface-rechecks/F04.download/vscode-card-action-absent-16871.png and its ARIA snapshot. Keep this linked to the live editor-link consumer; do not infer the whole issue resolved from the removed card action.
  • Known issue OpenHands/software-agent-sdk#5845 (F08.vscode-link): reserved characters in the workspace path can split the editor URL's unencoded folder parameter and open the wrong folder. Live prerequisite: a supported origin serving a genuinely enabled/running editor and an owned conversation in a path containing & or #. A direct URL-constructor call does not exercise the link; Cloud exposed_urls takes another path.
  • Known issue OpenHands/software-agent-sdk#5784 (F08.files-rich-plain, F08.files-fallbacks, F08.files-open-new-window): a path-prefixed Agent Server can set a workspace cookie whose Path omits the prefix, leaving HTML/image/file previews unauthorized. Live prerequisite: an authenticated path-prefixed owned Local server with OH_WEB_URL and a real cookie-authenticated preview; the ordinary root-path launch cannot exercise that cookie Path.

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.