What happens when two windows edit the same note?
Two windows save the same note and the later save is refused. What happens next decides whether the person keeps their text: a revision check, a three-way merge applications opt into, a Keep Mine choice, and a test that holds a save back on purpose.

On this page
You have a note open in two browser windows, signed in as the same person. In the left one you rewrite the second sentence, and its save sets off over a slow connection. A moment later you rewrite the same sentence differently in the right one, and that save reaches the server first and is stored. Then the left window’s save arrives. Both saves were correct when they left, neither window knew about the other, and the server is now holding a request to replace the whole document with a version that has never seen the right window’s sentence.
Macalicious, the Catalina-style desktop I developed with coding agents, has to handle exactly this in Notes and Stickies, and the way it does is worth following, because most of the decisions that protect the person’s text come after the server has noticed the problem. “Window” needs pinning down first. Two Notes windows inside one browser page don’t compete, because every window of an app reads the page’s one copy of the document, so a change saved in one appears in the others. The conflict needs a second page: another tab or another browser, each holding its own copy of the same person’s document.
The refusal
Notes, Reminders and Stickies keep their documents on the desktop’s server, on Ferrite, the application platform I own, through one shared protocol. The server owns an integer revision per document, and a read returns the data with its revision. A save is a PUT of the whole document together with base, the revision the page last had. If base is still the current revision the server stores the document and answers with the next number, and any other base is answered 409 with nothing stored.
Suppose both windows had read revision 12. The right window’s save arrives with base 12, is stored and becomes revision 13. The left window’s save arrives with base 12 and is refused. Because a save replaces the whole document, that check is the only thing standing between the late arrival and the silent loss of a sentence it never saw. It is also all the check does. This is not a CRDT, nothing here converges, and the server hasn’t preserved anything on the left window’s behalf. It has told the page it is out of date, and the person’s rewritten sentence now exists only in that page.
Ruling out your own save
Before a page can treat a 409 as somebody else’s change, it has to rule out its own. A save that meets a network error before any answer comes is sent once more, the same body on the same base. If the first attempt did reach the server and only the answer was lost, the retry finds the revision has moved and earns a 409 against the page’s own earlier work. So after a 409 the page reads the stored document, and when that turns out to be the page’s own save, nothing is dropped and the changes made in the meantime are saved on top of it. One save per document is under way at a time, which stops a page racing itself, and every read and save settles by a deadline, so a hung request can’t keep the document from saving again.
What a generic layer can do
If the stored document really is someone else’s, the shared layer has a problem it can’t solve, because it knows nothing about what a document contains. It can’t combine two versions of something it doesn’t understand, and overwriting would destroy a save that another window has already been told succeeded. So the default goes the other way: the page’s unsaved changes are dropped, it reads the stored document and every window of the app shows it. The later conflicting edit is lost. The same default applies when the outcome can’t be known, after a second network error, a server error or an answer that can’t be read. The reasoning also explains why a page that couldn’t read its document never writes at all, since a write built on the starting data would replace something the page has never seen.
That’s a defensible rule for a layer that knows nothing, and not good enough for a paragraph somebody wrote. So a document can opt in to a merge function that takes three versions:
merge: (base, mine, theirs) => document
base is the stored document the page’s changes were made on, mine is the page’s and theirs is the stored one now, and whatever comes back is what the page keeps and saves on the new revision. A document that has been forgotten on the server is never merged back, so a stale window can’t revive what someone deleted. What goes inside the function is the application’s decision, because only the application knows which parts of its document can be true at the same time.
Changes that can both be true
Recent Items is the simplest case. It is kept in the desktop’s settings document, and if one window opens one app while another opens a different one, both of those things happened and nobody needs to be asked about it. The refused window merges and saves again, and the person never hears about it.
Stickies needs more care. Its document is a list of notes, merged note by note against the base, so two windows working on different notes never meet. Within a note the fields merge independently: colour, frame and states each on their own, with the text and its formats treated as one unit, since formatting separated from the text it formats means nothing. One window can recolour a note while the other moves it, and both changes survive.
Changes that can’t
The text of one note is different. If both windows have changed it, a merge would produce a sentence neither of them wrote, so Stickies doesn’t attempt one. The version saved first stays in the note, the note tells the person so, and the page’s own text and formats are kept aside in the page until the person chooses. “First” here means first to reach the server, which says nothing about which version is better. In the opening scene the left window’s edit came first and lost because of its connection, and this is why the other version has to stay within reach.
Choosing the other window’s version just dismisses the notice. Choosing Keep Mine is an ordinary edit: the page’s text and formats go into the current document and are saved on the current revision, and the note is rebuilt from the result. That does overwrite what the other window wrote, but the overwrite now comes from someone who has seen both versions and decided, which is a different event from a slow packet arriving late. Notes arrives at the same place with a different interface, a conflicted copy beside the note with Show My Version and Keep My Version.
Stickies also narrows the window in which any of this can happen. A change made in another tab or browser reaches the open page and the document is read again, with anything still being typed merged onto it and the caret kept where it was, so a window can catch up before its own save goes stale.
Holding a save on purpose
You can’t test this by typing quickly in two tabs and hoping. The end-to-end test written for it opens two pages of one session, changes the same note in Notes and the same sticky in Stickies, and holds back the first window’s save while the second’s is stored, as a slow network would. Released, the held save meets the newer revision and has to be refused. The window whose change arrived second must then show the version saved first, say so and keep its own.
The exit condition is the part I like. The run exits 0 only when every step passed, each app’s held save was refused exactly once, and neither window logged any other console error, page error, failed request or HTTP error. Exactly once cuts both ways: no refusal means the test never built the conflict it claims to cover, and a second one means the recovery is going wrong. The browser logs a refused save as a failed resource, so the test ignores that one message and counts the response itself, and everything else it sees is a failure.
Recent Items gets a sharper requirement, because there a 409 should be invisible to the person, and the cheapest way to make an error invisible is to catch it and do nothing. So a 409 on the settings document counts as a problem unless a 200 save by the same window follows it. The test also requires that no “did not keep a change” notice appeared, that the stored Recent Items hold the apps both windows opened, and that both windows list them after a reload. A clean console alone could not tell you any of that.
None of this makes the desktop a loss-free editor. A document that doesn’t opt in still loses the later edit, as the default rule says, and the copy Stickies keeps aside lives in the page’s memory, so closing the page before choosing loses that too. From the person’s side, the opted-in path comes down to a button on a sticky note. That button can exist because the server refused the stale save and stored nothing, the page held on to its own text, and the merge separated the one field that couldn’t be reconciled from all the ones that could.
Chris Chabot, November 2026