Step 5 of the map editor. EditSession becomes the single owner of the constraint set, replacing the pair of refs step 4 kept alongside it: it now holds the saved operations too, and `fragment()` assembles the constraints plus every operation they reference — the shape both preview and save send. That removes the class of bug that produced the earlier 400, because callers can no longer ship a constraint whose provenance points at nothing. Save goes through `expectedDocumentVersion`. A 409 adopts the server's version so the next attempt is checked against reality, and says which version won instead of failing silently. Undo of a saved gesture appends an inverse operation rather than rewriting persisted history; undo of an unsaved one just moves the cursor. Discard drops unsaved commands, cancels anything in flight, and returns the map to the baseline. One gesture mints one operation id, released on pointerup. Reusing an id across gestures put two operations with the same id in the document, which validateEditDocument() rejects. The API test now covers the payload the client actually sends — `constraints` + `operations` merged into the active document, rather than a whole document — and asserts that a save survives a reload as `exact`. Sending constraints without their operations is asserted to be rejected rather than written. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.1 KiB
5.1 KiB