LearnNewsExamplesServices
Frontmatter
title>-
authorneo-opus-ada
stateMerged
createdAtMay 29, 2026, 6:10 PM
updatedAtMay 29, 2026, 6:31 PM
closedAtMay 29, 2026, 6:31 PM
mergedAtMay 29, 2026, 6:31 PM
branchesdevstore-remote-crud
urlhttps://github.com/neomjs/neo/pull/12174
Merged
neo-opus-ada
neo-opus-ada commented on May 29, 2026, 6:10 PM

Authored by Claude Opus 4.8 (Claude Code). Memory-core orchestrator deactivated this session — no Origin Session ID.

FAIR-band: operator-directed PR (explicit request to implement #12173) — not autonomous lane self-selection.

Resolves #12173

Adds the async backend-first CRUD layer on Neo.data.Store: remoteCreate / remoteUpdate / remoteDestroy. The sync mutators (add/insert/remove) stay sync; the new methods call api.{create,update,destroy} first and only mutate locally on success (applying the server-authoritative response), reject + fire a mutationFailed event on failure, and fall back to the local op when the matching api verb is not configured. No RPC plumbing was required — the api dispatch is already method-generic (register() auto-stubs every descriptor method; Message.onMessage routes by method+type), so create/update/destroy dispatch like read.

Evidence: L1 (unit: success / backend-failure / no-api fallback across the three verbs, with a fake api service registered in-namespace) → L1 required (no runtime-only ACs on the neo side; the live websocket round-trip is exercised by the consuming app's CRUD wiring, tracked separately). No residuals.

Deltas from ticket

  • remoteUpdate(record, data) takes the changed data explicitly (the caller passes the form values) rather than auto-deriving the trackModifiedFields delta. There is no public Record delta accessor yet, and the explicit form is cleaner for the Save/Cancel dialog flow. Auto-delta is deferred as a follow-up enhancement.
  • Added remoteRpc(verb, payload) as a @protected helper (resolves the stub and awaits Neo.remotes.Api.ready() when racing registration, mirroring load()).
  • The api config JSDoc already documented {create, read, update, destroy}, so no config change was needed — only the consuming methods were missing.

Test Evidence

  • npm run test-unit -- test/playwright/unit/data/StoreRemoteCrud.spec.mjs7 passed
  • npm run test-unit -- test/playwright/unit/data/63 passed (no regression across Store/TreeStore)

Post-Merge Validation

  • A consuming app wires its CRUD dialogs to remoteCreate/remoteUpdate/remoteDestroy over a live remotes-api websocket and persists create/update/delete end-to-end (tracked in that app's own backlog).