Engel’s notebook

Secrets

F14 · 12 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.

F14 Secrets

12 behaviors · 16 scripts & contracts · Maintained source

Entry points & prerequisites

What this family covers

Secrets are named values the agent can use during conversations. A user lists, adds, edits and deletes them under Settings → Secrets; saved values are never shown again, and every new conversation receives them as environment variables.

How a user gets here

  • Sidebar settings gear (backend-selector-settings-link), then Secrets in the settings navigation (sidebar-settings-/settings/secrets).
  • Direct URL /settings/secrets.
  • Command menu (Control+k or Meta+k, or command-menu-trigger in the sidebar): search "Secrets", choose Secrets settings.
  • The agent itself, in a conversation: its request_secret client tool opens the request dialog (testid=secret-request-modal) on /conversations/<id> (F14.agent-request).

Given

Preconditions:

  • Baseline state (launched, doctored, onboard --skip done).
  • No secret named QA_TMP_SECRET, QA_AGENT_SECRET, QA_RENAMED_SECRET or QA_BACK_SECRET exists (control-openhands api GET /api/settings/secrets).
  • F14.agent-access, F14.rename, F14.edit-value and F14.agent-request need an active LLM profile (control-openhands llm preset deepseek).
  • No secret named QA_REQ_SECRET or QA_REQ_CANCEL exists, for F14.agent-request.
Open the listF14.listMapped behavior

Scenario F14--open-the-list · Permalink · Source

Given

Use F14’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 “Settings”.
  2. Do
    Choose “Secrets settings”.

Expect

the Secrets page lists each secret's name and description with Edit and Delete actions; the launcher-seeded OPENHANDS_AUTOMATION_API_KEY is always present.

Exact CLI recipe & bug checks

Navigate from the sidebar. Run control-openhands browser click 'testid=backend-selector-settings-link', control-openhands browser click 'testid=sidebar-settings-/settings/secrets', then control-openhands browser count 'testid=secret-item >> has-text=OPENHANDS_AUTOMATION_API_KEY'. The count is 1 and the URL is /settings/secrets.

Command-menu entryF14.listMapped behavior

Scenario F14--command-menu-entry · Permalink · Source

Given

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

Do — reading outline

  1. Do
    Open /.
  2. Do
    Press Control+k.
  3. Do
    Enter “Secrets” in “Command menu” / the combobox.
  4. Do
    Press Enter.

Expect

the Secrets page lists each secret's name and description with Edit and Delete actions; the launcher-seeded OPENHANDS_AUTOMATION_API_KEY is always present.

Exact CLI recipe & bug checks

Run control-openhands browser goto / (the previous bullet left /settings/secrets, which would make the URL check pass vacuously), then control-openhands browser press Control+k, control-openhands browser type 'testid=command-menu >> role=combobox' Secrets, control-openhands browser press Enter, then control-openhands browser url. The URL ends in /settings/secrets.

CreateF14.createMapped behavior

Scenario F14--create · Permalink · Source

Given

Use F14’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 “Add secret”.
  2. Do
    Enter “QA_TMP_SECRET” in “Add secret form” / “Name”.
  3. Do
    Enter “dummy-value-123” in “Add secret form” / “Value”.
  4. Do
    Enter “QA dummy” in “Add secret form” / “Description”.
  5. Do
    Choose “Add secret form” / “Submit”.
  6. Do
    Reload the page.

Expect

adding a secret persists it; it is listed after a reload.

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=add-secret-button', control-openhands browser fill 'testid=add-secret-form >> testid=name-input' QA_TMP_SECRET, control-openhands browser fill 'testid=add-secret-form >> testid=value-input' dummy-value-123, control-openhands browser fill 'testid=add-secret-form >> testid=description-input' 'QA dummy', control-openhands browser click 'testid=add-secret-form >> testid=submit-button', then control-openhands browser reload and control-openhands browser count 'testid=secret-item >> has-text=QA_TMP_SECRET'. The count is 1 after the reload.

Duplicate nameF14.create-validationMapped behavior

Scenario F14--duplicate-name · Permalink · Source

Given

Use F14’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 “Add secret”.

Expect

duplicate names, names that break ^[a-zA-Z][a-zA-Z0-9_]{0,63}$ and empty values are refused with a message.

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=add-secret-button', fill name-input with QA_TMP_SECRET and value-input with other as above, click testid=add-secret-form >> testid=submit-button, then control-openhands browser snapshot 'testid=add-secret-form'. The snapshot shows the paragraph Secret already exists and the form stays open. The paragraph uses the theme's danger colour (#18199): control-openhands browser eval "getComputedStyle([...document.querySelectorAll('[data-testid=add-secret-form] p')].find(e=>e.innerText==='Secret already exists')).color" is rgb(231, 106, 94) on OpenHands-Neutral and rgb(209, 52, 56) on Light+ (F16.color-theme).

Invalid nameF14.create-validationMapped behavior

Scenario F14--invalid-name · Permalink · Source

Given

Use F14’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
    Enter “bad name!” in “Add secret form” / “Name”.
  2. Do
    Choose “Add secret form” / “Cancel”.

Expect

duplicate names, names that break ^[a-zA-Z][a-zA-Z0-9_]{0,63}$ and empty values are refused with a message.

Exact CLI recipe & bug checks

In the same form run control-openhands browser fill 'testid=add-secret-form >> testid=name-input' 'bad name!', click submit, then control-openhands browser eval "document.querySelector('[data-testid=add-secret-form] [data-testid=name-input]').validationMessage". The value is the browser's pattern message (Please match the requested format. in Chromium) and no secret is created (api GET /api/settings/secrets). Close with control-openhands browser click 'testid=add-secret-form >> testid=cancel-button'.

Empty valueF14.create-validationMapped behavior

Scenario F14--empty-value · Permalink · Source

Given

Use F14’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 “Add secret”.

Expect

duplicate names, names that break ^[a-zA-Z][a-zA-Z0-9_]{0,63}$ and empty values are refused with a message.

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=add-secret-button', fill testid=add-secret-form >> testid=name-input with QA_EMPTY_VALUE and leave value-input empty. control-openhands browser enabled 'testid=add-secret-form >> testid=submit-button' is false, and control-openhands browser eval "document.querySelector('[data-testid=add-secret-form] [data-testid=value-input]').validationMessage" is Please fill out this field. Close with testid=add-secret-form >> testid=cancel-button; api GET /api/settings/secrets lists no QA_EMPTY_VALUE.

Edit keeps the valueF14.editKnown issue

Scenario F14--edit-keeps-the-value · Permalink · Source

Given

Use F14’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 “Secret row” / containing “QA_TMP_SECRET” / “Edit secret”.
  2. Do
    Reload the page.

Expect

editing the description with the value left blank keeps the stored value; Save stays disabled until something changes.

Linked known issues

Known issue #17710: On the Cloud backend, deleting a secret reportedly adds a suffixed duplicate and leaves the original. Reproduction needs a disposable writable OpenHands Cloud account at the affected service; Local secret CRUD exercises a different transport. (F14.delete, F14.edit).

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=secret-item >> has-text=QA_TMP_SECRET >> testid=edit-secret-button', control-openhands browser value 'testid=edit-secret-form >> testid=value-input' (empty) and control-openhands browser enabled 'testid=edit-secret-form >> testid=submit-button' (false). Fill testid=edit-secret-form >> testid=description-input with QA dummy edited; enabled turns true. Click testid=edit-secret-form >> testid=submit-button, control-openhands browser reload, then control-openhands browser text 'testid=secret-item >> has-text=QA_TMP_SECRET'. The row text is QA_TMP_SECRET and QA dummy edited separated by a tab ("QA_TMP_SECRET\tQA dummy edited" in the JSON output).

Delete with confirmationF14.deleteKnown issue

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

Given

Use F14’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 “Secret row” / containing “QA_TMP_SECRET” / “Delete secret”.
  2. Do
    Choose “Confirmation dialog” / “Cancel”.
  3. Do
    Choose “Confirmation dialog” / “Confirm”.
  4. Do
    Reload the page.

Expect

deleting asks for confirmation; Cancel keeps the row, Confirm removes it for good.

Linked known issues

Known issue #17710: On the Cloud backend, deleting a secret reportedly adds a suffixed duplicate and leaves the original. Reproduction needs a disposable writable OpenHands Cloud account at the affected service; Local secret CRUD exercises a different transport. (F14.delete, F14.edit).

Exact CLI recipe & bug checks

Run control-openhands browser click 'testid=secret-item >> has-text=QA_TMP_SECRET >> testid=delete-secret-button'; the dialog testid=confirmation-modal reads Are you sure you want to delete this key?. Run control-openhands browser click 'testid=confirmation-modal >> testid=cancel-button'; the row count stays 1. Repeat the delete click, then control-openhands browser click 'testid=confirmation-modal >> testid=confirm-button', control-openhands browser reload and control-openhands browser count 'testid=secret-item >> has-text=QA_TMP_SECRET'. The count is 0.

Agent receives the secretF14.agent-accessMapped behavior

Scenario F14--agent-receives-the-secret · Permalink · Source

Given

Use F14’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 “Run exactly this command and reply with only its output: python3 -c "import os; print(os.environ.get('QA_AGENT_SECRET') == 'qa-dummy-4821')"”.

Expect

a conversation started after the secret was saved sees it as an environment variable.

Exact CLI recipe & bug checks

Create QA_AGENT_SECRET with value qa-dummy-4821 through the form as in Create. Run control-openhands conversation start --prompt "Run exactly this command and reply with only its output: python3 -c \"import os; print(os.environ.get('QA_AGENT_SECRET') == 'qa-dummy-4821')\"" --wait --timeout 240, then control-openhands conversation events <id> --kinds ObservationEvent. The terminal observation's text is True. Assert the observation, not the agent's reply: deepseek-flash often declines to echo anything about a secret (see Gotchas). Keep QA_AGENT_SECRET for Rename and Replace value below; they delete it.

Phone layoutF14.phoneMapped behavior

Scenario F14--phone-layout · Permalink · Source

Given

Use F14’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 /settings/secrets.
  2. Do
    Start a conversation as specified in the recipe.
  3. Setup
    Use the phone viewport.
  4. Do
    Choose “Add secret”.
  5. Setup
    Use the desktop viewport.

Expect

the page and its forms fit a 390 px viewport without horizontal overflow.

Exact CLI recipe & bug checks

Run control-openhands browser goto /settings/secrets (conversation start left the browser on the conversation), control-openhands browser viewport phone, control-openhands browser bbox 'testid=secrets-settings-screen' and control-openhands browser screenshot --feature F14.phone --name list. insideViewport is true and pageHorizontalOverflow is false; the screenshot shows the table with Edit and Delete icons. Then control-openhands browser click 'testid=add-secret-button', control-openhands browser bbox 'testid=add-secret-form' (insideViewport true, no page overflow) and control-openhands browser screenshot --feature F14.phone --name add-form; cancel the form. Return with control-openhands browser viewport desktop.

Back discards the draftF14.form-backMapped behavior

Scenario F14--back-discards-the-draft · Permalink · Source

Given

Use F14’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 “Add secret”.
  2. Do
    Choose “Back to secrets”.

Expect

the Add and Edit forms replace the list with an Add a Secret / Edit a Secret title and a Back button; Back (like Cancel) returns to the list and discards the draft.

Exact CLI recipe & bug checks

On /settings/secrets run control-openhands browser click 'testid=add-secret-button' and control-openhands browser text 'testid=secret-editor-title' (Add a Secret). Fill testid=add-secret-form >> testid=name-input with QA_BACK_SECRET and value-input with dummy, then control-openhands browser click 'testid=back-to-secrets'. control-openhands browser count 'testid=add-secret-form' is 0, the list is back (browser count 'testid=secret-item' > 0) and control-openhands api GET /api/settings/secrets lists no QA_BACK_SECRET. Reopening Add shows an empty name-input (browser value); leave with testid=back-to-secrets. The Edit form shows Edit a Secret in the same secret-editor-title.

Rename keeps the valueF14.renameMapped behavior

Scenario F14--rename-keeps-the-value · Permalink · Source

Given

Use F14’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 /settings/secrets.
  2. Do
    Choose “Secret row” / containing “QA_AGENT_SECRET” / “Edit secret”.
  3. Do
    Reload the page.
  4. Do
    Start a conversation with the prompt “Run exactly this command and reply with only its output: python3 -c "import os; print(os.environ.get('QA_RENAMED_SECRET') == 'qa-dummy-4821', 'QA_AGENT_SECRET' in os.environ)"”.

Expect

the Edit form's name is editable; renaming with the value left blank keeps the stored value under the new name, and renaming onto an existing name is refused with Secret already exists.

Exact CLI recipe & bug checks

Needs QA_AGENT_SECRET from Agent receives the secret. Run control-openhands browser goto /settings/secrets, control-openhands browser click 'testid=secret-item >> has-text=QA_AGENT_SECRET >> testid=edit-secret-button', fill testid=edit-secret-form >> testid=name-input with OPENHANDS_AUTOMATION_API_KEY and click testid=edit-secret-form >> testid=submit-button: control-openhands browser snapshot 'testid=edit-secret-form' shows the paragraph Secret already exists and the form stays open. Fill name-input with QA_RENAMED_SECRET (value left blank), click submit, control-openhands browser reload; browser count 'testid=secret-item >> has-text=QA_RENAMED_SECRET' is 1, the QA_AGENT_SECRET count is 0, and api GET /api/settings/secrets lists only the new name. Then run control-openhands conversation start --prompt "Run exactly this command and reply with only its output: python3 -c \"import os; print(os.environ.get('QA_RENAMED_SECRET') == 'qa-dummy-4821', 'QA_AGENT_SECRET' in os.environ)\"" --wait --timeout 240 and control-openhands conversation events <id> --kinds ObservationEvent: the observation is True False.

Replace the valueF14.edit-valueMapped behavior

Scenario F14--replace-the-value · Permalink · Source

Given

Use F14’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 /settings/secrets.
  2. Do
    Choose “Secret row” / containing “QA_RENAMED_SECRET” / “Edit secret”.

Expect

typing a value in the Edit form (labelled "Secret Value (leave blank to preserve the existing value)") replaces the stored value for new conversations.

Exact CLI recipe & bug checks

Run control-openhands browser goto /settings/secrets, control-openhands browser click 'testid=secret-item >> has-text=QA_RENAMED_SECRET >> testid=edit-secret-button' and control-openhands browser snapshot 'testid=edit-secret-form' (the value textbox is named Secret Value (leave blank to preserve the existing value)). Fill testid=edit-secret-form >> testid=value-input with qa-dummy-7350; browser enabled 'testid=edit-secret-form >> testid=submit-button' is true. Click submit; browser count 'testid=edit-secret-form' is 0. Start a conversation with the prompt Run exactly this command and reply with only its output: python3 -c "import os; print(os.environ.get('QA_RENAMED_SECRET') == 'qa-dummy-7350')" (quoted as above) and read its ObservationEvent: True.

Agent requests a secretF14.agent-requestKnown failure

Scenario F14--agent-requests-a-secret · Permalink · Source

Given

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

Do — reading outline

  1. Do
    Open /.
  2. Do
    Start a conversation with the prompt “Call the request_secret tool with name QA_REQ_SECRET and reason 'QA check: a dummy token for verification.' Do not run any other tools. After calling it, end your turn and wait.”.
  3. Do
    Press Tab.
  4. Do
    Press Escape.
  5. Do
    Use browser mouse click as specified in the exact recipe.
  6. Do
    Reload the page.
  7. Do
    Enter “qa-dummy-5190” in “Secret request modal” / “Value”.
  8. Do
    Choose “Secret request modal” / “Submit”.
  9. Wait
    Wait for the conversation to finish.
  10. Do
    Send a follow-up with the prompt “Run exactly this command and reply with only its output: python3 -c "import os; print(os.environ.get('QA_REQ_SECRET') == 'qa-dummy-5190')"”.
  11. Do
    Open /.
  12. Do
    Choose “Secret request modal” / “Cancel”.
  13. Setup
    Use the desktop viewport.

Expect

in a local conversation the agent's request_secret tool opens The agent needs a secret over the chat with its reason and the name prefilled; Add secret saves it, attaches it to the running conversation and reports saved to the agent, Cancel reports not_saved, and a name that is already saved is attached without a dialog. The value never reaches the agent's events.

Known bug observations

Known failure (reproduced 2026-10-14, #18432): at 390×844 browser bbox 'testid=secret-request-modal' is 520 px wide at x -65, insideViewport false, so the title, the field labels and Cancel are cut off on the left (and 65 px on the right); run the block below. Return with browser viewport desktop. QA_REQ_SECRET is deleted in the cleanup below.

Exact CLI recipe & bug checks

Run control-openhands browser goto /, then control-openhands conversation start --prompt "Call the request_secret tool with name QA_REQ_SECRET and reason 'QA check: a dummy token for verification.' Do not run any other tools. After calling it, end your turn and wait." --wait --timeout 240 (prints <req-id>; it leaves the browser on the conversation). control-openhands conversation events <req-id> --kinds ActionEvent,ObservationEvent shows the request_secret action and the observation Tool call dispatched to client.; control-openhands browser snapshot 'role=dialog' reads dialog "The agent needs a secret", the reason QA check: a dummy token for verification., textbox Name holding QA_REQ_SECRET, Value, Description Optional, Cancel and Add secret [disabled] (browser screenshot --feature F14.agent-request --name modal). Focus starts in name-input and Tab cycles Name, Value, Description and Cancel without leaving the dialog (control-openhands browser eval "document.activeElement.dataset.testid" after each browser press Tab). control-openhands browser press Escape and control-openhands browser mouse-click 30 500 leave it open (browser count 'testid=secret-request-modal' stays 1), and so does control-openhands browser reload, with the name still prefilled. Run control-openhands browser fill 'testid=secret-request-modal >> testid=value-input' qa-dummy-5190 and control-openhands browser click 'testid=secret-request-modal >> testid=submit-button': the dialog closes, control-openhands api GET /api/settings/secrets lists QA_REQ_SECRET, and after control-openhands conversation wait <req-id> --fresh --timeout 180 control-openhands conversation events <req-id> --kinds MessageEvent holds the user message [secret-request] {"status":"saved","name":"QA_REQ_SECRET"}, which the chat does not show (browser count 'text=[secret-request]' is 0). The running conversation now has the value: control-openhands conversation send <req-id> --prompt "Run exactly this command and reply with only its output: python3 -c \"import os; print(os.environ.get('QA_REQ_SECRET') == 'qa-dummy-5190')\"" --wait --timeout 240, then the last terminal ObservationEvent is True. Cancel: browser goto /, start a second conversation with the same prompt naming QA_REQ_CANCEL and reason QA cancel check., fill value-input with should-not-save and control-openhands browser click 'testid=secret-request-modal >> testid=cancel-button': the dialog closes, api GET /api/settings/secrets lists no QA_REQ_CANCEL, the agent receives [secret-request] {"status":"not_saved","name":"QA_REQ_CANCEL","guidance":...}, and a reload does not bring the dialog back. Already saved: a third conversation asking for QA_REQ_SECRET again shows no dialog (browser count 'testid=secret-request-modal' 0) and its events hold [secret-request] {"status":"saved","name":"QA_REQ_SECRET"}. Expected: at phone width the dialog fits the viewport (at most 90vw, 351 px at 390 px, insideViewport true) with the title, every field and both buttons visible. Known failure (reproduced 2026-10-14, #18432): at 390×844 browser bbox 'testid=secret-request-modal' is 520 px wide at x -65, insideViewport false, so the title, the field labels and Cancel are cut off on the left (and 65 px on the right); run the block below. Return with browser viewport desktop. QA_REQ_SECRET is deleted in the cleanup below.

sh known-bug F14.agent-request phone-width

  control-openhands browser viewport desktop
  control-openhands browser goto /
  control-openhands conversation start --prompt "Call the request_secret tool with name QA_REQ_PHONE and reason 'QA phone check.' Do not run any other tools. After calling it, end your turn and wait." --wait --timeout 240
  control-openhands browser wait 'testid=secret-request-modal'
  control-openhands assert --field insideViewport --equals true -- browser bbox 'testid=secret-request-modal'
  control-openhands browser viewport phone
  control-openhands browser screenshot --feature F14.agent-request --name phone-width
  control-openhands assert --field insideViewport --equals true --bug 'insideViewport eq false && box.width eq 520' -- browser bbox 'testid=secret-request-modal'  # bug
  control-openhands browser click 'testid=secret-request-modal >> role=button[name="Cancel"]'
  control-openhands browser wait 'testid=secret-request-modal' --state detached
Escape cancels deleteF14.delete-escapeMapped behavior

Scenario F14--escape-cancels-delete · Permalink · Source

Given

Use F14’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 /settings/secrets.
  2. Do
    Choose “Secret row” / containing “QA_RENAMED_SECRET” / “Delete secret”.
  3. Do
    Press Escape.
  4. Do
    Reload the page.

Expect

Escape closes the delete confirmation without deleting.

Exact CLI recipe & bug checks

Run control-openhands browser goto /settings/secrets, control-openhands browser click 'testid=secret-item >> has-text=QA_RENAMED_SECRET >> testid=delete-secret-button', control-openhands browser press Escape, then control-openhands browser count 'testid=confirmation-modal' (0) and browser count 'testid=secret-item >> has-text=QA_RENAMED_SECRET' (1). Clean up: delete QA_RENAMED_SECRET and then QA_REQ_SECRET (from F14.agent-request), each with delete-secret-button → testid=confirmation-modal >> testid=confirm-button, browser reload; api GET /api/settings/secrets lists only OPENHANDS_AUTOMATION_API_KEY.

Cipher mismatch refuses mutationF14.create · F14.edit · F14.deleteKnown failureKnown issue

Scenario F14--cipher-mismatch-refuses-mutation · Permalink · Source

Given

Use F14’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
    This is an external-state fixture, not a credential test. Arrange two own dummy secrets and a dummy loopback LLM key; privately back up the run-owned encryption-key, settings.json and secrets.json. Replace only the own cipher file with a newly generated value (never display it), restart and doctor. After bootstrap, restore the old encrypted store bytes under the replacement cipher to isolate the UI write. Add a new dummy secret through the real Secrets form, and inspect only boolean preservation metadata / availability of the old dummy value privately. Independently restore the encrypted settings baseline and change Sound Notifications through Application → Save. Expected: reject writes when existing encrypted state cannot decrypt, preserving it. Known failure, reproduced 2026-10-12 (OpenHands/software-agent-sdk#5622): the secret create succeeds and rewrites an old entry while its dummy value is unavailable (getter 404, name still listed); two unrelated settings saves persist the old LLM key as null. Repeat with a fresh replacement cipher. Immediately restore the original cipher and both stores in finally, restart and doctor; original state must be readable again. This drives secret creation and settings Save, not every edit/delete permutation. Keep all cipher bytes, values and raw store snapshots private.

Expect

adding a secret persists it; it is listed after a reload.

editing the description with the value left blank keeps the stored value; Save stays disabled until something changes.

deleting asks for confirmation; Cancel keeps the row, Confirm removes it for good.

Known bug observations

Known failure, reproduced 2026-10-12 (OpenHands/software-agent-sdk#5622): the secret create succeeds and rewrites an old entry while its dummy value is unavailable (getter 404, name still listed); two unrelated settings saves persist the old LLM key as null. Repeat with a fresh replacement cipher. Immediately restore the original cipher and both stores in finally, restart and doctor; original state must be readable again. This drives secret creation and settings Save, not every edit/delete permutation. Keep all cipher bytes, values and raw store snapshots private.

Linked known issues

Known issue #17710: On the Cloud backend, deleting a secret reportedly adds a suffixed duplicate and leaves the original. Reproduction needs a disposable writable OpenHands Cloud account at the affected service; Local secret CRUD exercises a different transport. (F14.delete, F14.edit).

Exact CLI recipe & bug checks

This is an external-state fixture, not a credential test. Arrange two own dummy secrets and a dummy loopback LLM key; privately back up the run-owned encryption-key, settings.json and secrets.json. Replace only the own cipher file with a newly generated value (never display it), restart and doctor. After bootstrap, restore the old encrypted store bytes under the replacement cipher to isolate the UI write. Add a new dummy secret through the real Secrets form, and inspect only boolean preservation metadata / availability of the old dummy value privately. Independently restore the encrypted settings baseline and change Sound Notifications through Application → Save. Expected: reject writes when existing encrypted state cannot decrypt, preserving it. Known failure, reproduced 2026-10-12 (OpenHands/software-agent-sdk#5622): the secret create succeeds and rewrites an old entry while its dummy value is unavailable (getter 404, name still listed); two unrelated settings saves persist the old LLM key as null. Repeat with a fresh replacement cipher. Immediately restore the original cipher and both stores in finally, restart and doctor; original state must be readable again. This drives secret creation and settings Save, not every edit/delete permutation. Keep all cipher bytes, values and raw store snapshots private.

Family notes, prerequisites for known issues & limits Known issue
  • Settings nav test ids embed the route, slashes included: sidebar-settings-/settings/secrets. Quote the selector as written.
  • The value editor is a plain textarea, not a password field: use dummy values only, and never ask the agent to print a real secret.
  • Edit forms intentionally show an empty value; that is not data loss.
  • The invalid-name check is native browser validation. It shows a tooltip that screenshots do not capture, and it leaves any earlier Secret already exists paragraph on screen. Assert with validationMessage and the API, not the paragraph.
  • The model is the weak link in the agent checks, not the product: asked to print a secret's length or last characters, deepseek-flash replies "I can't run that one" and runs nothing, and even for the equality check it may refuse to repeat True in its reply. Use the equality prompt and read the terminal ObservationEvent; conversation events truncates message text to about 160 characters.
  • The conversation Overview has no Secrets entry point: its "..." menu (conversation-overview-ellipsis) offers Workspace and Git sections only.
  • There is no empty state to reach safely: secrets-empty renders only with zero secrets, which means deleting the launcher-seeded key (see below).
  • Secrets saved in Settings reach only conversations started after the save. Reuse an older conversation and the variable is missing. Only request_secret (F14.agent-request) attaches a secret to a running conversation, and only on a local backend.
  • request_secret is registered on every new OpenHands-agent conversation this frontend launches (not on ACP agents or older conversations) and appears in Show Agent Tools & Metadata (see F07.agent-tools-modal). The agent learns the outcome from a hidden [secret-request] ... user message; read it with conversation events <id> --kinds MessageEvent, not from the chat.
  • At 390 px the name column truncates long names (OPEN…); use browser text on the row rather than the screenshot to read a name.
  • OPENHANDS_AUTOMATION_API_KEY is seeded by the launcher for automations; deleting it breaks F18/F19 runs in the same stack.
  • Known issue #17710: On the Cloud backend, deleting a secret reportedly adds a suffixed duplicate and leaves the original. Reproduction needs a disposable writable OpenHands Cloud account at the affected service; Local secret CRUD exercises a different transport. (F14.delete, F14.edit).

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.