Skip to content

Editing a root node opened by navigation in browser mode fails to save #2940

Closed

Description

@malibio

Overview

In browser mode (dev-proxy transport), editing a field on a root node that was opened by navigation never persists. The store tries to create the node instead of updating it, the daemon rejects the create with a 500, and the edit is gone after a reload.

Problem Statement

Repro (identical on main):

  1. Run browser mode against an isolated daemon (see the run-app skill: NODESPACE_HOME + short NODESPACED_SOCKET, then dev:browser)
  2. Create a root person node through the proxy: POST /api/nodes with { id, nodeType: "person", properties: { first_name, last_name, email } }
  3. Open it: getNavigationService().navigateToNode(id, true)
  4. Change a property field (e.g. Last name) and tab away

Observed:

  • Console: [SharedNodeStore] Database write failed for node <id>: BackendError: Failed to insert node: Failed to create node
  • POST /api/nodes → HTTP 500
  • The field shows the new value until reload. GET /api/nodes/<id> still returns the old value.

Expected: an update against the existing node, which persists.

Root Cause (partially established)

Location: packages/desktop-app/src/lib/services/shared-node-store.svelte.ts (the updateNode persistence closure, the isPersistedToDatabase branch around line 1755 and the create fallback around line 1985)

The branch choice is this.persistedNodeIds.has(nodeId). For this node it is false, so the update goes down the "node doesn't exist yet" path and calls backendAdapter.createNode for a node that already exists.

Not yet established: which load path puts the page's root node into the store without marking it persisted. Database-sourced setNode/batchSetNodes do add to persistedNodeIds (around lines 2392, 2413, 2806, 2827). The first step is to trace how BaseNodeViewer / navigation inserts the root (base-node-viewer.svelte around lines 440-463, loadChildrenTree) and which source or options it uses. Also check whether Tauri mode is affected; this was only reproduced in browser mode.

Proposed Solution

Find the ingress that inserts the root without persisted tracking, and make it mark the node persisted: use a database source, or markAsPersistedOnly, consistent with the other load paths. Do not add a per-write existence query; persistedNodeIds exists to avoid one.

Architecture Impact

No ADR is affected. nodespace-docs/architecture/frontend-state-and-persistence.md documents persistedNodeIds as distinguishing backend-existing nodes from in-memory ones. This bug violates that contract; the fix restores it.

Acceptance Criteria

  • The load path that leaves the root untracked is identified and fixed
  • Editing a field on a navigated-to root node in browser mode issues an update (no POST /api/nodes), and the value survives a reload
  • Tauri mode checked for the same defect
  • Regression test: a root node loaded through the navigation/page-load path is in persistedNodeIds, and a subsequent updateNode takes the update path
  • Code passes bun run quality:fix
  • Tests pass (bun run test:all)

Technical Specifications

Reference Files

  • Store: packages/desktop-app/src/lib/services/shared-node-store.svelte.ts: persistedNodeIds, updateNode persistence closure, setNode / batchSetNodes / doLoadChildrenTree
  • Page loader: packages/desktop-app/src/lib/design/components/base-node-viewer.svelte (root/children load)
  • Navigation: packages/desktop-app/src/lib/services/navigation-service.ts

Related Issues

Activity

  1. malibio commented on Sep 25, 2026

    @malibio
    CollaboratorAuthor

    Frontend baseline: 264 files / 5286 tests passed

  2. self-assigned this
    on Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingfrontend

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions