Skip to content

fix: align dev-stack OH_PERSISTENCE_DIR and automation DB path with Docker#960

Open
tofarr wants to merge 4 commits into
mainfrom
fix/dev-docker-state-path-alignment
Open

fix: align dev-stack OH_PERSISTENCE_DIR and automation DB path with Docker#960
tofarr wants to merge 4 commits into
mainfrom
fix/dev-docker-state-path-alignment

Conversation

@tofarr
Copy link
Copy Markdown
Contributor

@tofarr tofarr commented May 30, 2026

  • A human has tested these changes.

Why

Two path mismatches between npm run dev and Docker caused configs and automations to be stored in different locations, so switching between the two modes produced a blank slate.

Observed:

  • ~/.openhands/agent-canvas/automations.db (created by npm run dev)
  • ~/.openhands/automation/automations.db (created by Docker — the correct one)

This PR supersedes #959 (which added OH_PERSISTENCE_DIR with the wrong value).

Root Causes

1. OH_PERSISTENCE_DIR set to wrong path (fixes #959's attempt)

docker/entrypoint.sh sets:

OPENHANDS_DIR="$HOME/.openhands"
export OH_PERSISTENCE_DIR="${OH_PERSISTENCE_DIR:-${OPENHANDS_DIR}}"  # = ~/.openhands

PR #959 added OH_PERSISTENCE_DIR: config.stateDir to buildAgentServerEnv(PR #959 added OH_PERSiris~/.openhands/agent-canvas— **one level too deep**. Settings/secrets written by Docker live at~PR #959 added OH_PERSISTENCE_DIR: config.stateDir to buildAgentServerEnvettPR #959 added OH_PERSISTENCE_DIR: conronPRdirectory

dev-with-automation.mjs used join(config.stateDir, "automations.db")~/.openhands/agent-canvas/automations.db.

Docker uses $OPENHANDS_DIR/$CONFIG_AUTOMATION_DB where CONFIG_AUTOMATION_DB = automation/automations.db (from config/defaults.json) → ~/.openhands/automation/automations.db.

Summary

  • scripts/dev-safe.mjs: Set OH_PERSISTENCE_DIR: path.dirname(config.stateDir) (= ~/.openhands), matching Docker; write npm conversations to dev_conversations/ instead of conversations/
  • scripts/dev-with-automation.mjs: Use join(dirname(config.stateDir), SHARED_DEFAULTS.paths.automationDb) for AUTOMATION_DB_URL (= ~/.openhands/automation/automations.db); also create that directory on startup
  • scripts/dev-static.mjs: Updated mkdir and releaseStaleConversationLeases to use dev_conversations/
  • playwright.mock-llm.config.ts: Clean both STATE_DIR and the new AUTOMATION_DB_DIR (.tmp/automation/) before each test run
  • AGENTS.md: Document the new automation DB location

Known Limitations

Conversations are not shared between npm and Docker modes. Each conversation's meta.json eConversations are not shared between npm and Docker modes. Each conversation's meta.json eConversations are not shared between npm and Docker modes. Each conversation's meta.rkspace/project/<id>) that do not exist on the host. Rather than try to share them, npm now uses a separate dev_conversations/ directory (OH_CONVERSATIONS_PATH), so Docker's conversations/ dir is left untouched and vice versa.

How to Test

  1. Run npm run dev and configure an LLM provider → confirm settings written to ~/.openhands/ (not ~/.openhands/agent-canvas/)
  2. Run Docker and configure an LLM provider → same path
  3. Restart with the other mode → config should be preserved
  4. Start a conversation in npm mode → confirm it appears in ~/.openhands/agent-canvas/dev_conversations/, not conversations/

Screenshots

My last conversation before switching npm to use dev_conversations...
image

And the first one afterwards...
image

And running in docker...
image

  • Docs / chore

This PR was created by an AI agent (OpenHands) on behalf of the user.


🐳 Docker images for this PR

GHCR package: https://github.com/OpenHands/agent-canvas/pkgs/container/agent-canvas

Component Value
Image ghcr.io/openhands/agent-canvas
Architectures amd64, arm64
Agent Server ghcr.io/openhands/agent-server:1.24.0-python
Automation openhands-automation==1.0.0a5
Commit b8c93c3ed110076921e238cdaae84a8141e1b255

Pull (multi-arch manifest)

# Multi-arch manifest — Docker automatically pulls the correct architecture
docker pull ghcr.io/openhands/agent-canvas:sha-b8c93c3

Run

docker run -it --rm \
  -p 8000:8000 \
  ghcr.io/openhands/agent-canvas:sha-b8c93c3

All tags pushed for this build

ghcr.io/openhands/agent-canvas:sha-b8c93c3-amd64
ghcr.io/openhands/agent-canvas:fix-dev-docker-state-path-alignment-amd64
ghcr.io/openhands/agent-canvas:pr-960-amd64
ghcr.io/openhands/agent-canvas:sha-b8c93c3-arm64
ghcr.io/openhands/agent-canvas:fix-dev-docker-state-path-alignment-arm64
ghcr.io/openhands/agent-canvas:pr-960-arm64
ghcr.io/openhands/agent-canvas:sha-b8c93c3
ghcr.io/openhands/agent-canvas:fix-dev-docker-state-path-alignment
ghcr.io/openhands/agent-canvas:pr-960

About Multi-Architecture Support

  • Each tag (e.g., sha-b8c93c3) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., sha-b8c93c3-amd64) are also available if needed

…ocker

Two path mismatches between `npm run dev` and Docker prevented config and
automation data from being shared across the two modes:

1. **OH_PERSISTENCE_DIR wrong (supersedes PR #959)**
   Docker's entrypoint.sh sets OH_PERSISTENCE_DIR to $HOME/.openhands
   (OPENHANDS_DIR). PR #959 added the env var to buildAgentServerEnv() but
   used config.stateDir (~/.openhands/agent-canvas) — one level too deep.
   Settings and secrets written by Docker live at ~/.openhands/settings.toml
   etc; dev wrote to ~/.openhands/agent-canvas/settings.toml.
   Fix: use path.dirname(config.stateDir) = ~/.openhands, matching Docker.

2. **Automation DB in wrong directory**
   dev-with-automation.mjs put the SQLite DB at
   ~/.openhands/agent-canvas/automations.db. Docker puts it at
   ~/.openhands/automation/automations.db (from config/defaults.json
   paths.automationDb = "automation/automations.db" relative to OPENHANDS_DIR).
   Fix: use join(dirname(config.stateDir), SHARED_DEFAULTS.paths.automationDb)
   so both modes resolve to ~/.openhands/automation/automations.db.
   Also ensure the directory is created on startup (mirrors Docker's mkdir -p).

3. **Mock-LLM test cleanup**
   Update playwright.mock-llm.config.ts to clean both STATE_DIR and the new
   AUTOMATION_DB_DIR (.tmp/automation/) before each test run, since the DB now
   lives outside STATE_DIR.

Co-authored-by: openhands <openhands@all-hands.dev>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-canvas Ready Ready Preview, Comment May 30, 2026 4:00pm

Request Review

…ations

npm dev mode now writes conversations to ~/.openhands/agent-canvas/dev_conversations
instead of conversations, keeping them separate from Docker's conversations dir.

OH_CONVERSATIONS_PATH (set via buildAgentServerEnv) is the single control point;
all mkdir and releaseStaleConversationLeases calls updated to match.

Also condense verbose multi-line comments on OH_PERSISTENCE_DIR and
AUTOMATION_DB_URL to single lines.

Co-authored-by: openhands <openhands@all-hands.dev>
The outer config in dev-with-automation.mjs does not have conversationsPath
(that is a SafeDevConfig property). Use the same join(stateDir, ...) pattern
as the other dirs in the list.

Also removes the debug console.log and restores dev-safe.mjs and dev-static.mjs
to dev_conversations after the manual revert.

Co-authored-by: openhands <openhands@all-hands.dev>
The implementation in buildConfigFromPorts was changed to use
'dev_conversations' as the subdirectory name, but the corresponding
test assertion was not updated.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions
Copy link
Copy Markdown
Contributor

📸 Snapshot Test Report

✅ All snapshots match the main branch baselines.

Category Count
🔴 Changed 0
🆕 New 0
✅ Unchanged 73
Total 73
✅ Unchanged snapshots (73)

archived-conversation

  • conversation-panel-with-archived-badges
  • conversation-view-archived
  • conversation-view-sandbox-error

automations

  • automations-delete-modal
  • automations-list-active-inactive
  • automations-no-automations
  • automations-search-no-results

backends-extended

  • backend-add-blank-disabled
  • backend-add-cloud-advanced-open
  • backend-add-cloud-no-key-disabled
  • backend-add-cloud-with-key-enabled
  • backend-add-form-partially-filled
  • backend-add-invalid-url-disabled
  • backend-add-local-ready
  • backend-add-name-only-disabled
  • backend-add-two-column-layout
  • backend-add-whitespace-host-disabled
  • backend-after-switch
  • backend-cancel-nothing-saved
  • backend-dropdown-two-backends
  • backend-edit-prefilled
  • backend-manage-after-removal
  • backend-manage-two-listed
  • backend-remove-cancelled
  • backend-remove-confirmation
  • backend-switch-overlay

backends

  • backend-add-modal
  • backend-manage-modal
  • backend-selector-open

changes-tab

  • changes-deleted-file
  • changes-diff-viewer
  • changes-empty

collapsible-thinking

  • reasoning-content-collapsed
  • reasoning-content-expanded
  • think-action-collapsed
  • think-action-expanded

mcp-page

  • mcp-custom-server-1-editor-open
  • mcp-custom-server-2-url-filled
  • mcp-custom-server-3-all-filled
  • mcp-custom-server-4-installed
  • mcp-custom-server-editor
  • mcp-empty-installed
  • mcp-search-filtered
  • mcp-slack-install-1-marketplace
  • mcp-slack-install-2-modal
  • mcp-slack-install-3-filled
  • mcp-slack-install-4-installed

onboarding

  • onboarding-step-0-choose-agent
  • onboarding-step-1-check-backend
  • onboarding-step-2-setup-llm
  • onboarding-step-3-say-hello

projects-workspace-browser

  • projects-workspace-browser

settings-page

  • add-backend-modal
  • analytics-consent-modal
  • home-screen
  • settings-app-page
  • settings-page

settings-secrets

  • secrets-add-form-filled
  • secrets-add-form
  • secrets-after-save
  • secrets-delete-confirm
  • secrets-list

settings-verification

  • condenser-settings
  • verification-settings-off
  • verification-settings-on

sidebar

  • sidebar-collapsed
  • sidebar-conversation-panel
  • sidebar-filter-menu

skills-page

  • skills-empty
  • skills-loaded
  • skills-no-match
  • skills-search-filtered
  • skills-type-filter

Generated by the Snapshot Tests workflow. This comment was created by an AI agent (OpenHands) on behalf of the repo maintainers.

@tofarr tofarr marked this pull request as ready for review May 30, 2026 16:34
@tofarr tofarr requested a review from all-hands-bot May 30, 2026 19:06
Copy link
Copy Markdown
Contributor

all-hands-bot commented May 30, 2026

Review complete.

This review was performed through OpenHands Cloud Automation. You can log in and view the conversation here.

Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall Assessment

This is a clean, well-scoped fix to a genuine path-alignment problem. The root cause is clearly diagnosed, each change is minimal and purposeful, and the PR description gives an excellent explanation of the two independent bugs being addressed.

What works well:

  • is now correctly set to (= ), matching Docker's — the previous attempt in #959 was one level too deep.
  • is now derived via so the path is a single source of truth shared with Docker and .
  • Segregating npm conversations under (via ) is the right call: the workaround for workspace-path mismatches is pragmatic and the trade-off is clearly documented in the PR description.
  • Playwright now cleans both and the new — previously the automation DB would survive a wipe of and cause cross-run contamination.
  • Test and updates are included and accurate.

A couple of minor nits inline; nothing blocking.


This review was generated by an AI agent (OpenHands) on behalf of the user through OpenHands Automation. View conversation

join(config.stateDir, "bash_events"),
join(config.stateDir, "storage"),
// Automation DB directory — matches docker/entrypoint.sh mkdir -p behaviour.
dirname(join(dirname(config.stateDir), SHARED_DEFAULTS.paths.automationDb)),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit — nested is slightly hard to parse at a glance.

Since , the directory is always the fixed segment relative to the parent of . The double-dirname wrapping is correct, but this reads more clearly as:

or simply with a short comment. Non-blocking.

Comment thread scripts/dev-static.mjs
process.exit(1);
}
const conversationsPath = join(config.stateDir, "conversations");
const conversationsPath = join(config.stateDir, "dev_conversations");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor — conversations path is duplicated in instead of reading from .

(built in ) already holds . Both here and the identical expression at line 560 will drift silently if the subdirectory name ever changes in . Not a bug today, but worth keeping in mind for a follow-up if is ever refactored to expose a canonical .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants