Last write wins is not a collaboration strategy

D4U7 converged by choosing the newest write and silently removed valid prose, so conflict recovery moved to immutable bases, type-specific merges, and authored resolution.

Last write wins gave D4U7 one clean current document and two people who believed their work had been saved.

I opened the same synthetic proposal on a laptop and a tablet. The laptop added a constraint near the beginning. The tablet rewrote the conclusion. Both started from revision 4. The laptop saved revision 5. The tablet, still holding revision 4, saved a moment later and replaced the whole body.

The database converged. The laptop paragraph disappeared.

No request failed. No duplicate existed. The interface showed “Saved” on both devices. The conflict strategy had optimized for one value and discarded one valid intent.

I stopped treating last write wins as a collaboration mechanism. It was a deterministic tie-breaker with a loss policy.

Convergence was necessary and insufficient

A replicated or cached system needs a way to arrive at coherent state. Last write wins is attractive because it is simple: compare timestamps or versions and keep the later value.

The rule can be appropriate for values where the latest command should replace the earlier one and loss is acceptable. A personal display preference or ephemeral expanded panel may not need a conflict ceremony.

Long-form proposals were different. Two edits could affect separate paragraphs and both remain meaningful. Even overlapping edits could contain reasoning a person needed to preserve. Choosing by arrival time said nothing about semantic compatibility.

The server also received writes in an order shaped by networks and sleep. “Last” could mean the device that reconnected later, not the thought authored later or the change based on newer evidence.

Convergence answered which value the system stored. Collaboration required explaining how concurrent intentions met and ensuring that loss was deliberate.

D4U7 kept deterministic server state while refusing to equate determinism with a justified merge.

Every revision named its base

The first correction was optimistic concurrency. A save supplied the immutable revision from which the editor began:

contributionId: proposal-7
baseRevision: proposal-7/r4
operationId: op-8d1
body: ...

The server accepted the write only if revision 4 remained current. It created revision 5 and returned a receipt. When the tablet later submitted another revision based on 4, the server returned base-changed with the current revision identity.

The tablet text stayed local. The server did not overwrite revision 5 and did not pretend the request had succeeded.

This turned a silent loss into an explicit conflict. It did not resolve the conflict. That was an important improvement in scope. Detection could be reliable even when automatic combination was uncertain.

Stable operation identity handled retries of the same attempt. Base revision handled concurrent meaning. The two IDs served different purposes and both appeared in the revision lineage.

A three-way merge needed the shared ancestor

Comparing only the laptop and tablet bodies could show differences without saying which side changed each passage. Revision 4 supplied the shared ancestor.

A three-way merge classified edits relative to that base:

  • Text changed only in the laptop revision could be carried forward.
  • Text changed only in the tablet draft could be carried forward.
  • Identical changes could collapse.
  • Changes affecting overlapping base ranges required review.
  • Deletion on one side and modification on the other required review.

For the synthetic proposal, the added constraint and rewritten conclusion occupied separate regions. D4U7 produced a merged draft containing both and asked the tablet author to review it before saving against revision 5.

The merge was a proposal, not a hidden write. Markdown structure, moved paragraphs, list renumbering, and wrapped lines could make a textual diff look noisier than the conceptual change. The author remained responsible for the result.

The new revision recorded both its current parent and the conflicting base/draft relationship. History could explain why it existed.

“Mine” and “theirs” described the wrong relationship

Conflict tools often label two sides “yours” and “theirs.” In an offline application, the server revision may also contain the same person's work from another device. The local draft may have been written earlier than the server value but delivered later.

I used neutral, temporal labels:

  • Shared base: the revision both edits began from.
  • Current room version: the latest accepted server revision.
  • Draft on this device: the unsent local text.
  • Proposed merge: a new editable combination not yet accepted.

The labels said what the system knew. They did not imply ownership, correctness, or adversarial sides.

Each panel showed author and time as supporting context, with clock limitations understood. More important, it showed the exact revision identity and change relative to the shared base.

Language mattered during recovery. A conflict already asks a person to reason about loss. The interface should not create a winner before they begin.

Different fields needed different merge semantics

The contribution was more than one Markdown body. It had a title, status, evidence links, labels, and review state. Applying the same merge to every field would be convenient and wrong.

D4U7 defined policies by data meaning:

  • Markdown body: three-way merge proposal with review for overlaps.
  • Short title: explicit choice when both sides changed.
  • Evidence references: set-like addition where independent; explicit review for removal of evidence used elsewhere.
  • Status: domain transition, never merged by timestamp.
  • Deadline: explicit current-state decision with authority check.
  • Acknowledgements: add/remove operations scoped to immutable revision.
  • Display preference: local last write could be acceptable.

This made the merge engine smaller than a generic document merger. It also made schema review more demanding. Every new mutable field needed a concurrency question.

I preferred that demand to silent fallback. If the product could not state how concurrent values should combine, the server should detect conflict rather than invent a rule from storage shape.

JSON does not carry product semantics merely because two values share a key.

Status changes could invalidate text edits

While one device edited a proposal, another could withdraw it or a room could record a decision. The text might merge cleanly and no longer be publishable as an ordinary active proposal revision.

Conflict recovery therefore considered the room lifecycle after textual merge. A proposed merge could be saved as a local draft, attached to an amendment request, or discarded. It could not reactivate a withdrawn proposal by arriving later.

The server evaluated:

  • Is this contribution still editable?
  • Does the current person retain authority?
  • Is the room in a phase that accepts this revision type?
  • Would the revision invalidate a decision referencing an earlier immutable version?

An earlier decision remained linked to the exact proposal revision it had used. Later edits could coexist as history or amendment without rewriting the decision's basis.

This separated content compatibility from workflow validity. A clean diff did not grant permission to change current state.

The outbox's delivery policy and the document's merge policy met at this boundary.

Comments attached to character offsets drifted after edits. A note about “the second sentence” could point into different text or an empty range after a paragraph moved.

D4U7 anchored a comment to an immutable contribution revision, a quoted excerpt, and surrounding context. On a newer revision, the interface attempted to reattach the excerpt. The result could be exact, moved, modified, ambiguous, or absent.

An uncertain reattachment stayed visible as uncertain. The comment always opened its original revision, so the reader could recover what had been discussed.

During a merge, comments from both lineages were considered. If a changed region resolved the concern, the author could mark it addressed and link the new passage. If the relevant text was removed, the comment did not silently jump to a nearby offset.

This added storage and interface work, but it protected the relation between prose and review. A decision process is not only the final text; it includes questions about exact earlier claims.

Immutable revisions made that relationship possible without freezing the document forever.

Conflict resolution deserved a page

I first placed the merge in a modal. The dialog showed two narrow columns, truncated context, and a textarea. It worked for a changed title and failed for a multi-paragraph proposal.

Resolution needed space, a stable URL, and the ability to pause. I built a full page with:

  • A concise explanation of why automatic delivery stopped.
  • Shared base, current revision, and local draft identities.
  • A change-region navigator.
  • Side-by-side or stacked views depending on width.
  • An editable proposed merge.
  • Explicit choices for short fields and status conflicts.
  • Save-locally, copy, export, abandon, and submit-again paths.

The proposed merge itself was autosaved locally under a new draft identity. Refreshing or losing the network did not restart resolution.

Submitting created a new operation against the current server revision. If another revision arrived meanwhile, the process could conflict again without losing either attempt.

Treating conflict as a page acknowledged that recovery was real work, not an error toast to dismiss.

Accessibility changed the comparison design

Colored insertions and deletions were not enough. Keyboard and screen-reader users needed a coherent path through changed regions without reading three complete documents repeatedly.

Each change region had a number, type, and concise label: added in current revision, added in local draft, changed on both, or deleted and modified. Navigation buttons moved between regions and updated a persistent heading. Color supported but did not carry meaning.

The shared base could be opened for context. Current and local excerpts were associated with explicit headings. The proposed merge announced when a choice changed its text without flooding the live region for every keystroke.

Focus stayed on the resolved region after choosing current, local, or combined text. Saving a local merge returned to the same place. No remote update stole focus.

I tested the flow with keyboard-only navigation and a screen reader using captured conflict fixtures. Automated accessibility checks found relationships and labels; manual scenarios judged whether the sequence made sense.

The dense comparison became better for everyone when its structure could be narrated.

Offline networking was not required. Two browser tabs could open the same proposal, each with the same base, and save minutes apart.

I added a short-lived local coordination lease through browser messaging so one tab could warn that the same contribution was open elsewhere. The warning reduced surprise and did not become a lock. Tabs could crash, sleep, or fail to receive the message.

The server's base revision remained the correctness boundary. A multi-tab lease could not grant permission to overwrite or guarantee exclusivity.

Local drafts received tab-independent identities and updated timestamps. If one tab accepted a server revision, another learned that its base was behind and entered conflict before attempting an automatic save where possible.

I did not merge keystrokes continuously. The product optimized for asynchronous long-form contribution, and adding real-time editing would introduce a much larger protocol and interface commitment.

The warning helped. Immutable revisions and server preconditions kept helping after the warning failed.

In 2020, mature ideas for real-time collaborative text existed, and conflict-free replicated data types attracted well-earned interest. They could support simultaneous editing and automatic convergence under disconnection.

D4U7 did not have that product requirement. Contributions were bounded Markdown documents, usually authored by one person and reviewed asynchronously. Conflicts were uncommon enough to deserve deliberate resolution, and status/evidence/decision semantics would still sit outside a text algorithm.

Adopting a general collaborative text model would add identifier growth, selection mapping, persistence rules, garbage collection, client compatibility, and a different editing experience. It could solve concurrent character operations while leaving the room lifecycle conflicts untouched.

I kept immutable revisions and three-way merge. This was not a claim that manual conflict is universally superior. It matched the project's low-concurrency, high-explanation scope.

The decision record included a review trigger: reconsider if simultaneous editing becomes common enough that resolution cost dominates or if the product expands beyond bounded Markdown.

A fashionable algorithm should enter through a demonstrated need, not fear of appearing old.

Revision history avoided turning into a branch forest

Preserving every conflicting draft could produce a confusing graph. D4U7 distinguished accepted contribution revisions from local or rejected operation artifacts.

The server held an immutable linear current lineage for ordinary contribution revisions. A conflict did not create two equally current server branches. The unaccepted draft remained in the local outbox or, if explicitly uploaded for recovery, as a non-current conflict artifact with limited retention.

A resolved revision linked the prior current revision, the shared base, and the operation that supplied the local intent. The metadata explained its merge without making every read traverse a general version-control graph.

If an amendment intentionally forked a prior decision, that was a domain relationship with its own type—not an accidental storage branch.

This kept ordinary history readable while preserving enough provenance to reconstruct loss-sensitive events.

Retention policies differed. Accepted revisions followed room history. Abandoned local drafts remained under device control. Uploaded recovery artifacts expired after the conflict was resolved or exported.

Preservation needed a model, not an infinite pile.

The first search index contained only current contribution text. After a merge, a result supporting an earlier decision could disappear or open into rewritten prose.

I indexed immutable accepted revisions with their effective intervals and relationships. Ordinary search preferred current revisions. Historical and decision-context search could return older text with a clear superseded label.

A decision indexed the exact proposal and evidence revisions it referenced. Opening that result showed the historical basis first and offered the current revision separately.

Local conflict drafts were not added to server search before acceptance. Searching the device outbox remained a separate local concern. This prevented unpublished text from leaking into a shared index.

The merge model therefore affected retrieval. “Current truth” and “historical basis” were different search scopes.

Once again, preserving revisions paid for more than an audit trail. It allowed later features to keep temporal meaning.

Resolution had an outcome receipt

After a person reviewed a merge, the final Save could still time out. Creating another merge operation with a new ID risked duplicate revisions.

The resolution submission used a stable operation ID and named the current base. Its receipt linked:

  • New accepted revision.
  • Prior current revision.
  • Shared conflict base.
  • Superseded local operation.
  • Chosen field resolutions.
  • Author and accepted time.

If the response disappeared, retrying returned the same receipt. The conflict page could query outcome before asking the person to repeat work.

The receipt did not store every transient cursor movement or comparison click. It preserved the decisions that changed the document and allowed the old pending operation to close safely.

This joined X8B6's idempotency with D4U7's revision semantics. A safe retry protected the resolution action; the merge review protected the content.

Both were required to say the conflict had ended without loss.

The tests looked for preserved intent

A typical sync test asserted that all clients eventually displayed the same text. Last write wins passed.

I changed the invariant. After concurrent edits, the system had to reach one of these states:

  • Both independent changes appeared in an explicitly reviewed accepted revision.
  • The conflict remained visible with both inputs recoverable.
  • A person deliberately chose one side and the receipt preserved that choice.

No valid text could disappear merely because a request arrived later.

Fixtures covered separate paragraphs, overlapping sentences, delete-versus-edit, moved Markdown sections, title conflicts, evidence add/remove, room phase change, multiple tabs, offline resume, and a second server revision during merge.

Manual scenarios asked the author to explain which text was current, which remained local, and what would happen on Save. A technically preserved draft hidden behind an incomprehensible error did not satisfy the product invariant.

The test target changed from convergence alone to recoverable convergence.

Losing automatically became choosing explicitly

D4U7 ended with a slower conflict path than last write wins. Most saves stayed fast because their base remained current. The uncommon concurrent case required attention and preserved the reason.

The trade was appropriate for thoughtful proposals. Silent prose loss would damage trust more than a visible reconciliation step. The system could make that step rare through collision hints, clear ownership, and immutable review phases without pretending concurrency never occurred.

Last write wins remained useful for a few low-consequence local values. It stopped being the default inherited from a database field or timestamp.

Collaboration is not achieved when every device eventually shows the same bytes. It is achieved when different intentions can meet without one disappearing through an implementation detail.

The final document still had one current revision. The path to it could now explain what was combined, what was chosen, and what remained in history.