Skip to content

fix(bindx): cancel a local has-many connect when the item is removed again - #165

Merged
vparys merged 2 commits into
mainfrom
fix/remove-unsaved-hasmany-pick
Nov 11, 2026
Merged

vparys merged 2 commits into
mainfrom
fix/remove-unsaved-hasmany-pick

Conversation

@vparys

@vparys vparys commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

Fixes #144.

Problem

connect() on a has-many records the item in plannedAdditions as connected. Removing it again goes through HasManyStore.removeFromHasMany, which cancelled only additions of kind created; for everything else it called planHasManyRemoval, so the item ended up in plannedRemovals although the server never held the edge.

  • On a create form (pick an option in a MultiSelectField, remove it with the chip's × or a second click, save) the mutation fails: "disconnect" is not defined by type …CreateInput. This hit an NPI production form today.
  • On an edit form it sends a disconnect of an item that was never connected.
  • remove() picks delete for a one-has-many with a non-nullable FK. There, pick-then-remove planned a delete. On a new parent the create input rejected it; on a persisted parent the save would delete a row that belongs to another parent.

Fix

Any removal of an item that is a planned connected addition and is in no view's server baseline (relationServerIds) now cancels the addition. This is the same path that removing a created item already takes. Removing or deleting a server member still plans the removal, including an item that was removed, re-connected and removed again.

Tests

tests/unit/persistence/removeUnsavedHasManyPick.test.ts has 9 cases; the pick-then-remove ones fail without the fix. The full suite passes (2558), and tsc --build and eslint are clean. An independent review checked filtered views, the undo journal, in-flight persist reconciliation (reconcileHasManyState re-plans the removal for a connect that was already sent) and has-one (already guarded).

https://claude.ai/code/session_01XJuCeTRNu3XJ65oj8yJjvt

…again

Picking an existing entity into a has-many and removing it before saving
planned a disconnect of an edge the server never held. On a create form the
mutation then failed, because a create input does not accept `disconnect`;
on an edit form it sent a pointless disconnect.

A disconnect of an item that is only a planned connection and is in no view's
server baseline now cancels the connection, the same way removing a created
item cancels its create. Server members and deletes keep planning a removal.

Fixes #144

Claude-Session: https://claude.ai/code/session_01XJuCeTRNu3XJ65oj8yJjvt
`remove()` falls back to `delete` for a one-has-many with a non-nullable
foreign key. Picking an existing child and removing it again then planned a
delete: on a new parent the create input rejected it, on a persisted parent
the save would delete a row that belongs to another parent. A locally
connected item that no view's server baseline holds now cancels its connect
for either removal type.

Claude-Session: https://claude.ai/code/session_01XJuCeTRNu3XJ65oj8yJjvt
@vparys
vparys merged commit 66dfe34 into main Nov 11, 2026
5 checks passed
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.

Removing an unsaved has-many pick records a disconnect, so a create mutation fails

1 participant