EN / field notes OpenHands feature map

OpenHands / F09

Settings shell and navigation

Settings is one shell around eight pages (Agent, LLM, Model Router, Condenser, Agent Context, Verification, Application, Secrets). On desktop a pinned left column lists the pages, highlights the current one and ends with an "Agent Canvas" version card and a "synced from … backend" note; each page gets a title and a grey subline. Below 1024 px (phones and tablets) /settings is a hub list instead, and at phone width every page shows a Back chevron to the hub. Users reach Settings from the sidebar gear, the collapsed rail, the command menu and several deep links (LLM banner, getting started checklist, composer pickers). The individual pages are other families (F10–F16; Secrets is F14).

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

How to get to it

  • Sidebar footer gear next to the backend selector (backend-selector-settings-link), on desktop or inside the phone drawer (sidebar-mobile-menu-toggle, then sidebar-mobile-drawer >> backend-selector-settings-link).
  • Collapsed sidebar: the gear icon at the bottom of the rail (collapsed-settings-link).
  • Command menu (Control+k/Meta+k or command-menu-trigger): group Settings with Settings, Agent settings, LLM profiles, Model Router, Condenser settings, Agent Context, Verification settings, Application settings, Secrets settings.
  • Deep links: Home banner Set up LLM (home-llm-not-configured-action, only while no LLM key is configured); getting-started checklist Add LLM API key and Customize your agent; composer LLM pill (chat-input-llm-profile) → LLM Profiles; composer + (chat-plus-button) → Switch agent profile → Manage agent profiles. Application settings also links "Manage LLM profiles" (F15's page).
  • Direct URLs: /settings, /settings/agents, /settings/llm, /settings/meta-llm, /settings/condenser, /settings/agent-context, /settings/verification, /settings/app, /settings/secrets, and the legacy /settings/agent.
  • Inside Settings: the left nav (sidebar-settings-/settings/<page>) on desktop; the hub list (settings-mobile-hub) plus the top-bar Back chevron (sidebar-mobile-back-button) on phones.

Before you start

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

Preconditions:

  • Baseline state (launched, doctored, onboard --skip done), desktop viewport.
  • Run the banner and checklist deep links (F09.entry-deep-links, first bullet) before configuring a model: the banner only exists while no LLM key is set.
  • The composer picker deep links need a configured profile: control-openhands llm preset deepseek (no model call is made).
  • F09.cloud-links positive case and F09.locked-cloud-landing need a Cloud backend/account (blocked in a local run). F09.hidden-llm-redirect is blocked: the frontend hardcodes hide_llm_settings: false. The update states depend on the browser reaching registry.npmjs.org: with access and a current client you get F09.update-up-to-date; F09.update-available and the version tile also need an npm latest newer than the client (blocked while the client is the latest release); the failure text of F09.update-modal needs the registry to be unreachable (no mocks).

Behavior inventory

25 stable behavior IDs and their expected behavior
  • F09.entry-gear the sidebar gear opens Settings on the Agent page; the gear is marked active on every settings URL; at phone width the drawer's gear opens the hub and closes the drawer. Read recipe ↓
  • F09.entry-collapsed-rail with the sidebar collapsed, the rail's Settings icon opens the Agent page. Read recipe ↓
  • F09.entry-command-menu the command menu's Settings group (Settings, Agent settings, LLM profiles, Model Router, Condenser, Agent Context, Verification, Application, Secrets settings) opens the matching page. Read recipe ↓
  • F09.command-menu-coverage every page in the settings nav, including Model Router and Agent Context, can be found in the command menu and opens from it. Read recipe ↓
  • F09.index-redirect on desktop, /settings replaces itself with /settings/agents (Back skips /settings). Read recipe ↓
  • F09.locked-cloud-landing on a canvas locked to a Cloud host, /settings lands on /settings/app instead.
  • F09.legacy-agent-redirect the retired /settings/agent URL replaces itself with /settings/agents. Read recipe ↓
  • F09.hidden-llm-redirect with the hide_llm_settings feature flag, the LLM link disappears and /settings/llm redirects to /settings/agents.
  • F09.desktop-nav the desktop nav lists the eight pages in order, exactly one link is aria-current="page", and the nav stays pinned while the page content scrolls. Read recipe ↓
  • F09.page-header each page shows its nav title as an H2 with a grey subline; Secrets draws its own header instead, and the Agent profile editor hides the shell header until you leave it. Read recipe ↓
  • F09.landmarks the settings screen exposes a single main landmark, on desktop pages and on the phone hub. Read recipe ↓
  • F09.phone-hub below 1024 px (phones and tablets), /settings stays put and shows a "Settings" heading, the eight links, the version card and the synced note, with no page header and no horizontal overflow. Read recipe ↓
  • F09.hub-resize-redirect widening the window to ≥1024 px while the phone hub is open replaces /settings with /settings/agents; narrowing on a sub-page keeps its URL and adds the Back chevron. Read recipe ↓
  • F09.breakpoint-sweep at 767, 768, 820, 1023, 1024 and 1440 px, /settings is the hub below 1024 px and redirects to the desktop nav from 1024 px up, and the Application page's main is at least 340 px wide with no horizontal overflow. Read recipe ↓
  • F09.phone-back at phone width each settings page shows a Back chevron (label "Settings") in the top bar that returns to the hub. Read recipe ↓
  • F09.synced-badge the nav footer reads "These settings are synced from Local backend (<origin>)" for the active backend. Read recipe ↓
  • F09.update-card the "Agent Canvas / Version x.y.z" card opens a dialog; its "Up to date" / "Update" badge appears only once the npm check succeeded. Read recipe ↓
  • F09.update-modal the dialog shows the version, a "Check for updates" button that re-runs the check, and an inline status; a failed check reads "Couldn't check for updates. Try again later."; Close and Escape dismiss it. Read recipe ↓
  • F09.update-up-to-date when the npm check succeeds and the client is current, the card badge reads "Up to date", the dialog status reads "You're running the latest version." with a "What's new" link to the GitHub releases page (new tab), and "Check for updates" briefly shows "Checking for updates…". Read recipe ↓
  • F09.update-available when npm has a newer version, the dialog shows "Version … is available.", a "What's new" link, and npm/Docker command tabs with a copy button. Read recipe ↓
  • F09.update-modal-phone the update dialog fits a 390 px (and a 320 px) viewport with its title and Close button on screen, and Close dismisses it. Read recipe ↓
  • F09.sidebar-version-tile the main sidebar shows an "Agent Canvas" version tile only when an update is available. Read recipe ↓
  • F09.unknown-subpath an unknown /settings/<x> URL shows the app's "Page not found" page inside the app shell (sidebar and a Home link), without the settings shell. Read recipe ↓

Readable recipes

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

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

Gear entry and index redirect #

  1. Note
    From /, run
  2. Do
    control-openhands browser click 'testid=backend-selector-settings-link'
  3. Wait
    control-openhands browser wait-url '/settings/agents$'
  4. Check
    control-openhands browser attr 'testid=sidebar-settings-/settings/agents' aria-current

    is page and

  5. Check
    control-openhands browser attr 'testid=backend-selector-settings-link' data-active

    is true (it is false again on /).

  6. Do
    control-openhands browser back
  7. Check
    control-openhands browser url
  8. Note
    : the URL is /, not /settings, which proves the redirect replaced history.
  9. Note
    Direct entry:
  10. Do
    control-openhands browser goto /settings

    returns "url": ".../settings/agents".

Collapsed rail #

  1. Note
    From / (control-openhands browser goto /; the previous bullet ends on /settings/agents, where the URL wait would pass at once) run
  2. Do
    control-openhands browser click 'testid=sidebar-collapse-toggle'
  3. Do
    control-openhands browser click 'testid=collapsed-settings-link'
  4. Wait
    control-openhands browser wait-url '/settings/agents$'
  5. Do
    control-openhands browser screenshot --feature F09.entry-collapsed-rail --name rail
  6. Expect
    The screenshot shows the icon rail, the settings nav with Agent highlighted.
  7. Note
    Restore with
  8. Do
    control-openhands browser click 'testid=sidebar-collapse-toggle'

    (the label flips back to "Collapse sidebar").

Command menu #

  1. Note
    First rest the pointer off the list with
  2. Do
    control-openhands browser hover 'testid=command-menu-trigger'
  3. Note
    : a hover can move the menu's selection, and Enter runs the selected option (F02 Gotchas).
  4. Note
    For each title run
  5. Do
    control-openhands browser goto /
  6. Do
    control-openhands browser press Control+k
  7. Do
    control-openhands browser type 'testid=command-menu >> role=combobox' 'LLM profiles'
  8. Do
    control-openhands browser press Enter
  9. Wait
    control-openhands browser wait-url '/settings/llm$'
  10. Note
    Expected targets: Settings → /settings/agents, Agent settings → /settings/agents, LLM profiles → /settings/llm, Model Router → /settings/meta-llm, Condenser settings → /settings/condenser, Agent Context → /settings/agent-context, Verification settings → /settings/verification, Application settings → /settings/app, Secrets settings → /settings/secrets.
  11. Check
    control-openhands browser snapshot 'testid=command-menu >> role=listbox'
  12. Note
    after typing LLM profiles shows two options under the "Settings" group text: LLM profiles [selected] and Model Router, whose subline mentions LLM profiles.
  13. Note
    Enter runs the selected one.

Command menu coverage #

  1. Do
    control-openhands browser goto /
  2. Do
    control-openhands browser press Control+k
  3. Do
    control-openhands browser type 'testid=command-menu >> role=combobox' 'Model Router'
  4. Check
    control-openhands browser snapshot 'testid=command-menu >> role=listbox'
  5. Expect
    It shows one option under the "Settings" group text: Model Router Meta-profiles that route each task to the right LLM profile. Go [selected].
  6. Do
    control-openhands browser press Enter
  7. Wait
    control-openhands browser wait-url '/settings/meta-llm$'
  8. Check
    control-openhands browser count 'testid=command-menu'

    is 0.

  9. Note
    Repeat from / with Agent Context: the option reads Agent Context Let the agent keep notes and recall them in new conversations. Go and the URL becomes /settings/agent-context.
  10. Note
    With the previous bullet, every page in the settings nav has opened from the menu.

Deep links without a model #

  1. Note
    On / with no LLM key (control-openhands browser goto /):
  2. Do
    control-openhands browser click 'testid=home-llm-not-configured-action'
  3. Wait
    control-openhands browser wait-url '/settings/'
  4. Note
    → /settings/llm.
  5. Do
    control-openhands browser goto /
  6. Do
    control-openhands browser click 'testid=sidebar-onboarding-checklist-item-configure-llm'
  7. Wait
    control-openhands browser wait-url '/settings/'
  8. Note
    → /settings/llm.
  9. Do
    control-openhands browser goto /
  10. Do
    control-openhands browser click 'testid=sidebar-onboarding-checklist-item-customize-agent'
  11. Wait
    control-openhands browser wait-url '/settings/'
  12. Note
    → /settings/agents.

Deep links from the composer #

  1. Note
    After
  2. Arrange
    control-openhands llm preset deepseek
  3. Do
    control-openhands browser goto /
  4. Do
    control-openhands browser click 'testid=chat-input-llm-profile'
  5. Do
    control-openhands browser click 'testid=chat-input-llm-profile-popover >> role=link[name="LLM Profiles"]'
  6. Wait
    control-openhands browser wait-url '/settings/llm$'
  7. Check
    control-openhands browser count 'testid=chat-input-llm-profile-popover'

    is 0.

  8. Do
    control-openhands browser goto /
  9. Do
    control-openhands browser click 'testid=chat-plus-button'
  10. Do
    control-openhands browser hover 'testid=switch-agent-profile-button'
  11. Do
    control-openhands browser click 'testid=agent-profile-submenu >> role=link[name="Manage agent profiles"]'
  12. Wait
    control-openhands browser wait-url '/settings/agents$'
  13. Check
    control-openhands browser count 'testid=tools-context-menu'

    is 0.

Legacy URL #

  1. Do
    control-openhands browser goto /
  2. Do
    control-openhands browser goto /settings/agent
  3. Note
    the result already reads .../settings/agents.
  4. Do
    control-openhands browser back

    returns to /.

Desktop nav #

  1. Note
    On any settings page (control-openhands browser goto /settings/agents; Legacy URL ends on /) run
  2. Check
    control-openhands browser snapshot 'testid=settings-navbar-desktop'
  3. Note
    : links Agent, LLM, Model Router, Condenser, Agent Context, Verification, Application, Secrets (with /urls), then button "Agent Canvas Version …" and the synced text.
  4. Note
    Click each one, e.g.
  5. Do
    control-openhands browser click 'testid=sidebar-settings-/settings/meta-llm'
  6. Wait
    control-openhands browser wait-url '/settings/meta-llm$'
  7. Check
    control-openhands browser attr 'testid=sidebar-settings-/settings/meta-llm' aria-current

    (page), and

  8. Check
    control-openhands browser count 'testid=settings-navbar-desktop >> [aria-current="page"]'

    (1).

  9. Note
    Pinned:
  10. Do
    control-openhands browser goto /settings/app

    (the only page taller than the viewport),

  11. Check
    control-openhands browser bbox 'testid=settings-navbar-desktop'

    (y 32),

  12. Do
    control-openhands browser hover 'testid=settings-page-subtitle'
  13. Do
    control-openhands browser scroll --by 1000
  14. Check
    control-openhands browser bbox 'testid=settings-page-subtitle'

    (negative y, content scrolled) and

  15. Check
    control-openhands browser bbox 'testid=settings-navbar-desktop'

    (still y 32);

  16. Do
    control-openhands browser screenshot --feature F09.desktop-nav --name scrolled

    shows Git Settings on the right and the full nav on the left.

Agent settings with a separate settings navigation listing Agent, LLM, Model Router, Condenser, Agent Context, Verification, Application and Secrets.
The desktop settings navigation stays beside each settings page. CLI capture · 1440 × 1000 · 9 October 2026 · Canvas 8793c111

Agent settings with a separate settings navigation listing Agent, LLM, Model Router, Condenser, Agent Context, Verification, Application and Secrets.

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

How this screenshot was taken

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

OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser goto /settings/agents
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser wait testid=agent-profile-row
OH_VERIFY_RUN="$OH_VERIFY_RUN" control-openhands browser screenshot --feature F09.desktop-nav --name settings-navigation

Page header #

  1. Expect
    After each nav click,
  2. Check
    control-openhands browser text 'testid=settings-screen >> header'

    reads title + subline: Agent "Create and manage reusable agent setups.", LLM "Model, API key, and options for the agent.", Model Router "Meta-profiles that route each task to the right LLM profile.", Condenser "Summarize long chats to stay within context limits.", Agent Context "Let the agent keep notes and recall them in new conversations.", Verification "Confirmation prompts and security checks on actions.", Application "Language, theme, notifications, and Git identity.".

  3. Note
    On Secrets,
  4. Check
    control-openhands browser count 'testid=settings-screen >> header >> h2'

    is 0 (the page draws its own "Secrets" heading).

  5. Note
    Editor: on /settings/agents (control-openhands browser goto /settings/agents) run
  6. Do
    control-openhands browser click 'testid=add-agent-profile'
  7. Check
    control-openhands browser count 'testid=settings-page-subtitle'

    is 0 and

  8. Check
    control-openhands browser text 'testid=settings-screen >> h2'

    is Add agent profile.

  9. Do
    control-openhands browser click 'testid=back-to-agent-profiles'
  10. Note
    brings the subtitle back (count 1); opening the editor again and clicking testid=sidebar-settings-/settings/condenser also shows the Condenser subline.

Landmarks #

  1. Do
    control-openhands browser goto /settings/agents
  2. Check
    control-openhands browser count 'role=main'

    (1) and

  3. Do
    control-openhands browser eval "document.querySelector('[data-testid=settings-screen]').tagName"

    (DIV: the shell wrapper is not a landmark; the layout's own <main> is).

  4. Do
    control-openhands browser goto /settings/app
  5. Note
    and the same count is 1.
  6. Note
    At phone width (control-openhands browser viewport phone, control-openhands browser goto /settings) the hub also counts 1; return with
  7. Do
    control-openhands browser viewport desktop

Phone hub and Back #

  1. Do
    control-openhands browser viewport phone
  2. Do
    control-openhands browser goto /settings

    (URL stays /settings),

  3. Check
    control-openhands browser snapshot 'testid=settings-mobile-hub'

    (heading "Settings", the eight links, the version button, the synced text),

  4. Check
    control-openhands browser count 'testid=settings-page-subtitle'

    (0),

  5. Check
    control-openhands browser bbox 'testid=settings-mobile-hub'

    (pageHorizontalOverflow false) and

  6. Do
    control-openhands browser screenshot --feature F09.phone-hub --name hub
  7. Do
    control-openhands browser click 'testid=settings-mobile-hub >> testid=sidebar-settings-/settings/condenser'
  8. Wait
    control-openhands browser wait-url '/settings/condenser$'
  9. Check
    control-openhands browser attr 'testid=sidebar-mobile-back-button' aria-label

    (Settings),

  10. Do
    control-openhands browser click 'testid=sidebar-mobile-back-button'
  11. Wait
    control-openhands browser wait-url '/settings$'
  12. Check
    control-openhands browser visible 'testid=settings-mobile-hub'

    (true).

  13. Note
    Drawer entry:
  14. Do
    control-openhands browser goto /
  15. Do
    control-openhands browser click 'testid=sidebar-mobile-menu-toggle'
  16. Do
    control-openhands browser click 'testid=sidebar-mobile-drawer >> testid=backend-selector-settings-link'
  17. Wait
    control-openhands browser wait-url '/settings$'
  18. Check
    control-openhands browser visible 'testid=sidebar-mobile-drawer'

    is false (the drawer closed).

  19. Note
    Return with
  20. Do
    control-openhands browser viewport desktop

Resize from the hub #

  1. Do
    control-openhands browser goto /
  2. Do
    control-openhands browser viewport phone
  3. Do
    control-openhands browser goto /settings
  4. Check
    control-openhands browser visible 'testid=settings-mobile-hub'

    (true).

  5. Do
    control-openhands browser viewport desktop
  6. Wait
    control-openhands browser wait-url '/settings/agents$'
  7. Note
    : the hub is gone (count 'testid=settings-mobile-hub' is 0) and
  8. Do
    control-openhands browser back
  9. Note
    lands on / (the redirect replaced /settings).
  10. Note
    Reverse:
  11. Do
    control-openhands browser goto /settings/condenser
  12. Do
    control-openhands browser viewport phone
  13. Check
    control-openhands browser url

    (still /settings/condenser) and

  14. Check
    control-openhands browser visible 'testid=sidebar-mobile-back-button'

    (true).

  15. Note
    Tablets get the hub too, and the boundary is 1024 px: run
  16. Do
    control-openhands browser viewport tablet

    (820 px),

  17. Do
    control-openhands browser goto /settings

    (URL stays /settings) and

  18. Check
    control-openhands browser visible 'testid=settings-mobile-hub'

    (true); then

  19. Do
    control-openhands browser viewport 1023x900
  20. Do
    control-openhands browser goto /settings
  21. Note
    and the same visible (true); then
  22. Do
    control-openhands browser viewport 1024x900
  23. Wait
    control-openhands browser wait-url '/settings/agents$'

    (browser visible 'testid=settings-navbar-desktop' is true).

  24. Note
    Return with
  25. Do
    control-openhands browser viewport desktop

Widths around the breakpoint #

  1. Note
    For each width W in 767, 768, 820, 1023, 1024, 1440 run
  2. Do
    control-openhands browser viewport Wx1024
  3. Do
    control-openhands browser goto /settings
  4. Note
    Below 1024 px the URL stays /settings,
  5. Check
    control-openhands browser visible 'testid=settings-mobile-hub'

    is true and

  6. Check
    control-openhands browser bbox 'testid=settings-mobile-hub'

    has pageHorizontalOverflow false (725, 426, 478 and 681 px wide); from 1024 px up

  7. Wait
    control-openhands browser wait-url '/settings/agents$'
  8. Note
    passes and
  9. Check
    control-openhands browser visible 'testid=settings-navbar-desktop'

    is true.

  10. Do
    control-openhands browser goto /settings/app
  11. Check
    control-openhands browser bbox 'role=main'
  12. Note
    : width is at least 340 (767, 468, 520, 723, 424 and 840 today: full width at 767 px, W − 300 beside the rail up to 1023 px, W − 600 beside the rail and the settings nav from 1024 px), scrollWidth equals clientWidth and pageHorizontalOverflow is false (insideViewport is false only because the page is taller than 1024 px).
  13. Note
    Take
  14. Do
    control-openhands browser screenshot --feature F09.breakpoint-sweep --name app-767
  15. Note
    and --name app-1024.
  16. Note
    Finish with
  17. Do
    control-openhands browser viewport desktop

Synced badge #

  1. Check
    control-openhands browser text 'testid=backend-synced-settings-badge'
  2. Do
    control-openhands browser eval 'location.origin'
  3. Expect
    The text is These settings are synced from Local backend (<origin>) with the same origin.

Cloud links #

  1. Note
    On a local backend
  2. Check
    control-openhands browser count 'testid=settings-integrations-link'
  3. Check
    control-openhands browser count 'testid=settings-cloud-link'

    are both 0.

  4. Note
    Positive case (blocked without a Cloud account): with a Cloud backend active both exist; "All Cloud Settings" points at <cloud host>/settings?org=<org id> and opens in a new tab unless the canvas is locked to Cloud.

Update card and dialog #

  1. Note
    On a settings page run
  2. Check
    control-openhands browser text 'testid=agent-canvas-update-toggle'

    (Agent Canvas, Version, the client version, plus the badge text when present),

  3. Check
    control-openhands browser count 'testid=agent-canvas-update-badge'

    (0 while the npm check fails; 1 with "Up to date" or "Update" when it succeeds), then

  4. Do
    control-openhands browser click 'testid=agent-canvas-update-toggle'
  5. Wait
    control-openhands browser wait 'testid=agent-canvas-update-modal'
  6. Check
    control-openhands browser text 'testid=agent-canvas-update-status'
  7. Note
    With registry access and a current client the status is You're running the latest version. What's new, and
  8. Check
    control-openhands browser attr 'testid=agent-canvas-update-release-notes' href

    is https://github.com/OpenHands/OpenHands/releases (target _blank).

  9. Note
    Without registry access the status is Couldn't check for updates. Try again later. Re-check: run
  10. Check
    control-openhands browser network --clear
  11. Do
    control-openhands browser click 'testid=agent-canvas-update-check-button' --observe 'testid=agent-canvas-update-modal' --observe-ms 2000

    (the observed states include Checking for updates… before the final status), then

  12. Check
    control-openhands browser network
  13. Note
    : exactly one new GET https://registry.npmjs.org/@openhands/agent-canvas/latest proves the button re-checked (browser errors does not list successful requests).
  14. Check
    control-openhands browser enabled 'testid=agent-canvas-update-check-button'

    is true again.

  15. Note
    Close with
  16. Do
    control-openhands browser click 'testid=close-agent-canvas-update-modal'

    (modal count 0); reopen and

  17. Do
    control-openhands browser press Escape
  18. Note
    also closes it.

Update available #

  1. Note
    Blocked unless the browser reaches the registry and npm latest is newer than the client (a checkout at the latest release shows F09.update-up-to-date instead).
  2. Note
    With both, and a newer npm latest than the client, the status shows Version <x> is available. with testid=agent-canvas-update-release-notes ("What's new"), the npm tab testid=agent-canvas-update-command-npm reads
  3. Do
    npm install -g @openhands/agent-canvas@latest
  4. Note
    the Docker tab reads
  5. Do
    docker pull ghcr.io/openhands/agent-canvas:latest
  6. Note
    and testid=copy-to-clipboard turns into a check mark for 2 s.

Update dialog at phone width #

  1. Do
    control-openhands browser viewport phone
  2. Do
    control-openhands browser goto /settings
  3. Do
    control-openhands browser click 'testid=settings-mobile-hub >> testid=agent-canvas-update-toggle'
  4. Wait
    control-openhands browser wait 'testid=agent-canvas-update-modal'
  5. Check
    control-openhands browser bbox 'testid=agent-canvas-update-modal'
  6. Check
    control-openhands browser bbox 'testid=close-agent-canvas-update-modal'
  7. Do
    control-openhands browser screenshot --feature F09.update-modal-phone --name dialog
  8. Expect
    Both boxes have insideViewport true and pageHorizontalOverflow false: the dialog is 351 px wide at x 19.5 and Close is at x 329.5; the screenshot shows the whole "Agent Canvas" title and the X.
  9. Do
    control-openhands browser click 'testid=close-agent-canvas-update-modal'
  10. Note
    closes it (control-openhands browser count 'testid=agent-canvas-update-modal' is 0).
  11. Do
    control-openhands browser viewport narrow

    (320×700) and repeat the goto, click, wait and both bbox commands: the dialog is 288 px wide at x 16 and Close is at x 263, both inside the viewport;

  12. Do
    control-openhands browser press Escape
  13. Note
    closes it.
  14. Do
    control-openhands browser viewport desktop

Sidebar version tile #

  1. Note
    On / run
  2. Check
    control-openhands browser count 'testid=agent-canvas-version-tile'
  3. Expect
    It is 0 unless the npm check found a newer version, also when the check succeeded with "Up to date" (positive case blocked without registry access or while the client is the latest release).

Unknown sub-page #

  1. Do
    control-openhands browser goto /settings/does-not-exist
  2. Check
    control-openhands browser snapshot 'testid=not-found-screen'
  3. Note
    : heading "Page not found", the paragraph This address does not match any page. Check the URL, or go back to the home page. and link "Home".
  4. Expect
    The settings shell is absent (control-openhands browser count 'testid=settings-screen' and control-openhands browser count 'testid=settings-navbar-desktop' are 0), while the app sidebar stays (control-openhands browser count 'aside[data-collapsed]' is 1).
  5. Do
    control-openhands browser screenshot --feature F09.unknown-subpath --name not-found

    shows the message beside the sidebar.

  6. Note
    Leave with
  7. Check
    control-openhands browser click 'testid=not-found-home-link' --expect-url '/$'

After the family #

  1. Check
    control-openhands browser errors --clear
  2. Note
    before the family and
  3. Check
    control-openhands browser errors --app-only
  4. Note
    after it.
  5. Note
    Expected: pageErrors 0 and appErrors 0; the unknown sub-page adds no errors.
  6. Expect
    The Agent profile editor logs [i18n] Missing translation for key "SCHEMA$TOOL_CONCURRENCY_LIMIT$…" console warnings, counted under warnings, not as errors (the Agent page's family).
  7. Expect
    A registry request failure, when the sandbox blocks npm, is external.

Gotchas and known limits

  • browser click returns the URL before client-side navigation and redirects settle (it prints / or /settings). Always follow a navigating click with browser wait-url '<regex>$' before asserting.
  • At phone width there are two backend-selector-settings-link elements (hidden desktop sidebar plus the drawer); scope with testid=sidebar-mobile-drawer >> ... or the click fails in strict mode. The hub's nav links share testids with the desktop nav, so scope them with testid=settings-mobile-hub >> ....
  • The page does not scroll inside the settings <main>; the app shell column does. browser scroll <selector> --by N only scrolls the element into view and ignores --by; hover the content first, then browser scroll --by N (mouse wheel at the pointer).
  • Only /settings/app is taller than a 1000 px viewport in a fresh run; use it for the pinned-nav check.
  • Visiting /settings/agents ticks the checklist item "Customize your agent" for good (struck through, still a working link, so the bullet order above is fine), and configuring a model hides the Home banner. Run the banner deep link before llm preset on a fresh run.
  • Registry reachability depends on the sandbox and changes between runs. When the browser cannot reach registry.npmjs.org (seen as ERR_CERT_AUTHORITY_INVALID through a proxy), the version badge and sidebar tile stay hidden and the dialog shows the failure state; that is environment, not product. When it can, a checkout at the latest release shows "Up to date" and the failure text cannot be produced. Check browser network for the registry request before deciding which update rows are blocked.
  • The update dialog's role="dialog" wrapper has no accessible name, so role=dialog[name=...] does not match it; use testid=agent-canvas-update-modal.
  • The /settings → /settings/agents redirect only happens at ≥1024 px. Below that (phones and tablets) /settings is the hub; do not expect a redirect there.
  • From 768 to 1023 px the sidebar rail stays and the top bar is gone, so a settings sub-page shows neither the settings nav nor a Back chevron (browser visible 'testid=sidebar-mobile-back-button' is false); the rail's gear (backend-selector-settings-link) returns to the hub.
  • The phone hub is indented 32 px while sub-pages start at 16 px (hub padding inside the page padding); that is current layout, not overflow.
  • The "LLM Profiles" rename in useSettingsNavItems targets a /settings nav item that does not exist, so it never applies; the LLM nav item always reads "LLM".

Source paths: src/routes.ts, src/routes/settings.tsx, src/routes/settings-index.tsx, src/routes/agent-settings.tsx, src/constants/settings-nav.tsx, src/hooks/use-settings-nav-items.ts, src/utils/settings-utils.ts, src/components/features/settings/settings-layout.tsx, src/components/features/settings/settings-desktop-sidebar.tsx, src/components/features/settings/settings-mobile-hub.tsx, src/components/features/settings/backend-synced-settings-badge.tsx, src/components/features/settings/agent-canvas-update-card.tsx, src/components/features/settings/agent-canvas-version-tile.tsx, src/utils/mobile-section-nav.ts, src/components/features/command-menu/command-menu-items.tsx.